[wgpu-headers] Introduce 2nd userdata to requestDevice callback.

- Updates the wire to use the new APIs
- Updates ValidationTest framework so that we can use the new helpers.
  Also modernizes the way we pass toggles and features a bit.
- Updates relevant usages to use new C++ helpers

Bug: 42241461
Change-Id: Ia810d7704ed566e547c5d11febaa8d04b391dae9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188083
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index d28a123..4833529 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -170,6 +170,16 @@
                 ]
             },
             {
+                "name": "request device 2",
+                "_comment": "TODO(crbug.com/dawn/2021): This is dawn/emscripten-only until we rename it to replace the old API. See bug for details.",
+                "tags": ["dawn", "emscripten"],
+                "returns": "future",
+                "args": [
+                    {"name": "options", "type": "device descriptor", "annotation": "const*", "optional": true, "no_default": true},
+                    {"name": "callback info", "type": "request device callback info 2"}
+                ]
+            },
+            {
                 "name": "create device",
                 "tags": ["dawn"],
                 "returns": "device",
@@ -3268,6 +3278,15 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "request device callback 2": {
+        "category": "callback function",
+        "_comment": "crbug.com/1234617: Revisit optional status of device once requestDevice can return lost devices",
+        "args": [
+            {"name": "status", "type": "request device status"},
+            {"name": "device", "type": "device", "optional": true},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}
+        ]
+    },
     "request device callback info": {
          "category": "structure",
          "extensible": "in",
@@ -3277,6 +3296,12 @@
             {"name": "userdata", "type": "void *"}
          ]
     },
+    "request device callback info 2": {
+        "category": "callback info",
+        "members": [
+           {"name": "callback", "type": "request device callback 2"}
+        ]
+   },
 
     "request device status": {
         "category": "enum",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index d554885..212d7ba 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -117,7 +117,8 @@
             { "name": "future", "type": "future" },
             { "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
             { "name": "device lost future", "type": "future" },
-            { "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
+            { "name": "descriptor", "type": "device descriptor", "annotation": "const*" },
+            { "name": "userdata count", "type": "uint8_t", "_comment": "TODO(crbug.com/dawn/2509): Remove this once Chromium overrides the correct functions in the proc table."}
         ]
     },
     "return commands": {
@@ -212,6 +213,7 @@
             "AdapterEnumerateFeatures",
             "AdapterRequestDevice",
             "AdapterRequestDeviceF",
+            "AdapterRequestDevice2",
             "BufferMapAsync",
             "BufferMapAsyncF",
             "BufferGetConstMappedRange",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 41324f4..c9b005a 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -298,26 +298,44 @@
 
 Future AdapterBase::APIRequestDeviceF(const DeviceDescriptor* descriptor,
                                       const RequestDeviceCallbackInfo& callbackInfo) {
+    return APIRequestDevice2(
+        descriptor, {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
+                     [](WGPURequestDeviceStatus status, WGPUDevice device, char const* message,
+                        void* callback, void* userdata) {
+                         auto cb = reinterpret_cast<WGPURequestDeviceCallback>(callback);
+                         cb(status, device, message, userdata);
+                     },
+                     reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata});
+}
+
+Future AdapterBase::APIRequestDevice2(const DeviceDescriptor* descriptor,
+                                      const WGPURequestDeviceCallbackInfo2& callbackInfo) {
     struct RequestDeviceEvent final : public EventManager::TrackedEvent {
-        WGPURequestDeviceCallback mCallback;
-        raw_ptr<void> mUserdata;
+        WGPURequestDeviceCallback2 mCallback;
+        raw_ptr<void> mUserdata1;
+        raw_ptr<void> mUserdata2;
 
         WGPURequestDeviceStatus mStatus;
         Ref<DeviceBase> mDevice = nullptr;
         std::string mMessage;
 
-        RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo, Ref<DeviceBase> device)
-            : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+        RequestDeviceEvent(const WGPURequestDeviceCallbackInfo2& callbackInfo,
+                           Ref<DeviceBase> device)
+            : TrackedEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode),
+                           TrackedEvent::Completed{}),
               mCallback(callbackInfo.callback),
-              mUserdata(callbackInfo.userdata),
+              mUserdata1(callbackInfo.userdata1),
+              mUserdata2(callbackInfo.userdata2),
               mStatus(WGPURequestDeviceStatus_Success),
               mDevice(std::move(device)) {}
 
-        RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo,
+        RequestDeviceEvent(const WGPURequestDeviceCallbackInfo2& callbackInfo,
                            const std::string& message)
-            : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+            : TrackedEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode),
+                           TrackedEvent::Completed{}),
               mCallback(callbackInfo.callback),
-              mUserdata(callbackInfo.userdata),
+              mUserdata1(callbackInfo.userdata1),
+              mUserdata2(callbackInfo.userdata2),
               mStatus(WGPURequestDeviceStatus_Error),
               mMessage(message) {}
 
@@ -330,7 +348,8 @@
                 mMessage = "A valid external Instance reference no longer exists.";
             }
             mCallback(mStatus, ToAPI(ReturnToAPI(std::move(mDevice))),
-                      mMessage.empty() ? nullptr : mMessage.c_str(), mUserdata.ExtractAsDangling());
+                      mMessage.empty() ? nullptr : mMessage.c_str(), mUserdata1.ExtractAsDangling(),
+                      mUserdata2.ExtractAsDangling());
         }
     };
 
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index a800eba..e11a98b 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -69,6 +69,8 @@
                           void* userdata);
     Future APIRequestDeviceF(const DeviceDescriptor* descriptor,
                              const RequestDeviceCallbackInfo& callbackInfo);
+    Future APIRequestDevice2(const DeviceDescriptor* descriptor,
+                             const WGPURequestDeviceCallbackInfo2& callbackInfo);
     DeviceBase* APICreateDevice(const DeviceDescriptor* descriptor = nullptr);
     bool APIGetFormatCapabilities(wgpu::TextureFormat format, FormatCapabilities* capabilities);
 
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index 0fe90e6..6c38c4b 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -771,9 +771,10 @@
         return {0};
     };
 
-    procs.adapterRequestDevice = [](WGPUAdapter cAdapter, const WGPUDeviceDescriptor* descriptor,
-                                    WGPURequestDeviceCallback callback, void* userdata) {
+    procs.adapterRequestDevice2 = [](WGPUAdapter cAdapter, const WGPUDeviceDescriptor* descriptor,
+                                     WGPURequestDeviceCallbackInfo2 callbackInfo) -> WGPUFuture {
         DAWN_ASSERT(gCurrentTest);
+        DAWN_ASSERT(callbackInfo.mode == WGPUCallbackMode_AllowSpontaneous);
 
         // Isolation keys may be enqueued by CreateDevice(std::string isolationKey).
         // CreateDevice calls requestAdapter, so consume them there and forward them
@@ -787,7 +788,11 @@
         DAWN_ASSERT(cDevice != nullptr);
 
         gCurrentTest->mLastCreatedBackendDevice = cDevice;
-        callback(WGPURequestDeviceStatus_Success, cDevice, nullptr, userdata);
+        callbackInfo.callback(WGPURequestDeviceStatus_Success, cDevice, nullptr,
+                              callbackInfo.userdata1, callbackInfo.userdata2);
+
+        // Returning a placeholder future that we should never be waiting on.
+        return {0};
     };
 
     mWireHelper = utils::CreateWireHelper(procs, gTestEnv->UsesWire(), gTestEnv->GetWireTraceDir());
@@ -1139,12 +1144,9 @@
     // RequestDevice is overriden by CreateDeviceImpl and device descriptor is ignored by it.
     wgpu::DeviceDescriptor deviceDesc = {};
 
-    adapter.RequestDevice(
-        &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
-            *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
-        },
-        &apiDevice);
+    adapter.RequestDevice(&deviceDesc, wgpu::CallbackMode::AllowSpontaneous,
+                          [&apiDevice](wgpu::RequestDeviceStatus, wgpu::Device result,
+                                       const char*) { apiDevice = std::move(result); });
     FlushWire();
     DAWN_ASSERT(apiDevice);
 
diff --git a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
index 2a894c1..e029b7a 100644
--- a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
+++ b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
@@ -30,6 +30,7 @@
 #include <benchmark/benchmark.h>
 #include <dawn/webgpu_cpp.h>
 #include <memory>
+#include <utility>
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/Log.h"
@@ -59,33 +60,28 @@
 
             // Create the device.
             wgpu::DeviceDescriptor desc = GetDeviceDescriptor();
