Ensure all wire child objects are destroyed before their device
Destroying a device will implicit destroy all its child objects.
Attempting to use a child object after results in a fatal error.
Bug: dawn:384
Change-Id: I43c27c92cacde759be83cca79ac890f41bac3927
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37002
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/dawn_wire.json b/dawn_wire.json
index 505af37..4d1df0f 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -33,20 +33,20 @@
{ "name": "write flush info", "type": "uint8_t", "annotation": "const*", "length": "write flush info length", "skip_serialize": true}
],
"device create buffer": [
- { "name": "device", "type": "device" },
+ { "name": "device id", "type": "ObjectId" },
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
{ "name": "handle create info length", "type": "uint64_t" },
{ "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
],
"device create ready compute pipeline": [
- { "name": "device", "type": "device" },
+ { "name": "device id", "type": "ObjectId" },
{ "name": "request serial", "type": "uint64_t" },
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "compute pipeline"},
{ "name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"}
],
"device create ready render pipeline": [
- { "name": "device", "type": "device" },
+ { "name": "device id", "type": "ObjectId" },
{ "name": "request serial", "type": "uint64_t" },
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "render pipeline"},
{ "name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"}
diff --git a/generator/templates/dawn_wire/server/ServerBase.h b/generator/templates/dawn_wire/server/ServerBase.h
index 4c488ee..66193a4 100644
--- a/generator/templates/dawn_wire/server/ServerBase.h
+++ b/generator/templates/dawn_wire/server/ServerBase.h
@@ -32,7 +32,7 @@
protected:
void DestroyAllObjects(const DawnProcTable& procs) {
//* Free all objects when the server is destroyed
- {% for type in by_category["object"] %}
+ {% for type in by_category["object"] if type.name.get() != "device" %}
{
std::vector<{{as_cType(type.name)}}> handles = mKnown{{type.name.CamelCase()}}.AcquireAllHandles();
for ({{as_cType(type.name)}} handle : handles) {
@@ -40,6 +40,13 @@
}
}
{% endfor %}
+ //* Release devices last because dawn_native requires this.
+ {
+ std::vector<WGPUDevice> handles = mKnownDevice.AcquireAllHandles();
+ for (WGPUDevice handle : handles) {
+ procs.deviceRelease(handle);
+ }
+ }
}
{% for type in by_category["object"] %}
diff --git a/generator/templates/dawn_wire/server/ServerDoers.cpp b/generator/templates/dawn_wire/server/ServerDoers.cpp
index 08b407a..0c6ce42 100644
--- a/generator/templates/dawn_wire/server/ServerDoers.cpp
+++ b/generator/templates/dawn_wire/server/ServerDoers.cpp
@@ -77,9 +77,28 @@
if (data == nullptr) {
return false;
}
+ if (data->device != nullptr) {
+ auto* device = static_cast<ObjectData<WGPUDevice>*>(data->device);
+ if (!UntrackDeviceChild(device, objectType, objectId)) {
+ return false;
+ }
+ }
{% if type.name.CamelCase() in server_reverse_lookup_objects %}
{{type.name.CamelCase()}}ObjectIdTable().Remove(data->handle);
{% endif %}
+ {% if type.name.get() == "device" %}
+ //* TODO(crbug.com/dawn/384): This is a hack to make sure that all child objects
+ //* are destroyed before their device. We should have a solution in
+ //* Dawn native that makes all child objects internally null if their
+ //* Device is destroyed.
+ while (data->childObjectTypesAndIds.size() > 0) {
+ ObjectType childObjectType;
+ ObjectId childObjectId;
+ std::tie(childObjectType, childObjectId) = UnpackObjectTypeAndId(
+ *data->childObjectTypesAndIds.begin());
+ DoDestroyObject(childObjectType, childObjectId);
+ }
+ {% endif %}
if (data->handle != nullptr) {
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
}
diff --git a/generator/templates/dawn_wire/server/ServerHandlers.cpp b/generator/templates/dawn_wire/server/ServerHandlers.cpp
index 22bbc8c..1341e41 100644
--- a/generator/templates/dawn_wire/server/ServerHandlers.cpp
+++ b/generator/templates/dawn_wire/server/ServerHandlers.cpp
@@ -17,7 +17,6 @@
namespace dawn_wire { namespace server {
{% for command in cmd_records["command"] %}
- {% set type = command.derived_object %}
{% set method = command.derived_method %}
{% set is_method = method != None %}
{% set returns = is_method and method.return_type.name.canonical_case() != "void" %}
@@ -53,6 +52,24 @@
return false;
}
{{name}}Data->generation = cmd.{{name}}.generation;
+
+ //* TODO(crbug.com/dawn/384): This is a hack to make sure that all child objects
+ //* are destroyed before their device. The dawn_native device needs to track all child objects so
+ //* it can destroy them if the device is destroyed first.
+ {% if command.derived_object %}
+ {% set type = command.derived_object %}
+ {% if type.name.get() == "device" %}
+ {{name}}Data->device = DeviceObjects().Get(cmd.selfId);
+ {% else %}
+ auto* selfData = {{type.name.CamelCase()}}Objects().Get(cmd.selfId);
+ {{name}}Data->device = selfData->device;
+ {% endif %}
+ if ({{name}}Data->device != nullptr) {
+ if (!TrackDeviceChild({{name}}Data->device, ObjectType::{{Type}}, cmd.{{name}}.id)) {
+ return false;
+ }
+ }
+ {% endif %}
{% endfor %}
//* Do command
diff --git a/src/dawn_wire/WireServer.cpp b/src/dawn_wire/WireServer.cpp
index 0056d5b..723f691 100644
--- a/src/dawn_wire/WireServer.cpp
+++ b/src/dawn_wire/WireServer.cpp
@@ -32,8 +32,12 @@
return mImpl->HandleCommands(commands, size);
}
- bool WireServer::InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation) {
- return mImpl->InjectTexture(texture, id, generation);
+ bool WireServer::InjectTexture(WGPUTexture texture,
+ uint32_t id,
+ uint32_t generation,
+ uint32_t deviceId,
+ uint32_t deviceGeneration) {
+ return mImpl->InjectTexture(texture, id, generation, deviceId, deviceGeneration);
}
namespace server {
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index 2244ace..40b5759 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -66,7 +66,7 @@
buffer->mSize = descriptor->size;
DeviceCreateBufferCmd cmd;
- cmd.device = ToAPI(device);
+ cmd.deviceId = device->id;
cmd.descriptor = descriptor;
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
diff --git a/src/dawn_wire/client/Client.cpp b/src/dawn_wire/client/Client.cpp
index 51af5a6..0ca5f61 100644
--- a/src/dawn_wire/client/Client.cpp
+++ b/src/dawn_wire/client/Client.cpp
@@ -59,6 +59,9 @@
void Client::DestroyAllObjects() {
for (auto& objectList : mObjects) {
ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.data());
+ if (objectType == ObjectType::Device) {
+ continue;
+ }
while (!objectList.empty()) {
ObjectBase* object = objectList.head()->value();
@@ -69,6 +72,16 @@
FreeObject(objectType, object);
}
}
+
+ while (!mObjects[ObjectType::Device].empty()) {
+ ObjectBase* object = mObjects[ObjectType::Device].head()->value();
+
+ DestroyObjectCmd cmd;
+ cmd.objectType = ObjectType::Device;
+ cmd.objectId = object->id;
+ SerializeCommand(cmd);
+ FreeObject(ObjectType::Device, object);
+ }
}
WGPUDevice Client::GetDevice() {
@@ -85,6 +98,8 @@
result.texture = ToAPI(allocation->object.get());
result.id = allocation->object->id;
result.generation = allocation->generation;
+ result.deviceId = FromAPI(device)->id;
+ result.deviceGeneration = DeviceAllocator().GetGeneration(FromAPI(device)->id);
return result;
}
diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp
index af4cbfb..08a3e33 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -215,7 +215,7 @@
}
DeviceCreateReadyComputePipelineCmd cmd;
- cmd.device = ToAPI(this);
+ cmd.deviceId = this->id;
cmd.descriptor = descriptor;
uint64_t serial = mCreateReadyPipelineRequestSerial++;
@@ -270,7 +270,7 @@
"GPU device disconnected", userdata);
}
DeviceCreateReadyRenderPipelineCmd cmd;
- cmd.device = ToAPI(this);
+ cmd.deviceId = this->id;
cmd.descriptor = descriptor;
uint64_t serial = mCreateReadyPipelineRequestSerial++;
diff --git a/src/dawn_wire/server/ObjectStorage.h b/src/dawn_wire/server/ObjectStorage.h
index 9963158..74cc5a7 100644
--- a/src/dawn_wire/server/ObjectStorage.h
+++ b/src/dawn_wire/server/ObjectStorage.h
@@ -20,6 +20,7 @@
#include <algorithm>
#include <map>
+#include <unordered_set>
namespace dawn_wire { namespace server {
@@ -32,6 +33,8 @@
// Whether this object has been allocated, used by the KnownObjects queries
// TODO(cwallez@chromium.org): make this an internal bit vector in KnownObjects.
bool allocated;
+
+ ObjectDataBase<WGPUDevice>* device = nullptr;
};
// Stores what the backend knows about the type.
@@ -48,6 +51,26 @@
BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped;
};
+ // Pack the ObjectType and ObjectId as a single value for storage in
+ // an std::unordered_set. This lets us avoid providing our own hash and
+ // equality comparison operators.
+ inline uint64_t PackObjectTypeAndId(ObjectType type, ObjectId id) {
+ static_assert(sizeof(ObjectType) * 8 <= 32, "");
+ static_assert(sizeof(ObjectId) * 8 <= 32, "");
+ return (static_cast<uint64_t>(type) << 32) + id;
+ }
+
+ inline std::pair<ObjectType, ObjectId> UnpackObjectTypeAndId(uint64_t payload) {
+ ObjectType type = static_cast<ObjectType>(payload >> 32);
+ ObjectId id = payload & 0xFFFFFFFF;
+ return std::make_pair(type, id);
+ }
+
+ template <>
+ struct ObjectData<WGPUDevice> : public ObjectDataBase<WGPUDevice> {
+ std::unordered_set<uint64_t> childObjectTypesAndIds;
+ };
+
// Keeps track of the mapping between client IDs and backend objects.
template <typename T>
class KnownObjects {
diff --git a/src/dawn_wire/server/Server.cpp b/src/dawn_wire/server/Server.cpp
index bb5644b..67a9dd5 100644
--- a/src/dawn_wire/server/Server.cpp
+++ b/src/dawn_wire/server/Server.cpp
@@ -66,15 +66,29 @@
DestroyAllObjects(mProcs);
}
- bool Server::InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation) {
+ bool Server::InjectTexture(WGPUTexture texture,
+ uint32_t id,
+ uint32_t generation,
+ uint32_t deviceId,
+ uint32_t deviceGeneration) {
+ ObjectData<WGPUDevice>* device = DeviceObjects().Get(deviceId);
+ if (device == nullptr || device->generation != deviceGeneration) {
+ return false;
+ }
+
ObjectData<WGPUTexture>* data = TextureObjects().Allocate(id);
if (data == nullptr) {
return false;
}
+ if (!TrackDeviceChild(device, ObjectType::Texture, id)) {
+ return false;
+ }
+
data->handle = texture;
data->generation = generation;
data->allocated = true;
+ data->device = device;
// 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.
@@ -83,4 +97,25 @@
return true;
}
+ bool TrackDeviceChild(ObjectDataBase<WGPUDevice>* device, ObjectType type, ObjectId id) {
+ auto it = static_cast<ObjectData<WGPUDevice>*>(device)->childObjectTypesAndIds.insert(
+ PackObjectTypeAndId(type, id));
+ if (!it.second) {
+ // An object of this type and id already exists.
+ return false;
+ }
+ return true;
+ }
+
+ bool UntrackDeviceChild(ObjectDataBase<WGPUDevice>* device, ObjectType type, ObjectId id) {
+ auto& children = static_cast<ObjectData<WGPUDevice>*>(device)->childObjectTypesAndIds;
+ auto it = children.find(PackObjectTypeAndId(type, id));
+ if (it == children.end()) {
+ // An object of this type and id was already deleted.
+ return false;
+ }
+ children.erase(it);
+ return true;
+ }
+
}} // namespace dawn_wire::server
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index 867f29b..f45ed0d 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -161,7 +161,11 @@
const volatile char* HandleCommandsImpl(const volatile char* commands,
size_t size) override;
- bool InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation);
+ bool InjectTexture(WGPUTexture texture,
+ uint32_t id,
+ uint32_t generation,
+ uint32_t deviceId,
+ uint32_t deviceGeneration);
template <typename T,
typename Enable = std::enable_if<std::is_base_of<CallbackUserdata, T>::value>>
@@ -215,6 +219,9 @@
std::shared_ptr<bool> mIsAlive;
};
+ bool TrackDeviceChild(ObjectDataBase<WGPUDevice>* device, ObjectType type, ObjectId id);
+ bool UntrackDeviceChild(ObjectDataBase<WGPUDevice>* device, ObjectType type, ObjectId id);
+
std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService();
}} // namespace dawn_wire::server
diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp
index e842bb1..fdc850b 100644
--- a/src/dawn_wire/server/ServerBuffer.cpp
+++ b/src/dawn_wire/server/ServerBuffer.cpp
@@ -120,18 +120,27 @@
return true;
}
- bool Server::DoDeviceCreateBuffer(WGPUDevice device,
+ bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
const WGPUBufferDescriptor* descriptor,
ObjectHandle bufferResult,
uint64_t handleCreateInfoLength,
const uint8_t* handleCreateInfo) {
+ auto* device = DeviceObjects().Get(deviceId);
+ if (device == nullptr) {
+ return false;
+ }
+
// Create and register the buffer object.
auto* resultData = BufferObjects().Allocate(bufferResult.id);
if (resultData == nullptr) {
return false;
}
resultData->generation = bufferResult.generation;
- resultData->handle = mProcs.deviceCreateBuffer(device, descriptor);
+ resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
+ resultData->device = device;
+ if (!TrackDeviceChild(device, ObjectType::Buffer, bufferResult.id)) {
+ return false;
+ }
// If the buffer isn't mapped at creation, we are done.
if (!descriptor->mappedAtCreation) {
diff --git a/src/dawn_wire/server/ServerDevice.cpp b/src/dawn_wire/server/ServerDevice.cpp
index cd2eba7..8884dd1 100644
--- a/src/dawn_wire/server/ServerDevice.cpp
+++ b/src/dawn_wire/server/ServerDevice.cpp
@@ -59,23 +59,32 @@
}
bool Server::DoDeviceCreateReadyComputePipeline(
- WGPUDevice cDevice,
+ ObjectId deviceId,
uint64_t requestSerial,
ObjectHandle pipelineObjectHandle,
const WGPUComputePipelineDescriptor* descriptor) {
+ auto* device = DeviceObjects().Get(deviceId);
+ if (device == nullptr) {
+ return false;
+ }
+
auto* resultData = ComputePipelineObjects().Allocate(pipelineObjectHandle.id);
if (resultData == nullptr) {
return false;
}
resultData->generation = pipelineObjectHandle.generation;
+ resultData->device = device;
+ if (!TrackDeviceChild(device, ObjectType::ComputePipeline, pipelineObjectHandle.id)) {
+ return false;
+ }
auto userdata = MakeUserdata<CreateReadyPipelineUserData>();
userdata->requestSerial = requestSerial;
userdata->pipelineObjectID = pipelineObjectHandle.id;
mProcs.deviceCreateReadyComputePipeline(
- cDevice, descriptor,
+ device->handle, descriptor,
ForwardToServer<decltype(&Server::OnCreateReadyComputePipelineCallback)>::Func<
&Server::OnCreateReadyComputePipelineCallback>(),
userdata.release());
@@ -116,23 +125,32 @@
SerializeCommand(cmd);
}
- bool Server::DoDeviceCreateReadyRenderPipeline(WGPUDevice cDevice,
+ bool Server::DoDeviceCreateReadyRenderPipeline(ObjectId deviceId,
uint64_t requestSerial,
ObjectHandle pipelineObjectHandle,
const WGPURenderPipelineDescriptor* descriptor) {
+ auto* device = DeviceObjects().Get(deviceId);
+ if (device == nullptr) {
+ return false;
+ }
+
auto* resultData = RenderPipelineObjects().Allocate(pipelineObjectHandle.id);
if (resultData == nullptr) {
return false;
}
resultData->generation = pipelineObjectHandle.generation;
+ resultData->device = device;
+ if (!TrackDeviceChild(device, ObjectType::RenderPipeline, pipelineObjectHandle.id)) {
+ return false;
+ }
auto userdata = MakeUserdata<CreateReadyPipelineUserData>();
userdata->requestSerial = requestSerial;
userdata->pipelineObjectID = pipelineObjectHandle.id;
mProcs.deviceCreateReadyRenderPipeline(
- cDevice, descriptor,
+ device->handle, descriptor,
ForwardToServer<decltype(&Server::OnCreateReadyRenderPipelineCallback)>::Func<
&Server::OnCreateReadyRenderPipelineCallback>(),
userdata.release());
diff --git a/src/include/dawn_wire/WireClient.h b/src/include/dawn_wire/WireClient.h
index 1a22fe5..8af02a97 100644
--- a/src/include/dawn_wire/WireClient.h
+++ b/src/include/dawn_wire/WireClient.h
@@ -34,6 +34,8 @@
WGPUTexture texture;
uint32_t id;
uint32_t generation;
+ uint32_t deviceId;
+ uint32_t deviceGeneration;
};
struct DAWN_WIRE_EXPORT WireClientDescriptor {
diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h
index 7627ab8..ad36f44 100644
--- a/src/include/dawn_wire/WireServer.h
+++ b/src/include/dawn_wire/WireServer.h
@@ -43,7 +43,12 @@
const volatile char* HandleCommands(const volatile char* commands,
size_t size) override final;
- bool InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation);
+ // TODO(enga): Remove defaults after updating Chrome.
+ bool InjectTexture(WGPUTexture texture,
+ uint32_t id,
+ uint32_t generation,
+ uint32_t deviceId = 1,
+ uint32_t deviceGeneration = 0);
private:
std::unique_ptr<server::Server> mImpl;
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 8fec31f..1c78506 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -218,6 +218,7 @@
"unittests/wire/WireBasicTests.cpp",
"unittests/wire/WireBufferMappingTests.cpp",
"unittests/wire/WireCreateReadyPipelineTests.cpp",
+ "unittests/wire/WireDestroyObjectTests.cpp",
"unittests/wire/WireDisconnectTests.cpp",
"unittests/wire/WireErrorCallbackTests.cpp",
"unittests/wire/WireExtensionTests.cpp",
diff --git a/src/tests/unittests/wire/WireDestroyObjectTests.cpp b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
new file mode 100644
index 0000000..f5e16b7
--- /dev/null
+++ b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
@@ -0,0 +1,45 @@
+// 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 "tests/unittests/wire/WireTest.h"
+
+using namespace testing;
+using namespace dawn_wire;
+
+class WireDestroyObjectTests : public WireTest {};
+
+// Test that destroying the device also destroys child objects.
+TEST_F(WireDestroyObjectTests, DestroyDeviceDestroysChildren) {
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
+
+ WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
+ EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
+
+ FlushClient();
+
+ // Release the device. It should cause the command encoder to be destroyed.
+ wgpuDeviceRelease(device);
+
+ Sequence s1, s2;
+ // The device and child objects should be released.
+ EXPECT_CALL(api, CommandEncoderRelease(apiEncoder)).InSequence(s1);
+ EXPECT_CALL(api, QueueRelease(apiQueue)).InSequence(s2);
+ EXPECT_CALL(api, DeviceRelease(apiDevice)).InSequence(s1, s2);
+
+ FlushClient();
+
+ // Using the command encoder should be an error.
+ wgpuCommandEncoderFinish(encoder, nullptr);
+ FlushClient(false);
+}
diff --git a/src/tests/unittests/wire/WireDisconnectTests.cpp b/src/tests/unittests/wire/WireDisconnectTests.cpp
index 4c99ced..f44df13 100644
--- a/src/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/tests/unittests/wire/WireDisconnectTests.cpp
@@ -145,9 +145,10 @@
DeleteClient();
// Expect release on all objects created by the client.
- EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1);
- EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1);
- EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1);
- EXPECT_CALL(api, SamplerRelease(apiSampler)).Times(1);
+ Sequence s1, s2, s3;
+ EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1).InSequence(s1);
+ EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1).InSequence(s2);
+ EXPECT_CALL(api, SamplerRelease(apiSampler)).Times(1).InSequence(s3);
+ EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1).InSequence(s1, s2, s3);
FlushClient();
}
diff --git a/src/tests/unittests/wire/WireInjectTextureTests.cpp b/src/tests/unittests/wire/WireInjectTextureTests.cpp
index 9e327cf..c6a1f2c 100644
--- a/src/tests/unittests/wire/WireInjectTextureTests.cpp
+++ b/src/tests/unittests/wire/WireInjectTextureTests.cpp
@@ -34,7 +34,8 @@
WGPUTexture apiTexture = api.GetNewTexture();
EXPECT_CALL(api, TextureReference(apiTexture));
- ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+ ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+ reservation.deviceId, reservation.deviceGeneration));
wgpuTextureCreateView(reservation.texture, nullptr);
WGPUTextureView apiDummyView = api.GetNewTextureView();
@@ -57,11 +58,13 @@
WGPUTexture apiTexture = api.GetNewTexture();
EXPECT_CALL(api, TextureReference(apiTexture));
- ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+ ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+ reservation.deviceId, reservation.deviceGeneration));
// ID already in use, call fails.
- ASSERT_FALSE(
- GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+ ASSERT_FALSE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+ reservation.deviceId,
+ reservation.deviceGeneration));
}
// Test that the server only borrows the texture and does a single reference-release
@@ -71,7 +74,8 @@
// Injecting the texture adds a reference
WGPUTexture apiTexture = api.GetNewTexture();
EXPECT_CALL(api, TextureReference(apiTexture));
- ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+ ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+ reservation.deviceId, reservation.deviceGeneration));
// Releasing the texture removes a single reference.
wgpuTextureRelease(reservation.texture);