[dawn][emscripten] Adds Future implementation for device creation.
- Adds EventSource to allow for passing through event handling.
- Implements RequestDevice and DeviceLost events together since
they are very intertwined.
- Adds handling for UncapturedError.
Bug: 358445329
Change-Id: Iea02cccafdb5dd1609498d6e131d7cfbe361045f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/203421
Reviewed-by: Shrek Shao <shrekshao@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/samples/SampleUtils.cpp b/src/dawn/samples/SampleUtils.cpp
index 400fba1..18cee4b 100644
--- a/src/dawn/samples/SampleUtils.cpp
+++ b/src/dawn/samples/SampleUtils.cpp
@@ -146,8 +146,6 @@
break;
}
- // TODO(crbug.com/42241221): Once the headers are more stable and implemented in Emscripten,
- // we could probably unify branched code below a bit more.
#ifndef __EMSCRIPTEN__
dawnProcSetProcs(&dawn::native::GetProcs());
@@ -171,6 +169,10 @@
instanceDescriptor.nextInChain = &toggles;
instanceDescriptor.features.timedWaitAnyEnable = true;
sample->instance = wgpu::CreateInstance(&instanceDescriptor);
+#else
+ // Create the instance
+ sample->instance = wgpu::CreateInstance(nullptr);
+#endif // __EMSCRIPTEN__
// Synchronously create the adapter
sample->instance.WaitAny(
@@ -254,6 +256,7 @@
return 1;
}
+#ifndef __EMSCRIPTEN__
if (!sample->Setup()) {
dawn::ErrorLog() << "Failed to perform sample setup";
return 1;
@@ -268,46 +271,11 @@
}
}
#else
- // Create the instance
- sample->instance = wgpu::CreateInstance(nullptr);
-
- // Synchronously create the adapter
- sample->instance.WaitAny(
- sample->instance.RequestAdapter(
- &adapterOptions, wgpu::CallbackMode::WaitAnyOnly,
- [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, const char* message) {
- if (status != wgpu::RequestAdapterStatus::Success) {
- dawn::ErrorLog() << "Failed to get an adapter:" << message;
- return;
- }
- sample->adapter = std::move(adapter);
- }),
- UINT64_MAX);
- if (sample->adapter == nullptr) {
- return 1;
+ if (sample->Setup()) {
+ emscripten_set_main_loop([]() { sample->FrameImpl(); }, 0, false);
+ } else {
+ dawn::ErrorLog() << "Failed to setup sample";
}
-
- // Create the device and set the emscripten loop via callbacks
- // TODO(crbug.com/42241221) Update to use the newer APIs once they are implemented in
- // Emscripten.
- wgpu::DeviceDescriptor deviceDesc = {};
- sample->adapter.RequestDevice(
- &deviceDesc,
- [](WGPURequestDeviceStatus status, WGPUDevice device, const char* message, void* userdata) {
- if (status != WGPURequestDeviceStatus_Success) {
- dawn::ErrorLog() << "Failed to get an device:" << message;
- return;
- }
- sample->device = wgpu::Device::Acquire(device);
- sample->queue = sample->device.GetQueue();
-
- if (sample->Setup()) {
- emscripten_set_main_loop([]() { sample->FrameImpl(); }, 0, false);
- } else {
- dawn::ErrorLog() << "Failed to setup sample";
- }
- },
- nullptr);
#endif // __EMSCRIPTEN__
return 0;
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index 0d05bbb..fb72053 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -480,13 +480,14 @@
// Extra helper that allow for directly inserting Devices (and their
// corresponding Queue) that is called from the HTML5 library since there
// isn't access to the C++ in webgpu.cpp there.
- emwgpuTableInsertDevice__deps: ['emwgpuCreateDevice', 'emwgpuCreateQueue'],
+ emwgpuTableInsertDevice__deps: ['emwgpuCreateDevice', 'emwgpuCreateQueue', 'wgpuCreateInstance'],
emwgpuTableInsertDevice: (device) => {
+ var instancePtr = _wgpuCreateInstance();
var queuePtr = _emwgpuCreateQueue();
WebGPU._tableInsert(queuePtr, device.queue);
- var devicePtr = _emwgpuCreateDevice(queuePtr);
+ var devicePtr = _emwgpuCreateDevice(instancePtr, queuePtr);
WebGPU._tableInsert(devicePtr, device);
- return devicePtr;
+ return { instancePtr, devicePtr };
},
#if ASYNCIFY
@@ -612,8 +613,14 @@
return adapter.features.has(WebGPU.FeatureName[featureEnumValue]);
},
- wgpuAdapterRequestDevice__deps: ['$callUserCallback', '$stringToUTF8OnStack', 'emwgpuCreateDevice', 'emwgpuCreateQueue'],
- wgpuAdapterRequestDevice: (adapterPtr, descriptor, callback, userdata) => {
+ emwgpuAdapterRequestDevice__i53abi: false,
+ emwgpuAdapterRequestDevice__deps: ['$callUserCallback', '$stringToUTF8OnStack', 'emwgpuCreateQueue', 'emwgpuOnDeviceLostCompleted', 'emwgpuOnRequestDeviceCompleted', 'emwgpuOnUncapturedError'],
+ emwgpuAdapterRequestDevice: (
+ adapterPtr,
+ futureIdL, futureIdH,
+ deviceLostFutureIdL, deviceLostFutureIdH,
+ devicePtr, queuePtr, descriptor
+ ) => {
var adapter = WebGPU._tableGet(adapterPtr);
var desc = {};
@@ -689,39 +696,57 @@
desc["defaultQueue"] = defaultQueueDesc;
}
- var deviceLostCallbackPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostCallbackInfo + C_STRUCTS.WGPUDeviceLostCallbackInfo.callback, '*') }}};
- var deviceLostUserdataPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.deviceLostCallbackInfo + C_STRUCTS.WGPUDeviceLostCallbackInfo.userdata, '*') }}};
-
var labelPtr = {{{ makeGetValue('descriptor', C_STRUCTS.WGPUDeviceDescriptor.label, '*') }}};
if (labelPtr) desc["label"] = UTF8ToString(labelPtr);
}
{{{ runtimeKeepalivePush() }}}
- adapter.requestDevice(desc).then((device) => {
+ var hasDeviceLostFutureId = !!deviceLostFutureIdH || !!deviceLostFutureIdL;
+ WebGPU._futureInsert(futureIdL, futureIdH, adapter.requestDevice(desc).then((device) => {
{{{ runtimeKeepalivePop() }}}
- callUserCallback(() => {
- var queuePtr = _emwgpuCreateQueue();
- WebGPU._tableInsert(queuePtr, device.queue);
+ WebGPU._tableInsert(queuePtr, device.queue);
+ WebGPU._tableInsert(devicePtr, device);
- var devicePtr = _emwgpuCreateDevice(queuePtr);
- WebGPU._tableInsert(devicePtr, device);
- if (deviceLostCallbackPtr) {
- device.lost.then((info) => {
- callUserCallback(() => WebGPU.errorCallback(deviceLostCallbackPtr,
- WebGPU.Int_DeviceLostReason[info.reason], info.message, deviceLostUserdataPtr));
+ // Set up device lost promise resolution.
+ if (hasDeviceLostFutureId) {
+ WebGPU._futureInsert(deviceLostFutureIdL, deviceLostFutureIdH, device.lost.then((info) => {
+ // Unset the uncaptured error handler.
+ device.onuncapturederror = (ev) => {};
+ withStackSave(() => {
+ var messagePtr = stringToUTF8OnStack(info.message);
+ _emwgpuOnDeviceLostCompleted(deviceLostFutureIdL, deviceLostFutureIdH, WebGPU.Int_DeviceLostReason[info.reason], messagePtr);
});
- }
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestDeviceStatus.Success }}}, devicePtr, 0, userdata);
- });
- }, function(ex) {
+ }));
+ }
+
+ // Set up uncaptured error handlers.
+#if ASSERTIONS
+ assert(typeof GPUValidationError != 'undefined');
+ assert(typeof GPUOutOfMemoryError != 'undefined');
+ assert(typeof GPUInternalError != 'undefined');
+#endif
+ device.onuncapturederror = (ev) => {
+ var type = {{{ gpu.ErrorType.Unknown }}};;
+ if (ev.error instanceof GPUValidationError) type = {{{ gpu.ErrorType.Validation }}};
+ else if (ev.error instanceof GPUOutOfMemoryError) type = {{{ gpu.ErrorType.OutOfMemory }}};
+ else if (ev.error instanceof GPUInternalError) type = {{{ gpu.ErrorType.Internal }}};
+ withStackSave(() => {
+ var messagePtr = stringToUTF8OnStack(ev.error.message);
+ _emwgpuOnUncapturedError(devicePtr, type, messagePtr);
+ });
+ };
+
+ _emwgpuOnRequestDeviceCompleted(futureIdL, futureIdH, {{{ gpu.RequestDeviceStatus.Success }}}, devicePtr, 0);
+ }, (ex) => {
{{{ runtimeKeepalivePop() }}}
- callUserCallback(() => {
- var sp = stackSave();
+ withStackSave(() => {
var messagePtr = stringToUTF8OnStack(ex.message);
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestDeviceStatus.Error }}}, 0, messagePtr, userdata);
- stackRestore(sp);
+ _emwgpuOnRequestDeviceCompleted(futureIdL, futureIdH, {{{ gpu.RequestDeviceStatus.Error }}}, devicePtr, messagePtr);
+ if (deviceLostFutureId) {
+ _emwgpuOnDeviceLostCompleted(deviceLostFutureIdL, deviceLostFutureIdH, {{{ gpu.DeviceLostReason.FailedCreation }}}, messagePtr);
+ }
});
- });
+ }));
},
// --------------------------------------------------------------------------
@@ -1814,6 +1839,7 @@
device.label = UTF8ToString(labelPtr);
},
+ // TODO(42241415) Remove this after verifying that it's not used and/or updating users.
wgpuDeviceSetUncapturedErrorCallback__deps: ['$callUserCallback'],
wgpuDeviceSetUncapturedErrorCallback: (devicePtr, callback, userdata) => {
var device = WebGPU._tableGet(devicePtr);
@@ -1906,7 +1932,7 @@
WebGPU._futureInsert(futureIdL, futureIdH, navigator["gpu"]["requestAdapter"](opts).then((adapter) => {
{{{ runtimeKeepalivePop() }}}
if (adapter) {
- var adapterPtr = _emwgpuCreateAdapter();
+ var adapterPtr = _emwgpuCreateAdapter(instancePtr);
WebGPU._tableInsert(adapterPtr, adapter);
_emwgpuOnRequestAdapterCompleted(futureIdL, futureIdH, {{{ gpu.RequestAdapterStatus.Success }}}, adapterPtr, 0);
} else {
diff --git a/third_party/emdawnwebgpu/webgpu.cpp b/third_party/emdawnwebgpu/webgpu.cpp
index d529881..f1c12c2 100644
--- a/third_party/emdawnwebgpu/webgpu.cpp
+++ b/third_party/emdawnwebgpu/webgpu.cpp
@@ -19,6 +19,7 @@
#include <mutex>
#include <optional>
#include <set>
+#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -40,6 +41,12 @@
uint64_t const* timeoutNSPtr);
// Future/async operation that need to be forwarded to JS.
+void emwgpuAdapterRequestDevice(WGPUAdapter adapter,
+ FutureID futureId,
+ FutureID deviceLostFutureId,
+ WGPUDevice device,
+ WGPUQueue queue,
+ const WGPUDeviceDescriptor* descriptor);
void emwgpuInstanceRequestAdapter(WGPUInstance instance,
FutureID futureId,
const WGPURequestAdapterOptions* options);
@@ -245,7 +252,6 @@
// X Macro to help generate boilerplate code for all passthrough object types.
// Passthrough objects refer to objects that are implemented via JS objects.
#define WGPU_PASSTHROUGH_OBJECTS(X) \
- X(Adapter) \
X(BindGroup) \
X(BindGroupLayout) \
X(Buffer) \
@@ -277,7 +283,9 @@
Shutdown,
};
enum class EventType {
+ DeviceLost,
RequestAdapter,
+ RequestDevice,
};
class EventManager;
@@ -302,6 +310,27 @@
bool mIsReady = false;
};
+// Compositable class for objects that provide entry point(s) that produce
+// Events, i.e. returns a Future.
+//
+// Note that while it would be nice to make it so that C++ entry points
+// implemented in here, and called from JS could use this abstraction in
+// signatures, pointers passed between JS and C++ in WASM do not cast properly
+// and results in undefined behavior. As an example, given:
+// (1) WGPUAdapter emwgpuCreateAdapter(const EventSource* source);
+// (2) WGPUAdapter emwgpuCreateAdapter(WGPUInstance instance);
+// I tried to use (1), but when calling from JS, the pointer is not correctly
+// adjusted so the value we end up getting when calling GetInstanceId() is some
+// garbage.
+class EventSource {
+ public:
+ explicit EventSource(InstanceID instanceId) : mInstanceId(instanceId) {}
+ InstanceID GetInstanceId() const { return mInstanceId; }
+
+ private:
+ const InstanceID mInstanceId = 0;
+};
+
// Thread-safe EventManager class that tracks all events.
//
// Note that there is a single global EventManager that should be accessed via
@@ -542,10 +571,102 @@
struct WGPU##Name##Impl final : public RefCounted {};
WGPU_PASSTHROUGH_OBJECTS(DEFINE_WGPU_DEFAULT_STRUCT)
+// Instance is specially implemented in order to handle Futures implementation.
+struct WGPUInstanceImpl final : public RefCounted, public EventSource {
+ public:
+ WGPUInstanceImpl();
+ ~WGPUInstanceImpl();
+
+ void ProcessEvents();
+ WGPUWaitStatus WaitAny(size_t count,
+ WGPUFutureWaitInfo* infos,
+ uint64_t timeoutNS);
+
+ private:
+ static InstanceID GetNextInstanceId();
+};
+
+struct WGPUAdapterImpl final : public RefCounted, public EventSource {
+ public:
+ WGPUAdapterImpl(const EventSource* source);
+};
+
+// Device is specially implemented in order to handle refcounting the Queue.
+struct WGPUDeviceImpl final : public RefCountedWithExternalCount,
+ public EventSource {
+ public:
+ // Reservation constructor used when calling RequestDevice.
+ WGPUDeviceImpl(const EventSource* source,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPUQueue queue);
+ // Injection constructor used when we already have a backing Device.
+ WGPUDeviceImpl(const EventSource* source, WGPUQueue queue);
+
+ WGPUQueue GetQueue() const;
+
+ void OnDeviceLost(WGPUDeviceLostReason reason, const char* message);
+ void OnUncapturedError(WGPUErrorType type, char const* message);
+
+ private:
+ void WillDropLastExternalRef() override;
+
+ Ref<WGPUQueue> mQueue;
+ WGPUUncapturedErrorCallbackInfo2 mUncapturedErrorCallbackInfo =
+ WGPU_UNCAPTURED_ERROR_CALLBACK_INFO_2_INIT;
+ FutureID mDeviceLostFutureId = kNullFutureId;
+};
+
// ----------------------------------------------------------------------------
// Future events.
// ----------------------------------------------------------------------------
+class DeviceLostEvent final : public TrackedEvent {
+ public:
+ static constexpr EventType kType = EventType::DeviceLost;
+
+ DeviceLostEvent(InstanceID instance,
+ WGPUDevice device,
+ const WGPUDeviceLostCallbackInfo2& callbackInfo)
+ : TrackedEvent(instance, callbackInfo.mode),
+ mCallback(callbackInfo.callback),
+ mUserdata1(callbackInfo.userdata1),
+ mUserdata2(callbackInfo.userdata2),
+ mDevice(device) {
+ assert(mDevice);
+ }
+
+ EventType GetType() override { return kType; }
+
+ void ReadyHook(WGPUDeviceLostReason reason, const char* message) {
+ mReason = reason;
+ mMessage = message;
+ }
+
+ void Complete(FutureID futureId, EventCompletionType type) override {
+ if (type == EventCompletionType::Shutdown) {
+ mReason = WGPUDeviceLostReason_InstanceDropped;
+ mMessage = "A valid external Instance reference no longer exists.";
+ }
+ if (mCallback) {
+ WGPUDevice device = mReason != WGPUDeviceLostReason_FailedCreation
+ ? mDevice.Get()
+ : nullptr;
+ mCallback(&device, mReason, mMessage ? mMessage->c_str() : nullptr,
+ mUserdata1, mUserdata2);
+ }
+ }
+
+ private:
+ WGPUDeviceLostCallback2 mCallback = nullptr;
+ void* mUserdata1 = nullptr;
+ void* mUserdata2 = nullptr;
+
+ Ref<WGPUDevice> mDevice;
+
+ WGPUDeviceLostReason mReason;
+ std::optional<std::string> mMessage;
+};
+
class RequestAdapterEvent final : public TrackedEvent {
public:
static constexpr EventType kType = EventType::RequestAdapter;
@@ -591,50 +712,136 @@
std::optional<std::string> mMessage = std::nullopt;
};
+class RequestDeviceEvent final : public TrackedEvent {
+ public:
+ static constexpr EventType kType = EventType::RequestDevice;
+
+ RequestDeviceEvent(InstanceID instance,
+ const WGPURequestDeviceCallbackInfo2& callbackInfo)
+ : TrackedEvent(instance, callbackInfo.mode),
+ mCallback(callbackInfo.callback),
+ mUserdata1(callbackInfo.userdata1),
+ mUserdata2(callbackInfo.userdata2) {}
+
+ EventType GetType() override { return kType; }
+
+ void ReadyHook(WGPURequestDeviceStatus status,
+ WGPUDevice device,
+ const char* message) {
+ mStatus = status;
+ mDevice.Acquire(device);
+ mMessage = message;
+ }
+
+ void Complete(FutureID futureId, EventCompletionType type) override {
+ if (type == EventCompletionType::Shutdown) {
+ mStatus = WGPURequestDeviceStatus_InstanceDropped;
+ mMessage = "A valid external Instance reference no longer exists.";
+ }
+ if (mCallback) {
+ mCallback(mStatus,
+ mStatus == WGPURequestDeviceStatus_Success
+ ? ReturnToAPI(std::move(mDevice))
+ : nullptr,
+ mMessage ? mMessage->c_str() : nullptr, mUserdata1, mUserdata2);
+ }
+ }
+
+ private:
+ WGPURequestDeviceCallback2 mCallback = nullptr;
+ void* mUserdata1 = nullptr;
+ void* mUserdata2 = nullptr;
+
+ WGPURequestDeviceStatus mStatus;
+ Ref<WGPUDevice> mDevice;
+ std::optional<std::string> mMessage = std::nullopt;
+};
+
// ----------------------------------------------------------------------------
// WGPU struct implementations.
// ----------------------------------------------------------------------------
-// Instance is specially implemented in order to handle Futures implementation.
-struct WGPUInstanceImpl : public RefCounted {
- public:
- WGPUInstanceImpl() {
- mId = GetNextInstanceId();
- GetEventManager().RegisterInstance(mId);
+// ----------------------------------------------------------------------------
+// WGPUAdapterImpl implementations.
+// ----------------------------------------------------------------------------
+
+WGPUAdapterImpl::WGPUAdapterImpl(const EventSource* source)
+ : EventSource(source->GetInstanceId()) {}
+
+// ----------------------------------------------------------------------------
+// WGPUInstanceImpl implementations.
+// ----------------------------------------------------------------------------
+
+WGPUInstanceImpl::WGPUInstanceImpl() : EventSource(GetNextInstanceId()) {
+ GetEventManager().RegisterInstance(GetInstanceId());
+}
+WGPUInstanceImpl::~WGPUInstanceImpl() {
+ GetEventManager().UnregisterInstance(GetInstanceId());
+}
+
+void WGPUInstanceImpl::ProcessEvents() {
+ GetEventManager().ProcessEvents(GetInstanceId());
+}
+
+WGPUWaitStatus WGPUInstanceImpl::WaitAny(size_t count,
+ WGPUFutureWaitInfo* infos,
+ uint64_t timeoutNS) {
+ return GetEventManager().WaitAny(GetInstanceId(), count, infos, timeoutNS);
+}
+
+InstanceID WGPUInstanceImpl::GetNextInstanceId() {
+ static std::atomic<InstanceID> kNextInstanceId = 1;
+ return kNextInstanceId++;
+}
+
+// ----------------------------------------------------------------------------
+// WGPUDeviceImpl implementations.
+// ----------------------------------------------------------------------------
+
+WGPUDeviceImpl::WGPUDeviceImpl(const EventSource* source,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPUQueue queue)
+ : EventSource(source->GetInstanceId()),
+ mUncapturedErrorCallbackInfo(descriptor->uncapturedErrorCallbackInfo2) {
+ // Create the DeviceLostEvent now.
+ std::tie(mDeviceLostFutureId, std::ignore) =
+ GetEventManager().TrackEvent(std::make_unique<DeviceLostEvent>(
+ source->GetInstanceId(), this, descriptor->deviceLostCallbackInfo2));
+ mQueue.Acquire(queue);
+}
+
+WGPUDeviceImpl::WGPUDeviceImpl(const EventSource* source, WGPUQueue queue)
+ : EventSource(source->GetInstanceId()) {
+ mQueue.Acquire(queue);
+}
+
+WGPUQueue WGPUDeviceImpl::GetQueue() const {
+ auto queue = mQueue;
+ return ReturnToAPI(std::move(queue));
+}
+
+void WGPUDeviceImpl::OnDeviceLost(WGPUDeviceLostReason reason,
+ const char* message) {
+ if (mDeviceLostFutureId != kNullFutureId) {
+ GetEventManager().SetFutureReady<DeviceLostEvent>(mDeviceLostFutureId,
+ reason, message);
}
- ~WGPUInstanceImpl() { GetEventManager().UnregisterInstance(mId); }
- InstanceID GetId() const { return mId; }
+ mDeviceLostFutureId = kNullFutureId;
+}
- void ProcessEvents() { GetEventManager().ProcessEvents(mId); }
-
- WGPUWaitStatus WaitAny(size_t count,
- WGPUFutureWaitInfo* infos,
- uint64_t timeoutNS) {
- return GetEventManager().WaitAny(mId, count, infos, timeoutNS);
+void WGPUDeviceImpl::OnUncapturedError(WGPUErrorType type,
+ char const* message) {
+ if (mUncapturedErrorCallbackInfo.callback) {
+ WGPUDeviceImpl* device = this;
+ mUncapturedErrorCallbackInfo.callback(
+ &device, type, message, mUncapturedErrorCallbackInfo.userdata1,
+ mUncapturedErrorCallbackInfo.userdata2);
}
+}
- private:
- static InstanceID GetNextInstanceId() {
- static std::atomic<InstanceID> kNextInstanceId = 1;
- return kNextInstanceId++;
- }
-
- InstanceID mId;
-};
-
-// Device is specially implemented in order to handle refcounting the Queue.
-struct WGPUDeviceImpl : public RefCounted {
- public:
- WGPUDeviceImpl(WGPUQueue queue) { mQueue.Acquire(queue); }
-
- WGPUQueue GetQueue() {
- auto queue = mQueue;
- return ReturnToAPI(std::move(queue));
- }
-
- private:
- Ref<WGPUQueue> mQueue;
-};
+void WGPUDeviceImpl::WillDropLastExternalRef() {
+ OnDeviceLost(WGPUDeviceLostReason_Destroyed, "Device was destroyed.");
+}
// ----------------------------------------------------------------------------
// Definitions for C++ emwgpu functions (callable from library_webgpu.js)
@@ -649,11 +856,20 @@
}
WGPU_PASSTHROUGH_OBJECTS(DEFINE_EMWGPU_DEFAULT_CREATE)
-WGPUDevice emwgpuCreateDevice(WGPUQueue queue) {
- return new WGPUDeviceImpl(queue);
+WGPUAdapter emwgpuCreateAdapter(WGPUInstance instance) {
+ return new WGPUAdapterImpl(instance);
+}
+
+WGPUDevice emwgpuCreateDevice(WGPUInstance instance, WGPUQueue queue) {
+ return new WGPUDeviceImpl(instance, queue);
}
// Future event callbacks.
+void emwgpuOnDeviceLostCompleted(FutureID futureId,
+ WGPUDeviceLostReason reason,
+ const char* message) {
+ GetEventManager().SetFutureReady<DeviceLostEvent>(futureId, reason, message);
+}
void emwgpuOnRequestAdapterCompleted(FutureID futureId,
WGPURequestAdapterStatus status,
WGPUAdapter adapter,
@@ -661,6 +877,33 @@
GetEventManager().SetFutureReady<RequestAdapterEvent>(futureId, status,
adapter, message);
}
+void emwgpuOnRequestDeviceCompleted(FutureID futureId,
+ WGPURequestDeviceStatus status,
+ WGPUDevice device,
+ const char* message) {
+ // This handler should always have a device since we pre-allocate it before
+ // calling out to JS.
+ assert(device);
+ if (status == WGPURequestDeviceStatus_Success) {
+ GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
+ device, message);
+ } else {
+ // If the request failed, we need to resolve the DeviceLostEvent.
+ device->OnDeviceLost(WGPUDeviceLostReason_FailedCreation,
+ "Device failed at creation.");
+ GetEventManager().SetFutureReady<RequestDeviceEvent>(futureId, status,
+ nullptr, message);
+ }
+}
+
+// Uncaptured error handler is similar to the Future event callbacks, but it
+// doesn't go through the EventManager and just calls the callback on the Device
+// immediately.
+void emwgpuOnUncapturedError(WGPUDevice device,
+ WGPUErrorType type,
+ char const* message) {
+ device->OnUncapturedError(type, message);
+}
} // extern "C"
@@ -710,6 +953,48 @@
// Methods of Adapter
// ----------------------------------------------------------------------------
+void wgpuAdapterRequestDevice(WGPUAdapter adapter,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPURequestDeviceCallback callback,
+ void* userdata) {
+ WGPURequestDeviceCallbackInfo2 callbackInfo = {};
+ callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
+ callbackInfo.callback = [](WGPURequestDeviceStatus status, WGPUDevice device,
+ char const* message, void* callback,
+ void* userdata) {
+ auto cb = reinterpret_cast<WGPURequestDeviceCallback>(callback);
+ cb(status, device, message, userdata);
+ };
+ callbackInfo.userdata1 = reinterpret_cast<void*>(callback);
+ callbackInfo.userdata2 = userdata;
+ wgpuAdapterRequestDevice2(adapter, descriptor, callbackInfo);
+}
+
+WGPUFuture wgpuAdapterRequestDevice2(
+ WGPUAdapter adapter,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPURequestDeviceCallbackInfo2 callbackInfo) {
+ auto [futureId, tracked] =
+ GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(
+ adapter->GetInstanceId(), callbackInfo));
+ if (!tracked) {
+ return WGPUFuture{kNullFutureId};
+ }
+
+ // For RequestDevice, we always create a Device and Queue up front. The
+ // Device is also immediately associated with the DeviceLostEvent.
+ WGPUQueue queue = new WGPUQueueImpl();
+ WGPUDevice device = new WGPUDeviceImpl(adapter, descriptor, queue);
+
+ auto [deviceLostFutureId, _] = GetEventManager().TrackEvent(
+ std::make_unique<DeviceLostEvent>(adapter->GetInstanceId(), device,
+ descriptor->deviceLostCallbackInfo2));
+
+ emwgpuAdapterRequestDevice(adapter, futureId, deviceLostFutureId, device,
+ queue, descriptor);
+ return WGPUFuture{futureId};
+}
+
// ----------------------------------------------------------------------------
// Methods of BindGroup
// ----------------------------------------------------------------------------
@@ -775,8 +1060,9 @@
WGPUInstance instance,
WGPURequestAdapterOptions const* options,
WGPURequestAdapterCallbackInfo2 callbackInfo) {
- auto [futureId, tracked] = GetEventManager().TrackEvent(
- std::make_unique<RequestAdapterEvent>(instance->GetId(), callbackInfo));
+ auto [futureId, tracked] =
+ GetEventManager().TrackEvent(std::make_unique<RequestAdapterEvent>(
+ instance->GetInstanceId(), callbackInfo));
if (!tracked) {
return WGPUFuture{kNullFutureId};
}