-            adapter.RequestDevice(
-                &desc,
-                [](WGPURequestDeviceStatus status, WGPUDevice cDevice, char const* message,
-                   void* userdata) {
-                    DAWN_ASSERT(status == WGPURequestDeviceStatus_Success);
-                    *reinterpret_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
-                },
-                &device);
-            while (!device) {
-                wgpuInstanceProcessEvents(nativeInstance->Get());
-            }
-
-            device.SetUncapturedErrorCallback(
-                [](WGPUErrorType, char const* message, void* userdata) {
-                    dawn::ErrorLog() << message;
-                    DAWN_UNREACHABLE();
-                },
-                nullptr);
-
-            device.SetDeviceLostCallback(
-                [](WGPUDeviceLostReason reason, char const* message, void* userdata) {
+            desc.deviceLostCallbackInfo = {
+                nullptr, wgpu::CallbackMode::AllowSpontaneous,
+                [](WGPUDevice const*, WGPUDeviceLostReason reason, char const* message, void*) {
                     if (reason == WGPUDeviceLostReason_Unknown) {
                         dawn::ErrorLog() << message;
                         DAWN_UNREACHABLE();
                     }
                 },
-                nullptr);
+                this};
+            desc.uncapturedErrorCallbackInfo = {nullptr,
+                                                [](WGPUErrorType, char const* message, void*) {
+                                                    dawn::ErrorLog() << message;
+                                                    DAWN_UNREACHABLE();
+                                                },
+                                                this};
+
+            adapter.RequestDevice(
+                &desc, wgpu::CallbackMode::AllowSpontaneous,
+                [this](wgpu::RequestDeviceStatus status, wgpu::Device result, const char*) {
+                    DAWN_ASSERT(status == wgpu::RequestDeviceStatus::Success);
+                    device = std::move(result);
+                });
         }
         mNumDoneThreads = 0;
         mCv.notify_all();
diff --git a/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp b/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp
index 999b4b0..91dee40 100644
--- a/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -96,12 +96,8 @@
         return desc;
     }
 
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 
     void DoTextureSampleTypeTest(bool success,
@@ -630,12 +626,8 @@
 
 class BindGroupValidationTest_Float32Filterable : public BindGroupValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Float32Filterable};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Float32Filterable};
     }
 };
 
@@ -1770,12 +1762,8 @@
 }
 
 class BindGroupLayoutWithStaticSamplersValidationTest : public BindGroupLayoutValidationTest {
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::StaticSamplers};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::StaticSamplers};
     }
 };
 
@@ -1986,12 +1974,8 @@
 
 class SetBindGroupValidationTest : public ValidationTest {
   public:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::StaticSamplers};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::StaticSamplers};
     }
 
     void SetUp() override {
diff --git a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
index 429c1e9..f858e1d 100644
--- a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
@@ -27,6 +27,7 @@
 
 #include <limits>
 #include <memory>
+#include <vector>
 
 #include "dawn/common/Platform.h"
 #include "dawn/tests/unittests/validation/ValidationTest.h"
@@ -1368,12 +1369,8 @@
         BufferValidationTest::SetUp();
     }
 
-    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[] = {wgpu::FeatureName::BufferMapExtendedUsages};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::BufferMapExtendedUsages};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/CompatValidationTests.cpp b/src/dawn/tests/unittests/validation/CompatValidationTests.cpp
index 58715d4..7d7c4a1 100644
--- a/src/dawn/tests/unittests/validation/CompatValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CompatValidationTests.cpp
@@ -1341,18 +1341,14 @@
 
 class CompatCompressedCopyT2BAndCopyT2TValidationTests : public CompatValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         std::vector<wgpu::FeatureName> requiredFeatures;
         for (TextureInfo textureInfo : textureInfos) {
             if (adapter.HasFeature(textureInfo.feature)) {
                 requiredFeatures.push_back(textureInfo.feature);
             }
         }
-
-        descriptor.requiredFeatures = requiredFeatures.data();
-        descriptor.requiredFeatureCount = requiredFeatures.size();
-        return dawnAdapter.CreateDevice(&descriptor);
+        return requiredFeatures;
     }
 
     struct TextureInfo {
diff --git a/src/dawn/tests/unittests/validation/ComputeValidationTests.cpp b/src/dawn/tests/unittests/validation/ComputeValidationTests.cpp
index 1abd760..1c8c920 100644
--- a/src/dawn/tests/unittests/validation/ComputeValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/ComputeValidationTests.cpp
@@ -87,14 +87,8 @@
 class ComputePipelineValidationTestWithSubgroupFeaturesEnabled
     : public ComputePipelineValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        std::vector<wgpu::FeatureName> requiredFeatures = {
-            wgpu::FeatureName::ChromiumExperimentalSubgroups};
-        descriptor.requiredFeatures = requiredFeatures.data();
-        descriptor.requiredFeatureCount = requiredFeatures.size();
-
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::ChromiumExperimentalSubgroups};
     }
 
     // Helper function that create a shader module with compute entry point named main and
diff --git a/src/dawn/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/dawn/tests/unittests/validation/CopyCommandsValidationTests.cpp
index cdd2b71..23bba66 100644
--- a/src/dawn/tests/unittests/validation/CopyCommandsValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CopyCommandsValidationTests.cpp
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <tuple>
+#include <vector>
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/Constants.h"
@@ -436,12 +437,8 @@
 
 class CopyCommandTest_B2T : public CopyCommandTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 };
 
@@ -1044,12 +1041,8 @@
 
 class CopyCommandTest_T2B : public CopyCommandTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 };
 
@@ -1681,12 +1674,8 @@
 
 class CopyCommandTest_T2T : public CopyCommandTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 
     wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
@@ -2160,14 +2149,9 @@
 
 class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[3] = {wgpu::FeatureName::TextureCompressionBC,
-                                                 wgpu::FeatureName::TextureCompressionETC2,
-                                                 wgpu::FeatureName::TextureCompressionASTC};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 3;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TextureCompressionBC, wgpu::FeatureName::TextureCompressionETC2,
+                wgpu::FeatureName::TextureCompressionASTC};
     }
 
     wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
diff --git a/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp b/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp
index 6f9acd8..58e78c4 100644
--- a/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp
+++ b/src/dawn/tests/unittests/validation/CopyTextureForBrowserTests.cpp
@@ -25,6 +25,8 @@
 // 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 <vector>
+
 #include "dawn/common/Assert.h"
 #include "dawn/common/Constants.h"
 #include "dawn/common/Math.h"
@@ -114,12 +116,8 @@
 
 class CopyTextureForBrowserInternalUsageTest : public CopyTextureForBrowserTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName feature = wgpu::FeatureName::DawnInternalUsages;
-        descriptor.requiredFeatures = &feature;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnInternalUsages};
     }
 };
 
@@ -155,12 +153,8 @@
 
 class CopyExternalTextureForBrowserInternalUsageTest : public CopyExternalTextureForBrowserTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName feature = wgpu::FeatureName::DawnInternalUsages;
-        descriptor.requiredFeatures = &feature;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnInternalUsages};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
index 0219be6..a6f04bc 100644
--- a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
@@ -29,9 +29,16 @@
 
 #include "dawn/native/Device.h"
 #include "dawn/native/dawn_platform.h"
+#include "dawn/tests/MockCallback.h"
 #include "dawn/tests/unittests/validation/ValidationTest.h"
 
-using ::testing::HasSubstr;
+namespace dawn {
+namespace {
+
+using testing::IsNull;
+using testing::MockCppCallback;
+using testing::NotNull;
+using testing::WithArgs;
 
 class RequestDeviceValidationTest : public ValidationTest {
   protected:
@@ -40,51 +47,24 @@
         DAWN_SKIP_TEST_IF(UsesWire());
     }
 
-    static void ExpectRequestDeviceSuccess(WGPURequestDeviceStatus status,
-                                           WGPUDevice cDevice,
-                                           const char* message,
-                                           void* userdata) {
-        wgpu::Device device = wgpu::Device::Acquire(cDevice);
-        EXPECT_EQ(status, WGPURequestDeviceStatus_Success);
-        EXPECT_NE(device, nullptr);
-        EXPECT_STREQ(message, nullptr);
-        if (userdata != nullptr) {
-            CallCheckDevice(static_cast<std::function<void(wgpu::Device)>*>(userdata),
-                            std::move(device));
-        }
-    }
-
-    static void ExpectRequestDeviceError(WGPURequestDeviceStatus status,
-                                         WGPUDevice cDevice,
-                                         const char* message,
-                                         void* userdata) {
-        wgpu::Device device = wgpu::Device::Acquire(cDevice);
-        EXPECT_EQ(status, WGPURequestDeviceStatus_Error);
-        EXPECT_EQ(device, nullptr);
-        EXPECT_STRNE(message, nullptr);
-    }
-
-    template <typename F>
-    static void* CheckDevice(F&& f) {
-        return new std::function<void(wgpu::Device)>(f);
-    }
-
-    static void CallCheckDevice(std::function<void(wgpu::Device)>* f, wgpu::Device d) {
-        (*f)(std::move(d));
-        delete f;
-    }
+    MockCppCallback<void (*)(wgpu::RequestDeviceStatus, wgpu::Device, const char*)>
+        mRequestDeviceCallback;
 };
 
 // Test that requesting a device without specifying limits is valid.
 TEST_F(RequestDeviceValidationTest, NoRequiredLimits) {
     wgpu::DeviceDescriptor descriptor;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
-                                      CheckDevice([](wgpu::Device device) {
-                                          // Check one of the default limits.
-                                          wgpu::SupportedLimits limits;
-                                          device.GetLimits(&limits);
-                                          EXPECT_EQ(limits.limits.maxBindGroups, 4u);
-                                      }));
+
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([](wgpu::Device device) {
+            // Check one of the default limits.
+            wgpu::SupportedLimits limits;
+            device.GetLimits(&limits);
+            EXPECT_EQ(limits.limits.maxBindGroups, 4u);
+        }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 }
 
 // Test that requesting a device with the default limits is valid.
@@ -92,13 +72,17 @@
     wgpu::RequiredLimits limits = {};
     wgpu::DeviceDescriptor descriptor;
     descriptor.requiredLimits = &limits;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
-                                      CheckDevice([](wgpu::Device device) {
-                                          // Check one of the default limits.
-                                          wgpu::SupportedLimits limits;
-                                          device.GetLimits(&limits);
-                                          EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
-                                      }));
+
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([](wgpu::Device device) {
+            // Check one of the default limits.
+            wgpu::SupportedLimits limits;
+            device.GetLimits(&limits);
+            EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+        }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 }
 
 // Test that requesting a device where a required limit is above the maximum value.
