Add SwapChain::GetCurrentTexture

This is to eventually allow more using swapchain textures as CopySrc and
CopyDst. Note that this commit doesn't add any additional usages.

Because textures can reflect their creation parameters, swapchains now
need to pass in the correct texture descriptor in all code paths. This
requires additional handling in dawn::native error swapchains, and
dawn::wire::client's SwapChain reservation and Device::CreateSwapChain
code.

Tests are added for all of these code paths except
dawn::wire::client::Device::CreateSwapChain because there is no way to
create a Surface in wire tests at the moment (they don't have an
instance).

Bug: dawn:1551
Change-Id: I22d5e909e1e94d48eb52cae57aabff8a7f0c04c1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/133463
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/dawn.json b/dawn.json
index fe3bf9e..3d0bdc5 100644
--- a/dawn.json
+++ b/dawn.json
@@ -2576,6 +2576,7 @@
         "category": "object",
         "methods": [
             {"name": "get current texture view", "returns": "texture view"},
+            {"name": "get current texture", "returns": "texture"},
             {"name": "present"}
         ]
     },
diff --git a/dawn_wire.json b/dawn_wire.json
index aa96202..a22d133 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -224,12 +224,14 @@
             "BufferUnmap",
             "DeviceCreateErrorBuffer",
             "DeviceCreateQuerySet",
+            "DeviceCreateSwapChain",
             "DeviceCreateTexture",
             "DeviceCreateErrorTexture",
             "DeviceGetAdapter",
             "DeviceGetQueue",
             "DeviceGetSupportedSurfaceUsage",
-            "DeviceInjectError"
+            "DeviceInjectError",
+            "SwapChainGetCurrentTexture"
         ],
         "client_special_objects": [
             "Adapter",
@@ -239,6 +241,7 @@
             "QuerySet",
             "Queue",
             "ShaderModule",
+            "SwapChain",
             "Texture"
         ],
         "server_custom_pre_handler_commands": [
diff --git a/include/dawn/wire/WireClient.h b/include/dawn/wire/WireClient.h
index 02b4c07..1139715 100644
--- a/include/dawn/wire/WireClient.h
+++ b/include/dawn/wire/WireClient.h
@@ -71,7 +71,8 @@
     const volatile char* HandleCommands(const volatile char* commands, size_t size) override;
 
     ReservedTexture ReserveTexture(WGPUDevice device, const WGPUTextureDescriptor* descriptor);
-    ReservedSwapChain ReserveSwapChain(WGPUDevice device);
+    ReservedSwapChain ReserveSwapChain(WGPUDevice device,
+                                       const WGPUSwapChainDescriptor* descriptor);
     ReservedDevice ReserveDevice();
     ReservedInstance ReserveInstance();
 
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index d8714f0..fccf50d 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -1268,7 +1268,7 @@
     Ref<SwapChainBase> result;
     if (ConsumedError(CreateSwapChain(surface, descriptor), &result,
                       "calling %s.CreateSwapChain(%s).", this, descriptor)) {
-        return SwapChainBase::MakeError(this);
+        return SwapChainBase::MakeError(this, descriptor);
     }
     return result.Detach();
 }
diff --git a/src/dawn/native/ObjectBase.cpp b/src/dawn/native/ObjectBase.cpp
index 75c45d0..996424b 100644
--- a/src/dawn/native/ObjectBase.cpp
+++ b/src/dawn/native/ObjectBase.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include <mutex>
+#include <utility>
 
 #include "absl/strings/str_format.h"
 #include "dawn/native/Device.h"
@@ -84,8 +85,7 @@
 }
 
 void ApiObjectBase::APISetLabel(const char* label) {
-    mLabel = label;
-    SetLabelImpl();
+    SetLabel(label);
 }
 
 void ApiObjectBase::APIRelease() {
@@ -97,6 +97,11 @@
     Release();
 }
 
+void ApiObjectBase::SetLabel(std::string label) {
+    mLabel = std::move(label);
+    SetLabelImpl();
+}
+
 const std::string& ApiObjectBase::GetLabel() const {
     return mLabel;
 }
diff --git a/src/dawn/native/ObjectBase.h b/src/dawn/native/ObjectBase.h
index a4be1a1..830be94 100644
--- a/src/dawn/native/ObjectBase.h
+++ b/src/dawn/native/ObjectBase.h
@@ -87,6 +87,7 @@
     ~ApiObjectBase() override;
 
     virtual ObjectType GetType() const = 0;
