Vulkan: Add proper error handling for all regular object creation.

This makes all the regular WebGPU object creation handle errors properly
in the Vulkan backend instead of ASSERTing no Vulkan error is raised.

Static Create functions are added to all these types so that the details
of how the initialization is done is private, and it isn't possible to
construct an object but forget to initialize it.

BUG=dawn:19

Change-Id: I362b2d66b74dd7799ffbf69d732bc58caa97950b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/11861
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index 31c3575..336d9a6 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -16,6 +16,7 @@
 
 #include "common/BitSetIterator.h"
 #include "dawn_native/vulkan/DeviceVk.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -60,8 +61,17 @@
         }
     }
 
-    BindGroupLayout::BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor)
-        : BindGroupLayoutBase(device, descriptor) {
+    // static
+    ResultOrError<BindGroupLayout*> BindGroupLayout::Create(
+        Device* device,
+        const BindGroupLayoutDescriptor* descriptor) {
+        std::unique_ptr<BindGroupLayout> bgl =
+            std::make_unique<BindGroupLayout>(device, descriptor);
+        DAWN_TRY(bgl->Initialize());
+        return bgl.release();
+    }
+
+    MaybeError BindGroupLayout::Initialize() {
         const auto& info = GetBindingInfo();
 
         // Compute the bindings that will be chained in the DescriptorSetLayout create info. We add
@@ -88,10 +98,10 @@
         createInfo.bindingCount = numBindings;
         createInfo.pBindings = bindings.data();
 
-        if (device->fn.CreateDescriptorSetLayout(device->GetVkDevice(), &createInfo, nullptr,
-                                                 &mHandle) != VK_SUCCESS) {
-            ASSERT(false);
-        }
+        Device* device = ToBackend(GetDevice());
+        return CheckVkSuccess(device->fn.CreateDescriptorSetLayout(device->GetVkDevice(),
+                                                                   &createInfo, nullptr, &mHandle),
+                              "CreateDescriptorSetLayout");
     }
 
     BindGroupLayout::~BindGroupLayout() {
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.h b/src/dawn_native/vulkan/BindGroupLayoutVk.h
index 37cfbc5..12ba2b6 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.h
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.h
@@ -27,7 +27,8 @@
 
     class BindGroupLayout : public BindGroupLayoutBase {
       public:
-        BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor);
+        static ResultOrError<BindGroupLayout*> Create(Device* device,
+                                                      const BindGroupLayoutDescriptor* descriptor);
         ~BindGroupLayout();
 
         VkDescriptorSetLayout GetHandle() const;
@@ -37,6 +38,9 @@
         PoolSizeSpec ComputePoolSizes(uint32_t* numPoolSizes) const;
 
       private:
+        using BindGroupLayoutBase::BindGroupLayoutBase;
+        MaybeError Initialize();
+
         VkDescriptorSetLayout mHandle = VK_NULL_HANDLE;
     };
 
diff --git a/src/dawn_native/vulkan/BindGroupVk.cpp b/src/dawn_native/vulkan/BindGroupVk.cpp
index cc7ddd4..aa0f5b5 100644
--- a/src/dawn_native/vulkan/BindGroupVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupVk.cpp
@@ -14,19 +14,28 @@
 
 #include "dawn_native/vulkan/BindGroupVk.h"
 
+#include "common/BitSetIterator.h"
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 #include "dawn_native/vulkan/BufferVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
 #include "dawn_native/vulkan/SamplerVk.h"
 #include "dawn_native/vulkan/TextureVk.h"
-
-#include "common/BitSetIterator.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
-    BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor)
-        : BindGroupBase(device, descriptor) {
+    // static
+    ResultOrError<BindGroup*> BindGroup::Create(Device* device,
+                                                const BindGroupDescriptor* descriptor) {
+        std::unique_ptr<BindGroup> group = std::make_unique<BindGroup>(device, descriptor);
+        DAWN_TRY(group->Initialize());
+        return group.release();
+    }
+
+    MaybeError BindGroup::Initialize() {
+        Device* device = ToBackend(GetDevice());
+
         // Create a pool to hold our descriptor set.
         // TODO(cwallez@chromium.org): This horribly inefficient, find a way to be better, for
         // example by having one pool per bind group layout instead.
@@ -41,10 +50,9 @@
         createInfo.poolSizeCount = numPoolSizes;
         createInfo.pPoolSizes = poolSizes.data();
 
-        if (device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, nullptr, &mPool) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        DAWN_TRY(CheckVkSuccess(
+            device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, nullptr, &mPool),
+            "CreateDescriptorPool"));
 
         // Now do the allocation of one descriptor set, this is very suboptimal too.
         VkDescriptorSetLayout vkLayout = ToBackend(GetLayout())->GetHandle();
@@ -56,10 +64,9 @@
         allocateInfo.descriptorSetCount = 1;
         allocateInfo.pSetLayouts = &vkLayout;
 
-        if (device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, &mHandle) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        DAWN_TRY(CheckVkSuccess(
+            device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, &mHandle),
+            "AllocateDescriptorSets"));
 
         // Now do a write of a single descriptor set with all possible chained data allocated on the
         // stack.
