[dawn][emscripten] Implements initial Future's API
- Implements the base features for Futures along with an implementation
for instance.RequestAdapter.
Bug: 358445329
Change-Id: I8b1a7e30b4be682fa5b9d80d51cf9df5b6bdd834
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/202176
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index f5d4926..ac3b0df 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -83,6 +83,23 @@
WebGPU._table[ptr] = value;
},
+ // Future to promise management, and temporary list allocated up-front for
+ // WaitAny implementation on the promises. Note that all FutureIDs
+ // (uint64_t) are passed either as a low and high value or by pointer
+ // because they need to be passed back and forth between JS and C++, and JS
+ // is currently unable to pass a value to a C++ function as a uint64_t.
+ // This might be possible with -sWASM_BIGINT, but I was unable to get that
+ // to work properly at the time of writing.
+ _futures: [],
+ _futureInsert: (futureIdL, futureIdH, promise) => {
+#if ASYNCIFY
+ var futureId = futureIdH * 0x100000000 + futureIdL;
+ WebGPU._futures[futureId] =
+ new Promise((resolve) => promise.finally(resolve(futureId)));
+#endif
+ },
+ _waitAnyPromisesList: [],
+
errorCallback: (callback, type, message, userdata) => {
var sp = stackSave();
var messagePtr = stringToUTF8OnStack(message);
@@ -452,13 +469,46 @@
},
// ----------------------------------------------------------------------------
- // Definitions for JS emwgpu functions (callable from webgpu.cpp)
+ // Definitions for standalone JS emwgpu functions (callable from webgpu.cpp)
// ----------------------------------------------------------------------------
emwgpuDelete: (id) => {
delete WebGPU._table[id];
},
+#if ASYNCIFY
+ // Returns a FutureID that was resolved, or kNullFutureId if timed out.
+ emwgpuWaitAny__async: true,
+ emwgpuWaitAny: (futurePtr, futureCount, timeoutNSPtr) => {
+ var promises = WebGPU._waitAnyPromisesList;
+ if (timeoutNSPtr) {
+ var timeoutMS = {{{ gpu.makeGetU64('timeoutNSPtr', 0) }}} / 1000000;
+ promises.length = futureCount + 1;
+ promise[futureCount] = new Promise((resolve) => setTimeout(resolve, timeoutMS, 0));
+ } else {
+ promises.length = futureCount;
+ }
+
+ for (var i = 0; i < futureCount; ++i) {
+ // If any of the FutureIDs are not tracked, it means it must be done.
+ var futureId = {{{ gpu.makeGetU64('(futurePtr + i * 8)', 0) }}};
+ if (!(futureId in WebGPU._futures)) {
+ return futureId;
+ }
+ promises[i] = WebGPU._futures[futureId];
+ }
+
+ var result = Asyncify.handleAsync(async () => {
+ return await Promise.race(promises);
+ });
+
+ // Clean up internal futures state.
+ delete WebGPU._futures[result];
+ WebGPU._waitAnyPromisesList.length = 0;
+ return result;
+ },
+#endif
+
// --------------------------------------------------------------------------
// WebGPU function definitions, with methods organized by "class".
//
@@ -1817,15 +1867,9 @@
return navigator["gpu"]["wgslLanguageFeatures"].has(WebGPU.WGSLFeatureName[featureEnumValue]);
},
- wgpuInstanceProcessEvents: (instance) => {
- // TODO: This could probably be emulated with ASYNCIFY.
-#if ASSERTIONS
- abort('wgpuInstanceProcessEvents is unsupported (use requestAnimationFrame via html5.h instead)');
-#endif
- },
-
- wgpuInstanceRequestAdapter__deps: ['$callUserCallback', '$stringToUTF8OnStack', 'emwgpuCreateAdapter'],
- wgpuInstanceRequestAdapter: (instancePtr, options, callback, userdata) => {
+ emwgpuInstanceRequestAdapter__i53abi: false,
+ emwgpuInstanceRequestAdapter__deps: ['$callUserCallback', '$stringToUTF8OnStack', 'emwgpuCreateAdapter', 'emwgpuOnRequestAdapterCompleted'],
+ emwgpuInstanceRequestAdapter: (instancePtr, futureIdL, futureIdH, options) => {
var opts;
if (options) {
{{{ gpu.makeCheckDescriptor('options') }}}
@@ -1838,37 +1882,35 @@
}
if (!('gpu' in navigator)) {
- var sp = stackSave();
- var messagePtr = stringToUTF8OnStack('WebGPU not available on this browser (navigator.gpu is not available)');
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestAdapterStatus.Unavailable }}}, 0, messagePtr, userdata);
- stackRestore(sp);
+ withStackSave(() => {
+ var messagePtr = stringToUTF8OnStack('WebGPU not available on this browser (navigator.gpu is not available)');
+ _emwgpuOnRequestAdapterCompleted(futureIdL, futureIdH, {{{ gpu.RequestAdapterStatus.Unavailable }}}, 0, messagePtr);
+ });
return;
}
{{{ runtimeKeepalivePush() }}}
- navigator["gpu"]["requestAdapter"](opts).then((adapter) => {
+ WebGPU._futureInsert(futureIdL, futureIdH, navigator["gpu"]["requestAdapter"](opts).then((adapter) => {
{{{ runtimeKeepalivePop() }}}
- callUserCallback(() => {
- if (adapter) {
- var adapterPtr = _emwgpuCreateAdapter();
- WebGPU._tableInsert(adapterPtr, adapter);
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestAdapterStatus.Success }}}, adapterPtr, 0, userdata);
- } else {
- var sp = stackSave();
- var messagePtr = stringToUTF8OnStack('WebGPU not available on this system (requestAdapter returned null)');
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestAdapterStatus.Unavailable }}}, 0, messagePtr, userdata);
- stackRestore(sp);
- }
- });
+ if (adapter) {
+ var adapterPtr = _emwgpuCreateAdapter();
+ WebGPU._tableInsert(adapterPtr, adapter);
+ _emwgpuOnRequestAdapterCompleted(futureIdL, futureIdH, {{{ gpu.RequestAdapterStatus.Success }}}, adapterPtr, 0);
+ } else {
+ withStackSave(() => {
+ var messagePtr = stringToUTF8OnStack('WebGPU not available on this browser (requestAdapter returned null)');
+ _emwgpuOnRequestAdapterCompleted(futureIdL, futureIdH, {{{ gpu.RequestAdapterStatus.Unavailable }}}, 0, messagePtr);
+ });
+ }
+ return;
}, (ex) => {
{{{ runtimeKeepalivePop() }}}
- callUserCallback(() => {
- var sp = stackSave();
+ withStackSave(() => {
var messagePtr = stringToUTF8OnStack(ex.message);
- {{{ makeDynCall('vippp', 'callback') }}}({{{ gpu.RequestAdapterStatus.Error }}}, 0, messagePtr, userdata);
- stackRestore(sp);
+ _emwgpuOnRequestAdapterCompleted(futureIdL, futureIdH, {{{ gpu.RequestAdapterStatus.Error }}}, 0, messagePtr);
});
- });
+ return;
+ }));
},
// --------------------------------------------------------------------------
@@ -2485,7 +2527,9 @@
for (const key of Object.keys(LibraryWebGPU)) {
if (typeof LibraryWebGPU[key] === 'function') {
- LibraryWebGPU[key + '__i53abi'] = true;
+ if (!(key + '__i53abi' in LibraryWebGPU)) {
+ LibraryWebGPU[key + '__i53abi'] = true;
+ }
}
}