diff --git a/BUILD.gn b/BUILD.gn index f58ef64374..3ad34dcd79 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -593,6 +593,7 @@ test("dawn_unittests") { "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 c4e4088620..ceb1c68a33 100644 --- a/src/dawn_wire/WireClient.cpp +++ b/src/dawn_wire/WireClient.cpp @@ -36,4 +36,8 @@ namespace dawn_wire { 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 79706e089f..bfa186f9c5 100644 --- a/src/dawn_wire/WireServer.cpp +++ b/src/dawn_wire/WireServer.cpp @@ -31,4 +31,8 @@ namespace dawn_wire { 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 ca2fe94367..195c55fd65 100644 --- a/src/dawn_wire/client/Client.cpp +++ b/src/dawn_wire/client/Client.cpp @@ -27,4 +27,15 @@ namespace dawn_wire { namespace client { DeviceAllocator().Free(mDevice); } + ReservedTexture Client::ReserveTexture(DawnDevice cDevice) { + Device* device = reinterpret_cast(cDevice); + ObjectAllocator::ObjectAndSerial* allocation = TextureAllocator().New(device); + + ReservedTexture result; + result.texture = reinterpret_cast(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 7ea62790c0..5fd74ceb31 100644 --- a/src/dawn_wire/client/Client.h +++ b/src/dawn_wire/client/Client.h @@ -17,6 +17,7 @@ #include +#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 @@ namespace dawn_wire { namespace client { ~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 752b60fe86..6d420baf96 100644 --- a/src/dawn_wire/server/Server.cpp +++ b/src/dawn_wire/server/Server.cpp @@ -24,7 +24,7 @@ namespace dawn_wire { namespace server { deviceData->valid = true; auto userdata = static_cast(reinterpret_cast(this)); - procs.deviceSetErrorCallback(device, ForwardDeviceError, userdata); + mProcs.deviceSetErrorCallback(device, ForwardDeviceError, userdata); } Server::~Server() { @@ -35,4 +35,22 @@ namespace dawn_wire { namespace server { return mSerializer->GetCmdSpace(size); } + bool Server::InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation) { + ObjectData* 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 ac622ea954..2081f552e3 100644 --- a/src/dawn_wire/server/Server.h +++ b/src/dawn_wire/server/Server.h @@ -42,6 +42,8 @@ namespace dawn_wire { namespace server { 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 d0d9277244..4e9e56a422 100644 --- a/src/include/dawn_wire/WireClient.h +++ b/src/include/dawn_wire/WireClient.h @@ -25,6 +25,12 @@ namespace dawn_wire { 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 @@ namespace dawn_wire { DawnProcTable GetProcs() const; const char* HandleCommands(const char* commands, size_t size) override final; + ReservedTexture ReserveTexture(DawnDevice device); + private: std::unique_ptr mImpl; }; diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h index 04220f8c35..084ab54e08 100644 --- a/src/include/dawn_wire/WireServer.h +++ b/src/include/dawn_wire/WireServer.h @@ -32,6 +32,8 @@ namespace dawn_wire { 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 mImpl; }; diff --git a/src/tests/unittests/wire/WireInjectTextureTests.cpp b/src/tests/unittests/wire/WireInjectTextureTests.cpp new file mode 100644 index 0000000000..7c3a83bd2f --- /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 72b1236420..7bc94e0b3d 100644 --- a/src/tests/unittests/wire/WireTest.cpp +++ b/src/tests/unittests/wire/WireTest.cpp @@ -74,6 +74,18 @@ void WireTest::FlushServer() { 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 bddf613bcb..3d3243766b 100644 --- a/src/tests/unittests/wire/WireTest.h +++ b/src/tests/unittests/wire/WireTest.h @@ -89,6 +89,11 @@ class WireTest : public testing::Test { DawnDevice apiDevice; DawnDevice device; + dawn_wire::WireServer* GetWireServer(); + dawn_wire::WireClient* GetWireClient(); + + void DeleteServer(); + private: void SetupIgnoredCallExpectations(); bool mIgnoreSetCallbackCalls = false;