webgpu.h introduce a base struct for extension structures.

 struct WGPUChainedStruct {
     WGPUChainedStruct const * nextInChain;
     WGPUSType sType;
 };

And changes all the nextInChain to point to such structures. This adds
more type safety to extension structs and requires less casting to check
sTypes and friends.

Bug: dawn:269

Change-Id: I443f363cdb55dbec7c7f6e897245d4a7ea0ebe70
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15080
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/dawn.json b/dawn.json
index 15e17b6..8f1b0b7 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1298,6 +1298,12 @@
             {"name": "implementation", "type": "uint64_t"}
         ]
     },
+    "s type": {
+        "category": "enum",
+        "values": [
+            {"value": 0, "name": "invalid"}
+        ]
+    },
     "texture": {
         "category": "object",
         "methods": [
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 3a67951..83cdd2f 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -140,7 +140,11 @@
     def __init__(self, name, json_data):
         Record.__init__(self, name)
         Type.__init__(self, name, json_data)
+        self.chained = json_data.get("chained", False)
         self.extensible = json_data.get("extensible", False)
+        # Chained structs inherit from wgpu::ChainedStruct which has nextInChain so setting
+        # both extensible and chained would result in two nextInChain members.
+        assert(not (self.extensible and self.chained))
 
 class Command(Record):
     def __init__(self, name, members=None):
diff --git a/generator/templates/dawn_native/wgpu_structs.cpp b/generator/templates/dawn_native/wgpu_structs.cpp
index b327f70..bec3ebf 100644
--- a/generator/templates/dawn_native/wgpu_structs.cpp
+++ b/generator/templates/dawn_native/wgpu_structs.cpp
@@ -16,6 +16,15 @@
 
 namespace dawn_native {
 
+    static_assert(sizeof(ChainedStruct) == sizeof(WGPUChainedStruct),
+            "sizeof mismatch for ChainedStruct");
+    static_assert(alignof(ChainedStruct) == alignof(WGPUChainedStruct),
+            "alignof mismatch for ChainedStruct");
+    static_assert(offsetof(ChainedStruct, nextInChain) == offsetof(WGPUChainedStruct, nextInChain),
+            "offsetof mismatch for ChainedStruct::nextInChain");
+    static_assert(offsetof(ChainedStruct, sType) == offsetof(WGPUChainedStruct, sType),
+            "offsetof mismatch for ChainedStruct::sType");
+
     {% for type in by_category["structure"] %}
         {% set CppType = as_cppType(type.name) %}
         {% set CType = as_cType(type.name) %}
diff --git a/generator/templates/dawn_native/wgpu_structs.h b/generator/templates/dawn_native/wgpu_structs.h
index 1241a00..5614336 100644
--- a/generator/templates/dawn_native/wgpu_structs.h
+++ b/generator/templates/dawn_native/wgpu_structs.h
@@ -32,13 +32,25 @@
     {%- endif -%}
 {%- endmacro %}
 
+    struct ChainedStruct {
+        ChainedStruct const * nextInChain = nullptr;
+        wgpu::SType sType = wgpu::SType::Invalid;
+    };
+
     {% for type in by_category["structure"] %}
-        struct {{as_cppType(type.name)}} {
+        {% if type.chained %}
+            struct {{as_cppType(type.name)}} : ChainedStruct {
+                {{as_cppType(type.name)}}() {
+                    sType = wgpu::SType::{{type.name.CamelCase()}};
+                }
+        {% else %}
+            struct {{as_cppType(type.name)}} {
+        {% endif %}
             {% if type.extensible %}
-                const void* nextInChain = nullptr;
+                ChainedStruct const * nextInChain = nullptr;
             {% endif %}
             {% for member in type.members %}
-            {{as_annotated_frontendType(member)}} {{render_cpp_default_value(member)}};
+                {{as_annotated_frontendType(member)}} {{render_cpp_default_value(member)}};
             {% endfor %}
         };
 
diff --git a/generator/templates/webgpu.h b/generator/templates/webgpu.h
index d1fcb32..aec3a18 100644
--- a/generator/templates/webgpu.h
+++ b/generator/templates/webgpu.h
@@ -73,10 +73,19 @@
 
 {% endfor %}
 
+typedef struct WGPUChainedStruct {
+    struct WGPUChainedStruct const * nextInChain;
+    WGPUSType sType;
+} WGPUChainedStruct;
+
 {% for type in by_category["structure"] %}
     typedef struct {{as_cType(type.name)}} {
         {% if type.extensible %}
-            void const * nextInChain;
+            WGPUChainedStruct const * nextInChain;
+        {% endif %}
+        {% if type.chained %}
+            WGPUChainedStruct const * nextInChain;
+            WGPUSType sType;
         {% endif %}
         {% for member in type.members %}
             {{as_annotated_cType(member)}};
diff --git a/generator/templates/webgpu_cpp.cpp b/generator/templates/webgpu_cpp.cpp
index 0bfcbd4..fcaac3e 100644
--- a/generator/templates/webgpu_cpp.cpp
+++ b/generator/templates/webgpu_cpp.cpp
@@ -42,6 +42,15 @@
 
     {% endfor %}
 
+    static_assert(sizeof(ChainedStruct) == sizeof(WGPUChainedStruct),
+            "sizeof mismatch for ChainedStruct");
+    static_assert(alignof(ChainedStruct) == alignof(WGPUChainedStruct),
+            "alignof mismatch for ChainedStruct");
+    static_assert(offsetof(ChainedStruct, nextInChain) == offsetof(WGPUChainedStruct, nextInChain),
+            "offsetof mismatch for ChainedStruct::nextInChain");
+    static_assert(offsetof(ChainedStruct, sType) == offsetof(WGPUChainedStruct, sType),
+            "offsetof mismatch for ChainedStruct::sType");
+
     {% for type in by_category["structure"] %}
         {% set CppType = as_cppType(type.name) %}
         {% set CType = as_cType(type.name) %}
diff --git a/generator/templates/webgpu_cpp.h b/generator/templates/webgpu_cpp.h
index 14b5a2e..6bfcdb7 100644
--- a/generator/templates/webgpu_cpp.h
+++ b/generator/templates/webgpu_cpp.h
@@ -186,10 +186,22 @@
     Instance CreateInstance(InstanceDescriptor const * descriptor = nullptr);
     Proc GetProcAddress(Device const& device, const char* procName);
 
+    struct ChainedStruct {
+        ChainedStruct const * nextInChain = nullptr;
+        SType sType = SType::Invalid;
+    };
+
     {% for type in by_category["structure"] %}
-        struct {{as_cppType(type.name)}} {
+        {% if type.chained %}
+            struct {{as_cppType(type.name)}} : ChainedStruct {
+                {{as_cppType(type.name)}}() {
+                    sType = SType::{{type.name.CamelCase()}};
+                }
+        {% else %}
+            struct {{as_cppType(type.name)}} {
+        {% endif %}
             {% if type.extensible %}
-                const void* nextInChain = nullptr;
+                ChainedStruct const * nextInChain = nullptr;
             {% endif %}
             {% for member in type.members %}
                 {{as_annotated_cppType(member)}}{{render_cpp_default_value(member)}};
diff --git a/src/tests/end2end/D3D12ResourceWrappingTests.cpp b/src/tests/end2end/D3D12ResourceWrappingTests.cpp
index 1896ea9..1c6a2b5 100644
--- a/src/tests/end2end/D3D12ResourceWrappingTests.cpp
+++ b/src/tests/end2end/D3D12ResourceWrappingTests.cpp
@@ -144,7 +144,9 @@
 // Test an error occurs if the texture descriptor is invalid
 TEST_P(D3D12SharedHandleValidation, InvalidTextureDescriptor) {
     DAWN_SKIP_TEST_IF(UsesWire());
-    dawnDescriptor.nextInChain = this;
+
+    wgpu::ChainedStruct chainedDescriptor;
+    dawnDescriptor.nextInChain = &chainedDescriptor;
 
     wgpu::Texture texture;
     ComPtr<ID3D11Texture2D> d3d11Texture;
diff --git a/src/tests/end2end/IOSurfaceWrappingTests.cpp b/src/tests/end2end/IOSurfaceWrappingTests.cpp
index f2fe59a..908b108 100644
--- a/src/tests/end2end/IOSurfaceWrappingTests.cpp
+++ b/src/tests/end2end/IOSurfaceWrappingTests.cpp
@@ -136,7 +136,9 @@
 // Test an error occurs if the texture descriptor is invalid
 TEST_P(IOSurfaceValidationTests, InvalidTextureDescriptor) {
     DAWN_SKIP_TEST_IF(UsesWire());
-    descriptor.nextInChain = this;
+
+    wgpu::ChainedStruct chainedDescriptor;
+    descriptor.nextInChain = &chainedDescriptor;
 
     ASSERT_DEVICE_ERROR(wgpu::Texture texture =
                             WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
diff --git a/src/tests/unittests/validation/BindGroupValidationTests.cpp b/src/tests/unittests/validation/BindGroupValidationTests.cpp
index 1916339..0b11b0b 100644
--- a/src/tests/unittests/validation/BindGroupValidationTests.cpp
+++ b/src/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -75,7 +75,8 @@
     device.CreateBindGroup(&descriptor);
 
     // Check that nextInChain != nullptr is an error.
-    descriptor.nextInChain = static_cast<void*>(&descriptor);
+    wgpu::ChainedStruct chainedDescriptor;
+    descriptor.nextInChain = &chainedDescriptor;
     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
 }
 
diff --git a/src/tests/white_box/VulkanImageWrappingTests.cpp b/src/tests/white_box/VulkanImageWrappingTests.cpp
index 541554f..de7d8fe 100644
--- a/src/tests/white_box/VulkanImageWrappingTests.cpp
+++ b/src/tests/white_box/VulkanImageWrappingTests.cpp
@@ -256,7 +256,8 @@
 // Test an error occurs if the texture descriptor is invalid
 TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
     DAWN_SKIP_TEST_IF(UsesWire());
-    defaultDescriptor.nextInChain = this;
+    wgpu::ChainedStruct chainedDescriptor;
+    defaultDescriptor.nextInChain = &chainedDescriptor;
 
     ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage(
                             device, &defaultDescriptor, defaultFd, defaultAllocationSize,