@@ -113,8 +97,9 @@
     // If we can support better than the default, test below the max.
     if (supportedLimits.limits.maxBindGroups > 4u) {
         limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups - 1;
-        GetBackendAdapter().RequestDevice(
-            &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+        EXPECT_CALL(mRequestDeviceCallback,
+                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+            .WillOnce(WithArgs<1>([&](wgpu::Device device) {
                 wgpu::SupportedLimits limits;
                 device.GetLimits(&limits);
 
@@ -123,12 +108,15 @@
                 // Check another default limit.
                 EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
             }));
+        adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                              mRequestDeviceCallback.Callback());
     }
 
     // Test the max.
     limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups;
-    GetBackendAdapter().RequestDevice(
-        &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
 
@@ -137,21 +125,29 @@
             // Check another default limit.
             EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
         }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 
     // Test above the max.
     limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups + 1;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
+    EXPECT_CALL(mRequestDeviceCallback, Call(wgpu::RequestDeviceStatus::Error, IsNull(), NotNull()))
+        .Times(1);
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 
     // Test worse than the default
     limits.limits.maxBindGroups = 3u;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
-                                      CheckDevice([&](wgpu::Device device) {
-                                          wgpu::SupportedLimits limits;
-                                          device.GetLimits(&limits);
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([&](wgpu::Device device) {
+            wgpu::SupportedLimits limits;
+            device.GetLimits(&limits);
 
-                                          // Check we got the default.
-                                          EXPECT_EQ(limits.limits.maxBindGroups, 4u);
-                                      }));
+            // Check we got the default.
+            EXPECT_EQ(limits.limits.maxBindGroups, 4u);
+        }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 }
 
 // Test that requesting a device where a required limit is below the minimum value.
@@ -166,13 +162,17 @@
     // Test below the min.
     limits.limits.minUniformBufferOffsetAlignment =
         supportedLimits.limits.minUniformBufferOffsetAlignment / 2;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
+    EXPECT_CALL(mRequestDeviceCallback, Call(wgpu::RequestDeviceStatus::Error, IsNull(), NotNull()))
+        .Times(1);
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 
     // Test the min.
     limits.limits.minUniformBufferOffsetAlignment =
         supportedLimits.limits.minUniformBufferOffsetAlignment;
-    GetBackendAdapter().RequestDevice(
-        &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
 
@@ -182,13 +182,16 @@
             // Check another default limit.
             EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
         }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 
     // IF we can support better than the default, test above the min.
     if (supportedLimits.limits.minUniformBufferOffsetAlignment > 256u) {
         limits.limits.minUniformBufferOffsetAlignment =
             supportedLimits.limits.minUniformBufferOffsetAlignment * 2;
-        GetBackendAdapter().RequestDevice(
-            &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+        EXPECT_CALL(mRequestDeviceCallback,
+                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+            .WillOnce(WithArgs<1>([&](wgpu::Device device) {
                 wgpu::SupportedLimits limits;
                 device.GetLimits(&limits);
 
@@ -198,18 +201,23 @@
                 // Check another default limit.
                 EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
             }));
+        adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                              mRequestDeviceCallback.Callback());
     }
 
     // Test worse than the default
     limits.limits.minUniformBufferOffsetAlignment = 2u * 256u;
-    GetBackendAdapter().RequestDevice(
-        &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+        .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
 
             // Check we got the default.
             EXPECT_EQ(limits.limits.minUniformBufferOffsetAlignment, 256u);
         }));
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 }
 
 // Test that it is an error to request limits with an invalid chained struct
@@ -220,7 +228,10 @@
 
     wgpu::DeviceDescriptor descriptor;
     descriptor.requiredLimits = &limits;
-    GetBackendAdapter().RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
+    EXPECT_CALL(mRequestDeviceCallback, Call(wgpu::RequestDeviceStatus::Error, IsNull(), NotNull()))
+        .Times(1);
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
 }
 
 class DeviceTickValidationTest : public ValidationTest {};
@@ -231,3 +242,6 @@
     device.Destroy();
     device.Tick();
 }
+
+}  // anonymous namespace
+}  // namespace dawn
diff --git a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
index e9ddf11..bf6cc19 100644
--- a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
+++ b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
@@ -25,8 +25,9 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "dawn/tests/unittests/validation/ValidationTest.h"
+#include <vector>
 
+#include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
 #include "dawn/utils/WGPUHelpers.h"
 
@@ -764,12 +765,8 @@
   protected:
     void SetUp() override { ExternalTextureTest::SetUp(); }
 
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Unorm16TextureFormats};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Unorm16TextureFormats};
     }
 
     static constexpr wgpu::TextureFormat kBiplanarPlane0FormatNorm16 =
diff --git a/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp b/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
index c1e3764..aa2999f 100644
--- a/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
@@ -25,8 +25,9 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "dawn/tests/unittests/validation/ValidationTest.h"
+#include <vector>
 
+#include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "dawn/utils/WGPUHelpers.h"
 
 namespace dawn {
@@ -80,12 +81,8 @@
 }
 
 class TextureInternalUsageValidationTest : public ValidationTest {
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::DawnInternalUsages};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnInternalUsages};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/ObjectCachingTests.cpp b/src/dawn/tests/unittests/validation/ObjectCachingTests.cpp
index 8a6071a..530dfb9 100644
--- a/src/dawn/tests/unittests/validation/ObjectCachingTests.cpp
+++ b/src/dawn/tests/unittests/validation/ObjectCachingTests.cpp
@@ -40,12 +40,8 @@
 // These tests works assuming Dawn Native's object deduplication. Comparing the pointer is
 // exploiting an implementation detail of Dawn Native.
 class ObjectCachingTest : public ValidationTest {
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::StaticSamplers};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::StaticSamplers};
     }
 
     void SetUp() override {
diff --git a/src/dawn/tests/unittests/validation/OverridableConstantsValidationTests.cpp b/src/dawn/tests/unittests/validation/OverridableConstantsValidationTests.cpp
index 78a0a17..a178674 100644
--- a/src/dawn/tests/unittests/validation/OverridableConstantsValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/OverridableConstantsValidationTests.cpp
@@ -37,26 +37,8 @@
 
 class ComputePipelineOverridableConstantsValidationTest : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor deviceDescriptor) override {
-        std::vector<const char*> enabledToggles;
-        std::vector<const char*> disabledToggles;
-
-        enabledToggles.push_back("allow_unsafe_apis");
-
-        wgpu::DawnTogglesDescriptor deviceTogglesDesc;
-        deviceTogglesDesc.enabledToggles = enabledToggles.data();
-        deviceTogglesDesc.enabledToggleCount = enabledToggles.size();
-        deviceTogglesDesc.disabledToggles = disabledToggles.data();
-        deviceTogglesDesc.disabledToggleCount = disabledToggles.size();
-
-        const wgpu::FeatureName requiredFeatures[] = {wgpu::FeatureName::ShaderF16};
-
-        deviceDescriptor.nextInChain = &deviceTogglesDesc;
-        deviceDescriptor.requiredFeatures = requiredFeatures;
-        deviceDescriptor.requiredFeatureCount = 1;
-
-        return dawnAdapter.CreateDevice(&deviceDescriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::ShaderF16};
     }
 
     void SetUpShadersWithDefaultValueConstants() {
diff --git a/src/dawn/tests/unittests/validation/PixelLocalStorageTests.cpp b/src/dawn/tests/unittests/validation/PixelLocalStorageTests.cpp
index 947af89..5603b22 100644
--- a/src/dawn/tests/unittests/validation/PixelLocalStorageTests.cpp
+++ b/src/dawn/tests/unittests/validation/PixelLocalStorageTests.cpp
@@ -117,14 +117,10 @@
 
 class PixelLocalStorageOtherExtensionTest : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         // Only test the coherent extension. The non-coherent one has the rest of the validation
         // tests.
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::PixelLocalStorageCoherent};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+        return {wgpu::FeatureName::PixelLocalStorageCoherent};
     }
 };
 
