webgpu.h: Add Adapter and Device GetFeatures() methods

Bug: 368672123
Change-Id: I0b16347f6c2912e71228f5f613d665efa1f95161
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/212174
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Fr <beaufort.francois@gmail.com>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 9f66565..6b80fec 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -301,6 +301,7 @@
                 m for m in json_data['members'] if is_enabled(m)
             ]
         Type.__init__(self, name, dict(json_data, **json_data_override))
+        self.out = json_data.get('out', False)
         self.chained = json_data.get('chained', None)
         self.extensible = json_data.get('extensible', None)
         if self.chained:
@@ -332,7 +333,10 @@
 
     @property
     def output(self):
-        return self.chained == "out" or self.extensible == "out"
+        # self.out is a temporary way to express that this is an output structure
+        # without also making it extensible. See
+        # https://dawn-review.googlesource.com/c/dawn/+/212174/comment/2271690b_1fd82ea9/
+        return self.chained == "out" or self.extensible == "out" or self.out
 
     @property
     def has_free_members_function(self):
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index bfa79d0..cbdca32 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -156,12 +156,20 @@
             },
             {
                 "name": "enumerate features",
+                "tags": ["deprecated"],
                 "returns": "size_t",
                 "args": [
                     {"name": "features", "type": "feature name", "annotation": "*"}
                 ]
             },
             {
+                "name": "get features",
+                "returns": "void",
+                "args": [
+                    {"name": "features", "type": "supported features", "annotation": "*"}
+                ]
+            },
+            {
                 "name": "request device",
                 "args": [
                     {"name": "descriptor", "type": "device descriptor", "annotation": "const*", "optional": true, "no_default": true},
@@ -1560,12 +1568,20 @@
             },
             {
                 "name": "enumerate features",
+                "tags": ["deprecated"],
                 "returns": "size_t",
                 "args": [
                     {"name": "features", "type": "feature name", "annotation": "*"}
                 ]
             },
             {
+                "name": "get features",
+                "returns": "void",
+                "args": [
+                    {"name": "features", "type": "supported features", "annotation": "*"}
+                ]
+            },
+            {
                 "name": "get adapter",
                 "returns": "adapter",
                 "tags": ["dawn"]
@@ -1867,6 +1883,14 @@
             {"name": "limits", "type": "limits"}
         ]
     },
+    "supported features": {
+        "category": "structure",
+        "out": true,
+        "members": [
+            {"name": "feature count", "type": "size_t"},
+            {"name": "features", "type": "feature name", "annotation": "const*", "length": "feature count"}
+        ]
+    },
     "logging callback": {
         "category": "function pointer",
         "tags": ["dawn"],
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 270bb10..f5a5107 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -218,6 +218,7 @@
         ],
         "client_side_commands": [
             "AdapterCreateDevice",
+            "AdapterGetFeatures",
             "AdapterGetFormatCapabilities",
             "AdapterGetInfo",
             "AdapterGetInstance",
@@ -243,6 +244,7 @@
             "DeviceCreateRenderPipelineAsyncF",
             "DeviceCreateRenderPipelineAsync2",
             "DeviceGetAdapter",
+            "DeviceGetFeatures",
             "DeviceGetLimits",
             "DeviceHasFeature",
             "DeviceEnumerateFeatures",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 57958d6..be0cca3 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -228,6 +228,18 @@
     return mSupportedFeatures.EnumerateFeatures(features);
 }
 
