1 // Copyright 2021 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/unittests/wire/WireTest.h"
16
17 #include "dawn_wire/WireClient.h"
18 #include "dawn_wire/WireServer.h"
19
20 using namespace testing;
21 using namespace dawn_wire;
22
23 class WireInjectDeviceTests : public WireTest {
24 public:
WireInjectDeviceTests()25 WireInjectDeviceTests() {
26 }
27 ~WireInjectDeviceTests() override = default;
28 };
29
30 // Test that reserving and injecting a device makes calls on the client object forward to the
31 // server object correctly.
TEST_F(WireInjectDeviceTests,CallAfterReserveInject)32 TEST_F(WireInjectDeviceTests, CallAfterReserveInject) {
33 ReservedDevice reservation = GetWireClient()->ReserveDevice();
34
35 WGPUDevice serverDevice = api.GetNewDevice();
36 EXPECT_CALL(api, DeviceReference(serverDevice));
37 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, _, _));
38 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, _, _));
39 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, _, _));
40 ASSERT_TRUE(
41 GetWireServer()->InjectDevice(serverDevice, reservation.id, reservation.generation));
42
43 WGPUBufferDescriptor bufferDesc = {};
44 wgpuDeviceCreateBuffer(reservation.device, &bufferDesc);
45 WGPUBuffer serverBuffer = api.GetNewBuffer();
46 EXPECT_CALL(api, DeviceCreateBuffer(serverDevice, _)).WillOnce(Return(serverBuffer));
47 FlushClient();
48
49 // Called on shutdown.
50 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, nullptr, nullptr))
51 .Times(Exactly(1));
52 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, nullptr, nullptr)).Times(Exactly(1));
53 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, nullptr, nullptr))
54 .Times(Exactly(1));
55 }
56
57 // Test that reserve correctly returns different IDs each time.
TEST_F(WireInjectDeviceTests,ReserveDifferentIDs)58 TEST_F(WireInjectDeviceTests, ReserveDifferentIDs) {
59 ReservedDevice reservation1 = GetWireClient()->ReserveDevice();
60 ReservedDevice reservation2 = GetWireClient()->ReserveDevice();
61
62 ASSERT_NE(reservation1.id, reservation2.id);
63 ASSERT_NE(reservation1.device, reservation2.device);
64 }
65
66 // Test that injecting the same id without a destroy first fails.
TEST_F(WireInjectDeviceTests,InjectExistingID)67 TEST_F(WireInjectDeviceTests, InjectExistingID) {
68 ReservedDevice reservation = GetWireClient()->ReserveDevice();
69
70 WGPUDevice serverDevice = api.GetNewDevice();
71 EXPECT_CALL(api, DeviceReference(serverDevice));
72 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, _, _));
73 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, _, _));
74 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, _, _));
75 ASSERT_TRUE(
76 GetWireServer()->InjectDevice(serverDevice, reservation.id, reservation.generation));
77
78 // ID already in use, call fails.
79 ASSERT_FALSE(
80 GetWireServer()->InjectDevice(serverDevice, reservation.id, reservation.generation));
81
82 // Called on shutdown.
83 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, nullptr, nullptr))
84 .Times(Exactly(1));
85 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, nullptr, nullptr)).Times(Exactly(1));
86 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, nullptr, nullptr))
87 .Times(Exactly(1));
88 }
89
90 // Test that the server only borrows the device and does a single reference-release
TEST_F(WireInjectDeviceTests,InjectedDeviceLifetime)91 TEST_F(WireInjectDeviceTests, InjectedDeviceLifetime) {
92 ReservedDevice reservation = GetWireClient()->ReserveDevice();
93
94 // Injecting the device adds a reference
95 WGPUDevice serverDevice = api.GetNewDevice();
96 EXPECT_CALL(api, DeviceReference(serverDevice));
97 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, _, _));
98 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, _, _));
99 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, _, _));
100 ASSERT_TRUE(
101 GetWireServer()->InjectDevice(serverDevice, reservation.id, reservation.generation));
102
103 // Releasing the device removes a single reference and clears its error callbacks.
104 wgpuDeviceRelease(reservation.device);
105 EXPECT_CALL(api, DeviceRelease(serverDevice));
106 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, nullptr, nullptr)).Times(1);
107 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, nullptr, nullptr)).Times(1);
108 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, nullptr, nullptr)).Times(1);
109 FlushClient();
110
111 // Deleting the server doesn't release a second reference.
112 DeleteServer();
113 Mock::VerifyAndClearExpectations(&api);
114 }
115
116 // Test that it is an error to get the primary queue of a device before it has been
117 // injected on the server.
TEST_F(WireInjectDeviceTests,GetQueueBeforeInject)118 TEST_F(WireInjectDeviceTests, GetQueueBeforeInject) {
119 ReservedDevice reservation = GetWireClient()->ReserveDevice();
120
121 wgpuDeviceGetQueue(reservation.device);
122 FlushClient(false);
123 }
124
125 // Test that it is valid to get the primary queue of a device after it has been
126 // injected on the server.
TEST_F(WireInjectDeviceTests,GetQueueAfterInject)127 TEST_F(WireInjectDeviceTests, GetQueueAfterInject) {
128 ReservedDevice reservation = GetWireClient()->ReserveDevice();
129
130 WGPUDevice serverDevice = api.GetNewDevice();
131 EXPECT_CALL(api, DeviceReference(serverDevice));
132 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, _, _));
133 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, _, _));
134 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, _, _));
135 ASSERT_TRUE(
136 GetWireServer()->InjectDevice(serverDevice, reservation.id, reservation.generation));
137
138 wgpuDeviceGetQueue(reservation.device);
139
140 WGPUQueue apiQueue = api.GetNewQueue();
141 EXPECT_CALL(api, DeviceGetQueue(serverDevice)).WillOnce(Return(apiQueue));
142 FlushClient();
143
144 // Called on shutdown.
145 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice, nullptr, nullptr))
146 .Times(Exactly(1));
147 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice, nullptr, nullptr)).Times(Exactly(1));
148 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice, nullptr, nullptr))
149 .Times(Exactly(1));
150 }
151
152 // Test that the list of live devices can be reflected using GetDevice.
TEST_F(WireInjectDeviceTests,ReflectLiveDevices)153 TEST_F(WireInjectDeviceTests, ReflectLiveDevices) {
154 // Reserve two devices.
155 ReservedDevice reservation1 = GetWireClient()->ReserveDevice();
156 ReservedDevice reservation2 = GetWireClient()->ReserveDevice();
157
158 // Inject both devices.
159
160 WGPUDevice serverDevice1 = api.GetNewDevice();
161 EXPECT_CALL(api, DeviceReference(serverDevice1));
162 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice1, _, _));
163 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice1, _, _));
164 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice1, _, _));
165 ASSERT_TRUE(
166 GetWireServer()->InjectDevice(serverDevice1, reservation1.id, reservation1.generation));
167
168 WGPUDevice serverDevice2 = api.GetNewDevice();
169 EXPECT_CALL(api, DeviceReference(serverDevice2));
170 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice2, _, _));
171 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice2, _, _));
172 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice2, _, _));
173 ASSERT_TRUE(
174 GetWireServer()->InjectDevice(serverDevice2, reservation2.id, reservation2.generation));
175
176 // Test that both devices can be reflected.
177 ASSERT_EQ(serverDevice1, GetWireServer()->GetDevice(reservation1.id, reservation1.generation));
178 ASSERT_EQ(serverDevice2, GetWireServer()->GetDevice(reservation2.id, reservation2.generation));
179
180 // Release the first device
181 wgpuDeviceRelease(reservation1.device);
182 EXPECT_CALL(api, DeviceRelease(serverDevice1));
183 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice1, nullptr, nullptr)).Times(1);
184 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice1, nullptr, nullptr)).Times(1);
185 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice1, nullptr, nullptr)).Times(1);
186 FlushClient();
187
188 // The first device should no longer reflect, but the second should
189 ASSERT_EQ(nullptr, GetWireServer()->GetDevice(reservation1.id, reservation1.generation));
190 ASSERT_EQ(serverDevice2, GetWireServer()->GetDevice(reservation2.id, reservation2.generation));
191
192 // Called on shutdown.
193 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice2, nullptr, nullptr)).Times(1);
194 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice2, nullptr, nullptr)).Times(1);
195 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice2, nullptr, nullptr)).Times(1);
196 }
197
198 // This is a regression test where a second device reservation invalidated pointers into the
199 // KnownObjects std::vector of devices. The fix was to store pointers to heap allocated
200 // objects instead.
TEST_F(WireInjectDeviceTests,TrackChildObjectsWithTwoReservedDevices)201 TEST_F(WireInjectDeviceTests, TrackChildObjectsWithTwoReservedDevices) {
202 // Reserve one device, inject it, and get the primary queue.
203 ReservedDevice reservation1 = GetWireClient()->ReserveDevice();
204
205 WGPUDevice serverDevice1 = api.GetNewDevice();
206 EXPECT_CALL(api, DeviceReference(serverDevice1));
207 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice1, _, _));
208 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice1, _, _));
209 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice1, _, _));
210 ASSERT_TRUE(
211 GetWireServer()->InjectDevice(serverDevice1, reservation1.id, reservation1.generation));
212
213 WGPUCommandEncoder commandEncoder =
214 wgpuDeviceCreateCommandEncoder(reservation1.device, nullptr);
215
216 WGPUCommandEncoder serverCommandEncoder = api.GetNewCommandEncoder();
217 EXPECT_CALL(api, DeviceCreateCommandEncoder(serverDevice1, _))
218 .WillOnce(Return(serverCommandEncoder));
219 FlushClient();
220
221 // Reserve a second device, and inject it.
222 ReservedDevice reservation2 = GetWireClient()->ReserveDevice();
223
224 WGPUDevice serverDevice2 = api.GetNewDevice();
225 EXPECT_CALL(api, DeviceReference(serverDevice2));
226 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice2, _, _));
227 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice2, _, _));
228 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice2, _, _));
229 ASSERT_TRUE(
230 GetWireServer()->InjectDevice(serverDevice2, reservation2.id, reservation2.generation));
231
232 // Release the encoder. This should work without error because it stores a stable
233 // pointer to its device's list of child objects. On destruction, it removes itself from the
234 // list.
235 wgpuCommandEncoderRelease(commandEncoder);
236 EXPECT_CALL(api, CommandEncoderRelease(serverCommandEncoder));
237 FlushClient();
238
239 // Called on shutdown.
240 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice1, nullptr, nullptr)).Times(1);
241 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice1, nullptr, nullptr)).Times(1);
242 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice1, nullptr, nullptr)).Times(1);
243 EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(serverDevice2, nullptr, nullptr)).Times(1);
244 EXPECT_CALL(api, OnDeviceSetLoggingCallback(serverDevice2, nullptr, nullptr)).Times(1);
245 EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(serverDevice2, nullptr, nullptr)).Times(1);
246 }
247
248 // Test that a device reservation can be reclaimed. This is necessary to
249 // avoid leaking ObjectIDs for reservations that are never injected.
TEST_F(WireInjectDeviceTests,ReclaimDeviceReservation)250 TEST_F(WireInjectDeviceTests, ReclaimDeviceReservation) {
251 // Test that doing a reservation and full release is an error.
252 {
253 ReservedDevice reservation = GetWireClient()->ReserveDevice();
254 wgpuDeviceRelease(reservation.device);
255 FlushClient(false);
256 }
257
258 // Test that doing a reservation and then reclaiming it recycles the ID.
259 {
260 ReservedDevice reservation1 = GetWireClient()->ReserveDevice();
261 GetWireClient()->ReclaimDeviceReservation(reservation1);
262
263 ReservedDevice reservation2 = GetWireClient()->ReserveDevice();
264
265 // The ID is the same, but the generation is still different.
266 ASSERT_EQ(reservation1.id, reservation2.id);
267 ASSERT_NE(reservation1.generation, reservation2.generation);
268
269 // No errors should occur.
270 FlushClient();
271 }
272 }
273