+    void SetLabel(std::string label);
     const std::string& GetLabel() const;
 
     virtual void FormatLabel(absl::FormatSink* s) const;
diff --git a/src/dawn/native/SwapChain.cpp b/src/dawn/native/SwapChain.cpp
index 435aede..7726d50 100644
--- a/src/dawn/native/SwapChain.cpp
+++ b/src/dawn/native/SwapChain.cpp
@@ -28,7 +28,8 @@
 
 class ErrorSwapChain final : public SwapChainBase {
   public:
-    explicit ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {}
+    explicit ErrorSwapChain(DeviceBase* device, const SwapChainDescriptor* desc)
+        : SwapChainBase(device, desc, ObjectBase::kError) {}
 
   private:
     ResultOrError<Ref<TextureBase>> GetCurrentTextureImpl() override { UNREACHABLE(); }
@@ -120,12 +121,19 @@
     ASSERT(!mAttached);
 }
 
-SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag)
-    : ApiObjectBase(device, tag) {}
+SwapChainBase::SwapChainBase(DeviceBase* device,
+                             const SwapChainDescriptor* descriptor,
+                             ObjectBase::ErrorTag tag)
+    : ApiObjectBase(device, tag),
+      mWidth(descriptor->width),
+      mHeight(descriptor->height),
+      mFormat(descriptor->format),
+      mUsage(descriptor->usage),
+      mPresentMode(descriptor->presentMode) {}
 
 // static
-SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
-    return new ErrorSwapChain(device);
+SwapChainBase* SwapChainBase::MakeError(DeviceBase* device, const SwapChainDescriptor* desc) {
+    return new ErrorSwapChain(device, desc);
 }
 
 void SwapChainBase::DestroyImpl() {}
@@ -154,11 +162,25 @@
         DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains."));
 }
 
+TextureBase* SwapChainBase::APIGetCurrentTexture() {
+    Ref<TextureBase> result;
+    if (GetDevice()->ConsumedError(GetCurrentTexture(), &result, "calling %s.GetCurrentTexture()",
+                                   this)) {
+        TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
+        TextureBase* errorTexture = TextureBase::MakeError(GetDevice(), &desc);
+        SetChildLabel(errorTexture);
+        return errorTexture;
+    }
+    return result.Detach();
+}
+
 TextureViewBase* SwapChainBase::APIGetCurrentTextureView() {
     Ref<TextureViewBase> result;
     if (GetDevice()->ConsumedError(GetCurrentTextureView(), &result,
                                    "calling %s.GetCurrentTextureView()", this)) {
-        return TextureViewBase::MakeError(GetDevice());
+        TextureViewBase* errorView = TextureViewBase::MakeError(GetDevice());
+        SetChildLabel(errorView);
+        return errorView;
     }
     return result.Detach();
 }
@@ -172,6 +194,7 @@
     }
 
     DAWN_TRY_ASSIGN(mCurrentTexture, GetCurrentTextureImpl());
+    SetChildLabel(mCurrentTexture.Get());
 
     // Check that the return texture matches exactly what was given for this descriptor.
     ASSERT(mCurrentTexture->GetFormat().format == mFormat);
@@ -258,4 +281,8 @@
     return {};
 }
 
+void SwapChainBase::SetChildLabel(ApiObjectBase* child) const {
+    child->SetLabel(absl::StrFormat("of %s", this));
+}
+
 }  // namespace dawn::native
diff --git a/src/dawn/native/SwapChain.h b/src/dawn/native/SwapChain.h
index 14bb4b7..362e002 100644
--- a/src/dawn/native/SwapChain.h
+++ b/src/dawn/native/SwapChain.h
@@ -32,7 +32,7 @@
   public:
     SwapChainBase(DeviceBase* device, Surface* surface, const SwapChainDescriptor* descriptor);
 
-    static SwapChainBase* MakeError(DeviceBase* device);
+    static SwapChainBase* MakeError(DeviceBase* device, const SwapChainDescriptor* descriptor);
     ObjectType GetType() const override;
 
     // This is called when the swapchain is detached when one of the following happens:
@@ -59,6 +59,7 @@
                       wgpu::TextureUsage allowedUsage,
                       uint32_t width,
                       uint32_t height);
+    TextureBase* APIGetCurrentTexture();
     TextureViewBase* APIGetCurrentTextureView();
     void APIPresent();
 