@@ -198,14 +194,10 @@
 
 class PixelLocalStorageTest : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         // Test only the non-coherent version, and assume that the same validation code paths are
         // taken for the coherent path.
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::PixelLocalStorageNonCoherent};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+        return {wgpu::FeatureName::PixelLocalStorageNonCoherent};
     }
 
     void InitializePLSRenderPass(ComboTestPLSRenderPassDescriptor* desc) {
@@ -1181,13 +1173,9 @@
 
 class PixelLocalStorageAndRenderToSingleSampledTest : public PixelLocalStorageTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::PixelLocalStorageNonCoherent,
-                                                 wgpu::FeatureName::MSAARenderToSingleSampled};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::PixelLocalStorageNonCoherent,
+                wgpu::FeatureName::MSAARenderToSingleSampled};
     }
 };
 
@@ -1207,13 +1195,9 @@
 }
 
 class PixelLocalStorageAndTransientAttachmentTest : public PixelLocalStorageTest {
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::PixelLocalStorageNonCoherent,
-                                                 wgpu::FeatureName::TransientAttachments};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::PixelLocalStorageNonCoherent,
+                wgpu::FeatureName::TransientAttachments};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/QueryValidationTests.cpp b/src/dawn/tests/unittests/validation/QueryValidationTests.cpp
index d40decc..54c3f51 100644
--- a/src/dawn/tests/unittests/validation/QueryValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueryValidationTests.cpp
@@ -263,13 +263,8 @@
 
 class TimestampQueryValidationTest : public QuerySetValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::TimestampQuery};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TimestampQuery};
     }
 
     void EncodeRenderPassWithTimestampWrites(
@@ -525,18 +520,12 @@
 
 class TimestampQueryInsidePassesValidationTest : public QuerySetValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         // The timestamp query feature must be supported if the chromium experimental timestamp
         // query inside passes feature is supported. Enable timestamp query for validating queries
         // overwrite inside and outside of the passes.
-        wgpu::FeatureName requiredFeatures[2] = {
-            wgpu::FeatureName::TimestampQuery,
-            wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-
-        return dawnAdapter.CreateDevice(&descriptor);
+        return {wgpu::FeatureName::TimestampQuery,
+                wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/src/dawn/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
index 8d62422..33dad3d 100644
--- a/src/dawn/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
@@ -550,14 +550,9 @@
 
 class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[3] = {wgpu::FeatureName::TextureCompressionBC,
-                                                 wgpu::FeatureName::TextureCompressionETC2,
-                                                 wgpu::FeatureName::TextureCompressionASTC};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 3;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TextureCompressionBC, wgpu::FeatureName::TextureCompressionETC2,
+                wgpu::FeatureName::TextureCompressionASTC};
     }
 
     wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
diff --git a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
index a40a8ed..2e1efd8 100644
--- a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
@@ -1721,12 +1721,8 @@
         mRenderToSingleSampledDesc.implicitSampleCount = kSampleCount;
     }
 
-    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::MSAARenderToSingleSampled};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::MSAARenderToSingleSampled};
     }
 
     utils::ComboRenderPassDescriptor CreateMultisampledRenderToSingleSampledRenderPass(
@@ -1861,14 +1857,8 @@
 
 class DawnLoadResolveTextureValidationTest : public MultisampledRenderPassDescriptorValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::DawnLoadResolveTexture,
-                                                 wgpu::FeatureName::TransientAttachments};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnLoadResolveTexture, wgpu::FeatureName::TransientAttachments};
     }
 
     // Create a view for a resolve texture that can be used with LoadOp::ExpandResolveTexture.
diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
index b8a7494..071d7b1 100644
--- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -40,13 +40,8 @@
 
 class RenderPipelineValidationTest : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::ShaderF16};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::ShaderF16};
     }
 
     void SetUp() override {
@@ -1920,12 +1915,8 @@
 
 class DepthClipControlValidationTest : public RenderPipelineValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::DepthClipControl};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DepthClipControl};
     }
 };
 
@@ -2219,13 +2210,8 @@
 
 class RenderPipelineTransientAttachmentValidationTest : public RenderPipelineValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::ShaderF16,
-                                                 wgpu::FeatureName::TransientAttachments};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::ShaderF16, wgpu::FeatureName::TransientAttachments};
     }
 };
 
@@ -2345,12 +2331,8 @@
             })");
     }
 
-    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::DawnLoadResolveTexture};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnLoadResolveTexture};
     }
 
     wgpu::Texture CreateTexture(wgpu::TextureUsage textureUsage, uint32_t sampleCount) {
@@ -2596,12 +2578,8 @@
 
 class DualSourceBlendingFeatureTest : public RenderPipelineValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::DualSourceBlending};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DualSourceBlending};
     }
 };
 
@@ -2702,12 +2680,8 @@
 
 class FramebufferFetchFeatureTest : public RenderPipelineValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::FramebufferFetch};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::FramebufferFetch};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp b/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
index 0c2ff7d..5fdecba 100644
--- a/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
@@ -764,51 +764,12 @@
         ValidationTest::SetUp();
     }
 
-    // Create testing adapter with the AllowUnsafeAPIs toggle explicitly enabled or disabled,
-    // overriding the instance's toggle.
-    void CreateTestAdapterWithUnsafeAPIToggle(wgpu::RequestAdapterOptions options,
-                                              bool allowUnsafeAPIs) {
-        wgpu::DawnTogglesDescriptor deviceTogglesDesc{};
-        options.nextInChain = &deviceTogglesDesc;
-        const char* toggle = "allow_unsafe_apis";
-        // Explicitly enable or disable the AllowUnsafeAPIs toggle.
-        if (allowUnsafeAPIs) {
-            deviceTogglesDesc.enabledToggles = &toggle;
-            deviceTogglesDesc.enabledToggleCount = 1;
-        } else {
-            deviceTogglesDesc.disabledToggles = &toggle;
-            deviceTogglesDesc.disabledToggleCount = 1;
-        }
-
-        instance.RequestAdapter(
-            &options,
-            [](WGPURequestAdapterStatus, WGPUAdapter cAdapter, const char*, void* userdata) {
-                *static_cast<wgpu::Adapter*>(userdata) = wgpu::Adapter::Acquire(cAdapter);
-            },
-            &adapter);
-        FlushWire();
-    }
-
-    // Create the device with none or all valid features required.
-    WGPUDevice CreateTestDeviceWithAllFeatures(native::Adapter dawnAdapter,
-                                               wgpu::DeviceDescriptor descriptor,
-                                               bool requireAllFeatures) {
+    std::vector<wgpu::FeatureName> GetAllFeatures() {
         std::vector<wgpu::FeatureName> requiredFeatures;
-
-        if (requireAllFeatures) {
-            // Require all features that the adapter supports.
-            WGPUAdapter adapter = dawnAdapter.Get();
-            const size_t adapterSupportedFeaturesCount =
-                wgpuAdapterEnumerateFeatures(adapter, nullptr);
-            requiredFeatures.resize(adapterSupportedFeaturesCount);
-            wgpuAdapterEnumerateFeatures(
-                adapter, reinterpret_cast<WGPUFeatureName*>(requiredFeatures.data()));
-        }
-
-        descriptor.requiredFeatures = requiredFeatures.data();
-        descriptor.requiredFeatureCount = requiredFeatures.size();
-
-        return dawnAdapter.CreateDevice(&descriptor);
+        const size_t featureCount = adapter.EnumerateFeatures(nullptr);
+        requiredFeatures.resize(featureCount);
+        adapter.EnumerateFeatures(requiredFeatures.data());
+        return requiredFeatures;
     }
 };
 
