Populate WGPUAdapterPropertiesSubgroups

Including: In the wire Adapter, initializes subgroup properties
so they can be read even if adapter acquisition fails.

Bug: 354751907
Change-Id: I28733c7cf3165b84c6858415357b77c8be06b105
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/216117
Reviewed-by: Loko Kung <lokokung@google.com>
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: David Neto <dneto@google.com>
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index ff41288..9e5d3bf 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -191,6 +191,12 @@
     if (auto* powerPreferenceDesc = unpacked.Get<DawnAdapterPropertiesPowerPreference>()) {
         powerPreferenceDesc->powerPreference = mPowerPreference;
     }
+    if (auto* subgroupsProperties = unpacked.Get<AdapterPropertiesSubgroups>()) {
+        // When the feature is *not* supported, these must be 4 and 128.
+        // Set those defaults now, but a backend may override this.
+        subgroupsProperties->subgroupMinSize = 4;
+        subgroupsProperties->subgroupMaxSize = 128;
+    }
 
     mPhysicalDevice->PopulateBackendProperties(unpacked);
 
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 34f436f..3918eaa 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -1868,6 +1868,7 @@
 
     limits->limits = mLimits.v1;
 
+    // TODO(354751907): Move this to AdapterInfo
     if (auto* subgroupLimits = unpacked.Get<DawnExperimentalSubgroupLimits>()) {
         wgpu::ChainedStructOut* originalChain = subgroupLimits->nextInChain;
         if (!HasFeature(Feature::Subgroups)) {
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index d072704..c5d1cba 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -585,7 +585,7 @@
       "crbug.com/372698905", ToggleStage::Device}},
     {Toggle::D3D12RelaxMinSubgroupSizeTo8,
      {"d3d12_relax_min_subgroup_size_to_8",
-      "Relax the adapters and devices' minSubgroupSize to the minimium of D3D12 reported "
+      "Relax the adapters and devices' subgroupMinSize to the minimium of D3D12 reported "
       "minWaveLaneCount and 8. Some D3D12 drivers is possible to run fragment shader with wave "
       "count 8 while reporting minWaveLaneCount 16.",
       "https://crbug.com/381969450", ToggleStage::Adapter}},
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index c46df96..5f6ea1a 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -368,6 +368,7 @@
     // - maxVertexBufferArrayStride
 
     // Experimental limits for subgroups
+    // TODO(crbug.com/354751907) Move this to AdapterInfo
     limits->experimentalSubgroupLimits.minSubgroupSize = mDeviceInfo.waveLaneCountMin;
     // Currently the WaveLaneCountMax queried from D3D12 API is not reliable and the meaning is
     // unclear. Use 128 instead, which is the largest possible size. Reference:
@@ -844,6 +845,13 @@
 }
 
 void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterInfo>& info) const {
+    if (auto* subgroupProperties = info.Get<AdapterPropertiesSubgroups>()) {
+        subgroupProperties->subgroupMinSize = mDeviceInfo.waveLaneCountMin;
+        // Currently the WaveLaneCountMax queried from D3D12 API is not reliable and the meaning is
+        // unclear. Use 128 instead, which is the largest possible size. Reference:
+        // https://github.com/Microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics#:~:text=UINT%20WaveLaneCountMax
+        subgroupProperties->subgroupMaxSize = 128u;
+    }
     if (auto* memoryHeapProperties = info.Get<AdapterPropertiesMemoryHeaps>()) {
         // https://microsoft.github.io/DirectX-Specs/d3d/D3D12GPUUploadHeaps.html describes
         // the properties of D3D12 Default/Upload/Readback heaps.
diff --git a/src/dawn/native/metal/PhysicalDeviceMTL.mm b/src/dawn/native/metal/PhysicalDeviceMTL.mm
index d03ee34..b3cc059 100644
--- a/src/dawn/native/metal/PhysicalDeviceMTL.mm
+++ b/src/dawn/native/metal/PhysicalDeviceMTL.mm
@@ -749,10 +749,13 @@
     // explicitly use Xcode 13.3 and MacOS 12.3 version 21E226, so does not support
     // MTLGPUFamilyMetal3.
     // Note that supportsFamily: method requires macOS 10.15+ or iOS 13.0+
+    // TODO(380326541): Check that reduction operations are supported in Apple6. The support
+    // table says Apple7.
     if (@available(macOS 10.15, iOS 13.0, *)) {
         if ([*mDevice supportsFamily:MTLGPUFamilyApple6] ||
             [*mDevice supportsFamily:MTLGPUFamilyMac2]) {
             EnableFeature(Feature::Subgroups);
+            // TODO(crbug.com/380244620) remove SubgroupsF16
             EnableFeature(Feature::SubgroupsF16);
         }
     }
@@ -952,6 +955,7 @@
     // - maxVertexBufferArrayStride
 
     // Experimental limits for subgroups
+    // TODO(354751907): Move to AdapterInfo
     limits->experimentalSubgroupLimits.minSubgroupSize = 4;
     limits->experimentalSubgroupLimits.maxSubgroupSize = 64;
 
@@ -965,6 +969,10 @@
 }
 
 void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterInfo>& info) const {
+    if (auto* subgroupProperties = info.Get<AdapterPropertiesSubgroups>()) {
+        subgroupProperties->subgroupMinSize = 4;
+        subgroupProperties->subgroupMaxSize = 64;
+    }
     if (auto* memoryHeapProperties = info.Get<AdapterPropertiesMemoryHeaps>()) {
         if ([*mDevice hasUnifiedMemory]) {
             auto* heapInfo = new MemoryHeapInfo[1];
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index 3a2a120..c5173c6 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -111,6 +111,10 @@
 }
 
 void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterInfo>& info) const {
+    if (auto* subgroupProperties = info.Get<AdapterPropertiesSubgroups>()) {
+        subgroupProperties->subgroupMinSize = 4;
+        subgroupProperties->subgroupMaxSize = 128;
+    }
     if (auto* memoryHeapProperties = info.Get<AdapterPropertiesMemoryHeaps>()) {
         auto* heapInfo = new MemoryHeapInfo[1];
         memoryHeapProperties->heapCount = 1;
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 3306fb9..8470900 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -639,6 +639,7 @@
     }
 
     // Experimental limits for subgroups
+    // TODO(crbug.com/354751907) Move this to AdapterInfo
     limits->experimentalSubgroupLimits.minSubgroupSize =
         mDeviceInfo.subgroupSizeControlProperties.minSubgroupSize;
     limits->experimentalSubgroupLimits.maxSubgroupSize =
@@ -1035,6 +1036,13 @@
 }
 
 void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterInfo>& info) const {
+    if (auto* subgroupProperties = info.Get<AdapterPropertiesSubgroups>()) {
+        // Subgroups are supported only if subgroup size control is supported.
+        subgroupProperties->subgroupMinSize =
+            mDeviceInfo.subgroupSizeControlProperties.minSubgroupSize;
+        subgroupProperties->subgroupMaxSize =
+            mDeviceInfo.subgroupSizeControlProperties.maxSubgroupSize;
+    }
     if (auto* memoryHeapProperties = info.Get<AdapterPropertiesMemoryHeaps>()) {
         size_t count = mDeviceInfo.memoryHeaps.size();
         auto* heapInfo = new MemoryHeapInfo[count];
diff --git a/src/dawn/tests/end2end/SubgroupsTests.cpp b/src/dawn/tests/end2end/SubgroupsTests.cpp
index eb7c4ba..494e471 100644
--- a/src/dawn/tests/end2end/SubgroupsTests.cpp
+++ b/src/dawn/tests/end2end/SubgroupsTests.cpp
@@ -39,6 +39,105 @@
 namespace dawn {
 namespace {
 
+enum class RequestSubgroups {
+    WhenAvailable,
+    Never,
+};
+std::ostream& operator<<(std::ostream& o, const RequestSubgroups& r) {
+    switch (r) {
+        case RequestSubgroups::WhenAvailable:
+            o << "when_supported";
+            break;
+        case RequestSubgroups::Never:
+            o << "never";
+            break;
+    }
+    return o;
+}
+
+DAWN_TEST_PARAM_STRUCT(SubgroupsPropertiesTestsParams, RequestSubgroups);
+
+template <class Params>
+class SubgroupsPropertiesTestBase : public DawnTestWithParams<Params> {
+  public:
+    using DawnTestWithParams<Params>::GetParam;
+    using DawnTestWithParams<Params>::SupportsFeatures;
+
+  protected:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        if (GetParam().mRequestSubgroups == RequestSubgroups::WhenAvailable &&
+            SupportsFeatures({wgpu::FeatureName::Subgroups})) {
+            return {wgpu::FeatureName::Subgroups};
+        }
+        return {};
+    }
+
+    // Checks valid values for min and max subgroup sizes, per spec.
+    void CheckValidSizes(uint32_t subgroupMinSize, uint32_t subgroupMaxSize) {
+        EXPECT_GE(subgroupMinSize, 4u) << subgroupMinSize;
+        EXPECT_TRUE(IsPowerOfTwo(subgroupMinSize)) << subgroupMinSize;
+
+        EXPECT_LE(subgroupMaxSize, 128u) << subgroupMaxSize;
+        EXPECT_TRUE(IsPowerOfTwo(subgroupMaxSize));
+
+        EXPECT_LE(subgroupMinSize, subgroupMaxSize)
+            << subgroupMinSize << " should be less equal than " << subgroupMaxSize;
+    }
+};
+
+using SubgroupsPropertiesTests = SubgroupsPropertiesTestBase<SubgroupsPropertiesTestsParams>;
+
+TEST_P(SubgroupsPropertiesTests, FromAdapter) {
+    wgpu::AdapterPropertiesSubgroups subgroup_properties;
+
+    // Write invalid values to start with, to make sure they are overwritten.
+    subgroup_properties.subgroupMinSize = 3;
+    subgroup_properties.subgroupMaxSize = 443;
+
+    wgpu::AdapterInfo info;
+    info.nextInChain = &subgroup_properties;
+
+    adapter.GetInfo(&info);
+
+    // Check the integrity of the struct.
+    EXPECT_EQ(subgroup_properties.sType, wgpu::SType::AdapterPropertiesSubgroups);
+    EXPECT_EQ(subgroup_properties.nextInChain, nullptr);
+
+    CheckValidSizes(subgroup_properties.subgroupMinSize, subgroup_properties.subgroupMaxSize);
+}
+
+TEST_P(SubgroupsPropertiesTests, FromDevice) {
+    wgpu::AdapterPropertiesSubgroups subgroup_properties;
+    wgpu::AdapterInfo info;
+    info.nextInChain = &subgroup_properties;
+
+    device.GetAdapterInfo(&info);
+
+    CheckValidSizes(subgroup_properties.subgroupMinSize, subgroup_properties.subgroupMaxSize);
+}
+
+TEST_P(SubgroupsPropertiesTests, DeviceAndAdapterAgree) {
+    wgpu::AdapterPropertiesSubgroups adapter_subgroup_properties;
+    wgpu::AdapterInfo adapter_info;
+    adapter_info.nextInChain = &adapter_subgroup_properties;
+    adapter.GetInfo(&adapter_info);
+
+    wgpu::AdapterPropertiesSubgroups device_subgroup_properties;
+    wgpu::AdapterInfo device_info;
+    device_info.nextInChain = &device_subgroup_properties;
+    device.GetAdapterInfo(&device_info);
+
+    EXPECT_EQ(device_subgroup_properties.subgroupMinSize,
+              adapter_subgroup_properties.subgroupMinSize);
+    EXPECT_EQ(device_subgroup_properties.subgroupMaxSize,
+              adapter_subgroup_properties.subgroupMaxSize);
+}
+
+DAWN_INSTANTIATE_TEST_P(SubgroupsPropertiesTests,
+                        {D3D12Backend(), D3D12Backend({}, {"use_dxc"}), MetalBackend(),
+                         VulkanBackend()},
+                        {RequestSubgroups::WhenAvailable, RequestSubgroups::Never});
+
 template <class Params>
 class SubgroupsTestsBase : public DawnTestWithParams<Params> {
   public:
@@ -389,8 +488,7 @@
 constexpr int32_t SubgroupBroadcastConstantValueForInvocation0 = 1;
 constexpr int32_t SubgroupRegisterInitializer = 555;
 
-class SubgroupsBroadcastTests
-    : public SubgroupsTestsBase<SubgroupsBroadcastTestsParams> {
+class SubgroupsBroadcastTests : public SubgroupsTestsBase<SubgroupsBroadcastTestsParams> {
   protected:
     // Testing subgroup broadcasting. The shader declares a workgroup size of [workgroupSize, 1, 1],
     // in which each invocation hold a register initialized to SubgroupRegisterInitializer, then
@@ -604,7 +702,6 @@
                         // SubgroupBroadcastValueOfInvocation0
 );
 
-
 // Core functions that may be polyfilled
 enum class SubgroupIntrinsicOp : uint8_t {
     Add,
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index 480b55b..9b4d055 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -233,10 +233,16 @@
     fakeVkProperties.chain.sType = WGPUSType_AdapterPropertiesVk;
     fakeVkProperties.driverVersion = 0x801F6000;
 
+    WGPUAdapterPropertiesSubgroups fakeSubgroupsProperties = {};
+    fakeSubgroupsProperties.chain.sType = WGPUSType_AdapterPropertiesSubgroups;
+    fakeSubgroupsProperties.subgroupMinSize = 4;
+    fakeSubgroupsProperties.subgroupMaxSize = 128;
+
     std::initializer_list<WGPUFeatureName> fakeFeaturesList = {
         WGPUFeatureName_AdapterPropertiesMemoryHeaps,
         WGPUFeatureName_AdapterPropertiesD3D,
         WGPUFeatureName_AdapterPropertiesVk,
+        WGPUFeatureName_Subgroups,
     };
 
     // Expect the server to receive the message. Then, mock a fake reply.
@@ -267,6 +273,10 @@
                                 *reinterpret_cast<WGPUAdapterPropertiesVk*>(chain) =
                                     fakeVkProperties;
                                 break;
+                            case WGPUSType_AdapterPropertiesSubgroups:
+                                *reinterpret_cast<WGPUAdapterPropertiesSubgroups*>(chain) =
+                                    fakeSubgroupsProperties;
+                                break;
                             default:
                                 ADD_FAILURE() << "Unexpected chain";
                                 return WGPUStatus_Error;
@@ -327,6 +337,17 @@
                 adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info));
                 // Expect them to match.
                 EXPECT_EQ(vkProperties.driverVersion, fakeVkProperties.driverVersion);
+
+                // Get the Subgroups properties.
+                WGPUAdapterPropertiesSubgroups subgroupsProperties = {};
+                subgroupsProperties.chain.sType = WGPUSType_AdapterPropertiesSubgroups;
+                info.nextInChain = &subgroupsProperties.chain;
+                adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info));
+                // Expect them to match.
+                EXPECT_EQ(subgroupsProperties.subgroupMinSize,
+                          fakeSubgroupsProperties.subgroupMinSize);
+                EXPECT_EQ(subgroupsProperties.subgroupMaxSize,
+                          fakeSubgroupsProperties.subgroupMaxSize);
             })));
 
         FlushCallbacks();
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 2302b6d..3ed95ee 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -200,6 +200,13 @@
                 mVkProperties.driverVersion = vkProperties->driverVersion;
                 break;
             }