+void AdapterBase::APIGetFeatures(wgpu::SupportedFeatures* features) const {
+    this->APIGetFeatures(reinterpret_cast<SupportedFeatures*>(features));
+}
+
+void AdapterBase::APIGetFeatures(SupportedFeatures* features) const {
+    mSupportedFeatures.ToSupportedFeatures(features);
+}
+
+void APISupportedFeaturesFreeMembers(WGPUSupportedFeatures supportedFeatures) {
+    delete[] supportedFeatures.features;
+}
+
 // TODO(https://crbug.com/dawn/2465) Could potentially re-implement via AllowSpontaneous async mode.
 DeviceBase* AdapterBase::APICreateDevice(const DeviceDescriptor* descriptor) {
     if (descriptor == nullptr) {
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index b0e1b08..28b5bcf 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -64,6 +64,8 @@
     wgpu::Status APIGetInfo(AdapterInfo* info) const;
     bool APIHasFeature(wgpu::FeatureName feature) const;
     size_t APIEnumerateFeatures(wgpu::FeatureName* features) const;
+    void APIGetFeatures(SupportedFeatures* features) const;
+    void APIGetFeatures(wgpu::SupportedFeatures* features) const;
     void APIRequestDevice(const DeviceDescriptor* descriptor,
                           WGPURequestDeviceCallback callback,
                           void* userdata);
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 7ea30103..7fa685d 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -2009,6 +2009,14 @@
     return mEnabledFeatures.EnumerateFeatures(features);
 }
 
+void DeviceBase::APIGetFeatures(wgpu::SupportedFeatures* features) const {
+    this->APIGetFeatures(reinterpret_cast<SupportedFeatures*>(features));
+}
+
+void DeviceBase::APIGetFeatures(SupportedFeatures* features) const {
+    mEnabledFeatures.ToSupportedFeatures(features);
+}
+
 void DeviceBase::APIInjectError(wgpu::ErrorType type, StringView message) {
     if (ConsumedError(ValidateErrorType(type))) {
         return;
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index a93516d..7b2170e 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -289,6 +289,8 @@
     wgpu::Status APIGetLimits(SupportedLimits* limits) const;
     bool APIHasFeature(wgpu::FeatureName feature) const;
     size_t APIEnumerateFeatures(wgpu::FeatureName* features) const;
+    void APIGetFeatures(wgpu::SupportedFeatures* features) const;
+    void APIGetFeatures(SupportedFeatures* features) const;
     void APIInjectError(wgpu::ErrorType type, StringView message);
     bool APITick();
     void APIValidateTextureDescriptor(const TextureDescriptor* desc);
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index 703fd90..a548e10 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -444,6 +444,29 @@
     return enabledFeatureNames;
 }
 
+void FeaturesSet::ToSupportedFeatures(SupportedFeatures* supportedFeatures) const {
+    if (!supportedFeatures) {
+        return;
+    }
+
+    const size_t count = featuresBitSet.count();
+    supportedFeatures->featureCount = count;
+    supportedFeatures->features = nullptr;
+
+    if (count == 0) {
+        return;
+    }
+
+    // This will be freed by wgpuSupportedFeaturesFreeMembers.
+    wgpu::FeatureName* features = new wgpu::FeatureName[count];
+    uint32_t index = 0;
+    for (Feature f : IterateBitSet(featuresBitSet)) {
+        features[index++] = ToAPI(f);
+    }
+    DAWN_ASSERT(index == count);
+    supportedFeatures->features = features;
+}
+
 }  // namespace dawn::native
 
 #include "dawn/native/Features_autogen.inl"
diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h
index f97359e..d97d0b5 100644
--- a/src/dawn/native/Features.h
+++ b/src/dawn/native/Features.h
@@ -60,6 +60,7 @@
     // non-null.
     size_t EnumerateFeatures(wgpu::FeatureName* features) const;
     std::vector<const char*> GetEnabledFeatureNames() const;
+    void ToSupportedFeatures(SupportedFeatures* supportedFeatures) const;
 };
 
 }  // namespace dawn::native
diff --git a/src/dawn/node/binding/GPUAdapter.cpp b/src/dawn/node/binding/GPUAdapter.cpp
index ce5992f..2b38434 100644
--- a/src/dawn/node/binding/GPUAdapter.cpp
+++ b/src/dawn/node/binding/GPUAdapter.cpp
@@ -88,15 +88,11 @@
                        std::shared_ptr<AsyncRunner> async)
     : adapter_(a), flags_(flags), async_(async) {}
 