@@ -841,15 +802,8 @@
 class ShaderModuleExtensionValidationTestSafeNoFeature
     : public ShaderModuleExtensionValidationTestBase {
   protected:
-    void CreateTestAdapter(wgpu::RequestAdapterOptions options) override {
-        // Create a safe adapter
-        CreateTestAdapterWithUnsafeAPIToggle(options, false);
-    }
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        // Create a device requiring no features
-        return CreateTestDeviceWithAllFeatures(dawnAdapter, descriptor, false);
-    }
+    bool AllowUnsafeAPIs() override { return false; }
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override { return {}; }
 };
 
 TEST_F(ShaderModuleExtensionValidationTestSafeNoFeature,
@@ -873,15 +827,7 @@
 class ShaderModuleExtensionValidationTestUnsafeNoFeature
     : public ShaderModuleExtensionValidationTestBase {
   protected:
-    void CreateTestAdapter(wgpu::RequestAdapterOptions options) override {
-        // Create an unsafe adapter
-        CreateTestAdapterWithUnsafeAPIToggle(options, true);
-    }
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        // Create a device requiring no features
-        return CreateTestDeviceWithAllFeatures(dawnAdapter, descriptor, false);
-    }
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override { return {}; }
 };
 
 TEST_F(ShaderModuleExtensionValidationTestUnsafeNoFeature,
@@ -905,15 +851,8 @@
 class ShaderModuleExtensionValidationTestSafeAllFeatures
     : public ShaderModuleExtensionValidationTestBase {
   protected:
-    void CreateTestAdapter(wgpu::RequestAdapterOptions options) override {
-        // Create a safe adapter
-        CreateTestAdapterWithUnsafeAPIToggle(options, false);
-    }
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        // Create a device requiring all features
-        return CreateTestDeviceWithAllFeatures(dawnAdapter, descriptor, true);
-    }
+    bool AllowUnsafeAPIs() override { return false; }
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override { return GetAllFeatures(); }
 };
 
 TEST_F(ShaderModuleExtensionValidationTestSafeAllFeatures, OnlyStableExtensionsAllowed) {
@@ -935,15 +874,7 @@
 class ShaderModuleExtensionValidationTestUnsafeAllFeatures
     : public ShaderModuleExtensionValidationTestBase {
   protected:
-    void CreateTestAdapter(wgpu::RequestAdapterOptions options) override {
-        // Create an unsafe adapter
-        CreateTestAdapterWithUnsafeAPIToggle(options, true);
-    }
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        // Create a device requiring all features
-        return CreateTestDeviceWithAllFeatures(dawnAdapter, descriptor, true);
-    }
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override { return GetAllFeatures(); }
 };
 
 TEST_F(ShaderModuleExtensionValidationTestUnsafeAllFeatures, AllExtensionsAllowed) {
diff --git a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
index 0bf03db..a20d502 100644
--- a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -285,14 +285,11 @@
       // Bool param indicates whether requires the BGRA8UnormStorage feature or not.
       public ::testing::WithParamInterface<bool> {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         if (GetParam()) {
-            descriptor.requiredFeatures = requiredFeatures;
-            descriptor.requiredFeatureCount = 1;
+            return {wgpu::FeatureName::BGRA8UnormStorage};
         }
-        return dawnAdapter.CreateDevice(&descriptor);
+        return {};
     }
 };
 
@@ -494,12 +491,8 @@
 
 class BGRA8UnormStorageBindGroupLayoutTest : public StorageTextureValidationTests {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::BGRA8UnormStorage};
     }
 };
 
@@ -1192,12 +1185,8 @@
 }
 
 class R8UnormStorageValidationTests : public StorageTextureValidationTests {
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::R8UnormStorage};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::R8UnormStorage};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
index 59b7b37..e57fc85 100644
--- a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
@@ -25,10 +25,11 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "dawn/tests/unittests/validation/ValidationTest.h"
+#include <vector>
 
 #include "dawn/common/Constants.h"
 #include "dawn/common/Math.h"
+#include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
 #include "dawn/utils/TextureUtils.h"
 #include "dawn/utils/WGPUHelpers.h"
@@ -713,12 +714,8 @@
 
 class D32S8TextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 };
 
@@ -735,15 +732,9 @@
 
 class CompressedTextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[3] = {wgpu::FeatureName::TextureCompressionBC,
-                                                 wgpu::FeatureName::TextureCompressionETC2,
-                                                 wgpu::FeatureName::TextureCompressionASTC};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 3;
-
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TextureCompressionBC, wgpu::FeatureName::TextureCompressionETC2,
+                wgpu::FeatureName::TextureCompressionASTC};
     }
 
     wgpu::TextureDescriptor CreateDefaultTextureDescriptor() {
@@ -892,12 +883,8 @@
 
 class RG11B10UfloatTextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::RG11B10UfloatRenderable};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::RG11B10UfloatRenderable};
     }
 };
 
@@ -915,12 +902,8 @@
 
 class BGRA8UnormTextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::BGRA8UnormStorage};
     }
 };
 
@@ -936,12 +919,8 @@
 
 class Unorm16TextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Unorm16TextureFormats};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Unorm16TextureFormats};
     }
 };
 
@@ -961,12 +940,8 @@
 
 class Snorm16TextureFormatsValidationTests : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Snorm16TextureFormats};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Snorm16TextureFormats};
     }
 };
 
@@ -1132,12 +1107,8 @@
 
 class TransientAttachmentValidationTest : public TextureValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::TransientAttachments};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TransientAttachments};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
index cf17283..f56277d 100644
--- a/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <array>
+#include <vector>
 
 #include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "dawn/utils/WGPUHelpers.h"
@@ -946,12 +947,8 @@
 
 class D32S8TextureViewValidationTests : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Depth32FloatStencil8};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::Depth32FloatStencil8};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/UnsafeAPIValidationTests.cpp b/src/dawn/tests/unittests/validation/UnsafeAPIValidationTests.cpp
index b7129c7..de7ee23 100644
--- a/src/dawn/tests/unittests/validation/UnsafeAPIValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/UnsafeAPIValidationTests.cpp
@@ -42,16 +42,10 @@
   protected:
     // UnsafeAPIValidationTest create the device with the AllowUnsafeAPIs toggle explicitly
     // disabled, which overrides the inheritance.
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
+    std::vector<const char*> GetDisabledToggles() override {
         // Disable the AllowUnsafeAPIs toggles in device toggles descriptor to override the
         // inheritance and create a device disallowing unsafe apis.
-        wgpu::DawnTogglesDescriptor deviceTogglesDesc;
-        descriptor.nextInChain = &deviceTogglesDesc;
-        const char* toggle = "allow_unsafe_apis";
-        deviceTogglesDesc.disabledToggles = &toggle;
-        deviceTogglesDesc.disabledToggleCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+        return {"allow_unsafe_apis"};
     }
 };
 
@@ -72,17 +66,9 @@
 
 class TimestampQueryUnsafeAPIValidationTest : public ValidationTest {
   protected:
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::DawnTogglesDescriptor deviceTogglesDesc;
-        descriptor.nextInChain = &deviceTogglesDesc;
-        const char* toggle = "allow_unsafe_apis";
-        deviceTogglesDesc.disabledToggles = &toggle;
-        deviceTogglesDesc.disabledToggleCount = 1;
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::TimestampQuery};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<const char*> GetDisabledToggles() override { return {"allow_unsafe_apis"}; }
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::TimestampQuery};
     }
 };
 
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index 13ab01c..8256473 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -106,13 +106,13 @@
 
     // Forward to dawn::native instanceRequestAdapter, but save the returned adapter in
     // gCurrentTest->mBackendAdapter.