@@ -72,11 +73,13 @@
     wgpu::BackendType GetBackendType() const;
 
   protected:
-    SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag);
+    SwapChainBase(DeviceBase* device, const SwapChainDescriptor* desc, ObjectBase::ErrorTag tag);
     ~SwapChainBase() override;
     void DestroyImpl() override;
 
   private:
+    void SetChildLabel(ApiObjectBase* child) const;
+
     bool mAttached = false;
     uint32_t mWidth;
     uint32_t mHeight;
diff --git a/src/dawn/samples/SampleUtils.cpp b/src/dawn/samples/SampleUtils.cpp
index 33e886b..55ec5d7 100644
--- a/src/dawn/samples/SampleUtils.cpp
+++ b/src/dawn/samples/SampleUtils.cpp
@@ -216,7 +216,7 @@
                                      deviceReservation.generation);
             cDevice = deviceReservation.device;
 
-            auto swapChainReservation = wireClient->ReserveSwapChain(cDevice);
+            auto swapChainReservation = wireClient->ReserveSwapChain(cDevice, &swapChainDesc);
             wireServer->InjectSwapChain(backendSwapChain, swapChainReservation.id,
                                         swapChainReservation.generation, deviceReservation.id,
                                         deviceReservation.generation);
diff --git a/src/dawn/tests/end2end/SwapChainTests.cpp b/src/dawn/tests/end2end/SwapChainTests.cpp
index 44b7562..8c10fb4 100644
--- a/src/dawn/tests/end2end/SwapChainTests.cpp
+++ b/src/dawn/tests/end2end/SwapChainTests.cpp
@@ -356,6 +356,10 @@
 
 // Test that sampling from swapchain is supported.
 TEST_P(SwapChainWithAdditionalUsageTests, SamplingFromSwapChain) {
+    // TODO(dawn:1551): Reenable on D3D11 after suppressing the D3D11 debug layer warning for
+    // setting the same private data multiple times.
+    DAWN_SUPPRESS_TEST_IF(IsD3D11());
+
     // Skip all tests if readable surface doesn't support texture binding
     DAWN_TEST_UNSUPPORTED_IF(
         (device.GetSupportedSurfaceUsage(surface) & wgpu::TextureUsage::TextureBinding) == 0);
diff --git a/src/dawn/tests/end2end/SwapChainValidationTests.cpp b/src/dawn/tests/end2end/SwapChainValidationTests.cpp
index 6b0b8af..bff4954 100644
--- a/src/dawn/tests/end2end/SwapChainValidationTests.cpp
+++ b/src/dawn/tests/end2end/SwapChainValidationTests.cpp
@@ -70,30 +70,34 @@
     wgpu::SwapChainDescriptor badDescriptor;
 
     // Checks that a RenderAttachment view is an error by trying to create a render pass on it.
-    void CheckTextureViewIsError(wgpu::TextureView view) { CheckTextureView(view, true, false); }
+    void CheckTextureIsError(wgpu::Texture texture) { CheckTexture(texture, true, false); }
 
     // Checks that a RenderAttachment view is an error by trying to submit a render pass on it.
-    void CheckTextureViewIsDestroyed(wgpu::TextureView view) {
-        CheckTextureView(view, false, true);
-    }
+    void CheckTextureIsDestroyed(wgpu::Texture texture) { CheckTexture(texture, false, true); }
 
     // Checks that a RenderAttachment view is valid by submitting a render pass on it.
-    void CheckTextureViewIsValid(wgpu::TextureView view) { CheckTextureView(view, false, false); }
+    void CheckTextureIsValid(wgpu::Texture texture) { CheckTexture(texture, false, false); }
 
   private:
-    void CheckTextureView(wgpu::TextureView view, bool errorAtFinish, bool errorAtSubmit) {
+    void CheckTexture(wgpu::Texture texture, bool error, bool destroyed) {
+        wgpu::TextureView view;
+        if (error) {
+            ASSERT_DEVICE_ERROR(view = texture.CreateView());
+        } else {
+            view = texture.CreateView();
+        }
         utils::ComboRenderPassDescriptor renderPassDesc({view});
 
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
         pass.End();
 
-        if (errorAtFinish) {
+        if (error) {
             ASSERT_DEVICE_ERROR(encoder.Finish());
         } else {
             wgpu::CommandBuffer commands = encoder.Finish();
 
-            if (errorAtSubmit) {
+            if (destroyed) {
                 ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
             } else {
                 queue.Submit(1, &commands);
@@ -102,10 +106,22 @@
     }
 };
 
+void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
+                                   const wgpu::SwapChainDescriptor& desc) {
+    EXPECT_EQ(desc.width, tex.GetWidth());
+    EXPECT_EQ(desc.height, tex.GetHeight());
+    EXPECT_EQ(desc.usage, tex.GetUsage());
+    EXPECT_EQ(desc.format, tex.GetFormat());
+    EXPECT_EQ(1u, tex.GetDepthOrArrayLayers());
+    EXPECT_EQ(1u, tex.GetMipLevelCount());
+    EXPECT_EQ(1u, tex.GetSampleCount());
+    EXPECT_EQ(wgpu::TextureDimension::e2D, tex.GetDimension());
+}
+
 // Control case for a successful swapchain creation and presenting.
 TEST_P(SwapChainValidationTests, CreationSuccess) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    swapchain.GetCurrentTexture();
     swapchain.Present();
 }
 
@@ -174,51 +190,51 @@
     wgpu::SwapChain swapchain;
     ASSERT_DEVICE_ERROR(swapchain = device.CreateSwapChain(surface, &badDescriptor));
 
-    wgpu::TextureView view;
-    ASSERT_DEVICE_ERROR(view = swapchain.GetCurrentTextureView());
-    CheckTextureViewIsError(view);
+    wgpu::Texture texture;
+    ASSERT_DEVICE_ERROR(texture = swapchain.GetCurrentTexture());
+    CheckTextureIsError(texture);
 
     ASSERT_DEVICE_ERROR(swapchain.Present());
 }
 
-// Check it is invalid to call present without getting a current view.
-TEST_P(SwapChainValidationTests, PresentWithoutCurrentView) {
+// Check it is invalid to call present without getting a current texture.
+TEST_P(SwapChainValidationTests, PresentWithoutCurrentTexture) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
 
-    // Check it is invalid if we never called GetCurrentTextureView
+    // Check it is invalid if we never called GetCurrentTexture
     ASSERT_DEVICE_ERROR(swapchain.Present());
 
     // Check it is invalid if we never called since the last present.
-    swapchain.GetCurrentTextureView();
+    swapchain.GetCurrentTexture();
     swapchain.Present();
     ASSERT_DEVICE_ERROR(swapchain.Present());
 }
 
-// Check that the current view isn't destroyed when the ref to the swapchain is lost because the
+// Check that the current texture isn't destroyed when the ref to the swapchain is lost because the
 // swapchain is kept alive by the surface. Also check after we lose all refs to the surface, the
 // texture is destroyed.
-TEST_P(SwapChainValidationTests, ViewValidAfterSwapChainRefLost) {
+TEST_P(SwapChainValidationTests, TextureValidAfterSwapChainRefLost) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    wgpu::Texture texture = swapchain.GetCurrentTexture();
 
     swapchain = nullptr;
-    CheckTextureViewIsValid(view);
+    CheckTextureIsValid(texture);
 
     surface = nullptr;
-    CheckTextureViewIsDestroyed(view);
+    CheckTextureIsDestroyed(texture);
 }
 
-// Check that the current view is the destroyed state after present.
-TEST_P(SwapChainValidationTests, ViewDestroyedAfterPresent) {
+// Check that the current texture is the destroyed state after present.
+TEST_P(SwapChainValidationTests, TextureDestroyedAfterPresent) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    wgpu::Texture texture = swapchain.GetCurrentTexture();
     swapchain.Present();
 
-    CheckTextureViewIsDestroyed(view);
+    CheckTextureIsDestroyed(texture);
 }
 
-// Check that returned view is of the current format / usage / dimension / size / sample count
-TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
+// Check that returned texture is of the current format / usage / dimension / size / sample count
+TEST_P(SwapChainValidationTests, ReturnedTextureCharacteristics) {
     utils::ComboRenderPipelineDescriptor pipelineDesc;
     pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
         @vertex fn main() -> @builtin(position) vec4f {
@@ -255,7 +271,7 @@
 
     // Get the swapchain view and try to use it in the render pass to trigger all the validation.
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    wgpu::TextureView view = swapchain.GetCurrentTexture().CreateView();
 
     // Validation will also check the dimension of the view is 2D, and it's usage contains
     // RenderAttachment
@@ -267,11 +283,42 @@
 
     queue.Submit(1, &commands);
 
-    // Check that view doesn't have extra formats like Sampled.
-    // TODO(cwallez@chromium.org): also check for [Readonly]Storage once that's implemented.
+    // Check that view doesn't have an extra Sampled usage.
     wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
         device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
     ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}}));