-// TODO(dawn:1133): Avoid the extra copy by making the generator make a virtual method with const
-// std::string&
 interop::Interface<interop::GPUSupportedFeatures> GPUAdapter::getFeatures(Napi::Env env) {
-    wgpu::Adapter adapter(adapter_.Get());
-    size_t count = adapter.EnumerateFeatures(nullptr);
-    std::vector<wgpu::FeatureName> features(count);
-    adapter.EnumerateFeatures(&features[0]);
-    return interop::GPUSupportedFeatures::Create<GPUSupportedFeatures>(env, env,
-                                                                       std::move(features));
+    wgpu::Adapter wgpuAdapter = adapter_.Get();
+    wgpu::SupportedFeatures features{};
+    wgpuAdapter.GetFeatures(&features);
+    return interop::GPUSupportedFeatures::Create<GPUSupportedFeatures>(env, env, features);
 }
 
 interop::Interface<interop::GPUSupportedLimits> GPUAdapter::getLimits(Napi::Env env) {
diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp
index 741380e..7953066 100644
--- a/src/dawn/node/binding/GPUDevice.cpp
+++ b/src/dawn/node/binding/GPUDevice.cpp
@@ -176,13 +176,9 @@
 }
 
 interop::Interface<interop::GPUSupportedFeatures> GPUDevice::getFeatures(Napi::Env env) {
-    size_t count = device_.EnumerateFeatures(nullptr);
-    std::vector<wgpu::FeatureName> features(count);
-    if (count > 0) {
-        device_.EnumerateFeatures(features.data());
-    }
-    return interop::GPUSupportedFeatures::Create<GPUSupportedFeatures>(env, env,
-                                                                       std::move(features));
+    wgpu::SupportedFeatures features{};
+    device_.GetFeatures(&features);
+    return interop::GPUSupportedFeatures::Create<GPUSupportedFeatures>(env, env, features);
 }
 
 interop::Interface<interop::GPUSupportedLimits> GPUDevice::getLimits(Napi::Env env) {
diff --git a/src/dawn/node/binding/GPUSupportedFeatures.cpp b/src/dawn/node/binding/GPUSupportedFeatures.cpp
index 0fa29f7..1d31e40 100644
--- a/src/dawn/node/binding/GPUSupportedFeatures.cpp
+++ b/src/dawn/node/binding/GPUSupportedFeatures.cpp
@@ -36,12 +36,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GPUSupportedFeatures::GPUSupportedFeatures(Napi::Env env,
-                                           const std::vector<wgpu::FeatureName>& features) {
+                                           const wgpu::SupportedFeatures& supportedFeatures) {
     Converter conv(env);
 
     // Add all known GPUFeatureNames that are known by dawn.node and skip the other ones are they
     // may be native-only extension, Dawn-specific or other special cases.
-    for (wgpu::FeatureName feature : features) {
+    for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+        wgpu::FeatureName feature = supportedFeatures.features[i];
         interop::GPUFeatureName gpuFeature;
         if (conv(gpuFeature, feature)) {
             enabled_.emplace(gpuFeature);
diff --git a/src/dawn/node/binding/GPUSupportedFeatures.h b/src/dawn/node/binding/GPUSupportedFeatures.h
index 417c4b3..d7a93e7 100644
--- a/src/dawn/node/binding/GPUSupportedFeatures.h
+++ b/src/dawn/node/binding/GPUSupportedFeatures.h
@@ -42,7 +42,7 @@
 // GPUSupportedLFeatures is an implementation of interop::GPUSupportedFeatures.
 class GPUSupportedFeatures final : public interop::GPUSupportedFeatures {
   public:
-    GPUSupportedFeatures(Napi::Env env, const std::vector<wgpu::FeatureName>& features);
+    GPUSupportedFeatures(Napi::Env env, const wgpu::SupportedFeatures& features);
 
     // interop::GPUSupportedFeatures interface compliance
     bool has(Napi::Env, std::string name) override;
diff --git a/src/dawn/samples/DawnInfo.cpp b/src/dawn/samples/DawnInfo.cpp
index cc5eaa6..8ddfe07 100644
--- a/src/dawn/samples/DawnInfo.cpp
+++ b/src/dawn/samples/DawnInfo.cpp
@@ -235,13 +235,12 @@
 }
 
 void DumpAdapterFeatures(const wgpu::Adapter& adapter) {
-    auto feature_count = adapter.EnumerateFeatures(nullptr);
-    std::vector<wgpu::FeatureName> features(feature_count);
-    adapter.EnumerateFeatures(features.data());
-
+    wgpu::SupportedFeatures supportedFeatures;
+    adapter.GetFeatures(&supportedFeatures);
     std::cout << "  Features\n";
     std::cout << "  ========\n";
-    for (const auto& f : features) {
+    for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+        wgpu::FeatureName f = supportedFeatures.features[i];
         auto info = dawn::native::GetFeatureInfo(f);
         std::cout << "   * " << info->name << "\n";
         std::cout << WrapString(info->description, "      ") << "\n";
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index 99bd853..f9c926f9 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -1073,14 +1073,13 @@
 
 bool DawnTestBase::SupportsFeatures(const std::vector<wgpu::FeatureName>& features) {
     DAWN_ASSERT(mBackendAdapter);
-    std::vector<wgpu::FeatureName> supportedFeatures;
-    uint32_t count = native::GetProcs().adapterEnumerateFeatures(mBackendAdapter.Get(), nullptr);
-    supportedFeatures.resize(count);
-    native::GetProcs().adapterEnumerateFeatures(
-        mBackendAdapter.Get(), reinterpret_cast<WGPUFeatureName*>(&supportedFeatures[0]));
+    wgpu::SupportedFeatures supportedFeatures;
+    native::GetProcs().adapterGetFeatures(
+        mBackendAdapter.Get(), reinterpret_cast<WGPUSupportedFeatures*>(&supportedFeatures));
 
     std::unordered_set<wgpu::FeatureName> supportedSet;
-    for (wgpu::FeatureName f : supportedFeatures) {
+    for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+        wgpu::FeatureName f = supportedFeatures.features[i];
         supportedSet.insert(f);
     }
 
diff --git a/src/dawn/tests/unittests/FeatureTests.cpp b/src/dawn/tests/unittests/FeatureTests.cpp
index ff7abdc..e7d8472 100644
--- a/src/dawn/tests/unittests/FeatureTests.cpp
+++ b/src/dawn/tests/unittests/FeatureTests.cpp
@@ -165,11 +165,12 @@
         // Helper to check the returned device has all required features
         auto ExpectDeviceHasRequiredFeatures =
             [&requiredFeaturesSet](native::DeviceBase* deviceBase) {
-                ASSERT_EQ(requiredFeaturesSet.size(), deviceBase->APIEnumerateFeatures(nullptr));
-                std::vector<wgpu::FeatureName> enabledFeatures(requiredFeaturesSet.size());
-                deviceBase->APIEnumerateFeatures(enabledFeatures.data());
-                for (auto enabledFeature : enabledFeatures) {
-                    EXPECT_TRUE(requiredFeaturesSet.count(enabledFeature) > 0);
+                native::SupportedFeatures enabledFeatures;
+                deviceBase->APIGetFeatures(&enabledFeatures);
+                ASSERT_EQ(requiredFeaturesSet.size(), enabledFeatures.featureCount);
+                for (uint32_t i = 0; i < enabledFeatures.featureCount; ++i) {
+                    wgpu::FeatureName enabledFeature = enabledFeatures.features[i];
+                    EXPECT_TRUE(requiredFeaturesSet.contains(enabledFeature));
                 }
             };
 
diff --git a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
index 4a1efec..cc62cd0 100644
--- a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
+++ b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
@@ -168,6 +168,11 @@
             wgpu::FeatureName enabledFeature;
             device.EnumerateFeatures(&enabledFeature);
             EXPECT_EQ(enabledFeature, featureName);
+
+            wgpu::SupportedFeatures supportedFeatures;
+            device.GetFeatures(&supportedFeatures);
+            ASSERT_EQ(1u, supportedFeatures.featureCount);
+            EXPECT_EQ(enabledFeature, supportedFeatures.features[0]);
         }
 
         // Test creating device with AllowUnsafeApis enabled in device toggle descriptor will
@@ -188,6 +193,11 @@
                 wgpu::FeatureName enabledFeature;
                 device.EnumerateFeatures(&enabledFeature);
                 EXPECT_EQ(enabledFeature, featureName);
+
+                wgpu::SupportedFeatures supportedFeatures;
+                device.GetFeatures(&supportedFeatures);
+                ASSERT_EQ(1u, supportedFeatures.featureCount);
+                EXPECT_EQ(enabledFeature, supportedFeatures.features[0]);
             }
 
             // Test on adapter with AllowUnsafeApis disabled.
@@ -199,6 +209,11 @@
                 wgpu::FeatureName enabledFeature;
                 device.EnumerateFeatures(&enabledFeature);
                 EXPECT_EQ(enabledFeature, featureName);
+
+                wgpu::SupportedFeatures supportedFeatures;
+                device.GetFeatures(&supportedFeatures);
+                ASSERT_EQ(1u, supportedFeatures.featureCount);
+                EXPECT_EQ(enabledFeature, supportedFeatures.features[0]);
             }
         }
 
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index b4b42bc..e242e5e 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -183,15 +183,17 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, DeviceGetFeatures(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (wgpu::FeatureName feature : fakeFeatures) {
-                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                        features[index++] = static_cast<WGPUFeatureName>(feature);
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
+                    return WGPUStatus_Success;
                 })));
 
             // The backend device should still not be known by the wire server since the
@@ -220,13 +222,13 @@
                           fakeLimits.limits.maxTextureDimension1D);
                 EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
 
-                std::vector<wgpu::FeatureName> features;
-                features.resize(device.EnumerateFeatures(nullptr));
-                ASSERT_EQ(features.size(), fakeFeatures.size());
-                EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
+                wgpu::SupportedFeatures supportedFeatures;
+                device.GetFeatures(&supportedFeatures);
+                ASSERT_EQ(supportedFeatures.featureCount, fakeFeatures.size());
 
                 std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
-                for (wgpu::FeatureName feature : features) {
+                for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+                    wgpu::FeatureName feature = supportedFeatures.features[i];
                     EXPECT_EQ(featureSet.erase(feature), 1u);
                 }
             })));