@@ -118,6 +125,8 @@
 
         device->fn.UpdateDescriptorSets(device->GetVkDevice(), numWrites, writes.data(), 0,
                                         nullptr);
+
+        return {};
     }
 
     BindGroup::~BindGroup() {
diff --git a/src/dawn_native/vulkan/BindGroupVk.h b/src/dawn_native/vulkan/BindGroupVk.h
index 5071796..bfa3fea 100644
--- a/src/dawn_native/vulkan/BindGroupVk.h
+++ b/src/dawn_native/vulkan/BindGroupVk.h
@@ -25,12 +25,16 @@
 
     class BindGroup : public BindGroupBase {
       public:
-        BindGroup(Device* device, const BindGroupDescriptor* descriptor);
+        static ResultOrError<BindGroup*> Create(Device* device,
+                                                const BindGroupDescriptor* descriptor);
         ~BindGroup();
 
         VkDescriptorSet GetHandle() const;
 
       private:
+        using BindGroupBase::BindGroupBase;
+        MaybeError Initialize();
+
         VkDescriptorPool mPool = VK_NULL_HANDLE;
         VkDescriptorSet mHandle = VK_NULL_HANDLE;
     };
diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp
index de1306e..2133457 100644
--- a/src/dawn_native/vulkan/BufferVk.cpp
+++ b/src/dawn_native/vulkan/BufferVk.cpp
@@ -114,8 +114,11 @@
 
     }  // namespace
 
-    Buffer::Buffer(Device* device, const BufferDescriptor* descriptor)
-        : BufferBase(device, descriptor) {
+    // static
+    ResultOrError<Buffer*> Buffer::Create(Device* device, const BufferDescriptor* descriptor) {
+        std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(device, descriptor);
+        DAWN_TRY(buffer->Initialize());
+        return buffer.release();
     }
 
     MaybeError Buffer::Initialize() {
diff --git a/src/dawn_native/vulkan/BufferVk.h b/src/dawn_native/vulkan/BufferVk.h
index d3b44ca..210fadc 100644
--- a/src/dawn_native/vulkan/BufferVk.h
+++ b/src/dawn_native/vulkan/BufferVk.h
@@ -29,11 +29,9 @@
 
     class Buffer : public BufferBase {
       public:
-        Buffer(Device* device, const BufferDescriptor* descriptor);
+        static ResultOrError<Buffer*> Create(Device* device, const BufferDescriptor* descriptor);
         ~Buffer();
 
-        MaybeError Initialize();
-
         void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data);
         void OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data);
 
@@ -45,6 +43,9 @@
         void TransitionUsageNow(CommandRecordingContext* recordingContext, dawn::BufferUsage usage);
 
       private:
+        using BufferBase::BufferBase;
+        MaybeError Initialize();
+
         // Dawn API
         MaybeError MapReadAsyncImpl(uint32_t serial) override;
         MaybeError MapWriteAsyncImpl(uint32_t serial) override;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index c01d5fd..93bd6c6 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -286,6 +286,12 @@
         }
     }  // anonymous namespace
 
+    // static
+    CommandBuffer* CommandBuffer::Create(CommandEncoderBase* encoder,
+                                         const CommandBufferDescriptor* descriptor) {
+        return new CommandBuffer(encoder, descriptor);
+    }
+
     CommandBuffer::CommandBuffer(CommandEncoderBase* encoder,
                                  const CommandBufferDescriptor* descriptor)
         : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
