Descriptorize SwapChain.

This also makes SwapChain support WebGPU-style error handling.

BUG=dawn:8

Change-Id: I5a142ae58600445f0f44f6dbe419cb7c3cdc9464
Reviewed-on: https://dawn-review.googlesource.com/c/4660
Reviewed-by: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index 2326056..68b5ae2 100644
--- a/dawn.json
+++ b/dawn.json
@@ -475,8 +475,11 @@
                 ]
             },
             {
-                "name": "create swap chain builder",
-                "returns": "swap chain builder"
+                "name": "create swap chain",
+                "returns": "swap chain",
+                "args": [
+                    {"name": "descriptor", "type": "swap chain descriptor", "annotation": "const*"}
+                ]
             },
             {
                 "name": "create texture",
@@ -957,19 +960,11 @@
             }
         ]
     },
-    "swap chain builder": {
-        "category": "object",
-        "methods": [
-            {
-                "name": "get result",
-                "returns": "swap chain"
-            },
-            {
-                "name": "set implementation",
-                "args": [
-                    {"name": "implementation", "type": "uint64_t"}
-                ]
-            }
+    "swap chain descriptor": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "implementation", "type": "uint64_t"}
         ]
     },
     "texture": {
diff --git a/examples/CHelloTriangle.cpp b/examples/CHelloTriangle.cpp
index 17a1d05..8f2a2e6 100644
--- a/examples/CHelloTriangle.cpp
+++ b/examples/CHelloTriangle.cpp
@@ -29,11 +29,10 @@
     queue = dawnDeviceCreateQueue(device);
 
     {
-        dawnSwapChainBuilder builder = dawnDeviceCreateSwapChainBuilder(device);
-        uint64_t swapchainImpl = GetSwapChainImplementation();
-        dawnSwapChainBuilderSetImplementation(builder, swapchainImpl);
-        swapchain = dawnSwapChainBuilderGetResult(builder);
-        dawnSwapChainBuilderRelease(builder);
+        dawnSwapChainDescriptor descriptor;
+        descriptor.nextInChain = nullptr;
+        descriptor.implementation = GetSwapChainImplementation();
+        swapchain = dawnDeviceCreateSwapChain(device, &descriptor);
     }
     swapChainFormat = static_cast<dawnTextureFormat>(GetPreferredSwapChainTextureFormat());
     dawnSwapChainConfigure(swapchain, swapChainFormat, DAWN_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT, 640,
diff --git a/examples/SampleUtils.cpp b/examples/SampleUtils.cpp
index 87f4982..715e1b5 100644
--- a/examples/SampleUtils.cpp
+++ b/examples/SampleUtils.cpp
@@ -150,9 +150,9 @@
 }
 
 dawn::SwapChain GetSwapChain(const dawn::Device &device) {
-    return device.CreateSwapChainBuilder()
-        .SetImplementation(GetSwapChainImplementation())
-        .GetResult();
+    dawn::SwapChainDescriptor swapChainDesc;
+    swapChainDesc.implementation = GetSwapChainImplementation();
+    return device.CreateSwapChain(&swapChainDesc);
 }
 
 dawn::TextureView CreateDefaultDepthStencilView(const dawn::Device& device) {
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 095fb0a..8a45075 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -220,8 +220,14 @@
 
         return result;
     }
-    SwapChainBuilder* DeviceBase::CreateSwapChainBuilder() {
-        return new SwapChainBuilder(this);
+    SwapChainBase* DeviceBase::CreateSwapChain(const SwapChainDescriptor* descriptor) {
+        SwapChainBase* result = nullptr;
+
+        if (ConsumedError(CreateSwapChainInternal(&result, descriptor))) {
+            return SwapChainBase::MakeError(this);
+        }
+
+        return result;
     }
     TextureBase* DeviceBase::CreateTexture(const TextureDescriptor* descriptor) {
         TextureBase* result = nullptr;
@@ -337,6 +343,13 @@
         return {};
     }
 
+    MaybeError DeviceBase::CreateSwapChainInternal(SwapChainBase** result,
+                                                   const SwapChainDescriptor* descriptor) {
+        DAWN_TRY(ValidateSwapChainDescriptor(this, descriptor));
+        DAWN_TRY_ASSIGN(*result, CreateSwapChainImpl(descriptor));
+        return {};
+    }
+
     MaybeError DeviceBase::CreateTextureInternal(TextureBase** result,
                                                  const TextureDescriptor* descriptor) {
         DAWN_TRY(ValidateTextureDescriptor(this, descriptor));
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 94f91d0..3dc8b83 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -62,7 +62,6 @@
         virtual InputStateBase* CreateInputState(InputStateBuilder* builder) = 0;
         virtual RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) = 0;
-        virtual SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) = 0;
 
         virtual Serial GetCompletedCommandSerial() const = 0;
         virtual Serial GetLastSubmittedCommandSerial() const = 0;
@@ -101,7 +100,7 @@
         RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor);
         SamplerBase* CreateSampler(const SamplerDescriptor* descriptor);
         ShaderModuleBase* CreateShaderModule(const ShaderModuleDescriptor* descriptor);
