• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "dawn_wire/client/Device.h"
16 
17 #include "common/Assert.h"
18 #include "common/Log.h"
19 #include "dawn_wire/client/ApiObjects_autogen.h"
20 #include "dawn_wire/client/Client.h"
21 #include "dawn_wire/client/ObjectAllocator.h"
22 
23 namespace dawn_wire { namespace client {
24 
Device(Client * clientIn,uint32_t initialRefcount,uint32_t initialId)25     Device::Device(Client* clientIn, uint32_t initialRefcount, uint32_t initialId)
26         : ObjectBase(clientIn, initialRefcount, initialId), mIsAlive(std::make_shared<bool>()) {
27 #if defined(DAWN_ENABLE_ASSERTS)
28         mErrorCallback = [](WGPUErrorType, char const*, void*) {
29             static bool calledOnce = false;
30             if (!calledOnce) {
31                 calledOnce = true;
32                 dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is "
33                                       "probably not intended. If you really want to ignore errors "
34                                       "and suppress this message, set the callback to null.";
35             }
36         };
37 
38         mDeviceLostCallback = [](WGPUDeviceLostReason, char const*, void*) {
39             static bool calledOnce = false;
40             if (!calledOnce) {
41                 calledOnce = true;
42                 dawn::WarningLog() << "No Dawn device lost callback was set. This is probably not "
43                                       "intended. If you really want to ignore device lost "
44                                       "and suppress this message, set the callback to null.";
45             }
46         };
47 #endif  // DAWN_ENABLE_ASSERTS
48     }
49 
~Device()50     Device::~Device() {
51         mErrorScopes.CloseAll([](ErrorScopeData* request) {
52             request->callback(WGPUErrorType_Unknown, "Device destroyed before callback",
53                               request->userdata);
54         });
55 
56         mCreatePipelineAsyncRequests.CloseAll([](CreatePipelineAsyncRequest* request) {
57             if (request->createComputePipelineAsyncCallback != nullptr) {
58                 request->createComputePipelineAsyncCallback(
59                     WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
60                     "Device destroyed before callback", request->userdata);
61             } else {
62                 ASSERT(request->createRenderPipelineAsyncCallback != nullptr);
63                 request->createRenderPipelineAsyncCallback(
64                     WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
65                     "Device destroyed before callback", request->userdata);
66             }
67         });
68     }
69 
HandleError(WGPUErrorType errorType,const char * message)70     void Device::HandleError(WGPUErrorType errorType, const char* message) {
71         if (mErrorCallback) {
72             mErrorCallback(errorType, message, mErrorUserdata);
73         }
74     }
75 
HandleLogging(WGPULoggingType loggingType,const char * message)76     void Device::HandleLogging(WGPULoggingType loggingType, const char* message) {
77         if (mLoggingCallback) {
78             // Since client always run in single thread, calling the callback directly is safe.
79             mLoggingCallback(loggingType, message, mLoggingUserdata);
80         }
81     }
82 
HandleDeviceLost(WGPUDeviceLostReason reason,const char * message)83     void Device::HandleDeviceLost(WGPUDeviceLostReason reason, const char* message) {
84         if (mDeviceLostCallback && !mDidRunLostCallback) {
85             mDidRunLostCallback = true;
86             mDeviceLostCallback(reason, message, mDeviceLostUserdata);
87         }
88     }
89 
CancelCallbacksForDisconnect()90     void Device::CancelCallbacksForDisconnect() {
91         mErrorScopes.CloseAll([](ErrorScopeData* request) {
92             request->callback(WGPUErrorType_DeviceLost, "Device lost", request->userdata);
93         });
94 
95         mCreatePipelineAsyncRequests.CloseAll([](CreatePipelineAsyncRequest* request) {
96             if (request->createComputePipelineAsyncCallback != nullptr) {
97                 request->createComputePipelineAsyncCallback(
98                     WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr, "Device lost",
99                     request->userdata);
100             } else {
101                 ASSERT(request->createRenderPipelineAsyncCallback != nullptr);
102                 request->createRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceLost,
103                                                            nullptr, "Device lost",
104                                                            request->userdata);
105             }
106         });
107     }
108 
GetAliveWeakPtr()109     std::weak_ptr<bool> Device::GetAliveWeakPtr() {
110         return mIsAlive;
111     }
112 
SetUncapturedErrorCallback(WGPUErrorCallback errorCallback,void * errorUserdata)113     void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) {
114         mErrorCallback = errorCallback;
115         mErrorUserdata = errorUserdata;
116     }
117 
SetLoggingCallback(WGPULoggingCallback callback,void * userdata)118     void Device::SetLoggingCallback(WGPULoggingCallback callback, void* userdata) {
119         mLoggingCallback = callback;
120         mLoggingUserdata = userdata;
121     }
122 
SetDeviceLostCallback(WGPUDeviceLostCallback callback,void * userdata)123     void Device::SetDeviceLostCallback(WGPUDeviceLostCallback callback, void* userdata) {
124         mDeviceLostCallback = callback;
125         mDeviceLostUserdata = userdata;
126     }
127 
PushErrorScope(WGPUErrorFilter filter)128     void Device::PushErrorScope(WGPUErrorFilter filter) {
129         mErrorScopeStackSize++;
130 
131         DevicePushErrorScopeCmd cmd;
132         cmd.self = ToAPI(this);
133         cmd.filter = filter;
134 
135         client->SerializeCommand(cmd);
136     }
137 
PopErrorScope(WGPUErrorCallback callback,void * userdata)138     bool Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) {
139         if (mErrorScopeStackSize == 0) {
140             return false;
141         }
142         mErrorScopeStackSize--;
143 
144         if (client->IsDisconnected()) {
145             callback(WGPUErrorType_DeviceLost, "GPU device disconnected", userdata);
146             return true;
147         }
148 
149         uint64_t serial = mErrorScopes.Add({callback, userdata});
150 
151         DevicePopErrorScopeCmd cmd;
152         cmd.deviceId = this->id;
153         cmd.requestSerial = serial;
154 
155         client->SerializeCommand(cmd);
156 
157         return true;
158     }
159 
OnPopErrorScopeCallback(uint64_t requestSerial,WGPUErrorType type,const char * message)160     bool Device::OnPopErrorScopeCallback(uint64_t requestSerial,
161                                          WGPUErrorType type,
162                                          const char* message) {
163         switch (type) {
164             case WGPUErrorType_NoError:
165             case WGPUErrorType_Validation:
166             case WGPUErrorType_OutOfMemory:
167             case WGPUErrorType_Unknown:
168             case WGPUErrorType_DeviceLost:
169                 break;
170             default:
171                 return false;
172         }
173 
174         ErrorScopeData request;
175         if (!mErrorScopes.Acquire(requestSerial, &request)) {
176             return false;
177         }
178 
179         request.callback(type, message, request.userdata);
180         return true;
181     }
182 
InjectError(WGPUErrorType type,const char * message)183     void Device::InjectError(WGPUErrorType type, const char* message) {
184         DeviceInjectErrorCmd cmd;
185         cmd.self = ToAPI(this);
186         cmd.type = type;
187         cmd.message = message;
188         client->SerializeCommand(cmd);
189     }
190 
CreateBuffer(const WGPUBufferDescriptor * descriptor)191     WGPUBuffer Device::CreateBuffer(const WGPUBufferDescriptor* descriptor) {
192         return Buffer::Create(this, descriptor);
193     }
194 
CreateErrorBuffer()195     WGPUBuffer Device::CreateErrorBuffer() {
196         return Buffer::CreateError(this);
197     }
198 
GetLimits(WGPUSupportedLimits * limits)199     bool Device::GetLimits(WGPUSupportedLimits* limits) {
200         // Not implemented in the wire.
201         UNREACHABLE();
202         return false;
203     }
204 
GetQueue()205     WGPUQueue Device::GetQueue() {
206         // The queue is lazily created because if a Device is created by
207         // Reserve/Inject, we cannot send the GetQueue message until
208         // it has been injected on the Server. It cannot happen immediately
209         // on construction.
210         if (mQueue == nullptr) {
211             // Get the primary queue for this device.
212             auto* allocation = client->QueueAllocator().New(client);
213             mQueue = allocation->object.get();
214 
215             DeviceGetQueueCmd cmd;
216             cmd.self = ToAPI(this);
217             cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
218 
219             client->SerializeCommand(cmd);
220         }
221 
222         mQueue->refcount++;
223         return ToAPI(mQueue);
224     }
225 
CreateComputePipelineAsync(WGPUComputePipelineDescriptor const * descriptor,WGPUCreateComputePipelineAsyncCallback callback,void * userdata)226     void Device::CreateComputePipelineAsync(WGPUComputePipelineDescriptor const* descriptor,
227                                             WGPUCreateComputePipelineAsyncCallback callback,
228                                             void* userdata) {
229         if (client->IsDisconnected()) {
230             return callback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
231                             "GPU device disconnected", userdata);
232         }
233 
234         auto* allocation = client->ComputePipelineAllocator().New(client);
235 
236         CreatePipelineAsyncRequest request = {};
237         request.createComputePipelineAsyncCallback = callback;
238         request.userdata = userdata;
239         request.pipelineObjectID = allocation->object->id;
240 
241         uint64_t serial = mCreatePipelineAsyncRequests.Add(std::move(request));
242 
243         DeviceCreateComputePipelineAsyncCmd cmd;
244         cmd.deviceId = this->id;
245         cmd.descriptor = descriptor;
246         cmd.requestSerial = serial;
247         cmd.pipelineObjectHandle = ObjectHandle{allocation->object->id, allocation->generation};
248 
249         client->SerializeCommand(cmd);
250     }
251 
OnCreateComputePipelineAsyncCallback(uint64_t requestSerial,WGPUCreatePipelineAsyncStatus status,const char * message)252     bool Device::OnCreateComputePipelineAsyncCallback(uint64_t requestSerial,
253                                                       WGPUCreatePipelineAsyncStatus status,
254                                                       const char* message) {
255         CreatePipelineAsyncRequest request;
256         if (!mCreatePipelineAsyncRequests.Acquire(requestSerial, &request)) {
257             return false;
258         }
259 
260         auto pipelineAllocation =
261             client->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
262 
263         // If the return status is a failure we should give a null pipeline to the callback and
264         // free the allocation both on the client side and the server side.
265         if (status != WGPUCreatePipelineAsyncStatus_Success) {
266             client->ComputePipelineAllocator().Free(pipelineAllocation);
267             request.createComputePipelineAsyncCallback(status, nullptr, message, request.userdata);
268             return true;
269         }
270 
271         WGPUComputePipeline pipeline = reinterpret_cast<WGPUComputePipeline>(pipelineAllocation);
272         request.createComputePipelineAsyncCallback(status, pipeline, message, request.userdata);
273 
274         return true;
275     }
276 
CreateRenderPipelineAsync(WGPURenderPipelineDescriptor const * descriptor,WGPUCreateRenderPipelineAsyncCallback callback,void * userdata)277     void Device::CreateRenderPipelineAsync(WGPURenderPipelineDescriptor const* descriptor,
278                                            WGPUCreateRenderPipelineAsyncCallback callback,
279                                            void* userdata) {
280         if (client->IsDisconnected()) {
281             return callback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
282                             "GPU device disconnected", userdata);
283         }
284 
285         auto* allocation = client->RenderPipelineAllocator().New(client);
286 
287         CreatePipelineAsyncRequest request = {};
288         request.createRenderPipelineAsyncCallback = callback;
289         request.userdata = userdata;
290         request.pipelineObjectID = allocation->object->id;
291 
292         uint64_t serial = mCreatePipelineAsyncRequests.Add(std::move(request));
293 
294         DeviceCreateRenderPipelineAsyncCmd cmd;
295         cmd.deviceId = this->id;
296         cmd.descriptor = descriptor;
297         cmd.requestSerial = serial;
298         cmd.pipelineObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
299 
300         client->SerializeCommand(cmd);
301     }
302 
OnCreateRenderPipelineAsyncCallback(uint64_t requestSerial,WGPUCreatePipelineAsyncStatus status,const char * message)303     bool Device::OnCreateRenderPipelineAsyncCallback(uint64_t requestSerial,
304                                                      WGPUCreatePipelineAsyncStatus status,
305                                                      const char* message) {
306         CreatePipelineAsyncRequest request;
307         if (!mCreatePipelineAsyncRequests.Acquire(requestSerial, &request)) {
308             return false;
309         }
310 
311         auto pipelineAllocation =
312             client->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
313 
314         // If the return status is a failure we should give a null pipeline to the callback and
315         // free the allocation both on the client side and the server side.
316         if (status != WGPUCreatePipelineAsyncStatus_Success) {
317             client->RenderPipelineAllocator().Free(pipelineAllocation);
318             request.createRenderPipelineAsyncCallback(status, nullptr, message, request.userdata);
319             return true;
320         }
321 
322         WGPURenderPipeline pipeline = reinterpret_cast<WGPURenderPipeline>(pipelineAllocation);
323         request.createRenderPipelineAsyncCallback(status, pipeline, message, request.userdata);
324 
325         return true;
326     }
327 
328 }}  // namespace dawn_wire::client
329