@@ -265,15 +267,16 @@
     WGPUDevice apiDevice = api.GetNewDevice();
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, DeviceGetFeatures(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (wgpu::FeatureName feature : fakeFeatures) {
-                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                        features[index++] = static_cast<WGPUFeatureName>(feature);
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
                 })));
 
             // The device was actually created, but the wire didn't support its features.
@@ -349,15 +352,16 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, DeviceGetFeatures(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (wgpu::FeatureName feature : fakeFeatures) {
-                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                        features[index++] = static_cast<WGPUFeatureName>(feature);
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
                 })));
 
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index a2950f7..92a32de 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -161,15 +161,16 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (WGPUFeatureName feature : fakeFeatures) {
-                        *(features++) = feature;
+                        features[index++] = feature;
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
                                                    apiAdapter, kEmptyOutputStringView);
@@ -204,13 +205,13 @@
                           fakeLimits.limits.maxTextureDimension1D);
                 EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
 
-                std::vector<WGPUFeatureName> features;
-                features.resize(wgpuAdapterEnumerateFeatures(adapter, nullptr));
-                ASSERT_EQ(features.size(), fakeFeatures.size());
-                EXPECT_EQ(wgpuAdapterEnumerateFeatures(adapter, &features[0]), features.size());
+                WGPUSupportedFeatures supportedFeatures;
+                wgpuAdapterGetFeatures(adapter, &supportedFeatures);
+                ASSERT_EQ(supportedFeatures.featureCount, fakeFeatures.size());
 
                 std::unordered_set<WGPUFeatureName> featureSet(fakeFeatures);
