// Copyright 2018-2026 the Deno authors. MIT license.

use std::borrow::Cow;
#[allow(
  clippy::disallowed_types,
  reason = "deterministic ordering not needed for GPU feature sets"
)]
use std::collections::HashSet;

use deno_core::WebIDL;
use deno_core::cppgc::Ref;
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_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::PinScope<'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())
        && !iter.is_undefined()
      {
        let conv = <Vec<u32>>::convert(
          scope,
          value,
          prefix.clone(),
          context.borrowed(),
          &IntOptions {
            clamp: false,
            enforce_range: true,
          },
        )?;
        if conv.is_empty() || 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::PinScope<'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())
        && !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::PinScope<'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())
        && !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(Ref<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::PinScope<'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 = "texture-compression-astc-sliced-3d")]
  TextureCompressionAstcSliced3d,
  #[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 = "storage-texture-array-non-uniform-indexing")]
  StorageTextureArrayNonUniformIndexing,
  #[webidl(rename = "uniform-buffer-binding-arrays")]
  UniformBufferBindingArrays,
  #[webidl(rename = "partially-bound-binding-array")]
  PartiallyBoundBindingArray,
  #[webidl(rename = "multi-draw-indirect-count")]
  MultiDrawIndirectCount,
  #[webidl(rename = "immediate-data")]
  ImmediateData,
  #[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 = "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,
  #[webidl(rename = "passthrough-shaders")]
  PassthroughShaders,
}

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::TextureCompressionAstcSliced3d => Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
      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::StorageTextureArrayNonUniformIndexing => Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
      GPUFeatureName::UniformBufferBindingArrays => Features::UNIFORM_BUFFER_BINDING_ARRAYS,
      GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY,
      GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT,
      GPUFeatureName::ImmediateData => Features::IMMEDIATES,
      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::Multiview => Features::MULTIVIEW,
      GPUFeatureName::VertexAttribute64Bit => Features::VERTEX_ATTRIBUTE_64BIT,
      GPUFeatureName::ShaderF64 => Features::SHADER_F64,
      GPUFeatureName::ShaderI16 => Features::SHADER_I16,
      GPUFeatureName::ShaderPrimitiveIndex => Features::SHADER_PRIMITIVE_INDEX,
      GPUFeatureName::ShaderEarlyDepthTest => Features::SHADER_EARLY_DEPTH_TEST,
      GPUFeatureName::PassthroughShaders => Features::EXPERIMENTAL_PASSTHROUGH_SHADERS,
    };
    features.set(feature, true);
  }

  features
}

#[allow(
  clippy::disallowed_types,
  reason = "deterministic ordering not needed for GPU feature sets"
)]
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::TEXTURE_COMPRESSION_ASTC_SLICED_3D)
  {
    return_features.insert(TextureCompressionAstcSliced3d);
  }
  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::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
  {
    return_features.insert(StorageTextureArrayNonUniformIndexing);
  }
  if features.contains(wgpu_types::Features::UNIFORM_BUFFER_BINDING_ARRAYS) {
    return_features.insert(UniformBufferBindingArrays);
  }
  if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
    return_features.insert(PartiallyBoundBindingArray);
  }
  if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
    return_features.insert(MultiDrawIndirectCount);
  }
  if features.contains(wgpu_types::Features::IMMEDIATES) {
    return_features.insert(ImmediateData);
  }
  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::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);
  }
  if features.contains(wgpu_types::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS) {
    return_features.insert(PassthroughShaders);
  }

  return_features
}
