Reland "[wgpu-headers] Align device lost callbacks with webgpu.h and futures." Relanding after the following fixes to users: - Chrome: https://chromium-review.googlesource.com/c/chromium/src/+/5441439 - Skia/Graphite: https://skia-review.googlesource.com/c/skia/+/838480 - MLDrift: https://critique.corp.google.com/cl/623262112 Original change's description: > [wgpu-headers] Align device lost callbacks with webgpu.h and futures. > > - Overrides wire::ObjectBase::Release logic for Device. This was > necessary because the DeviceLostEvent holds a ref to the Device. > - Introduces non-progressing SystemEvents. This is needed because we > want to make sure that polling doesn't happen as long as a device > lost event exists. > > Bug: dawn:2021 > Change-Id: I120ca3c1e4b2bfd00b43bb9f26d91cb5c3f2e4d6 > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/165527 > Reviewed-by: Corentin Wallez <cwallez@chromium.org> > Reviewed-by: Austin Eng <enga@chromium.org> > Reviewed-by: Kai Ninomiya <kainino@chromium.org> > Commit-Queue: Loko Kung <lokokung@google.com> Bug: dawn:2450, dawn:2021 Change-Id: I72d6105d719942d1b9745d476e0d07498e659a51 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/183162 Commit-Queue: Loko Kung <lokokung@google.com> Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/generator/templates/dawn/wire/server/ServerDoers.cpp b/generator/templates/dawn/wire/server/ServerDoers.cpp index 3bc147a..4431f14 100644 --- a/generator/templates/dawn/wire/server/ServerDoers.cpp +++ b/generator/templates/dawn/wire/server/ServerDoers.cpp
@@ -85,16 +85,17 @@ WIRE_TRY({{type.name.CamelCase()}}Objects().Get(objectId, &obj)); if (obj->state == AllocationState::Allocated) { - DAWN_ASSERT(obj->handle != nullptr); {% if type.name.get() == "device" %} if (obj->handle != nullptr) { //* Deregisters uncaptured error and device lost callbacks since //* they should not be forwarded if the device no longer exists on the wire. ClearDeviceCallbacks(obj->handle); + mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle); } + {% else %} + DAWN_ASSERT(obj->handle != nullptr); + mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle); {% endif %} - - mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle); } {{type.name.CamelCase()}}Objects().Free(objectId); return WireResult::Success;
diff --git a/generator/templates/mock_api.cpp b/generator/templates/mock_api.cpp index ec792af..c7eeb8c 100644 --- a/generator/templates/mock_api.cpp +++ b/generator/templates/mock_api.cpp
@@ -76,13 +76,15 @@ //* Generate the older Call*Callback if there is no Future call equivalent. //* Includes: -//* - setDeviceLostCallback //* - setUncapturedErrorCallback //* - setLoggingCallback -{% set LegacyCallbackFunctions = ['set device lost callback', 'set uncaptured error callback', 'set logging callback'] %} +{% set LegacyCallbackFunctions = ['set uncaptured error callback', 'set logging callback'] %} + +//* Manually implemented mock functions due to incompatibility. +{% set ManuallyMockedFunctions = ['set device lost callback'] %} {% for type in by_category["object"] %} - {% for method in type.methods %} + {% for method in type.methods if method.name.get() not in ManuallyMockedFunctions %} {% set Suffix = as_CppMethodSuffix(type.name, method.name) %} {% if has_callback_arguments(method) %} {{as_cType(method.return_type.name)}} ProcTableAsClass::{{Suffix}}( @@ -186,6 +188,28 @@ {% endfor %} {% endfor %} +// Manually implement device lost related callback helpers for testing. +void ProcTableAsClass::DeviceSetDeviceLostCallback(WGPUDevice device, + WGPUDeviceLostCallback callback, + void* userdata) { + ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device); + object->mDeviceLostOldCallback = callback; + object->mDeviceLostUserdata = userdata; + + OnDeviceSetDeviceLostCallback(device, callback, userdata); +} +void ProcTableAsClass::CallDeviceSetDeviceLostCallbackCallback(WGPUDevice device, + WGPUDeviceLostReason reason, + char const* message) { + ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device); + // If we have an old callback set, call that one, otherwise call the new one. + if (object->mDeviceLostOldCallback != nullptr) { + object->mDeviceLostOldCallback(reason, message, object->mDeviceLostUserdata); + } else { + object->mDeviceLostCallback(&device, reason, message, object->mDeviceLostUserdata); + } +} + {% for type in by_category["object"] %} {{as_cType(type.name)}} ProcTableAsClass::GetNew{{type.name.CamelCase()}}() { mObjects.emplace_back(new Object);
diff --git a/generator/templates/mock_api.h b/generator/templates/mock_api.h index e30a33c..77769b0 100644 --- a/generator/templates/mock_api.h +++ b/generator/templates/mock_api.h
@@ -63,13 +63,16 @@ //* - setDeviceLostCallback //* - setUncapturedErrorCallback //* - setLoggingCallback - {%- set LegacyCallbackFunctions = ['set device lost callback', 'set uncaptured error callback', 'set logging callback'] %} + {%- set LegacyCallbackFunctions = ['set uncaptured error callback', 'set logging callback'] %} + + //* Manually implemented mock functions due to incompatibility. + {% set ManuallyMockedFunctions = ['set device lost callback'] %} {%- for type in by_category["object"] %} virtual void {{as_MethodSuffix(type.name, Name("reference"))}}({{as_cType(type.name)}} self) = 0; virtual void {{as_MethodSuffix(type.name, Name("release"))}}({{as_cType(type.name)}} self) = 0; - {% for method in type.methods %} + {% for method in type.methods if method.name.get() not in ManuallyMockedFunctions %} {% set Suffix = as_CppMethodSuffix(type.name, method.name) %} {% if not has_callback_arguments(method) and not has_callback_info(method) %} virtual {{as_cType(method.return_type.name)}} {{Suffix}}( @@ -122,6 +125,17 @@ {% endfor %} {% endfor %} + // Manually implement device lost related callback helpers for testing. + void DeviceSetDeviceLostCallback(WGPUDevice device, + WGPUDeviceLostCallback callback, + void* userdata); + virtual void OnDeviceSetDeviceLostCallback(WGPUDevice device, + WGPUDeviceLostCallback callback, + void* userdata) = 0; + void CallDeviceSetDeviceLostCallbackCallback(WGPUDevice device, + WGPUDeviceLostReason reason, + char const* message); + struct Object { ProcTableAsClass* procs = nullptr; {% for type in by_category["object"] %} @@ -138,6 +152,10 @@ {% endfor %} {% endfor %} {% endfor %} + // Manually implement device lost related callback helpers for testing. + WGPUDeviceLostCallback mDeviceLostOldCallback = nullptr; + WGPUDeviceLostCallbackNew mDeviceLostCallback = nullptr; + void* mDeviceLostUserdata = 0; }; private: @@ -178,6 +196,12 @@ ), (override)); {% endfor %} {% endfor %} + + // Manually implement device lost related callback helpers for testing. + MOCK_METHOD(void, + OnDeviceSetDeviceLostCallback, + (WGPUDevice device, WGPUDeviceLostCallback callback, void* userdata), + (override)); }; #endif // MOCK_{{API}}_H
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json index 5a634bf..a50d79c 100644 --- a/src/dawn/dawn.json +++ b/src/dawn/dawn.json
@@ -208,8 +208,10 @@ {"name": "required features", "type": "feature name", "annotation": "const*", "length": "required feature count", "default": "nullptr"}, {"name": "required limits", "type": "required limits", "annotation": "const*", "optional": true}, {"name": "default queue", "type": "queue descriptor"}, - {"name": "device lost callback", "type": "device lost callback", "default": "nullptr"}, - {"name": "device lost userdata", "type": "void *", "default": "nullptr"} + {"name": "device lost callback", "type": "device lost callback", "default": "nullptr", "tags": ["deprecated"]}, + {"name": "device lost userdata", "type": "void *", "default": "nullptr", "tags": ["deprecated"]}, + {"name": "device lost callback info", "type": "device lost callback info"}, + {"name": "uncaptured error callback info", "type": "uncaptured error callback info"} ] }, "dawn toggles descriptor": { @@ -1509,12 +1511,32 @@ {"name": "userdata", "type": "void *"} ] }, + "device lost callback new": { + "category": "function pointer", + "args": [ + {"name": "device", "type": "device", "annotation": "const*", "length": 1}, + {"name": "reason", "type": "device lost reason"}, + {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}, + {"name": "userdata", "type": "void *"} + ] + }, + "device lost callback info": { + "category": "structure", + "extensible": "in", + "members": [ + {"name": "mode", "type": "callback mode", "default": "wait any only", "_comment": "TODO(crbug.com/dawn/2458) Default should be removed."}, + {"name": "callback", "type": "device lost callback new", "default": "nullptr"}, + {"name": "userdata", "type": "void *", "default": "nullptr"} + ] + }, "device lost reason": { "category": "enum", "emscripten_no_enum_table": true, "values": [ {"value": 0, "name": "undefined", "jsrepr": "undefined"}, - {"value": 1, "name": "destroyed"} + {"value": 1, "name": "destroyed"}, + {"value": 2, "name": "instance dropped"}, + {"value": 3, "name": "failed creation"} ] }, "double": { @@ -1528,6 +1550,14 @@ {"name": "userdata", "type": "void *"} ] }, + "uncaptured error callback info": { + "category": "structure", + "extensible": "in", + "members": [ + {"name": "callback", "type": "error callback", "default": "nullptr"}, + {"name": "userdata", "type": "void *", "default": "nullptr"} + ] + }, "pop error scope status": { "category": "enum", "emscripten_no_enum_table": true, @@ -2373,7 +2403,7 @@ "callback mode": { "category": "enum", "emscripten_no_enum_table": true, - "_comment": "TODO(crbug.com/dawn/2224): Should this be renumbered to reserve 0?", + "_comment": "TODO(crbug.com/dawn/2458): Should this be renumbered to reserve 0?", "values": [ {"value": 0, "name": "wait any only"}, {"value": 1, "name": "allow process events"},
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json index 4b230a3..f87a712 100644 --- a/src/dawn/dawn_wire.json +++ b/src/dawn/dawn_wire.json
@@ -115,6 +115,7 @@ { "name": "event manager handle", "type": "ObjectHandle" }, { "name": "future", "type": "future" }, { "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"}, + { "name": "device lost future", "type": "future" }, { "name": "descriptor", "type": "device descriptor", "annotation": "const*" } ] }, @@ -149,7 +150,8 @@ { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } ], "device lost callback" : [ - { "name": "device", "type": "ObjectHandle", "handle_type": "device" }, + { "name": "event manager", "type": "ObjectHandle" }, + { "name": "future", "type": "future" }, { "name": "reason", "type": "device lost reason" }, { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } ],
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp index 7d19538..6441adc 100644 --- a/src/dawn/native/Adapter.cpp +++ b/src/dawn/native/Adapter.cpp
@@ -29,10 +29,12 @@ #include <algorithm> #include <memory> +#include <string> #include <tuple> #include <utility> #include <vector> +#include "dawn/common/Log.h" #include "dawn/native/ChainUtils.h" #include "dawn/native/Device.h" #include "dawn/native/Instance.h" @@ -40,6 +42,9 @@ #include "partition_alloc/pointers/raw_ptr.h" namespace dawn::native { +namespace { +static constexpr DeviceDescriptor kDefaultDeviceDesc = {}; +} // anonymous namespace AdapterBase::AdapterBase(Ref<PhysicalDeviceBase> physicalDevice, FeatureLevel featureLevel, @@ -191,20 +196,24 @@ return mSupportedFeatures.EnumerateFeatures(features); } +// TODO(https://crbug.com/dawn/2465) Could potentially re-implement via AllowSpontaneous async mode. DeviceBase* AdapterBase::APICreateDevice(const DeviceDescriptor* descriptor) { - constexpr DeviceDescriptor kDefaultDesc = {}; if (descriptor == nullptr) { - descriptor = &kDefaultDesc; + descriptor = &kDefaultDeviceDesc; } + auto [lostEvent, result] = CreateDevice(descriptor); + mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent(lostEvent); Ref<DeviceBase> device; - if (mPhysicalDevice->GetInstance()->ConsumedError(CreateDevice(descriptor), &device)) { + if (mPhysicalDevice->GetInstance()->ConsumedError(std::move(result), &device)) { return nullptr; } return ReturnToAPI(std::move(device)); } -ResultOrError<Ref<DeviceBase>> AdapterBase::CreateDevice(const DeviceDescriptor* rawDescriptor) { +ResultOrError<Ref<DeviceBase>> AdapterBase::CreateDeviceInternal( + const DeviceDescriptor* rawDescriptor, + Ref<DeviceBase::DeviceLostEvent> lostEvent) { DAWN_ASSERT(rawDescriptor != nullptr); // Create device toggles state from required toggles descriptor and inherited adapter toggles @@ -255,29 +264,30 @@ "validating required limits"); } - return mPhysicalDevice->CreateDevice(this, descriptor, deviceToggles); + return mPhysicalDevice->CreateDevice(this, descriptor, deviceToggles, std::move(lostEvent)); +} + +std::pair<Ref<DeviceBase::DeviceLostEvent>, ResultOrError<Ref<DeviceBase>>> +AdapterBase::CreateDevice(const DeviceDescriptor* descriptor) { + DAWN_ASSERT(descriptor != nullptr); + + Ref<DeviceBase::DeviceLostEvent> lostEvent = DeviceBase::DeviceLostEvent::Create(descriptor); + + auto result = CreateDeviceInternal(descriptor, lostEvent); + if (result.IsError()) { + lostEvent->mReason = wgpu::DeviceLostReason::FailedCreation; + lostEvent->mMessage = "Failed to create device."; + mPhysicalDevice->GetInstance()->GetEventManager()->SetFutureReady(lostEvent.Get()); + } + return {lostEvent, std::move(result)}; } void AdapterBase::APIRequestDevice(const DeviceDescriptor* descriptor, WGPURequestDeviceCallback callback, void* userdata) { - constexpr DeviceDescriptor kDefaultDescriptor = {}; - if (descriptor == nullptr) { - descriptor = &kDefaultDescriptor; - } - auto result = CreateDevice(descriptor); - if (result.IsError()) { - std::unique_ptr<ErrorData> errorData = result.AcquireError(); - // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents - callback(WGPURequestDeviceStatus_Error, nullptr, errorData->GetFormattedMessage().c_str(), - userdata); - return; - } - Ref<DeviceBase> device = result.AcquireSuccess(); - WGPURequestDeviceStatus status = - device == nullptr ? WGPURequestDeviceStatus_Unknown : WGPURequestDeviceStatus_Success; - // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents - callback(status, ToAPI(ReturnToAPI(std::move(device))), nullptr, userdata); + // Default legacy callback mode for RequestDevice is spontaneous. + APIRequestDeviceF(descriptor, + {nullptr, wgpu::CallbackMode::AllowSpontaneous, callback, userdata}); } Future AdapterBase::APIRequestDeviceF(const DeviceDescriptor* descriptor, @@ -285,47 +295,53 @@ struct RequestDeviceEvent final : public EventManager::TrackedEvent { WGPURequestDeviceCallback mCallback; raw_ptr<void> mUserdata; - ResultOrError<Ref<DeviceBase>> mDeviceOrError; - RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo, - ResultOrError<Ref<DeviceBase>> deviceOrError) + WGPURequestDeviceStatus mStatus; + Ref<DeviceBase> mDevice = nullptr; + std::string mMessage; + + RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo, Ref<DeviceBase> device) : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}), mCallback(callbackInfo.callback), mUserdata(callbackInfo.userdata), - mDeviceOrError(std::move(deviceOrError)) {} + mStatus(WGPURequestDeviceStatus_Success), + mDevice(std::move(device)) {} + + RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo, + const std::string& message) + : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}), + mCallback(callbackInfo.callback), + mUserdata(callbackInfo.userdata), + mStatus(WGPURequestDeviceStatus_Error), + mMessage(message) {} ~RequestDeviceEvent() override { EnsureComplete(EventCompletionType::Shutdown); } void Complete(EventCompletionType completionType) override { - WGPURequestDeviceStatus status; - Ref<DeviceBase> device; - if (completionType == EventCompletionType::Shutdown) { - status = WGPURequestDeviceStatus_InstanceDropped; - } else { - if (mDeviceOrError.IsError()) { - std::unique_ptr<ErrorData> errorData = mDeviceOrError.AcquireError(); - mCallback(WGPURequestDeviceStatus_Error, nullptr, - errorData->GetFormattedMessage().c_str(), - mUserdata.ExtractAsDangling()); - return; - } - device = mDeviceOrError.AcquireSuccess(); - status = device == nullptr ? WGPURequestDeviceStatus_Unknown - : WGPURequestDeviceStatus_Success; + mStatus = WGPURequestDeviceStatus_InstanceDropped; + mDevice = nullptr; + mMessage = "A valid external Instance reference no longer exists."; } - mCallback(status, ToAPI(ReturnToAPI(std::move(device))), nullptr, - mUserdata.ExtractAsDangling()); + mCallback(mStatus, ToAPI(ReturnToAPI(std::move(mDevice))), + mMessage.empty() ? nullptr : mMessage.c_str(), mUserdata.ExtractAsDangling()); } }; - constexpr DeviceDescriptor kDefaultDescriptor = {}; if (descriptor == nullptr) { - descriptor = &kDefaultDescriptor; + descriptor = &kDefaultDeviceDesc; } - FutureID futureID = mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent( - AcquireRef(new RequestDeviceEvent(callbackInfo, CreateDevice(descriptor)))); + FutureID futureID = kNullFutureID; + auto [lostEvent, result] = CreateDevice(descriptor); + if (result.IsSuccess()) { + futureID = mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent( + AcquireRef(new RequestDeviceEvent(callbackInfo, result.AcquireSuccess()))); + } else { + futureID = mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent(AcquireRef( + new RequestDeviceEvent(callbackInfo, result.AcquireError()->GetFormattedMessage()))); + } + mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent(std::move(lostEvent)); return {futureID}; }
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h index 5b99069f..038de4b 100644 --- a/src/dawn/native/Adapter.h +++ b/src/dawn/native/Adapter.h
@@ -28,11 +28,13 @@ #ifndef SRC_DAWN_NATIVE_ADAPTER_H_ #define SRC_DAWN_NATIVE_ADAPTER_H_ +#include <utility> #include <vector> #include "dawn/common/Ref.h" #include "dawn/common/RefCounted.h" #include "dawn/native/DawnNative.h" +#include "dawn/native/Device.h" #include "dawn/native/PhysicalDevice.h" #include "dawn/native/dawn_platform.h" @@ -62,7 +64,6 @@ Future APIRequestDeviceF(const DeviceDescriptor* descriptor, const RequestDeviceCallbackInfo& callbackInfo); DeviceBase* APICreateDevice(const DeviceDescriptor* descriptor = nullptr); - ResultOrError<Ref<DeviceBase>> CreateDevice(const DeviceDescriptor* rawDescriptor); bool APIGetFormatCapabilities(wgpu::TextureFormat format, FormatCapabilities* capabilities); void SetUseTieredLimits(bool useTieredLimits); @@ -78,6 +79,11 @@ FeatureLevel GetFeatureLevel() const; private: + std::pair<Ref<DeviceBase::DeviceLostEvent>, ResultOrError<Ref<DeviceBase>>> CreateDevice( + const DeviceDescriptor* rawDescriptor); + ResultOrError<Ref<DeviceBase>> CreateDeviceInternal(const DeviceDescriptor* rawDescriptor, + Ref<DeviceBase::DeviceLostEvent> lostEvent); + Ref<PhysicalDeviceBase> mPhysicalDevice; FeatureLevel mFeatureLevel; bool mUseTieredLimits = false;
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp index b1084d4..3efce35 100644 --- a/src/dawn/native/Buffer.cpp +++ b/src/dawn/native/Buffer.cpp
@@ -235,7 +235,6 @@ (*buffer)->mState = BufferState::Mapped; pendingMapEvent = std::move((*buffer)->mPendingMapEvent); - (*buffer)->mPendingMapFutureID = kNullFutureID; } }); mCallback(ToAPI(status), mUserdata); @@ -636,9 +635,6 @@ } FutureID futureID = GetInstance()->GetEventManager()->TrackEvent(std::move(event)); - if (!earlyStatus) { - mPendingMapFutureID = futureID; - } return {futureID}; } @@ -713,10 +709,8 @@ // state and pending map event needs to be atomic w.r.t. MapAsyncEvent::Complete. Ref<MapAsyncEvent> pendingMapEvent = std::move(mPendingMapEvent); if (pendingMapEvent != nullptr) { - DAWN_ASSERT(mPendingMapFutureID != kNullFutureID); pendingMapEvent->UnmapEarly(static_cast<wgpu::BufferMapAsyncStatus>(callbackStatus)); - GetInstance()->GetEventManager()->SetFutureReady(mPendingMapFutureID); - mPendingMapFutureID = kNullFutureID; + GetInstance()->GetEventManager()->SetFutureReady(pendingMapEvent.Get()); } else { GetDevice()->GetCallbackTaskManager()->AddCallbackTask( PrepareMappingCallback(mLastMapID, callbackStatus));
diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h index 401ff66..6204fff 100644 --- a/src/dawn/native/Buffer.h +++ b/src/dawn/native/Buffer.h
@@ -186,7 +186,6 @@ size_t mMapSize = 0; struct MapAsyncEvent; - FutureID mPendingMapFutureID = kNullFutureID; Ref<MapAsyncEvent> mPendingMapEvent; };
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp index c014fa0..46957d0 100644 --- a/src/dawn/native/Device.cpp +++ b/src/dawn/native/Device.cpp
@@ -130,6 +130,7 @@ }; namespace { + struct LoggingCallbackTask : CallbackTask { public: LoggingCallbackTask() = delete; @@ -163,8 +164,95 @@ std::string mMessage; raw_ptr<void> mUserdata; }; + +static constexpr UncapturedErrorCallbackInfo kEmptyUncapturedErrorCallbackInfo = {nullptr, nullptr, + nullptr}; + } // anonymous namespace +DeviceBase::DeviceLostEvent::DeviceLostEvent(const DeviceLostCallbackInfo& callbackInfo) + : TrackedEvent(callbackInfo.mode, SystemEvent::CreateNonProgressingEvent()), + mCallback(callbackInfo.callback), + mUserdata(callbackInfo.userdata) {} + +DeviceBase::DeviceLostEvent::DeviceLostEvent(wgpu::DeviceLostCallback oldCallback, void* userdata) + : TrackedEvent(wgpu::CallbackMode::AllowProcessEvents, + SystemEvent::CreateNonProgressingEvent()), + mOldCallback(oldCallback), + mUserdata(userdata) {} + +DeviceBase::DeviceLostEvent::~DeviceLostEvent() { + EnsureComplete(EventCompletionType::Shutdown); +} + +// static +Ref<DeviceBase::DeviceLostEvent> DeviceBase::DeviceLostEvent::Create( + const DeviceDescriptor* descriptor) { + DAWN_ASSERT(descriptor != nullptr); + +#if defined(DAWN_ENABLE_ASSERTS) + // TODO(crbug.com/dawn/2465) Make default AllowSpontaneous once SetDeviceLostCallback is gone. + static constexpr DeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = { + nullptr, wgpu::CallbackMode::AllowProcessEvents, + [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*) { + static bool calledOnce = false; + if (!calledOnce) { + calledOnce = true; + dawn::WarningLog() << "No Dawn device lost callback was set. This is probably not " + "intended. If you really want to ignore device lost and " + "suppress this message, set the callback explicitly."; + } + }, + nullptr}; +#else + static constexpr DeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = { + nullptr, wgpu::CallbackMode::AllowProcessEvents, nullptr, nullptr}; +#endif // DAWN_ENABLE_ASSERTS + + Ref<DeviceBase::DeviceLostEvent> lostEvent; + if (descriptor->deviceLostCallback != nullptr) { + dawn::WarningLog() + << "DeviceDescriptor.deviceLostCallback and DeviceDescriptor.deviceLostUserdata are " + "deprecated. Use DeviceDescriptor.deviceLostCallbackInfo instead."; + lostEvent = AcquireRef(new DeviceBase::DeviceLostEvent(descriptor->deviceLostCallback, + descriptor->deviceLostUserdata)); + } else { + DeviceLostCallbackInfo deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo; + if (descriptor->deviceLostCallbackInfo.callback != nullptr || + descriptor->deviceLostCallbackInfo.mode != wgpu::CallbackMode::WaitAnyOnly) { + deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo; + if (deviceLostCallbackInfo.mode != wgpu::CallbackMode::AllowSpontaneous) { + // TODO(dawn:2458) Currently we default the callback mode to ProcessEvents if not + // passed for backwards compatibility. We should add warning logging for it though + // when available. + deviceLostCallbackInfo.mode = wgpu::CallbackMode::AllowProcessEvents; + } + } + lostEvent = AcquireRef(new DeviceBase::DeviceLostEvent(deviceLostCallbackInfo)); + } + + return lostEvent; +} + +void DeviceBase::DeviceLostEvent::Complete(EventCompletionType completionType) { + if (completionType == EventCompletionType::Shutdown) { + mReason = wgpu::DeviceLostReason::InstanceDropped; + mMessage = "A valid external Instance reference no longer exists."; + } + if (mReason == wgpu::DeviceLostReason::InstanceDropped || + mReason == wgpu::DeviceLostReason::FailedCreation) { + mDevice = nullptr; + } + + if (mOldCallback) { + mOldCallback(ToAPI(mReason), mMessage.c_str(), mUserdata.ExtractAsDangling()); + } else if (mCallback) { + auto device = ToAPI(mDevice.Get()); + mCallback(&device, ToAPI(mReason), mMessage.c_str(), mUserdata.ExtractAsDangling()); + } + mDevice = nullptr; +} + ResultOrError<Ref<PipelineLayoutBase>> ValidateLayoutAndGetComputePipelineDescriptorWithDefaults( DeviceBase* device, const ComputePipelineDescriptor& descriptor, @@ -210,12 +298,37 @@ DeviceBase::DeviceBase(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) - : mAdapter(adapter), mToggles(deviceToggles), mNextPipelineCompatibilityToken(1) { + const TogglesState& deviceToggles, + Ref<DeviceLostEvent>&& lostEvent) + : mLostEvent(std::move(lostEvent)), + mAdapter(adapter), + mToggles(deviceToggles), + mNextPipelineCompatibilityToken(1) { DAWN_ASSERT(descriptor); - mDeviceLostCallback = descriptor->deviceLostCallback; - mDeviceLostUserdata = descriptor->deviceLostUserdata; + mLostEvent->mDevice = this; + +#if defined(DAWN_ENABLE_ASSERTS) + static constexpr UncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = { + nullptr, + [](WGPUErrorType, char const*, void*) { + static bool calledOnce = false; + if (!calledOnce) { + calledOnce = true; + dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is " + "probably not intended. If you really want to ignore errors " + "and suppress this message, set the callback explicitly."; + } + }, + nullptr}; +#else + static constexpr UncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = + kEmptyUncapturedErrorCallbackInfo; +#endif // DAWN_ENABLE_ASSERTS + mUncapturedErrorCallbackInfo = kDefaultUncapturedErrorCallbackInfo; + if (descriptor->uncapturedErrorCallbackInfo.callback != nullptr) { + mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo; + } AdapterProperties adapterProperties; adapter->APIGetProperties(&adapterProperties); @@ -287,12 +400,18 @@ DeviceBase::DeviceBase() : mState(State::Alive), mToggles(ToggleStage::Device) { GetDefaultLimits(&mLimits.v1, FeatureLevel::Core); mFormatTable = BuildFormatTable(this); + + DeviceDescriptor desc = {}; + desc.deviceLostCallbackInfo = {nullptr, wgpu::CallbackMode::AllowSpontaneous}; + mLostEvent = DeviceLostEvent::Create(&desc); + mLostEvent->mDevice = this; } DeviceBase::~DeviceBase() { // We need to explicitly release the Queue before we complete the destructor so that the // Queue does not get destroyed after the Device. mQueue = nullptr; + mLostEvent = nullptr; } MaybeError DeviceBase::Initialize(Ref<QueueBase> defaultQueue) { @@ -300,30 +419,6 @@ SetWGSLExtensionAllowList(); -#if defined(DAWN_ENABLE_ASSERTS) - mUncapturedErrorCallback = [](WGPUErrorType, char const*, void*) { - static bool calledOnce = false; - if (!calledOnce) { - calledOnce = true; - dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is " - "probably not intended. If you really want to ignore errors " - "and suppress this message, set the callback to null."; - } - }; - - if (!mDeviceLostCallback) { - mDeviceLostCallback = [](WGPUDeviceLostReason, char const*, void*) { - static bool calledOnce = false; - if (!calledOnce) { - calledOnce = true; - dawn::WarningLog() << "No Dawn device lost callback was set. This is probably not " - "intended. If you really want to ignore device lost " - "and suppress this message, set the callback to null."; - } - }; - } -#endif // DAWN_ENABLE_ASSERTS - mCaches = std::make_unique<DeviceBase::Caches>(); mErrorScopeStack = std::make_unique<ErrorScopeStack>(); mDynamicUploader = std::make_unique<DynamicUploader>(this); @@ -411,17 +506,7 @@ // Reset callbacks since after dropping the last external reference, the application may have // freed any device-scope memory needed to run the callback. - mUncapturedErrorCallback = [](WGPUErrorType, char const* message, void*) { - dawn::WarningLog() << "Uncaptured error after last external device reference dropped.\n" - << message; - }; - mUncapturedErrorUserdata = nullptr; - - mDeviceLostCallback = [](WGPUDeviceLostReason, char const* message, void*) { - dawn::WarningLog() << "Device lost after last external device reference dropped.\n" - << message; - }; - mDeviceLostUserdata = nullptr; + mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo; // mAdapter is not set for mock test devices. // TODO(crbug.com/dawn/1702): using a mock adapter could avoid the null checking. @@ -495,11 +580,11 @@ // Skip handling device facilities if they haven't even been created (or failed doing so) if (mState != State::BeingCreated) { // The device is being destroyed so it will be lost, call the application callback. - if (mDeviceLostCallback != nullptr) { - mCallbackTaskManager->AddCallbackTask( - std::bind(mDeviceLostCallback, WGPUDeviceLostReason_Destroyed, - "Device was destroyed.", mDeviceLostUserdata)); - mDeviceLostCallback = nullptr; + if (mLostEvent != nullptr) { + mLostEvent->mReason = wgpu::DeviceLostReason::Destroyed; + mLostEvent->mMessage = "Device was destroyed."; + GetInstance()->GetEventManager()->SetFutureReady(mLostEvent.Get()); + mLostEvent = nullptr; } // Call all the callbacks immediately as the device is about to shut down. @@ -639,12 +724,11 @@ // The device was lost, schedule the application callback's execution. // Note: we don't invoke the callbacks directly here because it could cause re-entrances -> // possible deadlock. - if (mDeviceLostCallback != nullptr) { - mCallbackTaskManager->AddCallbackTask([callback = mDeviceLostCallback, lostReason, - messageStr, userdata = mDeviceLostUserdata] { - callback(lostReason, messageStr.c_str(), userdata); - }); - mDeviceLostCallback = nullptr; + if (mLostEvent != nullptr) { + mLostEvent->mReason = FromAPI(lostReason); + mLostEvent->mMessage = messageStr; + GetInstance()->GetEventManager()->SetFutureReady(mLostEvent.Get()); + mLostEvent = nullptr; } mQueue->HandleDeviceLoss(); @@ -663,9 +747,10 @@ if (!captured) { // Only call the uncaptured error callback if the device is alive. After the // device is lost, the uncaptured error callback should cease firing. - if (mUncapturedErrorCallback != nullptr && mState == State::Alive) { - mUncapturedErrorCallback(static_cast<WGPUErrorType>(ToWGPUErrorType(type)), - messageStr.c_str(), mUncapturedErrorUserdata); + if (mUncapturedErrorCallbackInfo.callback != nullptr && mState == State::Alive) { + mUncapturedErrorCallbackInfo.callback( + static_cast<WGPUErrorType>(ToWGPUErrorType(type)), messageStr.c_str(), + mUncapturedErrorCallbackInfo.userdata); } } } @@ -694,19 +779,18 @@ // Clearing the callback and userdata is allowed because in Chromium they should be cleared // after Dawn device is destroyed and before Dawn wire server is destroyed. if (callback == nullptr) { - mUncapturedErrorCallback = nullptr; - mUncapturedErrorUserdata = nullptr; + mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo; return; } if (IsLost()) { return; } - mUncapturedErrorCallback = callback; - mUncapturedErrorUserdata = userdata; + mUncapturedErrorCallbackInfo = {nullptr, callback, userdata}; } void DeviceBase::APISetDeviceLostCallback(wgpu::DeviceLostCallback callback, void* userdata) { - // TODO(chromium:1234617): Add a deprecation warning. + EmitDeprecationWarning( + "SetDeviceLostCallback is deprecated. Pass the callback in the device descriptor instead."); // The registered callback function and userdata pointer are stored and used by deferred // callback tasks, and after setting a different callback (especially in the case of @@ -718,15 +802,16 @@ // Clearing the callback and userdata is allowed because in Chromium they should be cleared // after Dawn device is destroyed and before Dawn wire server is destroyed. if (callback == nullptr) { - mDeviceLostCallback = nullptr; - mDeviceLostUserdata = nullptr; + mLostEvent->mCallback = nullptr; + mLostEvent->mOldCallback = nullptr; + mLostEvent->mUserdata = nullptr; return; } if (IsLost()) { return; } - mDeviceLostCallback = callback; - mDeviceLostUserdata = userdata; + mLostEvent->mOldCallback = callback; + mLostEvent->mUserdata = userdata; } void DeviceBase::APIPushErrorScope(wgpu::ErrorFilter filter) {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h index 48301a4..715767e 100644 --- a/src/dawn/native/Device.h +++ b/src/dawn/native/Device.h
@@ -79,9 +79,40 @@ class DeviceBase : public ErrorSink, public RefCountedWithExternalCount { public: + struct DeviceLostEvent final : public EventManager::TrackedEvent { + // TODO(https://crbug.com/dawn/2465): Pass just the DeviceLostCallbackInfo when setters are + // deprecated. Creates and sets the device lost event for the given device if applicable. If + // the device is nullptr, an event is still created, but the caller owns the last ref of the + // event. When passing a device, note that device construction can be successful but fail + // later at initialization, and this should only be called with the device if initialization + // was successful. + static Ref<DeviceLostEvent> Create(const DeviceDescriptor* descriptor); + + // Event result fields need to be public so that they can easily be updated prior to + // completing the event. + wgpu::DeviceLostReason mReason; + std::string mMessage; + + wgpu::DeviceLostCallbackNew mCallback = nullptr; + // TODO(https://crbug.com/dawn/2465): Remove old callback when setters are deprecated, and + // move userdata into private. + wgpu::DeviceLostCallback mOldCallback = nullptr; + raw_ptr<void> mUserdata; + // Note that the device is set when the event is passed to construct a device. + Ref<DeviceBase> mDevice = nullptr; + + private: + explicit DeviceLostEvent(const DeviceLostCallbackInfo& callbackInfo); + DeviceLostEvent(wgpu::DeviceLostCallback oldCallback, void* userdata); + ~DeviceLostEvent() override; + + void Complete(EventCompletionType completionType) override; + }; + DeviceBase(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceLostEvent>&& lostEvent); ~DeviceBase() override; // Handles the error, causing a device loss if applicable. Almost always when a device loss @@ -96,7 +127,9 @@ MaybeError ValidateObject(const ApiObjectBase* object) const; - InstanceBase* GetInstance() const; + // TODO(dawn:1702) Remove virtual when we mock the adapter. + virtual InstanceBase* GetInstance() const; + AdapterBase* GetAdapter() const; PhysicalDeviceBase* GetPhysicalDevice() const; virtual dawn::platform::Platform* GetPlatform() const; @@ -417,6 +450,11 @@ void DestroyObjects(); void Destroy(); + // Device lost event needs to be protected for now because mock device needs it. + // TODO(dawn:1702) Make this private and move the class in the implementation file when we mock + // the adapter. + Ref<DeviceLostEvent> mLostEvent = nullptr; + private: void WillDropLastExternalRef() override; @@ -509,16 +547,12 @@ const TextureCopy& dst, const Extent3D& copySizePixels) = 0; - wgpu::ErrorCallback mUncapturedErrorCallback = nullptr; - raw_ptr<void> mUncapturedErrorUserdata = nullptr; + UncapturedErrorCallbackInfo mUncapturedErrorCallbackInfo; std::shared_mutex mLoggingMutex; wgpu::LoggingCallback mLoggingCallback = nullptr; raw_ptr<void> mLoggingUserdata = nullptr; - wgpu::DeviceLostCallback mDeviceLostCallback = nullptr; - raw_ptr<void> mDeviceLostUserdata = nullptr; - std::unique_ptr<ErrorScopeStack> mErrorScopeStack; Ref<AdapterBase> mAdapter;
diff --git a/src/dawn/native/EventManager.cpp b/src/dawn/native/EventManager.cpp index 7037557..62cfb50 100644 --- a/src/dawn/native/EventManager.cpp +++ b/src/dawn/native/EventManager.cpp
@@ -345,62 +345,61 @@ FutureID EventManager::TrackEvent(Ref<TrackedEvent>&& event) { FutureID futureID = mNextFutureID++; - return mEvents.Use([&](auto events) { - if (!events->has_value()) { + event->mFutureID = futureID; + + // Handle the event now if it's spontaneous and ready. + if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) { + bool isReady = false; + auto completionData = event->GetCompletionData(); + if (std::holds_alternative<Ref<SystemEvent>>(completionData)) { + isReady = std::get<Ref<SystemEvent>>(completionData)->IsSignaled(); + } + if (std::holds_alternative<QueueAndSerial>(completionData)) { + auto& queueAndSerial = std::get<QueueAndSerial>(completionData); + isReady = queueAndSerial.completionSerial <= + queueAndSerial.queue->GetCompletedCommandSerial(); + } + if (isReady) { + event->EnsureComplete(EventCompletionType::Ready); return futureID; } + } - if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) { - bool isReady = false; - auto completionData = event->GetCompletionData(); - if (std::holds_alternative<Ref<SystemEvent>>(completionData)) { - isReady = std::get<Ref<SystemEvent>>(completionData)->IsSignaled(); - } - if (std::holds_alternative<QueueAndSerial>(completionData)) { - auto& queueAndSerial = std::get<QueueAndSerial>(completionData); - isReady = queueAndSerial.completionSerial <= - queueAndSerial.queue->GetCompletedCommandSerial(); - } - if (isReady) { - event->EnsureComplete(EventCompletionType::Ready); - return futureID; - } - } - - (*events)->emplace(futureID, std::move(event)); - return futureID; - }); -} - -void EventManager::SetFutureReady(FutureID futureID) { - Ref<TrackedEvent> spontaneousEvent; mEvents.Use([&](auto events) { if (!events->has_value()) { return; } - - if (auto it = (*events)->find(futureID); it != (*events)->end()) { - auto& event = it->second; - - auto completionData = event->GetCompletionData(); - if (std::holds_alternative<Ref<SystemEvent>>(completionData)) { - std::get<Ref<SystemEvent>>(event->GetCompletionData())->Signal(); - } - if (std::holds_alternative<QueueAndSerial>(completionData)) { - auto& queueAndSerial = std::get<QueueAndSerial>(completionData); - queueAndSerial.completionSerial = queueAndSerial.queue->GetCompletedCommandSerial(); - } - - if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) { - spontaneousEvent = std::move(event); - (*events)->erase(futureID); - } - } + (*events)->emplace(futureID, std::move(event)); }); + return futureID; +} + +void EventManager::SetFutureReady(TrackedEvent* event) { + auto completionData = event->GetCompletionData(); + if (std::holds_alternative<Ref<SystemEvent>>(completionData)) { + std::get<Ref<SystemEvent>>(completionData)->Signal(); + } + if (std::holds_alternative<QueueAndSerial>(completionData)) { + auto& queueAndSerial = std::get<QueueAndSerial>(completionData); + queueAndSerial.completionSerial = queueAndSerial.queue->GetCompletedCommandSerial(); + } + + // Sometimes, events might become ready before they are even tracked. This can happen because + // tracking is ordered to uphold callback ordering, but events may become ready in any order. If + // the event is spontaneous, it will be completed when it is tracked. + if (event->mFutureID == kNullFutureID) { + return; + } // Handle spontaneous completion now. - if (spontaneousEvent) { - spontaneousEvent->EnsureComplete(EventCompletionType::Ready); + if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) { + mEvents.Use([&](auto events) { + if (!events->has_value()) { + return; + } + (*events)->erase(event->mFutureID); + }); + event->EnsureComplete(EventCompletionType::Ready); } } @@ -409,7 +408,8 @@ std::vector<TrackedEvent::WaitRef> completable; wgpu::WaitStatus waitStatus; - auto needFutureProcessEvents = mEvents.Use([&](auto events) { + bool hasProgressingEvents = false; + auto hasIncompleteEvents = mEvents.Use([&](auto events) { // Iterate all events and record poll events and spontaneous events since they are both // allowed to be completed in the ProcessPoll call. Note that spontaneous events are allowed // to trigger anywhere which is why we include them in the call. @@ -417,6 +417,17 @@ futures.reserve((*events)->size()); for (auto& [futureID, event] : **events) { if (event->mCallbackMode != wgpu::CallbackMode::WaitAnyOnly) { + // Figure out if there are any progressing events. If we only have non-progressing + // events, we need to return false to indicate that there isn't any polling work to + // be done. + auto completionData = event->GetCompletionData(); + if (std::holds_alternative<Ref<SystemEvent>>(completionData)) { + hasProgressingEvents |= + std::get<Ref<SystemEvent>>(completionData)->IsProgressing(); + } else { + hasProgressingEvents = true; + } + futures.push_back( TrackedFutureWaitInfo{futureID, TrackedEvent::WaitRef{event.Get()}, 0, false}); } @@ -429,7 +440,6 @@ waitStatus = WaitImpl(futures, Nanoseconds(0)); if (waitStatus == wgpu::WaitStatus::TimedOut) { - // Return the beginning to indicate that nothing completed. return true; } DAWN_ASSERT(waitStatus == wgpu::WaitStatus::Success); @@ -449,7 +459,9 @@ for (auto& event : completable) { event->EnsureComplete(EventCompletionType::Ready); } - return needFutureProcessEvents; + // Note that in the event of all progressing events completing, but there exists non-progressing + // events, we will return true one extra time. + return hasIncompleteEvents && hasProgressingEvents; } wgpu::WaitStatus EventManager::WaitAny(size_t count, FutureWaitInfo* infos, Nanoseconds timeout) { @@ -548,6 +560,7 @@ : TrackedEvent(callbackMode, SystemEvent::CreateSignaled()) {} EventManager::TrackedEvent::~TrackedEvent() { + DAWN_ASSERT(mFutureID != kNullFutureID); DAWN_ASSERT(mCompleted); }
diff --git a/src/dawn/native/EventManager.h b/src/dawn/native/EventManager.h index 024dc37..b824610 100644 --- a/src/dawn/native/EventManager.h +++ b/src/dawn/native/EventManager.h
@@ -72,8 +72,8 @@ class TrackedEvent; // Track a TrackedEvent and give it a FutureID. - [[nodiscard]] FutureID TrackEvent(Ref<TrackedEvent>&&); - void SetFutureReady(FutureID futureID); + FutureID TrackEvent(Ref<TrackedEvent>&&); + void SetFutureReady(TrackedEvent* event); // Returns true if future ProcessEvents is needed. bool ProcessPollEvents(); @@ -152,6 +152,7 @@ virtual void Complete(EventCompletionType) = 0; wgpu::CallbackMode mCallbackMode; + FutureID mFutureID = kNullFutureID; #if DAWN_ENABLE_ASSERTS std::atomic<bool> mCurrentlyBeingWaited = false;
diff --git a/src/dawn/native/ObjectBase.h b/src/dawn/native/ObjectBase.h index 9fd8f51..6ac6ac9 100644 --- a/src/dawn/native/ObjectBase.h +++ b/src/dawn/native/ObjectBase.h
@@ -172,6 +172,9 @@ template <class T> T* ReturnToAPI(Ref<T>&& object) { + if (object == nullptr) { + return nullptr; + } if constexpr (T::HasExternalRefCount) { // For an object which has external ref count, just need to increase the external ref count, // and keep the total ref count unchanged.
diff --git a/src/dawn/native/PhysicalDevice.cpp b/src/dawn/native/PhysicalDevice.cpp index b90c77c..fa91398 100644 --- a/src/dawn/native/PhysicalDevice.cpp +++ b/src/dawn/native/PhysicalDevice.cpp
@@ -29,6 +29,7 @@ #include <algorithm> #include <memory> +#include <utility> #include "dawn/common/Constants.h" #include "dawn/common/GPUInfo.h" @@ -72,8 +73,9 @@ ResultOrError<Ref<DeviceBase>> PhysicalDeviceBase::CreateDevice( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - return CreateDeviceImpl(adapter, descriptor, deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + return CreateDeviceImpl(adapter, descriptor, deviceToggles, std::move(lostEvent)); } void PhysicalDeviceBase::InitializeVendorArchitectureImpl() {
diff --git a/src/dawn/native/PhysicalDevice.h b/src/dawn/native/PhysicalDevice.h index c2ac2a6..2c1861f 100644 --- a/src/dawn/native/PhysicalDevice.h +++ b/src/dawn/native/PhysicalDevice.h
@@ -37,6 +37,7 @@ #include "dawn/common/Ref.h" #include "dawn/common/RefCounted.h" #include "dawn/common/ityp_span.h" +#include "dawn/native/Device.h" #include "dawn/native/Error.h" #include "dawn/native/Features.h" #include "dawn/native/Forward.h" @@ -67,7 +68,8 @@ ResultOrError<Ref<DeviceBase>> CreateDevice(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); uint32_t GetVendorId() const; uint32_t GetDeviceId() const; @@ -141,7 +143,8 @@ virtual ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) = 0; + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) = 0; virtual MaybeError InitializeImpl() = 0;
diff --git a/src/dawn/native/SystemEvent.cpp b/src/dawn/native/SystemEvent.cpp index 6d2a3a6..c22787f 100644 --- a/src/dawn/native/SystemEvent.cpp +++ b/src/dawn/native/SystemEvent.cpp
@@ -130,6 +130,15 @@ return ev; } +// static +Ref<SystemEvent> SystemEvent::CreateNonProgressingEvent() { + return AcquireRef(new SystemEvent(kNonProgressingPayload)); +} + +bool SystemEvent::IsProgressing() const { + return GetRefCountPayload() != kNonProgressingPayload; +} + bool SystemEvent::IsSignaled() const { return mSignaled.load(std::memory_order_acquire); }
diff --git a/src/dawn/native/SystemEvent.h b/src/dawn/native/SystemEvent.h index 9e75f64..f641cf7 100644 --- a/src/dawn/native/SystemEvent.h +++ b/src/dawn/native/SystemEvent.h
@@ -103,8 +103,12 @@ class SystemEvent : public RefCounted { public: - static Ref<SystemEvent> CreateSignaled(); + using RefCounted::RefCounted; + static Ref<SystemEvent> CreateSignaled(); + static Ref<SystemEvent> CreateNonProgressingEvent(); + + bool IsProgressing() const; bool IsSignaled() const; void Signal(); @@ -113,6 +117,10 @@ const SystemEventReceiver& GetOrCreateSystemEventReceiver(); private: + // Some SystemEvents may be non-progressing, i.e. DeviceLost. We tag these events so that we can + // correctly return whether there is progressing work when users are polling. + static constexpr uint64_t kNonProgressingPayload = 1; + // mSignaled indicates whether the event has already been signaled. // It is stored outside the mPipe mutex so its status can quickly be checked without // acquiring a lock.
diff --git a/src/dawn/native/d3d/DeviceD3D.cpp b/src/dawn/native/d3d/DeviceD3D.cpp index 312acb4..02d0621 100644 --- a/src/dawn/native/d3d/DeviceD3D.cpp +++ b/src/dawn/native/d3d/DeviceD3D.cpp
@@ -27,6 +27,8 @@ #include "dawn/native/d3d/DeviceD3D.h" +#include <utility> + #include "dawn/native/d3d/BackendD3D.h" #include "dawn/native/d3d/ExternalImageDXGIImpl.h" #include "dawn/native/d3d/Forward.h" @@ -37,8 +39,9 @@ Device::Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) - : DeviceBase(adapter, descriptor, deviceToggles) {} + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) + : DeviceBase(adapter, descriptor, deviceToggles, std::move(lostEvent)) {} Device::~Device() { Destroy();
diff --git a/src/dawn/native/d3d/DeviceD3D.h b/src/dawn/native/d3d/DeviceD3D.h index cae7682..272abd9 100644 --- a/src/dawn/native/d3d/DeviceD3D.h +++ b/src/dawn/native/d3d/DeviceD3D.h
@@ -47,7 +47,8 @@ public: Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; ResultOrError<wgpu::TextureUsage> GetSupportedSurfaceUsageImpl(
diff --git a/src/dawn/native/d3d11/DeviceD3D11.cpp b/src/dawn/native/d3d11/DeviceD3D11.cpp index dc5b66d..c673c0d 100644 --- a/src/dawn/native/d3d11/DeviceD3D11.cpp +++ b/src/dawn/native/d3d11/DeviceD3D11.cpp
@@ -112,8 +112,10 @@ // static ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - Ref<Device> device = AcquireRef(new Device(adapter, descriptor, deviceToggles)); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + Ref<Device> device = + AcquireRef(new Device(adapter, descriptor, deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; }
diff --git a/src/dawn/native/d3d11/DeviceD3D11.h b/src/dawn/native/d3d11/DeviceD3D11.h index 21aca83..b9bc161 100644 --- a/src/dawn/native/d3d11/DeviceD3D11.h +++ b/src/dawn/native/d3d11/DeviceD3D11.h
@@ -44,7 +44,8 @@ public: static ResultOrError<Ref<Device>> Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor);
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp index 938bbf5..1c33d0b 100644 --- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp +++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -320,8 +320,9 @@ ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - return Device::Create(adapter, descriptor, deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + return Device::Create(adapter, descriptor, deviceToggles, std::move(lostEvent)); } // Resets the backend device and creates a new one. If any D3D11 objects belonging to the
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h index 01e28fc..7f0f18b 100644 --- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h +++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
@@ -61,9 +61,11 @@ void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override; void SetupBackendDeviceToggles(TogglesState* deviceToggles) const override; - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override; + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override; MaybeError ResetInternalDeviceForTestingImpl() override;
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp index 2bec840..5aea624 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.cpp +++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -81,8 +81,10 @@ // static ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - Ref<Device> device = AcquireRef(new Device(adapter, descriptor, deviceToggles)); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + Ref<Device> device = + AcquireRef(new Device(adapter, descriptor, deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; } @@ -200,8 +202,9 @@ Device::Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) - : Base(adapter, descriptor, deviceToggles) {} + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) + : Base(adapter, descriptor, deviceToggles, std::move(lostEvent)) {} Device::~Device() = default;
diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h index c8040cc..c34751eb 100644 --- a/src/dawn/native/d3d12/DeviceD3D12.h +++ b/src/dawn/native/d3d12/DeviceD3D12.h
@@ -63,7 +63,8 @@ public: static ResultOrError<Ref<Device>> Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor); @@ -187,7 +188,8 @@ Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ResultOrError<Ref<BindGroupBase>> CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override;
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp index e7a8b03..cc9775d 100644 --- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp +++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -792,8 +792,9 @@ ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - return Device::Create(adapter, descriptor, deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + return Device::Create(adapter, descriptor, deviceToggles, std::move(lostEvent)); } // Resets the backend device and creates a new one. If any D3D12 objects belonging to the
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.h b/src/dawn/native/d3d12/PhysicalDeviceD3D12.h index 606f709..c9e910e 100644 --- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.h +++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.h
@@ -61,9 +61,11 @@ void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override; void SetupBackendDeviceToggles(TogglesState* deviceToggles) const override; - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override; + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override; MaybeError ResetInternalDeviceForTestingImpl() override;
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm index e9f65b0..41f36ec 100644 --- a/src/dawn/native/metal/BackendMTL.mm +++ b/src/dawn/native/metal/BackendMTL.mm
@@ -304,10 +304,12 @@ bool SupportsFeatureLevel(FeatureLevel) const override { return true; } private: - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override { - return Device::Create(adapter, mDevice, descriptor, deviceToggles); + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override { + return Device::Create(adapter, mDevice, descriptor, deviceToggles, std::move(lostEvent)); } void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override {}
diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h index fa40bbb..2c8df01 100644 --- a/src/dawn/native/metal/DeviceMTL.h +++ b/src/dawn/native/metal/DeviceMTL.h
@@ -53,7 +53,8 @@ static ResultOrError<Ref<Device>> Create(AdapterBase* adapter, NSPRef<id<MTLDevice>> mtlDevice, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor); @@ -90,7 +91,8 @@ Device(AdapterBase* adapter, NSPRef<id<MTLDevice>> mtlDevice, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ResultOrError<Ref<BindGroupBase>> CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override;
diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm index 2ac4c5f..10e9fbb 100644 --- a/src/dawn/native/metal/DeviceMTL.mm +++ b/src/dawn/native/metal/DeviceMTL.mm
@@ -124,10 +124,11 @@ ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter, NSPRef<id<MTLDevice>> mtlDevice, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { @autoreleasepool { - Ref<Device> device = - AcquireRef(new Device(adapter, std::move(mtlDevice), descriptor, deviceToggles)); + Ref<Device> device = AcquireRef(new Device(adapter, std::move(mtlDevice), descriptor, + deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; } @@ -136,8 +137,10 @@ Device::Device(AdapterBase* adapter, NSPRef<id<MTLDevice>> mtlDevice, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) - : DeviceBase(adapter, descriptor, deviceToggles), mMtlDevice(std::move(mtlDevice)) { + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) + : DeviceBase(adapter, descriptor, deviceToggles, std::move(lostEvent)), + mMtlDevice(std::move(mtlDevice)) { // On macOS < 11.0, we only can check whether counter sampling is supported, and the counter // only can be sampled between command boundary using sampleCountersInBuffer API if it's // supported.
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp index db54784..adfc7be 100644 --- a/src/dawn/native/null/DeviceNull.cpp +++ b/src/dawn/native/null/DeviceNull.cpp
@@ -92,8 +92,9 @@ ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - return Device::Create(adapter, descriptor, deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + return Device::Create(adapter, descriptor, deviceToggles, std::move(lostEvent)); } void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const { @@ -165,8 +166,10 @@ // static ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - Ref<Device> device = AcquireRef(new Device(adapter, descriptor, deviceToggles)); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + Ref<Device> device = + AcquireRef(new Device(adapter, descriptor, deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; }
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h index db6b596..694e049 100644 --- a/src/dawn/native/null/DeviceNull.h +++ b/src/dawn/native/null/DeviceNull.h
@@ -104,7 +104,8 @@ public: static ResultOrError<Ref<Device>> Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor); @@ -208,9 +209,11 @@ void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override; void SetupBackendDeviceToggles(TogglesState* deviceToggles) const override; - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override; + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override; void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override; };
diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp index 7c5b95f..7293d47 100644 --- a/src/dawn/native/opengl/DeviceGL.cpp +++ b/src/dawn/native/opengl/DeviceGL.cpp
@@ -123,9 +123,10 @@ const UnpackedPtr<DeviceDescriptor>& descriptor, const OpenGLFunctions& functions, std::unique_ptr<Context> context, - const TogglesState& deviceToggles) { - Ref<Device> device = - AcquireRef(new Device(adapter, descriptor, functions, std::move(context), deviceToggles)); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + Ref<Device> device = AcquireRef(new Device(adapter, descriptor, functions, std::move(context), + deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; } @@ -134,8 +135,9 @@ const UnpackedPtr<DeviceDescriptor>& descriptor, const OpenGLFunctions& functions, std::unique_ptr<Context> context, - const TogglesState& deviceToggles) - : DeviceBase(adapter, descriptor, deviceToggles), + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) + : DeviceBase(adapter, descriptor, deviceToggles, std::move(lostEvent)), mGL(functions), mContext(std::move(context)) {}
diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h index 2ad1e2e..1302ef7 100644 --- a/src/dawn/native/opengl/DeviceGL.h +++ b/src/dawn/native/opengl/DeviceGL.h
@@ -55,7 +55,8 @@ const UnpackedPtr<DeviceDescriptor>& descriptor, const OpenGLFunctions& functions, std::unique_ptr<Context> context, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor); @@ -110,7 +111,8 @@ const UnpackedPtr<DeviceDescriptor>& descriptor, const OpenGLFunctions& functions, std::unique_ptr<Context> context, - const TogglesState& deviceToggless); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ResultOrError<Ref<BindGroupBase>> CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override;
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp index 25bf2cc..b9a14a7 100644 --- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp +++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -430,7 +430,8 @@ ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { EGLenum api = GetBackendType() == wgpu::BackendType::OpenGL ? EGL_OPENGL_API : EGL_OPENGL_ES_API; std::unique_ptr<Device::Context> context; @@ -443,7 +444,8 @@ DAWN_TRY_ASSIGN(context, ContextEGL::Create(mEGLFunctions, api, mDisplay, useANGLETextureSharing)); - return Device::Create(adapter, descriptor, mFunctions, std::move(context), deviceToggles); + return Device::Create(adapter, descriptor, mFunctions, std::move(context), deviceToggles, + std::move(lostEvent)); } bool PhysicalDevice::SupportsFeatureLevel(FeatureLevel featureLevel) const {
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.h b/src/dawn/native/opengl/PhysicalDeviceGL.h index e2f4771..2dda6e9 100644 --- a/src/dawn/native/opengl/PhysicalDeviceGL.h +++ b/src/dawn/native/opengl/PhysicalDeviceGL.h
@@ -61,9 +61,11 @@ void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override; void SetupBackendDeviceToggles(TogglesState* deviceToggles) const override; - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override; + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override; void PopulateBackendProperties(UnpackedPtr<AdapterProperties>& properties) const override;
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp index d88ee8f..265185c 100644 --- a/src/dawn/native/vulkan/DeviceVk.cpp +++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -77,16 +77,20 @@ // static ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - Ref<Device> device = AcquireRef(new Device(adapter, descriptor, deviceToggles)); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + Ref<Device> device = + AcquireRef(new Device(adapter, descriptor, deviceToggles, std::move(lostEvent))); DAWN_TRY(device->Initialize(descriptor)); return device; } Device::Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) - : DeviceBase(adapter, descriptor, deviceToggles), mDebugPrefix(GetNextDeviceDebugPrefix()) {} + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) + : DeviceBase(adapter, descriptor, deviceToggles, std::move(lostEvent)), + mDebugPrefix(GetNextDeviceDebugPrefix()) {} MaybeError Device::Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor) { // Copy the adapter's device info to the device so that we can change the "knobs"
diff --git a/src/dawn/native/vulkan/DeviceVk.h b/src/dawn/native/vulkan/DeviceVk.h index 82856ec..ab716ff 100644 --- a/src/dawn/native/vulkan/DeviceVk.h +++ b/src/dawn/native/vulkan/DeviceVk.h
@@ -59,7 +59,8 @@ public: static ResultOrError<Ref<Device>> Create(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ~Device() override; MaybeError Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor); @@ -126,7 +127,8 @@ private: Device(AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent); ResultOrError<Ref<BindGroupBase>> CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override;
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp index f5894dd..ce58ea0 100644 --- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp +++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -29,6 +29,7 @@ #include <algorithm> #include <string> +#include <utility> #include "dawn/common/GPUInfo.h" #include "dawn/native/ChainUtils.h" @@ -767,8 +768,9 @@ ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl( AdapterBase* adapter, const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) { - return Device::Create(adapter, descriptor, deviceToggles); + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) { + return Device::Create(adapter, descriptor, deviceToggles, std::move(lostEvent)); } FeatureValidationResult PhysicalDevice::ValidateFeatureSupportedWithTogglesImpl(
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.h b/src/dawn/native/vulkan/PhysicalDeviceVk.h index bbb66c5..b40f0b1 100644 --- a/src/dawn/native/vulkan/PhysicalDeviceVk.h +++ b/src/dawn/native/vulkan/PhysicalDeviceVk.h
@@ -72,9 +72,11 @@ void SetupBackendAdapterToggles(TogglesState* adapterToggles) const override; void SetupBackendDeviceToggles(TogglesState* deviceToggles) const override; - ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(AdapterBase* adapter, - const UnpackedPtr<DeviceDescriptor>& descriptor, - const TogglesState& deviceToggles) override; + ResultOrError<Ref<DeviceBase>> CreateDeviceImpl( + AdapterBase* adapter, + const UnpackedPtr<DeviceDescriptor>& descriptor, + const TogglesState& deviceToggles, + Ref<DeviceBase::DeviceLostEvent>&& lostEvent) override; uint32_t FindDefaultComputeSubgroupSize() const; bool CheckSemaphoreSupport(DeviceExt deviceExt,
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp index b58fb64..6896f61 100644 --- a/src/dawn/tests/DawnTest.cpp +++ b/src/dawn/tests/DawnTest.cpp
@@ -67,6 +67,9 @@ namespace dawn { namespace { +using testing::_; +using testing::AtMost; + struct MapReadUserdata { raw_ptr<DawnTestBase> test; size_t slot; @@ -1089,6 +1092,16 @@ deviceDescriptor.requiredFeatures = requiredFeatures.data(); deviceDescriptor.requiredFeatureCount = requiredFeatures.size(); + // Set up the mocks for device loss. + void* deviceUserdata = GetUniqueUserdata(); + deviceDescriptor.deviceLostCallbackInfo.mode = wgpu::CallbackMode::AllowSpontaneous; + deviceDescriptor.deviceLostCallbackInfo.callback = mDeviceLostCallback.Callback(); + deviceDescriptor.deviceLostCallbackInfo.userdata = + mDeviceLostCallback.MakeUserdata(deviceUserdata); + // The loss of the device is expected to happen at the end of the test so at it directly. + EXPECT_CALL(mDeviceLostCallback, Call(_, WGPUDeviceLostReason_Destroyed, _, deviceUserdata)) + .Times(AtMost(1)); + wgpu::DawnCacheDeviceDescriptor cacheDesc = {}; deviceDescriptor.nextInChain = &cacheDesc; cacheDesc.isolationKey = isolationKey.c_str(); @@ -1111,11 +1124,6 @@ // RequestDevice is overriden by CreateDeviceImpl and device descriptor is ignored by it. wgpu::DeviceDescriptor deviceDesc = {}; - // Set up the mocks for device loss. - void* deviceUserdata = GetUniqueUserdata(); - deviceDesc.deviceLostCallback = mDeviceLostCallback.Callback(); - deviceDesc.deviceLostUserdata = mDeviceLostCallback.MakeUserdata(deviceUserdata); - adapter.RequestDevice( &deviceDesc, [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) { @@ -1129,11 +1137,6 @@ apiDevice.SetUncapturedErrorCallback(mDeviceErrorCallback.Callback(), mDeviceErrorCallback.MakeUserdata(apiDevice.Get())); - // The loss of the device is expected to happen at the end of the test so at it directly. - EXPECT_CALL(mDeviceLostCallback, - Call(WGPUDeviceLostReason_Destroyed, testing::_, deviceUserdata)) - .Times(testing::AtMost(1)); - apiDevice.SetLoggingCallback( [](WGPULoggingType type, char const* message, void*) { switch (type) { @@ -1222,8 +1225,7 @@ resolvedDevice = device; } - EXPECT_CALL(mDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, testing::_, testing::_)) - .Times(1); + EXPECT_CALL(mDeviceLostCallback, Call(_, WGPUDeviceLostReason_Undefined, _, _)).Times(1); resolvedDevice.ForceLoss(wgpu::DeviceLostReason::Undefined, "Device lost for testing"); resolvedDevice.Tick(); }
diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h index 03378f0..e58f2f4 100644 --- a/src/dawn/tests/DawnTest.h +++ b/src/dawn/tests/DawnTest.h
@@ -336,7 +336,7 @@ // device loss that aren't expected should result in test failures and not just some warnings // printed to stdout. testing::StrictMock<testing::MockCallback<WGPUErrorCallback>> mDeviceErrorCallback; - testing::StrictMock<testing::MockCallback<WGPUDeviceLostCallback>> mDeviceLostCallback; + testing::StrictMock<testing::MockCallback<WGPUDeviceLostCallbackNew>> mDeviceLostCallback; // Helper methods to implement the EXPECT_ macros std::ostringstream& AddBufferExpectation(const char* file,
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp index 5c8ebad..98bf703 100644 --- a/src/dawn/tests/end2end/DeviceLostTests.cpp +++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -436,12 +436,6 @@ // First LoseDeviceForTesting call should occur normally. The callback is already set in SetUp. LoseDeviceForTesting(); - // Second LoseDeviceForTesting call should result in no callbacks. Note we also reset the - // callback first since by default the device clears the callback after the device is lost. - device.SetDeviceLostCallback(mDeviceLostCallback.Callback(), - mDeviceLostCallback.MakeUserdata(device.Get())); - EXPECT_CALL(mDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, testing::_, device.Get())) - .Times(0); device.ForceLoss(wgpu::DeviceLostReason::Undefined, "Device lost for testing"); FlushWire(); testing::Mock::VerifyAndClearExpectations(&mDeviceLostCallback);
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp index 30acd1c..f4b0342 100644 --- a/src/dawn/tests/end2end/EventTests.cpp +++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -152,7 +152,7 @@ void LoseTestDevice() { EXPECT_CALL(mDeviceLostCallback, - Call(WGPUDeviceLostReason_Undefined, testing::_, testing::_)) + Call(testing::_, WGPUDeviceLostReason_Undefined, testing::_, testing::_)) .Times(1); testDevice.ForceLoss(wgpu::DeviceLostReason::Undefined, "Device lost for testing"); testInstance.ProcessEvents(); @@ -427,12 +427,15 @@ }, &status}); - // Callback should have been called immediately because we leaked it since there's no way to - // call WaitAny or ProcessEvents anymore. - // - // TODO(crbug.com/dawn/2059): Once Spontaneous is implemented, this should no longer expect the - // callback to be cleaned up immediately (and should expect it to happen on a future Tick). - ASSERT_EQ(status, WGPUQueueWorkDoneStatus_InstanceDropped); + if (IsSpontaneous()) { + // TODO(crbug.com/dawn/2059): Once Spontaneous is implemented, this should no longer expect + // the callback to be cleaned up immediately (and should expect it to happen on a future + // Tick). + ASSERT_THAT(status, AnyOf(Eq(WGPUQueueWorkDoneStatus_Success), + Eq(WGPUQueueWorkDoneStatus_InstanceDropped))); + } else { + ASSERT_EQ(status, WGPUQueueWorkDoneStatus_InstanceDropped); + } } TEST_P(EventCompletionTests, WorkDoneDropInstanceAfterEvent) { @@ -449,8 +452,6 @@ }, &status}); - // For spontaneous cases, it is possible that since there is no work to be done, the serial can - // already be caught up and hence the callback fires immediately. if (IsSpontaneous()) { testInstance = nullptr; // Drop the last external ref to the instance.
diff --git a/src/dawn/tests/unittests/native/mocks/DeviceMock.cpp b/src/dawn/tests/unittests/native/mocks/DeviceMock.cpp index 07d4c4f..16b33bf 100644 --- a/src/dawn/tests/unittests/native/mocks/DeviceMock.cpp +++ b/src/dawn/tests/unittests/native/mocks/DeviceMock.cpp
@@ -123,6 +123,7 @@ ON_CALL(*this, TickImpl).WillByDefault([]() -> MaybeError { return {}; }); // Initialize the device. + GetInstance()->GetEventManager()->TrackEvent(mLostEvent); QueueDescriptor desc = {}; EXPECT_FALSE(Initialize(AcquireRef(new NiceMock<QueueMock>(this, &desc))).IsError()); } @@ -133,6 +134,10 @@ return mInstance->GetPlatform(); } +dawn::native::InstanceBase* DeviceMock::GetInstance() const { + return mInstance.Get(); +} + QueueMock* DeviceMock::GetQueueMock() { return reinterpret_cast<QueueMock*>(GetQueue()); }
diff --git a/src/dawn/tests/unittests/native/mocks/DeviceMock.h b/src/dawn/tests/unittests/native/mocks/DeviceMock.h index d49e9a8..64b405b 100644 --- a/src/dawn/tests/unittests/native/mocks/DeviceMock.h +++ b/src/dawn/tests/unittests/native/mocks/DeviceMock.h
@@ -55,6 +55,8 @@ ~DeviceMock() override; dawn::platform::Platform* GetPlatform() const override; + dawn::native::InstanceBase* GetInstance() const override; + // Mock specific functionality. QueueMock* GetQueueMock();
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp index 7afa6cb..fc2432c 100644 --- a/src/dawn/tests/unittests/validation/ValidationTest.cpp +++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -332,11 +332,13 @@ // Reinitialize the device. mExpectDestruction = true; wgpu::DeviceDescriptor deviceDescriptor = {}; - deviceDescriptor.deviceLostCallback = ValidationTest::OnDeviceLost; - deviceDescriptor.deviceLostUserdata = this; + deviceDescriptor.deviceLostCallbackInfo = {nullptr, wgpu::CallbackMode::AllowSpontaneous, + ValidationTest::OnDeviceLost, this}; + deviceDescriptor.uncapturedErrorCallbackInfo.callback = ValidationTest::OnDeviceError; + deviceDescriptor.uncapturedErrorCallbackInfo.userdata = this; + device = RequestDeviceSync(deviceDescriptor); backendDevice = mLastCreatedBackendDevice; - device.SetUncapturedErrorCallback(ValidationTest::OnDeviceError, this); mExpectDestruction = false; } @@ -362,7 +364,8 @@ self->mError = true; } -void ValidationTest::OnDeviceLost(WGPUDeviceLostReason reason, +void ValidationTest::OnDeviceLost(WGPUDevice const* device, + WGPUDeviceLostReason reason, const char* message, void* userdata) { auto* self = static_cast<ValidationTest*>(userdata);
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.h b/src/dawn/tests/unittests/validation/ValidationTest.h index 8708e7a..6eb7d26 100644 --- a/src/dawn/tests/unittests/validation/ValidationTest.h +++ b/src/dawn/tests/unittests/validation/ValidationTest.h
@@ -179,7 +179,10 @@ wgpu::Device RequestDeviceSync(const wgpu::DeviceDescriptor& deviceDesc); static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata); - static void OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata); + static void OnDeviceLost(WGPUDevice const* device, + WGPUDeviceLostReason reason, + const char* message, + void* userdata); virtual bool UseCompatibilityMode() const;
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp index 5588561..b865b3e 100644 --- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp +++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -114,15 +114,18 @@ }); } -static void DeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void* userdata) {} +static void DeviceLostCallback(WGPUDevice const* device, + WGPUDeviceLostReason reason, + const char* message, + void* userdata) {} // Test that the DeviceDescriptor is not allowed to pass a device lost callback from the client to // the server. TEST_P(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) { int userdata = 1337; wgpu::DeviceDescriptor desc = {}; - desc.deviceLostCallback = DeviceLostCallback; - desc.deviceLostUserdata = &userdata; + desc.deviceLostCallbackInfo.callback = DeviceLostCallback; + desc.deviceLostCallbackInfo.userdata = &userdata; AdapterRequestDevice(adapter, &desc); @@ -130,9 +133,11 @@ .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) { EXPECT_STREQ(apiDesc->label, desc.label); - // The callback should not be passed through to the server. - ASSERT_EQ(apiDesc->deviceLostCallback, nullptr); - ASSERT_EQ(apiDesc->deviceLostUserdata, nullptr); + // The callback should not be passed through to the server, and it should be overridden. + ASSERT_NE(apiDesc->deviceLostCallbackInfo.callback, nullptr); + ASSERT_NE(apiDesc->deviceLostCallbackInfo.callback, &DeviceLostCallback); + ASSERT_NE(apiDesc->deviceLostCallbackInfo.userdata, nullptr); + ASSERT_NE(apiDesc->deviceLostCallbackInfo.userdata, &userdata); // Call the callback so the test doesn't wait indefinitely. api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr, @@ -172,8 +177,6 @@ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull())) .Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull())) - .Times(1); EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull())) .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) { @@ -244,7 +247,6 @@ // Cleared when the device is destroyed. EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, DeviceRelease(apiDevice)); // Server has not recevied the release yet, so the device should be known. @@ -349,8 +351,6 @@ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull())) .Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull())) - .Times(1); EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull())) .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) { @@ -389,7 +389,6 @@ // Cleared when the device is destroyed. EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, DeviceRelease(apiDevice)); FlushClient(); }
diff --git a/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp b/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp index 564b444..cd14dd9 100644 --- a/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp +++ b/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp
@@ -109,10 +109,6 @@ ASSERT_TRUE(wireHelper->FlushServer()); ASSERT_NE(device, nullptr); - wgpu::BufferDescriptor bufferDesc = {}; - bufferDesc.size = 128; - bufferDesc.usage = wgpu::BufferUsage::Uniform; - // Destroy the device. device.Destroy();
diff --git a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp index 8220400..dc9a06d 100644 --- a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp +++ b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
@@ -81,12 +81,8 @@ // Check that disconnecting the wire client calls the device lost callback exacty once. TEST_F(WireDisconnectTests, CallsDeviceLostCallback) { - MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; - wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), - mockDeviceLostCallback.MakeUserdata(this)); - // Disconnect the wire client. We should receive device lost only once. - EXPECT_CALL(mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this)) + EXPECT_CALL(deviceLostCallback, Call(_, WGPUDeviceLostReason_InstanceDropped, _, this)) .Times(Exactly(1)); GetWireClient()->Disconnect(); GetWireClient()->Disconnect(); @@ -95,21 +91,17 @@ // Check that disconnecting the wire client after a device loss does not trigger the callback // again. TEST_F(WireDisconnectTests, ServerLostThenDisconnect) { - MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; - wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), - mockDeviceLostCallback.MakeUserdata(this)); - api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Undefined, "some reason"); // Flush the device lost return command. - EXPECT_CALL(mockDeviceLostCallback, - Call(WGPUDeviceLostReason_Undefined, StrEq("some reason"), this)) + EXPECT_CALL(deviceLostCallback, + Call(_, WGPUDeviceLostReason_Undefined, StrEq("some reason"), this)) .Times(Exactly(1)); FlushServer(); // Disconnect the client. We shouldn't see the lost callback again. - EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0)); + EXPECT_CALL(deviceLostCallback, Call).Times(Exactly(0)); GetWireClient()->Disconnect(); } @@ -141,7 +133,7 @@ mockDeviceLostCallback.MakeUserdata(this)); // Disconnect the client. We should see the callback once. - EXPECT_CALL(mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this)) + EXPECT_CALL(mockDeviceLostCallback, Call(WGPUDeviceLostReason_InstanceDropped, _, this)) .Times(Exactly(1)); GetWireClient()->Disconnect(); @@ -180,9 +172,6 @@ EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)) .Times(1) .InSequence(s1, s2); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)) - .Times(1) - .InSequence(s1, s2); EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1).InSequence(s1, s2, s3, s4, s5); EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1).InSequence(s1); EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1).InSequence(s2);
diff --git a/src/dawn/tests/unittests/wire/WireQueueTests.cpp b/src/dawn/tests/unittests/wire/WireQueueTests.cpp index 87e2603..69142e8 100644 --- a/src/dawn/tests/unittests/wire/WireQueueTests.cpp +++ b/src/dawn/tests/unittests/wire/WireQueueTests.cpp
@@ -176,7 +176,6 @@ // These set X callback methods are called before the device is released. EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1); FlushClient(); // Indicate to the fixture that the device was already released. @@ -198,7 +197,6 @@ // These set X callback methods are called before the device is released. EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1); FlushClient(); // Release the external queue reference. The queue should be released.
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp index 1fb4be0..d96a4cb 100644 --- a/src/dawn/tests/unittests/wire/WireTest.cpp +++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -35,6 +35,7 @@ using testing::_; using testing::AnyNumber; +using testing::AtMost; using testing::Exactly; using testing::Invoke; using testing::Mock; @@ -129,29 +130,41 @@ // Create the device for testing. apiDevice = api.GetNewDevice(); WGPUDeviceDescriptor deviceDesc = {}; + deviceDesc.deviceLostCallbackInfo.callback = deviceLostCallback.Callback(); + deviceDesc.deviceLostCallbackInfo.userdata = deviceLostCallback.MakeUserdata(this); + EXPECT_CALL(deviceLostCallback, Call).Times(AtMost(1)); + deviceDesc.uncapturedErrorCallbackInfo.callback = uncapturedErrorCallback.Callback(); + deviceDesc.uncapturedErrorCallbackInfo.userdata = uncapturedErrorCallback.MakeUserdata(this); MockCallback<WGPURequestDeviceCallback> deviceCb; wgpuAdapterRequestDevice(adapter.Get(), &deviceDesc, deviceCb.Callback(), deviceCb.MakeUserdata(this)); - EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _)).WillOnce([&]() { - // Set on device creation to forward callbacks to the client. - EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull())) - .Times(1); - EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull())).Times(1); + EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _)) + .WillOnce(WithArg<1>([&](const WGPUDeviceDescriptor* desc) { + // Set on device creation to forward callbacks to the client. + EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull())) + .Times(1); + EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1); - EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull())) - .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) { - *limits = {}; - return true; - }))); + // The mock objects currently require us to manually set the callbacks because we are no + // longer explicitly calling SetDeviceLostCallback anymore. + ProcTableAsClass::Object* object = + reinterpret_cast<ProcTableAsClass::Object*>(apiDevice); + object->mDeviceLostCallback = desc->deviceLostCallbackInfo.callback; + object->mDeviceLostUserdata = desc->deviceLostCallbackInfo.userdata; - EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr)) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull())) + .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) { + *limits = {}; + return true; + }))); - api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success, apiDevice, - nullptr); - }); + EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr)) + .WillOnce(Return(0)) + .WillOnce(Return(0)); + + api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success, + apiDevice, nullptr); + })); FlushClient(); EXPECT_CALL(deviceCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this)) .WillOnce(SaveArg<1>(&device)); @@ -189,8 +202,6 @@ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)) .Times(Exactly(1)); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(Exactly(1)); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)) - .Times(Exactly(1)); } mWireServer = nullptr; } @@ -238,8 +249,6 @@ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)) .Times(Exactly(1)); EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(Exactly(1)); - EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)) - .Times(Exactly(1)); } mWireServer = nullptr; }
diff --git a/src/dawn/tests/unittests/wire/WireTest.h b/src/dawn/tests/unittests/wire/WireTest.h index 7b4074b..c9c86a7 100644 --- a/src/dawn/tests/unittests/wire/WireTest.h +++ b/src/dawn/tests/unittests/wire/WireTest.h
@@ -32,6 +32,7 @@ #include "dawn/common/Log.h" #include "dawn/mock_webgpu.h" +#include "dawn/tests/MockCallback.h" #include "gtest/gtest.h" #include "webgpu/webgpu_cpp.h" @@ -148,6 +149,10 @@ void DefaultApiAdapterWasReleased(); testing::StrictMock<MockProcTable> api; + + testing::MockCallback<WGPUDeviceLostCallbackNew> deviceLostCallback; + testing::MockCallback<WGPUErrorCallback> uncapturedErrorCallback; + WGPUInstance instance; WGPUInstance apiInstance; wgpu::Adapter adapter;
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp index ae378a8..287acee 100644 --- a/src/dawn/wire/client/Adapter.cpp +++ b/src/dawn/wire/client/Adapter.cpp
@@ -69,18 +69,30 @@ private: void CompleteImpl(FutureID futureID, EventCompletionType completionType) override { + Device* device = mDevice; if (completionType == EventCompletionType::Shutdown) { mStatus = WGPURequestDeviceStatus_InstanceDropped; mMessage = "A valid external Instance reference no longer exists."; } - if (mStatus != WGPURequestDeviceStatus_Success && mDevice != nullptr) { - // If there was an error, we may need to reclaim the device allocation, otherwise the - // device is returned to the user who owns it. - mDevice->GetClient()->Free(mDevice.get()); - mDevice = nullptr; + if (mStatus != WGPURequestDeviceStatus_Success) { + device = nullptr; } if (mCallback) { - mCallback(mStatus, ToAPI(mDevice), mMessage ? mMessage->c_str() : nullptr, mUserdata); + mCallback(mStatus, ToAPI(device), mMessage ? mMessage->c_str() : nullptr, mUserdata); + } + + if (mStatus != WGPURequestDeviceStatus_Success) { + // If there was an error, we may need to call the device lost callback and reclaim the + // device allocation, otherwise the device is returned to the user who owns it. + if (mStatus == WGPURequestDeviceStatus_InstanceDropped) { + mDevice->HandleDeviceLost(WGPUDeviceLostReason_InstanceDropped, + "A valid external Instance reference no longer exists."); + } else { + mDevice->HandleDeviceLost(WGPUDeviceLostReason_FailedCreation, + "Device failed at creation."); + } + mDevice->Release(); + mDevice = nullptr; } } @@ -257,13 +269,17 @@ return {futureIDInternal}; } - // Ensure the device lost callback isn't serialized as part of the command, as it cannot be - // passed between processes. + // Ensure callbacks are not serialized as part of the command, as they cannot be passed between + // processes. WGPUDeviceDescriptor wireDescriptor = {}; if (descriptor) { wireDescriptor = *descriptor; wireDescriptor.deviceLostCallback = nullptr; wireDescriptor.deviceLostUserdata = nullptr; + wireDescriptor.deviceLostCallbackInfo.callback = nullptr; + wireDescriptor.deviceLostCallbackInfo.userdata = nullptr; + wireDescriptor.uncapturedErrorCallbackInfo.callback = nullptr; + wireDescriptor.uncapturedErrorCallbackInfo.userdata = nullptr; } AdapterRequestDeviceCmd cmd; @@ -271,6 +287,7 @@ cmd.eventManagerHandle = GetEventManagerHandle(); cmd.future = {futureIDInternal}; cmd.deviceObjectHandle = device->GetWireHandle(); + cmd.deviceLostFuture = device->GetDeviceLostFuture(); cmd.descriptor = &wireDescriptor; client->SerializeCommand(cmd);
diff --git a/src/dawn/wire/client/Client.cpp b/src/dawn/wire/client/Client.cpp index 2bc76dc..3d9aac3 100644 --- a/src/dawn/wire/client/Client.cpp +++ b/src/dawn/wire/client/Client.cpp
@@ -60,6 +60,11 @@ } Client::~Client() { + // Transition all event managers to ClientDropped state. + for (auto& [_, eventManager] : mEventManagers) { + eventManager->TransitionTo(EventManager::State::ClientDropped); + } + DestroyAllObjects(); } @@ -124,8 +129,7 @@ } // Reserve an EventManager for the given instance and make the association in the map. - mEventManagers.emplace(ObjectHandle(instance->GetWireId(), instance->GetWireGeneration()), - std::make_unique<EventManager>()); + mEventManagers.emplace(instance->GetWireHandle(), std::make_unique<EventManager>()); ReservedInstance result; result.instance = ToAPI(instance); @@ -159,6 +163,11 @@ mDisconnected = true; mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance()); + // Transition all event managers to ClientDropped state. + for (auto& [_, eventManager] : mEventManagers) { + eventManager->TransitionTo(EventManager::State::ClientDropped); + } + auto& deviceList = mObjects[ObjectType::Device]; { for (LinkNode<ObjectBase>* device = deviceList.head(); device != deviceList.end(); @@ -173,11 +182,6 @@ object->value()->CancelCallbacksForDisconnect(); } } - - // Transition all event managers to ClientDropped state. - for (auto& [_, eventManager] : mEventManagers) { - eventManager->TransitionTo(EventManager::State::ClientDropped); - } } bool Client::IsDisconnected() const {
diff --git a/src/dawn/wire/client/ClientDoers.cpp b/src/dawn/wire/client/ClientDoers.cpp index 5dfdb7d..0d74781 100644 --- a/src/dawn/wire/client/ClientDoers.cpp +++ b/src/dawn/wire/client/ClientDoers.cpp
@@ -66,15 +66,4 @@ return WireResult::Success; } -WireResult Client::DoDeviceLostCallback(Device* device, - WGPUDeviceLostReason reason, - char const* message) { - if (device == nullptr) { - // The device might have been deleted or recreated so this isn't an error. - return WireResult::Success; - } - device->HandleDeviceLost(reason, message); - return WireResult::Success; -} - } // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp index 3762b98..5196d82 100644 --- a/src/dawn/wire/client/Device.cpp +++ b/src/dawn/wire/client/Device.cpp
@@ -162,45 +162,128 @@ EventType::CreateRenderPipeline, WGPUCreateRenderPipelineAsyncCallbackInfo>; +static constexpr WGPUUncapturedErrorCallbackInfo kEmptyUncapturedErrorCallbackInfo = { + nullptr, nullptr, nullptr}; + } // namespace +class Device::DeviceLostEvent : public TrackedEvent { + public: + static constexpr EventType kType = EventType::DeviceLost; + + DeviceLostEvent(const WGPUDeviceLostCallbackInfo& callbackInfo, Device* device) + : TrackedEvent(callbackInfo.mode), mDevice(device) { + DAWN_ASSERT(device != nullptr); + mDevice->Reference(); + } + + ~DeviceLostEvent() override { mDevice->Release(); } + + EventType GetType() override { return kType; } + + WireResult ReadyHook(FutureID futureID, WGPUDeviceLostReason reason, const char* message) { + mReason = reason; + if (message != nullptr) { + mMessage = message; + } + mDevice->mDeviceLostInfo.futureID = kNullFutureID; + return WireResult::Success; + } + + private: + void CompleteImpl(FutureID futureID, EventCompletionType completionType) override { + if (completionType == EventCompletionType::Shutdown) { + mReason = WGPUDeviceLostReason_InstanceDropped; + mMessage = "A valid external Instance reference no longer exists."; + } + + if (mDevice->mDeviceLostInfo.oldCallback != nullptr) { + mDevice->mDeviceLostInfo.oldCallback(mReason, mMessage ? mMessage->c_str() : nullptr, + mDevice->mDeviceLostInfo.userdata); + } else if (mDevice->mDeviceLostInfo.callback != nullptr) { + auto device = mReason != WGPUDeviceLostReason_FailedCreation ? ToAPI(mDevice) : nullptr; + mDevice->mDeviceLostInfo.callback(&device, mReason, + mMessage ? mMessage->c_str() : nullptr, + mDevice->mDeviceLostInfo.userdata); + } + mDevice->mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo; + } + + WGPUDeviceLostReason mReason; + // Note that the message is optional because we want to return nullptr when it wasn't set + // instead of a pointer to an empty string. + std::optional<std::string> mMessage; + + // Strong reference to the device so that when we call the callback we can pass the device. + // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire. + raw_ptr<Device, DanglingUntriaged> mDevice; +}; + Device::Device(const ObjectBaseParams& params, const ObjectHandle& eventManagerHandle, const WGPUDeviceDescriptor* descriptor) - : ObjectWithEventsBase(params, eventManagerHandle), mIsAlive(std::make_shared<bool>()) { - if (descriptor && descriptor->deviceLostCallback) { - mDeviceLostCallback = descriptor->deviceLostCallback; - mDeviceLostUserdata = descriptor->deviceLostUserdata; - } - + : ObjectWithEventsBase(params, eventManagerHandle), mIsAlive(std::make_shared<bool>(true)) { #if defined(DAWN_ENABLE_ASSERTS) - mErrorCallback = [](WGPUErrorType, char const*, void*) { - static bool calledOnce = false; - if (!calledOnce) { - calledOnce = true; - dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is " - "probably not intended. If you really want to ignore errors " - "and suppress this message, set the callback to null."; - } - }; - - if (!mDeviceLostCallback) { - mDeviceLostCallback = [](WGPUDeviceLostReason, char const*, void*) { + static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = { + nullptr, WGPUCallbackMode_AllowSpontaneous, + [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*) { static bool calledOnce = false; if (!calledOnce) { calledOnce = true; dawn::WarningLog() << "No Dawn device lost callback was set. This is probably not " "intended. If you really want to ignore device lost " - "and suppress this message, set the callback to null."; + "and suppress this message, set the callback explicitly."; } - }; - } + }, + nullptr}; + static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = { + nullptr, + [](WGPUErrorType, char const*, void*) { + static bool calledOnce = false; + if (!calledOnce) { + calledOnce = true; + dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is " + "probably not intended. If you really want to ignore errors " + "and suppress this message, set the callback explicitly."; + } + }, + nullptr}; +#else + static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = { + nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr}; + static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = + kEmptyUncapturedErrorCallbackInfo; #endif // DAWN_ENABLE_ASSERTS + + WGPUDeviceLostCallbackInfo deviceLostCallbackInfo = kDefaultDeviceLostCallbackInfo; + if (descriptor != nullptr) { + if (descriptor->deviceLostCallbackInfo.callback != nullptr) { + deviceLostCallbackInfo = descriptor->deviceLostCallbackInfo; + if (deviceLostCallbackInfo.mode == WGPUCallbackMode_WaitAnyOnly) { + // TODO(dawn:2458) Currently we default the callback mode to Spontaneous if not + // passed for backwards compatibility. We should add warning logging for it though + // when available. Update this when we have WGPUCallbackMode_Undefined. + deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowSpontaneous; + } + mDeviceLostInfo.callback = deviceLostCallbackInfo.callback; + mDeviceLostInfo.userdata = deviceLostCallbackInfo.userdata; + } else if (descriptor->deviceLostCallback != nullptr) { + deviceLostCallbackInfo = {nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr}; + mDeviceLostInfo.oldCallback = descriptor->deviceLostCallback; + mDeviceLostInfo.userdata = descriptor->deviceLostUserdata; + } + } + mDeviceLostInfo.event = std::make_unique<DeviceLostEvent>(deviceLostCallbackInfo, this); + + mUncapturedErrorCallbackInfo = kDefaultUncapturedErrorCallbackInfo; + if (descriptor && descriptor->uncapturedErrorCallbackInfo.callback != nullptr) { + mUncapturedErrorCallbackInfo = descriptor->uncapturedErrorCallbackInfo; + } } Device::~Device() { if (mQueue != nullptr) { - GetProcs().queueRelease(ToAPI(mQueue.ExtractAsDangling())); + mQueue.ExtractAsDangling()->Release(); } } @@ -208,6 +291,17 @@ return ObjectType::Device; } +uint32_t Device::Release() { + // The device always has a reference in it's DeviceLossEvent which is created at construction, + // so when we drop to 1, we want to set the event so that the device can be loss according to + // the callback mode. + uint32_t refCount = ObjectBase::Release(); + if (refCount == 1) { + HandleDeviceLost(WGPUDeviceLostReason_Destroyed, "Device was destroyed."); + } + return refCount; +} + bool Device::GetLimits(WGPUSupportedLimits* limits) const { return mLimitsAndFeatures.GetLimits(limits); } @@ -229,8 +323,9 @@ } void Device::HandleError(WGPUErrorType errorType, const char* message) { - if (mErrorCallback) { - mErrorCallback(errorType, message, mErrorUserdata); + if (mUncapturedErrorCallbackInfo.callback) { + mUncapturedErrorCallbackInfo.callback(errorType, message, + mUncapturedErrorCallbackInfo.userdata); } } @@ -242,19 +337,33 @@ } void Device::HandleDeviceLost(WGPUDeviceLostReason reason, const char* message) { - if (mDeviceLostCallback && !mDidRunLostCallback) { - mDidRunLostCallback = true; - mDeviceLostCallback(reason, message, mDeviceLostUserdata); + FutureID futureID = GetDeviceLostFuture().id; + if (futureID != kNullFutureID) { + DAWN_CHECK(GetEventManager().SetFutureReady<DeviceLostEvent>(futureID, reason, message) == + WireResult::Success); } } +WGPUFuture Device::GetDeviceLostFuture() { + // Lazily track the device lost event so that event ordering w.r.t RequestDevice is correct. + if (mDeviceLostInfo.event != nullptr) { + auto [deviceLostFutureIDInternal, tracked] = + GetEventManager().TrackEvent(std::move(mDeviceLostInfo.event)); + if (tracked) { + mDeviceLostInfo.futureID = deviceLostFutureIDInternal; + } + } + return {mDeviceLostInfo.futureID}; +} + std::weak_ptr<bool> Device::GetAliveWeakPtr() { return mIsAlive; } void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) { - mErrorCallback = errorCallback; - mErrorUserdata = errorUserdata; + if (mDeviceLostInfo.futureID != kNullFutureID) { + mUncapturedErrorCallbackInfo = {nullptr, errorCallback, errorUserdata}; + } } void Device::SetLoggingCallback(WGPULoggingCallback callback, void* userdata) { @@ -263,8 +372,18 @@ } void Device::SetDeviceLostCallback(WGPUDeviceLostCallback callback, void* userdata) { - mDeviceLostCallback = callback; - mDeviceLostUserdata = userdata; + if (mDeviceLostInfo.futureID != kNullFutureID) { + mDeviceLostInfo.oldCallback = callback; + mDeviceLostInfo.userdata = userdata; + } +} + +WireResult Client::DoDeviceLostCallback(ObjectHandle eventManager, + WGPUFuture future, + WGPUDeviceLostReason reason, + char const* message) { + return GetEventManager(eventManager) + .SetFutureReady<Device::DeviceLostEvent>(future.id, reason, message); } void Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) {
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h index 6d16647..4467c4b 100644 --- a/src/dawn/wire/client/Device.h +++ b/src/dawn/wire/client/Device.h
@@ -52,6 +52,9 @@ ObjectType GetObjectType() const override; + // Override the default Release implementation to handle the device lost event. + uint32_t Release(); + void SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata); void SetLoggingCallback(WGPULoggingCallback errorCallback, void* errorUserdata); void SetDeviceLostCallback(WGPUDeviceLostCallback errorCallback, void* errorUserdata); @@ -83,9 +86,12 @@ void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount); WGPUQueue GetQueue(); + WGPUFuture GetDeviceLostFuture(); std::weak_ptr<bool> GetAliveWeakPtr(); + class DeviceLostEvent; + private: template <typename Event, typename Cmd, @@ -95,14 +101,21 @@ LimitsAndFeatures mLimitsAndFeatures; - WGPUErrorCallback mErrorCallback = nullptr; - WGPUDeviceLostCallback mDeviceLostCallback = nullptr; + // TODO(crbug.com/dawn/2465): This can probably just be the future id once SetDeviceLostCallback + // is deprecated, and the callback and userdata moved into the DeviceLostEvent. + struct DeviceLostInfo { + FutureID futureID = kNullFutureID; + std::unique_ptr<TrackedEvent> event = nullptr; + WGPUDeviceLostCallbackNew callback = nullptr; + WGPUDeviceLostCallback oldCallback = nullptr; + // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire: + raw_ptr<void, DanglingUntriaged> userdata = nullptr; + }; + DeviceLostInfo mDeviceLostInfo; + + WGPUUncapturedErrorCallbackInfo mUncapturedErrorCallbackInfo; WGPULoggingCallback mLoggingCallback = nullptr; - bool mDidRunLostCallback = false; // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire: - raw_ptr<void, DanglingUntriaged> mErrorUserdata = nullptr; - // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire: - raw_ptr<void, DanglingUntriaged> mDeviceLostUserdata = nullptr; raw_ptr<void> mLoggingUserdata = nullptr; raw_ptr<Queue> mQueue = nullptr;
diff --git a/src/dawn/wire/client/EventManager.h b/src/dawn/wire/client/EventManager.h index de30fb3..ff5663e 100644 --- a/src/dawn/wire/client/EventManager.h +++ b/src/dawn/wire/client/EventManager.h
@@ -49,6 +49,7 @@ CompilationInfo, CreateComputePipeline, CreateRenderPipeline, + DeviceLost, MapAsync, PopErrorScope, RequestAdapter,
diff --git a/src/dawn/wire/client/ObjectBase.cpp b/src/dawn/wire/client/ObjectBase.cpp index cc992fc..df76a08 100644 --- a/src/dawn/wire/client/ObjectBase.cpp +++ b/src/dawn/wire/client/ObjectBase.cpp
@@ -59,11 +59,11 @@ mRefcount++; } -void ObjectBase::Release() { +uint32_t ObjectBase::Release() { DAWN_ASSERT(mRefcount != 0); - mRefcount--; - if (mRefcount == 0) { + uint32_t refCount = --mRefcount; + if (refCount == 0) { DestroyObjectCmd cmd; cmd.objectType = GetObjectType(); cmd.objectId = GetWireId(); @@ -72,6 +72,8 @@ client->SerializeCommand(cmd); client->Free(this, GetObjectType()); } + + return refCount; } ObjectWithEventsBase::ObjectWithEventsBase(const ObjectBaseParams& params,
diff --git a/src/dawn/wire/client/ObjectBase.h b/src/dawn/wire/client/ObjectBase.h index 4bfaa66..16d4fbf6 100644 --- a/src/dawn/wire/client/ObjectBase.h +++ b/src/dawn/wire/client/ObjectBase.h
@@ -64,7 +64,7 @@ Client* GetClient() const; void Reference(); - void Release(); + uint32_t Release(); protected: uint32_t GetRefcount() const { return mRefcount; }
diff --git a/src/dawn/wire/server/Server.cpp b/src/dawn/wire/server/Server.cpp index bf04246..7ed991e 100644 --- a/src/dawn/wire/server/Server.cpp +++ b/src/dawn/wire/server/Server.cpp
@@ -162,21 +162,13 @@ info->server->OnLogging(info->self, type, message); }, device->info.get()); - mProcs.deviceSetDeviceLostCallback( - device->handle, - [](WGPUDeviceLostReason reason, const char* message, void* userdata) { - DeviceInfo* info = static_cast<DeviceInfo*>(userdata); - info->server->OnDeviceLost(info->self, reason, message); - }, - device->info.get()); } void Server::ClearDeviceCallbacks(WGPUDevice device) { - // Un-set the error and lost callbacks since we cannot forward them + // Un-set the error and logging callbacks since we cannot forward them // after the server has been destroyed. mProcs.deviceSetUncapturedErrorCallback(device, nullptr, nullptr); mProcs.deviceSetLoggingCallback(device, nullptr, nullptr); - mProcs.deviceSetDeviceLostCallback(device, nullptr, nullptr); } } // namespace dawn::wire::server
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h index 1e15f1b..f908b3d 100644 --- a/src/dawn/wire/server/Server.h +++ b/src/dawn/wire/server/Server.h
@@ -160,6 +160,14 @@ ObjectHandle eventManager; WGPUFuture future; ObjectId deviceObjectId; + WGPUFuture deviceLostFuture; +}; + +struct DeviceLostUserdata : CallbackUserdata { + using CallbackUserdata::CallbackUserdata; + + ObjectHandle eventManager; + WGPUFuture future; }; class Server : public ServerBase { @@ -207,8 +215,13 @@ // Error callbacks void OnUncapturedError(ObjectHandle device, WGPUErrorType type, const char* message); - void OnDeviceLost(ObjectHandle device, WGPUDeviceLostReason reason, const char* message); void OnLogging(ObjectHandle device, WGPULoggingType type, const char* message); + + // Async event callbacks + void OnDeviceLost(DeviceLostUserdata* userdata, + WGPUDevice const* device, + WGPUDeviceLostReason reason, + const char* message); void OnDevicePopErrorScope(ErrorScopeUserdata* userdata, WGPUErrorType type, const char* message);
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp index a95e78c..c5bbba1 100644 --- a/src/dawn/wire/server/ServerAdapter.cpp +++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -36,6 +36,7 @@ ObjectHandle eventManager, WGPUFuture future, ObjectHandle deviceHandle, + WGPUFuture deviceLostFuture, const WGPUDeviceDescriptor* descriptor) { Known<WGPUDevice> device; WIRE_TRY(DeviceObjects().Allocate(&device, deviceHandle, AllocationState::Reserved)); @@ -44,8 +45,19 @@ userdata->eventManager = eventManager; userdata->future = future; userdata->deviceObjectId = device.id; + userdata->deviceLostFuture = deviceLostFuture; - mProcs.adapterRequestDevice(adapter->handle, descriptor, + // Update the descriptor with the device lost callback associated with this request. + auto deviceLostUserdata = MakeUserdata<DeviceLostUserdata>(); + deviceLostUserdata->eventManager = eventManager; + deviceLostUserdata->future = deviceLostFuture; + + WGPUDeviceDescriptor desc = *descriptor; + desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents; + desc.deviceLostCallbackInfo.callback = ForwardToServer<&Server::OnDeviceLost>; + desc.deviceLostCallbackInfo.userdata = deviceLostUserdata.release(); + + mProcs.adapterRequestDevice(adapter->handle, &desc, ForwardToServer<&Server::OnRequestDeviceCallback>, userdata.release()); return WireResult::Success; @@ -61,11 +73,19 @@ cmd.status = status; cmd.message = message; - if (status != WGPURequestDeviceStatus_Success) { - // Free the ObjectId which will make it unusable. - DeviceObjects().Free(data->deviceObjectId); - DAWN_ASSERT(device == nullptr); + // We always fill the reservation once we complete so that the client is the one to release it. + auto FillReservation = [&]() { + Known<WGPUDevice> reservation = + DeviceObjects().FillReservation(data->deviceObjectId, device); + reservation->info->server = this; + reservation->info->self = reservation.AsHandle(); SerializeCommand(cmd); + return reservation; + }; + + if (status != WGPURequestDeviceStatus_Success) { + DAWN_ASSERT(device == nullptr); + FillReservation(); return; } @@ -83,12 +103,11 @@ if (!IsFeatureSupported(f)) { // Release the device. mProcs.deviceRelease(device); - // Free the ObjectId which will make it unusable. - DeviceObjects().Free(data->deviceObjectId); + device = nullptr; cmd.status = WGPURequestDeviceStatus_Error; cmd.message = "Requested feature not supported."; - SerializeCommand(cmd); + FillReservation(); return; } } @@ -105,13 +124,9 @@ cmd.limits = &limits; // Assign the handle and allocated status if the device is created successfully. - Known<WGPUDevice> reservation = DeviceObjects().FillReservation(data->deviceObjectId, device); + Known<WGPUDevice> reservation = FillReservation(); DAWN_ASSERT(reservation.data != nullptr); - reservation->info->server = this; - reservation->info->self = reservation.AsHandle(); SetForwardingDeviceCallbacks(reservation); - - SerializeCommand(cmd); } } // namespace dawn::wire::server
diff --git a/src/dawn/wire/server/ServerDevice.cpp b/src/dawn/wire/server/ServerDevice.cpp index 3d9fd40..f9fa66b 100644 --- a/src/dawn/wire/server/ServerDevice.cpp +++ b/src/dawn/wire/server/ServerDevice.cpp
@@ -56,9 +56,13 @@ SerializeCommand(cmd); } -void Server::OnDeviceLost(ObjectHandle device, WGPUDeviceLostReason reason, const char* message) { +void Server::OnDeviceLost(DeviceLostUserdata* userdata, + WGPUDevice const* device, + WGPUDeviceLostReason reason, + const char* message) { ReturnDeviceLostCallbackCmd cmd; - cmd.device = device; + cmd.eventManager = userdata->eventManager; + cmd.future = userdata->future; cmd.reason = reason; cmd.message = message;