diff --git a/src/dawn_native/vulkan/CommandBufferVk.h b/src/dawn_native/vulkan/CommandBufferVk.h
index c6d15c2..1029681 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.h
+++ b/src/dawn_native/vulkan/CommandBufferVk.h
@@ -17,6 +17,7 @@
 
 #include "dawn_native/CommandAllocator.h"
 #include "dawn_native/CommandBuffer.h"
+#include "dawn_native/Error.h"
 
 #include "common/vulkan_platform.h"
 
@@ -32,12 +33,15 @@
 
     class CommandBuffer : public CommandBufferBase {
       public:
-        CommandBuffer(CommandEncoderBase* encoder, const CommandBufferDescriptor* descriptor);
+        static CommandBuffer* Create(CommandEncoderBase* encoder,
+                                     const CommandBufferDescriptor* descriptor);
         ~CommandBuffer();
 
         void RecordCommands(CommandRecordingContext* recordingContext);
 
       private:
+        CommandBuffer(CommandEncoderBase* encoder, const CommandBufferDescriptor* descriptor);
+
         void RecordComputePass(CommandRecordingContext* recordingContext);
         void RecordRenderPass(CommandRecordingContext* recordingContext,
                               BeginRenderPassCmd* renderPass);
diff --git a/src/dawn_native/vulkan/ComputePipelineVk.cpp b/src/dawn_native/vulkan/ComputePipelineVk.cpp
index 3437543..2f37620 100644
--- a/src/dawn_native/vulkan/ComputePipelineVk.cpp
+++ b/src/dawn_native/vulkan/ComputePipelineVk.cpp
@@ -18,11 +18,21 @@
 #include "dawn_native/vulkan/FencedDeleter.h"
 #include "dawn_native/vulkan/PipelineLayoutVk.h"
 #include "dawn_native/vulkan/ShaderModuleVk.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
-    ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor)
-        : ComputePipelineBase(device, descriptor) {
+    // static
+    ResultOrError<ComputePipeline*> ComputePipeline::Create(
+        Device* device,
+        const ComputePipelineDescriptor* descriptor) {
+        std::unique_ptr<ComputePipeline> pipeline =
+            std::make_unique<ComputePipeline>(device, descriptor);
+        DAWN_TRY(pipeline->Initialize(descriptor));
+        return pipeline.release();
+    }
+
+    MaybeError ComputePipeline::Initialize(const ComputePipelineDescriptor* descriptor) {
         VkComputePipelineCreateInfo createInfo;
         createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
         createInfo.pNext = nullptr;
@@ -39,10 +49,11 @@
         createInfo.stage.pName = descriptor->computeStage.entryPoint;
         createInfo.stage.pSpecializationInfo = nullptr;
 
-        if (device->fn.CreateComputePipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, &createInfo,
-                                              nullptr, &mHandle) != VK_SUCCESS) {
-            ASSERT(false);
-        }
+        Device* device = ToBackend(GetDevice());
+        return CheckVkSuccess(
+            device->fn.CreateComputePipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, &createInfo,
+                                              nullptr, &mHandle),
+            "CreateComputePipeline");
     }
 
     ComputePipeline::~ComputePipeline() {
diff --git a/src/dawn_native/vulkan/ComputePipelineVk.h b/src/dawn_native/vulkan/ComputePipelineVk.h
index d1b589c..ca35847 100644
--- a/src/dawn_native/vulkan/ComputePipelineVk.h
+++ b/src/dawn_native/vulkan/ComputePipelineVk.h
@@ -18,6 +18,7 @@
 #include "dawn_native/ComputePipeline.h"
 
 #include "common/vulkan_platform.h"
+#include "dawn_native/Error.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -25,12 +26,16 @@
 
     class ComputePipeline : public ComputePipelineBase {
       public:
-        ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor);
+        static ResultOrError<ComputePipeline*> Create(Device* device,
+                                                      const ComputePipelineDescriptor* descriptor);
         ~ComputePipeline();
 
         VkPipeline GetHandle() const;
 
       private:
+        using ComputePipelineBase::ComputePipelineBase;
+        MaybeError Initialize(const ComputePipelineDescriptor* descriptor);
+
         VkPipeline mHandle = VK_NULL_HANDLE;
     };
 
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index e8581aa..7cbba72 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -147,54 +147,52 @@
 
     ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl(
         const BindGroupDescriptor* descriptor) {
-        return new BindGroup(this, descriptor);
+        return BindGroup::Create(this, descriptor);
     }
     ResultOrError<BindGroupLayoutBase*> Device::CreateBindGroupLayoutImpl(
         const BindGroupLayoutDescriptor* descriptor) {
-        return new BindGroupLayout(this, descriptor);
+        return BindGroupLayout::Create(this, descriptor);
     }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
