// Copyright 2021 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/tests/unittests/wire/WireTest.h" #include "dawn/wire/WireClient.h" #include "dawn/wire/WireServer.h" namespace dawn::wire { namespace { using testing::Mock; using testing::Return; class WireInjectSwapChainTests : public WireTest { public: 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, &swapChainDesc); WGPUSwapChain apiSwapchain = api.GetNewSwapChain(); EXPECT_CALL(api, SwapChainReference(apiSwapchain)); ASSERT_TRUE(GetWireServer()->InjectSwapChain(apiSwapchain, reservation.id, reservation.generation, reservation.deviceId, reservation.deviceGeneration)); wgpuSwapChainPresent(reservation.swapchain); EXPECT_CALL(api, SwapChainPresent(apiSwapchain)); FlushClient(); } // Test that reserve correctly returns different IDs each time. TEST_F(WireInjectSwapChainTests, ReserveDifferentIDs) { ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc); ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc); ASSERT_NE(reservation1.id, reservation2.id); ASSERT_NE(reservation1.swapchain, reservation2.swapchain); } // Test that injecting the same id without a destroy first fails. TEST_F(WireInjectSwapChainTests, InjectExistingID) { 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)); // ID already in use, call fails. ASSERT_FALSE(GetWireServer()->InjectSwapChain(apiSwapchain, reservation.id, reservation.generation, reservation.deviceId, reservation.deviceGeneration)); } // Test that the server only borrows the swapchain and does a single reference-release TEST_F(WireInjectSwapChainTests, InjectedSwapChainLifetime) { ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc); // Injecting the swapchain adds a reference WGPUSwapChain apiSwapchain = api.GetNewSwapChain(); EXPECT_CALL(api, SwapChainReference(apiSwapchain)); ASSERT_TRUE(GetWireServer()->InjectSwapChain(apiSwapchain, reservation.id, reservation.generation, reservation.deviceId, reservation.deviceGeneration)); // Releasing the swapchain removes a single reference. wgpuSwapChainRelease(reservation.swapchain); EXPECT_CALL(api, SwapChainRelease(apiSwapchain)); FlushClient(); // Deleting the server doesn't release a second reference. DeleteServer(); Mock::VerifyAndClearExpectations(&api); } // Test that a swapchain reservation can be reclaimed. This is necessary to // avoid leaking ObjectIDs for reservations that are never injected. TEST_F(WireInjectSwapChainTests, ReclaimSwapChainReservation) { // Test that doing a reservation and full release is an error. { 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, &swapChainDesc); GetWireClient()->ReclaimSwapChainReservation(reservation1); ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc); // The ID is the same, but the generation is still different. ASSERT_EQ(reservation1.id, reservation2.id); ASSERT_NE(reservation1.generation, reservation2.generation); // No errors should occur. FlushClient(); } } // 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