Updates DawnInstanceDescriptor to pass in the Platform.

Notes:
- Separates ChainedStruct to be reusable without cpp header. (Also
  updates native structs to directly use it.)
- Manually implements the descriptor in DawnNative.
- Reworks ChainUtils with mapping from struct to STypes.
- Updates the tests to use either SetPlatformForTesting which is still
  required because DawnTest uses a "global" instance for all tests and
  some tests require setting (and cleaning up) a test specific platform.

Bug: dawn:1374
Change-Id: I078c78f22c5137030cf3cf0e8358fe4373ee9c6c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/132268
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 16207fc..d0d502a 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -965,6 +965,11 @@
                            'include/dawn/' + api + '_cpp_print.h',
                            [RENDER_PARAMS_BASE, params_dawn]))
 
+            renders.append(
+                FileRender('api_cpp_chained_struct.h',
+                           'include/dawn/' + api + '_cpp_chained_struct.h',
+                           [RENDER_PARAMS_BASE, params_dawn]))
+
         if 'proc' in targets:
             renders.append(
                 FileRender('dawn_proc.c', 'src/dawn/' + prefix + '_proc.c',
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index 54bf66d..4d2a072 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -13,7 +13,7 @@
 //* limitations under the License.
 {% set API = metadata.api.upper() %}
 {% set api = API.lower() %}
-{% if 'dawn' not in enabled_tags %}
+{% if 'dawn' in enabled_tags %}
     #ifdef __EMSCRIPTEN__
     #error "Do not include this header. Emscripten already provides headers needed for {{metadata.api}}."
     #endif
@@ -22,17 +22,12 @@
 #define {{API}}_CPP_H_
 
 #include "dawn/{{api}}.h"
+#include "dawn/{{api}}_cpp_chained_struct.h"
 #include "dawn/EnumClassBitmasks.h"
 #include <cmath>
 
 namespace {{metadata.namespace}} {
 
-    namespace detail {
-        constexpr size_t ConstexprMax(size_t a, size_t b) {
-            return a > b ? a : b;
-        }
-    }  // namespace detail
-
     {% set c_prefix = metadata.c_prefix %}
     {% for constant in by_category["constant"] %}
         {% set type = as_cppType(constant.type.name) %}
@@ -218,16 +213,6 @@
         );
     {% endfor %}
 
-    struct ChainedStruct {
-        ChainedStruct const * nextInChain = nullptr;
-        SType sType = SType::Invalid;
-    };
-
-    struct ChainedStructOut {
-        ChainedStruct * nextInChain = nullptr;
-        SType sType = SType::Invalid;
-    };
-
     {% for type in by_category["structure"] %}
         {% set Out = "Out" if type.output else "" %}
         {% set const = "const" if not type.output else "" %}
diff --git a/generator/templates/api_cpp_chained_struct.h b/generator/templates/api_cpp_chained_struct.h
new file mode 100644
index 0000000..59a0fd0
--- /dev/null
+++ b/generator/templates/api_cpp_chained_struct.h
@@ -0,0 +1,48 @@
+//* 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 API = metadata.api.upper() %}
+{% if 'dawn' in enabled_tags %}
+    #ifdef __EMSCRIPTEN__
+    #error "Do not include this header. Emscripten already provides headers needed for {{metadata.api}}."
+    #endif
+{% endif %}
+#ifndef {{API}}_CPP_CHAINED_STRUCT_H_
+#define {{API}}_CPP_CHAINED_STRUCT_H_
+
+// This header file declares the ChainedStruct structures separately from the {{metadata.api}}
+// headers so that dependencies can directly extend structures without including the larger header
+// which exposes capabilities that may require correctly set proc tables.
+namespace {{metadata.namespace}} {
+
+    namespace detail {
+        constexpr size_t ConstexprMax(size_t a, size_t b) {
+            return a > b ? a : b;
+        }
+    }  // namespace detail
+
+    enum class SType : uint32_t;
+
+    struct ChainedStruct {
+        ChainedStruct const * nextInChain = nullptr;
+        SType sType = SType(0u);
+    };
+
+    struct ChainedStructOut {
+        ChainedStructOut * nextInChain = nullptr;
+        SType sType = SType(0u);
+    };
+
+}  // namespace {{metadata.namespace}}}
+
+#endif // {{API}}_CPP_CHAINED_STRUCT_H_
diff --git a/generator/templates/dawn/native/ChainUtils.cpp b/generator/templates/dawn/native/ChainUtils.cpp
index 95f5e43..1600c41 100644
--- a/generator/templates/dawn/native/ChainUtils.cpp
+++ b/generator/templates/dawn/native/ChainUtils.cpp
@@ -23,21 +23,6 @@
 namespace {{native_namespace}} {
 
 {% set namespace = metadata.namespace %}
-{% for value in types["s type"].values %}
-    {% if value.valid %}
-        {% set const_qualifier = "const " if types[value.name.get()].chained == "in" else "" %}
-        {% set chained_struct_type = "ChainedStruct" if types[value.name.get()].chained == "in" else "ChainedStructOut" %}
-        void FindInChain({{const_qualifier}}{{chained_struct_type}}* chain, {{const_qualifier}}{{as_cppEnum(value.name)}}** out) {
-            for (; chain; chain = chain->nextInChain) {
-                if (chain->sType == {{namespace}}::SType::{{as_cppEnum(value.name)}}) {
-                    *out = static_cast<{{const_qualifier}}{{as_cppEnum(value.name)}}*>(chain);
-                    break;
-                }
-            }
-        }
-    {% endif %}
-{% endfor %}
-
 MaybeError ValidateSTypes(const ChainedStruct* chain,
                           std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints) {
     std::unordered_set<{{namespace}}::SType> allSTypes;
diff --git a/generator/templates/dawn/native/ChainUtils.h b/generator/templates/dawn/native/ChainUtils.h
index 3b5a926..1495f7b 100644
--- a/generator/templates/dawn/native/ChainUtils.h
+++ b/generator/templates/dawn/native/ChainUtils.h
@@ -18,6 +18,7 @@
 #define {{DIR}}_CHAIN_UTILS_H_
 
 {% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
+{% set namespace = metadata.namespace %}
 {% set namespace_name = Name(metadata.native_namespace) %}
 {% set native_namespace = namespace_name.namespace_case() %}
 {% set native_dir = impl_dir + namespace_name.Dirs() %}
@@ -26,13 +27,44 @@
 #include "{{native_dir}}/Error.h"
 
 namespace {{native_namespace}} {
+namespace detail {
+    // Mapping from native types to the expected STypes is implemented as template specializations.
+    template <typename T>
+    struct STypeForImpl;
     {% for value in types["s type"].values %}
-        {% if value.valid %}
-            {% set const_qualifier = "const " if types[value.name.get()].chained == "in" else "" %}
-            {% set chained_struct_type = "ChainedStruct" if types[value.name.get()].chained == "in" else "ChainedStructOut" %}
-            void FindInChain({{const_qualifier}}{{chained_struct_type}}* chain, {{const_qualifier}}{{as_cppEnum(value.name)}}** out);
+        {% if value.valid and value.name.get() in types %}
+            template <>
+            struct STypeForImpl<{{as_cppEnum(value.name)}}> {
+                static constexpr {{namespace}}::SType value = {{namespace}}::SType::{{as_cppEnum(value.name)}};
+            };
         {% endif %}
     {% endfor %}
+    template <>
+    struct STypeForImpl<DawnInstanceDescriptor> {
+        static constexpr {{namespace}}::SType value = {{namespace}}::SType::DawnInstanceDescriptor;
+    };
+}  // namespace detail
+
+    template <typename T>
+    constexpr {{namespace}}::SType STypeFor = detail::STypeForImpl<T>::value;
+    template <typename T>
+    void FindInChain(const ChainedStruct* chain, const T** out) {
+        for (; chain; chain = chain->nextInChain) {
+            if (chain->sType == STypeFor<T>) {
+                *out = static_cast<const T*>(chain);
+                break;
+            }
+        }
+    }
+    template <typename T>
+    void FindInChain(ChainedStructOut* chain, T** out) {
+        for (; chain; chain = chain->nextInChain) {
+            if (chain->sType == STypeFor<T>) {
+                *out = static_cast<T*>(chain);
+                break;
+            }
+        }
+    }
 
     // Verifies that |chain| only contains ChainedStructs of types enumerated in
     // |oneOfConstraints| and contains no duplicate sTypes. Each vector in
@@ -40,7 +72,6 @@
     // For example:
     //   ValidateSTypes(chain, { { ShaderModuleSPIRVDescriptor, ShaderModuleWGSLDescriptor } }))
     //   ValidateSTypes(chain, { { Extension1 }, { Extension2 } })
-    {% set namespace = metadata.namespace %}
     MaybeError ValidateSTypes(const ChainedStruct* chain,
                               std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints);
     MaybeError ValidateSTypes(const ChainedStructOut* chain,
diff --git a/generator/templates/dawn/native/api_structs.h b/generator/templates/dawn/native/api_structs.h
index 84a6404..2de5805 100644
--- a/generator/templates/dawn/native/api_structs.h
+++ b/generator/templates/dawn/native/api_structs.h
@@ -44,15 +44,8 @@
     {%- endif -%}
 {%- endmacro %}
 
-    struct ChainedStruct {
-        ChainedStruct const * nextInChain = nullptr;
-        {{namespace}}::SType sType = {{namespace}}::SType::Invalid;
-    };
-
-    struct ChainedStructOut {
-        ChainedStructOut * nextInChain = nullptr;
-        {{namespace}}::SType sType = {{namespace}}::SType::Invalid;
-    };
+    using {{namespace}}::ChainedStruct;
+    using {{namespace}}::ChainedStructOut;
 
     {% for type in by_category["structure"] %}
         {% if type.chained %}