Add AdapterPropertiesD3D

This allows Chrome to query the Adapter's D3D shader model and do
its own blocklisting instead of relying on Dawn.

Bug: dawn:1254
Change-Id: I376021f066d03c26623a3ed86c1b7bf55c7d8cb5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/170563
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/docs/dawn/features/adapter_properties.md b/docs/dawn/features/adapter_properties.md
index c4d348b..147b239 100644
--- a/docs/dawn/features/adapter_properties.md
+++ b/docs/dawn/features/adapter_properties.md
@@ -27,3 +27,17 @@
 ```
 
 `wgpu::MemoryHeapInfo::size` is the size that should be allocated out of this heap. Allocating more than this may result in poor performance or may deterministically run out of memory.
+
+
+## D3D
+
+`wgpu::FeatureName::AdapterPropertiesD3D` allows querying D3D information from the adapter.
+
+`wgpu::AdapterPropertiesD3D` may be chained on `wgpu::AdapterProperties` in a call to `wgpu::Adapter::GetProperties` in order to query D3D information on that adapter.
+
+Adds `wgpu::AdapterPropertiesD3D` which is a struct describing the D3D adapter.
+```
+struct AdapterPropertiesD3D {
+    uint32_t shaderModel;  // The D3D shader model
+};
+```
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index e4f23b1..2106708 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -1973,6 +1973,7 @@
             {"value": 1025, "name": "framebuffer fetch", "tags": ["dawn"]},
             {"value": 1026, "name": "buffer map extended usages", "tags": ["dawn"]},
             {"value": 1027, "name": "adapter properties memory heaps", "tags": ["dawn"]},
+            {"value": 1028, "name": "adapter properties D3D", "tags": ["dawn"]},
 
             {"value": 1100, "name": "shared texture memory vk dedicated allocation", "tags": ["dawn", "native"]},
             {"value": 1101, "name": "shared texture memory a hardware buffer", "tags": ["dawn", "native"]},
@@ -3383,9 +3384,10 @@
             {"value": 1017, "name": "buffer host mapped pointer", "tags": ["dawn"]},
             {"value": 1018, "name": "dawn experimental subgroup limits", "tags": ["dawn"]},
             {"value": 1019, "name": "adapter properties memory heaps", "tags": ["dawn"]},
-            {"value": 1020, "name": "dawn compute pipeline full subgroups", "tags": ["dawn"]},
-            {"value": 1021, "name": "dawn wire WGSL control", "tags": ["dawn"]},
-            {"value": 1022, "name": "dawn WGSL blocklist", "tags": ["dawn", "native"]},
+            {"value": 1020, "name": "adapter properties D3D", "tags": ["dawn"]},
+            {"value": 1021, "name": "dawn compute pipeline full subgroups", "tags": ["dawn"]},
+            {"value": 1022, "name": "dawn wire WGSL control", "tags": ["dawn"]},
+            {"value": 1023, "name": "dawn WGSL blocklist", "tags": ["dawn", "native"]},
 
             {"value": 1100, "name": "shared texture memory vk image descriptor", "tags": ["dawn", "native"]},
             {"value": 1101, "name": "shared texture memory vk dedicated allocation descriptor", "tags": ["dawn", "native"]},
@@ -3909,6 +3911,15 @@
             {"name": "heap info", "type": "memory heap info", "annotation": "const*", "length": "heap count"}
         ]
     },
+    "adapter properties D3D": {
+        "category": "structure",
+        "chained": "out",
+        "chain roots": ["adapter properties"],
+        "tags": ["dawn"],
+        "members": [
+            {"name": "shader model", "type": "uint32_t"}
+        ]
+    },
     "dawn buffer descriptor error info from wire client": {
         "category": "structure",
         "chained": "in",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 6af2145..d37b727 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -114,17 +114,22 @@
         return;
     }
 
-    if (auto* memoryHeaps = unpacked.Get<AdapterPropertiesMemoryHeaps>()) {
-        if (!mSupportedFeatures.IsEnabled(wgpu::FeatureName::AdapterPropertiesMemoryHeaps)) {
-            mPhysicalDevice->GetInstance()->ConsumedError(
-                DAWN_VALIDATION_ERROR("Feature AdapterPropertiesMemoryHeaps is not available."));
-        }
-        mPhysicalDevice->PopulateMemoryHeapInfo(memoryHeaps);
+    if (unpacked.Get<AdapterPropertiesMemoryHeaps>() != nullptr &&
+        !mSupportedFeatures.IsEnabled(wgpu::FeatureName::AdapterPropertiesMemoryHeaps)) {
+        instance->ConsumedError(
+            DAWN_VALIDATION_ERROR("Feature AdapterPropertiesMemoryHeaps is not available."));
+    }
+    if (unpacked.Get<AdapterPropertiesD3D>() != nullptr &&
+        !mSupportedFeatures.IsEnabled(wgpu::FeatureName::AdapterPropertiesD3D)) {
+        instance->ConsumedError(
+            DAWN_VALIDATION_ERROR("Feature AdapterPropertiesD3D is not available."));
     }
     if (auto* powerPreferenceDesc = unpacked.Get<DawnAdapterPropertiesPowerPreference>()) {
         powerPreferenceDesc->powerPreference = mPowerPreference;
     }
 
+    mPhysicalDevice->PopulateBackendProperties(unpacked);
+
     properties->vendorID = mPhysicalDevice->GetVendorId();
     properties->deviceID = mPhysicalDevice->GetDeviceId();
     properties->adapterType = mPhysicalDevice->GetAdapterType();
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index f6f24b7..fb09b14 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -290,6 +290,11 @@
       "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
       "adapter_properties.md",
       FeatureInfo::FeatureState::Stable}},
+    {Feature::AdapterPropertiesD3D,
+     {"Support querying D3D info from the adapter.",
+      "https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/dawn/features/"
+      "adapter_properties.md",
+      FeatureInfo::FeatureState::Stable}},
 };
 
 }  // anonymous namespace
diff --git a/src/dawn/native/PhysicalDevice.h b/src/dawn/native/PhysicalDevice.h
index 2d56f42..d509f4e 100644
--- a/src/dawn/native/PhysicalDevice.h
+++ b/src/dawn/native/PhysicalDevice.h
@@ -107,10 +107,8 @@
     FeatureValidationResult ValidateFeatureSupportedWithToggles(wgpu::FeatureName feature,
                                                                 const TogglesState& toggles) const;
 
-    // Populate information about the memory heaps. Ownership of allocations written to
-    // `memoryHeapProperties` are owned by the caller.
-    virtual void PopulateMemoryHeapInfo(
-        AdapterPropertiesMemoryHeaps* memoryHeapProperties) const = 0;
+    // Populate backend properties. Ownership of allocations written are owned by the caller.
+    virtual void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const = 0;
 
   protected:
     uint32_t mVendorId = 0xFFFFFFFF;
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index 59e887c..d1efb23 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -190,6 +190,7 @@
     EnableFeature(Feature::DualSourceBlending);
     EnableFeature(Feature::Norm16TextureFormats);
     EnableFeature(Feature::AdapterPropertiesMemoryHeaps);
+    EnableFeature(Feature::AdapterPropertiesD3D);
 
     // To import multi planar textures, we need to at least tier 2 support.
     if (mDeviceInfo.supportsSharedResourceCapabilityTier2) {
@@ -329,32 +330,37 @@
     return {};
 }
 
-void PhysicalDevice::PopulateMemoryHeapInfo(
-    AdapterPropertiesMemoryHeaps* memoryHeapProperties) const {
-    // https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html describes
-    // the properties of D3D12 Default/Upload/Readback heaps. The assumption is that these are
-    // roughly how D3D11 allocates memory has well.
-    if (mDeviceInfo.isUMA) {
-        auto* heapInfo = new MemoryHeapInfo[1];
-        memoryHeapProperties->heapCount = 1;
-        memoryHeapProperties->heapInfo = heapInfo;
+void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const {
+    if (auto* memoryHeapProperties = properties.Get<AdapterPropertiesMemoryHeaps>()) {
+        // https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html describes
+        // the properties of D3D12 Default/Upload/Readback heaps. The assumption is that these are
+        // roughly how D3D11 allocates memory has well.
+        if (mDeviceInfo.isUMA) {
+            auto* heapInfo = new MemoryHeapInfo[1];
+            memoryHeapProperties->heapCount = 1;
+            memoryHeapProperties->heapInfo = heapInfo;
 
-        heapInfo[0].size =
-            std::max(mDeviceInfo.dedicatedVideoMemory, mDeviceInfo.sharedSystemMemory);
-        heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
-                                 wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
-    } else {
-        auto* heapInfo = new MemoryHeapInfo[2];
-        memoryHeapProperties->heapCount = 2;
-        memoryHeapProperties->heapInfo = heapInfo;
+            heapInfo[0].size =
+                std::max(mDeviceInfo.dedicatedVideoMemory, mDeviceInfo.sharedSystemMemory);
+            heapInfo[0].properties =
+                wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+                wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
+        } else {
+            auto* heapInfo = new MemoryHeapInfo[2];
+            memoryHeapProperties->heapCount = 2;
+            memoryHeapProperties->heapInfo = heapInfo;
 
-        heapInfo[0].size = mDeviceInfo.dedicatedVideoMemory;
-        heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
+            heapInfo[0].size = mDeviceInfo.dedicatedVideoMemory;
+            heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
 
-        heapInfo[1].size = mDeviceInfo.sharedSystemMemory;
-        heapInfo[1].properties = wgpu::HeapProperty::HostVisible |
-                                 wgpu::HeapProperty::HostCoherent |
-                                 wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
+            heapInfo[1].size = mDeviceInfo.sharedSystemMemory;
+            heapInfo[1].properties =
+                wgpu::HeapProperty::HostVisible | wgpu::HeapProperty::HostCoherent |
+                wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
+        }
+    }
+    if (auto* d3dProperties = properties.Get<AdapterPropertiesD3D>()) {
+        d3dProperties->shaderModel = GetDeviceInfo().shaderModel;
     }
 }
 
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
index 9b2efa8..01e28fc 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
@@ -75,7 +75,7 @@
         wgpu::FeatureName feature,
         const TogglesState& toggles) const override;
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
 
     const bool mIsSharedD3D11Device;
     ComPtr<ID3D11Device> mD3D11Device;
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index 34fa781..204f704 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -144,6 +144,7 @@
     EnableFeature(Feature::DualSourceBlending);
     EnableFeature(Feature::Norm16TextureFormats);
     EnableFeature(Feature::AdapterPropertiesMemoryHeaps);
+    EnableFeature(Feature::AdapterPropertiesD3D);
 
     if (AreTimestampQueriesSupported()) {
         EnableFeature(Feature::TimestampQuery);
@@ -724,39 +725,43 @@
     return {};
 }
 
-void PhysicalDevice::PopulateMemoryHeapInfo(
-    AdapterPropertiesMemoryHeaps* memoryHeapProperties) const {
-    // https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html describes
-    // the properties of D3D12 Default/Upload/Readback heaps.
-    if (mDeviceInfo.isUMA) {
-        auto* heapInfo = new MemoryHeapInfo[1];
-        memoryHeapProperties->heapCount = 1;
-        memoryHeapProperties->heapInfo = heapInfo;
+void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const {
+    if (auto* memoryHeapProperties = properties.Get<AdapterPropertiesMemoryHeaps>()) {
+        // https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html describes
+        // the properties of D3D12 Default/Upload/Readback heaps.
+        if (mDeviceInfo.isUMA) {
+            auto* heapInfo = new MemoryHeapInfo[1];
+            memoryHeapProperties->heapCount = 1;
+            memoryHeapProperties->heapInfo = heapInfo;
 
-        heapInfo[0].size =
-            std::max(mDeviceInfo.dedicatedVideoMemory, mDeviceInfo.sharedSystemMemory);
+            heapInfo[0].size =
+                std::max(mDeviceInfo.dedicatedVideoMemory, mDeviceInfo.sharedSystemMemory);
 
-        if (mDeviceInfo.isCacheCoherentUMA) {
-            heapInfo[0].properties =
-                wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
-                wgpu::HeapProperty::HostCoherent | wgpu::HeapProperty::HostCached;
+            if (mDeviceInfo.isCacheCoherentUMA) {
+                heapInfo[0].properties =
+                    wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+                    wgpu::HeapProperty::HostCoherent | wgpu::HeapProperty::HostCached;
+            } else {
+                heapInfo[0].properties =
+                    wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+                    wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
+            }
         } else {
-            heapInfo[0].properties =
-                wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+            auto* heapInfo = new MemoryHeapInfo[2];
+            memoryHeapProperties->heapCount = 2;
+            memoryHeapProperties->heapInfo = heapInfo;
+
+            heapInfo[0].size = mDeviceInfo.dedicatedVideoMemory;
+            heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
+
+            heapInfo[1].size = mDeviceInfo.sharedSystemMemory;
+            heapInfo[1].properties =
+                wgpu::HeapProperty::HostVisible | wgpu::HeapProperty::HostCoherent |
                 wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
         }
-    } else {
-        auto* heapInfo = new MemoryHeapInfo[2];
-        memoryHeapProperties->heapCount = 2;
-        memoryHeapProperties->heapInfo = heapInfo;
-
-        heapInfo[0].size = mDeviceInfo.dedicatedVideoMemory;
-        heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
-
-        heapInfo[1].size = mDeviceInfo.sharedSystemMemory;
-        heapInfo[1].properties = wgpu::HeapProperty::HostVisible |
-                                 wgpu::HeapProperty::HostCoherent |
-                                 wgpu::HeapProperty::HostUncached | wgpu::HeapProperty::HostCached;
+    }
+    if (auto* d3dProperties = properties.Get<AdapterPropertiesD3D>()) {
+        d3dProperties->shaderModel = GetDeviceInfo().shaderModel;
     }
 }
 
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.h b/src/dawn/native/d3d12/PhysicalDeviceD3D12.h
index 9a978a5..96f6974 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.h
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.h
@@ -76,7 +76,7 @@
     MaybeError InitializeDebugLayerFilters();
     void CleanUpDebugLayerFilters();
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
 
     ComPtr<ID3D12Device> mD3d12Device;
 
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 8df96d4..ac41c1b 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -891,49 +891,51 @@
         return {};
     }
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override {
-        if ([*mDevice hasUnifiedMemory]) {
-            auto* heapInfo = new MemoryHeapInfo[1];
-            memoryHeapProperties->heapCount = 1;
-            memoryHeapProperties->heapInfo = heapInfo;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override {
+        if (auto* memoryHeapProperties = properties.Get<AdapterPropertiesMemoryHeaps>()) {
+            if ([*mDevice hasUnifiedMemory]) {
+                auto* heapInfo = new MemoryHeapInfo[1];
+                memoryHeapProperties->heapCount = 1;
+                memoryHeapProperties->heapInfo = heapInfo;
 
-            heapInfo[0].properties =
-                wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
-                wgpu::HeapProperty::HostCoherent | wgpu::HeapProperty::HostCached;
+                heapInfo[0].properties =
+                    wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+                    wgpu::HeapProperty::HostCoherent | wgpu::HeapProperty::HostCached;
 // TODO(dawn:2249): Enable on iOS. Some XCode or SDK versions seem to not match the docs.
 #if DAWN_PLATFORM_IS(MACOS)
-            if (@available(macOS 10.12, iOS 16.0, *)) {
-                heapInfo[0].size = [*mDevice recommendedMaxWorkingSetSize];
-            } else
+                if (@available(macOS 10.12, iOS 16.0, *)) {
+                    heapInfo[0].size = [*mDevice recommendedMaxWorkingSetSize];
+                } else
 #endif
-            {
-                // Since AdapterPropertiesMemoryHeaps is already gated on the
-                // availability and #ifdef above, we should never reach this case, however
-                // excluding the conditional causes build errors.
-                DAWN_UNREACHABLE();
-            }
-        } else {
+                {
+                    // Since AdapterPropertiesMemoryHeaps is already gated on the
+                    // availability and #ifdef above, we should never reach this case, however
+                    // excluding the conditional causes build errors.
+                    DAWN_UNREACHABLE();
+                }
+            } else {
 #if DAWN_PLATFORM_IS(MACOS)
-            auto* heapInfo = new MemoryHeapInfo[2];
-            memoryHeapProperties->heapCount = 2;
-            memoryHeapProperties->heapInfo = heapInfo;
+                auto* heapInfo = new MemoryHeapInfo[2];
+                memoryHeapProperties->heapCount = 2;
+                memoryHeapProperties->heapInfo = heapInfo;
 
-            heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
-            heapInfo[0].size = [*mDevice recommendedMaxWorkingSetSize];
+                heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal;
+                heapInfo[0].size = [*mDevice recommendedMaxWorkingSetSize];
 
-            mach_msg_type_number_t hostBasicInfoMsg = HOST_BASIC_INFO_COUNT;
-            host_basic_info_data_t hostInfo{};
-            DAWN_CHECK(host_info(mach_host_self(), HOST_BASIC_INFO,
-                                 reinterpret_cast<host_info_t>(&hostInfo),
-                                 &hostBasicInfoMsg) == KERN_SUCCESS);
+                mach_msg_type_number_t hostBasicInfoMsg = HOST_BASIC_INFO_COUNT;
+                host_basic_info_data_t hostInfo{};
+                DAWN_CHECK(host_info(mach_host_self(), HOST_BASIC_INFO,
+                                     reinterpret_cast<host_info_t>(&hostInfo),
+                                     &hostBasicInfoMsg) == KERN_SUCCESS);
 
-            heapInfo[1].properties = wgpu::HeapProperty::HostVisible |
-                                     wgpu::HeapProperty::HostCoherent |
-                                     wgpu::HeapProperty::HostCached;
-            heapInfo[1].size = hostInfo.max_mem;
+                heapInfo[1].properties = wgpu::HeapProperty::HostVisible |
+                                         wgpu::HeapProperty::HostCoherent |
+                                         wgpu::HeapProperty::HostCached;
+                heapInfo[1].size = hostInfo.max_mem;
 #else
-            DAWN_UNREACHABLE();
+                DAWN_UNREACHABLE();
 #endif
+            }
         }
     }
 
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index fc3cbd8..0568e9e 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -95,15 +95,19 @@
     return Device::Create(adapter, descriptor, deviceToggles);
 }
 
-void PhysicalDevice::PopulateMemoryHeapInfo(
-    AdapterPropertiesMemoryHeaps* memoryHeapProperties) const {
-    auto* heapInfo = new MemoryHeapInfo[1];
-    memoryHeapProperties->heapCount = 1;
-    memoryHeapProperties->heapInfo = heapInfo;
+void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const {
+    if (auto* memoryHeapProperties = properties.Get<AdapterPropertiesMemoryHeaps>()) {
+        auto* heapInfo = new MemoryHeapInfo[1];
+        memoryHeapProperties->heapCount = 1;
+        memoryHeapProperties->heapInfo = heapInfo;
 
-    heapInfo[0].size = 1024 * 1024 * 1024;
-    heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
-                             wgpu::HeapProperty::HostCached;
+        heapInfo[0].size = 1024 * 1024 * 1024;
+        heapInfo[0].properties = wgpu::HeapProperty::DeviceLocal | wgpu::HeapProperty::HostVisible |
+                                 wgpu::HeapProperty::HostCached;
+    }
+    if (auto* d3dProperties = properties.Get<AdapterPropertiesD3D>()) {
+        d3dProperties->shaderModel = 0;
+    }
 }
 
 FeatureValidationResult PhysicalDevice::ValidateFeatureSupportedWithTogglesImpl(
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h
index 153a6b2..3dc298f 100644
--- a/src/dawn/native/null/DeviceNull.h
+++ b/src/dawn/native/null/DeviceNull.h
@@ -211,7 +211,7 @@
                                                     const UnpackedPtr<DeviceDescriptor>& descriptor,
                                                     const TogglesState& deviceToggles) override;
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
 };
 
 // Helper class so |BindGroup| can allocate memory for its binding data,
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
index 21fbb2b..4d95453 100644
--- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp
+++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -428,9 +428,6 @@
     return {};
 }
 
-void PhysicalDevice::PopulateMemoryHeapInfo(
-    AdapterPropertiesMemoryHeaps* memoryHeapProperties) const {
-    DAWN_UNREACHABLE();
-}
+void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const {}
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.h b/src/dawn/native/opengl/PhysicalDeviceGL.h
index 4af8540..e2f4771 100644
--- a/src/dawn/native/opengl/PhysicalDeviceGL.h
+++ b/src/dawn/native/opengl/PhysicalDeviceGL.h
@@ -65,7 +65,7 @@
                                                     const UnpackedPtr<DeviceDescriptor>& descriptor,
                                                     const TogglesState& deviceToggles) override;
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
 
     OpenGLFunctions mFunctions;
     EGLDisplay mDisplay;
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index f9e98d0..2530f43 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -815,31 +815,32 @@
     return mDefaultComputeSubgroupSize;
 }
 
-void PhysicalDevice::PopulateMemoryHeapInfo(
-    AdapterPropertiesMemoryHeaps* memoryHeapProperties) const {
-    size_t count = mDeviceInfo.memoryHeaps.size();
-    auto* heapInfo = new MemoryHeapInfo[count];
-    memoryHeapProperties->heapCount = count;
-    memoryHeapProperties->heapInfo = heapInfo;
+void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const {
+    if (auto* memoryHeapProperties = properties.Get<AdapterPropertiesMemoryHeaps>()) {
+        size_t count = mDeviceInfo.memoryHeaps.size();
+        auto* heapInfo = new MemoryHeapInfo[count];
+        memoryHeapProperties->heapCount = count;
+        memoryHeapProperties->heapInfo = heapInfo;
 
-    for (size_t i = 0; i < count; ++i) {
-        heapInfo[i].size = mDeviceInfo.memoryHeaps[i].size;
-        heapInfo[i].properties = {};
-        if (mDeviceInfo.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
-            heapInfo[i].properties |= wgpu::HeapProperty::DeviceLocal;
+        for (size_t i = 0; i < count; ++i) {
+            heapInfo[i].size = mDeviceInfo.memoryHeaps[i].size;
+            heapInfo[i].properties = {};
+            if (mDeviceInfo.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
+                heapInfo[i].properties |= wgpu::HeapProperty::DeviceLocal;
+            }
         }
-    }
-    for (const auto& memoryType : mDeviceInfo.memoryTypes) {
-        if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
-            heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostVisible;
-        }
-        if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
-            heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCoherent;
-        }
-        if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
-            heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCached;
-        } else {
-            heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostUncached;
+        for (const auto& memoryType : mDeviceInfo.memoryTypes) {
+            if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+                heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostVisible;
+            }
+            if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
+                heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCoherent;
+            }
+            if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
+                heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCached;
+            } else {
+                heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostUncached;
+            }
         }
     }
 }
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.h b/src/dawn/native/vulkan/PhysicalDeviceVk.h
index 2c8038b..8da4917 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.h
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.h
@@ -80,7 +80,7 @@
     bool CheckSemaphoreSupport(DeviceExt deviceExt,
                                VkExternalSemaphoreHandleTypeFlagBits handleType) const;
 
-    void PopulateMemoryHeapInfo(AdapterPropertiesMemoryHeaps* memoryHeapProperties) const override;
+    void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
 
     VkPhysicalDevice mVkPhysicalDevice;
     Ref<VulkanInstance> mVulkanInstance;
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 49583af2..a5ffcab 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -573,6 +573,7 @@
   sources = [
     "end2end/AdapterCreationTests.cpp",
     "end2end/AdapterEnumerationTests.cpp",
+    "end2end/AdapterPropertiesD3DTests.cpp",
     "end2end/BasicTests.cpp",
     "end2end/BindGroupTests.cpp",
     "end2end/BufferHostMappedPointerTests.cpp",
diff --git a/src/dawn/tests/end2end/AdapterPropertiesD3DTests.cpp b/src/dawn/tests/end2end/AdapterPropertiesD3DTests.cpp
new file mode 100644
index 0000000..f480dc5
--- /dev/null
+++ b/src/dawn/tests/end2end/AdapterPropertiesD3DTests.cpp
@@ -0,0 +1,61 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "dawn/tests/DawnTest.h"
+
+namespace dawn {
+namespace {
+
+class AdapterPropertiesD3DTest : public DawnTest {};
+
+// TODO(dawn:2257) test that is is invalid to request AdapterPropertiesD3D if the
+// feature is not available.
+
+// Test that it is possible to query the d3d properties, and it is populated with a valid data.
+TEST_P(AdapterPropertiesD3DTest, GetD3DProperties) {
+    DAWN_TEST_UNSUPPORTED_IF(!adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesD3D));
+
+    wgpu::AdapterProperties properties;
+    wgpu::AdapterPropertiesD3D d3dProperties;
+    properties.nextInChain = &d3dProperties;
+
+    adapter.GetProperties(&properties);
+
+    // This is the minimum D3D shader model Dawn supports.
+    EXPECT_GE(d3dProperties.shaderModel, 50u);
+}
+
+DAWN_INSTANTIATE_TEST(AdapterPropertiesD3DTest,
+                      D3D11Backend(),
+                      D3D12Backend(),
+                      MetalBackend(),
+                      OpenGLBackend(),
+                      OpenGLESBackend(),
+                      VulkanBackend());
+
+}  // anonymous namespace
+}  // namespace dawn
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 1f6b889..97d08c3 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -77,9 +77,7 @@
         apiAdapter = api.GetNewAdapter();
         EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
             .WillOnce(InvokeWithoutArgs([&] {
-                EXPECT_CALL(api, AdapterHasFeature(apiAdapter,
-                                                   WGPUFeatureName_AdapterPropertiesMemoryHeaps))
-                    .WillOnce(Return(false));
+                EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false));
 
                 EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
                     .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index 2caf2e0..7357998 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -207,8 +207,8 @@
     });
 }
 
-// Test that RequestAdapter forwards the memory heap properties to the client.
-TEST_P(WireInstanceTests, RequestAdapterPassesMemoryHeapProperties) {
+// Test that RequestAdapter forwards all chained properties to the client.
+TEST_P(WireInstanceTests, RequestAdapterPassesChainedProperties) {
     WGPURequestAdapterOptions options = {};
     InstanceRequestAdapter(instance, &options, nullptr);
 
@@ -223,17 +223,22 @@
     fakeMemoryHeapProperties.heapCount = 3;
     fakeMemoryHeapProperties.heapInfo = fakeHeapInfo;
 
+    WGPUAdapterPropertiesD3D fakeD3DProperties = {};
+    fakeD3DProperties.chain.sType = WGPUSType_AdapterPropertiesD3D;
+    fakeD3DProperties.shaderModel = 61;
+
     std::initializer_list<WGPUFeatureName> fakeFeatures = {
         WGPUFeatureName_AdapterPropertiesMemoryHeaps,
+        WGPUFeatureName_AdapterPropertiesD3D,
     };
 
     // Expect the server to receive the message. Then, mock a fake reply.
     WGPUAdapter apiAdapter = api.GetNewAdapter();
     EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
         .WillOnce(InvokeWithoutArgs([&] {
-            EXPECT_CALL(api,
-                        AdapterHasFeature(apiAdapter, WGPUFeatureName_AdapterPropertiesMemoryHeaps))
-                .WillOnce(Return(true));
+            for (WGPUFeatureName feature : fakeFeatures) {
+                EXPECT_CALL(api, AdapterHasFeature(apiAdapter, feature)).WillOnce(Return(true));
+            }
 
             EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
@@ -242,10 +247,27 @@
                     properties->name = "fake adapter";
                     properties->driverDescription = "hello world";
 
-                    EXPECT_EQ(properties->nextInChain->sType,
-                              WGPUSType_AdapterPropertiesMemoryHeaps);
-                    *reinterpret_cast<WGPUAdapterPropertiesMemoryHeaps*>(properties->nextInChain) =
-                        fakeMemoryHeapProperties;
+                    WGPUChainedStructOut* chain = properties->nextInChain;
+                    while (chain != nullptr) {
+                        auto* next = chain->next;
+                        switch (chain->sType) {
+                            case WGPUSType_AdapterPropertiesMemoryHeaps:
+                                *reinterpret_cast<WGPUAdapterPropertiesMemoryHeaps*>(chain) =
+                                    fakeMemoryHeapProperties;
+                                break;
+                            case WGPUSType_AdapterPropertiesD3D:
+                                *reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain) =
+                                    fakeD3DProperties;
+                                break;
+                            default:
+                                FAIL() << "Unexpected chain";
+                        }
+                        // update next pointer back to the original since it would be overwritten
+                        // in the switch statement
+                        chain->next = next;
+
+                        chain = next;
+                    }
                 })));
 
             EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull()))
@@ -295,6 +317,14 @@
                     EXPECT_EQ(memoryHeapProperties.heapInfo[i].size,
                               fakeMemoryHeapProperties.heapInfo[i].size);
                 }
+
+                // Get the D3D properties.
+                WGPUAdapterPropertiesD3D d3dProperties = {};
+                d3dProperties.chain.sType = WGPUSType_AdapterPropertiesD3D;
+                properties.nextInChain = &d3dProperties.chain;
+                wgpuAdapterGetProperties(adapter, &properties);
+                // Expect them to match.
+                EXPECT_EQ(d3dProperties.shaderModel, fakeD3DProperties.shaderModel);
             })));
 
         FlushCallbacks();
diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp
index 1526eb8..1a9a893 100644
--- a/src/dawn/wire/SupportedFeatures.cpp
+++ b/src/dawn/wire/SupportedFeatures.cpp
@@ -93,6 +93,7 @@
         case WGPUFeatureName_Norm16TextureFormats:
         case WGPUFeatureName_FramebufferFetch:
         case WGPUFeatureName_AdapterPropertiesMemoryHeaps:
+        case WGPUFeatureName_AdapterPropertiesD3D:
             return true;
     }
 
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 7537079..20c9dae 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -151,6 +151,11 @@
                     memoryHeapProperties->heapInfo + memoryHeapProperties->heapCount};
                 break;
             }
+            case WGPUSType_AdapterPropertiesD3D: {
+                auto* d3dProperties = reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain);
+                mD3DProperties.shaderModel = d3dProperties->shaderModel;
+                break;
+            }
             default:
                 DAWN_UNREACHABLE();
                 break;
@@ -176,6 +181,11 @@
                 memoryHeapProperties->heapInfo = heapInfo;
                 break;
             }
+            case WGPUSType_AdapterPropertiesD3D: {
+                auto* d3dProperties = reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain);
+                d3dProperties->shaderModel = mD3DProperties.shaderModel;
+                break;
+            }
             default:
                 break;
         }
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index 79021e9..c200053 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -68,6 +68,7 @@
     LimitsAndFeatures mLimitsAndFeatures;
     WGPUAdapterProperties mProperties;
     std::vector<WGPUMemoryHeapInfo> mMemoryHeapInfo;
+    WGPUAdapterPropertiesD3D mD3DProperties;
 
     struct RequestDeviceData {
         WGPURequestDeviceCallback callback = nullptr;
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 24a69a2..bb09833 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -88,12 +88,22 @@
 
     // Query and report the adapter properties.
     WGPUAdapterProperties properties = {};
+    WGPUChainedStructOut** propertiesChain = &properties.nextInChain;
 
     // Query AdapterPropertiesMemoryHeaps if the feature is supported.
     WGPUAdapterPropertiesMemoryHeaps memoryHeapProperties = {};
     memoryHeapProperties.chain.sType = WGPUSType_AdapterPropertiesMemoryHeaps;
     if (mProcs.adapterHasFeature(adapter, WGPUFeatureName_AdapterPropertiesMemoryHeaps)) {
-        properties.nextInChain = &memoryHeapProperties.chain;
+        *propertiesChain = &memoryHeapProperties.chain;
+        propertiesChain = &(*propertiesChain)->next;
+    }
+
+    // Query AdapterPropertiesD3D if the feature is supported.
+    WGPUAdapterPropertiesD3D d3dProperties = {};
+    d3dProperties.chain.sType = WGPUSType_AdapterPropertiesD3D;
+    if (mProcs.adapterHasFeature(adapter, WGPUFeatureName_AdapterPropertiesD3D)) {
+        *propertiesChain = &d3dProperties.chain;
+        propertiesChain = &(*propertiesChain)->next;
     }
 
     mProcs.adapterGetProperties(adapter, &properties);