+
+    // Check that view doesn't have an extra Storage usage.
+    bgl = utils::MakeBindGroupLayout(
+        device, {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly,
+                  wgpu::TextureFormat::R32Uint}});
+    ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}}));
+}
+
+// Check the reflection of textures returned by GetCurrentTexture on valid swapchain.
+TEST_P(SwapChainValidationTests, ReflectionValidGetCurrentTexture) {
+    // Check with the goodDescriptor.
+    {
+        wgpu::SwapChain swapChain = device.CreateSwapChain(surface, &goodDescriptor);
+        CheckTextureMatchesDescriptor(swapChain.GetCurrentTexture(), goodDescriptor);
+    }
+    // Check with properties that can be changed while keeping a valid descriptor.
+    {
+        wgpu::SwapChainDescriptor otherDescriptor = goodDescriptor;
+        otherDescriptor.width = 2;
+        otherDescriptor.height = 7;
+        wgpu::SwapChain swapChain = device.CreateSwapChain(surface, &goodDescriptor);
+        CheckTextureMatchesDescriptor(swapChain.GetCurrentTexture(), goodDescriptor);
+    }
+}
+
+// Check the reflection of textures returned by GetCurrentTexture on valid swapchain.
+TEST_P(SwapChainValidationTests, ReflectionErrorGetCurrentTexture) {
+    wgpu::SwapChain swapChain;
+    ASSERT_DEVICE_ERROR(swapChain = device.CreateSwapChain(surface, &badDescriptor));
+    wgpu::Texture texture;
+    ASSERT_DEVICE_ERROR(texture = swapChain.GetCurrentTexture());
+    CheckTextureMatchesDescriptor(texture, badDescriptor);
 }
 
 // Check that failing to create a new swapchain doesn't replace the previous one.
