@shopify/react-native-skia 2.9.0 → 2.9.1
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/android/cpp/jni/JniWebGPUView.cpp +52 -7
- package/android/src/main/java/com/shopify/reactnative/skia/WebGPUSurfaceView.java +3 -1
- package/android/src/main/java/com/shopify/reactnative/skia/WebGPUTextureView.java +11 -5
- package/android/src/main/java/com/shopify/reactnative/skia/WebGPUView.java +10 -7
- package/android/src/main/java/com/shopify/reactnative/skia/WebGPUViewAPI.java +7 -2
- package/android/src/main/java/com/shopify/reactnative/skia/WebGPUViewManager.java +1 -1
- package/apple/WebGPUMetalView.mm +49 -6
- package/cpp/api/JsiSkParagraphStyle.h +20 -10
- package/cpp/api/JsiSkStrutStyle.h +18 -8
- package/cpp/api/JsiSkTextStyle.h +34 -24
- package/cpp/rnskia/RNSkManager.cpp +6 -0
- package/cpp/rnwgpu/SurfaceRegistry.h +469 -131
- package/cpp/rnwgpu/api/GPUAdapter.cpp +10 -3
- package/cpp/rnwgpu/api/GPUAdapter.h +5 -4
- package/cpp/rnwgpu/api/GPUBuffer.cpp +12 -2
- package/cpp/rnwgpu/api/GPUBuffer.h +3 -2
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +33 -19
- package/cpp/rnwgpu/api/GPUCanvasContext.h +3 -1
- package/cpp/rnwgpu/api/GPUDevice.cpp +32 -17
- package/cpp/rnwgpu/api/GPUDevice.h +9 -7
- package/cpp/rnwgpu/api/GPUQueue.cpp +6 -2
- package/cpp/rnwgpu/api/GPUQueue.h +3 -3
- package/cpp/rnwgpu/api/GPUShaderModule.cpp +7 -2
- package/cpp/rnwgpu/api/GPUShaderModule.h +3 -3
- package/cpp/rnwgpu/api/RNWebGPU.h +20 -0
- package/cpp/rnwgpu/async/RuntimeContext.cpp +5 -0
- package/cpp/rnwgpu/async/RuntimeContext.h +15 -2
- package/lib/commonjs/views/WebGPUCanvas.d.ts +1 -0
- package/lib/commonjs/views/WebGPUCanvas.js +13 -0
- package/lib/commonjs/views/WebGPUCanvas.js.map +1 -1
- package/lib/module/views/WebGPUCanvas.d.ts +1 -0
- package/lib/module/views/WebGPUCanvas.js +14 -1
- package/lib/module/views/WebGPUCanvas.js.map +1 -1
- package/lib/typescript/src/views/WebGPUCanvas.d.ts +1 -0
- package/package.json +2 -2
- package/src/views/WebGPUCanvas.tsx +17 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
namespace rnwgpu {
|
|
19
19
|
|
|
20
20
|
async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
21
|
+
jsi::Runtime &runtime,
|
|
21
22
|
std::optional<std::shared_ptr<GPUDeviceDescriptor>> descriptor) {
|
|
22
23
|
wgpu::DeviceDescriptor aDescriptor;
|
|
23
24
|
Convertor conv;
|
|
@@ -104,9 +105,15 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
104
105
|
descriptor.has_value() ? descriptor.value()->label.value_or("") : "";
|
|
105
106
|
|
|
106
107
|
auto creationRuntime = getCreationRuntime();
|
|
107
|
-
|
|
108
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
109
|
+
// thread that requested it (see GPUBuffer::mapAsync). The GPUDevice is also
|
|
110
|
+
// bound to this context, honoring the contract that a device belongs to the
|
|
111
|
+
// runtime that requested it.
|
|
112
|
+
auto context =
|
|
113
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
114
|
+
return context->postTask(
|
|
108
115
|
[this, aDescriptor, descriptor, label = std::move(label),
|
|
109
|
-
deviceLostBinding,
|
|
116
|
+
deviceLostBinding, context,
|
|
110
117
|
creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
111
118
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
112
119
|
// Build a local mutable copy so we can chain Dawn's device toggles.
|
|
@@ -138,7 +145,7 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
138
145
|
}
|
|
139
146
|
_instance.RequestDevice(
|
|
140
147
|
&deviceDesc, wgpu::CallbackMode::AllowProcessEvents,
|
|
141
|
-
[context
|
|
148
|
+
[context, resolve, reject, label, creationRuntime,
|
|
142
149
|
deviceLostBinding](wgpu::RequestDeviceStatus status,
|
|
143
150
|
wgpu::Device device,
|
|
144
151
|
wgpu::StringView message) {
|
|
@@ -33,8 +33,9 @@ public:
|
|
|
33
33
|
public:
|
|
34
34
|
std::string getBrand() { return CLASS_NAME; }
|
|
35
35
|
|
|
36
|
-
async::AsyncTaskHandle
|
|
37
|
-
|
|
36
|
+
async::AsyncTaskHandle requestDevice(
|
|
37
|
+
jsi::Runtime &runtime,
|
|
38
|
+
std::optional<std::shared_ptr<GPUDeviceDescriptor>> descriptor);
|
|
38
39
|
|
|
39
40
|
std::unordered_set<std::string> getFeatures();
|
|
40
41
|
std::shared_ptr<GPUSupportedLimits> getLimits();
|
|
@@ -42,8 +43,8 @@ public:
|
|
|
42
43
|
|
|
43
44
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
44
45
|
installGetter(runtime, prototype, "__brand", &GPUAdapter::getBrand);
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
installMethodWithRuntime(runtime, prototype, "requestDevice",
|
|
47
|
+
&GPUAdapter::requestDevice);
|
|
47
48
|
installGetter(runtime, prototype, "features", &GPUAdapter::getFeatures);
|
|
48
49
|
installGetter(runtime, prototype, "limits", &GPUAdapter::getLimits);
|
|
49
50
|
installGetter(runtime, prototype, "info", &GPUAdapter::getInfo);
|
|
@@ -37,7 +37,8 @@ GPUBuffer::getMappedRange(std::optional<size_t> o, std::optional<size_t> size) {
|
|
|
37
37
|
|
|
38
38
|
void GPUBuffer::destroy() { _instance.Destroy(); }
|
|
39
39
|
|
|
40
|
-
async::AsyncTaskHandle GPUBuffer::mapAsync(
|
|
40
|
+
async::AsyncTaskHandle GPUBuffer::mapAsync(jsi::Runtime &runtime,
|
|
41
|
+
uint64_t modeIn,
|
|
41
42
|
std::optional<uint64_t> offset,
|
|
42
43
|
std::optional<uint64_t> size) {
|
|
43
44
|
Convertor conv;
|
|
@@ -51,7 +52,16 @@ async::AsyncTaskHandle GPUBuffer::mapAsync(uint64_t modeIn,
|
|
|
51
52
|
auto bufferHandle = _instance;
|
|
52
53
|
uint64_t resolvedOffset = offset.value_or(0);
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
// Post to the CALLING runtime's context, not the one captured at buffer
|
|
56
|
+
// creation (_async): the buffer may have been created on another runtime and
|
|
57
|
+
// boxed across (e.g. device created on the main JS runtime, mapAsync called
|
|
58
|
+
// from a worklet). The returned Promise lives on the calling runtime, so it
|
|
59
|
+
// must be settled from that runtime's own thread — and postTask itself
|
|
60
|
+
// schedules the pump through its context's runtime (setTimeout), which is
|
|
61
|
+
// only safe for the runtime we are currently executing on.
|
|
62
|
+
auto context =
|
|
63
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
64
|
+
return context->postTask(
|
|
55
65
|
[bufferHandle, mode, resolvedOffset,
|
|
56
66
|
rangeSize](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
57
67
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
@@ -33,7 +33,7 @@ public:
|
|
|
33
33
|
public:
|
|
34
34
|
std::string getBrand() { return CLASS_NAME; }
|
|
35
35
|
|
|
36
|
-
async::AsyncTaskHandle mapAsync(uint64_t modeIn,
|
|
36
|
+
async::AsyncTaskHandle mapAsync(jsi::Runtime &runtime, uint64_t modeIn,
|
|
37
37
|
std::optional<uint64_t> offset,
|
|
38
38
|
std::optional<uint64_t> size);
|
|
39
39
|
std::shared_ptr<ArrayBuffer> getMappedRange(std::optional<size_t> offset,
|
|
@@ -53,7 +53,8 @@ public:
|
|
|
53
53
|
|
|
54
54
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
55
55
|
installGetter(runtime, prototype, "__brand", &GPUBuffer::getBrand);
|
|
56
|
-
|
|
56
|
+
installMethodWithRuntime(runtime, prototype, "mapAsync",
|
|
57
|
+
&GPUBuffer::mapAsync);
|
|
57
58
|
installMethod(runtime, prototype, "getMappedRange",
|
|
58
59
|
&GPUBuffer::getMappedRange);
|
|
59
60
|
installMethod(runtime, prototype, "unmap", &GPUBuffer::unmap);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#include "GPUCanvasContext.h"
|
|
2
2
|
#include "Convertors.h"
|
|
3
|
+
#include <algorithm>
|
|
3
4
|
#include <memory>
|
|
5
|
+
#include <utility>
|
|
6
|
+
#include <vector>
|
|
4
7
|
|
|
5
8
|
namespace rnwgpu {
|
|
6
9
|
|
|
@@ -9,37 +12,50 @@ void GPUCanvasContext::configure(
|
|
|
9
12
|
Convertor conv;
|
|
10
13
|
wgpu::SurfaceConfiguration surfaceConfiguration;
|
|
11
14
|
surfaceConfiguration.device = configuration->device->get();
|
|
12
|
-
if (configuration->viewFormats.has_value()) {
|
|
13
|
-
if (!conv(surfaceConfiguration.viewFormats,
|
|
14
|
-
surfaceConfiguration.viewFormatCount,
|
|
15
|
-
configuration->viewFormats.value())) {
|
|
16
|
-
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
15
|
if (!conv(surfaceConfiguration.usage, configuration->usage) ||
|
|
20
16
|
!conv(surfaceConfiguration.format, configuration->format)) {
|
|
21
17
|
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
22
18
|
}
|
|
19
|
+
// viewFormats are deep-copied into SurfaceInfo (which outlives this call);
|
|
20
|
+
// Convertor-allocated arrays would dangle.
|
|
21
|
+
std::vector<wgpu::TextureFormat> viewFormats;
|
|
22
|
+
if (configuration->viewFormats.has_value()) {
|
|
23
|
+
viewFormats = configuration->viewFormats.value();
|
|
24
|
+
}
|
|
23
25
|
|
|
24
26
|
#ifdef __APPLE__
|
|
25
27
|
surfaceConfiguration.alphaMode = configuration->alphaMode;
|
|
26
28
|
#endif
|
|
27
29
|
surfaceConfiguration.presentMode = wgpu::PresentMode::Fifo;
|
|
28
|
-
_surfaceInfo->configure(surfaceConfiguration);
|
|
30
|
+
_surfaceInfo->configure(surfaceConfiguration, std::move(viewFormats));
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
void GPUCanvasContext::unconfigure() {}
|
|
33
|
+
void GPUCanvasContext::unconfigure() { _surfaceInfo->unconfigure(); }
|
|
32
34
|
|
|
33
35
|
std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
|
|
36
|
+
if (!_surfaceInfo->isConfigured()) {
|
|
37
|
+
// Web parity: on the web this is an InvalidStateError, not a crash.
|
|
38
|
+
throw std::runtime_error(
|
|
39
|
+
"[WebGPU] getCurrentTexture() called on a canvas context that is not "
|
|
40
|
+
"configured; call context.configure() first");
|
|
41
|
+
}
|
|
42
|
+
// The drawing buffer tracks canvas.width/height (like on the web); resize it
|
|
43
|
+
// lazily when they changed. Sizes are clamped to 1 so a canvas that has not
|
|
44
|
+
// been laid out yet (0x0) keeps working.
|
|
34
45
|
auto prevSize = _surfaceInfo->getConfig();
|
|
35
|
-
auto width = _canvas->getWidth();
|
|
36
|
-
auto height = _canvas->getHeight();
|
|
37
|
-
auto sizeHasChanged = prevSize.width != width ||
|
|
46
|
+
auto width = std::max(1, _canvas->getWidth());
|
|
47
|
+
auto height = std::max(1, _canvas->getHeight());
|
|
48
|
+
auto sizeHasChanged = prevSize.width != static_cast<uint32_t>(width) ||
|
|
49
|
+
prevSize.height != static_cast<uint32_t>(height);
|
|
38
50
|
if (sizeHasChanged) {
|
|
39
51
|
_surfaceInfo->reconfigure(width, height);
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
auto texture = _surfaceInfo->getCurrentTexture();
|
|
55
|
+
if (texture == nullptr) {
|
|
56
|
+
throw std::runtime_error(
|
|
57
|
+
"[WebGPU] getCurrentTexture() failed to acquire a texture");
|
|
58
|
+
}
|
|
43
59
|
|
|
44
60
|
auto size = _surfaceInfo->getSize();
|
|
45
61
|
_canvas->setClientWidth(size.width);
|
|
@@ -49,13 +65,11 @@ std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
|
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
void GPUCanvasContext::present() {
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
_surfaceInfo->presentFrame();
|
|
58
|
-
}
|
|
68
|
+
// presentFrame() is the end-of-frame boundary: it presents when this frame's
|
|
69
|
+
// texture was acquired from the on-screen surface (offscreen and dropped
|
|
70
|
+
// frames are skipped), clears the frame state, and adopts any surface that
|
|
71
|
+
// attached while the frame was in flight.
|
|
72
|
+
_surfaceInfo->presentFrame();
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
} // namespace rnwgpu
|
|
@@ -56,7 +56,9 @@ public:
|
|
|
56
56
|
std::shared_ptr<GPUTexture> getCurrentTexture();
|
|
57
57
|
// Present is explicit on every runtime (main JS, Reanimated UI, and dedicated
|
|
58
58
|
// worklet runtimes). It runs synchronously on the calling thread, preserving
|
|
59
|
-
// Dawn surface thread-affinity; offscreen
|
|
59
|
+
// Dawn surface thread-affinity; frames rendered offscreen (no surface
|
|
60
|
+
// attached) are skipped. It is also the end-of-frame boundary at which a
|
|
61
|
+
// newly attached native surface is adopted (see SurfaceInfo).
|
|
60
62
|
void present();
|
|
61
63
|
|
|
62
64
|
private:
|
|
@@ -327,6 +327,7 @@ std::shared_ptr<GPUSharedFence> GPUDevice::importSharedFence(
|
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
330
|
+
jsi::Runtime &runtime,
|
|
330
331
|
std::shared_ptr<GPUComputePipelineDescriptor> descriptor) {
|
|
331
332
|
wgpu::ComputePipelineDescriptor desc{};
|
|
332
333
|
Convertor conv;
|
|
@@ -339,12 +340,16 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
339
340
|
descriptor->label.has_value() ? descriptor->label.value() : "");
|
|
340
341
|
auto pipelineHolder = std::make_shared<GPUComputePipeline>(nullptr, label);
|
|
341
342
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
343
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
344
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
345
|
+
auto context =
|
|
346
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
347
|
+
return context->postTask([device = _instance, desc, descriptor,
|
|
348
|
+
pipelineHolder](
|
|
349
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
350
|
+
&resolve,
|
|
351
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
352
|
+
&reject) {
|
|
348
353
|
(void)descriptor;
|
|
349
354
|
device.CreateComputePipelineAsync(
|
|
350
355
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
@@ -368,6 +373,7 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
368
373
|
}
|
|
369
374
|
|
|
370
375
|
async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
376
|
+
jsi::Runtime &runtime,
|
|
371
377
|
std::shared_ptr<GPURenderPipelineDescriptor> descriptor) {
|
|
372
378
|
wgpu::RenderPipelineDescriptor desc{};
|
|
373
379
|
Convertor conv;
|
|
@@ -381,12 +387,16 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
381
387
|
descriptor->label.has_value() ? descriptor->label.value() : "");
|
|
382
388
|
auto pipelineHolder = std::make_shared<GPURenderPipeline>(nullptr, label);
|
|
383
389
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
391
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
392
|
+
auto context =
|
|
393
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
394
|
+
return context->postTask([device = _instance, desc, descriptor,
|
|
395
|
+
pipelineHolder](
|
|
396
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
397
|
+
&resolve,
|
|
398
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
399
|
+
&reject) {
|
|
390
400
|
(void)descriptor;
|
|
391
401
|
device.CreateRenderPipelineAsync(
|
|
392
402
|
&desc, wgpu::CallbackMode::AllowProcessEvents,
|
|
@@ -413,13 +423,18 @@ void GPUDevice::pushErrorScope(wgpu::ErrorFilter filter) {
|
|
|
413
423
|
_instance.PushErrorScope(filter);
|
|
414
424
|
}
|
|
415
425
|
|
|
416
|
-
async::AsyncTaskHandle GPUDevice::popErrorScope() {
|
|
426
|
+
async::AsyncTaskHandle GPUDevice::popErrorScope(jsi::Runtime &runtime) {
|
|
417
427
|
auto device = _instance;
|
|
418
428
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
429
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
430
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
431
|
+
auto context =
|
|
432
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
433
|
+
return context->postTask([device](
|
|
434
|
+
const async::AsyncTaskHandle::ResolveFunction
|
|
435
|
+
&resolve,
|
|
436
|
+
const async::AsyncTaskHandle::RejectFunction
|
|
437
|
+
&reject) {
|
|
423
438
|
device.PopErrorScope(
|
|
424
439
|
wgpu::CallbackMode::AllowProcessEvents,
|
|
425
440
|
[resolve, reject](wgpu::PopErrorScopeStatus status,
|
|
@@ -141,8 +141,10 @@ public:
|
|
|
141
141
|
std::shared_ptr<GPURenderPipeline>
|
|
142
142
|
createRenderPipeline(std::shared_ptr<GPURenderPipelineDescriptor> descriptor);
|
|
143
143
|
async::AsyncTaskHandle createComputePipelineAsync(
|
|
144
|
+
jsi::Runtime &runtime,
|
|
144
145
|
std::shared_ptr<GPUComputePipelineDescriptor> descriptor);
|
|
145
146
|
async::AsyncTaskHandle createRenderPipelineAsync(
|
|
147
|
+
jsi::Runtime &runtime,
|
|
146
148
|
std::shared_ptr<GPURenderPipelineDescriptor> descriptor);
|
|
147
149
|
std::shared_ptr<GPUCommandEncoder> createCommandEncoder(
|
|
148
150
|
std::optional<std::shared_ptr<GPUCommandEncoderDescriptor>> descriptor);
|
|
@@ -151,7 +153,7 @@ public:
|
|
|
151
153
|
std::shared_ptr<GPUQuerySet>
|
|
152
154
|
createQuerySet(std::shared_ptr<GPUQuerySetDescriptor> descriptor);
|
|
153
155
|
void pushErrorScope(wgpu::ErrorFilter filter);
|
|
154
|
-
async::AsyncTaskHandle popErrorScope();
|
|
156
|
+
async::AsyncTaskHandle popErrorScope(jsi::Runtime &runtime);
|
|
155
157
|
|
|
156
158
|
std::unordered_set<std::string> getFeatures();
|
|
157
159
|
std::shared_ptr<GPUSupportedLimits> getLimits();
|
|
@@ -197,10 +199,10 @@ public:
|
|
|
197
199
|
&GPUDevice::createComputePipeline);
|
|
198
200
|
installMethod(runtime, prototype, "createRenderPipeline",
|
|
199
201
|
&GPUDevice::createRenderPipeline);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
installMethodWithRuntime(runtime, prototype, "createComputePipelineAsync",
|
|
203
|
+
&GPUDevice::createComputePipelineAsync);
|
|
204
|
+
installMethodWithRuntime(runtime, prototype, "createRenderPipelineAsync",
|
|
205
|
+
&GPUDevice::createRenderPipelineAsync);
|
|
204
206
|
installMethod(runtime, prototype, "createCommandEncoder",
|
|
205
207
|
&GPUDevice::createCommandEncoder);
|
|
206
208
|
installMethod(runtime, prototype, "createRenderBundleEncoder",
|
|
@@ -209,8 +211,8 @@ public:
|
|
|
209
211
|
&GPUDevice::createQuerySet);
|
|
210
212
|
installMethod(runtime, prototype, "pushErrorScope",
|
|
211
213
|
&GPUDevice::pushErrorScope);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
installMethodWithRuntime(runtime, prototype, "popErrorScope",
|
|
215
|
+
&GPUDevice::popErrorScope);
|
|
214
216
|
installGetter(runtime, prototype, "features", &GPUDevice::getFeatures);
|
|
215
217
|
installGetter(runtime, prototype, "limits", &GPUDevice::getLimits);
|
|
216
218
|
installGetter(runtime, prototype, "queue", &GPUDevice::getQueue);
|
|
@@ -79,9 +79,13 @@ void GPUQueue::writeBuffer(std::shared_ptr<GPUBuffer> buffer,
|
|
|
79
79
|
static_cast<size_t>(size64));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
async::AsyncTaskHandle GPUQueue::onSubmittedWorkDone() {
|
|
82
|
+
async::AsyncTaskHandle GPUQueue::onSubmittedWorkDone(jsi::Runtime &runtime) {
|
|
83
83
|
auto queue = _instance;
|
|
84
|
-
|
|
84
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
85
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
86
|
+
auto context =
|
|
87
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
88
|
+
return context->postTask(
|
|
85
89
|
[queue](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
86
90
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
87
91
|
queue.OnSubmittedWorkDone(
|
|
@@ -40,7 +40,7 @@ public:
|
|
|
40
40
|
std::string getBrand() { return CLASS_NAME; }
|
|
41
41
|
|
|
42
42
|
void submit(std::vector<std::shared_ptr<GPUCommandBuffer>> commandBuffers);
|
|
43
|
-
async::AsyncTaskHandle onSubmittedWorkDone();
|
|
43
|
+
async::AsyncTaskHandle onSubmittedWorkDone(jsi::Runtime &runtime);
|
|
44
44
|
void writeBuffer(std::shared_ptr<GPUBuffer> buffer, uint64_t bufferOffset,
|
|
45
45
|
std::shared_ptr<ArrayBuffer> data,
|
|
46
46
|
std::optional<uint64_t> dataOffsetElements,
|
|
@@ -63,8 +63,8 @@ public:
|
|
|
63
63
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
64
64
|
installGetter(runtime, prototype, "__brand", &GPUQueue::getBrand);
|
|
65
65
|
installMethod(runtime, prototype, "submit", &GPUQueue::submit);
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
installMethodWithRuntime(runtime, prototype, "onSubmittedWorkDone",
|
|
67
|
+
&GPUQueue::onSubmittedWorkDone);
|
|
68
68
|
installMethod(runtime, prototype, "writeBuffer", &GPUQueue::writeBuffer);
|
|
69
69
|
installMethod(runtime, prototype, "writeTexture", &GPUQueue::writeTexture);
|
|
70
70
|
installMethod(runtime, prototype, "copyExternalImageToTexture",
|
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
namespace rnwgpu {
|
|
9
9
|
|
|
10
|
-
async::AsyncTaskHandle
|
|
10
|
+
async::AsyncTaskHandle
|
|
11
|
+
GPUShaderModule::getCompilationInfo(jsi::Runtime &runtime) {
|
|
11
12
|
auto module = _instance;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
// Post to the CALLING runtime's context so the promise settles on the
|
|
15
|
+
// thread that requested it (see GPUBuffer::mapAsync).
|
|
16
|
+
auto context =
|
|
17
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
18
|
+
return context->postTask(
|
|
14
19
|
[module](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
15
20
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
16
21
|
auto result = std::make_shared<GPUCompilationInfo>();
|
|
@@ -31,7 +31,7 @@ public:
|
|
|
31
31
|
public:
|
|
32
32
|
std::string getBrand() { return CLASS_NAME; }
|
|
33
33
|
|
|
34
|
-
async::AsyncTaskHandle getCompilationInfo();
|
|
34
|
+
async::AsyncTaskHandle getCompilationInfo(jsi::Runtime &runtime);
|
|
35
35
|
|
|
36
36
|
std::string getLabel() { return _label; }
|
|
37
37
|
void setLabel(const std::string &label) {
|
|
@@ -41,8 +41,8 @@ public:
|
|
|
41
41
|
|
|
42
42
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
43
43
|
installGetter(runtime, prototype, "__brand", &GPUShaderModule::getBrand);
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
installMethodWithRuntime(runtime, prototype, "getCompilationInfo",
|
|
45
|
+
&GPUShaderModule::getCompilationInfo);
|
|
46
46
|
installGetterSetter(runtime, prototype, "label", &GPUShaderModule::getLabel,
|
|
47
47
|
&GPUShaderModule::setLabel);
|
|
48
48
|
}
|
|
@@ -45,11 +45,31 @@ public:
|
|
|
45
45
|
nativeInfo.height);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Retires a canvas context from the JS side (Canvas unmount cleanup).
|
|
49
|
+
// Registry entries have two owners, split by whether a native surface is
|
|
50
|
+
// attached:
|
|
51
|
+
// - A native view currently owns a surface (or one is pending): its own
|
|
52
|
+
// teardown (WebGPUMetalView dealloc / WebGPUViewManager.onDropViewInstance)
|
|
53
|
+
// removes the entry. Skipping here keeps React StrictMode safe: its
|
|
54
|
+
// simulated unmount re-runs JS effects without unmounting native views,
|
|
55
|
+
// and removing the entry then would orphan the still-attached surface.
|
|
56
|
+
// - No native surface: the JS side is the last owner and removes the entry.
|
|
57
|
+
// The check-and-erase runs atomically under the registry lock
|
|
58
|
+
// (removeSurfaceInfoIfDetached), serialized against the UI thread's
|
|
59
|
+
// find-or-create + attach (SurfaceRegistry::attachSurface), so a concurrent
|
|
60
|
+
// attach can never be orphaned by this removal.
|
|
61
|
+
void destroyContext(int contextId) {
|
|
62
|
+
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
63
|
+
registry.removeSurfaceInfoIfDetached(contextId);
|
|
64
|
+
}
|
|
65
|
+
|
|
48
66
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
49
67
|
installGetter(runtime, prototype, "fabric", &RNWebGPU::getFabric);
|
|
50
68
|
installGetter(runtime, prototype, "gpu", &RNWebGPU::getGPU);
|
|
51
69
|
installMethod(runtime, prototype, "getNativeSurface",
|
|
52
70
|
&RNWebGPU::getNativeSurface);
|
|
71
|
+
installMethod(runtime, prototype, "destroyContext",
|
|
72
|
+
&RNWebGPU::destroyContext);
|
|
53
73
|
installMethod(runtime, prototype, "MakeWebGPUCanvasContext",
|
|
54
74
|
&RNWebGPU::MakeWebGPUCanvasContext);
|
|
55
75
|
}
|
|
@@ -39,6 +39,11 @@ void RuntimeContext::registerMainRuntime(
|
|
|
39
39
|
sMainInvoker = std::move(invoker);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
std::shared_ptr<facebook::react::CallInvoker>
|
|
43
|
+
RuntimeContext::mainCallInvoker() {
|
|
44
|
+
return sMainInvoker;
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
RuntimeContext::RuntimeContext(jsi::Runtime &runtime, wgpu::Instance instance)
|
|
43
48
|
: _runtime(runtime), _instance(std::move(instance)) {}
|
|
44
49
|
|
|
@@ -55,8 +55,15 @@ namespace rnwgpu::async {
|
|
|
55
55
|
* ProcessEvents on one instance is not guaranteed reentrant.
|
|
56
56
|
*
|
|
57
57
|
* Threading contract: a RuntimeContext must only be pumped from the runtime it
|
|
58
|
-
* was created for.
|
|
59
|
-
*
|
|
58
|
+
* was created for. Request/response async entry points (mapAsync,
|
|
59
|
+
* onSubmittedWorkDone, popErrorScope, createComputePipelineAsync,
|
|
60
|
+
* createRenderPipelineAsync, getCompilationInfo, requestAdapter,
|
|
61
|
+
* requestDevice) must NOT use a context captured when the object was created —
|
|
62
|
+
* the object may have been boxed across to another runtime. They resolve the
|
|
63
|
+
* CALLING runtime's context via getOrCreate(runtime, instance) so the promise
|
|
64
|
+
* is settled on the thread it was created on. Spontaneous events (device.lost,
|
|
65
|
+
* uncapturederror) remain bound to the device's own context (best-effort, main
|
|
66
|
+
* runtime only; see the class doc above).
|
|
60
67
|
*/
|
|
61
68
|
class RuntimeContext : public std::enable_shared_from_this<RuntimeContext> {
|
|
62
69
|
public:
|
|
@@ -84,6 +91,12 @@ public:
|
|
|
84
91
|
return _callInvoker;
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
// The main JS runtime's CallInvoker (registered on install), or null before
|
|
95
|
+
// install. Used by the platform views to apply latched surface attaches from
|
|
96
|
+
// the JS thread for contexts that are not actively rendering (see
|
|
97
|
+
// SurfaceInfo::applyPendingAttach).
|
|
98
|
+
static std::shared_ptr<facebook::react::CallInvoker> mainCallInvoker();
|
|
99
|
+
|
|
87
100
|
// The wgpu::Instance bound to this runtime.
|
|
88
101
|
wgpu::Instance instance() const { return _instance; }
|
|
89
102
|
|
|
@@ -6,6 +6,7 @@ declare global {
|
|
|
6
6
|
fabric: boolean;
|
|
7
7
|
getNativeSurface: (contextId: number) => NativeCanvas;
|
|
8
8
|
MakeWebGPUCanvasContext: (contextId: number, width: number, height: number) => RNCanvasContext;
|
|
9
|
+
destroyContext: (contextId: number) => void;
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
type SurfacePointer = bigint;
|
|
@@ -21,6 +21,19 @@ const WebGPUCanvas = ({
|
|
|
21
21
|
}) => {
|
|
22
22
|
const viewRef = (0, _react.useRef)(null);
|
|
23
23
|
const [contextId] = (0, _react.useState)(() => generateContextId());
|
|
24
|
+
|
|
25
|
+
// Retire the native registry entry for this contextId on unmount. When a
|
|
26
|
+
// native surface is still attached, this is a no-op and the native view's
|
|
27
|
+
// own teardown retires the entry instead — which keeps StrictMode's
|
|
28
|
+
// simulated unmount (which re-runs effects without unmounting native views)
|
|
29
|
+
// from orphaning a live surface.
|
|
30
|
+
(0, _react.useEffect)(() => {
|
|
31
|
+
return () => {
|
|
32
|
+
if (typeof RNWebGPU !== "undefined") {
|
|
33
|
+
RNWebGPU.destroyContext(contextId);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}, [contextId]);
|
|
24
37
|
(0, _react.useImperativeHandle)(ref, () => ({
|
|
25
38
|
getContextId: () => contextId,
|
|
26
39
|
getNativeSurface: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_WebGPUViewNativeComponent","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","CONTEXT_COUNTER","generateContextId","WebGPUCanvas","transparent","ref","props","viewRef","useRef","contextId","useState","useImperativeHandle","getContextId","getNativeSurface","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_WebGPUViewNativeComponent","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","CONTEXT_COUNTER","generateContextId","WebGPUCanvas","transparent","ref","props","viewRef","useRef","contextId","useState","useEffect","RNWebGPU","destroyContext","useImperativeHandle","getContextId","getNativeSurface","Error","getContext","contextName","current","view","size","getBoundingClientRect","unstable_getBoundingClientRect","MakeWebGPUCanvasContext","width","height","Platform","OS","createElement","View","collapsable","style","flex","exports"],"sources":["WebGPUCanvas.tsx"],"sourcesContent":["import React, { useEffect, useImperativeHandle, useRef, useState } from \"react\";\nimport type { ViewProps } from \"react-native\";\nimport { View, Platform } from \"react-native\";\n\nimport WebGPUNativeView from \"../specs/WebGPUViewNativeComponent\";\n\nlet CONTEXT_COUNTER = 1;\nfunction generateContextId() {\n return CONTEXT_COUNTER++;\n}\n\ndeclare global {\n var RNWebGPU: {\n gpu: GPU;\n fabric: boolean;\n getNativeSurface: (contextId: number) => NativeCanvas;\n MakeWebGPUCanvasContext: (\n contextId: number,\n width: number,\n height: number\n ) => RNCanvasContext;\n // Retires a canvas context; called by the Canvas on unmount (the Canvas\n // owns the native registry entry for its contextId).\n destroyContext: (contextId: number) => void;\n };\n}\n\ntype SurfacePointer = bigint;\n\nexport interface NativeCanvas {\n surface: SurfacePointer;\n width: number;\n height: number;\n clientWidth: number;\n clientHeight: number;\n}\n\nexport type RNCanvasContext = GPUCanvasContext & {\n present: () => void;\n};\n\nexport interface WebGPUCanvasRef {\n getContextId: () => number;\n getContext(contextName: \"webgpu\"): RNCanvasContext | null;\n getNativeSurface: () => NativeCanvas;\n}\n\ninterface WebGPUCanvasProps extends ViewProps {\n transparent?: boolean;\n ref?: React.Ref<WebGPUCanvasRef>;\n}\n\nexport const WebGPUCanvas = ({\n transparent,\n ref,\n ...props\n}: WebGPUCanvasProps) => {\n const viewRef = useRef(null);\n const [contextId] = useState(() => generateContextId());\n\n // Retire the native registry entry for this contextId on unmount. When a\n // native surface is still attached, this is a no-op and the native view's\n // own teardown retires the entry instead — which keeps StrictMode's\n // simulated unmount (which re-runs effects without unmounting native views)\n // from orphaning a live surface.\n useEffect(() => {\n return () => {\n if (typeof RNWebGPU !== \"undefined\") {\n RNWebGPU.destroyContext(contextId);\n }\n };\n }, [contextId]);\n\n useImperativeHandle(ref, () => ({\n getContextId: () => contextId,\n getNativeSurface: () => {\n if (typeof RNWebGPU === \"undefined\") {\n throw new Error(\n \"[WebGPU] RNWebGPU is not available. Make sure SK_GRAPHITE is enabled.\"\n );\n }\n return RNWebGPU.getNativeSurface(contextId);\n },\n getContext(contextName: \"webgpu\"): RNCanvasContext | null {\n if (contextName !== \"webgpu\") {\n throw new Error(`[WebGPU] Unsupported context: ${contextName}`);\n }\n if (!viewRef.current) {\n throw new Error(\"[WebGPU] Cannot get context before mount\");\n }\n if (typeof RNWebGPU === \"undefined\") {\n throw new Error(\n \"[WebGPU] RNWebGPU is not available. Make sure SK_GRAPHITE is enabled.\"\n );\n }\n // getBoundingClientRect became stable in RN 0.83\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const view = viewRef.current as any;\n const size =\n \"getBoundingClientRect\" in view\n ? view.getBoundingClientRect()\n : view.unstable_getBoundingClientRect();\n return RNWebGPU.MakeWebGPUCanvasContext(\n contextId,\n size.width,\n size.height\n );\n },\n }));\n\n // WebGPU Canvas is not supported on web\n if (Platform.OS === \"web\") {\n return <View {...props} />;\n }\n\n return (\n <View collapsable={false} ref={viewRef} {...props}>\n <WebGPUNativeView\n style={{ flex: 1 }}\n contextId={contextId}\n transparent={!!transparent}\n />\n </View>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,6 +6,7 @@ declare global {
|
|
|
6
6
|
fabric: boolean;
|
|
7
7
|
getNativeSurface: (contextId: number) => NativeCanvas;
|
|
8
8
|
MakeWebGPUCanvasContext: (contextId: number, width: number, height: number) => RNCanvasContext;
|
|
9
|
+
destroyContext: (contextId: number) => void;
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
type SurfacePointer = bigint;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
|
-
import React, { useImperativeHandle, useRef, useState } from "react";
|
|
2
|
+
import React, { useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
3
3
|
import { View, Platform } from "react-native";
|
|
4
4
|
import WebGPUNativeView from "../specs/WebGPUViewNativeComponent";
|
|
5
5
|
let CONTEXT_COUNTER = 1;
|
|
@@ -13,6 +13,19 @@ export const WebGPUCanvas = ({
|
|
|
13
13
|
}) => {
|
|
14
14
|
const viewRef = useRef(null);
|
|
15
15
|
const [contextId] = useState(() => generateContextId());
|
|
16
|
+
|
|
17
|
+
// Retire the native registry entry for this contextId on unmount. When a
|
|
18
|
+
// native surface is still attached, this is a no-op and the native view's
|
|
19
|
+
// own teardown retires the entry instead — which keeps StrictMode's
|
|
20
|
+
// simulated unmount (which re-runs effects without unmounting native views)
|
|
21
|
+
// from orphaning a live surface.
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
if (typeof RNWebGPU !== "undefined") {
|
|
25
|
+
RNWebGPU.destroyContext(contextId);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}, [contextId]);
|
|
16
29
|
useImperativeHandle(ref, () => ({
|
|
17
30
|
getContextId: () => contextId,
|
|
18
31
|
getNativeSurface: () => {
|