-        SwapChainBuilder* CreateSwapChainBuilder();
+        SwapChainBase* CreateSwapChain(const SwapChainDescriptor* descriptor);
         TextureBase* CreateTexture(const TextureDescriptor* descriptor);
         TextureViewBase* CreateTextureView(TextureBase* texture,
                                            const TextureViewDescriptor* descriptor);
@@ -141,6 +140,8 @@
             const SamplerDescriptor* descriptor) = 0;
         virtual ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) = 0;
+        virtual ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) = 0;
         virtual ResultOrError<TextureBase*> CreateTextureImpl(
             const TextureDescriptor* descriptor) = 0;
         virtual ResultOrError<TextureViewBase*> CreateTextureViewImpl(
@@ -163,6 +164,8 @@
         MaybeError CreateSamplerInternal(SamplerBase** result, const SamplerDescriptor* descriptor);
         MaybeError CreateShaderModuleInternal(ShaderModuleBase** result,
                                               const ShaderModuleDescriptor* descriptor);
+        MaybeError CreateSwapChainInternal(SwapChainBase** result,
+                                           const SwapChainDescriptor* descriptor);
         MaybeError CreateTextureInternal(TextureBase** result, const TextureDescriptor* descriptor);
         MaybeError CreateTextureViewInternal(TextureViewBase** result,
                                              TextureBase* texture,
diff --git a/src/dawn_native/SwapChain.cpp b/src/dawn_native/SwapChain.cpp
index fd1cb51..952306e 100644
--- a/src/dawn_native/SwapChain.cpp
+++ b/src/dawn_native/SwapChain.cpp
@@ -16,28 +16,79 @@
 
 #include "dawn_native/Device.h"
 #include "dawn_native/Texture.h"
+#include "dawn_native/ValidationUtils_autogen.h"
 
 namespace dawn_native {
 
+    namespace {
+
+        class ErrorSwapChain : public SwapChainBase {
+          public:
+            ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {
+            }
+
+          private:
+            TextureBase* GetNextTextureImpl(const TextureDescriptor*) override {
+                UNREACHABLE();
+            }
+
+            void OnBeforePresent(TextureBase* texture) override {
+                UNREACHABLE();
+            }
+        };
+
+    }  // anonymous namespace
+
+    MaybeError ValidateSwapChainDescriptor(const DeviceBase* device,
+                                           const SwapChainDescriptor* descriptor) {
+        if (descriptor->implementation == 0) {
+            return DAWN_VALIDATION_ERROR("Null implementation for the swapchain");
+        }
+
+        dawnSwapChainImplementation* impl =
+            reinterpret_cast<dawnSwapChainImplementation*>(descriptor->implementation);
+
+        if (!impl->Init || !impl->Destroy || !impl->Configure || !impl->GetNextTexture ||
+            !impl->Present) {
+            return DAWN_VALIDATION_ERROR("Implementation is incomplete");
+        }
+
+        return {};
+    }
+
     // SwapChain
 
-    SwapChainBase::SwapChainBase(SwapChainBuilder* builder)
-        : ObjectBase(builder->GetDevice()), mImplementation(builder->mImplementation) {
+    SwapChainBase::SwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor)
+        : ObjectBase(device),
+          mImplementation(
+              *reinterpret_cast<dawnSwapChainImplementation*>(descriptor->implementation)) {
+    }
+
+    SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag)
+        : ObjectBase(device, tag) {
     }
 
     SwapChainBase::~SwapChainBase() {
-        const auto& im = GetImplementation();
-        im.Destroy(im.userData);
+        if (!IsError()) {
+            const auto& im = GetImplementation();
+            im.Destroy(im.userData);
+        }
+    }
+
+    // static
+    SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
+        return new ErrorSwapChain(device);
     }
 
     void SwapChainBase::Configure(dawn::TextureFormat format,
                                   dawn::TextureUsageBit allowedUsage,
                                   uint32_t width,
                                   uint32_t height) {
-        if (width == 0 || height == 0) {
-            GetDevice()->HandleError("Swap chain cannot be configured to zero size");
+        if (GetDevice()->ConsumedError(ValidateConfigure(format, allowedUsage, width, height))) {
             return;
         }
+        ASSERT(!IsError());
+
         allowedUsage |= dawn::TextureUsageBit::Present;
 
         mFormat = format;
@@ -49,11 +100,10 @@
     }
 
     TextureBase* SwapChainBase::GetNextTexture() {
-        if (mWidth == 0) {
-            // If width is 0, it implies swap chain has never been configured
-            GetDevice()->HandleError("Swap chain needs to be configured before GetNextTexture");
-            return nullptr;
+        if (GetDevice()->ConsumedError(ValidateGetNextTexture())) {
+            return TextureBase::MakeError(GetDevice());
         }
+        ASSERT(!IsError());
 
         TextureDescriptor descriptor;
         descriptor.dimension = dawn::TextureDimension::e2D;
@@ -72,11 +122,10 @@
     }
 
     void SwapChainBase::Present(TextureBase* texture) {
-        // This also checks that the texture is valid since mLastNextTexture is always valid.
-        if (texture != mLastNextTexture) {
-            GetDevice()->HandleError("Tried to present something other than the last NextTexture");
+        if (GetDevice()->ConsumedError(ValidatePresent(texture))) {
             return;
         }
+        ASSERT(!IsError());
 
         OnBeforePresent(texture);
 
@@ -84,37 +133,48 @@
     }
 
     const dawnSwapChainImplementation& SwapChainBase::GetImplementation() {
+        ASSERT(!IsError());
         return mImplementation;
     }
 
-    // SwapChain Builder
+    MaybeError SwapChainBase::ValidateConfigure(dawn::TextureFormat format,
+                                                dawn::TextureUsageBit allowedUsage,
+                                                uint32_t width,
+                                                uint32_t height) const {
+        DAWN_TRY(GetDevice()->ValidateObject(this));
 
-    SwapChainBuilder::SwapChainBuilder(DeviceBase* device) : Builder(device) {
-    }
+        DAWN_TRY(ValidateTextureUsageBit(allowedUsage));
+        DAWN_TRY(ValidateTextureFormat(format));
 
-    SwapChainBase* SwapChainBuilder::GetResultImpl() {
-        if (!mImplementation.Init) {
-            HandleError("Implementation not set");
-            return nullptr;
-        }
-        return GetDevice()->CreateSwapChain(this);
-    }
-
-    void SwapChainBuilder::SetImplementation(uint64_t implementation) {
-        if (!implementation) {
-            HandleError("Implementation pointer is invalid");
-            return;
+        if (width == 0 || height == 0) {
+            return DAWN_VALIDATION_ERROR("Swap chain cannot be configured to zero size");
         }
 
-        dawnSwapChainImplementation& impl =
-            *reinterpret_cast<dawnSwapChainImplementation*>(implementation);
+        return {};
+    }
 
-        if (!impl.Init || !impl.Destroy || !impl.Configure || !impl.GetNextTexture ||
-            !impl.Present) {
-            HandleError("Implementation is incomplete");
-            return;
+    MaybeError SwapChainBase::ValidateGetNextTexture() const {
+        DAWN_TRY(GetDevice()->ValidateObject(this));
+
+        if (mWidth == 0) {
+            // If width is 0, it implies swap chain has never been configured
+            return DAWN_VALIDATION_ERROR("Swap chain needs to be configured before GetNextTexture");
         }
 
-        mImplementation = impl;
+        return {};
     }
+
+    MaybeError SwapChainBase::ValidatePresent(TextureBase* texture) const {
+        DAWN_TRY(GetDevice()->ValidateObject(this));
+        DAWN_TRY(GetDevice()->ValidateObject(texture));
+
+        // This also checks that the texture is valid since mLastNextTexture is always valid.
+        if (texture != mLastNextTexture) {
+            return DAWN_VALIDATION_ERROR(
+                "Tried to present something other than the last NextTexture");
+        }
+
+        return {};
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/SwapChain.h b/src/dawn_native/SwapChain.h
index de51a0a..fa9f710 100644
--- a/src/dawn_native/SwapChain.h
+++ b/src/dawn_native/SwapChain.h
@@ -16,6 +16,7 @@
 #define DAWNNATIVE_SWAPCHAIN_H_
 
 #include "dawn_native/Builder.h"
+#include "dawn_native/Error.h"
 #include "dawn_native/Forward.h"
 #include "dawn_native/ObjectBase.h"
 
@@ -24,11 +25,16 @@
 
 namespace dawn_native {
 
+    MaybeError ValidateSwapChainDescriptor(const DeviceBase* device,
+                                           const SwapChainDescriptor* descriptor);
+
     class SwapChainBase : public ObjectBase {
       public:
-        SwapChainBase(SwapChainBuilder* builder);
+        SwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor);
         ~SwapChainBase();
 
+        static SwapChainBase* MakeError(DeviceBase* device);
+
         // Dawn API
         void Configure(dawn::TextureFormat format,
                        dawn::TextureUsageBit allowedUsage,
@@ -38,11 +44,20 @@
         void Present(TextureBase* texture);
 
       protected:
+        SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag);
+
         const dawnSwapChainImplementation& GetImplementation();
         virtual TextureBase* GetNextTextureImpl(const TextureDescriptor*) = 0;
         virtual void OnBeforePresent(TextureBase* texture) = 0;
 
       private:
+        MaybeError ValidateConfigure(dawn::TextureFormat format,
+                                     dawn::TextureUsageBit allowedUsage,
+                                     uint32_t width,
+                                     uint32_t height) const;
+        MaybeError ValidateGetNextTexture() const;
+        MaybeError ValidatePresent(TextureBase* texture) const;
+
         dawnSwapChainImplementation mImplementation = {};
         dawn::TextureFormat mFormat = {};
         dawn::TextureUsageBit mAllowedUsage;
@@ -51,20 +66,6 @@
         TextureBase* mLastNextTexture = nullptr;
     };
 
-    class SwapChainBuilder : public Builder<SwapChainBase> {
-      public:
-        SwapChainBuilder(DeviceBase* device);
-
-        // Dawn API
-        SwapChainBase* GetResultImpl() override;
-        void SetImplementation(uint64_t implementation);
-
-      private:
-        friend class SwapChainBase;
-
-        dawnSwapChainImplementation mImplementation = {};
-    };
-
 }  // namespace dawn_native
 
 #endif  // DAWNNATIVE_SWAPCHAIN_H_
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 681d849..ec3593c 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -231,8 +231,9 @@
         const ShaderModuleDescriptor* descriptor) {
         return new ShaderModule(this, descriptor);
     }
-    SwapChainBase* Device::CreateSwapChain(SwapChainBuilder* builder) {
-        return new SwapChain(builder);
+    ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
+        const SwapChainDescriptor* descriptor) {
+        return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
         return new Texture(this, descriptor);
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 7182ae1..7a90b44 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -44,7 +44,6 @@
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
         RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) override;
-        SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) override;
 
         Serial GetCompletedCommandSerial() const final override;
         Serial GetLastSubmittedCommandSerial() const final override;
@@ -96,6 +95,8 @@
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
         ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) override;
+        ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) override;
         ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
         ResultOrError<TextureViewBase*> CreateTextureViewImpl(
             TextureBase* texture,
diff --git a/src/dawn_native/d3d12/SwapChainD3D12.cpp b/src/dawn_native/d3d12/SwapChainD3D12.cpp
index 2b3b6c4..cabc12b 100644
--- a/src/dawn_native/d3d12/SwapChainD3D12.cpp
+++ b/src/dawn_native/d3d12/SwapChainD3D12.cpp
@@ -21,7 +21,8 @@
 
 namespace dawn_native { namespace d3d12 {
 
-    SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) {
+    SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
+        : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
         dawnWSIContextD3D12 wsiContext = {};
         wsiContext.device = reinterpret_cast<dawnDevice>(GetDevice());
diff --git a/src/dawn_native/d3d12/SwapChainD3D12.h b/src/dawn_native/d3d12/SwapChainD3D12.h
index 8a82aa9..1ca8ded 100644
--- a/src/dawn_native/d3d12/SwapChainD3D12.h
+++ b/src/dawn_native/d3d12/SwapChainD3D12.h
@@ -19,9 +19,11 @@
 
 namespace dawn_native { namespace d3d12 {
 
+    class Device;
+
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(SwapChainBuilder* builder);
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h
index 1325dd4..7aa3997 100644
--- a/src/dawn_native/metal/DeviceMTL.h
+++ b/src/dawn_native/metal/DeviceMTL.h
@@ -41,7 +41,6 @@
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
         RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) override;
-        SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) override;
 
         Serial GetCompletedCommandSerial() const final override;
         Serial GetLastSubmittedCommandSerial() const final override;
@@ -79,6 +78,8 @@
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
         ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) override;
+        ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) override;
         ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
         ResultOrError<TextureViewBase*> CreateTextureViewImpl(
             TextureBase* texture,
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index 8e2ec5b..6a0f9ab 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -110,8 +110,9 @@
         const ShaderModuleDescriptor* descriptor) {
         return new ShaderModule(this, descriptor);
     }
-    SwapChainBase* Device::CreateSwapChain(SwapChainBuilder* builder) {
-        return new SwapChain(builder);
+    ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
+        const SwapChainDescriptor* descriptor) {
+        return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
         return new Texture(this, descriptor);
diff --git a/src/dawn_native/metal/SwapChainMTL.h b/src/dawn_native/metal/SwapChainMTL.h
index ac88ce1..063add6 100644
--- a/src/dawn_native/metal/SwapChainMTL.h
+++ b/src/dawn_native/metal/SwapChainMTL.h
@@ -23,7 +23,7 @@
 
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(SwapChainBuilder* builder);
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
diff --git a/src/dawn_native/metal/SwapChainMTL.mm b/src/dawn_native/metal/SwapChainMTL.mm
index f7d1a52..e1a636d 100644
--- a/src/dawn_native/metal/SwapChainMTL.mm
+++ b/src/dawn_native/metal/SwapChainMTL.mm
@@ -21,7 +21,8 @@
 
 namespace dawn_native { namespace metal {
 
-    SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) {
+    SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
+        : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
         dawnWSIContextMetal wsiContext = {};
         wsiContext.device = ToBackend(GetDevice())->GetMTLDevice();
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index 9bb6e19..070a817 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -112,8 +112,9 @@
 
         return module;
     }
-    SwapChainBase* Device::CreateSwapChain(SwapChainBuilder* builder) {
-        return new SwapChain(builder);
+    ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
+        const SwapChainDescriptor* descriptor) {
+        return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
         return new Texture(this, descriptor);
@@ -253,7 +254,8 @@
 
     // SwapChain
 
-    SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) {
+    SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
+        : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
         im.Init(im.userData, nullptr);
     }
diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h
index 1e24d13..770b61e 100644
--- a/src/dawn_native/null/DeviceNull.h
+++ b/src/dawn_native/null/DeviceNull.h
@@ -94,7 +94,6 @@
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
         RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) override;
-        SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) override;
 
         Serial GetCompletedCommandSerial() const final override;
         Serial GetLastSubmittedCommandSerial() const final override;
@@ -127,6 +126,8 @@
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
         ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) override;
+        ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) override;
         ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
         ResultOrError<TextureViewBase*> CreateTextureViewImpl(
             TextureBase* texture,
@@ -176,7 +177,7 @@
 
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(SwapChainBuilder* builder);
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index f1f80ed..42f8f26 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -90,8 +90,9 @@
         const ShaderModuleDescriptor* descriptor) {
         return new ShaderModule(this, descriptor);
     }
-    SwapChainBase* Device::CreateSwapChain(SwapChainBuilder* builder) {
-        return new SwapChain(builder);
+    ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
+        const SwapChainDescriptor* descriptor) {
+        return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
         return new Texture(this, descriptor);
diff --git a/src/dawn_native/opengl/DeviceGL.h b/src/dawn_native/opengl/DeviceGL.h
index 8c15162..fc3716e 100644
--- a/src/dawn_native/opengl/DeviceGL.h
+++ b/src/dawn_native/opengl/DeviceGL.h
@@ -44,7 +44,6 @@
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
         RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) override;
-        SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) override;
 
         Serial GetCompletedCommandSerial() const final override;
         Serial GetLastSubmittedCommandSerial() const final override;
@@ -74,6 +73,8 @@
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
         ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) override;