@@ -279,59 +326,59 @@
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
     ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &badDescriptor));
 
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    swapchain.GetCurrentTexture();
     swapchain.Present();
 }
 
-// Check that after replacement, all swapchain operations are errors and the view is destroyed.
+// Check that after replacement, all swapchain operations are errors and the texture is destroyed.
 TEST_P(SwapChainValidationTests, ReplacedSwapChainIsInvalid) {
     {
         wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
         device.CreateSwapChain(surface, &goodDescriptor);
-        ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
+        ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTexture());
     }
 
     {
         wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
-        wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
+        wgpu::Texture texture = replacedSwapChain.GetCurrentTexture();
         device.CreateSwapChain(surface, &goodDescriptor);
 
-        CheckTextureViewIsDestroyed(view);
+        CheckTextureIsDestroyed(texture);
         ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
     }
 }
 
-// Check that after surface destruction, all swapchain operations are errors and the view is
+// Check that after surface destruction, all swapchain operations are errors and the texture is
 // destroyed. The test is split in two to reset the wgpu::Surface in the middle.
-TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetView) {
+TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetTexture) {
     wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
     surface = nullptr;
-    ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
+    ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTexture());
 }
-TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetView) {
+TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetTexture) {
     wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
+    wgpu::Texture texture = replacedSwapChain.GetCurrentTexture();
     surface = nullptr;
 
-    CheckTextureViewIsDestroyed(view);
+    CheckTextureIsDestroyed(texture);
     ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
 }
 
 // Test that new swap chain present fails after device is lost
 TEST_P(SwapChainValidationTests, SwapChainPresentFailsAfterDeviceLost) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
-    wgpu::TextureView view = swapchain.GetCurrentTextureView();
+    swapchain.GetCurrentTexture();
 
     LoseDeviceForTesting();
     ASSERT_DEVICE_ERROR(swapchain.Present());
 }
 