-    procs.instanceRequestAdapter2 = [](WGPUInstance i, const WGPURequestAdapterOptions* options,
+    procs.instanceRequestAdapter2 = [](WGPUInstance self, const WGPURequestAdapterOptions* options,
                                        WGPURequestAdapterCallbackInfo2 callbackInfo) -> WGPUFuture {
         DAWN_ASSERT(gCurrentTest);
         DAWN_ASSERT(callbackInfo.mode == WGPUCallbackMode_AllowSpontaneous);
 
         return dawn::native::GetProcs().instanceRequestAdapter2(
-            i, options,
+            self, options,
             {nullptr, WGPUCallbackMode_AllowSpontaneous,
              [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter, char const* message,
                 void* userdata, void*) {
@@ -125,41 +125,72 @@
              new WGPURequestAdapterCallbackInfo2(callbackInfo), nullptr});
     };
 
-    procs.adapterRequestDevice = [](WGPUAdapter self, const WGPUDeviceDescriptor* descriptor,
-                                    WGPURequestDeviceCallback callback, void* userdata) {
+    // Forward to dawn::native instanceRequestAdapter, but save the returned backend device in
+    // gCurrentTest->mLastCreatedBackendDevice.
+    procs.adapterRequestDevice2 = [](WGPUAdapter self, const WGPUDeviceDescriptor* descriptor,
+                                     WGPURequestDeviceCallbackInfo2 callbackInfo) -> WGPUFuture {
         DAWN_ASSERT(gCurrentTest);
+        DAWN_ASSERT(callbackInfo.mode == WGPUCallbackMode_AllowSpontaneous);
+
         wgpu::DeviceDescriptor deviceDesc = {};
         if (descriptor != nullptr) {
             deviceDesc = *(reinterpret_cast<const wgpu::DeviceDescriptor*>(descriptor));
         }
-        WGPUDevice cDevice = gCurrentTest->CreateTestDevice(
-            dawn::native::Adapter(reinterpret_cast<dawn::native::AdapterBase*>(self)), deviceDesc);
-        DAWN_ASSERT(cDevice != nullptr);
-        gCurrentTest->mLastCreatedBackendDevice = cDevice;
-        callback(WGPURequestDeviceStatus_Success, cDevice, nullptr, userdata);
+
+        // Set the toggles for the device. We start with all test specific toggles, then toggle
+        // flags so that toggle flags will always take precedence. Note that disabling toggles also
+        // take precedence.
+        wgpu::DawnTogglesDescriptor deviceTogglesDesc;
+        deviceTogglesDesc.nextInChain = deviceDesc.nextInChain;
+        deviceDesc.nextInChain = &deviceTogglesDesc;
+
+        auto enabledToggles = gCurrentTest->GetEnabledToggles();
+        auto disabledToggles = gCurrentTest->GetDisabledToggles();
+        for (const std::string& toggle : gToggleParser->GetEnabledToggles()) {
+            enabledToggles.push_back(toggle.c_str());
+        }
+        for (const std::string& toggle : gToggleParser->GetDisabledToggles()) {
+            disabledToggles.push_back(toggle.c_str());
+        }
+        deviceTogglesDesc.enabledToggles = enabledToggles.data();
+        deviceTogglesDesc.enabledToggleCount = enabledToggles.size();
+        deviceTogglesDesc.disabledToggles = disabledToggles.data();
+        deviceTogglesDesc.disabledToggleCount = disabledToggles.size();
+
+        return dawn::native::GetProcs().adapterRequestDevice2(
+            self, reinterpret_cast<WGPUDeviceDescriptor*>(&deviceDesc),
+            {nullptr, WGPUCallbackMode_AllowSpontaneous,
+             [](WGPURequestDeviceStatus status, WGPUDevice cDevice, const char* message,
+                void* userdata, void*) {
+                 gCurrentTest->mLastCreatedBackendDevice = cDevice;
+
+                 auto* info = static_cast<WGPURequestDeviceCallbackInfo2*>(userdata);
+                 info->callback(status, cDevice, message, info->userdata1, info->userdata2);
+                 delete info;
+             },
+             new WGPURequestDeviceCallbackInfo2(callbackInfo), nullptr});
     };
 
     mWireHelper = dawn::utils::CreateWireHelper(procs, gUseWire, gWireTraceDir.c_str());
 }
 
 void ValidationTest::SetUp() {
-    std::string traceName =
-        std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
-        "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
-    mWireHelper->BeginWireTrace(traceName.c_str());
-
-    // Create an instance with toggle AllowUnsafeAPIs enabled, which would be inherited to
-    // adapter and device toggles and allow us to test unsafe apis (including experimental
+    // By default create the instance with toggle AllowUnsafeAPIs enabled, which would be inherited
+    // to adapter and device toggles and allow us to test unsafe apis (including experimental
     // features). To test device with AllowUnsafeAPIs disabled, require it in device toggles
-    // descriptor to override the inheritance.
-    const char* allowUnsafeApisToggle = "allow_unsafe_apis";
+    // descriptor to override the inheritance. Alternatively, override AllowUnsafeAPIs() if
+    // querying for features via the adapter, i.e. prior to device creation.
     wgpu::DawnTogglesDescriptor instanceToggles = {};
-    instanceToggles.enabledToggleCount = 1;
-    instanceToggles.enabledToggles = &allowUnsafeApisToggle;
+    if (AllowUnsafeAPIs()) {
+        static const char* allowUnsafeApisToggle = "allow_unsafe_apis";
+        instanceToggles.enabledToggleCount = 1;
+        instanceToggles.enabledToggles = &allowUnsafeApisToggle;
+    }
 
     wgpu::InstanceDescriptor instanceDesc = {};
     instanceDesc.nextInChain = &instanceToggles;
-    ReinitializeInstances(&instanceDesc);
+
+    SetUp(&instanceDesc);
 }
 
 ValidationTest::~ValidationTest() {
@@ -261,6 +292,22 @@
     return supportedLimits;
 }
 
+bool ValidationTest::AllowUnsafeAPIs() {
+    return true;
+}
+
+std::vector<wgpu::FeatureName> ValidationTest::GetRequiredFeatures() {
+    return {};
+}
+
+std::vector<const char*> ValidationTest::GetEnabledToggles() {
+    return {};
+}
+
+std::vector<const char*> ValidationTest::GetDisabledToggles() {
+    return {};
+}
+
 dawn::utils::WireHelper* ValidationTest::GetWireHelper() const {
     return mWireHelper.get();
 }
@@ -270,11 +317,14 @@
 
     wgpu::Device apiDevice;
     adapter.RequestDevice(
-        &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
-            *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
-        },
-        &apiDevice);
+        &deviceDesc, wgpu::CallbackMode::AllowSpontaneous,
+        [&apiDevice](wgpu::RequestDeviceStatus status, wgpu::Device result, const char* message) {
+            if (status != wgpu::RequestDeviceStatus::Success) {
+                ADD_FAILURE() << "Unable to create device: " << message;
+                DAWN_ASSERT(false);
+            }
+            apiDevice = std::move(result);
+        });
     FlushWire();
 
     DAWN_ASSERT(apiDevice);
@@ -285,63 +335,42 @@
     return mBackendAdapter;
 }
 
-void ValidationTest::CreateTestAdapter(wgpu::RequestAdapterOptions options) {
-    instance.RequestAdapter(
-        &options, wgpu::CallbackMode::AllowSpontaneous,
-        [](wgpu::RequestAdapterStatus status, wgpu::Adapter result, char const* message,
-           wgpu::Adapter* userdata) -> void { *userdata = std::move(result); },
-        &adapter);
-    FlushWire();
-}
+void ValidationTest::SetUp(const wgpu::InstanceDescriptor* nativeDesc,
+                           const wgpu::InstanceDescriptor* wireDesc) {
+    std::string traceName =
+        std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
+        "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
+    mWireHelper->BeginWireTrace(traceName.c_str());
 
-WGPUDevice ValidationTest::CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                            wgpu::DeviceDescriptor deviceDescriptor) {
-    std::vector<const char*> enabledToggles;
-    std::vector<const char*> disabledToggles;
-
-    for (const std::string& toggle : gToggleParser->GetEnabledToggles()) {
-        enabledToggles.push_back(toggle.c_str());
-    }
-
-    for (const std::string& toggle : gToggleParser->GetDisabledToggles()) {
-        disabledToggles.push_back(toggle.c_str());
-    }
-
-    wgpu::DawnTogglesDescriptor deviceTogglesDesc;
-    deviceDescriptor.nextInChain = &deviceTogglesDesc;
-
-    deviceTogglesDesc.enabledToggles = enabledToggles.data();
-    deviceTogglesDesc.enabledToggleCount = enabledToggles.size();
-    deviceTogglesDesc.disabledToggles = disabledToggles.data();
-    deviceTogglesDesc.disabledToggleCount = disabledToggles.size();
-
-    return dawnAdapter.CreateDevice(&deviceDescriptor);
-}
-
-void ValidationTest::ReinitializeInstances(const wgpu::InstanceDescriptor* nativeDesc,
-                                           const wgpu::InstanceDescriptor* wireDesc) {
-    // Reinitialize the instances.
+    // Initialize the instances.
     std::tie(instance, mDawnInstance) = mWireHelper->CreateInstances(nativeDesc, wireDesc);
 
-    // Reinitialize the adapter.
+    // Initialize the adapter.
     wgpu::RequestAdapterOptions options = {};
     options.backendType = wgpu::BackendType::Null;
     options.compatibilityMode = gCurrentTest->UseCompatibilityMode();
-
-    CreateTestAdapter(options);
+    instance.RequestAdapter(&options, wgpu::CallbackMode::AllowSpontaneous,
+                            [this](wgpu::RequestAdapterStatus, wgpu::Adapter result,
+                                   char const*) -> void { adapter = std::move(result); });
+    FlushWire();
     DAWN_ASSERT(adapter);
 
-    // Reinitialize the device.
-    mExpectDestruction = true;
+    // Initialize the device.
     wgpu::DeviceDescriptor deviceDescriptor = {};
     deviceDescriptor.deviceLostCallbackInfo = {nullptr, wgpu::CallbackMode::AllowSpontaneous,
                                                ValidationTest::OnDeviceLost, this};
-    deviceDescriptor.uncapturedErrorCallbackInfo.callback = ValidationTest::OnDeviceError;
-    deviceDescriptor.uncapturedErrorCallbackInfo.userdata = this;
+    deviceDescriptor.uncapturedErrorCallbackInfo = {nullptr, ValidationTest::OnDeviceError, this};
+
+    // Set the required features for the device.
+    auto requiredFeatures = GetRequiredFeatures();
+    deviceDescriptor.requiredFeatures = requiredFeatures.data();
+    deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
 
     device = RequestDeviceSync(deviceDescriptor);
+    DAWN_ASSERT(device);
+
+    // We only want to set the backendDevice when the device was created via the test setup.
     backendDevice = mLastCreatedBackendDevice;
-    mExpectDestruction = false;
 }
 
 bool ValidationTest::UseCompatibilityMode() const {
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.h b/src/dawn/tests/unittests/validation/ValidationTest.h
index cd746e6..974770a 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.h
+++ b/src/dawn/tests/unittests/validation/ValidationTest.h
@@ -30,6 +30,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "dawn/common/Log.h"
 #include "dawn/native/BindGroupLayout.h"
@@ -128,6 +129,8 @@
     ValidationTest();
     ~ValidationTest() override;
 
+    // The default setup initializes the Instance with AllowUnsafeAPIs enabled and additional
+    // toggles and features via the getters enabled/disabled on the device.
     void SetUp() override;
     void TearDown() override;
 
@@ -164,18 +167,17 @@
 
   protected:
     dawn::native::Adapter& GetBackendAdapter();
-    // Helper function to create testing adapter and device during SetUp. Override these functions
-    // to change the creation behavior.
-    virtual void CreateTestAdapter(wgpu::RequestAdapterOptions options);
-    virtual WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
-                                        wgpu::DeviceDescriptor descriptor);
 
