• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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