-                for (WGPUFeatureName feature : features) {
+                for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+                    WGPUFeatureName feature = supportedFeatures.features[i];
                     EXPECT_EQ(featureSet.erase(feature), 1u);
                 }
             })));
@@ -299,15 +300,16 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (WGPUFeatureName feature : fakeFeatures) {
-                        *(features++) = feature;
+                        features[index++] = feature;
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
                                                    apiAdapter, kEmptyOutputStringView);
@@ -396,15 +398,16 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
-                .WillOnce(Return(fakeFeatures.size()));
-
-            EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
-                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+            EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* supportedFeatures) {
+                    const size_t count = fakeFeatures.size();
+                    WGPUFeatureName* features = new WGPUFeatureName[count];
+                    uint32_t index = 0;
                     for (WGPUFeatureName feature : fakeFeatures) {
-                        *(features++) = feature;
+                        features[index++] = feature;
                     }
-                    return fakeFeatures.size();
+                    supportedFeatures->featureCount = count;
+                    supportedFeatures->features = features;
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
                                                    apiAdapter, kEmptyOutputStringView);
@@ -418,10 +421,10 @@
         EXPECT_CALL(mockCb,
                     Call(WGPURequestAdapterStatus_Success, NotNull(), EmptySizedString(), nullptr))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