-    // Reinitializes the ValidationTest internal members given the instance descriptors.
-    // Particularly useful when writing tests that may need to pass different toggles to the
-    // instance. Note that this also reinitializes the adapter and device on the new instances via
-    // potentially overriden CreateTest[Adapter|Device] functions above.
-    void ReinitializeInstances(const wgpu::InstanceDescriptor* nativeDesc,
-                               const wgpu::InstanceDescriptor* wireDesc = nullptr);
+    // Called during SetUp() to get the required features and toggles to be enabled for the tests.
+    // Override these appropriately for different tests.
+    virtual bool AllowUnsafeAPIs();
+    virtual std::vector<wgpu::FeatureName> GetRequiredFeatures();
+    virtual std::vector<const char*> GetEnabledToggles();
+    virtual std::vector<const char*> GetDisabledToggles();
+
+    // Sets up the internal members by initializing the instances, adapter, and device.
+    void SetUp(const wgpu::InstanceDescriptor* nativeDesc,
+               const wgpu::InstanceDescriptor* wireDesc = nullptr);
 
     wgpu::Device RequestDeviceSync(const wgpu::DeviceDescriptor& deviceDesc);
     static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
diff --git a/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
index 526f872..8bc7b18 100644
--- a/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/WGSLFeatureValidationTests.cpp
@@ -37,8 +37,6 @@
 
 class WGSLFeatureValidationTest : public ValidationTest {
   protected:
-    void SetUp() override { ValidationTest::SetUp(); }
-
     struct InstanceSpec {
         bool useTestingFeatures = true;
         bool allowUnsafeAPIs = false;
@@ -46,7 +44,10 @@
         std::vector<const char*> blocklist = {};
     };
 
-    void ReinitializeInstances(InstanceSpec spec) {
+    // Override the default SetUp to do nothing since the tests will call SetUp(InstanceSpec)
+    // explicitly each time.
+    void SetUp() override {}
+    void SetUp(InstanceSpec spec) {
         // The blocklist that will be shared between both the native and wire descriptors.
         wgpu::DawnWGSLBlocklist blocklist;
         blocklist.blocklistedFeatureCount = spec.blocklist.size();
@@ -82,7 +83,7 @@
         wgpu::InstanceDescriptor wireDesc;
         wireDesc.nextInChain = &wgslControl;
 
-        ValidationTest::ReinitializeInstances(&nativeDesc, &wireDesc);
+        ValidationTest::SetUp(&nativeDesc, &wireDesc);
     }
 };
 
@@ -90,7 +91,7 @@
 
 // Check HasFeature for an Instance that doesn't have unsafe APIs.
 TEST_F(WGSLFeatureValidationTest, HasFeatureDefaultInstance) {
-    ReinitializeInstances({});
+    SetUp({});
 
     // Shipped features are present.
     ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
@@ -111,7 +112,7 @@
 
 // Check HasFeature for an Instance that has unsafe APIs.
 TEST_F(WGSLFeatureValidationTest, HasFeatureExposeExperimental) {
-    ReinitializeInstances({.exposeExperimental = true});
+    SetUp({.exposeExperimental = true});
 
     // Shipped and experimental features are present.
     ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
@@ -132,7 +133,7 @@
 
 // Check HasFeature for an Instance that has unsafe APIs.
 TEST_F(WGSLFeatureValidationTest, HasFeatureAllowUnsafeInstance) {
-    ReinitializeInstances({.allowUnsafeAPIs = true});
+    SetUp({.allowUnsafeAPIs = true});
 
     // Shipped and experimental features are present.
     ASSERT_TRUE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
@@ -153,7 +154,7 @@
 
 // Check HasFeature for an Instance that doesn't have the expose_wgsl_testing_features toggle.
 TEST_F(WGSLFeatureValidationTest, HasFeatureWithoutExposeWGSLTestingFeatures) {
-    ReinitializeInstances({.useTestingFeatures = false});
+    SetUp({.useTestingFeatures = false});
 
     // None of the testing features are present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
@@ -169,7 +170,7 @@
 
 // Tests for the behavior of WGSL feature enumeration.
 TEST_F(WGSLFeatureValidationTest, EnumerateFeatures) {
-    ReinitializeInstances({});
+    SetUp({});
 
     size_t featureCount = instance.EnumerateWGSLLanguageFeatures(nullptr);
 
@@ -205,7 +206,7 @@
 
 // Check that the enabled / disabled features are used to validate the WGSL shaders.
 TEST_F(WGSLFeatureValidationTest, UsingFeatureInShaderModule) {
-    ReinitializeInstances({});
+    SetUp({});
 
     utils::CreateShaderModule(device, R"(
         requires chromium_testing_shipped;
@@ -227,8 +228,7 @@
 
 // Test using DawnWGSLBlocklist to block features with a killswitch by name.
 TEST_F(WGSLFeatureValidationTest, BlockListOfKillswitchedFeatures) {
-    ReinitializeInstances(
-        {.allowUnsafeAPIs = true, .blocklist = {"chromium_testing_shipped_with_killswitch"}});
+    SetUp({.allowUnsafeAPIs = true, .blocklist = {"chromium_testing_shipped_with_killswitch"}});
 
     // The blocklisted feature is not present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(
@@ -249,10 +249,9 @@
 
 // Test that DawnWGSLBlocklist can block any feature name (even without a killswitch).
 TEST_F(WGSLFeatureValidationTest, BlockListOfAnyFeature) {
-    ReinitializeInstances(
-        {.allowUnsafeAPIs = true,
-         .blocklist = {"chromium_testing_shipped", "chromium_testing_experimental",
-                       "chromium_testing_unsafe_experimental"}});
+    SetUp({.allowUnsafeAPIs = true,
+           .blocklist = {"chromium_testing_shipped", "chromium_testing_experimental",
+                         "chromium_testing_unsafe_experimental"}});
 
     // All blocklisted features aren't present.
     ASSERT_FALSE(instance.HasWGSLLanguageFeature(wgpu::WGSLFeatureName::ChromiumTestingShipped));
@@ -264,7 +263,7 @@
 
 // Test that DawnWGSLBlocklist can contain garbage names without causing problems.
 TEST_F(WGSLFeatureValidationTest, BlockListGarbageName) {
-    ReinitializeInstances({.blocklist = {"LE_GARBAGE"}});
+    SetUp({.blocklist = {"LE_GARBAGE"}});
     ASSERT_NE(instance, nullptr);
 }
 
diff --git a/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
index 0092982..8f4ad11 100644
--- a/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
@@ -105,13 +105,8 @@
         DAWN_SKIP_TEST_IF(UsesWire());
     }
 
-    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
-                                wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::StaticSamplers,
-                                                 wgpu::FeatureName::YCbCrVulkanSamplers};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 2;
-        return dawnAdapter.CreateDevice(&descriptor);
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::StaticSamplers, wgpu::FeatureName::YCbCrVulkanSamplers};
     }
 };
 
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index 63c3552..dc02191 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -138,18 +138,19 @@
     EXPECT_CALL(deviceLostCallback, Call).Times(AtMost(1));
     deviceDesc.uncapturedErrorCallbackInfo.callback = uncapturedErrorCallback.Callback();
     deviceDesc.uncapturedErrorCallbackInfo.userdata = uncapturedErrorCallback.MakeUserdata(this);
-    MockCallback<WGPURequestDeviceCallback> deviceCb;
-    wgpuAdapterRequestDevice(adapter.Get(), &deviceDesc, deviceCb.Callback(),
-                             deviceCb.MakeUserdata(this));
-    EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
+    MockCallback<WGPURequestDeviceCallback2> deviceCb;
+    wgpuAdapterRequestDevice2(adapter.Get(), &deviceDesc,
+                              {nullptr, WGPUCallbackMode_AllowSpontaneous, deviceCb.Callback(),
+                               nullptr, deviceCb.MakeUserdata(this)});
+    EXPECT_CALL(api, OnAdapterRequestDevice2(apiAdapter, NotNull(), _))
         .WillOnce(WithArg<1>([&](const WGPUDeviceDescriptor* desc) {
             // Set on device creation to forward callbacks to the client.
             EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
                 .Times(1);
             EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
 
-            // The mock objects currently require us to manually set the callbacks because we are no
-            // longer explicitly calling SetDeviceLostCallback anymore.
+            // The mock objects currently require us to manually set the callbacks because we
+            // are no longer explicitly calling SetDeviceLostCallback anymore.
             ProcTableAsClass::Object* object =
                 reinterpret_cast<ProcTableAsClass::Object*>(apiDevice);
             object->mDeviceLostCallback = desc->deviceLostCallbackInfo.callback;
@@ -165,11 +166,11 @@
                 .WillOnce(Return(0))
                 .WillOnce(Return(0));
 
-            api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
-                                                 apiDevice, nullptr);
+            api.CallAdapterRequestDevice2Callback(apiAdapter, WGPURequestDeviceStatus_Success,
+                                                  apiDevice, nullptr);
         }));
     FlushClient();
-    EXPECT_CALL(deviceCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+    EXPECT_CALL(deviceCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, nullptr, this))
         .WillOnce(SaveArg<1>(&device));
     FlushServer();
     EXPECT_NE(device, nullptr);
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index e4a240f..ccc3dc5 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -45,7 +45,14 @@
     RequestDeviceEvent(const WGPURequestDeviceCallbackInfo& callbackInfo, Device* device)
         : TrackedEvent(callbackInfo.mode),
           mCallback(callbackInfo.callback),
-          mUserdata(callbackInfo.userdata),
+          mUserdata1(callbackInfo.userdata),
+          mDevice(device) {}
+
+    RequestDeviceEvent(const WGPURequestDeviceCallbackInfo2& callbackInfo, Device* device)
+        : TrackedEvent(callbackInfo.mode),
+          mCallback2(callbackInfo.callback),
+          mUserdata1(callbackInfo.userdata1),
+          mUserdata2(callbackInfo.userdata2),
           mDevice(device) {}
 
     EventType GetType() override { return kType; }
@@ -76,10 +83,15 @@
         }
 
         Device* device = mDevice.ExtractAsDangling();
+        // Callback needs to happen before device lost handling to ensure resolution order.
         if (mCallback) {
-            // Callback needs to happen before device lost handling to ensure resolution order.
             mCallback(mStatus, ToAPI(mStatus == WGPURequestDeviceStatus_Success ? device : nullptr),
-                      mMessage ? mMessage->c_str() : nullptr, mUserdata.ExtractAsDangling());
+                      mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling());
+        } else if (mCallback2) {
+            mCallback2(mStatus,
+                       ToAPI(mStatus == WGPURequestDeviceStatus_Success ? device : nullptr),
+                       mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling(),
+                       mUserdata2.ExtractAsDangling());
         }
 
         if (mStatus != WGPURequestDeviceStatus_Success) {
@@ -94,15 +106,18 @@
             }
         }
 
-        if (mCallback == nullptr) {
+        if (mCallback == nullptr && mCallback2 == nullptr) {
             // If there's no callback, clean up the resources.
             device->Release();
-            mUserdata.ExtractAsDangling();
+            mUserdata1.ExtractAsDangling();
+            mUserdata2.ExtractAsDangling();
         }
     }
 
-    WGPURequestDeviceCallback mCallback;
-    raw_ptr<void> mUserdata;
+    WGPURequestDeviceCallback mCallback = nullptr;
+    WGPURequestDeviceCallback2 mCallback2 = nullptr;
+    raw_ptr<void> mUserdata1;
+    raw_ptr<void> mUserdata2;
 
     // Note that the message is optional because we want to return nullptr when it wasn't set
     // instead of a pointer to an empty string.
@@ -278,6 +293,43 @@
     cmd.deviceObjectHandle = device->GetWireHandle();
     cmd.deviceLostFuture = device->GetDeviceLostFuture();
     cmd.descriptor = &wireDescriptor;
+    cmd.userdataCount = 1;
+
+    client->SerializeCommand(cmd);
+    return {futureIDInternal};
+}
+
+WGPUFuture Adapter::RequestDevice2(const WGPUDeviceDescriptor* descriptor,
+                                   const WGPURequestDeviceCallbackInfo2& callbackInfo) {
+    Client* client = GetClient();
+    Device* device = client->Make<Device>(GetEventManagerHandle(), descriptor);
+    auto [futureIDInternal, tracked] =
+        GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(callbackInfo, device));
+    if (!tracked) {
+        return {futureIDInternal};
+    }
+
+    // Ensure callbacks are not serialized as part of the command, as they cannot be passed between
+    // processes.
+    WGPUDeviceDescriptor wireDescriptor = {};
+    if (descriptor) {
+        wireDescriptor = *descriptor;
+        wireDescriptor.deviceLostCallback = nullptr;
+        wireDescriptor.deviceLostUserdata = nullptr;
+        wireDescriptor.deviceLostCallbackInfo.callback = nullptr;
+        wireDescriptor.deviceLostCallbackInfo.userdata = nullptr;
+        wireDescriptor.uncapturedErrorCallbackInfo.callback = nullptr;
+        wireDescriptor.uncapturedErrorCallbackInfo.userdata = nullptr;
+    }
+
+    AdapterRequestDeviceCmd cmd;
+    cmd.adapterId = GetWireId();
+    cmd.eventManagerHandle = GetEventManagerHandle();
+    cmd.future = {futureIDInternal};
+    cmd.deviceObjectHandle = device->GetWireHandle();
+    cmd.deviceLostFuture = device->GetDeviceLostFuture();
+    cmd.descriptor = &wireDescriptor;
+    cmd.userdataCount = 2;
 
     client->SerializeCommand(cmd);
     return {futureIDInternal};
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index 1dfe881..7a89cd1 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -56,6 +56,8 @@
                        void* userdata);
     WGPUFuture RequestDeviceF(const WGPUDeviceDescriptor* descriptor,
                               const WGPURequestDeviceCallbackInfo& callbackInfo);