+            case WGPUSType_AdapterPropertiesSubgroups: {
+                auto* subgroupsProperties =
+                    reinterpret_cast<WGPUAdapterPropertiesSubgroups*>(chain);
+                mSubgroupsProperties.subgroupMinSize = subgroupsProperties->subgroupMinSize;
+                mSubgroupsProperties.subgroupMaxSize = subgroupsProperties->subgroupMaxSize;
+                break;
+            }
             default:
                 DAWN_UNREACHABLE();
                 break;
@@ -235,6 +242,13 @@
                 vkProperties->driverVersion = mVkProperties.driverVersion;
                 break;
             }
+            case WGPUSType_AdapterPropertiesSubgroups: {
+                auto* subgroupsProperties =
+                    reinterpret_cast<WGPUAdapterPropertiesSubgroups*>(chain);
+                subgroupsProperties->subgroupMinSize = mSubgroupsProperties.subgroupMinSize;
+                subgroupsProperties->subgroupMaxSize = mSubgroupsProperties.subgroupMaxSize;
+                break;
+            }
             default:
                 break;
         }
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index cfdebfd..756b411 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -76,6 +76,13 @@
     std::vector<WGPUMemoryHeapInfo> mMemoryHeapInfo;
     WGPUAdapterPropertiesD3D mD3DProperties;
     WGPUAdapterPropertiesVk mVkProperties;