-// Test that new swap chain get current texture view fails after device is lost
-TEST_P(SwapChainValidationTests, SwapChainGetCurrentTextureViewFailsAfterDevLost) {
+// Test that new swap chain get current texture fails after device is lost
+TEST_P(SwapChainValidationTests, SwapChainGetCurrentTextureFailsAfterDevLost) {
     wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
 
     LoseDeviceForTesting();
-    ASSERT_DEVICE_ERROR(swapchain.GetCurrentTextureView());
+    ASSERT_DEVICE_ERROR(swapchain.GetCurrentTexture());
 }
 
 // Test that creation of a new swapchain fails after device is lost
diff --git a/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp b/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp
index e7f0f83..1e3fe24 100644
--- a/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp
@@ -21,17 +21,27 @@
 namespace {
 
 using testing::Mock;
+using testing::Return;
 
 class WireInjectSwapChainTests : public WireTest {
   public:
-    WireInjectSwapChainTests() {}
+    WireInjectSwapChainTests() {
+        swapChainDesc = {};
+        swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
+        swapChainDesc.format = WGPUTextureFormat_RGBA8Unorm;
+        swapChainDesc.width = 17;
+        swapChainDesc.height = 42;
+        swapChainDesc.presentMode = WGPUPresentMode_Mailbox;
+    }
     ~WireInjectSwapChainTests() override = default;
+
+    WGPUSwapChainDescriptor swapChainDesc;
 };
 
 // Test that reserving and injecting a swapchain makes calls on the client object forward to the
 // server object correctly.
 TEST_F(WireInjectSwapChainTests, CallAfterReserveInject) {
-    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
+    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
 
     WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
     EXPECT_CALL(api, SwapChainReference(apiSwapchain));
@@ -46,8 +56,8 @@
 
 // Test that reserve correctly returns different IDs each time.
 TEST_F(WireInjectSwapChainTests, ReserveDifferentIDs) {
-    ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device);
-    ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device);
+    ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
+    ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
 
     ASSERT_NE(reservation1.id, reservation2.id);
     ASSERT_NE(reservation1.swapchain, reservation2.swapchain);
@@ -55,7 +65,7 @@
 
 // Test that injecting the same id without a destroy first fails.
 TEST_F(WireInjectSwapChainTests, InjectExistingID) {
-    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
+    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
 
     WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
     EXPECT_CALL(api, SwapChainReference(apiSwapchain));
@@ -71,7 +81,7 @@
 
 // Test that the server only borrows the swapchain and does a single reference-release
 TEST_F(WireInjectSwapChainTests, InjectedSwapChainLifetime) {
-    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
+    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
 
     // Injecting the swapchain adds a reference
     WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
@@ -95,17 +105,17 @@
 TEST_F(WireInjectSwapChainTests, ReclaimSwapChainReservation) {
     // Test that doing a reservation and full release is an error.
     {
-        ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
+        ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
         wgpuSwapChainRelease(reservation.swapchain);
         FlushClient(false);
     }
 
     // Test that doing a reservation and then reclaiming it recycles the ID.
     {
-        ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device);
+        ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
         GetWireClient()->ReclaimSwapChainReservation(reservation1);
 
-        ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device);
+        ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
 
         // The ID is the same, but the generation is still different.
         ASSERT_EQ(reservation1.id, reservation2.id);
@@ -116,5 +126,30 @@
     }
 }
 
+// Test that the texture's reflection is correct for injected swapchains in the wire.
+TEST_F(WireInjectSwapChainTests, SwapChainTextureReflection) {
+    ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
+
+    WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
+    EXPECT_CALL(api, SwapChainReference(apiSwapchain));
+    ASSERT_TRUE(GetWireServer()->InjectSwapChain(apiSwapchain, reservation.id,
+                                                 reservation.generation, reservation.deviceId,
+                                                 reservation.deviceGeneration));
+
+    WGPUTexture tex = wgpuSwapChainGetCurrentTexture(reservation.swapchain);
+    WGPUTexture apiTex = api.GetNewTexture();
+    EXPECT_CALL(api, SwapChainGetCurrentTexture(apiSwapchain)).WillOnce(Return(apiTex));
+    FlushClient();
+
+    EXPECT_EQ(swapChainDesc.width, wgpuTextureGetWidth(tex));
+    EXPECT_EQ(swapChainDesc.height, wgpuTextureGetHeight(tex));
+    EXPECT_EQ(swapChainDesc.usage, wgpuTextureGetUsage(tex));
+    EXPECT_EQ(swapChainDesc.format, wgpuTextureGetFormat(tex));
+    EXPECT_EQ(1u, wgpuTextureGetDepthOrArrayLayers(tex));
+    EXPECT_EQ(1u, wgpuTextureGetMipLevelCount(tex));
+    EXPECT_EQ(1u, wgpuTextureGetSampleCount(tex));
+    EXPECT_EQ(WGPUTextureDimension_2D, wgpuTextureGetDimension(tex));
+}
+
 }  // anonymous namespace
 }  // namespace dawn::wire
