Title: [293098] trunk/Source/WebGPU
Revision
293098
Author
[email protected]
Date
2022-04-20 09:10:11 -0700 (Wed, 20 Apr 2022)

Log Message

[WebGPU] Expand hardware capabilities to include features (beyond just limits)
https://bugs.webkit.org/show_bug.cgi?id=239443

Reviewed by Kimmo Kinnunen.

Only MTLGPUFamilyApple devices support depth/stencil textures in the managed/shared address space.
So, we have to expand the hardware capabilities infrastructure to be able to hold this kind
of information (so the texture creation function can consult with this state to know what storage
mode to use for the created texture). This patch replaces the stored WGPULimits struct with a new
struct, HardwareCapabilities, which holds a WGPULimits iniside it.

While I was here, I also included a Vector<WGPUFeature> inside the HardwareCapabilities struct,
and hooked it up to the various places which need to be guarded by the presence of features.
This is needed for compressed textures to work (among other things). Because there are only
a handful of possible features, I elected to use a sorted Vector instead of using the big HashSet
hammer.

* WebGPU.xcodeproj/project.pbxproj:
* WebGPU/Adapter.h:
(WebGPU::Adapter::create):
* WebGPU/Adapter.mm:
(WebGPU::Adapter::Adapter):
(WebGPU::Adapter::enumerateFeatures):
(WebGPU::Adapter::getLimits):
(WebGPU::Adapter::hasFeature):
(WebGPU::Adapter::requestDevice):
* WebGPU/Device.h:
(WebGPU::Device::limits const):
(WebGPU::Device::features const):
(WebGPU::Device::baseCapabilities const):
* WebGPU/Device.mm:
(WebGPU::Device::create):
(WebGPU::Device::Device):
(WebGPU::Device::enumerateFeatures):
(WebGPU::Device::getLimits):
(WebGPU::Device::hasFeature):
* WebGPU/HardwareCapabilities.h: Renamed from Source/WebGPU/WebGPU/HardwareLimits.h.
* WebGPU/HardwareCapabilities.mm: Added.
(WebGPU::baseCapabilities):
(WebGPU::baseFeatures):
(WebGPU::apple3):
(WebGPU::apple4):
(WebGPU::apple5):
(WebGPU::apple6):
(WebGPU::apple7):
(WebGPU::mac2):
(WebGPU::mergeMaximum):
(WebGPU::mergeAlignment):
(WebGPU::mergeLimits):
(WebGPU::mergeFeatures):
(WebGPU::mergeBaseCapabilities):
(WebGPU::rawHardwareCapabilities):
(WebGPU::anyLimitIsBetterThan):
(WebGPU::includesUnsupportedFeatures):
(WebGPU::checkLimits):
(WebGPU::hardwareCapabilities):
(WebGPU::isValid):
* WebGPU/HardwareLimits.mm: Removed.
* WebGPU/Instance.mm:
(WebGPU::Instance::requestAdapter):
* WebGPU/Texture.mm:
(WebGPU::featureRequirementForFormat):
(WebGPU::Device::validateCreateTexture):
(WebGPU::storageMode):
(WebGPU::Device::createTexture):

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/Source/WebGPU/ChangeLog (293097 => 293098)


--- trunk/Source/WebGPU/ChangeLog	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/ChangeLog	2022-04-20 16:10:11 UTC (rev 293098)
@@ -1,5 +1,73 @@
 2022-04-20  Myles C. Maxfield  <[email protected]>
 
+        [WebGPU] Expand hardware capabilities to include features (beyond just limits)
+        https://bugs.webkit.org/show_bug.cgi?id=239443
+
+        Reviewed by Kimmo Kinnunen.
+
+        Only MTLGPUFamilyApple devices support depth/stencil textures in the managed/shared address space.
+        So, we have to expand the hardware capabilities infrastructure to be able to hold this kind
+        of information (so the texture creation function can consult with this state to know what storage
+        mode to use for the created texture). This patch replaces the stored WGPULimits struct with a new
+        struct, HardwareCapabilities, which holds a WGPULimits iniside it.
+
+        While I was here, I also included a Vector<WGPUFeature> inside the HardwareCapabilities struct,
+        and hooked it up to the various places which need to be guarded by the presence of features.
+        This is needed for compressed textures to work (among other things). Because there are only
+        a handful of possible features, I elected to use a sorted Vector instead of using the big HashSet
+        hammer.
+
+        * WebGPU.xcodeproj/project.pbxproj:
+        * WebGPU/Adapter.h:
+        (WebGPU::Adapter::create):
+        * WebGPU/Adapter.mm:
+        (WebGPU::Adapter::Adapter):
+        (WebGPU::Adapter::enumerateFeatures):
+        (WebGPU::Adapter::getLimits):
+        (WebGPU::Adapter::hasFeature):
+        (WebGPU::Adapter::requestDevice):
+        * WebGPU/Device.h:
+        (WebGPU::Device::limits const):
+        (WebGPU::Device::features const):
+        (WebGPU::Device::baseCapabilities const):
+        * WebGPU/Device.mm:
+        (WebGPU::Device::create):
+        (WebGPU::Device::Device):
+        (WebGPU::Device::enumerateFeatures):
+        (WebGPU::Device::getLimits):
+        (WebGPU::Device::hasFeature):
+        * WebGPU/HardwareCapabilities.h: Renamed from Source/WebGPU/WebGPU/HardwareLimits.h.
+        * WebGPU/HardwareCapabilities.mm: Added.
+        (WebGPU::baseCapabilities):
+        (WebGPU::baseFeatures):
+        (WebGPU::apple3):
+        (WebGPU::apple4):
+        (WebGPU::apple5):
+        (WebGPU::apple6):
+        (WebGPU::apple7):
+        (WebGPU::mac2):
+        (WebGPU::mergeMaximum):
+        (WebGPU::mergeAlignment):
+        (WebGPU::mergeLimits):
+        (WebGPU::mergeFeatures):
+        (WebGPU::mergeBaseCapabilities):
+        (WebGPU::rawHardwareCapabilities):
+        (WebGPU::anyLimitIsBetterThan):
+        (WebGPU::includesUnsupportedFeatures):
+        (WebGPU::checkLimits):
+        (WebGPU::hardwareCapabilities):
+        (WebGPU::isValid):
+        * WebGPU/HardwareLimits.mm: Removed.
+        * WebGPU/Instance.mm:
+        (WebGPU::Instance::requestAdapter):
+        * WebGPU/Texture.mm:
+        (WebGPU::featureRequirementForFormat):
+        (WebGPU::Device::validateCreateTexture):
+        (WebGPU::storageMode):
+        (WebGPU::Device::createTexture):
+
+2022-04-20  Myles C. Maxfield  <[email protected]>
+
         [WebGPU] RGB9E5Ufloat textures are not renderable
         https://bugs.webkit.org/show_bug.cgi?id=239445
 

Modified: trunk/Source/WebGPU/WebGPU/Adapter.h (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Adapter.h	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Adapter.h	2022-04-20 16:10:11 UTC (rev 293098)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#import "HardwareCapabilities.h"
 #import <wtf/CompletionHandler.h>
 #import <wtf/FastMalloc.h>
 #import <wtf/Ref.h>
@@ -42,9 +43,9 @@
 class Adapter : public WGPUAdapterImpl, public RefCounted<Adapter> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<Adapter> create(id<MTLDevice> device, Instance& instance, const WGPULimits& limits)
+    static Ref<Adapter> create(id<MTLDevice> device, Instance& instance, HardwareCapabilities&& capabilities)
     {
-        return adoptRef(*new Adapter(device, instance, limits));
+        return adoptRef(*new Adapter(device, instance, WTFMove(capabilities)));
     }
     static Ref<Adapter> createInvalid(Instance& instance)
     {
@@ -67,13 +68,13 @@
 
 
 private:
-    Adapter(id<MTLDevice>, Instance&, const WGPULimits&);
+    Adapter(id<MTLDevice>, Instance&, HardwareCapabilities&&);
     Adapter(Instance&);
 
     id<MTLDevice> m_device { nil };
     const Ref<Instance> m_instance;
 
-    WGPULimits m_limits { };
+    HardwareCapabilities m_capabilities { };
 };
 
 } // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/Adapter.mm (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Adapter.mm	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Adapter.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -28,16 +28,16 @@
 
 #import "APIConversions.h"
 #import "Device.h"
-#import "HardwareLimits.h"
 #import "Instance.h"
