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_