-        std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(this, descriptor);
-        DAWN_TRY(buffer->Initialize());
-        return buffer.release();
+        return Buffer::Create(this, descriptor);
     }
     CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder,
                                                    const CommandBufferDescriptor* descriptor) {
-        return new CommandBuffer(encoder, descriptor);
+        return CommandBuffer::Create(encoder, descriptor);
     }
     ResultOrError<ComputePipelineBase*> Device::CreateComputePipelineImpl(
         const ComputePipelineDescriptor* descriptor) {
-        return new ComputePipeline(this, descriptor);
+        return ComputePipeline::Create(this, descriptor);
     }
     ResultOrError<PipelineLayoutBase*> Device::CreatePipelineLayoutImpl(
         const PipelineLayoutDescriptor* descriptor) {
-        return new PipelineLayout(this, descriptor);
+        return PipelineLayout::Create(this, descriptor);
     }
     ResultOrError<QueueBase*> Device::CreateQueueImpl() {
-        return new Queue(this);
+        return Queue::Create(this);
     }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
-        return new RenderPipeline(this, descriptor);
+        return RenderPipeline::Create(this, descriptor);
     }
     ResultOrError<SamplerBase*> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) {
-        return new Sampler(this, descriptor);
+        return Sampler::Create(this, descriptor);
     }
     ResultOrError<ShaderModuleBase*> Device::CreateShaderModuleImpl(
         const ShaderModuleDescriptor* descriptor) {
-        return new ShaderModule(this, descriptor);
+        return ShaderModule::Create(this, descriptor);
     }
     ResultOrError<SwapChainBase*> Device::CreateSwapChainImpl(
         const SwapChainDescriptor* descriptor) {
-        return new SwapChain(this, descriptor);
+        return SwapChain::Create(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
-        return new Texture(this, descriptor);
+        return Texture::Create(this, descriptor);
     }
     ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl(
         TextureBase* texture,
         const TextureViewDescriptor* descriptor) {
-        return new TextureView(texture, descriptor);
+        return TextureView::Create(texture, descriptor);
     }
 
     Serial Device::GetCompletedCommandSerial() const {
diff --git a/src/dawn_native/vulkan/PipelineLayoutVk.cpp b/src/dawn_native/vulkan/PipelineLayoutVk.cpp
index 4b5615e..dd123af 100644
--- a/src/dawn_native/vulkan/PipelineLayoutVk.cpp
+++ b/src/dawn_native/vulkan/PipelineLayoutVk.cpp
@@ -14,16 +14,25 @@
 
 #include "dawn_native/vulkan/PipelineLayoutVk.h"
 
+#include "common/BitSetIterator.h"
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
-
-#include "common/BitSetIterator.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
-    PipelineLayout::PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor)
-        : PipelineLayoutBase(device, descriptor) {
+    // static
+    ResultOrError<PipelineLayout*> PipelineLayout::Create(
+        Device* device,
+        const PipelineLayoutDescriptor* descriptor) {
+        std::unique_ptr<PipelineLayout> layout =
+            std::make_unique<PipelineLayout>(device, descriptor);
+        DAWN_TRY(layout->Initialize());
+        return layout.release();
+    }
+
+    MaybeError PipelineLayout::Initialize() {
         // Compute the array of VkDescriptorSetLayouts that will be chained in the create info.
         // TODO(cwallez@chromium.org) Vulkan doesn't allow holes in this array, should we expose
         // this constraints at the Dawn level?
@@ -43,10 +52,10 @@
         createInfo.pushConstantRangeCount = 0;
         createInfo.pPushConstantRanges = nullptr;
 
-        if (device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr,
-                                            &mHandle) != VK_SUCCESS) {
-            ASSERT(false);
-        }
+        Device* device = ToBackend(GetDevice());
+        return CheckVkSuccess(
+            device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
+            "CreatePipelineLayout");
     }
 
     PipelineLayout::~PipelineLayout() {
diff --git a/src/dawn_native/vulkan/PipelineLayoutVk.h b/src/dawn_native/vulkan/PipelineLayoutVk.h
index a5072eb..744eb84 100644
--- a/src/dawn_native/vulkan/PipelineLayoutVk.h
+++ b/src/dawn_native/vulkan/PipelineLayoutVk.h
@@ -18,6 +18,7 @@
 #include "dawn_native/PipelineLayout.h"
 
 #include "common/vulkan_platform.h"
+#include "dawn_native/Error.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -25,12 +26,16 @@
 
     class PipelineLayout : public PipelineLayoutBase {
       public:
-        PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor);
+        static ResultOrError<PipelineLayout*> Create(Device* device,
+                                                     const PipelineLayoutDescriptor* descriptor);
         ~PipelineLayout();
 
         VkPipelineLayout GetHandle() const;
 
       private:
+        using PipelineLayoutBase::PipelineLayoutBase;
+        MaybeError Initialize();
+
         VkPipelineLayout mHandle = VK_NULL_HANDLE;
     };
 
diff --git a/src/dawn_native/vulkan/QueueVk.cpp b/src/dawn_native/vulkan/QueueVk.cpp
index 83a765d..544ad95 100644
--- a/src/dawn_native/vulkan/QueueVk.cpp
+++ b/src/dawn_native/vulkan/QueueVk.cpp
@@ -20,7 +20,9 @@
 
 namespace dawn_native { namespace vulkan {
 
-    Queue::Queue(Device* device) : QueueBase(device) {
+    // static
+    Queue* Queue::Create(Device* device) {
+        return new Queue(device);
     }
 
     Queue::~Queue() {
diff --git a/src/dawn_native/vulkan/QueueVk.h b/src/dawn_native/vulkan/QueueVk.h
index ff77ffb..39e8314 100644
--- a/src/dawn_native/vulkan/QueueVk.h
+++ b/src/dawn_native/vulkan/QueueVk.h
@@ -24,10 +24,12 @@
 
     class Queue : public QueueBase {
       public:
-        Queue(Device* device);
+        static Queue* Create(Device* device);
         ~Queue();
 
       private:
+        using QueueBase::QueueBase;
+
         MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
     };
 
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp
index eed883e..a5d13f6 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp
@@ -21,6 +21,7 @@
 #include "dawn_native/vulkan/ShaderModuleVk.h"
 #include "dawn_native/vulkan/TextureVk.h"
 #include "dawn_native/vulkan/UtilsVulkan.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -318,8 +319,19 @@
 
     }  // anonymous namespace
 
-    RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
-        : RenderPipelineBase(device, descriptor) {
+    // static
+    ResultOrError<RenderPipeline*> RenderPipeline::Create(
+        Device* device,
+        const RenderPipelineDescriptor* descriptor) {
+        std::unique_ptr<RenderPipeline> pipeline =
+            std::make_unique<RenderPipeline>(device, descriptor);
+        DAWN_TRY(pipeline->Initialize(descriptor));
+        return pipeline.release();
+    }
+
+    MaybeError RenderPipeline::Initialize(const RenderPipelineDescriptor* descriptor) {
+        Device* device = ToBackend(GetDevice());
+
         VkPipelineShaderStageCreateInfo shaderStages[2];
         {
             shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -488,10 +500,10 @@
         createInfo.basePipelineHandle = VK_NULL_HANDLE;
         createInfo.basePipelineIndex = -1;
 
-        if (device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1,
-                                               &createInfo, nullptr, &mHandle) != VK_SUCCESS) {
-            ASSERT(false);
-        }
+        return CheckVkSuccess(
+            device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1,
+                                               &createInfo, nullptr, &mHandle),
+            "CreateGraphicsPipeline");
     }
 
     VkPipelineVertexInputStateCreateInfo RenderPipeline::ComputeVertexInputDesc(
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.h b/src/dawn_native/vulkan/RenderPipelineVk.h
index 083c3ab..9d2d300 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.h
+++ b/src/dawn_native/vulkan/RenderPipelineVk.h
@@ -18,6 +18,7 @@
 #include "dawn_native/RenderPipeline.h"
 
 #include "common/vulkan_platform.h"
+#include "dawn_native/Error.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -25,12 +26,16 @@
 
     class RenderPipeline : public RenderPipelineBase {
       public:
-        RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
+        static ResultOrError<RenderPipeline*> Create(Device* device,
+                                                     const RenderPipelineDescriptor* descriptor);
         ~RenderPipeline();
 
         VkPipeline GetHandle() const;
 
       private:
+        using RenderPipelineBase::RenderPipelineBase;
+        MaybeError Initialize(const RenderPipelineDescriptor* descriptor);
+
         VkPipelineVertexInputStateCreateInfo ComputeVertexInputDesc(
             const VertexInputDescriptor* vertexInput,
             std::array<VkVertexInputBindingDescription, kMaxVertexBuffers>* mBindings,
diff --git a/src/dawn_native/vulkan/SamplerVk.cpp b/src/dawn_native/vulkan/SamplerVk.cpp
index 5de2411..633fb5d 100644
--- a/src/dawn_native/vulkan/SamplerVk.cpp
+++ b/src/dawn_native/vulkan/SamplerVk.cpp
@@ -17,6 +17,7 @@
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
 #include "dawn_native/vulkan/UtilsVulkan.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -57,8 +58,14 @@
         }
     }  // anonymous namespace
 
-    Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor)
-        : SamplerBase(device, descriptor), mDevice(device) {
+    // static
+    ResultOrError<Sampler*> Sampler::Create(Device* device, const SamplerDescriptor* descriptor) {
+        std::unique_ptr<Sampler> sampler = std::make_unique<Sampler>(device, descriptor);
+        DAWN_TRY(sampler->Initialize(descriptor));
+        return sampler.release();
+    }
+
+    MaybeError Sampler::Initialize(const SamplerDescriptor* descriptor) {
         VkSamplerCreateInfo createInfo = {};
         createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
         createInfo.pNext = nullptr;
@@ -78,15 +85,15 @@
         createInfo.maxLod = descriptor->lodMaxClamp;
         createInfo.unnormalizedCoordinates = VK_FALSE;
 
-        if (device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        Device* device = ToBackend(GetDevice());
+        return CheckVkSuccess(
+            device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
+            "CreateSampler");
     }
 
     Sampler::~Sampler() {
         if (mHandle != VK_NULL_HANDLE) {
-            mDevice->GetFencedDeleter()->DeleteWhenUnused(mHandle);
+            ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
             mHandle = VK_NULL_HANDLE;
         }
     }
diff --git a/src/dawn_native/vulkan/SamplerVk.h b/src/dawn_native/vulkan/SamplerVk.h
index 2bd51f1..9ea7e0f 100644
--- a/src/dawn_native/vulkan/SamplerVk.h
+++ b/src/dawn_native/vulkan/SamplerVk.h
@@ -18,20 +18,23 @@
 #include "dawn_native/Sampler.h"
 
 #include "common/vulkan_platform.h"
+#include "dawn_native/Error.h"
 #include "dawn_native/vulkan/MemoryAllocator.h"
 
 namespace dawn_native { namespace vulkan {
 
     class Sampler : public SamplerBase {
       public:
-        Sampler(Device* device, const SamplerDescriptor* descriptor);
+        static ResultOrError<Sampler*> Create(Device* device, const SamplerDescriptor* descriptor);
         ~Sampler();
 
         VkSampler GetHandle() const;
 
       private:
+        using SamplerBase::SamplerBase;
+        MaybeError Initialize(const SamplerDescriptor* descriptor);
+
         VkSampler mHandle = VK_NULL_HANDLE;
-        Device* mDevice = nullptr;
     };
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/ShaderModuleVk.cpp b/src/dawn_native/vulkan/ShaderModuleVk.cpp
index 9f48e17..b741d7b 100644
--- a/src/dawn_native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn_native/vulkan/ShaderModuleVk.cpp
@@ -16,13 +16,21 @@
 
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 #include <spirv_cross.hpp>
 
 namespace dawn_native { namespace vulkan {
 
-    ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor)
-        : ShaderModuleBase(device, descriptor) {
+    // static
+    ResultOrError<ShaderModule*> ShaderModule::Create(Device* device,
+                                                      const ShaderModuleDescriptor* descriptor) {
+        std::unique_ptr<ShaderModule> module = std::make_unique<ShaderModule>(device, descriptor);
+        DAWN_TRY(module->Initialize(descriptor));
+        return module.release();
+    }
+
+    MaybeError ShaderModule::Initialize(const ShaderModuleDescriptor* descriptor) {
         // Use SPIRV-Cross to extract info from the SPIRV even if Vulkan consumes SPIRV. We want to
         // have a translation step eventually anyway.
         spirv_cross::Compiler compiler(descriptor->code, descriptor->codeSize);
@@ -35,10 +43,10 @@
         createInfo.codeSize = descriptor->codeSize * sizeof(uint32_t);
         createInfo.pCode = descriptor->code;
 
-        if (device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        Device* device = ToBackend(GetDevice());
+        return CheckVkSuccess(
+            device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
+            "CreateShaderModule");
     }
 
     ShaderModule::~ShaderModule() {
diff --git a/src/dawn_native/vulkan/ShaderModuleVk.h b/src/dawn_native/vulkan/ShaderModuleVk.h
index 8c904d2..f328dac 100644
--- a/src/dawn_native/vulkan/ShaderModuleVk.h
+++ b/src/dawn_native/vulkan/ShaderModuleVk.h
@@ -18,6 +18,7 @@
 #include "dawn_native/ShaderModule.h"
 
 #include "common/vulkan_platform.h"
+#include "dawn_native/Error.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -25,12 +26,16 @@
 
     class ShaderModule : public ShaderModuleBase {
       public:
-        ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
+        static ResultOrError<ShaderModule*> Create(Device* device,
+                                                   const ShaderModuleDescriptor* descriptor);
         ~ShaderModule();
 
         VkShaderModule GetHandle() const;
 
       private:
+        using ShaderModuleBase::ShaderModuleBase;
+        MaybeError Initialize(const ShaderModuleDescriptor* descriptor);
+
         VkShaderModule mHandle = VK_NULL_HANDLE;
     };
 
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index e9bcaf7..d1eafc9 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -19,6 +19,11 @@
 
 namespace dawn_native { namespace vulkan {
 
+    // static
+    SwapChain* SwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) {
+        return new SwapChain(device, descriptor);
+    }
+
     SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor)
         : SwapChainBase(device, descriptor) {
         const auto& im = GetImplementation();
diff --git a/src/dawn_native/vulkan/SwapChainVk.h b/src/dawn_native/vulkan/SwapChainVk.h
index 2e9db00..339d9da 100644
--- a/src/dawn_native/vulkan/SwapChainVk.h
+++ b/src/dawn_native/vulkan/SwapChainVk.h
@@ -25,10 +25,12 @@
 
     class SwapChain : public SwapChainBase {
       public:
-        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
+        static SwapChain* Create(Device* device, const SwapChainDescriptor* descriptor);
         ~SwapChain();
 
       protected:
+        SwapChain(Device* device, const SwapChainDescriptor* descriptor);
+
         TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override;
         MaybeError OnBeforePresent(TextureBase* texture) override;
 
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 2a26269..5e90cf8 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -26,6 +26,7 @@
 #include "dawn_native/vulkan/FencedDeleter.h"
 #include "dawn_native/vulkan/StagingBufferVk.h"
 #include "dawn_native/vulkan/UtilsVulkan.h"
+#include "dawn_native/vulkan/VulkanError.h"
 
 namespace dawn_native { namespace vulkan {
 
@@ -395,8 +396,17 @@
         return {};
     }
 
-    Texture::Texture(Device* device, const TextureDescriptor* descriptor)
-        : TextureBase(device, descriptor, TextureState::OwnedInternal) {
+    // static
+    ResultOrError<Texture*> Texture::Create(Device* device, const TextureDescriptor* descriptor) {
+        std::unique_ptr<Texture> texture =
+            std::make_unique<Texture>(device, descriptor, TextureState::OwnedInternal);
+        DAWN_TRY(texture->InitializeAsInternalTexture());
+        return texture.release();
+    }
+
+    MaybeError Texture::InitializeAsInternalTexture() {
+        Device* device = ToBackend(GetDevice());
+
         // Create the Vulkan image "container". We don't need to check that the format supports the
         // combination of sample, usage etc. because validation should have been done in the Dawn
         // frontend already based on the minimum supported formats in the Vulkan spec
@@ -428,29 +438,30 @@
         // also required for the implementation of robust resource initialization.
         createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
 
-        if (device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        DAWN_TRY(CheckVkSuccess(
+            device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
+            "CreateImage"));
 
         // Create the image memory and associate it with the container
         VkMemoryRequirements requirements;
         device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
 
         if (!device->GetMemoryAllocator()->Allocate(requirements, false, &mMemoryAllocation)) {
-            ASSERT(false);
+            return DAWN_OUT_OF_MEMORY_ERROR("Failed to allocate texture");
         }
 
-        if (device->fn.BindImageMemory(device->GetVkDevice(), mHandle,
-                                       mMemoryAllocation.GetMemory(),
-                                       mMemoryAllocation.GetMemoryOffset()) != VK_SUCCESS) {
-            ASSERT(false);
-        }
+        DAWN_TRY(CheckVkSuccess(device->fn.BindImageMemory(device->GetVkDevice(), mHandle,
+                                                           mMemoryAllocation.GetMemory(),
+                                                           mMemoryAllocation.GetMemoryOffset()),
+                                "BindImageMemory"));
+
         if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            device->ConsumedError(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(),
-                                               0, GetNumMipLevels(), 0, GetArrayLayers(),
-                                               TextureBase::ClearValue::NonZero));
+            DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(), 0,
+                                  GetNumMipLevels(), 0, GetArrayLayers(),
+                                  TextureBase::ClearValue::NonZero));
         }
+
+        return {};
     }
 
     // With this constructor, the lifetime of the resource is externally managed.
@@ -756,10 +767,16 @@
         }
     }
 
-    // TODO(jiawei.shao@intel.com): create texture view by TextureViewDescriptor
-    TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
-        : TextureViewBase(texture, descriptor) {
-        Device* device = ToBackend(texture->GetDevice());
+    // static
+    ResultOrError<TextureView*> TextureView::Create(TextureBase* texture,
+                                                    const TextureViewDescriptor* descriptor) {
+        std::unique_ptr<TextureView> view = std::make_unique<TextureView>(texture, descriptor);
+        DAWN_TRY(view->Initialize(descriptor));
+        return view.release();
+    }
+
+    MaybeError TextureView::Initialize(const TextureViewDescriptor* descriptor) {
+        Device* device = ToBackend(GetTexture()->GetDevice());
 
         VkImageViewCreateInfo createInfo;
         createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -776,10 +793,9 @@
         createInfo.subresourceRange.baseArrayLayer = descriptor->baseArrayLayer;
         createInfo.subresourceRange.layerCount = descriptor->arrayLayerCount;
 
-        if (device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
-            VK_SUCCESS) {
-            ASSERT(false);
-        }
+        return CheckVkSuccess(
+            device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &mHandle),
+            "CreateImageView");
     }
 
     TextureView::~TextureView() {
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 5d049e8..e32af0b 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -35,15 +35,7 @@
 
     class Texture : public TextureBase {
       public:
-        enum class ExternalState {
-            InternalOnly,
-            PendingAcquire,
-            Acquired,
-            PendingRelease,
-            Released
-        };
-
-        Texture(Device* device, const TextureDescriptor* descriptor);
+        static ResultOrError<Texture*> Create(Device* device, const TextureDescriptor* descriptor);
         Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage);
         Texture(Device* device,
                 const ExternalImageDescriptor* descriptor,
@@ -70,6 +62,9 @@
         MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
 
       private:
+        using TextureBase::TextureBase;
+        MaybeError InitializeAsInternalTexture();
+
         void DestroyImpl() override;
         MaybeError ClearTexture(CommandRecordingContext* recordingContext,
                                 uint32_t baseMipLevel,
@@ -82,24 +77,36 @@
         DeviceMemoryAllocation mMemoryAllocation;
         VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
 
+        enum class ExternalState {
+            InternalOnly,
+            PendingAcquire,
+            Acquired,
+            PendingRelease,
+            Released
+        };
         ExternalState mExternalState = ExternalState::InternalOnly;
         ExternalState mLastExternalState = ExternalState::InternalOnly;
+
         VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
         std::vector<VkSemaphore> mWaitRequirements;
 
         // A usage of none will make sure the texture is transitioned before its first use as
-        // required by the spec.
+        // required by the Vulkan spec.
         dawn::TextureUsage mLastUsage = dawn::TextureUsage::None;
     };
 
     class TextureView : public TextureViewBase {
       public:
-        TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor);
+        static ResultOrError<TextureView*> Create(TextureBase* texture,
+                                                  const TextureViewDescriptor* descriptor);
         ~TextureView();
 
         VkImageView GetHandle() const;
 
       private:
+        using TextureViewBase::TextureViewBase;
+        MaybeError Initialize(const TextureViewDescriptor* descriptor);
+
         VkImageView mHandle = VK_NULL_HANDLE;
     };