+#import <algorithm>
 #import <wtf/StdLibExtras.h>
 
 namespace WebGPU {
 
-Adapter::Adapter(id<MTLDevice> device, Instance& instance, const WGPULimits& limits)
+Adapter::Adapter(id<MTLDevice> device, Instance& instance, HardwareCapabilities&& capabilities)
     : m_device(device)
     , m_instance(instance)
-    , m_limits(limits)
+    , m_capabilities(WTFMove(capabilities))
 {
 }
 
@@ -48,10 +48,13 @@
 
 Adapter::~Adapter() = default;
 
-size_t Adapter::enumerateFeatures(WGPUFeatureName*)
+size_t Adapter::enumerateFeatures(WGPUFeatureName* features)
 {
-    // We support no optional features right now.
-    return 0;
+    // The API contract for this requires that sufficient space has already been allocated for the output.
+    // This requires the caller calling us twice: once to get the amount of space to allocate, and once to fill the space.
+    if (features)
+        std::copy(m_capabilities.features.begin(), m_capabilities.features.end(), features);
+    return m_capabilities.features.size();
 }
 
 bool Adapter::getLimits(WGPUSupportedLimits& limits)
@@ -59,8 +62,7 @@
     if (limits.nextInChain != nullptr)
         return false;
 
-    // FIXME: Implement this.
-    limits.limits = { };
+    limits.limits = m_capabilities.limits;
     return true;
 }
 
@@ -75,10 +77,9 @@
     properties.backendType = WGPUBackendType_Metal;
 }
 
-bool Adapter::hasFeature(WGPUFeatureName)
+bool Adapter::hasFeature(WGPUFeatureName feature)
 {
-    // We support no optional features right now.
-    return false;
+    return std::find(m_capabilities.features.begin(), m_capabilities.features.end(), feature);
 }
 
 void Adapter::requestDevice(const WGPUDeviceDescriptor& descriptor, CompletionHandler<void(WGPURequestDeviceStatus, Ref<Device>&&, String&&)>&& callback)
@@ -97,7 +98,7 @@
         return;
     }
 