+    WGPUFuture RequestDevice2(const WGPUDeviceDescriptor* descriptor,
+                              const WGPURequestDeviceCallbackInfo2& callbackInfo);
 
     // Unimplementable. Only availale in dawn_native.
     WGPUInstance GetInstance() const;
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 7a26e16..b608c9d 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -39,7 +39,8 @@
                                           WGPUFuture future,
                                           ObjectHandle deviceHandle,
                                           WGPUFuture deviceLostFuture,
-                                          const WGPUDeviceDescriptor* descriptor) {
+                                          const WGPUDeviceDescriptor* descriptor,
+                                          uint8_t userdataCount) {
     Reserved<WGPUDevice> device;
     WIRE_TRY(Objects<WGPUDevice>().Allocate(&device, deviceHandle, AllocationState::Reserved));
 
@@ -59,9 +60,16 @@
     desc.deviceLostCallbackInfo.callback = ForwardToServer<&Server::OnDeviceLost>;
     desc.deviceLostCallbackInfo.userdata = deviceLostUserdata.release();
 
-    mProcs.adapterRequestDevice(adapter->handle, &desc,
-                                ForwardToServer<&Server::OnRequestDeviceCallback>,
-                                userdata.release());
+    if (userdataCount == 1) {
+        mProcs.adapterRequestDevice(adapter->handle, &desc,
+                                    ForwardToServer<&Server::OnRequestDeviceCallback>,
+                                    userdata.release());
+    } else {
+        mProcs.adapterRequestDevice2(
+            adapter->handle, &desc,
+            {nullptr, WGPUCallbackMode_AllowSpontaneous,
+             ForwardToServer2<&Server::OnRequestDeviceCallback>, userdata.release(), nullptr});
+    }
     return WireResult::Success;
 }