Add dawn_wire entrypoints to inject texture in the wire This allows reserving a texture ID in the client and injecting textures in the wire, so that the WebGPU control channel can create WebGPU textures backed by SharedImages in Chromium. BUG=941543 Change-Id: I1efcfe3dce024bb2d3592f22225407a97b641c1f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/5820 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn index f58ef64..3ad34dc 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -593,6 +593,7 @@ "src/tests/unittests/wire/WireBufferMappingTests.cpp", "src/tests/unittests/wire/WireCallbackTests.cpp", "src/tests/unittests/wire/WireFenceTests.cpp", + "src/tests/unittests/wire/WireInjectTextureTests.cpp", "src/tests/unittests/wire/WireOptionalTests.cpp", "src/tests/unittests/wire/WireTest.cpp", "src/tests/unittests/wire/WireTest.h",
diff --git a/src/dawn_wire/WireClient.cpp b/src/dawn_wire/WireClient.cpp index c4e4088..ceb1c68 100644 --- a/src/dawn_wire/WireClient.cpp +++ b/src/dawn_wire/WireClient.cpp
@@ -36,4 +36,8 @@ return mImpl->HandleCommands(commands, size); } + ReservedTexture WireClient::ReserveTexture(DawnDevice device) { + return mImpl->ReserveTexture(device); + } + } // namespace dawn_wire
diff --git a/src/dawn_wire/WireServer.cpp b/src/dawn_wire/WireServer.cpp index 79706e0..bfa186f 100644 --- a/src/dawn_wire/WireServer.cpp +++ b/src/dawn_wire/WireServer.cpp
@@ -31,4 +31,8 @@ return mImpl->HandleCommands(commands, size); } + bool WireServer::InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation) { + return mImpl->InjectTexture(texture, id, generation); + } + } // namespace dawn_wire
diff --git a/src/dawn_wire/client/Client.cpp b/src/dawn_wire/client/Client.cpp index ca2fe94..195c55f 100644 --- a/src/dawn_wire/client/Client.cpp +++ b/src/dawn_wire/client/Client.cpp
@@ -27,4 +27,15 @@ DeviceAllocator().Free(mDevice); } + ReservedTexture Client::ReserveTexture(DawnDevice cDevice) { + Device* device = reinterpret_cast<Device*>(cDevice); + ObjectAllocator<Texture>::ObjectAndSerial* allocation = TextureAllocator().New(device); + + ReservedTexture result; + result.texture = reinterpret_cast<DawnTexture>(allocation->object.get()); + result.id = allocation->object->id; + result.generation = allocation->serial; + return result; + } + }} // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Client.h b/src/dawn_wire/client/Client.h index 7ea6279..5fd74ce 100644 --- a/src/dawn_wire/client/Client.h +++ b/src/dawn_wire/client/Client.h
@@ -17,6 +17,7 @@ #include <dawn_wire/Wire.h> +#include "dawn_wire/WireClient.h" #include "dawn_wire/WireCmd_autogen.h" #include "dawn_wire/WireDeserializeAllocator.h" #include "dawn_wire/client/ClientBase_autogen.h" @@ -31,6 +32,7 @@ ~Client(); const char* HandleCommands(const char* commands, size_t size); + ReservedTexture ReserveTexture(DawnDevice device); void* GetCmdSpace(size_t size) { return mSerializer->GetCmdSpace(size);
diff --git a/src/dawn_wire/server/Server.cpp b/src/dawn_wire/server/Server.cpp index 752b60f..6d420ba 100644 --- a/src/dawn_wire/server/Server.cpp +++ b/src/dawn_wire/server/Server.cpp
@@ -24,7 +24,7 @@ deviceData->valid = true; auto userdata = static_cast<DawnCallbackUserdata>(reinterpret_cast<intptr_t>(this)); - procs.deviceSetErrorCallback(device, ForwardDeviceError, userdata); + mProcs.deviceSetErrorCallback(device, ForwardDeviceError, userdata); } Server::~Server() { @@ -35,4 +35,22 @@ return mSerializer->GetCmdSpace(size); } + bool Server::InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation) { + ObjectData<DawnTexture>* data = TextureObjects().Allocate(id); + if (data == nullptr) { + return false; + } + + data->handle = texture; + data->serial = generation; + data->valid = true; + data->allocated = true; + + // The texture is externally owned so it shouldn't be destroyed when we receive a destroy + // message from the client. Add a reference to counterbalance the eventual release. + mProcs.textureReference(texture); + + return true; + } + }} // namespace dawn_wire::server
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h index ac622ea..2081f55 100644 --- a/src/dawn_wire/server/Server.h +++ b/src/dawn_wire/server/Server.h
@@ -42,6 +42,8 @@ const char* HandleCommands(const char* commands, size_t size); + bool InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation); + private: void* GetCmdSpace(size_t size);
diff --git a/src/include/dawn_wire/WireClient.h b/src/include/dawn_wire/WireClient.h index d0d9277..4e9e56a 100644 --- a/src/include/dawn_wire/WireClient.h +++ b/src/include/dawn_wire/WireClient.h
@@ -25,6 +25,12 @@ class Client; } + struct ReservedTexture { + DawnTexture texture; + uint32_t id; + uint32_t generation; + }; + class DAWN_WIRE_EXPORT WireClient : public CommandHandler { public: WireClient(CommandSerializer* serializer); @@ -34,6 +40,8 @@ DawnProcTable GetProcs() const; const char* HandleCommands(const char* commands, size_t size) override final; + ReservedTexture ReserveTexture(DawnDevice device); + private: std::unique_ptr<client::Client> mImpl; };
diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h index 04220f8..084ab54 100644 --- a/src/include/dawn_wire/WireServer.h +++ b/src/include/dawn_wire/WireServer.h
@@ -32,6 +32,8 @@ const char* HandleCommands(const char* commands, size_t size) override final; + bool InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation); + private: std::unique_ptr<server::Server> mImpl; };
diff --git a/src/tests/unittests/wire/WireInjectTextureTests.cpp b/src/tests/unittests/wire/WireInjectTextureTests.cpp new file mode 100644 index 0000000..7c3a83b --- /dev/null +++ b/src/tests/unittests/wire/WireInjectTextureTests.cpp
@@ -0,0 +1,83 @@ +// Copyright 2019 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 "tests/unittests/wire/WireTest.h" + +#include "dawn_wire/WireClient.h" +#include "dawn_wire/WireServer.h" + +using namespace testing; +using namespace dawn_wire; + +class WireInjectTextureTests : public WireTest { + public: + WireInjectTextureTests() : WireTest(true) { + } + ~WireInjectTextureTests() override = default; +}; + +// Test that reserving and injecting a texture makes calls on the client object forward to the +// server object correctly. +TEST_F(WireInjectTextureTests, CallAfterReserveInject) { + ReservedTexture reservation = GetWireClient()->ReserveTexture(device); + + DawnTexture apiTexture = api.GetNewTexture(); + EXPECT_CALL(api, TextureReference(apiTexture)); + ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); + + dawnTextureCreateDefaultTextureView(reservation.texture); + EXPECT_CALL(api, TextureCreateDefaultTextureView(apiTexture)).WillOnce(Return(nullptr)); + FlushClient(); +} + +// Test that reserve correctly returns different IDs each time. +TEST_F(WireInjectTextureTests, ReserveDifferentIDs) { + ReservedTexture reservation1 = GetWireClient()->ReserveTexture(device); + ReservedTexture reservation2 = GetWireClient()->ReserveTexture(device); + + ASSERT_NE(reservation1.id, reservation2.id); + ASSERT_NE(reservation1.texture, reservation2.texture); +} + +// Test that injecting the same id without a destroy first fails. +TEST_F(WireInjectTextureTests, InjectExistingID) { + ReservedTexture reservation = GetWireClient()->ReserveTexture(device); + + DawnTexture apiTexture = api.GetNewTexture(); + EXPECT_CALL(api, TextureReference(apiTexture)); + ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); + + // ID already in use, call fails. + ASSERT_FALSE( + GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); +} + +// Test that the server only borrows the texture and does a single reference-release +TEST_F(WireInjectTextureTests, InjectedTextureLifetime) { + ReservedTexture reservation = GetWireClient()->ReserveTexture(device); + + // Injecting the texture adds a reference + DawnTexture apiTexture = api.GetNewTexture(); + EXPECT_CALL(api, TextureReference(apiTexture)); + ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); + + // Releasing the texture removes a single reference. + dawnTextureRelease(reservation.texture); + EXPECT_CALL(api, TextureRelease(apiTexture)); + FlushClient(); + + // Deleting the server doesn't release a second reference. + DeleteServer(); + Mock::VerifyAndClearExpectations(&api); +}
diff --git a/src/tests/unittests/wire/WireTest.cpp b/src/tests/unittests/wire/WireTest.cpp index 72b1236..7bc94e0 100644 --- a/src/tests/unittests/wire/WireTest.cpp +++ b/src/tests/unittests/wire/WireTest.cpp
@@ -74,6 +74,18 @@ ASSERT_TRUE(mS2cBuf->Flush()); } +dawn_wire::WireServer* WireTest::GetWireServer() { + return mWireServer.get(); +} + +dawn_wire::WireClient* WireTest::GetWireClient() { + return mWireClient.get(); +} + +void WireTest::DeleteServer() { + mWireServer = nullptr; +} + void WireTest::SetupIgnoredCallExpectations() { if (mIgnoreSetCallbackCalls) { EXPECT_CALL(api, OnBuilderSetErrorCallback(_, _, _, _)).Times(AnyNumber());
diff --git a/src/tests/unittests/wire/WireTest.h b/src/tests/unittests/wire/WireTest.h index bddf613..3d32437 100644 --- a/src/tests/unittests/wire/WireTest.h +++ b/src/tests/unittests/wire/WireTest.h
@@ -89,6 +89,11 @@ DawnDevice apiDevice; DawnDevice device; + dawn_wire::WireServer* GetWireServer(); + dawn_wire::WireClient* GetWireClient(); + + void DeleteServer(); + private: void SetupIgnoredCallExpectations(); bool mIgnoreSetCallbackCalls = false;