Autogenerate Features mappings

Makes it much easier to add new features.
A few minor changes:
 - names are changed from hyphen-case to snake_case
 - make the features set a typed bitset

Change-Id: Ia5fff4c96421878952d676c56ab42c8719b27478
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/147820
Reviewed-by: Loko Kung <lokokung@google.com>
Auto-Submit: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index a40a306..cdc2dca 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1783,7 +1783,7 @@
     "feature name": {
         "category": "enum",
         "values": [
-            {"value": 0, "name": "undefined", "jsrepr": "undefined"},
+            {"value": 0, "name": "undefined", "jsrepr": "undefined", "valid": false},
             {"value": 1, "name": "depth clip control"},
             {"value": 2, "name": "depth32 float stencil8"},
             {"value": 3, "name": "timestamp query"},
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 1ebf88b..e33e73b 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -1116,6 +1116,14 @@
                            'src/' + native_dir + '/ChainUtils_autogen.cpp',
                            frontend_params))
             renders.append(
+                FileRender('dawn/native/Features.h',
+                           'src/' + native_dir + '/Features_autogen.h',
+                           frontend_params))
+            renders.append(
+                FileRender('dawn/native/Features.inl',
+                           'src/' + native_dir + '/Features_autogen.inl',
+                           frontend_params))
+            renders.append(
                 FileRender('dawn/native/api_absl_format.h',
                            'src/' + native_dir + '/' + api + '_absl_format_autogen.h',
                            frontend_params))