-                WGPUFeatureName feature;
-                ASSERT_EQ(wgpuAdapterEnumerateFeatures(adapter, nullptr), 1u);
-                wgpuAdapterEnumerateFeatures(adapter, &feature);
-                EXPECT_EQ(feature, WGPUFeatureName_Depth32FloatStencil8);
+                WGPUSupportedFeatures supportedFeatures;
+                wgpuAdapterGetFeatures(adapter, &supportedFeatures);
+                EXPECT_EQ(supportedFeatures.featureCount, 1u);
+                EXPECT_EQ(supportedFeatures.features[0], WGPUFeatureName_Depth32FloatStencil8);
             })));
 
         FlushCallbacks();
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index 7aeba35..8d653bd 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -118,9 +118,11 @@
                 return WGPUStatus_Success;
             })));
 
-        EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
-            .WillOnce(Return(0))
-            .WillOnce(Return(0));
+        EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull()))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* features) {
+                features->featureCount = 0;
+                return WGPUStatus_Success;
+            })));
 
         api.CallInstanceRequestAdapter2Callback(apiInstance, WGPURequestAdapterStatus_Success,
                                                 apiAdapter, kEmptyOutputStringView);
@@ -167,9 +169,11 @@
                     return WGPUStatus_Success;
                 })));
 
-            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
-                .WillOnce(Return(0))
-                .WillOnce(Return(0));
+            EXPECT_CALL(api, DeviceGetFeatures(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedFeatures* features) {
+                    features->featureCount = 0;
+                    return WGPUStatus_Success;
+                })));
 
             api.CallAdapterRequestDevice2Callback(apiAdapter, WGPURequestDeviceStatus_Success,
                                                   apiDevice, kEmptyOutputStringView);
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 8ed8052..2547eac 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -154,6 +154,10 @@
     return mLimitsAndFeatures.EnumerateFeatures(features);
 }
 
+void Adapter::GetFeatures(WGPUSupportedFeatures* features) const {
+    mLimitsAndFeatures.ToSupportedFeatures(features);
+}
+
 void Adapter::SetLimits(const WGPUSupportedLimits* limits) {
     return mLimitsAndFeatures.SetLimits(limits);
 }