-    auto limits = m_limits;
+    WGPULimits limits { };
 
     if (descriptor.requiredLimits) {
         if (descriptor.requiredLimits->nextInChain) {
@@ -114,7 +115,7 @@
             return;
         }
 
-        if (anyLimitIsBetterThan(descriptor.requiredLimits->limits, m_limits)) {
+        if (anyLimitIsBetterThan(descriptor.requiredLimits->limits, m_capabilities.limits)) {
             instance().scheduleWork([strongThis = Ref { *this }, callback = WTFMove(callback)]() mutable {
                 callback(WGPURequestDeviceStatus_Error, Device::createInvalid(strongThis), "Device does not support requested limits"_s);
             });
@@ -122,11 +123,26 @@
         }
 
         limits = descriptor.requiredLimits->limits;
+    } else
+        limits = defaultLimits();
+
+    auto features = Vector { descriptor.requiredFeatures, descriptor.requiredFeaturesCount };
+    if (includesUnsupportedFeatures(features, m_capabilities.features)) {
+        instance().scheduleWork([strongThis = Ref { *this }, callback = WTFMove(callback)]() mutable {
+            callback(WGPURequestDeviceStatus_Error, Device::createInvalid(strongThis), "Device does not support requested features"_s);
+        });
+        return;
     }
 
+    HardwareCapabilities capabilities {
+        limits,
+        WTFMove(features),
+        m_capabilities.baseCapabilities,
+    };
+
     auto label = fromAPI(descriptor.label);
-    instance().scheduleWork([strongThis = Ref { *this }, label = WTFMove(label), limits = WTFMove(limits), callback = WTFMove(callback)]() mutable {
-        callback(WGPURequestDeviceStatus_Success, Device::create(strongThis->m_device, WTFMove(label), WTFMove(limits), strongThis), { });
+    instance().scheduleWork([strongThis = Ref { *this }, label = WTFMove(label), capabilities = WTFMove(capabilities), callback = WTFMove(callback)]() mutable {
+        callback(WGPURequestDeviceStatus_Success, Device::create(strongThis->m_device, WTFMove(label), WTFMove(capabilities), strongThis), { });
     });
 }
 

Modified: trunk/Source/WebGPU/WebGPU/Device.h (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Device.h	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Device.h	2022-04-20 16:10:11 UTC (rev 293098)
@@ -26,6 +26,7 @@
 #pragma once
 
 #import "Adapter.h"
+#import "HardwareCapabilities.h"
 #import "Queue.h"
 #import <wtf/CompletionHandler.h>
 #import <wtf/FastMalloc.h>
@@ -61,7 +62,7 @@
 class Device : public WGPUDeviceImpl, public ThreadSafeRefCounted<Device>, public CanMakeWeakPtr<Device> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<Device> create(id<MTLDevice>, String&& deviceLabel, const WGPULimits&, Adapter&);
+    static Ref<Device> create(id<MTLDevice>, String&& deviceLabel, HardwareCapabilities&&, Adapter&);
     static Ref<Device> createInvalid(Adapter& adapter)
     {
         return adoptRef(*new Device(adapter));
@@ -97,6 +98,9 @@
 
     bool isValid() const { return m_device; }
     bool isLost() const { return m_isLost; }
+    const WGPULimits& limits() const { return m_capabilities.limits; }
+    const Vector<WGPUFeatureName>& features() const { return m_capabilities.features; }
+    const HardwareCapabilities::BaseCapabilities& baseCapabilities() const { return m_capabilities.baseCapabilities; }
 
     id<MTLDevice> device() const { return m_device; }
 
@@ -106,7 +110,7 @@
     bool hasUnifiedMemory() const { return m_device.hasUnifiedMemory; }
 
 private:
-    Device(id<MTLDevice>, id<MTLCommandQueue> defaultQueue, const WGPULimits&, Adapter&);
+    Device(id<MTLDevice>, id<MTLCommandQueue> defaultQueue, HardwareCapabilities&&, Adapter&);
     Device(Adapter&);
 
     struct ErrorScope;
@@ -138,7 +142,7 @@
     bool m_isLost { false };
     id<NSObject> m_deviceObserver { nil };
 
-    WGPULimits m_limits { };
+    HardwareCapabilities m_capabilities { };
 
     const Ref<Adapter> m_adapter;
 };

Modified: trunk/Source/WebGPU/WebGPU/Device.mm (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Device.mm	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Device.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -42,12 +42,13 @@
 #import "Surface.h"
 #import "SwapChain.h"
 #import "Texture.h"
+#import <algorithm>
 #import <wtf/StdLibExtras.h>
 #import <wtf/WeakPtr.h>
 
 namespace WebGPU {
 
-Ref<Device> Device::create(id<MTLDevice> device, String&& deviceLabel, const WGPULimits& limits, Adapter& adapter)
+Ref<Device> Device::create(id<MTLDevice> device, String&& deviceLabel, HardwareCapabilities&& capabilities, Adapter& adapter)
 {
     id<MTLCommandQueue> commandQueue = [device newCommandQueue];
     if (!commandQueue)
@@ -59,13 +60,13 @@
     if (!deviceLabel.isEmpty())
         commandQueue.label = [NSString stringWithFormat:@"Default queue for device %s", deviceLabel.utf8().data()];
 
-    return adoptRef(*new Device(device, commandQueue, limits, adapter));
+    return adoptRef(*new Device(device, commandQueue, WTFMove(capabilities), adapter));
 }
 
-Device::Device(id<MTLDevice> device, id<MTLCommandQueue> defaultQueue, const WGPULimits& limits, Adapter& adapter)
+Device::Device(id<MTLDevice> device, id<MTLCommandQueue> defaultQueue, HardwareCapabilities&& capabilities, Adapter& adapter)
     : m_device(device)
     , m_defaultQueue(Queue::create(defaultQueue, *this))
-    , m_limits(limits)
+    , m_capabilities(WTFMove(capabilities))
     , m_adapter(adapter)
 {
 #if PLATFORM(MAC)
@@ -137,10 +138,13 @@
     loseTheDevice(WGPUDeviceLostReason_Destroyed);
 }
 
-size_t Device::enumerateFeatures(WGPUFeatureName*)
+size_t Device::enumerateFeatures(WGPUFeatureName* features)
 {
-    // We support no optional features right now.
-    return 0;
+    // The API contract for this requires that sufficient space has already been allocated for the output.
+    // This requires the caller calling us twice: once to get the amount of space to allocate, and once to fill the space.
+    if (features)
+        std::copy(m_capabilities.features.begin(), m_capabilities.features.end(), features);
+    return m_capabilities.features.size();
 }
 
 bool Device::getLimits(WGPUSupportedLimits& limits)
@@ -148,8 +152,7 @@
     if (limits.nextInChain != nullptr)
         return false;
 
-    // FIXME: Implement this.
-    limits.limits = { };
+    limits.limits = m_capabilities.limits;
     return true;
 }
 
@@ -158,10 +161,9 @@
     return m_defaultQueue;
 }
 
-bool Device::hasFeature(WGPUFeatureName)
+bool Device::hasFeature(WGPUFeatureName feature)
 {
-    // We support no optional features right now.
-    return false;
+    return std::find(m_capabilities.features.begin(), m_capabilities.features.end(), feature);
 }
 
 auto Device::currentErrorScope(WGPUErrorFilter type) -> ErrorScope*

Copied: trunk/Source/WebGPU/WebGPU/HardwareCapabilities.h (from rev 293096, trunk/Source/WebGPU/WebGPU/HardwareLimits.h) (0 => 293098)


--- trunk/Source/WebGPU/WebGPU/HardwareCapabilities.h	                        (rev 0)
+++ trunk/Source/WebGPU/WebGPU/HardwareCapabilities.h	2022-04-20 16:10:11 UTC (rev 293098)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021-2022 Apple Inc. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <wtf/Vector.h>
+
+namespace WebGPU {
+
+struct HardwareCapabilities {
+    WGPULimits limits { };
+    Vector<WGPUFeatureName> features;
+
+    struct BaseCapabilities {
+        MTLArgumentBuffersTier argumentBuffersTier { MTLArgumentBuffersTier1 };
+        bool supportsNonPrivateDepthStencilTextures { false };
+        id<MTLCounterSet> timestampCounterSet { nil };
+        id<MTLCounterSet> statisticCounterSet { nil };
+    } baseCapabilities;
+};
+
+std::optional<HardwareCapabilities> hardwareCapabilities(id<MTLDevice>);
+bool isValid(const WGPULimits&);
+WGPULimits defaultLimits();
+bool anyLimitIsBetterThan(const WGPULimits& target, const WGPULimits& reference);
+bool includesUnsupportedFeatures(const Vector<WGPUFeatureName>& target, const Vector<WGPUFeatureName>& reference);
+
+} // namespace WebGPU

Added: trunk/Source/WebGPU/WebGPU/HardwareCapabilities.mm (0 => 293098)


--- trunk/Source/WebGPU/WebGPU/HardwareCapabilities.mm	                        (rev 0)
+++ trunk/Source/WebGPU/WebGPU/HardwareCapabilities.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2022 Apple Inc. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#import "config.h"
+#import "HardwareCapabilities.h"
+
+#import <algorithm>
+#import <limits>
+#import <wtf/MathExtras.h>
+#import <wtf/PageBlock.h>
+#import <wtf/StdLibExtras.h>
+
+namespace WebGPU {
+
+// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
+
+// FIXME: https://github.com/gpuweb/gpuweb/issues/2749 we need more limits.
+
+static HardwareCapabilities::BaseCapabilities baseCapabilities(id<MTLDevice> device)
+{
+    id<MTLCounterSet> timestampCounterSet = nil;
+    id<MTLCounterSet> statisticCounterSet = nil;
+
+    for (id<MTLCounterSet> counterSet in device.counterSets) {
+        if ([counterSet.name isEqualToString:MTLCommonCounterSetTimestamp])
+            timestampCounterSet = counterSet;
+        else if ([counterSet.name isEqualToString:MTLCommonCounterSetStatistic])
+            statisticCounterSet = counterSet;
+    }
+
+    return {
+        [device argumentBuffersSupport],
+        false, // To be filled in by the caller.
+        timestampCounterSet,
+        statisticCounterSet,
+    };
+}
+
+static Vector<WGPUFeatureName> baseFeatures(id<MTLDevice> device, const HardwareCapabilities::BaseCapabilities& baseCapabilities)
+{
+    Vector<WGPUFeatureName> features;
+
+    // FIXME: Determine if WGPUFeatureName_DepthClipControl is supported.
+
+#if PLATFORM(MAC) || PLATFORM(MACCATALYST)
+    if (device.depth24Stencil8PixelFormatSupported)
+        features.append(WGPUFeatureName_Depth24UnormStencil8);
+#else
+    UNUSED_PARAM(device);
+#endif
+
+    features.append(WGPUFeatureName_Depth32FloatStencil8);
+
+    if (baseCapabilities.timestampCounterSet)
+        features.append(WGPUFeatureName_TimestampQuery);
+
+    if (baseCapabilities.statisticCounterSet)
+        features.append(WGPUFeatureName_PipelineStatisticsQuery);
+
+#if PLATFORM(MAC)
+    if (device.supportsBCTextureCompression)
+        features.append(WGPUFeatureName_TextureCompressionBC);
+#endif
+
+    // WGPUFeatureName_TextureCompressionETC2 and WGPUFeatureName_TextureCompressionASTC are to be filled in by the caller.
+
+    // FIXME: Determine if WGPUFeatureName_IndirectFirstInstance is supported.
+
+    return features;
+}
+
+static HardwareCapabilities apple3(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = true;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    features.append(WGPUFeatureName_TextureCompressionETC2);
+    features.append(WGPUFeatureName_TextureCompressionASTC);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * 31,
+            /* maxSamplersPerShaderStage */    maxBindGroups * 16,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * 31,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * 31,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * 31,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    4,
+            /* minStorageBufferOffsetAlignment */    4,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    60,
+            /* maxComputeWorkgroupStorageSize */    16 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    512,
+            /* maxComputeWorkgroupSizeX */    512,
+            /* maxComputeWorkgroupSizeY */    512,
+            /* maxComputeWorkgroupSizeZ */    512,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+
+static HardwareCapabilities apple4(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = true;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    features.append(WGPUFeatureName_TextureCompressionETC2);
+    features.append(WGPUFeatureName_TextureCompressionASTC);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * 96,
+            /* maxSamplersPerShaderStage */    maxBindGroups * 16,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * 96,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * 96,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * 96,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    4,
+            /* minStorageBufferOffsetAlignment */    4,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    124,
+            /* maxComputeWorkgroupStorageSize */    32 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    1024,
+            /* maxComputeWorkgroupSizeX */    1024,
+            /* maxComputeWorkgroupSizeY */    1024,
+            /* maxComputeWorkgroupSizeZ */    1024,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+
+static HardwareCapabilities apple5(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = true;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    features.append(WGPUFeatureName_TextureCompressionETC2);
+    features.append(WGPUFeatureName_TextureCompressionASTC);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * 96,
+            /* maxSamplersPerShaderStage */    maxBindGroups * 16,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * 96,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * 96,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * 96,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    4,
+            /* minStorageBufferOffsetAlignment */    4,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    124,
+            /* maxComputeWorkgroupStorageSize */    32 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    1024,
+            /* maxComputeWorkgroupSizeX */    1024,
+            /* maxComputeWorkgroupSizeY */    1024,
+            /* maxComputeWorkgroupSizeZ */    1024,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+
+#if !PLATFORM(WATCHOS)
+static HardwareCapabilities apple6(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = true;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    features.append(WGPUFeatureName_TextureCompressionETC2);
+    features.append(WGPUFeatureName_TextureCompressionASTC);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxSamplersPerShaderStage */    maxBindGroups * 1024,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    4,
+            /* minStorageBufferOffsetAlignment */    4,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    124,
+            /* maxComputeWorkgroupStorageSize */    32 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    1024,
+            /* maxComputeWorkgroupSizeX */    1024,
+            /* maxComputeWorkgroupSizeY */    1024,
+            /* maxComputeWorkgroupSizeZ */    1024,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+
+static HardwareCapabilities apple7(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = true;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    features.append(WGPUFeatureName_TextureCompressionETC2);
+    features.append(WGPUFeatureName_TextureCompressionASTC);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxSamplersPerShaderStage */    maxBindGroups * 1024,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    4,
+            /* minStorageBufferOffsetAlignment */    4,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    124,
+            /* maxComputeWorkgroupStorageSize */    32 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    1024,
+            /* maxComputeWorkgroupSizeX */    1024,
+            /* maxComputeWorkgroupSizeY */    1024,
+            /* maxComputeWorkgroupSizeZ */    1024,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+#endif
+
+static HardwareCapabilities mac2(id<MTLDevice> device)
+{
+    auto baseCapabilities = WebGPU::baseCapabilities(device);
+
+    baseCapabilities.supportsNonPrivateDepthStencilTextures = false;
+
+    auto features = WebGPU::baseFeatures(device, baseCapabilities);
+
+    std::sort(features.begin(), features.end());
+
+    uint32_t buffersPerBindGroup = 0;
+    uint32_t texturesPerBindGroup = 0;
+    uint32_t samplersPerBindGroup = 0;
+    switch (baseCapabilities.argumentBuffersTier) {
+    case MTLArgumentBuffersTier1:
+        buffersPerBindGroup = 64;
+        texturesPerBindGroup = 128;
+        samplersPerBindGroup = 16;
+        break;
+    case MTLArgumentBuffersTier2:
+        buffersPerBindGroup = 500000 / 2;
+        texturesPerBindGroup = 500000 / 2;
+        samplersPerBindGroup = 1024;
+        break;
+    }
+
+    uint32_t maxBindGroups = 30;
+
+    return {
+        {
+            /* maxTextureDimension1D */    16384,
+            /* maxTextureDimension2D */    16384,
+            /* maxTextureDimension3D */    2048,
+            /* maxTextureArrayLayers */    2048,
+            /* maxBindGroups */    maxBindGroups,
+            /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
+            /* maxSampledTexturesPerShaderStage */    maxBindGroups * texturesPerBindGroup,
+            /* maxSamplersPerShaderStage */    maxBindGroups * samplersPerBindGroup,
+            /* maxStorageBuffersPerShaderStage */    maxBindGroups * buffersPerBindGroup,
+            /* maxStorageTexturesPerShaderStage */    maxBindGroups * texturesPerBindGroup,
+            /* maxUniformBuffersPerShaderStage */    maxBindGroups * buffersPerBindGroup,
+            /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
+            /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
+            /* minUniformBufferOffsetAlignment */    256,
+            /* minStorageBufferOffsetAlignment */    256,
+            /* maxVertexBuffers */    30,
+            /* maxVertexAttributes */    31,
+            /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
+            /* maxInterStageShaderComponents */    32,
+            /* maxComputeWorkgroupStorageSize */    32 * KB,
+            /* maxComputeInvocationsPerWorkgroup */    1024,
+            /* maxComputeWorkgroupSizeX */    1024,
+            /* maxComputeWorkgroupSizeY */    1024,
+            /* maxComputeWorkgroupSizeZ */    1024,
+            /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
+        },
+        WTFMove(features),
+        baseCapabilities,
+    };
+}
+
+template <typename T>
+static T mergeMaximum(T previous, T next)
+{
+    // https://gpuweb.github.io/gpuweb/#limit-class-maximum
+    return std::max(previous, next);
+};
+
+template <typename T>
+static T mergeAlignment(T previous, T next)
+{
+    // https://gpuweb.github.io/gpuweb/#limit-class-alignment
+    return std::min(WTF::roundUpToPowerOfTwo(previous), WTF::roundUpToPowerOfTwo(next));
+};
+
+static WGPULimits mergeLimits(const WGPULimits& previous, const WGPULimits& next)
+{
+    return {
+        mergeMaximum(previous.maxTextureDimension1D, next.maxTextureDimension1D),
+        mergeMaximum(previous.maxTextureDimension2D, next.maxTextureDimension2D),
+        mergeMaximum(previous.maxTextureDimension3D, next.maxTextureDimension3D),
+        mergeMaximum(previous.maxTextureArrayLayers, next.maxTextureArrayLayers),
+        mergeMaximum(previous.maxBindGroups, next.maxBindGroups),
+        mergeMaximum(previous.maxDynamicUniformBuffersPerPipelineLayout, next.maxDynamicUniformBuffersPerPipelineLayout),
+        mergeMaximum(previous.maxDynamicStorageBuffersPerPipelineLayout, next.maxDynamicStorageBuffersPerPipelineLayout),
+        mergeMaximum(previous.maxSampledTexturesPerShaderStage, next.maxSampledTexturesPerShaderStage),
+        mergeMaximum(previous.maxSamplersPerShaderStage, next.maxSamplersPerShaderStage),
+        mergeMaximum(previous.maxStorageBuffersPerShaderStage, next.maxStorageBuffersPerShaderStage),
+        mergeMaximum(previous.maxStorageTexturesPerShaderStage, next.maxStorageTexturesPerShaderStage),
+        mergeMaximum(previous.maxUniformBuffersPerShaderStage, next.maxUniformBuffersPerShaderStage),
+        mergeMaximum(previous.maxUniformBufferBindingSize, next.maxUniformBufferBindingSize),
+        mergeMaximum(previous.maxStorageBufferBindingSize, next.maxStorageBufferBindingSize),
+        mergeAlignment(previous.minUniformBufferOffsetAlignment, next.minUniformBufferOffsetAlignment),
+        mergeAlignment(previous.minStorageBufferOffsetAlignment, next.minStorageBufferOffsetAlignment),
+        mergeMaximum(previous.maxVertexBuffers, next.maxVertexBuffers),
+        mergeMaximum(previous.maxVertexAttributes, next.maxVertexAttributes),
+        mergeMaximum(previous.maxVertexBufferArrayStride, next.maxVertexBufferArrayStride),
+        mergeMaximum(previous.maxInterStageShaderComponents, next.maxInterStageShaderComponents),
+        mergeMaximum(previous.maxComputeWorkgroupStorageSize, next.maxComputeWorkgroupStorageSize),
+        mergeMaximum(previous.maxComputeInvocationsPerWorkgroup, next.maxComputeInvocationsPerWorkgroup),
+        mergeMaximum(previous.maxComputeWorkgroupSizeX, next.maxComputeWorkgroupSizeX),
+        mergeMaximum(previous.maxComputeWorkgroupSizeY, next.maxComputeWorkgroupSizeY),
+        mergeMaximum(previous.maxComputeWorkgroupSizeZ, next.maxComputeWorkgroupSizeZ),
+        mergeMaximum(previous.maxComputeWorkgroupsPerDimension, next.maxComputeWorkgroupsPerDimension),
+    };
+};
+
+static Vector<WGPUFeatureName> mergeFeatures(const Vector<WGPUFeatureName>& previous, const Vector<WGPUFeatureName>& next)
+{
+    ASSERT(WTF::isSortedConstExpr(previous.begin(), previous.end()));
+    ASSERT(WTF::isSortedConstExpr(next.begin(), next.end()));
+
+    Vector<WGPUFeatureName> result(previous.size() + next.size());
+    auto end = mergeDeduplicatedSorted(previous.begin(), previous.end(), next.begin(), next.end(), result.begin());
+    result.resize(end - result.begin());
+    return result;
+}
+
+static HardwareCapabilities::BaseCapabilities mergeBaseCapabilities(const HardwareCapabilities::BaseCapabilities& previous, const HardwareCapabilities::BaseCapabilities& next)
+{
+    ASSERT(previous.argumentBuffersTier == next.argumentBuffersTier);
+    ASSERT(!previous.timestampCounterSet || [previous.timestampCounterSet isEqual:next.timestampCounterSet]);
+    ASSERT(!previous.statisticCounterSet || [previous.statisticCounterSet isEqual:next.statisticCounterSet]);
+    return {
+        previous.argumentBuffersTier,
+        previous.supportsNonPrivateDepthStencilTextures || next.supportsNonPrivateDepthStencilTextures,
+        previous.timestampCounterSet,
+        previous.statisticCounterSet,
+    };
+}
+
+static std::optional<HardwareCapabilities> rawHardwareCapabilities(id<MTLDevice> device)
+{
+    std::optional<HardwareCapabilities> result;
+
+    auto merge = [&](const HardwareCapabilities& capabilities) {
+        if (!result) {
+            result = capabilities;
+            return;
+        }
+
+        result->limits = mergeLimits(result->limits, capabilities.limits);
+        result->features = mergeFeatures(result->features, capabilities.features);
+        result->baseCapabilities = mergeBaseCapabilities(result->baseCapabilities, capabilities.baseCapabilities);
+    };
+
+    // The feature set tables do not list limits for MTLGPUFamilyCommon1, MTLGPUFamilyCommon2, or MTLGPUFamilyCommon3.
+    // MTLGPUFamilyApple1 and MTLGPUFamilyApple2 are not supported.
+    if ([device supportsFamily:MTLGPUFamilyApple3])
+        merge(apple3(device));
+    if ([device supportsFamily:MTLGPUFamilyApple4])
+        merge(apple4(device));
+    if ([device supportsFamily:MTLGPUFamilyApple5])
+        merge(apple5(device));
+#if !PLATFORM(WATCHOS)
+    if ([device supportsFamily:MTLGPUFamilyApple6])
+        merge(apple6(device));
+    if ([device supportsFamily:MTLGPUFamilyApple7])
+        merge(apple7(device));
+#endif
+    // MTLGPUFamilyMac1 is not supported (yet?).
+    if ([device supportsFamily:MTLGPUFamilyMac2])
+        merge(mac2(device));
+
+    if (result) {
+        auto maxBufferLength = device.maxBufferLength;
+        result->limits.maxUniformBufferBindingSize = maxBufferLength;
+        result->limits.maxStorageBufferBindingSize = maxBufferLength;
+    }
+
+    return result;
+}
+
+bool anyLimitIsBetterThan(const WGPULimits& target, const WGPULimits& reference)
+{
+    if (target.maxTextureDimension1D > reference.maxTextureDimension1D)
+        return true;
+    if (target.maxTextureDimension2D > reference.maxTextureDimension2D)
+        return true;
+    if (target.maxTextureDimension3D > reference.maxTextureDimension3D)
+        return true;
+    if (target.maxTextureArrayLayers > reference.maxTextureArrayLayers)
+        return true;
+    if (target.maxBindGroups > reference.maxBindGroups)
+        return true;
+    if (target.maxDynamicUniformBuffersPerPipelineLayout > reference.maxDynamicUniformBuffersPerPipelineLayout)
+        return true;
+    if (target.maxDynamicStorageBuffersPerPipelineLayout > reference.maxDynamicStorageBuffersPerPipelineLayout)
+        return true;
+    if (target.maxSampledTexturesPerShaderStage > reference.maxSampledTexturesPerShaderStage)
+        return true;
+    if (target.maxSamplersPerShaderStage > reference.maxSamplersPerShaderStage)
+        return true;
+    if (target.maxStorageBuffersPerShaderStage > reference.maxStorageBuffersPerShaderStage)
+        return true;
+    if (target.maxStorageTexturesPerShaderStage > reference.maxStorageTexturesPerShaderStage)
+        return true;
+    if (target.maxUniformBuffersPerShaderStage > reference.maxUniformBuffersPerShaderStage)
+        return true;
+    if (target.maxUniformBufferBindingSize > reference.maxUniformBufferBindingSize)
+        return true;
+    if (target.maxStorageBufferBindingSize > reference.maxStorageBufferBindingSize)
+        return true;
+    if (target.minUniformBufferOffsetAlignment < reference.minUniformBufferOffsetAlignment)
+        return true;
+    if (target.minStorageBufferOffsetAlignment < reference.minStorageBufferOffsetAlignment)
+        return true;
+    if (target.maxVertexBuffers > reference.maxVertexBuffers)
+        return true;
+    if (target.maxVertexAttributes > reference.maxVertexAttributes)
+        return true;
+    if (target.maxVertexBufferArrayStride > reference.maxVertexBufferArrayStride)
+        return true;
+    if (target.maxInterStageShaderComponents > reference.maxInterStageShaderComponents)
+        return true;
+    if (target.maxComputeWorkgroupStorageSize > reference.maxComputeWorkgroupStorageSize)
+        return true;
+    if (target.maxComputeInvocationsPerWorkgroup > reference.maxComputeInvocationsPerWorkgroup)
+        return true;
+    if (target.maxComputeWorkgroupSizeX > reference.maxComputeWorkgroupSizeX)
+        return true;
+    if (target.maxComputeWorkgroupSizeY > reference.maxComputeWorkgroupSizeY)
+        return true;
+    if (target.maxComputeWorkgroupSizeZ > reference.maxComputeWorkgroupSizeZ)
+        return true;
+    if (target.maxComputeWorkgroupsPerDimension > reference.maxComputeWorkgroupsPerDimension)
+        return true;
+
+    return false;
+}
+
+bool includesUnsupportedFeatures(const Vector<WGPUFeatureName>& target, const Vector<WGPUFeatureName>& reference)
+{
+    ASSERT(WTF::isSortedConstExpr(target.begin(), target.end()));
+    ASSERT(WTF::isSortedConstExpr(reference.begin(), reference.end()));
+    for (auto feature : target) {
+        if (!std::find(reference.begin(), reference.end(), feature))
+            return true;
+    }
+    return false;
+}
+
+WGPULimits defaultLimits()
+{
+    // https://gpuweb.github.io/gpuweb/#limit-default
+
+    return {
+        /* maxTextureDimension1D */    8192,
+        /* maxTextureDimension2D */    8192,
+        /* maxTextureDimension3D */    2048,
+        /* maxTextureArrayLayers */    256,
+        /* maxBindGroups */    4,
+        /* maxDynamicUniformBuffersPerPipelineLayout */    8,
+        /* maxDynamicStorageBuffersPerPipelineLayout */    4,
+        /* maxSampledTexturesPerShaderStage */    16,
+        /* maxSamplersPerShaderStage */    16,
+        /* maxStorageBuffersPerShaderStage */    8,
+        /* maxStorageTexturesPerShaderStage */    4,
+        /* maxUniformBuffersPerShaderStage */    12,
+        /* maxUniformBufferBindingSize */    65536,
+        /* maxStorageBufferBindingSize */    134217728,
+        /* minUniformBufferOffsetAlignment */    256,
+        /* minStorageBufferOffsetAlignment */    256,
+        /* maxVertexBuffers */    8,
+        /* maxVertexAttributes */    16,
+        /* maxVertexBufferArrayStride */    2048,
+        /* maxInterStageShaderComponents */    32,
+        /* maxComputeWorkgroupStorageSize */    16352,
+        /* maxComputeInvocationsPerWorkgroup */    256,
+        /* maxComputeWorkgroupSizeX */    256,
+        /* maxComputeWorkgroupSizeY */    256,
+        /* maxComputeWorkgroupSizeZ */    64,
+        /* maxComputeWorkgroupsPerDimension */    65535,
+    };
+}
+
+std::optional<HardwareCapabilities> hardwareCapabilities(id<MTLDevice> device)
+{
+    auto result = rawHardwareCapabilities(device);
+
+    if (!result)
+        return std::nullopt;
+
+    if (anyLimitIsBetterThan(defaultLimits(), result->limits))
+        return std::nullopt;
+
+    return result;
+}
+
+bool isValid(const WGPULimits& limits)
+{
+    return isPowerOfTwo(limits.minUniformBufferOffsetAlignment) && isPowerOfTwo(limits.minStorageBufferOffsetAlignment);
+}
+
+} // namespace WebGPU

Deleted: trunk/Source/WebGPU/WebGPU/HardwareLimits.h (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/HardwareLimits.h	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/HardwareLimits.h	2022-04-20 16:10:11 UTC (rev 293098)
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2021-2022 Apple Inc. All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#pragma once
-
-#include <optional>
-
-namespace WebGPU {
-
-std::optional<WGPULimits> limits(id<MTLDevice>);
-bool isValid(const WGPULimits&);
-bool anyLimitIsBetterThan(const WGPULimits& target, const WGPULimits& reference);
-
-} // namespace WebGPU

Deleted: trunk/Source/WebGPU/WebGPU/HardwareLimits.mm (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/HardwareLimits.mm	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/HardwareLimits.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -1,449 +0,0 @@
-/*
- * Copyright (c) 2022 Apple Inc. All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#import "config.h"
-#import "HardwareLimits.h"
-
-#import <algorithm>
-#import <limits>
-#import <wtf/MathExtras.h>
-#import <wtf/PageBlock.h>
-#import <wtf/StdLibExtras.h>
-
-namespace WebGPU {
-
-// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
-
-// FIXME: https://github.com/gpuweb/gpuweb/issues/2749 we need more limits.
-
-constexpr static WGPULimits apple3()
-{
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * 31,
-        /* maxSamplersPerShaderStage */    maxBindGroups * 16,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * 31,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * 31,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * 31,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    4,
-        /* minStorageBufferOffsetAlignment */    4,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    60,
-        /* maxComputeWorkgroupStorageSize */    16 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    512,
-        /* maxComputeWorkgroupSizeX */    512,
-        /* maxComputeWorkgroupSizeY */    512,
-        /* maxComputeWorkgroupSizeZ */    512,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-
-constexpr static WGPULimits apple4()
-{
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * 96,
-        /* maxSamplersPerShaderStage */    maxBindGroups * 16,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * 96,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * 96,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * 96,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    4,
-        /* minStorageBufferOffsetAlignment */    4,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    124,
-        /* maxComputeWorkgroupStorageSize */    32 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    1024,
-        /* maxComputeWorkgroupSizeX */    1024,
-        /* maxComputeWorkgroupSizeY */    1024,
-        /* maxComputeWorkgroupSizeZ */    1024,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-
-constexpr static WGPULimits apple5()
-{
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * 96,
-        /* maxSamplersPerShaderStage */    maxBindGroups * 16,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * 96,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * 96,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * 96,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    4,
-        /* minStorageBufferOffsetAlignment */    4,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    124,
-        /* maxComputeWorkgroupStorageSize */    32 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    1024,
-        /* maxComputeWorkgroupSizeX */    1024,
-        /* maxComputeWorkgroupSizeY */    1024,
-        /* maxComputeWorkgroupSizeZ */    1024,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-
-#if !PLATFORM(WATCHOS)
-constexpr static WGPULimits apple6()
-{
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxSamplersPerShaderStage */    maxBindGroups * 1024,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    4,
-        /* minStorageBufferOffsetAlignment */    4,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    124,
-        /* maxComputeWorkgroupStorageSize */    32 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    1024,
-        /* maxComputeWorkgroupSizeX */    1024,
-        /* maxComputeWorkgroupSizeY */    1024,
-        /* maxComputeWorkgroupSizeZ */    1024,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-
-constexpr static WGPULimits apple7()
-{
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxSamplersPerShaderStage */    maxBindGroups * 1024,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * 500000 / 2,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    4,
-        /* minStorageBufferOffsetAlignment */    4,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    124,
-        /* maxComputeWorkgroupStorageSize */    32 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    1024,
-        /* maxComputeWorkgroupSizeX */    1024,
-        /* maxComputeWorkgroupSizeY */    1024,
-        /* maxComputeWorkgroupSizeZ */    1024,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-#endif
-
-static WGPULimits mac2(id<MTLDevice> device)
-{
-    uint32_t buffersPerBindGroup = 0;
-    uint32_t texturesPerBindGroup = 0;
-    uint32_t samplersPerBindGroup = 0;
-    switch ([device argumentBuffersSupport]) {
-    case MTLArgumentBuffersTier1:
-        buffersPerBindGroup = 64;
-        texturesPerBindGroup = 128;
-        samplersPerBindGroup = 16;
-        break;
-    case MTLArgumentBuffersTier2:
-        buffersPerBindGroup = 500000 / 2;
-        texturesPerBindGroup = 500000 / 2;
-        samplersPerBindGroup = 1024;
-        break;
-    }
-
-    uint32_t maxBindGroups = 30;
-
-    return WGPULimits {
-        /* maxTextureDimension1D */    16384,
-        /* maxTextureDimension2D */    16384,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    2048,
-        /* maxBindGroups */    maxBindGroups,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxDynamicStorageBuffersPerPipelineLayout */    std::numeric_limits<uint32_t>::max(),
-        /* maxSampledTexturesPerShaderStage */    maxBindGroups * texturesPerBindGroup,
-        /* maxSamplersPerShaderStage */    maxBindGroups * samplersPerBindGroup,
-        /* maxStorageBuffersPerShaderStage */    maxBindGroups * buffersPerBindGroup,
-        /* maxStorageTexturesPerShaderStage */    maxBindGroups * texturesPerBindGroup,
-        /* maxUniformBuffersPerShaderStage */    maxBindGroups * buffersPerBindGroup,
-        /* maxUniformBufferBindingSize */    0, // To be filled in by the caller.
-        /* maxStorageBufferBindingSize */    0, // To be filled in by the caller.
-        /* minUniformBufferOffsetAlignment */    256,
-        /* minStorageBufferOffsetAlignment */    256,
-        /* maxVertexBuffers */    30,
-        /* maxVertexAttributes */    31,
-        /* maxVertexBufferArrayStride */    std::numeric_limits<uint32_t>::max(),
-        /* maxInterStageShaderComponents */    32,
-        /* maxComputeWorkgroupStorageSize */    32 * KB,
-        /* maxComputeInvocationsPerWorkgroup */    1024,
-        /* maxComputeWorkgroupSizeX */    1024,
-        /* maxComputeWorkgroupSizeY */    1024,
-        /* maxComputeWorkgroupSizeZ */    1024,
-        /* maxComputeWorkgroupsPerDimension */    std::numeric_limits<uint32_t>::max(),
-    };
-}
-
-static std::optional<WGPULimits> rawLimits(id<MTLDevice> device)
-{
-    std::optional<WGPULimits> result;
-
-    auto merge = [&](const WGPULimits& limits) {
-        if (!result) {
-            result = limits;
-            return;
-        }
-
-        // https://gpuweb.github.io/gpuweb/#limit-class-maximum
-        auto mergeMaximum = [](auto previous, auto next) {
-            return std::max(previous, next);
-        };
-
-        // https://gpuweb.github.io/gpuweb/#limit-class-alignment
-        auto mergeAlignment = [](auto previous, auto next) {
-            return std::min(WTF::roundUpToPowerOfTwo(previous), WTF::roundUpToPowerOfTwo(next));
-        };
-
-        result->maxTextureDimension1D = mergeMaximum(result->maxTextureDimension1D, limits.maxTextureDimension1D);
-        result->maxTextureDimension2D = mergeMaximum(result->maxTextureDimension2D, limits.maxTextureDimension2D);
-        result->maxTextureDimension3D = mergeMaximum(result->maxTextureDimension3D, limits.maxTextureDimension3D);
-        result->maxTextureArrayLayers = mergeMaximum(result->maxTextureArrayLayers, limits.maxTextureArrayLayers);
-        result->maxBindGroups = mergeMaximum(result->maxBindGroups, limits.maxBindGroups);
-        result->maxDynamicUniformBuffersPerPipelineLayout = mergeMaximum(result->maxDynamicUniformBuffersPerPipelineLayout, limits.maxDynamicUniformBuffersPerPipelineLayout);
-        result->maxDynamicStorageBuffersPerPipelineLayout = mergeMaximum(result->maxDynamicStorageBuffersPerPipelineLayout, limits.maxDynamicStorageBuffersPerPipelineLayout);
-        result->maxSampledTexturesPerShaderStage = mergeMaximum(result->maxSampledTexturesPerShaderStage, limits.maxSampledTexturesPerShaderStage);
-        result->maxSamplersPerShaderStage = mergeMaximum(result->maxSamplersPerShaderStage, limits.maxSamplersPerShaderStage);
-        result->maxStorageBuffersPerShaderStage = mergeMaximum(result->maxStorageBuffersPerShaderStage, limits.maxStorageBuffersPerShaderStage);
-        result->maxStorageTexturesPerShaderStage = mergeMaximum(result->maxStorageTexturesPerShaderStage, limits.maxStorageTexturesPerShaderStage);
-        result->maxUniformBuffersPerShaderStage = mergeMaximum(result->maxUniformBuffersPerShaderStage, limits.maxUniformBuffersPerShaderStage);
-        result->maxUniformBufferBindingSize = mergeMaximum(result->maxUniformBufferBindingSize, limits.maxUniformBufferBindingSize);
-        result->maxStorageBufferBindingSize = mergeMaximum(result->maxStorageBufferBindingSize, limits.maxStorageBufferBindingSize);
-        result->minUniformBufferOffsetAlignment = mergeAlignment(result->minUniformBufferOffsetAlignment, limits.minUniformBufferOffsetAlignment);
-        result->minStorageBufferOffsetAlignment = mergeAlignment(result->minStorageBufferOffsetAlignment, limits.minStorageBufferOffsetAlignment);
-        result->maxVertexBuffers = mergeMaximum(result->maxVertexBuffers, limits.maxVertexBuffers);
-        result->maxVertexAttributes = mergeMaximum(result->maxVertexAttributes, limits.maxVertexAttributes);
-        result->maxVertexBufferArrayStride = mergeMaximum(result->maxVertexBufferArrayStride, limits.maxVertexBufferArrayStride);
-        result->maxInterStageShaderComponents = mergeMaximum(result->maxInterStageShaderComponents, limits.maxInterStageShaderComponents);
-        result->maxComputeWorkgroupStorageSize = mergeMaximum(result->maxComputeWorkgroupStorageSize, limits.maxComputeWorkgroupStorageSize);
-        result->maxComputeInvocationsPerWorkgroup = mergeMaximum(result->maxComputeInvocationsPerWorkgroup, limits.maxComputeInvocationsPerWorkgroup);
-        result->maxComputeWorkgroupSizeX = mergeMaximum(result->maxComputeWorkgroupSizeX, limits.maxComputeWorkgroupSizeX);
-        result->maxComputeWorkgroupSizeY = mergeMaximum(result->maxComputeWorkgroupSizeY, limits.maxComputeWorkgroupSizeY);
-        result->maxComputeWorkgroupSizeZ = mergeMaximum(result->maxComputeWorkgroupSizeZ, limits.maxComputeWorkgroupSizeZ);
-        result->maxComputeWorkgroupsPerDimension = mergeMaximum(result->maxComputeWorkgroupsPerDimension, limits.maxComputeWorkgroupsPerDimension);
-    };
-
-    // The feature set tables do not list limits for MTLGPUFamilyCommon1, MTLGPUFamilyCommon2, or MTLGPUFamilyCommon3.
-    // MTLGPUFamilyApple1 and MTLGPUFamilyApple2 are not supported.
-    if ([device supportsFamily:MTLGPUFamilyApple3])
-        merge(apple3());
-    if ([device supportsFamily:MTLGPUFamilyApple4])
-        merge(apple4());
-    if ([device supportsFamily:MTLGPUFamilyApple5])
-        merge(apple5());
-#if !PLATFORM(WATCHOS)
-    if ([device supportsFamily:MTLGPUFamilyApple6])
-        merge(apple6());
-    if ([device supportsFamily:MTLGPUFamilyApple7])
-        merge(apple7());
-#endif
-    // MTLGPUFamilyMac1 is not supported (yet?).
-    if ([device supportsFamily:MTLGPUFamilyMac2])
-        merge(mac2(device));
-
-    if (result) {
-        auto maxBufferLength = device.maxBufferLength;
-        result->maxUniformBufferBindingSize = maxBufferLength;
-        result->maxStorageBufferBindingSize = maxBufferLength;
-    }
-
-    return result;
-}
-
-static bool checkLimits(const WGPULimits& limits)
-{
-    // https://gpuweb.github.io/gpuweb/#limit-default
-    auto defaultLimits = WGPULimits {
-        /* maxTextureDimension1D */    8192,
-        /* maxTextureDimension2D */    8192,
-        /* maxTextureDimension3D */    2048,
-        /* maxTextureArrayLayers */    256,
-        /* maxBindGroups */    4,
-        /* maxDynamicUniformBuffersPerPipelineLayout */    8,
-        /* maxDynamicStorageBuffersPerPipelineLayout */    4,
-        /* maxSampledTexturesPerShaderStage */    16,
-        /* maxSamplersPerShaderStage */    16,
-        /* maxStorageBuffersPerShaderStage */    8,
-        /* maxStorageTexturesPerShaderStage */    4,
-        /* maxUniformBuffersPerShaderStage */    12,
-        /* maxUniformBufferBindingSize */    65536,
-        /* maxStorageBufferBindingSize */    134217728,
-        /* minUniformBufferOffsetAlignment */    256,
-        /* minStorageBufferOffsetAlignment */    256,
-        /* maxVertexBuffers */    8,
-        /* maxVertexAttributes */    16,
-        /* maxVertexBufferArrayStride */    2048,
-        /* maxInterStageShaderComponents */    32,
-        /* maxComputeWorkgroupStorageSize */    16352,
-        /* maxComputeInvocationsPerWorkgroup */    256,
-        /* maxComputeWorkgroupSizeX */    256,
-        /* maxComputeWorkgroupSizeY */    256,
-        /* maxComputeWorkgroupSizeZ */    64,
-        /* maxComputeWorkgroupsPerDimension */    65535,
-    };
-
-    return !anyLimitIsBetterThan(defaultLimits, limits);
-}
-
-std::optional<WGPULimits> limits(id<MTLDevice> device)
-{
-    auto result = rawLimits(device);
-
-    if (!result)
-        return std::nullopt;
-
-    if (!checkLimits(*result))
-        return std::nullopt;
-
-    return result;
-}
-
-bool isValid(const WGPULimits& limits)
-{
-    return isPowerOfTwo(limits.minUniformBufferOffsetAlignment) && isPowerOfTwo(limits.minStorageBufferOffsetAlignment);
-}
-
-bool anyLimitIsBetterThan(const WGPULimits& target, const WGPULimits& reference)
-{
-    if (target.maxTextureDimension1D > reference.maxTextureDimension1D)
-        return true;
-    if (target.maxTextureDimension2D > reference.maxTextureDimension2D)
-        return true;
-    if (target.maxTextureDimension3D > reference.maxTextureDimension3D)
-        return true;
-    if (target.maxTextureArrayLayers > reference.maxTextureArrayLayers)
-        return true;
-    if (target.maxBindGroups > reference.maxBindGroups)
-        return true;
-    if (target.maxDynamicUniformBuffersPerPipelineLayout > reference.maxDynamicUniformBuffersPerPipelineLayout)
-        return true;
-    if (target.maxDynamicStorageBuffersPerPipelineLayout > reference.maxDynamicStorageBuffersPerPipelineLayout)
-        return true;
-    if (target.maxSampledTexturesPerShaderStage > reference.maxSampledTexturesPerShaderStage)
-        return true;
-    if (target.maxSamplersPerShaderStage > reference.maxSamplersPerShaderStage)
-        return true;
-    if (target.maxStorageBuffersPerShaderStage > reference.maxStorageBuffersPerShaderStage)
-        return true;
-    if (target.maxStorageTexturesPerShaderStage > reference.maxStorageTexturesPerShaderStage)
-        return true;
-    if (target.maxUniformBuffersPerShaderStage > reference.maxUniformBuffersPerShaderStage)
-        return true;
-    if (target.maxUniformBufferBindingSize > reference.maxUniformBufferBindingSize)
-        return true;
-    if (target.maxStorageBufferBindingSize > reference.maxStorageBufferBindingSize)
-        return true;
-    if (target.minUniformBufferOffsetAlignment < reference.minUniformBufferOffsetAlignment)
-        return true;
-    if (target.minStorageBufferOffsetAlignment < reference.minStorageBufferOffsetAlignment)
-        return true;
-    if (target.maxVertexBuffers > reference.maxVertexBuffers)
-        return true;
-    if (target.maxVertexAttributes > reference.maxVertexAttributes)
-        return true;
-    if (target.maxVertexBufferArrayStride > reference.maxVertexBufferArrayStride)
-        return true;
-    if (target.maxInterStageShaderComponents > reference.maxInterStageShaderComponents)
-        return true;
-    if (target.maxComputeWorkgroupStorageSize > reference.maxComputeWorkgroupStorageSize)
-        return true;
-    if (target.maxComputeInvocationsPerWorkgroup > reference.maxComputeInvocationsPerWorkgroup)
-        return true;
-    if (target.maxComputeWorkgroupSizeX > reference.maxComputeWorkgroupSizeX)
-        return true;
-    if (target.maxComputeWorkgroupSizeY > reference.maxComputeWorkgroupSizeY)
-        return true;
-    if (target.maxComputeWorkgroupSizeZ > reference.maxComputeWorkgroupSizeZ)
-        return true;
-    if (target.maxComputeWorkgroupsPerDimension > reference.maxComputeWorkgroupsPerDimension)
-        return true;
-
-    return false;
-}
-
-} // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/Instance.mm (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Instance.mm	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Instance.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -28,7 +28,7 @@
 
 #import "APIConversions.h"
 #import "Adapter.h"
-#import "HardwareLimits.h"
+#import "HardwareCapabilities.h"
 #import "Surface.h"
 #import <cstring>
 #import <wtf/BlockPtr.h>
@@ -186,9 +186,9 @@
 
     auto device = sortedDevices[0];
 
-    auto deviceLimits = limits(device);
+    auto deviceCapabilties = hardwareCapabilities(device);
 
-    if (!deviceLimits) {
+    if (!deviceCapabilties) {
         scheduleWork([strongThis = Ref { *this }, callback = WTFMove(callback)]() mutable {
             callback(WGPURequestAdapterStatus_Error, Adapter::createInvalid(strongThis), "Device does not support WebGPU"_s);
         });
@@ -195,8 +195,8 @@
         return;
     }
 
-    scheduleWork([strongThis = Ref { *this }, device = sortedDevices[0], limits = WTFMove(*deviceLimits), callback = WTFMove(callback)]() mutable {
-        callback(WGPURequestAdapterStatus_Success, Adapter::create(device, strongThis, WTFMove(limits)), { });
+    scheduleWork([strongThis = Ref { *this }, device = sortedDevices[0], deviceCapabilties = WTFMove(*deviceCapabilties), callback = WTFMove(callback)]() mutable {
+        callback(WGPURequestAdapterStatus_Success, Adapter::create(device, strongThis, WTFMove(deviceCapabilties)), { });
     });
 }
 

Modified: trunk/Source/WebGPU/WebGPU/Texture.mm (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU/Texture.mm	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU/Texture.mm	2022-04-20 16:10:11 UTC (rev 293098)
@@ -37,7 +37,7 @@
 static std::optional<WGPUFeatureName> featureRequirementForFormat(WGPUTextureFormat format)
 {
     switch (format) {
-    case WGPUTextureFormat_Depth24UnormStencil8: // FIXME: This has to be guarded by MTLDevice.depth24Stencil8PixelFormatSupported
+    case WGPUTextureFormat_Depth24UnormStencil8:
         return WGPUFeatureName_Depth24UnormStencil8;
     case WGPUTextureFormat_Depth32FloatStencil8:
         return WGPUFeatureName_Depth32FloatStencil8;
@@ -1246,7 +1246,7 @@
 
     switch (descriptor.dimension) {
     case WGPUTextureDimension_1D:
-        if (descriptor.size.width > m_limits.maxTextureDimension1D)
+        if (descriptor.size.width > limits().maxTextureDimension1D)
             return false;
 
         if (descriptor.size.height != 1)
@@ -1262,23 +1262,23 @@
             return false;
         break;
     case WGPUTextureDimension_2D:
-        if (descriptor.size.width > m_limits.maxTextureDimension2D)
+        if (descriptor.size.width > limits().maxTextureDimension2D)
             return false;
 
-        if (descriptor.size.height > m_limits.maxTextureDimension2D)
+        if (descriptor.size.height > limits().maxTextureDimension2D)
             return false;
 
-        if (descriptor.size.depthOrArrayLayers > m_limits.maxTextureArrayLayers)
+        if (descriptor.size.depthOrArrayLayers > limits().maxTextureArrayLayers)
             return false;
         break;
     case WGPUTextureDimension_3D:
-        if (descriptor.size.width > m_limits.maxTextureDimension3D)
+        if (descriptor.size.width > limits().maxTextureDimension3D)
             return false;
 
-        if (descriptor.size.height > m_limits.maxTextureDimension3D)
+        if (descriptor.size.height > limits().maxTextureDimension3D)
             return false;
 
-        if (descriptor.size.depthOrArrayLayers > m_limits.maxTextureDimension3D)
+        if (descriptor.size.depthOrArrayLayers > limits().maxTextureDimension3D)
             return false;
 
         if (descriptor.sampleCount != 1)
@@ -1961,8 +1961,10 @@
     }
 }
 
-static MTLStorageMode storageMode(bool deviceHasUnifiedMemory)
+static MTLStorageMode storageMode(bool deviceHasUnifiedMemory, bool supportsNonPrivateDepthStencilTextures)
 {
+    if (!supportsNonPrivateDepthStencilTextures)
+        return MTLStorageModePrivate;
     if (deviceHasUnifiedMemory)
         return MTLStorageModeShared;
 #if PLATFORM(MAC) || PLATFORM(MACCATALYST)
@@ -1987,9 +1989,11 @@
 
     // https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture
 
-    if (featureRequirementForFormat(descriptor.format)) {
-        // FIXME: "but this.[[device]].[[features]] does not contain the feature, throw a TypeError."
-        return Texture::createInvalid(*this);
+    if (auto requirement = featureRequirementForFormat(descriptor.format)) {
+        if (!hasFeature(*requirement)) {
+            // FIXME: "throw a TypeError."
+            return Texture::createInvalid(*this);
+        }
     }
 
     if (!validateCreateTexture(descriptor, viewFormats)) {
@@ -2047,7 +2051,7 @@
 
     textureDescriptor.sampleCount = descriptor.sampleCount;
 
-    textureDescriptor.storageMode = storageMode(hasUnifiedMemory());
+    textureDescriptor.storageMode = storageMode(hasUnifiedMemory(), baseCapabilities().supportsNonPrivateDepthStencilTextures);
 
     // FIXME(PERFORMANCE): Consider write-combining CPU cache mode.
     // FIXME(PERFORMANCE): Consider implementing hazard tracking ourself.

Modified: trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj (293097 => 293098)


--- trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj	2022-04-20 16:03:21 UTC (rev 293097)
+++ trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj	2022-04-20 16:10:11 UTC (rev 293098)
@@ -10,8 +10,8 @@
 		1C023D3F27449070001DB734 /* WGSLLexerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C023D3E27449070001DB734 /* WGSLLexerTests.mm */; };
 		1C023D4027449070001DB734 /* libwgsl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CEBD7F22716B2CC00A5254D /* libwgsl.a */; };
 		1C023D4B274495B9001DB734 /* _javascript_Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C023D4A274495B9001DB734 /* _javascript_Core.framework */; };
-		1C0F41EE280940650005886D /* HardwareLimits.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0F41EC280940650005886D /* HardwareLimits.mm */; };
-		1C0F41EF280940650005886D /* HardwareLimits.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C0F41ED280940650005886D /* HardwareLimits.h */; };
+		1C0F41EE280940650005886D /* HardwareCapabilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0F41EC280940650005886D /* HardwareCapabilities.mm */; };
+		1C0F41EF280940650005886D /* HardwareCapabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C0F41ED280940650005886D /* HardwareCapabilities.h */; };
 		1C2CEDEE271E8A7300EDC16F /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C2CEDED271E8A7300EDC16F /* Metal.framework */; };
 		1C33755F27FA23B8002F1644 /* IsValidToUseWith.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C33755D27FA23B8002F1644 /* IsValidToUseWith.h */; };
 		1C5319C027BDC6CC00CD127E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5319BF27BDC6CC00CD127E /* main.swift */; };
@@ -133,8 +133,8 @@
 		1C023D3E27449070001DB734 /* WGSLLexerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WGSLLexerTests.mm; sourceTree = "<group>"; };
 		1C023D472744916D001DB734 /* WGSLUnitTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = WGSLUnitTests.xcconfig; sourceTree = "<group>"; };
 		1C023D4A274495B9001DB734 /* _javascript_Core.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = _javascript_Core.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		1C0F41EC280940650005886D /* HardwareLimits.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HardwareLimits.mm; sourceTree = "<group>"; };
-		1C0F41ED280940650005886D /* HardwareLimits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HardwareLimits.h; sourceTree = "<group>"; };
+		1C0F41EC280940650005886D /* HardwareCapabilities.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HardwareCapabilities.mm; sourceTree = "<group>"; };
+		1C0F41ED280940650005886D /* HardwareCapabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HardwareCapabilities.h; sourceTree = "<group>"; };
 		1C2CEDED271E8A7300EDC16F /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
 		1C33755D27FA23B8002F1644 /* IsValidToUseWith.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IsValidToUseWith.h; sourceTree = "<group>"; };
 		1C5319BD27BDC6CB00CD127E /* CommandLinePlayground */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLinePlayground; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -348,8 +348,8 @@
 				1C5ACA9B273A426D0095F8D5 /* Device.h */,
 				1C5ACAA3273A426D0095F8D5 /* Device.mm */,
 				1CEBD80B2716C37900A5254D /* ExportMacros.h */,
-				1C0F41ED280940650005886D /* HardwareLimits.h */,
-				1C0F41EC280940650005886D /* HardwareLimits.mm */,
+				1C0F41ED280940650005886D /* HardwareCapabilities.h */,
+				1C0F41EC280940650005886D /* HardwareCapabilities.mm */,
 				1C5ACAA0273A426D0095F8D5 /* Instance.h */,
 				1C5ACA92273A41C20095F8D5 /* Instance.mm */,
 				1C33755D27FA23B8002F1644 /* IsValidToUseWith.h */,
@@ -487,7 +487,7 @@
 				1C58301827E16823009B40F0 /* APIConversions.h in Headers */,
 				1C5ACAD7273A4D700095F8D5 /* BindGroup.h in Headers */,
 				1C582FFA27E04131009B40F0 /* CommandsMixin.h in Headers */,
-				1C0F41EF280940650005886D /* HardwareLimits.h in Headers */,
+				1C0F41EF280940650005886D /* HardwareCapabilities.h in Headers */,
 				1C33755F27FA23B8002F1644 /* IsValidToUseWith.h in Headers */,
 				1CEBD7E72716AFBA00A5254D /* WebGPU.h in Headers */,
 				1C5ACAD3273A4C860095F8D5 /* WebGPUExt.h in Headers */,
@@ -737,7 +737,7 @@
 				1C5ACAC6273A426D0095F8D5 /* ComputePassEncoder.mm in Sources */,
 				1C5ACAC0273A426D0095F8D5 /* ComputePipeline.mm in Sources */,
 				1C5ACAC1273A426D0095F8D5 /* Device.mm in Sources */,
-				1C0F41EE280940650005886D /* HardwareLimits.mm in Sources */,
+				1C0F41EE280940650005886D /* HardwareCapabilities.mm in Sources */,
 				1C5ACA94273A41C20095F8D5 /* Instance.mm in Sources */,
 				1C5ACAE5273A55DD0095F8D5 /* PipelineLayout.mm in Sources */,
 				1C5ACABD273A426D0095F8D5 /* QuerySet.mm in Sources */,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to