0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-11 06:37:12 -04:00
deno/ext/webgpu/webidl.rs
Leo Kettmeir 7253820764
refactor: object wrap WebGPU (#27665)
Fixes #25874
Fixes #26760
Fixes #24288
Fixes #24798
Fixes #25627
Fixes #25915
Fixes #26769
2025-02-12 13:45:41 +00:00

654 lines
21 KiB
Rust

// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::collections::HashSet;
use deno_core::cppgc::Ptr;
use deno_core::v8;
use deno_core::webidl::ContextFn;
use deno_core::webidl::IntOptions;
use deno_core::webidl::WebIdlConverter;
use deno_core::webidl::WebIdlError;
use deno_core::webidl::WebIdlErrorKind;
use deno_core::WebIDL;
use deno_error::JsErrorBox;
#[derive(WebIDL)]
#[webidl(dictionary)]
pub(crate) struct GPUExtent3DDict {
#[options(enforce_range = true)]
width: u32,
#[webidl(default = 1)]
#[options(enforce_range = true)]
height: u32,
#[webidl(default = 1)]
#[options(enforce_range = true)]
depth_or_array_layers: u32,
}
pub(crate) enum GPUExtent3D {
Dict(GPUExtent3DDict),
Sequence((u32, u32, u32)),
}
impl<'a> WebIdlConverter<'a> for GPUExtent3D {
type Options = ();
fn convert<'b>(
scope: &mut v8::HandleScope<'a>,
value: v8::Local<'a, v8::Value>,
prefix: Cow<'static, str>,
context: ContextFn<'b>,
options: &Self::Options,
) -> Result<Self, WebIdlError> {
if value.is_null_or_undefined() {
return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(
scope,
value,
prefix,
context.borrowed(),
options,
)?));
}
if let Ok(obj) = value.try_cast::<v8::Object>() {
let iter = v8::Symbol::get_iterator(scope);
if let Some(iter) = obj.get(scope, iter.into()) {
if !iter.is_undefined() {
let conv = <Vec<u32>>::convert(
scope,
value,
prefix.clone(),
context.borrowed(),
&IntOptions {
clamp: false,
enforce_range: true,
},
)?;
if !(conv.len() > 1 && conv.len() <= 3) {
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements", conv.len()))));
}
let mut iter = conv.into_iter();
return Ok(GPUExtent3D::Sequence((
iter.next().unwrap(),
iter.next().unwrap_or(1),
iter.next().unwrap_or(1),
)));
}
}
return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(
scope, value, prefix, context, options,
)?));
}
Err(WebIdlError::new(
prefix,
context,
WebIdlErrorKind::ConvertToConverterType(
"sequence<GPUIntegerCoordinate> or GPUExtent3DDict",
),
))
}
}
impl From<GPUExtent3D> for wgpu_types::Extent3d {
fn from(value: GPUExtent3D) -> Self {
match value {
GPUExtent3D::Dict(dict) => Self {
width: dict.width,
height: dict.height,
depth_or_array_layers: dict.depth_or_array_layers,
},
GPUExtent3D::Sequence((width, height, depth)) => Self {
width,
height,
depth_or_array_layers: depth,
},
}
}
}
#[derive(WebIDL)]
#[webidl(dictionary)]
pub(crate) struct GPUOrigin3DDict {
#[webidl(default = 0)]
#[options(enforce_range = true)]
x: u32,
#[webidl(default = 0)]
#[options(enforce_range = true)]
y: u32,
#[webidl(default = 0)]
#[options(enforce_range = true)]
z: u32,
}
pub(crate) enum GPUOrigin3D {
Dict(GPUOrigin3DDict),
Sequence((u32, u32, u32)),
}
impl Default for GPUOrigin3D {
fn default() -> Self {
GPUOrigin3D::Sequence((0, 0, 0))
}
}
impl<'a> WebIdlConverter<'a> for GPUOrigin3D {
type Options = ();
fn convert<'b>(
scope: &mut v8::HandleScope<'a>,
value: v8::Local<'a, v8::Value>,
prefix: Cow<'static, str>,
context: ContextFn<'b>,
options: &Self::Options,
) -> Result<Self, WebIdlError> {
if value.is_null_or_undefined() {
return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(
scope,
value,
prefix,
context.borrowed(),
options,
)?));
}
if let Ok(obj) = value.try_cast::<v8::Object>() {
let iter = v8::Symbol::get_iterator(scope);
if let Some(iter) = obj.get(scope, iter.into()) {
if !iter.is_undefined() {
let conv = <Vec<u32>>::convert(
scope,
value,
prefix.clone(),
context.borrowed(),
&IntOptions {
clamp: false,
enforce_range: true,
},
)?;
if conv.len() > 3 {
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUOrigin3D must have at most 3 elements, received {} elements", conv.len()))));
}
let mut iter = conv.into_iter();
return Ok(GPUOrigin3D::Sequence((
iter.next().unwrap_or(0),
iter.next().unwrap_or(0),
iter.next().unwrap_or(0),
)));
}
}
return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(
scope, value, prefix, context, options,
)?));
}
Err(WebIdlError::new(
prefix,
context,
WebIdlErrorKind::ConvertToConverterType(
"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict",
),
))
}
}
impl From<GPUOrigin3D> for wgpu_types::Origin3d {
fn from(value: GPUOrigin3D) -> Self {
match value {
GPUOrigin3D::Dict(dict) => Self {
x: dict.x,
y: dict.y,
z: dict.z,
},
GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z },
}
}
}
#[derive(WebIDL)]
#[webidl(dictionary)]
pub(crate) struct GPUColorDict {
r: f64,
g: f64,
b: f64,
a: f64,
}
pub(crate) enum GPUColor {
Dict(GPUColorDict),
Sequence((f64, f64, f64, f64)),
}
impl<'a> WebIdlConverter<'a> for GPUColor {
type Options = ();
fn convert<'b>(
scope: &mut v8::HandleScope<'a>,
value: v8::Local<'a, v8::Value>,
prefix: Cow<'static, str>,
context: ContextFn<'b>,
options: &Self::Options,
) -> Result<Self, WebIdlError> {
if value.is_null_or_undefined() {
return Ok(GPUColor::Dict(GPUColorDict::convert(
scope,
value,
prefix,
context.borrowed(),
options,
)?));
}
if let Ok(obj) = value.try_cast::<v8::Object>() {
let iter = v8::Symbol::get_iterator(scope);
if let Some(iter) = obj.get(scope, iter.into()) {
if !iter.is_undefined() {
let conv = <Vec<f64>>::convert(
scope,
value,
prefix.clone(),
context.borrowed(),
options,
)?;
if conv.len() != 4 {
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUColor must have exactly 4 elements, received {} elements", conv.len()))));
}
let mut iter = conv.into_iter();
return Ok(GPUColor::Sequence((
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
)));
}
}
return Ok(GPUColor::Dict(GPUColorDict::convert(
scope, value, prefix, context, options,
)?));
}
Err(WebIdlError::new(
prefix,
context,
WebIdlErrorKind::ConvertToConverterType(
"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict",
),
))
}
}
impl From<GPUColor> for wgpu_types::Color {
fn from(value: GPUColor) -> Self {
match value {
GPUColor::Dict(dict) => Self {
r: dict.r,
g: dict.g,
b: dict.b,
a: dict.a,
},
GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a },
}
}
}
#[derive(WebIDL)]
#[webidl(enum)]
pub(crate) enum GPUAutoLayoutMode {
Auto,
}
pub(crate) enum GPUPipelineLayoutOrGPUAutoLayoutMode {
PipelineLayout(Ptr<crate::pipeline_layout::GPUPipelineLayout>),
AutoLayoutMode(GPUAutoLayoutMode),
}
impl From<GPUPipelineLayoutOrGPUAutoLayoutMode>
for Option<wgpu_core::id::PipelineLayoutId>
{
fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self {
match value {
GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => {
Some(layout.id)
}
GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode(
GPUAutoLayoutMode::Auto,
) => None,
}
}
}
impl<'a> WebIdlConverter<'a> for GPUPipelineLayoutOrGPUAutoLayoutMode {
type Options = ();
fn convert<'b>(
scope: &mut v8::HandleScope<'a>,
value: v8::Local<'a, v8::Value>,
prefix: Cow<'static, str>,
context: ContextFn<'b>,
options: &Self::Options,
) -> Result<Self, WebIdlError> {
if value.is_object() {
Ok(Self::PipelineLayout(WebIdlConverter::convert(
scope, value, prefix, context, options,
)?))
} else {
Ok(Self::AutoLayoutMode(WebIdlConverter::convert(
scope, value, prefix, context, options,
)?))
}
}
}
#[derive(WebIDL, Clone, Hash, Eq, PartialEq)]
#[webidl(enum)]
pub enum GPUFeatureName {
#[webidl(rename = "depth-clip-control")]
DepthClipControl,
#[webidl(rename = "timestamp-query")]
TimestampQuery,
#[webidl(rename = "indirect-first-instance")]
IndirectFirstInstance,
#[webidl(rename = "shader-f16")]
ShaderF16,
#[webidl(rename = "depth32float-stencil8")]
Depth32floatStencil8,
#[webidl(rename = "texture-compression-bc")]
TextureCompressionBc,
#[webidl(rename = "texture-compression-bc-sliced-3d")]
TextureCompressionBcSliced3d,
#[webidl(rename = "texture-compression-etc2")]
TextureCompressionEtc2,
#[webidl(rename = "texture-compression-astc")]
TextureCompressionAstc,
#[webidl(rename = "rg11b10ufloat-renderable")]
Rg11b10ufloatRenderable,
#[webidl(rename = "bgra8unorm-storage")]
Bgra8unormStorage,
#[webidl(rename = "float32-filterable")]
Float32Filterable,
#[webidl(rename = "dual-source-blending")]
DualSourceBlending,
#[webidl(rename = "subgroups")]
Subgroups,
// extended from spec
#[webidl(rename = "texture-format-16-bit-norm")]
TextureFormat16BitNorm,
#[webidl(rename = "texture-compression-astc-hdr")]
TextureCompressionAstcHdr,
#[webidl(rename = "texture-adapter-specific-format-features")]
TextureAdapterSpecificFormatFeatures,
#[webidl(rename = "pipeline-statistics-query")]
PipelineStatisticsQuery,
#[webidl(rename = "timestamp-query-inside-passes")]
TimestampQueryInsidePasses,
#[webidl(rename = "mappable-primary-buffers")]
MappablePrimaryBuffers,
#[webidl(rename = "texture-binding-array")]
TextureBindingArray,
#[webidl(rename = "buffer-binding-array")]
BufferBindingArray,
#[webidl(rename = "storage-resource-binding-array")]
StorageResourceBindingArray,
#[webidl(
rename = "sampled-texture-and-storage-buffer-array-non-uniform-indexing"
)]
SampledTextureAndStorageBufferArrayNonUniformIndexing,
#[webidl(
rename = "uniform-buffer-and-storage-texture-array-non-uniform-indexing"
)]
UniformBufferAndStorageTextureArrayNonUniformIndexing,
#[webidl(rename = "partially-bound-binding-array")]
PartiallyBoundBindingArray,
#[webidl(rename = "multi-draw-indirect")]
MultiDrawIndirect,
#[webidl(rename = "multi-draw-indirect-count")]
MultiDrawIndirectCount,
#[webidl(rename = "push-constants")]
PushConstants,
#[webidl(rename = "address-mode-clamp-to-zero")]
AddressModeClampToZero,
#[webidl(rename = "address-mode-clamp-to-border")]
AddressModeClampToBorder,
#[webidl(rename = "polygon-mode-line")]
PolygonModeLine,
#[webidl(rename = "polygon-mode-point")]
PolygonModePoint,
#[webidl(rename = "conservative-rasterization")]
ConservativeRasterization,
#[webidl(rename = "vertex-writable-storage")]
VertexWritableStorage,
#[webidl(rename = "clear-texture")]
ClearTexture,
#[webidl(rename = "spirv-shader-passthrough")]
SpirvShaderPassthrough,
#[webidl(rename = "multiview")]
Multiview,
#[webidl(rename = "vertex-attribute-64-bit")]
VertexAttribute64Bit,
#[webidl(rename = "shader-f64")]
ShaderF64,
#[webidl(rename = "shader-i16")]
ShaderI16,
#[webidl(rename = "shader-primitive-index")]
ShaderPrimitiveIndex,
#[webidl(rename = "shader-early-depth-test")]
ShaderEarlyDepthTest,
}
pub fn feature_names_to_features(
names: Vec<GPUFeatureName>,
) -> wgpu_types::Features {
use wgpu_types::Features;
let mut features = Features::empty();
for name in names {
#[rustfmt::skip]
let feature = match name {
GPUFeatureName::DepthClipControl => Features::DEPTH_CLIP_CONTROL,
GPUFeatureName::TimestampQuery => Features::TIMESTAMP_QUERY,
GPUFeatureName::IndirectFirstInstance => Features::INDIRECT_FIRST_INSTANCE,
GPUFeatureName::ShaderF16 => Features::SHADER_F16,
GPUFeatureName::Depth32floatStencil8 => Features::DEPTH32FLOAT_STENCIL8,
GPUFeatureName::TextureCompressionBc => Features::TEXTURE_COMPRESSION_BC,
GPUFeatureName::TextureCompressionBcSliced3d => Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
GPUFeatureName::TextureCompressionEtc2 => Features::TEXTURE_COMPRESSION_ETC2,
GPUFeatureName::TextureCompressionAstc => Features::TEXTURE_COMPRESSION_ASTC,
GPUFeatureName::Rg11b10ufloatRenderable => Features::RG11B10UFLOAT_RENDERABLE,
GPUFeatureName::Bgra8unormStorage => Features::BGRA8UNORM_STORAGE,
GPUFeatureName::Float32Filterable => Features::FLOAT32_FILTERABLE,
GPUFeatureName::DualSourceBlending => Features::DUAL_SOURCE_BLENDING,
GPUFeatureName::Subgroups => Features::SUBGROUP,
GPUFeatureName::TextureFormat16BitNorm => Features::TEXTURE_FORMAT_16BIT_NORM,
GPUFeatureName::TextureCompressionAstcHdr => Features::TEXTURE_COMPRESSION_ASTC_HDR,
GPUFeatureName::TextureAdapterSpecificFormatFeatures => Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
GPUFeatureName::PipelineStatisticsQuery => Features::PIPELINE_STATISTICS_QUERY,
GPUFeatureName::TimestampQueryInsidePasses => Features::TIMESTAMP_QUERY_INSIDE_PASSES,
GPUFeatureName::MappablePrimaryBuffers => Features::MAPPABLE_PRIMARY_BUFFERS,
GPUFeatureName::TextureBindingArray => Features::TEXTURE_BINDING_ARRAY,
GPUFeatureName::BufferBindingArray => Features::BUFFER_BINDING_ARRAY,
GPUFeatureName::StorageResourceBindingArray => Features::STORAGE_RESOURCE_BINDING_ARRAY,
GPUFeatureName::SampledTextureAndStorageBufferArrayNonUniformIndexing => Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
GPUFeatureName::UniformBufferAndStorageTextureArrayNonUniformIndexing => Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY,
GPUFeatureName::MultiDrawIndirect => Features::MULTI_DRAW_INDIRECT,
GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT,
GPUFeatureName::PushConstants => Features::PUSH_CONSTANTS,
GPUFeatureName::AddressModeClampToZero => Features::ADDRESS_MODE_CLAMP_TO_ZERO,
GPUFeatureName::AddressModeClampToBorder => Features::ADDRESS_MODE_CLAMP_TO_BORDER,
GPUFeatureName::PolygonModeLine => Features::POLYGON_MODE_LINE,
GPUFeatureName::PolygonModePoint => Features::POLYGON_MODE_POINT,
GPUFeatureName::ConservativeRasterization => Features::CONSERVATIVE_RASTERIZATION,
GPUFeatureName::VertexWritableStorage => Features::VERTEX_WRITABLE_STORAGE,
GPUFeatureName::ClearTexture => Features::CLEAR_TEXTURE,
GPUFeatureName::SpirvShaderPassthrough => Features::SPIRV_SHADER_PASSTHROUGH,
GPUFeatureName::Multiview => Features::MULTIVIEW,
GPUFeatureName::VertexAttribute64Bit => Features::VERTEX_ATTRIBUTE_64BIT,
GPUFeatureName::ShaderF64 => Features::SHADER_F64,
GPUFeatureName::ShaderI16 => Features::SHADER_F16,
GPUFeatureName::ShaderPrimitiveIndex => Features::SHADER_PRIMITIVE_INDEX,
GPUFeatureName::ShaderEarlyDepthTest => Features::SHADER_EARLY_DEPTH_TEST,
};
features.set(feature, true);
}
features
}
pub fn features_to_feature_names(
features: wgpu_types::Features,
) -> HashSet<GPUFeatureName> {
use GPUFeatureName::*;
let mut return_features = HashSet::new();
// api
if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
return_features.insert(DepthClipControl);
}
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
return_features.insert(TimestampQuery);
}
if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
return_features.insert(IndirectFirstInstance);
}
// shader
if features.contains(wgpu_types::Features::SHADER_F16) {
return_features.insert(ShaderF16);
}
// texture formats
if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
return_features.insert(Depth32floatStencil8);
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
return_features.insert(TextureCompressionBc);
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) {
return_features.insert(TextureCompressionBcSliced3d);
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
return_features.insert(TextureCompressionEtc2);
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) {
return_features.insert(TextureCompressionAstc);
}
if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) {
return_features.insert(Rg11b10ufloatRenderable);
}
if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
return_features.insert(Bgra8unormStorage);
}
if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) {
return_features.insert(Float32Filterable);
}
if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) {
return_features.insert(DualSourceBlending);
}
if features.contains(wgpu_types::Features::SUBGROUP) {
return_features.insert(Subgroups);
}
// extended from spec
// texture formats
if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) {
return_features.insert(TextureFormat16BitNorm);
}
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
return_features.insert(TextureCompressionAstcHdr);
}
if features
.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
{
return_features.insert(TextureAdapterSpecificFormatFeatures);
}
// api
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
return_features.insert(PipelineStatisticsQuery);
}
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
return_features.insert(TimestampQueryInsidePasses);
}
if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
return_features.insert(MappablePrimaryBuffers);
}
if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
return_features.insert(TextureBindingArray);
}
if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
return_features.insert(BufferBindingArray);
}
if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
return_features.insert(StorageResourceBindingArray);
}
if features.contains(
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
) {
return_features.insert(SampledTextureAndStorageBufferArrayNonUniformIndexing);
}
if features.contains(
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
) {
return_features.insert(UniformBufferAndStorageTextureArrayNonUniformIndexing);
}
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
return_features.insert(PartiallyBoundBindingArray);
}
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
return_features.insert(MultiDrawIndirect);
}
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
return_features.insert(MultiDrawIndirectCount);
}
if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
return_features.insert(PushConstants);
}
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) {
return_features.insert(AddressModeClampToZero);
}
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
return_features.insert(AddressModeClampToBorder);
}
if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) {
return_features.insert(PolygonModeLine);
}
if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) {
return_features.insert(PolygonModePoint);
}
if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
return_features.insert(ConservativeRasterization);
}
if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
return_features.insert(VertexWritableStorage);
}
if features.contains(wgpu_types::Features::CLEAR_TEXTURE) {
return_features.insert(ClearTexture);
}
if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) {
return_features.insert(SpirvShaderPassthrough);
}
if features.contains(wgpu_types::Features::MULTIVIEW) {
return_features.insert(Multiview);
}
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
return_features.insert(VertexAttribute64Bit);
}
// shader
if features.contains(wgpu_types::Features::SHADER_F64) {
return_features.insert(ShaderF64);
}
if features.contains(wgpu_types::Features::SHADER_I16) {
return_features.insert(ShaderI16);
}
if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
return_features.insert(ShaderPrimitiveIndex);
}
if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) {
return_features.insert(ShaderEarlyDepthTest);
}
return_features
}