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;