react-native-webgpu 0.5.13 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/android/CMakeLists.txt +3 -3
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/webgpu/WebGPUModule.java +1 -1
- package/apple/WebGPUModule.mm +2 -2
- package/cpp/jsi/NativeObject.h +39 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +8 -0
- package/cpp/rnwgpu/SurfaceRegistry.h +33 -1
- package/cpp/rnwgpu/api/GPU.cpp +14 -11
- package/cpp/rnwgpu/api/GPU.h +6 -4
- package/cpp/rnwgpu/api/GPUAdapter.cpp +5 -8
- package/cpp/rnwgpu/api/GPUAdapter.h +3 -3
- package/cpp/rnwgpu/api/GPUBuffer.cpp +23 -24
- package/cpp/rnwgpu/api/GPUBuffer.h +3 -3
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +13 -16
- package/cpp/rnwgpu/api/GPUCanvasContext.h +3 -0
- package/cpp/rnwgpu/api/GPUDevice.cpp +103 -19
- package/cpp/rnwgpu/api/GPUDevice.h +17 -3
- package/cpp/rnwgpu/api/GPUQueue.h +3 -3
- package/cpp/rnwgpu/api/GPUShaderModule.h +3 -3
- package/cpp/rnwgpu/api/GPUSharedFence.cpp +77 -0
- package/cpp/rnwgpu/api/GPUSharedFence.h +53 -0
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +60 -11
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +13 -9
- package/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +6 -0
- package/cpp/rnwgpu/api/descriptors/GPUSharedFenceDescriptor.h +58 -0
- package/cpp/rnwgpu/api/descriptors/GPUSharedFenceState.h +51 -0
- package/cpp/rnwgpu/async/AsyncTaskHandle.cpp +55 -23
- package/cpp/rnwgpu/async/AsyncTaskHandle.h +8 -5
- package/cpp/rnwgpu/async/RuntimeContext.cpp +193 -0
- package/cpp/rnwgpu/async/RuntimeContext.h +122 -0
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/Offscreen.js +1 -1
- package/lib/commonjs/WebPolyfillGPUModule.js +2 -0
- package/lib/commonjs/WebPolyfillGPUModule.js.map +1 -1
- package/lib/commonjs/constants.js +40 -0
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/index.js +22 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/install.js +63 -0
- package/lib/commonjs/install.js.map +1 -0
- package/lib/commonjs/main/index.js +1 -1
- package/lib/commonjs/main/index.js.map +1 -1
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/Offscreen.js +1 -1
- package/lib/module/WebPolyfillGPUModule.js +2 -0
- package/lib/module/WebPolyfillGPUModule.js.map +1 -1
- package/lib/module/constants.js +34 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/install.js +57 -0
- package/lib/module/install.js.map +1 -0
- package/lib/module/main/index.js +1 -1
- package/lib/module/main/index.js.map +1 -1
- package/lib/typescript/lib/commonjs/constants.d.ts +3 -0
- package/lib/typescript/lib/commonjs/constants.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/install.d.ts +35 -0
- package/lib/typescript/lib/commonjs/install.d.ts.map +1 -0
- package/lib/typescript/lib/module/constants.d.ts +6 -0
- package/lib/typescript/lib/module/constants.d.ts.map +1 -0
- package/lib/typescript/lib/module/index.d.ts +2 -0
- package/lib/typescript/lib/module/install.d.ts +2 -0
- package/lib/typescript/lib/module/install.d.ts.map +1 -0
- package/lib/typescript/src/Canvas.d.ts +9 -0
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/constants.d.ts +6 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/install.d.ts +34 -0
- package/lib/typescript/src/install.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +34 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/{react-native-wgpu.podspec → react-native-webgpu.podspec} +5 -1
- package/src/Canvas.tsx +9 -0
- package/src/Offscreen.ts +1 -1
- package/src/WebPolyfillGPUModule.ts +3 -3
- package/src/constants.ts +37 -0
- package/src/index.tsx +13 -2
- package/src/install.ts +61 -0
- package/src/main/index.tsx +1 -1
- package/src/types.ts +69 -3
- package/cpp/rnwgpu/async/AsyncDispatcher.h +0 -28
- package/cpp/rnwgpu/async/AsyncRunner.cpp +0 -215
- package/cpp/rnwgpu/async/AsyncRunner.h +0 -53
- package/cpp/rnwgpu/async/JSIMicrotaskDispatcher.cpp +0 -23
- package/cpp/rnwgpu/async/JSIMicrotaskDispatcher.h +0 -22
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#include <utility>
|
|
7
7
|
#include <vector>
|
|
8
8
|
|
|
9
|
+
#include <ReactCommon/CallInvoker.h>
|
|
10
|
+
|
|
9
11
|
#include "Convertors.h"
|
|
10
12
|
#include "JSIConverter.h"
|
|
11
13
|
|
|
@@ -19,23 +21,33 @@ namespace rnwgpu {
|
|
|
19
21
|
|
|
20
22
|
void GPUDevice::notifyDeviceLost(wgpu::DeviceLostReason reason,
|
|
21
23
|
std::string message) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
std::optional<async::AsyncTaskHandle::ResolveFunction> resolveToCall;
|
|
25
|
+
std::shared_ptr<GPUDeviceLostInfo> info;
|
|
26
|
+
{
|
|
27
|
+
std::lock_guard<std::mutex> lock(_lostMutex);
|
|
28
|
+
if (_lostSettled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
_lostSettled = true;
|
|
33
|
+
_lostInfo = std::make_shared<GPUDeviceLostInfo>(reason, std::move(message));
|
|
34
|
+
info = _lostInfo;
|
|
35
|
+
|
|
36
|
+
if (_lostResolve.has_value()) {
|
|
37
|
+
resolveToCall = std::move(*_lostResolve);
|
|
38
|
+
_lostResolve.reset();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_lostHandle.reset();
|
|
42
|
+
}
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
resolve([info = _lostInfo](jsi::Runtime &runtime) mutable {
|
|
44
|
+
// Settle outside the lock: resolve() only enqueues onto the JS thread.
|
|
45
|
+
if (resolveToCall.has_value()) {
|
|
46
|
+
(*resolveToCall)([info](jsi::Runtime &runtime) mutable {
|
|
33
47
|
return JSIConverter<std::shared_ptr<GPUDeviceLostInfo>>::toJSI(runtime,
|
|
34
48
|
info);
|
|
35
49
|
});
|
|
36
50
|
}
|
|
37
|
-
|
|
38
|
-
_lostHandle.reset();
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
void GPUDevice::forceLossForTesting() {
|
|
@@ -286,6 +298,55 @@ std::shared_ptr<GPUSharedTextureMemory> GPUDevice::importSharedTextureMemory(
|
|
|
286
298
|
std::move(label));
|
|
287
299
|
}
|
|
288
300
|
|
|
301
|
+
std::shared_ptr<GPUSharedFence> GPUDevice::importSharedFence(
|
|
302
|
+
std::shared_ptr<GPUSharedFenceDescriptor> descriptor) {
|
|
303
|
+
if (!descriptor || descriptor->handle == nullptr) {
|
|
304
|
+
throw std::runtime_error("GPUDevice::importSharedFence(): handle must be a "
|
|
305
|
+
"non-null native handle");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
wgpu::SharedFenceDescriptor desc{};
|
|
309
|
+
std::string label = descriptor->label.value_or("");
|
|
310
|
+
if (!label.empty()) {
|
|
311
|
+
desc.label = wgpu::StringView(label.c_str(), label.size());
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// The chained platform descriptor must outlive the synchronous
|
|
315
|
+
// ImportSharedFence() below; declare them all and chain the matching one.
|
|
316
|
+
wgpu::SharedFenceMTLSharedEventDescriptor mtlDesc{};
|
|
317
|
+
wgpu::SharedFenceSyncFDDescriptor syncFdDesc{};
|
|
318
|
+
wgpu::SharedFenceVkSemaphoreOpaqueFDDescriptor vkFdDesc{};
|
|
319
|
+
|
|
320
|
+
const std::string &type = descriptor->type;
|
|
321
|
+
if (type == "mtl-shared-event") {
|
|
322
|
+
// handle is an id<MTLSharedEvent> pointer.
|
|
323
|
+
mtlDesc.sharedEvent = descriptor->handle;
|
|
324
|
+
desc.nextInChain = &mtlDesc;
|
|
325
|
+
} else if (type == "sync-fd") {
|
|
326
|
+
// handle is an OS file descriptor.
|
|
327
|
+
syncFdDesc.handle =
|
|
328
|
+
static_cast<int>(reinterpret_cast<uintptr_t>(descriptor->handle));
|
|
329
|
+
desc.nextInChain = &syncFdDesc;
|
|
330
|
+
} else if (type == "vk-semaphore-opaque-fd") {
|
|
331
|
+
vkFdDesc.handle =
|
|
332
|
+
static_cast<int>(reinterpret_cast<uintptr_t>(descriptor->handle));
|
|
333
|
+
desc.nextInChain = &vkFdDesc;
|
|
334
|
+
} else {
|
|
335
|
+
throw std::runtime_error(
|
|
336
|
+
"GPUDevice::importSharedFence(): unsupported fence type '" + type +
|
|
337
|
+
"' (expected 'mtl-shared-event', 'sync-fd' or "
|
|
338
|
+
"'vk-semaphore-opaque-fd')");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
auto fence = _instance.ImportSharedFence(&desc);
|
|
342
|
+
if (fence == nullptr) {
|
|
343
|
+
throw std::runtime_error(
|
|
344
|
+
"GPUDevice::importSharedFence(): ImportSharedFence returned null - is "
|
|
345
|
+
"the matching 'shared-fence-*' feature enabled on the device?");
|
|
346
|
+
}
|
|
347
|
+
return std::make_shared<GPUSharedFence>(std::move(fence), std::move(label));
|
|
348
|
+
}
|
|
349
|
+
|
|
289
350
|
async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
290
351
|
std::shared_ptr<GPUComputePipelineDescriptor> descriptor) {
|
|
291
352
|
wgpu::ComputePipelineDescriptor desc{};
|
|
@@ -318,9 +379,9 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
318
379
|
runtime, pipelineHolder);
|
|
319
380
|
});
|
|
320
381
|
} else {
|
|
321
|
-
std::string error =
|
|
322
|
-
|
|
323
|
-
|
|
382
|
+
std::string error = msg.length
|
|
383
|
+
? std::string(msg.data, msg.length)
|
|
384
|
+
: "Failed to create compute pipeline";
|
|
324
385
|
reject(std::move(error));
|
|
325
386
|
}
|
|
326
387
|
});
|
|
@@ -360,9 +421,8 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
360
421
|
runtime, pipelineHolder);
|
|
361
422
|
});
|
|
362
423
|
} else {
|
|
363
|
-
std::string error =
|
|
364
|
-
|
|
365
|
-
: "Failed to create render pipeline";
|
|
424
|
+
std::string error = msg.length ? std::string(msg.data, msg.length)
|
|
425
|
+
: "Failed to create render pipeline";
|
|
366
426
|
reject(std::move(error));
|
|
367
427
|
}
|
|
368
428
|
});
|
|
@@ -449,6 +509,11 @@ std::unordered_set<std::string> GPUDevice::getFeatures() {
|
|
|
449
509
|
}
|
|
450
510
|
|
|
451
511
|
async::AsyncTaskHandle GPUDevice::getLost() {
|
|
512
|
+
// Held across the whole body: the postTask callback below runs synchronously
|
|
513
|
+
// on this (JS) thread and touches the same _lost* fields, so it must not
|
|
514
|
+
// re-lock. notifyDeviceLost() takes the same lock from its (possibly worker)
|
|
515
|
+
// thread.
|
|
516
|
+
std::lock_guard<std::mutex> lock(_lostMutex);
|
|
452
517
|
if (_lostHandle.has_value()) {
|
|
453
518
|
return *_lostHandle;
|
|
454
519
|
}
|
|
@@ -463,7 +528,7 @@ async::AsyncTaskHandle GPUDevice::getLost() {
|
|
|
463
528
|
runtime, info);
|
|
464
529
|
});
|
|
465
530
|
},
|
|
466
|
-
false);
|
|
531
|
+
/*keepPumping=*/false);
|
|
467
532
|
}
|
|
468
533
|
|
|
469
534
|
auto handle = _async->postTask(
|
|
@@ -477,9 +542,10 @@ async::AsyncTaskHandle GPUDevice::getLost() {
|
|
|
477
542
|
return;
|
|
478
543
|
}
|
|
479
544
|
|
|
545
|
+
// Resolved later from notifyDeviceLost().
|
|
480
546
|
_lostResolve = resolve;
|
|
481
547
|
},
|
|
482
|
-
false);
|
|
548
|
+
/*keepPumping=*/false);
|
|
483
549
|
|
|
484
550
|
_lostHandle = handle;
|
|
485
551
|
return handle;
|
|
@@ -499,6 +565,24 @@ void GPUDevice::removeEventListener(std::string type, jsi::Function callback) {
|
|
|
499
565
|
|
|
500
566
|
void GPUDevice::notifyUncapturedError(wgpu::ErrorType type,
|
|
501
567
|
std::string message) {
|
|
568
|
+
// Dawn can surface an uncaptured error from any ProcessEvents pump (a worklet
|
|
569
|
+
// runtime sharing this instance may pump it on the wrong thread). Marshal to
|
|
570
|
+
// the owning runtime's JS thread via its CallInvoker before touching JSI. The
|
|
571
|
+
// invoker is wired only for the main JS runtime, so a device created on a
|
|
572
|
+
// worklet runtime does not deliver uncaptured errors to JS (best-effort; see
|
|
573
|
+
// README "Threading model").
|
|
574
|
+
auto invoker = _async ? _async->callInvoker() : nullptr;
|
|
575
|
+
if (!invoker) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
auto self = shared_from_this();
|
|
579
|
+
invoker->invokeAsync([self, type, message = std::move(message)]() mutable {
|
|
580
|
+
self->deliverUncapturedError(type, std::move(message));
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
void GPUDevice::deliverUncapturedError(wgpu::ErrorType type,
|
|
585
|
+
std::string message) {
|
|
502
586
|
auto it = _eventListeners.find("uncapturederror");
|
|
503
587
|
if (it == _eventListeners.end() || it->second.empty()) {
|
|
504
588
|
return;
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
#include "NativeObject.h"
|
|
17
17
|
|
|
18
|
-
#include "rnwgpu/async/AsyncRunner.h"
|
|
19
18
|
#include "rnwgpu/async/AsyncTaskHandle.h"
|
|
19
|
+
#include "rnwgpu/async/RuntimeContext.h"
|
|
20
20
|
|
|
21
21
|
#include "webgpu/webgpu_cpp.h"
|
|
22
22
|
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
#include "GPURenderPipelineDescriptor.h"
|
|
46
46
|
#include "GPUSampler.h"
|
|
47
47
|
#include "GPUSamplerDescriptor.h"
|
|
48
|
+
#include "GPUSharedFenceDescriptor.h"
|
|
48
49
|
#include "GPUSharedTextureMemory.h"
|
|
49
50
|
#include "GPUSharedTextureMemoryDescriptor.h"
|
|
50
51
|
#include "GPUShaderModule.h"
|
|
@@ -63,7 +64,7 @@ public:
|
|
|
63
64
|
static constexpr const char *CLASS_NAME = "GPUDevice";
|
|
64
65
|
|
|
65
66
|
explicit GPUDevice(wgpu::Device instance,
|
|
66
|
-
std::shared_ptr<async::
|
|
67
|
+
std::shared_ptr<async::RuntimeContext> async,
|
|
67
68
|
std::string label)
|
|
68
69
|
: NativeObject(CLASS_NAME), _instance(instance), _async(async),
|
|
69
70
|
_label(label) {}
|
|
@@ -120,6 +121,8 @@ public:
|
|
|
120
121
|
std::shared_ptr<GPUExternalTextureDescriptor> descriptor);
|
|
121
122
|
std::shared_ptr<GPUSharedTextureMemory> importSharedTextureMemory(
|
|
122
123
|
std::shared_ptr<GPUSharedTextureMemoryDescriptor> descriptor);
|
|
124
|
+
std::shared_ptr<GPUSharedFence> importSharedFence(
|
|
125
|
+
std::shared_ptr<GPUSharedFenceDescriptor> descriptor);
|
|
123
126
|
std::shared_ptr<GPUBindGroupLayout> createBindGroupLayout(
|
|
124
127
|
std::shared_ptr<GPUBindGroupLayoutDescriptor> descriptor);
|
|
125
128
|
std::shared_ptr<GPUPipelineLayout>
|
|
@@ -175,6 +178,8 @@ public:
|
|
|
175
178
|
&GPUDevice::importExternalTexture);
|
|
176
179
|
installMethod(runtime, prototype, "importSharedTextureMemory",
|
|
177
180
|
&GPUDevice::importSharedTextureMemory);
|
|
181
|
+
installMethod(runtime, prototype, "importSharedFence",
|
|
182
|
+
&GPUDevice::importSharedFence);
|
|
178
183
|
installMethod(runtime, prototype, "createBindGroupLayout",
|
|
179
184
|
&GPUDevice::createBindGroupLayout);
|
|
180
185
|
installMethod(runtime, prototype, "createPipelineLayout",
|
|
@@ -248,9 +253,18 @@ public:
|
|
|
248
253
|
private:
|
|
249
254
|
friend class GPUAdapter;
|
|
250
255
|
|
|
256
|
+
// Runs the uncapturederror listeners on the creation runtime's JS thread.
|
|
257
|
+
// Invoked from notifyUncapturedError via the main CallInvoker.
|
|
258
|
+
void deliverUncapturedError(wgpu::ErrorType type, std::string message);
|
|
259
|
+
|
|
251
260
|
wgpu::Device _instance;
|
|
252
|
-
std::shared_ptr<async::
|
|
261
|
+
std::shared_ptr<async::RuntimeContext> _async;
|
|
253
262
|
std::string _label;
|
|
263
|
+
// Guards the device-lost state below. In the ProcessEvents model both
|
|
264
|
+
// notifyDeviceLost() (fired by Dawn during ProcessEvents) and getLost() run on
|
|
265
|
+
// the owning runtime's own thread, but device destruction can also trigger
|
|
266
|
+
// notifyDeviceLost() synchronously, so the mutex keeps these fields safe.
|
|
267
|
+
std::mutex _lostMutex;
|
|
254
268
|
std::optional<async::AsyncTaskHandle> _lostHandle;
|
|
255
269
|
std::shared_ptr<GPUDeviceLostInfo> _lostInfo;
|
|
256
270
|
bool _lostSettled = false;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
#include "NativeObject.h"
|
|
10
10
|
|
|
11
|
-
#include "rnwgpu/async/AsyncRunner.h"
|
|
12
11
|
#include "rnwgpu/async/AsyncTaskHandle.h"
|
|
12
|
+
#include "rnwgpu/async/RuntimeContext.h"
|
|
13
13
|
|
|
14
14
|
#include "webgpu/webgpu_cpp.h"
|
|
15
15
|
|
|
@@ -28,7 +28,7 @@ public:
|
|
|
28
28
|
static constexpr const char *CLASS_NAME = "GPUQueue";
|
|
29
29
|
|
|
30
30
|
explicit GPUQueue(wgpu::Queue instance,
|
|
31
|
-
std::shared_ptr<async::
|
|
31
|
+
std::shared_ptr<async::RuntimeContext> async,
|
|
32
32
|
std::string label)
|
|
33
33
|
: NativeObject(CLASS_NAME), _instance(instance), _async(async),
|
|
34
34
|
_label(label) {}
|
|
@@ -74,7 +74,7 @@ public:
|
|
|
74
74
|
|
|
75
75
|
private:
|
|
76
76
|
wgpu::Queue _instance;
|
|
77
|
-
std::shared_ptr<async::
|
|
77
|
+
std::shared_ptr<async::RuntimeContext> _async;
|
|
78
78
|
std::string _label;
|
|
79
79
|
};
|
|
80
80
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
#include "NativeObject.h"
|
|
9
9
|
|
|
10
|
-
#include "rnwgpu/async/AsyncRunner.h"
|
|
11
10
|
#include "rnwgpu/async/AsyncTaskHandle.h"
|
|
11
|
+
#include "rnwgpu/async/RuntimeContext.h"
|
|
12
12
|
|
|
13
13
|
#include "webgpu/webgpu_cpp.h"
|
|
14
14
|
|
|
@@ -23,7 +23,7 @@ public:
|
|
|
23
23
|
static constexpr const char *CLASS_NAME = "GPUShaderModule";
|
|
24
24
|
|
|
25
25
|
explicit GPUShaderModule(wgpu::ShaderModule instance,
|
|
26
|
-
std::shared_ptr<async::
|
|
26
|
+
std::shared_ptr<async::RuntimeContext> async,
|
|
27
27
|
std::string label)
|
|
28
28
|
: NativeObject(CLASS_NAME), _instance(instance), _async(async),
|
|
29
29
|
_label(label) {}
|
|
@@ -59,7 +59,7 @@ public:
|
|
|
59
59
|
|
|
60
60
|
private:
|
|
61
61
|
wgpu::ShaderModule _instance;
|
|
62
|
-
std::shared_ptr<async::
|
|
62
|
+
std::shared_ptr<async::RuntimeContext> _async;
|
|
63
63
|
std::string _label;
|
|
64
64
|
};
|
|
65
65
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#include "GPUSharedFence.h"
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
#if defined(__ANDROID__)
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
namespace rnwgpu {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
// Kebab-case names matching the shared-fence-* feature strings (see Unions.h /
|
|
15
|
+
// GPUFeatures.h).
|
|
16
|
+
std::string sharedFenceTypeToString(wgpu::SharedFenceType type) {
|
|
17
|
+
switch (type) {
|
|
18
|
+
case wgpu::SharedFenceType::MTLSharedEvent:
|
|
19
|
+
return "mtl-shared-event";
|
|
20
|
+
case wgpu::SharedFenceType::SyncFD:
|
|
21
|
+
return "sync-fd";
|
|
22
|
+
case wgpu::SharedFenceType::VkSemaphoreOpaqueFD:
|
|
23
|
+
return "vk-semaphore-opaque-fd";
|
|
24
|
+
case wgpu::SharedFenceType::VkSemaphoreZirconHandle:
|
|
25
|
+
return "vk-semaphore-zircon-handle";
|
|
26
|
+
case wgpu::SharedFenceType::DXGISharedHandle:
|
|
27
|
+
return "dxgi-shared-handle";
|
|
28
|
+
case wgpu::SharedFenceType::EGLSync:
|
|
29
|
+
return "egl-sync";
|
|
30
|
+
default:
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
} // namespace
|
|
36
|
+
|
|
37
|
+
jsi::Value GPUSharedFence::exportInfo(jsi::Runtime &runtime, const jsi::Value &,
|
|
38
|
+
const jsi::Value *, size_t) {
|
|
39
|
+
wgpu::SharedFenceExportInfo info{};
|
|
40
|
+
uint64_t handle = 0;
|
|
41
|
+
|
|
42
|
+
#if defined(__APPLE__)
|
|
43
|
+
// Apple: the handle is an id<MTLSharedEvent> pointer.
|
|
44
|
+
wgpu::SharedFenceMTLSharedEventExportInfo mtlInfo{};
|
|
45
|
+
info.nextInChain = &mtlInfo;
|
|
46
|
+
_instance.ExportInfo(&info);
|
|
47
|
+
handle = reinterpret_cast<uint64_t>(mtlInfo.sharedEvent);
|
|
48
|
+
#elif defined(__ANDROID__)
|
|
49
|
+
// Android: the handle is an OS file descriptor (sync_fd). Dawn's ExportInfo returns a BORROWED fd — it is
|
|
50
|
+
// owned by the SharedFence and closed when the fence is destroyed. This exported handle is documented as
|
|
51
|
+
// caller-owned (the caller must close() it), so dup() it. Without the dup the same fd is closed twice —
|
|
52
|
+
// once by the caller and once by Dawn on fence destruction — tripping Android's fdsan (double-close abort).
|
|
53
|
+
wgpu::SharedFenceSyncFDExportInfo fdInfo{};
|
|
54
|
+
info.nextInChain = &fdInfo;
|
|
55
|
+
_instance.ExportInfo(&info);
|
|
56
|
+
int exportedFd =
|
|
57
|
+
fdInfo.handle >= 0 ? ::fcntl(fdInfo.handle, F_DUPFD_CLOEXEC, 0) : fdInfo.handle;
|
|
58
|
+
handle = static_cast<uint64_t>(static_cast<uint32_t>(exportedFd));
|
|
59
|
+
#else
|
|
60
|
+
// react-native-webgpu only targets Apple (Metal) and Android (Vulkan). On any
|
|
61
|
+
// other platform there is no native handle convention to expose, so fail loudly
|
|
62
|
+
// rather than handing back a meaningless handle of 0.
|
|
63
|
+
throw jsi::JSError(runtime,
|
|
64
|
+
"GPUSharedFence::export(): unsupported platform (only "
|
|
65
|
+
"Apple/Metal and Android/Vulkan are supported)");
|
|
66
|
+
#endif
|
|
67
|
+
|
|
68
|
+
jsi::Object result(runtime);
|
|
69
|
+
result.setProperty(
|
|
70
|
+
runtime, "type",
|
|
71
|
+
jsi::String::createFromUtf8(runtime, sharedFenceTypeToString(info.type)));
|
|
72
|
+
result.setProperty(runtime, "handle",
|
|
73
|
+
jsi::BigInt::fromUint64(runtime, handle));
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} // namespace rnwgpu
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
#include "NativeObject.h"
|
|
7
|
+
|
|
8
|
+
#include "webgpu/webgpu_cpp.h"
|
|
9
|
+
|
|
10
|
+
namespace rnwgpu {
|
|
11
|
+
|
|
12
|
+
namespace jsi = facebook::jsi;
|
|
13
|
+
|
|
14
|
+
// Wraps a wgpu::SharedFence: a native GPU sync primitive (id<MTLSharedEvent> on
|
|
15
|
+
// Apple, sync-fd / VkSemaphore on Android).
|
|
16
|
+
class GPUSharedFence : public NativeObject<GPUSharedFence> {
|
|
17
|
+
public:
|
|
18
|
+
static constexpr const char *CLASS_NAME = "GPUSharedFence";
|
|
19
|
+
|
|
20
|
+
explicit GPUSharedFence(wgpu::SharedFence instance, std::string label)
|
|
21
|
+
: NativeObject(CLASS_NAME), _instance(std::move(instance)),
|
|
22
|
+
_label(std::move(label)) {}
|
|
23
|
+
|
|
24
|
+
public:
|
|
25
|
+
std::string getBrand() { return CLASS_NAME; }
|
|
26
|
+
|
|
27
|
+
// export() -> { type, handle }: exposes the native handle (as a BigInt) so
|
|
28
|
+
// app code can wait on or signal the fence. The caller owns the returned
|
|
29
|
+
// handle (e.g. an exported sync-fd must be close()d).
|
|
30
|
+
jsi::Value exportInfo(jsi::Runtime &runtime, const jsi::Value &thisVal,
|
|
31
|
+
const jsi::Value *args, size_t count);
|
|
32
|
+
|
|
33
|
+
std::string getLabel() { return _label; }
|
|
34
|
+
void setLabel(const std::string &label) {
|
|
35
|
+
_label = label;
|
|
36
|
+
_instance.SetLabel(_label.c_str());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
40
|
+
installGetter(runtime, prototype, "__brand", &GPUSharedFence::getBrand);
|
|
41
|
+
installMethod(runtime, prototype, "export", &GPUSharedFence::exportInfo);
|
|
42
|
+
installGetterSetter(runtime, prototype, "label", &GPUSharedFence::getLabel,
|
|
43
|
+
&GPUSharedFence::setLabel);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
inline wgpu::SharedFence get() { return _instance; }
|
|
47
|
+
|
|
48
|
+
private:
|
|
49
|
+
wgpu::SharedFence _instance;
|
|
50
|
+
std::string _label;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
} // namespace rnwgpu
|
|
@@ -27,8 +27,9 @@ std::shared_ptr<GPUTexture> GPUSharedTextureMemory::createTexture(
|
|
|
27
27
|
descriptor.value()->label.value_or(""));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
void GPUSharedTextureMemory::beginAccess(
|
|
31
|
+
std::shared_ptr<GPUTexture> texture, bool initialized,
|
|
32
|
+
std::optional<std::vector<std::shared_ptr<GPUSharedFenceState>>> fences) {
|
|
32
33
|
if (!texture) {
|
|
33
34
|
throw std::runtime_error(
|
|
34
35
|
"GPUSharedTextureMemory::beginAccess(): texture is null");
|
|
@@ -36,9 +37,28 @@ bool GPUSharedTextureMemory::beginAccess(std::shared_ptr<GPUTexture> texture,
|
|
|
36
37
|
wgpu::SharedTextureMemoryBeginAccessDescriptor desc{};
|
|
37
38
|
desc.initialized = initialized;
|
|
38
39
|
desc.concurrentRead = false;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
// Built in lockstep so fenceCount covers both arrays, and kept in locals so
|
|
42
|
+
// the raw pointers outlive the synchronous BeginAccess() below.
|
|
43
|
+
std::vector<wgpu::SharedFence> rawFences;
|
|
44
|
+
std::vector<uint64_t> values;
|
|
45
|
+
if (fences.has_value()) {
|
|
46
|
+
for (const auto &state : *fences) {
|
|
47
|
+
if (state && state->fence) {
|
|
48
|
+
rawFences.push_back(state->fence->get());
|
|
49
|
+
values.push_back(state->signaledValue);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!rawFences.empty()) {
|
|
54
|
+
desc.fenceCount = rawFences.size();
|
|
55
|
+
desc.fences = rawFences.data();
|
|
56
|
+
desc.signaledValues = values.data();
|
|
57
|
+
} else {
|
|
58
|
+
desc.fenceCount = 0;
|
|
59
|
+
desc.fences = nullptr;
|
|
60
|
+
desc.signaledValues = nullptr;
|
|
61
|
+
}
|
|
42
62
|
|
|
43
63
|
#if defined(__ANDROID__)
|
|
44
64
|
// Dawn's Vulkan backend (AHardwareBuffer) validates that the begin-access
|
|
@@ -55,14 +75,21 @@ bool GPUSharedTextureMemory::beginAccess(std::shared_ptr<GPUTexture> texture,
|
|
|
55
75
|
#endif
|
|
56
76
|
|
|
57
77
|
auto status = _instance.BeginAccess(texture->get(), &desc);
|
|
58
|
-
|
|
78
|
+
if (!status) {
|
|
79
|
+
throw std::runtime_error("GPUSharedTextureMemory::beginAccess() failed");
|
|
80
|
+
}
|
|
59
81
|
}
|
|
60
82
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
jsi::Value GPUSharedTextureMemory::endAccess(jsi::Runtime &runtime,
|
|
84
|
+
const jsi::Value &,
|
|
85
|
+
const jsi::Value *args,
|
|
86
|
+
size_t count) {
|
|
87
|
+
if (count < 1 || !args[0].isObject()) {
|
|
88
|
+
throw jsi::JSError(
|
|
89
|
+
runtime, "GPUSharedTextureMemory::endAccess(): expected (texture)");
|
|
65
90
|
}
|
|
91
|
+
auto texture = GPUTexture::fromValue(runtime, args[0]);
|
|
92
|
+
|
|
66
93
|
wgpu::SharedTextureMemoryEndAccessState state{};
|
|
67
94
|
|
|
68
95
|
#if defined(__ANDROID__)
|
|
@@ -74,7 +101,29 @@ bool GPUSharedTextureMemory::endAccess(std::shared_ptr<GPUTexture> texture) {
|
|
|
74
101
|
#endif
|
|
75
102
|
|
|
76
103
|
auto status = _instance.EndAccess(texture->get(), &state);
|
|
77
|
-
|
|
104
|
+
if (!status) {
|
|
105
|
+
throw jsi::JSError(runtime, "GPUSharedTextureMemory::endAccess() failed");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Copy each wgpu::SharedFence (ref-counted) into its own GPUSharedFence
|
|
109
|
+
// wrapper before `state` is destroyed.
|
|
110
|
+
jsi::Array fences(runtime, state.fenceCount);
|
|
111
|
+
for (size_t i = 0; i < state.fenceCount; i++) {
|
|
112
|
+
wgpu::SharedFence fence = state.fences[i];
|
|
113
|
+
auto wrapper = std::make_shared<GPUSharedFence>(std::move(fence), "");
|
|
114
|
+
jsi::Object entry(runtime);
|
|
115
|
+
entry.setProperty(runtime, "fence",
|
|
116
|
+
GPUSharedFence::create(runtime, std::move(wrapper)));
|
|
117
|
+
entry.setProperty(runtime, "signaledValue",
|
|
118
|
+
jsi::BigInt::fromUint64(runtime, state.signaledValues[i]));
|
|
119
|
+
fences.setValueAtIndex(runtime, i, std::move(entry));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
jsi::Object result(runtime);
|
|
123
|
+
result.setProperty(runtime, "initialized",
|
|
124
|
+
jsi::Value(static_cast<bool>(state.initialized)));
|
|
125
|
+
result.setProperty(runtime, "fences", std::move(fences));
|
|
126
|
+
return result;
|
|
78
127
|
}
|
|
79
128
|
|
|
80
129
|
} // namespace rnwgpu
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <cstdint>
|
|
3
4
|
#include <memory>
|
|
4
5
|
#include <optional>
|
|
5
6
|
#include <string>
|
|
7
|
+
#include <vector>
|
|
6
8
|
|
|
7
9
|
#include "NativeObject.h"
|
|
8
10
|
|
|
9
11
|
#include "webgpu/webgpu_cpp.h"
|
|
10
12
|
|
|
13
|
+
#include "GPUSharedFence.h"
|
|
14
|
+
#include "GPUSharedFenceState.h"
|
|
11
15
|
#include "GPUTexture.h"
|
|
12
16
|
#include "GPUTextureDescriptor.h"
|
|
13
17
|
|
|
@@ -30,16 +34,16 @@ public:
|
|
|
30
34
|
std::shared_ptr<GPUTexture>
|
|
31
35
|
createTexture(std::optional<std::shared_ptr<GPUTextureDescriptor>> descriptor);
|
|
32
36
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
bool beginAccess(std::shared_ptr<GPUTexture> texture, bool initialized);
|
|
37
|
+
// Optional `fences` are wait fences: Dawn waits for each to reach its
|
|
38
|
+
// signaledValue before writing the surface. Throws on failure.
|
|
39
|
+
void beginAccess(
|
|
40
|
+
std::shared_ptr<GPUTexture> texture, bool initialized,
|
|
41
|
+
std::optional<std::vector<std::shared_ptr<GPUSharedFenceState>>> fences);
|
|
39
42
|
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
+
// endAccess(texture) -> { initialized, fences: { fence, signaledValue }[] }
|
|
44
|
+
// Surfaces the fences Dawn produced for the access. Throws on failure.
|
|
45
|
+
jsi::Value endAccess(jsi::Runtime &runtime, const jsi::Value &thisVal,
|
|
46
|
+
const jsi::Value *args, size_t count);
|
|
43
47
|
|
|
44
48
|
std::string getLabel() { return _label; }
|
|
45
49
|
void setLabel(const std::string &label) {
|
|
@@ -102,6 +102,12 @@ template <> struct JSIConverter<std::shared_ptr<rnwgpu::GPUDeviceDescriptor>> {
|
|
|
102
102
|
result->label = JSIConverter<std::optional<std::string>>::fromJSI(
|
|
103
103
|
runtime, prop, false);
|
|
104
104
|
}
|
|
105
|
+
if (value.hasProperty(runtime, "dawnToggles")) {
|
|
106
|
+
auto prop = value.getProperty(runtime, "dawnToggles");
|
|
107
|
+
result->dawnToggles = JSIConverter<
|
|
108
|
+
std::optional<std::shared_ptr<GPUDawnTogglesDescriptor>>>::fromJSI(
|
|
109
|
+
runtime, prop, false);
|
|
110
|
+
}
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
return result;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
#include "webgpu/webgpu_cpp.h"
|
|
8
|
+
|
|
9
|
+
#include "JSIConverter.h"
|
|
10
|
+
|
|
11
|
+
namespace jsi = facebook::jsi;
|
|
12
|
+
|
|
13
|
+
namespace rnwgpu {
|
|
14
|
+
|
|
15
|
+
// Descriptor for GPUDevice.importSharedFence. `handle` is the native handle
|
|
16
|
+
// (an id<MTLSharedEvent> pointer on Apple, an OS file descriptor on Android),
|
|
17
|
+
// passed from JS as a BigInt.
|
|
18
|
+
struct GPUSharedFenceDescriptor {
|
|
19
|
+
std::string type;
|
|
20
|
+
void *handle = nullptr;
|
|
21
|
+
std::optional<std::string> label;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace rnwgpu
|
|
25
|
+
|
|
26
|
+
namespace rnwgpu {
|
|
27
|
+
|
|
28
|
+
template <>
|
|
29
|
+
struct JSIConverter<std::shared_ptr<rnwgpu::GPUSharedFenceDescriptor>> {
|
|
30
|
+
static std::shared_ptr<rnwgpu::GPUSharedFenceDescriptor>
|
|
31
|
+
fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) {
|
|
32
|
+
auto result = std::make_unique<rnwgpu::GPUSharedFenceDescriptor>();
|
|
33
|
+
if (!outOfBounds && arg.isObject()) {
|
|
34
|
+
auto value = arg.getObject(runtime);
|
|
35
|
+
if (value.hasProperty(runtime, "type")) {
|
|
36
|
+
result->type =
|
|
37
|
+
value.getProperty(runtime, "type").asString(runtime).utf8(runtime);
|
|
38
|
+
}
|
|
39
|
+
if (value.hasProperty(runtime, "handle")) {
|
|
40
|
+
auto prop = value.getProperty(runtime, "handle");
|
|
41
|
+
result->handle = JSIConverter<void *>::fromJSI(runtime, prop, false);
|
|
42
|
+
}
|
|
43
|
+
if (value.hasProperty(runtime, "label")) {
|
|
44
|
+
auto prop = value.getProperty(runtime, "label");
|
|
45
|
+
result->label = JSIConverter<std::optional<std::string>>::fromJSI(
|
|
46
|
+
runtime, prop, false);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
static jsi::Value
|
|
52
|
+
toJSI(jsi::Runtime & /*runtime*/,
|
|
53
|
+
std::shared_ptr<rnwgpu::GPUSharedFenceDescriptor> /*arg*/) {
|
|
54
|
+
throw std::runtime_error("Invalid GPUSharedFenceDescriptor::toJSI()");
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
} // namespace rnwgpu
|