@@ -390,3 +394,8 @@
     WGPUDrmFormatCapabilities capabilities) {
     delete[] capabilities.properties;
 }
+
+DAWN_WIRE_EXPORT void wgpuDawnWireClientSupportedFeaturesFreeMembers(
+    WGPUSupportedFeatures supportedFeatures) {
+    delete[] supportedFeatures.features;
+}
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index 743598a..c21d7f2 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -52,6 +52,7 @@
     void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
     void SetInfo(const WGPUAdapterInfo* info);
     WGPUStatus GetInfo(WGPUAdapterInfo* info) const;
+    void GetFeatures(WGPUSupportedFeatures* features) const;
     void RequestDevice(const WGPUDeviceDescriptor* descriptor,
                        WGPURequestDeviceCallback callback,
                        void* userdata);
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index 8923e75..329dd6c 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -341,6 +341,10 @@
     return mLimitsAndFeatures.EnumerateFeatures(features);
 }
 
+void Device::GetFeatures(WGPUSupportedFeatures* features) const {
+    mLimitsAndFeatures.ToSupportedFeatures(features);
+}
+
 void Device::SetLimits(const WGPUSupportedLimits* limits) {
     return mLimitsAndFeatures.SetLimits(limits);
 }
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index c7503e2..baf70de 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -98,6 +98,7 @@
     WGPUStatus GetLimits(WGPUSupportedLimits* limits) const;
     bool HasFeature(WGPUFeatureName feature) const;
     size_t EnumerateFeatures(WGPUFeatureName* features) const;
+    void GetFeatures(WGPUSupportedFeatures* features) const;
     WGPUAdapter GetAdapter() const;
     WGPUQueue GetQueue();
 
diff --git a/src/dawn/wire/client/LimitsAndFeatures.cpp b/src/dawn/wire/client/LimitsAndFeatures.cpp
index 612890a..803480a 100644
--- a/src/dawn/wire/client/LimitsAndFeatures.cpp
+++ b/src/dawn/wire/client/LimitsAndFeatures.cpp
@@ -84,6 +84,29 @@
     return mFeatures.size();
 }
 
+void LimitsAndFeatures::ToSupportedFeatures(WGPUSupportedFeatures* supportedFeatures) const {
+    if (!supportedFeatures) {
+        return;
+    }
+
+    const size_t count = mFeatures.size();
+    supportedFeatures->featureCount = count;
+    supportedFeatures->features = nullptr;
+
+    if (count == 0) {
+        return;
+    }
+
+    // This will be freed by wgpuSupportedFeaturesFreeMembers.
+    WGPUFeatureName* features = new WGPUFeatureName[count];
+    uint32_t index = 0;
+    for (WGPUFeatureName f : mFeatures) {
+        features[index++] = f;
+    }
+    DAWN_ASSERT(index == count);
+    supportedFeatures->features = features;
+}
+
 void LimitsAndFeatures::SetLimits(const WGPUSupportedLimits* limits) {
     DAWN_ASSERT(limits != nullptr);
     mLimits = *limits;
diff --git a/src/dawn/wire/client/LimitsAndFeatures.h b/src/dawn/wire/client/LimitsAndFeatures.h
index 5846a58..4c4933f 100644
--- a/src/dawn/wire/client/LimitsAndFeatures.h
+++ b/src/dawn/wire/client/LimitsAndFeatures.h
@@ -42,6 +42,7 @@
     WGPUStatus GetLimits(WGPUSupportedLimits* limits) const;
     bool HasFeature(WGPUFeatureName feature) const;
     size_t EnumerateFeatures(WGPUFeatureName* features) const;
+    void ToSupportedFeatures(WGPUSupportedFeatures* supportedFeatures) const;
 
     void SetLimits(const WGPUSupportedLimits* limits);
     void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 8d4c3c8..65c2755 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -97,17 +97,15 @@
         return;
     }
 
