From 943a1a2d7a7c7caf8432bc78c78bfad22cb46efc Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Fri, 26 May 2023 10:32:17 +0000 Subject: [PATCH] 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 Reviewed-by: Austin Eng Commit-Queue: Corentin Wallez Reviewed-by: Loko Kung --- dawn.json | 1 + dawn_wire.json | 5 +- include/dawn/wire/WireClient.h | 3 +- src/dawn/native/Device.cpp | 2 +- src/dawn/native/ObjectBase.cpp | 9 +- src/dawn/native/ObjectBase.h | 1 + src/dawn/native/SwapChain.cpp | 39 ++++- src/dawn/native/SwapChain.h | 7 +- src/dawn/samples/SampleUtils.cpp | 2 +- src/dawn/tests/end2end/SwapChainTests.cpp | 4 + .../end2end/SwapChainValidationTests.cpp | 137 ++++++++++++------ .../wire/WireInjectSwapChainTests.cpp | 53 +++++-- src/dawn/wire/BUILD.gn | 2 + src/dawn/wire/CMakeLists.txt | 2 + src/dawn/wire/WireClient.cpp | 5 +- src/dawn/wire/client/ApiObjects.h | 1 + src/dawn/wire/client/Client.cpp | 5 +- src/dawn/wire/client/Client.h | 3 +- src/dawn/wire/client/Device.cpp | 5 + src/dawn/wire/client/Device.h | 1 + src/dawn/wire/client/SwapChain.cpp | 69 +++++++++ src/dawn/wire/client/SwapChain.h | 45 ++++++ 22 files changed, 328 insertions(+), 73 deletions(-) create mode 100644 src/dawn/wire/client/SwapChain.cpp create mode 100644 src/dawn/wire/client/SwapChain.h diff --git a/dawn.json b/dawn.json index fe3bf9eb94..3d0bdc5524 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 aa96202272..a22d1333c5 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 02b4c075a4..1139715c03 100644 --- a/include/dawn/wire/WireClient.h +++ b/include/dawn/wire/WireClient.h @@ -71,7 +71,8 @@ class DAWN_WIRE_EXPORT WireClient : public CommandHandler { 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 d8714f0d5a..fccf50df57 100644 --- a/src/dawn/native/Device.cpp +++ b/src/dawn/native/Device.cpp @@ -1268,7 +1268,7 @@ SwapChainBase* DeviceBase::APICreateSwapChain(Surface* surface, Ref 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 75c45d0c35..996424b42f 100644 --- a/src/dawn/native/ObjectBase.cpp +++ b/src/dawn/native/ObjectBase.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include "absl/strings/str_format.h" #include "dawn/native/Device.h" @@ -84,8 +85,7 @@ ApiObjectBase::~ApiObjectBase() { } void ApiObjectBase::APISetLabel(const char* label) { - mLabel = label; - SetLabelImpl(); + SetLabel(label); } void ApiObjectBase::APIRelease() { @@ -97,6 +97,11 @@ void ApiObjectBase::APIRelease() { 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 a4be1a101e..830be94234 100644 --- a/src/dawn/native/ObjectBase.h +++ b/src/dawn/native/ObjectBase.h @@ -87,6 +87,7 @@ class ApiObjectBase : public ObjectBase, public LinkNode { ~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 435aedeac2..7726d501dc 100644 --- a/src/dawn/native/SwapChain.cpp +++ b/src/dawn/native/SwapChain.cpp @@ -28,7 +28,8 @@ namespace { 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> GetCurrentTextureImpl() override { UNREACHABLE(); } @@ -120,12 +121,19 @@ SwapChainBase::~SwapChainBase() { 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 @@ void SwapChainBase::APIConfigure(wgpu::TextureFormat format, DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains.")); } +TextureBase* SwapChainBase::APIGetCurrentTexture() { + Ref 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 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 @@ ResultOrError> SwapChainBase::GetCurrentTexture() { } 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 @@ MaybeError SwapChainBase::ValidateGetCurrentTexture() const { 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 14bb4b747d..362e002c99 100644 --- a/src/dawn/native/SwapChain.h +++ b/src/dawn/native/SwapChain.h @@ -32,7 +32,7 @@ class SwapChainBase : public ApiObjectBase { 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 @@ class SwapChainBase : public ApiObjectBase { wgpu::TextureUsage allowedUsage, uint32_t width, uint32_t height); + TextureBase* APIGetCurrentTexture(); TextureViewBase* APIGetCurrentTextureView(); void APIPresent(); @@ -72,11 +73,13 @@ class SwapChainBase : public ApiObjectBase { 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 33e886b66d..55ec5d768d 100644 --- a/src/dawn/samples/SampleUtils.cpp +++ b/src/dawn/samples/SampleUtils.cpp @@ -216,7 +216,7 @@ wgpu::Device CreateCppDawnDevice() { 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 44b7562e27..8c10fb412c 100644 --- a/src/dawn/tests/end2end/SwapChainTests.cpp +++ b/src/dawn/tests/end2end/SwapChainTests.cpp @@ -356,6 +356,10 @@ TEST_P(SwapChainWithAdditionalUsageTests, GetSurfaceSupportedUsage) { // 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 6b0b8afee3..bff4954832 100644 --- a/src/dawn/tests/end2end/SwapChainValidationTests.cpp +++ b/src/dawn/tests/end2end/SwapChainValidationTests.cpp @@ -70,30 +70,34 @@ class SwapChainValidationTests : public DawnTest { 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 @@ class SwapChainValidationTests : public DawnTest { } }; +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 @@ TEST_P(SwapChainValidationTests, OperationsOnErrorSwapChain) { 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 @@ TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) { // 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 @@ TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) { 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 @@ TEST_P(SwapChainValidationTests, ErrorSwapChainDoesntReplacePreviousOne) { 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 e7f0f83284..1e3fe24f00 100644 --- a/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp +++ b/src/dawn/tests/unittests/wire/WireInjectSwapChainTests.cpp @@ -21,17 +21,27 @@ namespace dawn::wire { 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_F(WireInjectSwapChainTests, CallAfterReserveInject) { // 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_F(WireInjectSwapChainTests, ReserveDifferentIDs) { // 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_F(WireInjectSwapChainTests, InjectExistingID) { // 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, InjectedSwapChainLifetime) { 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_F(WireInjectSwapChainTests, ReclaimSwapChainReservation) { } } +// 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 56aa038879..b159113f6f 100644 --- a/src/dawn/wire/BUILD.gn +++ b/src/dawn/wire/BUILD.gn @@ -101,6 +101,8 @@ dawn_component("wire") { "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 c470cea071..7b7df167a6 100644 --- a/src/dawn/wire/CMakeLists.txt +++ b/src/dawn/wire/CMakeLists.txt @@ -74,6 +74,8 @@ target_sources(dawn_wire PRIVATE "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 9845b3844f..88a899b7e7 100644 --- a/src/dawn/wire/WireClient.cpp +++ b/src/dawn/wire/WireClient.cpp @@ -33,8 +33,9 @@ ReservedTexture WireClient::ReserveTexture(WGPUDevice device, 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 672b80a699..5bf283aa2c 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 1c5222d82a..5feaca19dc 100644 --- a/src/dawn/wire/client/Client.cpp +++ b/src/dawn/wire/client/Client.cpp @@ -93,8 +93,9 @@ ReservedTexture Client::ReserveTexture(WGPUDevice device, const WGPUTextureDescr return result; } -ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) { - SwapChain* swapChain = Make(); +ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device, + const WGPUSwapChainDescriptor* descriptor) { + SwapChain* swapChain = Make(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 f16af64e17..6a5e02f28b 100644 --- a/src/dawn/wire/client/Client.h +++ b/src/dawn/wire/client/Client.h @@ -71,7 +71,8 @@ class Client : public ClientBase { 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 badee9c1f9..cbe204c685 100644 --- a/src/dawn/wire/client/Device.cpp +++ b/src/dawn/wire/client/Device.cpp @@ -213,6 +213,11 @@ WGPUQuerySet Device::CreateQuerySet(const WGPUQuerySetDescriptor* descriptor) { 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 ecc73b0454..e5354e1478 100644 --- a/src/dawn/wire/client/Device.h +++ b/src/dawn/wire/client/Device.h @@ -49,6 +49,7 @@ class Device final : public ObjectBase { 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 0000000000..58b39abf4a --- /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(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(&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 0000000000..ada1058caa --- /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_