+        ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) override;
         ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
         ResultOrError<TextureViewBase*> CreateTextureViewImpl(
             TextureBase* texture,
diff --git a/src/dawn_native/opengl/SwapChainGL.cpp b/src/dawn_native/opengl/SwapChainGL.cpp
index 5e46808..fa8e46f 100644
--- a/src/dawn_native/opengl/SwapChainGL.cpp
+++ b/src/dawn_native/opengl/SwapChainGL.cpp
@@ -14,7 +14,7 @@
 
 #include "dawn_native/opengl/SwapChainGL.h"
 
-#include "dawn_native/Device.h"
+#include "dawn_native/opengl/DeviceGL.h"
 #include "dawn_native/opengl/Forward.h"
 #include "dawn_native/opengl/TextureGL.h"
 
@@ -22,7 +22,8 @@
 
 namespace dawn_native { namespace opengl {
 
-    SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) {
+    SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
+        : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
         im.Init(im.userData, nullptr);
     }
diff --git a/src/dawn_native/opengl/SwapChainGL.h b/src/dawn_native/opengl/SwapChainGL.h
index d1df750..5e532fa 100644
--- a/src/dawn_native/opengl/SwapChainGL.h
+++ b/src/dawn_native/opengl/SwapChainGL.h
@@ -25,7 +25,7 @@
 
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(SwapChainBuilder* builder);
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 5d1949d..559a954 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -179,13 +179,13 @@
         const ShaderModuleDescriptor* descriptor) {
         return new ShaderModule(this, descriptor);
     }
-    SwapChainBase* Device::CreateSwapChain(SwapChainBuilder* builder) {
-        return new SwapChain(builder);
+    ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
+        const SwapChainDescriptor* descriptor) {
+        return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
         return new Texture(this, descriptor);
     }
-
     ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl(
         TextureBase* texture,
         const TextureViewDescriptor* descriptor) {
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index ffa2a0d..ddbfc4c 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -68,7 +68,6 @@
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
         RenderPassDescriptorBase* CreateRenderPassDescriptor(
             RenderPassDescriptorBuilder* builder) override;
-        SwapChainBase* CreateSwapChain(SwapChainBuilder* builder) override;
 
         Serial GetCompletedCommandSerial() const final override;
         Serial GetLastSubmittedCommandSerial() const final override;
@@ -99,6 +98,8 @@
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
         ResultOrError<ShaderModuleBase*> CreateShaderModuleImpl(
             const ShaderModuleDescriptor* descriptor) override;
+        ResultOrError<SwapChainBase*> CreateSwapChainImpl(
+            const SwapChainDescriptor* descriptor) override;
         ResultOrError<TextureBase*> CreateTextureImpl(const TextureDescriptor* descriptor) override;
         ResultOrError<TextureViewBase*> CreateTextureViewImpl(
             TextureBase* texture,
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index dd84569..6028444 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -19,7 +19,8 @@
 
 namespace dawn_native { namespace vulkan {
 
-    SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) {
+    SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
+        : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
         dawnWSIContextVulkan wsiContext = {};
         im.Init(im.userData, &wsiContext);
diff --git a/src/dawn_native/vulkan/SwapChainVk.h b/src/dawn_native/vulkan/SwapChainVk.h
index 87f6ee5..e546c34 100644
--- a/src/dawn_native/vulkan/SwapChainVk.h
+++ b/src/dawn_native/vulkan/SwapChainVk.h
@@ -21,9 +21,11 @@
 
 namespace dawn_native { namespace vulkan {
 
+    class Device;
+
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(SwapChainBuilder* builder);
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
diff --git a/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp b/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp
index 91c91a9..f367133 100644
--- a/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp
+++ b/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp
@@ -35,14 +35,26 @@
     std::vector<char> buf;
 };
 
-void SkipSwapChainBuilderSetImplementation(dawnSwapChainBuilder builder, uint64_t) {
+static dawnProcDeviceCreateSwapChain originalDeviceCreateSwapChain = nullptr;
+
+dawnSwapChain ErrorDeviceCreateSwapChain(dawnDevice device, const dawnSwapChainDescriptor*) {
+    dawnSwapChainDescriptor desc;
+    desc.nextInChain = nullptr;
+    // A 0 implementation will trigger a swapchain creation error.
+    desc.implementation = 0;
+    return originalDeviceCreateSwapChain(device, &desc);
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     dawnProcTable procs = dawn_native::GetProcs();
-    // SwapChainSetImplementation receives a pointer, skip calls to it as they would be intercepted
-    // in embedders or dawn_wire too.
-    procs.swapChainBuilderSetImplementation = SkipSwapChainBuilderSetImplementation;
+
+    // Swapchains receive a pointer to an implementation. The fuzzer will pass garbage in so we
+    // intercept calls to create swapchains and make sure they always return error swapchains.
+    // This is ok for fuzzing because embedders of dawn_wire would always define their own
+    // swapchain handling.
+    originalDeviceCreateSwapChain = procs.deviceCreateSwapChain;
+    procs.deviceCreateSwapChain = ErrorDeviceCreateSwapChain;
+
     dawnSetProcs(&procs);
 
     // Create an instance and find the null adapter to create a device with.
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 7d45a90..23c32e5 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -235,9 +235,9 @@
 
     // The swapchain isn't used by tests but is useful when debugging with graphics debuggers that
     // capture at frame boundaries.
-    swapchain = device.CreateSwapChainBuilder()
-                    .SetImplementation(mBinding->GetSwapChainImplementation())
-                    .GetResult();
+    dawn::SwapChainDescriptor swapChainDesc;
+    swapChainDesc.implementation = mBinding->GetSwapChainImplementation();
+    swapchain = device.CreateSwapChain(&swapChainDesc);
     swapchain.Configure(
         static_cast<dawn::TextureFormat>(mBinding->GetPreferredSwapChainTextureFormat()),
         dawn::TextureUsageBit::OutputAttachment, 400, 400);