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