+    // Initialize subgroup properties so they can be read even if adapter
+    // acquisition fails.
+    WGPUAdapterPropertiesSubgroups mSubgroupsProperties = {
+        {nullptr, WGPUSType_AdapterPropertiesSubgroups},
+        4u,   // subgroupMinSize
+        128u  // subgroupMaxSize
+    };
 };
 
 }  // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/LimitsAndFeatures.cpp b/src/dawn/wire/client/LimitsAndFeatures.cpp
index e5e4ce4..cd70277 100644
--- a/src/dawn/wire/client/LimitsAndFeatures.cpp
+++ b/src/dawn/wire/client/LimitsAndFeatures.cpp
@@ -112,6 +112,7 @@
     for (auto* chain = limits->nextInChain; chain; chain = chain->next) {
         switch (chain->sType) {
             case (WGPUSType_DawnExperimentalSubgroupLimits): {
+                // TODO(crbug.com/354751907) Remove this, as it is now in AdapterInfo.
                 auto* experimentalSubgroupLimits =
                     reinterpret_cast<WGPUDawnExperimentalSubgroupLimits*>(chain);
                 mExperimentalSubgroupLimits = *experimentalSubgroupLimits;
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 992f6b0..60b4afd 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -127,6 +127,7 @@
     WGPUSupportedLimits limits = {};
 
     // Chained DawnExperimentalSubgroupLimits.
+    // TODO(crbug.com/354751907) Remove this, as it is now in AdapterInfo.
     WGPUDawnExperimentalSubgroupLimits experimentalSubgroupLimits = {};
     experimentalSubgroupLimits.chain.sType = WGPUSType_DawnExperimentalSubgroupLimits;
     limits.nextInChain = &experimentalSubgroupLimits.chain;
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 8c97bbe..e504950 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -120,6 +120,14 @@
         propertiesChain = &(*propertiesChain)->next;
     }
 
+    // Query AdapterPropertiesSubgroups if the feature is supported.
+    WGPUAdapterPropertiesSubgroups subgroupsProperties = {};
+    subgroupsProperties.chain.sType = WGPUSType_AdapterPropertiesSubgroups;
+    if (mProcs.adapterHasFeature(adapter, WGPUFeatureName_Subgroups)) {
+        *propertiesChain = &subgroupsProperties.chain;
+        propertiesChain = &(*propertiesChain)->next;
+    }
+
     mProcs.adapterGetInfo(adapter, &info);
     cmd.info = &info;
 
@@ -127,6 +135,7 @@
     // DawnExperimentalImmediateDataLimits, and DawnTexelCopyBufferRowAlignmentLimits.
     WGPUSupportedLimits limits = {};
 
+    // TODO(crbug.com/354751907) Remove this, as it is now in AdapterInfo.
     WGPUDawnExperimentalSubgroupLimits experimentalSubgroupLimits = {};
     experimentalSubgroupLimits.chain.sType = WGPUSType_DawnExperimentalSubgroupLimits;
     limits.nextInChain = &experimentalSubgroupLimits.chain;