diff --git a/generator/templates/dawn/native/Features.h b/generator/templates/dawn/native/Features.h
new file mode 100644
index 0000000..6907008
--- /dev/null
+++ b/generator/templates/dawn/native/Features.h
@@ -0,0 +1,44 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+{% set namespace_name = Name(metadata.native_namespace) %}
+{% set DIR = namespace_name.concatcase().upper() %}
+#ifndef {{DIR}}_FEATURES_AUTOGEN_H_
+#define {{DIR}}_FEATURES_AUTOGEN_H_
+
+#include "dawn/native/dawn_platform.h"
+#include "dawn/native/DawnNative.h"
+#include "dawn/common/ityp_array.h"
+
+namespace dawn::native {
+
+enum class Feature {
+  {% for enum in types["feature name"].values if enum.valid %}
+    {{as_cppEnum(enum.name)}},
+  {% endfor %}
+  InvalidEnum,
+};
+
+template<>
+struct EnumCount<Feature> {
+    {% set counter = namespace(value = 0) %}
+    {% for enum in types["feature name"].values if enum.valid -%}
+        {% set counter.value = counter.value + 1 %}
+    {% endfor %}
+    static constexpr uint32_t value = {{counter.value}};
+};
+
+}  // namespace dawn::native
+
+#endif  // {{DIR}}_FEATURES_AUTOGEN_H_
diff --git a/generator/templates/dawn/native/Features.inl b/generator/templates/dawn/native/Features.inl
new file mode 100644
index 0000000..f59769f
--- /dev/null
+++ b/generator/templates/dawn/native/Features.inl
@@ -0,0 +1,76 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace dawn::native {
+
+wgpu::FeatureName ToAPI(Feature feature) {
+  switch (feature) {
+    {% for enum in types["feature name"].values if enum.valid %}
+      case Feature::{{as_cppEnum(enum.name)}}:
+        return wgpu::FeatureName::{{as_cppEnum(enum.name)}};
+    {% endfor %}
+    case Feature::InvalidEnum:
+      UNREACHABLE();
+  }
+}
+
+Feature FromAPI(wgpu::FeatureName feature) {
+  switch (feature) {
+    {% for enum in types["feature name"].values %}
+      case wgpu::FeatureName::{{as_cppEnum(enum.name)}}:
+        {% if enum.valid %}
+          return Feature::{{as_cppEnum(enum.name)}};
+        {% else %}
+          return Feature::InvalidEnum;
+        {% endif %}
+    {% endfor %}
+    default:
+      return Feature::InvalidEnum;
+  }
+}
+
+static constexpr bool FeatureInfoIsDefined(Feature feature) {
+  for (const auto& info : kFeatureInfo) {
+    if (info.feature == feature) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static constexpr ityp::array<Feature, FeatureInfo, kEnumCount<Feature>> InitializeFeatureEnumAndInfoList() {
+  constexpr size_t kInfoCount = sizeof(kFeatureInfo) / sizeof(kFeatureInfo[0]);
+  ityp::array<Feature, FeatureInfo, kEnumCount<Feature>> list{};
+  {% for enum in types["feature name"].values if enum.valid %}
+    {
+      static_assert(FeatureInfoIsDefined(Feature::{{as_cppEnum(enum.name)}}),
+                    "Please define feature info for {{as_cppEnum(enum.name)}} in Features.cpp");
+      for (size_t i = 0; i < kInfoCount; ++i) {
+        if (kFeatureInfo[i].feature == Feature::{{as_cppEnum(enum.name)}}) {
+          list[Feature::{{as_cppEnum(enum.name)}}] = {
+            "{{enum.name.snake_case()}}",
+            kFeatureInfo[i].info.description,
+            kFeatureInfo[i].info.url,
+            kFeatureInfo[i].info.featureState,
+          };
+        }
+      }
+    }
+  {% endfor %}
+  return list;
+}
+
+const ityp::array<Feature, FeatureInfo, kEnumCount<Feature>> kFeatureNameAndInfoList = InitializeFeatureEnumAndInfoList();
+
+}
diff --git a/src/dawn/common/ityp_array.h b/src/dawn/common/ityp_array.h
index 1a20538..a18cab0 100644
--- a/src/dawn/common/ityp_array.h
+++ b/src/dawn/common/ityp_array.h
@@ -44,7 +44,7 @@
     // NOLINTNEXTLINE(runtime/explicit)
     constexpr array(Values&&... values) : Base{std::forward<Values>(values)...} {}
 
-    Value& operator[](Index i) {
+    constexpr Value& operator[](Index i) {
         I index = static_cast<I>(i);
         ASSERT(index >= 0 && index < I(Size));
         return Base::operator[](index);
diff --git a/src/dawn/common/ityp_bitset.h b/src/dawn/common/ityp_bitset.h
index 66dcbb3..782f509 100644
--- a/src/dawn/common/ityp_bitset.h
+++ b/src/dawn/common/ityp_bitset.h
@@ -35,6 +35,9 @@
     explicit constexpr bitset(const Base& rhs) : Base(rhs) {}
 
   public:
+    const Base& AsBase() const { return static_cast<const Base&>(*this); }
+    Base& AsBase() { return static_cast<Base&>(*this); }
+
     using reference = typename Base::reference;
 
     constexpr bitset() noexcept : Base() {}
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 756d01e..c3ae875 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -86,6 +86,8 @@
   outputs = [
     "src/dawn/native/ChainUtils_autogen.h",
     "src/dawn/native/ChainUtils_autogen.cpp",
+    "src/dawn/native/Features_autogen.h",
+    "src/dawn/native/Features_autogen.inl",
     "src/dawn/native/ProcTable.cpp",
     "src/dawn/native/dawn_platform_autogen.h",
     "src/dawn/native/wgpu_structs_autogen.h",
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 0ecd3cc..e82b377 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -272,7 +272,7 @@
     DAWN_INVALID_IF(
         !device->HasFeature(Feature::MSAARenderToSingleSampled),
         "The color attachment %s has implicit sample count while the %s feature is not enabled.",
-        colorAttachment.view, FeatureEnumToAPIFeature(Feature::MSAARenderToSingleSampled));
+        colorAttachment.view, ToAPI(Feature::MSAARenderToSingleSampled));
 
     DAWN_INVALID_IF(!IsValidSampleCount(msaaRenderToSingleSampledDesc->implicitSampleCount) ||
                         msaaRenderToSingleSampledDesc->implicitSampleCount <= 1,
diff --git a/src/dawn/native/CommandValidation.cpp b/src/dawn/native/CommandValidation.cpp
index afd37ba..7d30e42 100644
--- a/src/dawn/native/CommandValidation.cpp
+++ b/src/dawn/native/CommandValidation.cpp
@@ -85,12 +85,10 @@
                                   Feature requiredFeature) {
     DAWN_TRY(device->ValidateObject(querySet));
 
-    DAWN_INVALID_IF(!device->HasFeature(requiredFeature),
-                    "Timestamp queries used without the %s feature enabled.",
-                    device->GetPhysicalDevice()
-                        ->GetInstance()
-                        ->GetFeatureInfo(FeatureEnumToAPIFeature(requiredFeature))
-                        ->name);
+    DAWN_INVALID_IF(
+        !device->HasFeature(requiredFeature),
+        "Timestamp queries used without the %s feature enabled.",
+        device->GetPhysicalDevice()->GetInstance()->GetFeatureInfo(ToAPI(requiredFeature))->name);
 
     DAWN_INVALID_IF(querySet->GetQueryType() != wgpu::QueryType::Timestamp,
                     "The type of %s is not %s.", querySet, wgpu::QueryType::Timestamp);
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index 7c61f95..323d9e5 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -19,155 +19,139 @@
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/BitSetIterator.h"
+#include "dawn/common/ityp_array.h"
 
 namespace dawn::native {
 namespace {
 
-struct FeatureEnumAndInfo {
-    Feature feature;
-    FeatureInfo info;
+struct ManualFeatureInfo {
+    const char* description;
+    const char* url;
+    FeatureInfo::FeatureState featureState;
 };
 
-using FeatureEnumAndInfoList =
-    std::array<FeatureEnumAndInfo, static_cast<size_t>(Feature::EnumCount)>;
+struct FeatureEnumAndInfo {
+    Feature feature;
+    ManualFeatureInfo info;
+};
 
-static constexpr FeatureEnumAndInfoList kFeatureNameAndInfoList = {{
+static constexpr FeatureEnumAndInfo kFeatureInfo[] = {
     {Feature::TextureCompressionBC,
-     {"texture-compression-bc", "Support Block Compressed (BC) texture formats",
+     {"Support Block Compressed (BC) texture formats",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=42", FeatureInfo::FeatureState::Stable}},
     {Feature::TextureCompressionETC2,
-     {"texture-compression-etc2",
-      "Support Ericsson Texture Compressed (ETC2/EAC) texture "
-      "formats",
+     {"Support Ericsson Texture Compressed (ETC2/EAC) texture formats",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=955", FeatureInfo::FeatureState::Stable}},
     {Feature::TextureCompressionASTC,
-     {"texture-compression-astc",
-      "Support Adaptable Scalable Texture Compressed (ASTC) "
+     {"Support Adaptable Scalable Texture Compressed (ASTC) "
       "texture formats",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=955", FeatureInfo::FeatureState::Stable}},
     {Feature::PipelineStatisticsQuery,
-     {"pipeline-statistics-query", "Support Pipeline Statistics Query",
-      "https://bugs.chromium.org/p/dawn/issues/detail?id=434",
+     {"Support Pipeline Statistics Query", "https://bugs.chromium.org/p/dawn/issues/detail?id=434",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::TimestampQuery,
-     {"timestamp-query", "Support Timestamp Query",
-      "https://bugs.chromium.org/p/dawn/issues/detail?id=434",
+     {"Support Timestamp Query", "https://bugs.chromium.org/p/dawn/issues/detail?id=434",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::TimestampQueryInsidePasses,
-     {"timestamp-query-inside-passes", "Support Timestamp Query inside render/compute pass",
+     {"Support Timestamp Query inside render/compute pass",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=434",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::DepthClipControl,
-     {"depth-clip-control", "Disable depth clipping of primitives to the clip volume",
+     {"Disable depth clipping of primitives to the clip volume",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1178", FeatureInfo::FeatureState::Stable}},
     {Feature::Depth32FloatStencil8,
-     {"depth32float-stencil8", "Support depth32float-stencil8 texture format",
+     {"Support depth32float-stencil8 texture format",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=690", FeatureInfo::FeatureState::Stable}},
     {Feature::ChromiumExperimentalDp4a,
-     {"chromium-experimental-dp4a", "Support experimental DP4a instructions in WGSL",
+     {"Support experimental DP4a instructions in WGSL",
       "https://bugs.chromium.org/p/tint/issues/detail?id=1497",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::IndirectFirstInstance,
-     {"indirect-first-instance", "Support non-zero first instance values on indirect draw calls",
+     {"Support non-zero first instance values on indirect draw calls",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1197", FeatureInfo::FeatureState::Stable}},
     {Feature::ShaderF16,
-     {"shader-f16", "Supports the \"enable f16;\" directive in WGSL",
+     {"Supports the \"enable f16;\" directive in WGSL",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1510",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::RG11B10UfloatRenderable,
-     {"rg11b10ufloat-renderable",
-      "Allows the RENDER_ATTACHMENT usage on textures with format \"rg11b10ufloat\", and also "
+     {"Allows the RENDER_ATTACHMENT usage on textures with format \"rg11b10ufloat\", and also "
       "allows textures of that format to be multisampled.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1518", FeatureInfo::FeatureState::Stable}},
     {Feature::BGRA8UnormStorage,
-     {"bgra8unorm-storage", "Allows the STORAGE usage on textures with format \"bgra8unorm\".",
+     {"Allows the STORAGE usage on textures with format \"bgra8unorm\".",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1591", FeatureInfo::FeatureState::Stable}},
     {Feature::Float32Filterable,
-     {"float32-filterable",
-      "Allows textures with formats \"r32float\" \"rg32float\" and \"rgba32float\" to be filtered.",
+     {"Allows textures with formats \"r32float\" \"rg32float\" and \"rgba32float\" to be filtered.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1664",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::ChromiumExperimentalSubgroups,
-     {"chromium-experimental-subgroups",
-      "Experimental, allows using subgroup and supports the \"enable "
-      "chromium_experimental_subgroups\" directive "
-      "in WGSL. Only used to investigate the semantic of subgroups and should not be relied upon.",
+     {"Experimental, allows using subgroup and supports the \"enable "
+      "chromium_experimental_subgroups\" directive in WGSL. Only used to investigate the semantic "
+      "of subgroups and should not be relied upon.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=464",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::ChromiumExperimentalSubgroupUniformControlFlow,
-     {"chromium-experimental-subgroup-uniform-control-flow",
-      "Experimental, supports VK_KHR_shader_subgroup_uniform_control_flow on Vulkan devices. Only "
+     {"Experimental, supports VK_KHR_shader_subgroup_uniform_control_flow on Vulkan devices. Only "
       "used to investigate the semantic of subgroups and should not be relied upon.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=464",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::ChromiumExperimentalReadWriteStorageTexture,
-     {"chromium-experimental-read-write-storage-texture",
-      "Experimental, supports ReadOnly and ReadWrite as storage texture access mode.",
+     {"Experimental, supports ReadOnly and ReadWrite as storage texture access mode.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1972",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::DawnInternalUsages,
-     {"dawn-internal-usages",
-      "Add internal usages to resources to affect how the texture is allocated, but not "
+     {"Add internal usages to resources to affect how the texture is allocated, but not "
       "frontend validation. Other internal commands may access this usage.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "dawn_internal_usages.md",
       FeatureInfo::FeatureState::Stable}},
-    {Feature::MultiPlanarFormats,
-     {"multiplanar-formats", "Import and use multi-planar texture formats with per plane views",
+    {Feature::DawnMultiPlanarFormats,
+     {"Import and use multi-planar texture formats with per plane views",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=551", FeatureInfo::FeatureState::Stable}},
     {Feature::DawnNative,
-     {"dawn-native", "WebGPU is running on top of dawn_native.",
+     {"WebGPU is running on top of dawn_native.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "dawn_native.md",
       FeatureInfo::FeatureState::Stable}},
     {Feature::ImplicitDeviceSynchronization,
-     {"implicit-device-sync",
-      "Public API methods (except encoding) will have implicit device synchronization. So they "
+     {"Public API methods (except encoding) will have implicit device synchronization. So they "
       "will be safe to be used on multiple threads.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1662", FeatureInfo::FeatureState::Stable}},
     {Feature::SurfaceCapabilities,
-     {"surface-capabilities",
-      "Support querying Surface's capabilities such as supported usage flags. This feature also "
+     {"Support querying Surface's capabilities such as supported usage flags. This feature also "
       "enables swap chain to be created with usage other than RenderAttachment.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1760", FeatureInfo::FeatureState::Stable}},
     {Feature::TransientAttachments,
-     {"transient-attachments",
-      "Support transient attachments that allow render pass operations to stay in tile memory, "
+     {"Support transient attachments that allow render pass operations to stay in tile memory, "
       "avoiding VRAM traffic and potentially avoiding VRAM allocation for the textures.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1695", FeatureInfo::FeatureState::Stable}},
     {Feature::MSAARenderToSingleSampled,
-     {"msaa-render-to-single-sampled",
-      "Support multisampled rendering on single-sampled attachments efficiently.",
+     {"Support multisampled rendering on single-sampled attachments efficiently.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1710", FeatureInfo::FeatureState::Stable}},
     {Feature::DualSourceBlending,
-     {"dual-source-blending",
-      "Support dual source blending. Enables Src1, OneMinusSrc1, Src1Alpha, and OneMinusSrc1Alpha "
+     {"Support dual source blending. Enables Src1, OneMinusSrc1, Src1Alpha, and OneMinusSrc1Alpha "
       "blend factors along with @index WGSL output attribute.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "dual_source_blending.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::D3D11MultithreadProtected,
-     {"d3d11-multithread-protected",
-      "Enable ID3D11Multithread protection for interop with external users of the D3D11 device.",
+     {"Enable ID3D11Multithread protection for interop with external users of the D3D11 device.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1927", FeatureInfo::FeatureState::Stable}},
     {Feature::ANGLETextureSharing,
-     {"angle-texture-sharing",
-      "Enable ANGLE texture sharing to allow the OpenGL ES backend to share textures by external "
+     {"Enable ANGLE texture sharing to allow the OpenGL ES backend to share textures by external "
       "OpenGL texture ID.",
       "https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/"
       "EGL_ANGLE_display_texture_share_group.txt",
       FeatureInfo::FeatureState::Stable}},
     {Feature::PixelLocalStorageCoherent,
-     {"pixel-local-storage-coherent",
-      "Supports passing information between invocation in a render pass that cover the same pixel."
+     {"Supports passing information between invocation in a render pass that cover the same pixel."
       "This helps more efficiently implement algorithms that would otherwise require ping-ponging"
       "between render targets. The coherent version of this extension means that no barrier calls"
       "are needed to prevent data races between fragment shaders on the same pixel.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1704",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::PixelLocalStorageNonCoherent,
-     {"pixel-local-storage-non-coherent",
-      "Supports passing information between invocation in a render pass that cover the same pixel."
+     {"Supports passing information between invocation in a render pass that cover the same pixel."
       "This helps more efficiently implement algorithms that would otherwise require ping-ponging"
       "between render targets. The non-coherent version of this extension means that barrier calls"
       "are needed to prevent data races between fragment shaders on the same pixels (note that "
@@ -175,305 +159,100 @@
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1704",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::Norm16TextureFormats,
-     {"norm16-texture-formats", "Supports R/RG/RGBA16 norm texture formats",
+     {"Supports R/RG/RGBA16 norm texture formats",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1982", FeatureInfo::FeatureState::Stable}},
     {Feature::SharedTextureMemoryVkDedicatedAllocation,
-     {"shared-texture-memory-vk-dedicated-allocation",
-      "Support specifying whether a Vulkan allocation for shared texture memory is a dedicated "
+     {"Support specifying whether a Vulkan allocation for shared texture memory is a dedicated "
       "memory allocation.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryAHardwareBuffer,
-     {"shared-texture-memory-a-hardware-buffer",
-      "Support importing AHardwareBuffer as shared texture memory.",
+     {"Support importing AHardwareBuffer as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryDmaBuf,
-     {"shared-texture-memory-dma-buf", "Support importing DmaBuf as shared texture memory.",
+     {"Support importing DmaBuf as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryOpaqueFD,
-     {"shared-texture-memory-opaque-fd", "Support importing OpaqueFD as shared texture memory.",
+     {"Support importing OpaqueFD as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryZirconHandle,
-     {"shared-texture-memory-zircon-handle",
-      "Support importing ZirconHandle as shared texture memory.",
+     {"Support importing ZirconHandle as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryDXGISharedHandle,
-     {"shared-texture-memory-dxgi-handle",
-      "Support importing DXGISharedHandle as shared texture memory.",
+     {"Support importing DXGISharedHandle as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryD3D11Texture2D,
-     {"shared-texture-memory-d3d11-texture-2d",
-      "Support importing D3D11Texture2D as shared texture memory.",
+     {"Support importing D3D11Texture2D as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryIOSurface,
-     {"shared-texture-memory-io-surface", "Support importing IOSurface as shared texture memory.",
+     {"Support importing IOSurface as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedTextureMemoryEGLImage,
-     {"shared-texture-memory-egl-image", "Support importing EGLImage as shared texture memory.",
+     {"Support importing EGLImage as shared texture memory.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "shared_texture_memory.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedFenceVkSemaphoreOpaqueFD,
-     {"shared-fence-vk-semaphore-opaque-fd",
-      "Support for importing and exporting VkSemaphoreOpaqueFD used for GPU synchronization.",
+     {"Support for importing and exporting VkSemaphoreOpaqueFD used for GPU synchronization.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/shared_fence.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedFenceVkSemaphoreSyncFD,
-     {"shared-fence-vk-semaphore-sync-fd",
-      "Support for importing and exporting VkSemaphoreSyncFD used for GPU synchronization.",
+     {"Support for importing and exporting VkSemaphoreSyncFD used for GPU synchronization.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/shared_fence.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedFenceVkSemaphoreZirconHandle,
-     {"shared-fence-vk-semaphore-zircon-handle",
-      "Support for importing and exporting VkSemaphoreZirconHandle used for GPU synchronization.",
+     {"Support for importing and exporting VkSemaphoreZirconHandle used for GPU synchronization.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/shared_fence.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedFenceDXGISharedHandle,
-     {"shared-fence-vk-semaphore-dxgi-fence-handle",
-      "Support for importing and exporting DXGISharedHandle used for GPU synchronization.",
+     {"Support for importing and exporting DXGISharedHandle used for GPU synchronization.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/shared_fence.md",
       FeatureInfo::FeatureState::Experimental}},
     {Feature::SharedFenceMTLSharedEvent,
-     {"shared-fence-vk-semaphore-mtl-shared-event",
-      "Support for importing and exporting MTLSharedEvent used for GPU synchronization.",
+     {"Support for importing and exporting MTLSharedEvent used for GPU synchronization.",
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/shared_fence.md",
       FeatureInfo::FeatureState::Experimental}},
-}};
-
-Feature FromAPIFeature(wgpu::FeatureName feature) {
-    switch (feature) {
-        case wgpu::FeatureName::Undefined:
-            return Feature::InvalidEnum;
-
-        case wgpu::FeatureName::TimestampQuery:
-            return Feature::TimestampQuery;
-        case wgpu::FeatureName::TimestampQueryInsidePasses:
-            return Feature::TimestampQueryInsidePasses;
-        case wgpu::FeatureName::PipelineStatisticsQuery:
-            return Feature::PipelineStatisticsQuery;
-        case wgpu::FeatureName::TextureCompressionBC:
-            return Feature::TextureCompressionBC;
-        case wgpu::FeatureName::TextureCompressionETC2:
-            return Feature::TextureCompressionETC2;
-        case wgpu::FeatureName::TextureCompressionASTC:
-            return Feature::TextureCompressionASTC;
-        case wgpu::FeatureName::DepthClipControl:
-            return Feature::DepthClipControl;
-        case wgpu::FeatureName::Depth32FloatStencil8:
-            return Feature::Depth32FloatStencil8;
-        case wgpu::FeatureName::IndirectFirstInstance:
-            return Feature::IndirectFirstInstance;
-        case wgpu::FeatureName::DawnInternalUsages:
-            return Feature::DawnInternalUsages;
-        case wgpu::FeatureName::DawnMultiPlanarFormats:
-            return Feature::MultiPlanarFormats;
-        case wgpu::FeatureName::DawnNative:
-            return Feature::DawnNative;
-        case wgpu::FeatureName::ChromiumExperimentalDp4a:
-            return Feature::ChromiumExperimentalDp4a;
-        case wgpu::FeatureName::ShaderF16:
-            return Feature::ShaderF16;
-        case wgpu::FeatureName::RG11B10UfloatRenderable:
-            return Feature::RG11B10UfloatRenderable;
-        case wgpu::FeatureName::BGRA8UnormStorage:
-            return Feature::BGRA8UnormStorage;
-        case wgpu::FeatureName::ImplicitDeviceSynchronization:
-            return Feature::ImplicitDeviceSynchronization;
-        case wgpu::FeatureName::SurfaceCapabilities:
-            return Feature::SurfaceCapabilities;
-        case wgpu::FeatureName::TransientAttachments:
-            return Feature::TransientAttachments;
-        case wgpu::FeatureName::Float32Filterable:
-            return Feature::Float32Filterable;
-        case wgpu::FeatureName::MSAARenderToSingleSampled:
-            return Feature::MSAARenderToSingleSampled;
-        case wgpu::FeatureName::DualSourceBlending:
-            return Feature::DualSourceBlending;
-        case wgpu::FeatureName::D3D11MultithreadProtected:
-            return Feature::D3D11MultithreadProtected;
-        case wgpu::FeatureName::ANGLETextureSharing:
-            return Feature::ANGLETextureSharing;
-        case wgpu::FeatureName::PixelLocalStorageCoherent:
-            return Feature::PixelLocalStorageCoherent;
-        case wgpu::FeatureName::PixelLocalStorageNonCoherent:
-            return Feature::PixelLocalStorageNonCoherent;
-        case wgpu::FeatureName::Norm16TextureFormats:
-            return Feature::Norm16TextureFormats;
-        case wgpu::FeatureName::SharedTextureMemoryVkDedicatedAllocation:
-            return Feature::SharedTextureMemoryVkDedicatedAllocation;
-        case wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer:
-            return Feature::SharedTextureMemoryAHardwareBuffer;
-        case wgpu::FeatureName::SharedTextureMemoryDmaBuf:
-            return Feature::SharedTextureMemoryDmaBuf;
-        case wgpu::FeatureName::SharedTextureMemoryOpaqueFD:
-            return Feature::SharedTextureMemoryOpaqueFD;
-        case wgpu::FeatureName::SharedTextureMemoryZirconHandle:
-            return Feature::SharedTextureMemoryZirconHandle;
-        case wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle:
-            return Feature::SharedTextureMemoryDXGISharedHandle;
-        case wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D:
-            return Feature::SharedTextureMemoryD3D11Texture2D;
-        case wgpu::FeatureName::SharedTextureMemoryIOSurface:
-            return Feature::SharedTextureMemoryIOSurface;
-        case wgpu::FeatureName::SharedTextureMemoryEGLImage:
-            return Feature::SharedTextureMemoryEGLImage;
-        case wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD:
-            return Feature::SharedFenceVkSemaphoreOpaqueFD;
-        case wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD:
-            return Feature::SharedFenceVkSemaphoreSyncFD;
-        case wgpu::FeatureName::SharedFenceVkSemaphoreZirconHandle:
-            return Feature::SharedFenceVkSemaphoreZirconHandle;
-        case wgpu::FeatureName::SharedFenceDXGISharedHandle:
-            return Feature::SharedFenceDXGISharedHandle;
-        case wgpu::FeatureName::SharedFenceMTLSharedEvent:
-            return Feature::SharedFenceMTLSharedEvent;
-        case wgpu::FeatureName::ChromiumExperimentalSubgroups:
-            return Feature::ChromiumExperimentalSubgroups;
-        case wgpu::FeatureName::ChromiumExperimentalSubgroupUniformControlFlow:
-            return Feature::ChromiumExperimentalSubgroupUniformControlFlow;
-        case wgpu::FeatureName::ChromiumExperimentalReadWriteStorageTexture:
-            return Feature::ChromiumExperimentalReadWriteStorageTexture;
-    }
-    return Feature::InvalidEnum;
-}
-
-wgpu::FeatureName ToAPIFeature(Feature feature) {
-    switch (feature) {
-        case Feature::TextureCompressionBC:
-            return wgpu::FeatureName::TextureCompressionBC;
-        case Feature::TextureCompressionETC2:
-            return wgpu::FeatureName::TextureCompressionETC2;
-        case Feature::TextureCompressionASTC:
-            return wgpu::FeatureName::TextureCompressionASTC;
-        case Feature::PipelineStatisticsQuery:
-            return wgpu::FeatureName::PipelineStatisticsQuery;
-        case Feature::TimestampQuery:
-            return wgpu::FeatureName::TimestampQuery;
-        case Feature::TimestampQueryInsidePasses:
-            return wgpu::FeatureName::TimestampQueryInsidePasses;
-        case Feature::DepthClipControl:
-            return wgpu::FeatureName::DepthClipControl;
-        case Feature::Depth32FloatStencil8:
-            return wgpu::FeatureName::Depth32FloatStencil8;
-        case Feature::IndirectFirstInstance:
-            return wgpu::FeatureName::IndirectFirstInstance;
-        case Feature::DawnInternalUsages:
-            return wgpu::FeatureName::DawnInternalUsages;
-        case Feature::MultiPlanarFormats:
-            return wgpu::FeatureName::DawnMultiPlanarFormats;
-        case Feature::Norm16TextureFormats:
-            return wgpu::FeatureName::Norm16TextureFormats;
-        case Feature::DawnNative:
-            return wgpu::FeatureName::DawnNative;
-        case Feature::ChromiumExperimentalDp4a:
-            return wgpu::FeatureName::ChromiumExperimentalDp4a;
-        case Feature::ShaderF16:
-            return wgpu::FeatureName::ShaderF16;
-        case Feature::RG11B10UfloatRenderable:
-            return wgpu::FeatureName::RG11B10UfloatRenderable;
-        case Feature::BGRA8UnormStorage:
-            return wgpu::FeatureName::BGRA8UnormStorage;
-        case Feature::ImplicitDeviceSynchronization:
-            return wgpu::FeatureName::ImplicitDeviceSynchronization;
-        case Feature::SurfaceCapabilities:
-            return wgpu::FeatureName::SurfaceCapabilities;
-        case Feature::TransientAttachments:
-            return wgpu::FeatureName::TransientAttachments;
-        case Feature::Float32Filterable:
-            return wgpu::FeatureName::Float32Filterable;
-        case Feature::MSAARenderToSingleSampled:
-            return wgpu::FeatureName::MSAARenderToSingleSampled;
-        case Feature::DualSourceBlending:
-            return wgpu::FeatureName::DualSourceBlending;
-        case Feature::D3D11MultithreadProtected:
-            return wgpu::FeatureName::D3D11MultithreadProtected;
-        case Feature::ANGLETextureSharing:
-            return wgpu::FeatureName::ANGLETextureSharing;
-        case Feature::PixelLocalStorageCoherent:
-            return wgpu::FeatureName::PixelLocalStorageCoherent;
-        case Feature::PixelLocalStorageNonCoherent:
-            return wgpu::FeatureName::PixelLocalStorageNonCoherent;
-        case Feature::SharedTextureMemoryVkDedicatedAllocation:
-            return wgpu::FeatureName::SharedTextureMemoryVkDedicatedAllocation;
-        case Feature::SharedTextureMemoryAHardwareBuffer:
-            return wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer;
-        case Feature::SharedTextureMemoryDmaBuf:
-            return wgpu::FeatureName::SharedTextureMemoryDmaBuf;
-        case Feature::SharedTextureMemoryOpaqueFD:
-            return wgpu::FeatureName::SharedTextureMemoryOpaqueFD;
-        case Feature::SharedTextureMemoryZirconHandle:
-            return wgpu::FeatureName::SharedTextureMemoryZirconHandle;
-        case Feature::SharedTextureMemoryDXGISharedHandle:
-            return wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle;
-        case Feature::SharedTextureMemoryD3D11Texture2D:
-            return wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D;
-        case Feature::SharedTextureMemoryIOSurface:
-            return wgpu::FeatureName::SharedTextureMemoryIOSurface;
-        case Feature::SharedTextureMemoryEGLImage:
-            return wgpu::FeatureName::SharedTextureMemoryEGLImage;
-        case Feature::SharedFenceVkSemaphoreOpaqueFD:
-            return wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD;
-        case Feature::SharedFenceVkSemaphoreSyncFD:
-            return wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD;
-        case Feature::SharedFenceVkSemaphoreZirconHandle:
-            return wgpu::FeatureName::SharedFenceVkSemaphoreZirconHandle;
-        case Feature::SharedFenceDXGISharedHandle:
-            return wgpu::FeatureName::SharedFenceDXGISharedHandle;
-        case Feature::SharedFenceMTLSharedEvent:
-            return wgpu::FeatureName::SharedFenceMTLSharedEvent;
-        case Feature::ChromiumExperimentalSubgroups:
-            return wgpu::FeatureName::ChromiumExperimentalSubgroups;
-        case Feature::ChromiumExperimentalSubgroupUniformControlFlow:
-            return wgpu::FeatureName::ChromiumExperimentalSubgroupUniformControlFlow;
-        case Feature::ChromiumExperimentalReadWriteStorageTexture:
-            return wgpu::FeatureName::ChromiumExperimentalReadWriteStorageTexture;
-        case Feature::EnumCount:
-            break;
-    }
-    UNREACHABLE();
-}
+};
 
 }  // anonymous namespace
 
 void FeaturesSet::EnableFeature(Feature feature) {
     ASSERT(feature != Feature::InvalidEnum);
-    const size_t featureIndex = static_cast<size_t>(feature);
-    featuresBitSet.set(featureIndex);
+    featuresBitSet.set(feature);
 }
 
 void FeaturesSet::EnableFeature(wgpu::FeatureName feature) {
-    EnableFeature(FromAPIFeature(feature));
+    EnableFeature(FromAPI(feature));
 }
 
 bool FeaturesSet::IsEnabled(Feature feature) const {
     ASSERT(feature != Feature::InvalidEnum);
-    const size_t featureIndex = static_cast<size_t>(feature);
-    return featuresBitSet[featureIndex];
+    return featuresBitSet[feature];
 }
 
 bool FeaturesSet::IsEnabled(wgpu::FeatureName feature) const {
-    Feature f = FromAPIFeature(feature);
+    Feature f = FromAPI(feature);
     return f != Feature::InvalidEnum && IsEnabled(f);
 }
 
 size_t FeaturesSet::EnumerateFeatures(wgpu::FeatureName* features) const {
-    for (uint32_t i : IterateBitSet(featuresBitSet)) {
-        wgpu::FeatureName feature = ToAPIFeature(static_cast<Feature>(i));
+    for (Feature f : IterateBitSet(featuresBitSet)) {
+        wgpu::FeatureName feature = ToAPI(f);
         if (features != nullptr) {
             *features = feature;
             features += 1;
@@ -486,59 +265,14 @@
     std::vector<const char*> enabledFeatureNames(featuresBitSet.count());
 
     uint32_t index = 0;
-    for (uint32_t i : IterateBitSet(featuresBitSet)) {
-        Feature feature = static_cast<Feature>(i);
+    for (Feature feature : IterateBitSet(featuresBitSet)) {
         ASSERT(feature != Feature::InvalidEnum);
-
-        const FeatureEnumAndInfo& featureNameAndInfo = kFeatureNameAndInfoList[i];
-        ASSERT(featureNameAndInfo.feature == feature);
-
-        enabledFeatureNames[index] = featureNameAndInfo.info.name;
+        enabledFeatureNames[index] = kFeatureNameAndInfoList[feature].name;
         ++index;
     }
     return enabledFeatureNames;
 }
 
-wgpu::FeatureName FeatureEnumToAPIFeature(Feature feature) {
-    ASSERT(feature != Feature::InvalidEnum);
-    return ToAPIFeature(feature);
-}
-
-FeaturesInfo::FeaturesInfo() {
-    for (size_t index = 0; index < kFeatureNameAndInfoList.size(); ++index) {
-        const FeatureEnumAndInfo& featureNameAndInfo = kFeatureNameAndInfoList[index];
-        ASSERT(index == static_cast<size_t>(featureNameAndInfo.feature));
-        mFeatureNameToEnumMap[featureNameAndInfo.info.name] = featureNameAndInfo.feature;
-    }
-}
-
-FeaturesInfo::~FeaturesInfo() = default;
-
-const FeatureInfo* FeaturesInfo::GetFeatureInfo(wgpu::FeatureName feature) const {
-    Feature f = FromAPIFeature(feature);
-    if (f == Feature::InvalidEnum) {
-        return nullptr;
-    }
-    return &kFeatureNameAndInfoList[static_cast<size_t>(f)].info;
-}
-
-Feature FeaturesInfo::FeatureNameToEnum(const char* featureName) const {
-    ASSERT(featureName);
-
-    const auto& iter = mFeatureNameToEnumMap.find(featureName);
-    if (iter != mFeatureNameToEnumMap.cend()) {
-        return kFeatureNameAndInfoList[static_cast<size_t>(iter->second)].feature;
-    }
-    return Feature::InvalidEnum;
-}
-
-wgpu::FeatureName FeaturesInfo::FeatureNameToAPIEnum(const char* featureName) const {
-    Feature f = FeatureNameToEnum(featureName);
-    if (f != Feature::InvalidEnum) {
-        return ToAPIFeature(f);
-    }
-    // Pass something invalid.
-    return static_cast<wgpu::FeatureName>(-1);
-}
-
 }  // namespace dawn::native
+
+#include "dawn/native/Features_autogen.inl"
diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h
index 9014738..6d1ab59 100644
--- a/src/dawn/native/Features.h
+++ b/src/dawn/native/Features.h
@@ -22,69 +22,20 @@
 
 #include "dawn/common/ityp_bitset.h"
 #include "dawn/native/DawnNative.h"
+#include "dawn/native/Features_autogen.h"
 #include "dawn/webgpu_cpp.h"
 
 namespace dawn::native {
 
-enum class Feature {
-    TextureCompressionBC,
-    TextureCompressionETC2,
-    TextureCompressionASTC,
-    PipelineStatisticsQuery,
-    TimestampQuery,
-    TimestampQueryInsidePasses,
-    DepthClipControl,
-    Depth32FloatStencil8,
-    ChromiumExperimentalDp4a,
-    IndirectFirstInstance,
-    ShaderF16,
-    RG11B10UfloatRenderable,
-    BGRA8UnormStorage,
-    Float32Filterable,
-    ChromiumExperimentalSubgroups,
-    ChromiumExperimentalSubgroupUniformControlFlow,
-    ChromiumExperimentalReadWriteStorageTexture,
+extern const ityp::array<Feature, FeatureInfo, kEnumCount<Feature>> kFeatureNameAndInfoList;
 
-    // Dawn-specific
-    DawnInternalUsages,
-    MultiPlanarFormats,
-    DawnNative,
-    ImplicitDeviceSynchronization,
-    SurfaceCapabilities,
-    TransientAttachments,
-    MSAARenderToSingleSampled,
-    DualSourceBlending,
-    D3D11MultithreadProtected,
-    ANGLETextureSharing,
-    PixelLocalStorageCoherent,
-    PixelLocalStorageNonCoherent,
-    Norm16TextureFormats,
-
-    SharedTextureMemoryVkDedicatedAllocation,
-    SharedTextureMemoryAHardwareBuffer,
-    SharedTextureMemoryDmaBuf,
-    SharedTextureMemoryOpaqueFD,
-    SharedTextureMemoryZirconHandle,
-    SharedTextureMemoryDXGISharedHandle,
-    SharedTextureMemoryD3D11Texture2D,
-    SharedTextureMemoryIOSurface,
-    SharedTextureMemoryEGLImage,
-
-    SharedFenceVkSemaphoreOpaqueFD,
-    SharedFenceVkSemaphoreSyncFD,
-    SharedFenceVkSemaphoreZirconHandle,
-    SharedFenceDXGISharedHandle,
-    SharedFenceMTLSharedEvent,
-
-    EnumCount,
-    InvalidEnum = EnumCount,
-    FeatureMin = TextureCompressionBC,
-};
+wgpu::FeatureName ToAPI(Feature feature);
+Feature FromAPI(wgpu::FeatureName feature);
 
 // A wrapper of the bitset to store if an feature is enabled or not. This wrapper provides the
 // convenience to convert the enums of enum class Feature to the indices of a bitset.
 struct FeaturesSet {
-    std::bitset<static_cast<size_t>(Feature::EnumCount)> featuresBitSet;
+    ityp::bitset<Feature, kEnumCount<Feature>> featuresBitSet;
 
     void EnableFeature(Feature feature);
     void EnableFeature(wgpu::FeatureName feature);
@@ -96,23 +47,6 @@
     std::vector<const char*> GetEnabledFeatureNames() const;
 };
 
-wgpu::FeatureName FeatureEnumToAPIFeature(Feature feature);
-
-class FeaturesInfo {
-  public:
-    FeaturesInfo();
-    ~FeaturesInfo();
-
-    // Used to query the details of an feature. Return nullptr if featureName is not a valid
-    // name of an feature supported in Dawn
-    const FeatureInfo* GetFeatureInfo(wgpu::FeatureName feature) const;
-    Feature FeatureNameToEnum(const char* featureName) const;
-    wgpu::FeatureName FeatureNameToAPIEnum(const char* featureName) const;
-
-  private:
-    std::unordered_map<std::string, Feature> mFeatureNameToEnumMap;
-};
-
 }  // namespace dawn::native
 
 #endif  // SRC_DAWN_NATIVE_FEATURES_H_
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index 78b610e..9489d24 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -541,7 +541,7 @@
     AddCompressedFormat(wgpu::TextureFormat::ASTC12x12UnormSrgb, ByteSize(16), Width(12), Height(12), astcFormatUnsupportedReason, ComponentCount(4), wgpu::TextureFormat::ASTC12x12Unorm);
 
     // multi-planar formats
-    const UnsupportedReason multiPlanarFormatUnsupportedReason = device->HasFeature(Feature::MultiPlanarFormats) ?  Format::supported : RequiresFeature{wgpu::FeatureName::DawnMultiPlanarFormats};
+    const UnsupportedReason multiPlanarFormatUnsupportedReason = device->HasFeature(Feature::DawnMultiPlanarFormats) ?  Format::supported : RequiresFeature{wgpu::FeatureName::DawnMultiPlanarFormats};
     AddMultiAspectFormat(wgpu::TextureFormat::R8BG8Biplanar420Unorm, Aspect::Plane0 | Aspect::Plane1,
         wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, Cap::None, multiPlanarFormatUnsupportedReason, ComponentCount(3));
 
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 97aef81..fbd8ac2 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -328,7 +328,11 @@
 }
 
 const FeatureInfo* InstanceBase::GetFeatureInfo(wgpu::FeatureName feature) {
-    return mFeaturesInfo.GetFeatureInfo(feature);
+    Feature f = FromAPI(feature);
+    if (f == Feature::InvalidEnum) {
+        return nullptr;
+    }
+    return &kFeatureNameAndInfoList[f];
 }
 
 std::vector<Ref<AdapterBase>> InstanceBase::EnumerateAdapters(
diff --git a/src/dawn/native/Instance.h b/src/dawn/native/Instance.h
index 04f1184..5fca86f 100644
--- a/src/dawn/native/Instance.h
+++ b/src/dawn/native/Instance.h
@@ -198,8 +198,6 @@
     bool mDeprecatedDiscoveredDefaultPhysicalDevices = false;
 
     TogglesState mToggles;
-
-    FeaturesInfo mFeaturesInfo;
     TogglesInfo mTogglesInfo;
 
 #if defined(DAWN_USE_X11)
diff --git a/src/dawn/native/PhysicalDevice.cpp b/src/dawn/native/PhysicalDevice.cpp
index ab27cf2..2a95368 100644
--- a/src/dawn/native/PhysicalDevice.cpp
+++ b/src/dawn/native/PhysicalDevice.cpp
@@ -118,10 +118,8 @@
     FeaturesSet supportedFeaturesWithToggles;
     // Iterate each PhysicalDevice's supported feature and check if it is supported with given
     // toggles
-    for (uint32_t i : IterateBitSet(mSupportedFeatures.featuresBitSet)) {
-        Feature feature = static_cast<Feature>(i);
-        wgpu::FeatureName featureName = FeatureEnumToAPIFeature(feature);
-        if (IsFeatureSupportedWithToggles(featureName, toggles)) {
+    for (Feature feature : IterateBitSet(mSupportedFeatures.featuresBitSet)) {
+        if (IsFeatureSupportedWithToggles(ToAPI(feature), toggles)) {
             supportedFeaturesWithToggles.EnableFeature(feature);
         }
     }
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index c71430b..3915128 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -340,7 +340,7 @@
         DAWN_INVALID_IF(
             !device->HasFeature(Feature::TransientAttachments),
             "The texture usage (%s) includes %s, which requires the %s feature to be set", usage,
-            kTransientAttachment, FeatureEnumToAPIFeature(Feature::TransientAttachments));
+            kTransientAttachment, ToAPI(Feature::TransientAttachments));
 
         const auto kAllowedTransientUsage =
             kTransientAttachment | wgpu::TextureUsage::RenderAttachment;
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index b1d9437..56158a8 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -144,7 +144,7 @@
 
     // To import multi planar textures, we need to at least tier 2 support.
     if (mDeviceInfo.supportsSharedResourceCapabilityTier2) {
-        EnableFeature(Feature::MultiPlanarFormats);
+        EnableFeature(Feature::DawnMultiPlanarFormats);
     }
 }
 
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index dc916ba..08e3af9 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -118,7 +118,7 @@
 
 void PhysicalDevice::InitializeSupportedFeaturesImpl() {
     EnableFeature(Feature::TextureCompressionBC);
-    EnableFeature(Feature::MultiPlanarFormats);
+    EnableFeature(Feature::DawnMultiPlanarFormats);
     EnableFeature(Feature::Depth32FloatStencil8);
     EnableFeature(Feature::IndirectFirstInstance);
     EnableFeature(Feature::RG11B10UfloatRenderable);
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 8adf17d..d86fad4 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -514,7 +514,7 @@
         // Uses newTextureWithDescriptor::iosurface::plane which is available
         // on ios 11.0+ and macOS 11.0+
         if (@available(macOS 10.11, iOS 11.0, *)) {
-            EnableFeature(Feature::MultiPlanarFormats);
+            EnableFeature(Feature::DawnMultiPlanarFormats);
         }
 
         if (@available(macOS 11.0, iOS 10.0, *)) {
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index f248f75..edc6f6c 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -57,7 +57,7 @@
 
 void PhysicalDevice::InitializeSupportedFeaturesImpl() {
     // Enable all features by default for the convenience of tests.
-    for (uint32_t i = 0; i < static_cast<uint32_t>(Feature::EnumCount); i++) {
+    for (uint32_t i = 0; i < kEnumCount<Feature>; i++) {
         EnableFeature(static_cast<Feature>(i));
     }
 }
diff --git a/src/dawn/native/stream/Stream.h b/src/dawn/native/stream/Stream.h
index 6b93421..8873f05 100644
--- a/src/dawn/native/stream/Stream.h
+++ b/src/dawn/native/stream/Stream.h
@@ -31,6 +31,11 @@
 #include "dawn/native/stream/Sink.h"
 #include "dawn/native/stream/Source.h"
 
+namespace dawn::ityp {
+template <typename Index, size_t N>
+class bitset;
+}  // namespace dawn::ityp
+
 namespace dawn::native::stream {
 
 // Base Stream template for specialization. Specializations may define static methods:
@@ -146,6 +151,13 @@
     }
 };
 
+template <typename Index, size_t N>
+class Stream<ityp::bitset<Index, N>> {
+  public:
+    static void Write(Sink* s, const ityp::bitset<Index, N>& v) { StreamIn(s, v.AsBase()); }
+    static MaybeError Read(Source* s, ityp::bitset<Index, N>* v) { StreamOut(s, &v->AsBase()); }
+};
+
 // Stream specialization for enums.
 template <typename T>
 class Stream<T, std::enable_if_t<std::is_enum_v<T>>> {
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 651636e..fae26ae 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -316,7 +316,7 @@
     }
 
     if (allMultiplanarFormatsSupported) {
-        EnableFeature(Feature::MultiPlanarFormats);
+        EnableFeature(Feature::DawnMultiPlanarFormats);
     }
 
     EnableFeature(Feature::SurfaceCapabilities);
diff --git a/src/dawn/tests/unittests/FeatureTests.cpp b/src/dawn/tests/unittests/FeatureTests.cpp
index 211daed..99c9e97 100644
--- a/src/dawn/tests/unittests/FeatureTests.cpp
+++ b/src/dawn/tests/unittests/FeatureTests.cpp
@@ -43,12 +43,13 @@
     std::vector<wgpu::FeatureName> GetAllFeatureNames() {
         std::vector<wgpu::FeatureName> allFeatureNames(kTotalFeaturesCount);
         for (size_t i = 0; i < kTotalFeaturesCount; ++i) {
-            allFeatureNames[i] = FeatureEnumToAPIFeature(static_cast<native::Feature>(i));
+            allFeatureNames[i] = native::ToAPI(static_cast<native::Feature>(i));
         }
         return allFeatureNames;
     }
 
-    static constexpr size_t kTotalFeaturesCount = static_cast<size_t>(native::Feature::EnumCount);
+    static constexpr size_t kTotalFeaturesCount =
+        static_cast<size_t>(native::kEnumCount<native::Feature>);
 
   protected:
     // By default DisallowUnsafeAPIs is enabled in this instance.
@@ -77,7 +78,7 @@
             native::Adapter adapterWithoutFeature(&mAdapterBase);
 
             wgpu::DeviceDescriptor deviceDescriptor;
-            wgpu::FeatureName featureName = FeatureEnumToAPIFeature(notSupportedFeature);
+            wgpu::FeatureName featureName = native::ToAPI(notSupportedFeature);
             deviceDescriptor.requiredFeatures = &featureName;
             deviceDescriptor.requiredFeatureCount = 1;
 
@@ -92,7 +93,7 @@
             native::Adapter adapterWithoutFeature(&mUnsafeAdapterBase);
 
             wgpu::DeviceDescriptor deviceDescriptor;
-            wgpu::FeatureName featureName = FeatureEnumToAPIFeature(notSupportedFeature);
+            wgpu::FeatureName featureName = ToAPI(notSupportedFeature);
             deviceDescriptor.requiredFeatures = &featureName;
             deviceDescriptor.requiredFeatureCount = 1;
 
@@ -109,11 +110,10 @@
 TEST_F(FeatureTests, RequireAndGetEnabledFeatures) {
     native::Adapter adapter(&mAdapterBase);
     native::Adapter unsafeAdapterAllow(&mUnsafeAdapterBase);
-    native::FeaturesInfo featuresInfo;
 
     for (size_t i = 0; i < kTotalFeaturesCount; ++i) {
         native::Feature feature = static_cast<native::Feature>(i);
-        wgpu::FeatureName featureName = FeatureEnumToAPIFeature(feature);
+        wgpu::FeatureName featureName = ToAPI(feature);
 
         wgpu::DeviceDescriptor deviceDescriptor;
         deviceDescriptor.requiredFeatures = &featureName;
@@ -126,7 +126,7 @@
 
             // Creating a device with experimental feature requires the adapter enables
             // AllowUnsafeAPIs or disables DisallowUnsafeApis, otherwise expect validation error.
-            if (featuresInfo.GetFeatureInfo(featureName)->featureState ==
+            if (native::kFeatureNameAndInfoList[feature].featureState ==
                 native::FeatureInfo::FeatureState::Experimental) {
                 ASSERT_EQ(nullptr, deviceBase) << i;
             } else {
diff --git a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
index 1c12c9b..225a555 100644
--- a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
+++ b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
@@ -38,11 +38,11 @@
 class DeviceCreationTest : public testing::Test {
   protected:
     void SetUp() override {
-        dawnProcSetProcs(&dawn::native::GetProcs());
+        dawnProcSetProcs(&GetProcs());
 
         // Create an instance with default toggles and create an adapter from it.
         WGPUInstanceDescriptor safeInstanceDesc = {};
-        instance = std::make_unique<dawn::native::Instance>(&safeInstanceDesc);
+        instance = std::make_unique<Instance>(&safeInstanceDesc);
 
         wgpu::RequestAdapterOptions options = {};
         options.backendType = wgpu::BackendType::Null;
@@ -60,7 +60,7 @@
         WGPUInstanceDescriptor unsafeInstanceDesc = {};
         unsafeInstanceDesc.nextInChain = &unsafeInstanceTogglesDesc.chain;
 
-        unsafeInstance = std::make_unique<dawn::native::Instance>(&unsafeInstanceDesc);
+        unsafeInstance = std::make_unique<Instance>(&unsafeInstanceDesc);
         unsafeAdapter = unsafeInstance->EnumerateAdapters(&options)[0];
 
         ASSERT_NE(adapter.Get(), nullptr);
@@ -75,14 +75,12 @@
         dawnProcSetProcs(nullptr);
     }
 
-    static constexpr size_t kTotalFeaturesCount =
-        static_cast<size_t>(dawn::native::Feature::EnumCount);
+    static constexpr size_t kTotalFeaturesCount = static_cast<size_t>(kEnumCount<Feature>);
 
-    std::unique_ptr<dawn::native::Instance> instance;
-    std::unique_ptr<dawn::native::Instance> unsafeInstance;
-    dawn::native::Adapter adapter;
-    dawn::native::Adapter unsafeAdapter;
-    dawn::native::FeaturesInfo featuresInfo;
+    std::unique_ptr<Instance> instance;
+    std::unique_ptr<Instance> unsafeInstance;
+    Adapter adapter;
+    Adapter unsafeAdapter;
 };
 
 // Test successful call to CreateDevice with no descriptor
@@ -111,7 +109,7 @@
     wgpu::Device device = adapter.CreateDevice(&desc);
     EXPECT_NE(device, nullptr);
 
-    auto toggles = dawn::native::GetTogglesUsed(device.Get());
+    auto toggles = GetTogglesUsed(device.Get());
     EXPECT_THAT(toggles, Contains(StrEq(toggle)));
 }
 
@@ -119,21 +117,16 @@
 // instance but can be overriden by device toggles.
 TEST_F(DeviceCreationTest, CreateDeviceRequiringExperimentalFeatures) {
     // Ensure that unsafe apis are disallowed on safe adapter.
-    ASSERT_FALSE(dawn::native::FromAPI(adapter.Get())
-                     ->GetTogglesState()
-                     .IsEnabled(dawn::native::Toggle::AllowUnsafeAPIs));
+    ASSERT_FALSE(FromAPI(adapter.Get())->GetTogglesState().IsEnabled(Toggle::AllowUnsafeAPIs));
     // Ensure that unsafe apis are allowed unsafe adapter(s).
-    ASSERT_TRUE(dawn::native::FromAPI(unsafeAdapter.Get())
-                    ->GetTogglesState()
-                    .IsEnabled(dawn::native::Toggle::AllowUnsafeAPIs));
+    ASSERT_TRUE(FromAPI(unsafeAdapter.Get())->GetTogglesState().IsEnabled(Toggle::AllowUnsafeAPIs));
 
     for (size_t i = 0; i < kTotalFeaturesCount; i++) {
-        dawn::native::Feature feature = static_cast<dawn::native::Feature>(i);
-        wgpu::FeatureName featureName = dawn::native::FeatureEnumToAPIFeature(feature);
+        Feature feature = static_cast<Feature>(i);
+        wgpu::FeatureName featureName = ToAPI(feature);
 
         // Only test experimental features.
-        if (featuresInfo.GetFeatureInfo(featureName)->featureState ==
-            dawn::native::FeatureInfo::FeatureState::Stable) {
+        if (kFeatureNameAndInfoList[feature].featureState == FeatureInfo::FeatureState::Stable) {
             continue;
         }
 
@@ -228,8 +221,7 @@
         desc.nextInChain = &cacheDesc;
         wgpu::Device device2 = adapter.CreateDevice(&desc);
 
-        EXPECT_EQ(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
-                  dawn::native::FromAPI(device2.Get())->GetCacheKey());
+        EXPECT_EQ(FromAPI(device1.Get())->GetCacheKey(), FromAPI(device2.Get())->GetCacheKey());
     }
     // Default device descriptor should not have the same cache key as a device descriptor with
     // a non-default cache descriptor.
@@ -245,8 +237,7 @@
         wgpu::Device device2 = adapter.CreateDevice(&desc);
         EXPECT_NE(device2, nullptr);
 
-        EXPECT_NE(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
-                  dawn::native::FromAPI(device2.Get())->GetCacheKey());
+        EXPECT_NE(FromAPI(device1.Get())->GetCacheKey(), FromAPI(device2.Get())->GetCacheKey());
     }
     // Two non-default cache descriptors should not have the same cache key.
     {
@@ -264,8 +255,7 @@
         wgpu::Device device2 = adapter.CreateDevice(&desc);
         EXPECT_NE(device2, nullptr);
 
-        EXPECT_NE(dawn::native::FromAPI(device1.Get())->GetCacheKey(),
-                  dawn::native::FromAPI(device2.Get())->GetCacheKey());
+        EXPECT_NE(FromAPI(device1.Get())->GetCacheKey(), FromAPI(device2.Get())->GetCacheKey());
     }
 }