diff --git a/src/dawn/wire/BUILD.gn b/src/dawn/wire/BUILD.gn
index 56aa038..b159113 100644
--- a/src/dawn/wire/BUILD.gn
+++ b/src/dawn/wire/BUILD.gn
@@ -101,6 +101,8 @@
     "client/RequestTracker.h",
     "client/ShaderModule.cpp",
     "client/ShaderModule.h",
+    "client/SwapChain.cpp",
+    "client/SwapChain.h",
     "client/Texture.cpp",
     "client/Texture.h",
     "server/ObjectStorage.h",
diff --git a/src/dawn/wire/CMakeLists.txt b/src/dawn/wire/CMakeLists.txt
index c470cea..7b7df16 100644
--- a/src/dawn/wire/CMakeLists.txt
+++ b/src/dawn/wire/CMakeLists.txt
@@ -74,6 +74,8 @@
     "client/RequestTracker.h"
     "client/ShaderModule.cpp"
     "client/ShaderModule.h"
+    "client/SwapChain.cpp"
+    "client/SwapChain.h"
     "client/Texture.cpp"
     "client/Texture.h"
     "server/ObjectStorage.h"
diff --git a/src/dawn/wire/WireClient.cpp b/src/dawn/wire/WireClient.cpp
index 9845b38..88a899b 100644
--- a/src/dawn/wire/WireClient.cpp
+++ b/src/dawn/wire/WireClient.cpp
@@ -33,8 +33,9 @@
     return mImpl->ReserveTexture(device, descriptor);
 }
 
-ReservedSwapChain WireClient::ReserveSwapChain(WGPUDevice device) {
-    return mImpl->ReserveSwapChain(device);
+ReservedSwapChain WireClient::ReserveSwapChain(WGPUDevice device,
+                                               const WGPUSwapChainDescriptor* descriptor) {
+    return mImpl->ReserveSwapChain(device, descriptor);
 }
 
 ReservedDevice WireClient::ReserveDevice() {
diff --git a/src/dawn/wire/client/ApiObjects.h b/src/dawn/wire/client/ApiObjects.h
index 672b80a..5bf283a 100644
--- a/src/dawn/wire/client/ApiObjects.h
+++ b/src/dawn/wire/client/ApiObjects.h
@@ -24,6 +24,7 @@
 #include "dawn/wire/client/QuerySet.h"
 #include "dawn/wire/client/Queue.h"
 #include "dawn/wire/client/ShaderModule.h"
+#include "dawn/wire/client/SwapChain.h"
 #include "dawn/wire/client/Texture.h"
 
 #include "dawn/wire/client/ApiObjects_autogen.h"
diff --git a/src/dawn/wire/client/Client.cpp b/src/dawn/wire/client/Client.cpp
index 1c5222d..5feaca1 100644
--- a/src/dawn/wire/client/Client.cpp
+++ b/src/dawn/wire/client/Client.cpp
@@ -93,8 +93,9 @@
     return result;
 }
 
-ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) {
-    SwapChain* swapChain = Make<SwapChain>();
+ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device,
+                                           const WGPUSwapChainDescriptor* descriptor) {
+    SwapChain* swapChain = Make<SwapChain>(nullptr, descriptor);
 
     ReservedSwapChain result;
     result.swapchain = ToAPI(swapChain);
diff --git a/src/dawn/wire/client/Client.h b/src/dawn/wire/client/Client.h
index f16af64..6a5e02f 100644
--- a/src/dawn/wire/client/Client.h
+++ b/src/dawn/wire/client/Client.h
@@ -71,7 +71,8 @@
     MemoryTransferService* GetMemoryTransferService() const { return mMemoryTransferService; }
 
     ReservedTexture ReserveTexture(WGPUDevice device, const WGPUTextureDescriptor* descriptor);
-    ReservedSwapChain ReserveSwapChain(WGPUDevice device);
+    ReservedSwapChain ReserveSwapChain(WGPUDevice device,
+                                       const WGPUSwapChainDescriptor* descriptor);
     ReservedDevice ReserveDevice();
     ReservedInstance ReserveInstance();
 
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index badee9c1..cbe204c 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -213,6 +213,11 @@
     return QuerySet::Create(this, descriptor);
 }
 