-    std::vector<WGPUFeatureName> features;
-
-    size_t featuresCount = mProcs.deviceEnumerateFeatures(device, nullptr);
-    features.resize(featuresCount);
-    mProcs.deviceEnumerateFeatures(device, features.data());
+    WGPUSupportedFeatures supportedFeatures;
+    mProcs.deviceGetFeatures(device, &supportedFeatures);
 
     // The client should only be able to request supported features, so all enumerated
     // features that were enabled must also be supported by the wire.
     // Note: We fail the callback here, instead of immediately upon receiving
     // the request to preserve callback ordering.
-    for (WGPUFeatureName f : features) {
+    for (uint32_t i = 0; i < supportedFeatures.featureCount; ++i) {
+        WGPUFeatureName f = supportedFeatures.features[i];
         if (!IsFeatureSupported(f)) {
             // Release the device.
             mProcs.deviceRelease(device);
@@ -120,8 +118,8 @@
         }
     }
 
-    cmd.featuresCount = features.size();
-    cmd.features = features.data();
+    cmd.featuresCount = supportedFeatures.featureCount;
+    cmd.features = supportedFeatures.features;
 
     // Query and report the adapter limits, including DawnExperimentalSubgroupLimits and
     // DawnExperimentalImmediateDataLimits. Reporting to client.
@@ -153,6 +151,7 @@
     reservation->info->self = reservation.AsHandle();
     SetForwardingDeviceCallbacks(reservation);
     SerializeCommand(cmd);
+    mProcs.supportedFeaturesFreeMembers(supportedFeatures);
 }
 
 }  // namespace dawn::wire::server
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 4e1b883..02b998c 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -25,11 +25,7 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include <algorithm>
-#include <vector>
-
 #include "dawn/common/StringViewUtils.h"
-#include "dawn/wire/SupportedFeatures.h"
 #include "dawn/wire/server/ObjectStorage.h"
 #include "dawn/wire/server/Server.h"
 
@@ -87,17 +83,10 @@
     }
 
     // Query and report the adapter supported features.
-    std::vector<WGPUFeatureName> features;
-
-    size_t featuresCount = mProcs.adapterEnumerateFeatures(adapter, nullptr);
-    features.resize(featuresCount);
-    mProcs.adapterEnumerateFeatures(adapter, features.data());
-
-    // Hide features the wire cannot support.
-    auto it = std::partition(features.begin(), features.end(), IsFeatureSupported);
-
-    cmd.featuresCount = std::distance(features.begin(), it);
-    cmd.features = features.data();
+    WGPUSupportedFeatures supportedFeatures;
+    mProcs.adapterGetFeatures(adapter, &supportedFeatures);
+    cmd.featuresCount = supportedFeatures.featureCount;
+    cmd.features = supportedFeatures.features;
 
     // Query and report the adapter info.
     WGPUAdapterInfo info = {};
@@ -149,6 +138,7 @@
     SerializeCommand(cmd);
     mProcs.adapterInfoFreeMembers(info);
     mProcs.adapterPropertiesMemoryHeapsFreeMembers(memoryHeapProperties);
+    mProcs.supportedFeaturesFreeMembers(supportedFeatures);
 }
 
 }  // namespace dawn::wire::server
diff --git a/tools/android/BUILD.gn b/tools/android/BUILD.gn
index b25225b..815442d 100644
--- a/tools/android/BUILD.gn
+++ b/tools/android/BUILD.gn
@@ -182,6 +182,7 @@
     "java/android/dawn/StorageTextureAccess.kt",
     "java/android/dawn/StorageTextureBindingLayout.kt",
     "java/android/dawn/StoreOp.kt",
+    "java/android/dawn/SupportedFeatures.kt",
     "java/android/dawn/SupportedLimits.kt",
     "java/android/dawn/Surface.kt",
     "java/android/dawn/SurfaceCapabilities.kt",