+WGPUSwapChain Device::CreateSwapChain(WGPUSurface surface,
+                                      const WGPUSwapChainDescriptor* descriptor) {
+    return SwapChain::Create(this, surface, descriptor);
+}
+
 WGPUTexture Device::CreateTexture(const WGPUTextureDescriptor* descriptor) {
     return Texture::Create(this, descriptor);
 }
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index ecc73b0..e5354e1 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -49,6 +49,7 @@
                                    WGPUCreateRenderPipelineAsyncCallback callback,
                                    void* userdata);
     WGPUQuerySet CreateQuerySet(const WGPUQuerySetDescriptor* descriptor);
+    WGPUSwapChain CreateSwapChain(WGPUSurface surface, const WGPUSwapChainDescriptor* descriptor);
     WGPUTexture CreateTexture(const WGPUTextureDescriptor* descriptor);
     WGPUTexture CreateErrorTexture(const WGPUTextureDescriptor* descriptor);
 
diff --git a/src/dawn/wire/client/SwapChain.cpp b/src/dawn/wire/client/SwapChain.cpp
new file mode 100644
index 0000000..58b39ab
--- /dev/null
+++ b/src/dawn/wire/client/SwapChain.cpp
@@ -0,0 +1,69 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn/wire/client/SwapChain.h"
+
+#include "dawn/wire/client/Client.h"
+#include "dawn/wire/client/Device.h"
+#include "dawn/wire/client/Texture.h"
+
+namespace dawn::wire::client {
+
+// static
+WGPUSwapChain SwapChain::Create(Device* device,
+                                WGPUSurface surface,
+                                const WGPUSwapChainDescriptor* descriptor) {
+    Client* wireClient = device->GetClient();
+    SwapChain* swapChain = wireClient->Make<SwapChain>(surface, descriptor);
+
+    // Send the Device::CreateSwapChain command without modifications.
+    DeviceCreateSwapChainCmd cmd;
+    cmd.self = ToAPI(device);
+    cmd.selfId = device->GetWireId();
+    cmd.descriptor = descriptor;
+    cmd.result = swapChain->GetWireHandle();
+    wireClient->SerializeCommand(cmd);
+
+    return ToAPI(swapChain);
+}
+
+SwapChain::SwapChain(const ObjectBaseParams& params,
+                     WGPUSurface,
+                     const WGPUSwapChainDescriptor* descriptor)
+    : ObjectBase(params) {
+    mTextureDescriptor = {};
+    mTextureDescriptor.size = {descriptor->width, descriptor->height, 1};
+    mTextureDescriptor.format = descriptor->format;
+    mTextureDescriptor.usage = descriptor->usage;
+    mTextureDescriptor.dimension = WGPUTextureDimension_2D;
+    mTextureDescriptor.mipLevelCount = 1;
+    mTextureDescriptor.sampleCount = 1;
+}
+
+SwapChain::~SwapChain() = default;
+
+WGPUTexture SwapChain::GetCurrentTexture() {
+    Client* wireClient = GetClient();
+    Texture* texture = wireClient->Make<Texture>(&mTextureDescriptor);
+
+    SwapChainGetCurrentTextureCmd cmd;
+    cmd.self = ToAPI(this);
+    cmd.selfId = GetWireId();
+    cmd.result = texture->GetWireHandle();
+    wireClient->SerializeCommand(cmd);
+
+    return ToAPI(texture);
+}
+
+}  // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/SwapChain.h b/src/dawn/wire/client/SwapChain.h
new file mode 100644
index 0000000..ada1058
--- /dev/null
+++ b/src/dawn/wire/client/SwapChain.h
@@ -0,0 +1,45 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_
+#define SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_
+
+#include "dawn/webgpu.h"
+
+#include "dawn/wire/client/ObjectBase.h"
+
+namespace dawn::wire::client {
+
+class Device;
+
+class SwapChain final : public ObjectBase {
+  public:
+    static WGPUSwapChain Create(Device* device,
+                                WGPUSurface surface,
+                                const WGPUSwapChainDescriptor* descriptor);
+
+    SwapChain(const ObjectBaseParams& params,
+              WGPUSurface surface,
+              const WGPUSwapChainDescriptor* descriptor);
+    ~SwapChain() override;
+
+    WGPUTexture GetCurrentTexture();
+
+  private:
+    WGPUTextureDescriptor mTextureDescriptor;
+};
+
+}  // namespace dawn::wire::client
+
+#endif  // SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_