react-native-webgpu 0.5.14 → 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.
Files changed (79) hide show
  1. package/README.md +19 -6
  2. package/android/CMakeLists.txt +2 -2
  3. package/cpp/jsi/NativeObject.h +39 -0
  4. package/cpp/rnwgpu/RNWebGPUManager.cpp +8 -0
  5. package/cpp/rnwgpu/SurfaceRegistry.h +33 -1
  6. package/cpp/rnwgpu/api/GPU.cpp +14 -11
  7. package/cpp/rnwgpu/api/GPU.h +6 -4
  8. package/cpp/rnwgpu/api/GPUAdapter.cpp +5 -8
  9. package/cpp/rnwgpu/api/GPUAdapter.h +3 -3
  10. package/cpp/rnwgpu/api/GPUBuffer.cpp +23 -24
  11. package/cpp/rnwgpu/api/GPUBuffer.h +3 -3
  12. package/cpp/rnwgpu/api/GPUCanvasContext.cpp +13 -16
  13. package/cpp/rnwgpu/api/GPUCanvasContext.h +3 -0
  14. package/cpp/rnwgpu/api/GPUDevice.cpp +103 -19
  15. package/cpp/rnwgpu/api/GPUDevice.h +17 -3
  16. package/cpp/rnwgpu/api/GPUQueue.h +3 -3
  17. package/cpp/rnwgpu/api/GPUShaderModule.h +3 -3
  18. package/cpp/rnwgpu/api/GPUSharedFence.cpp +77 -0
  19. package/cpp/rnwgpu/api/GPUSharedFence.h +53 -0
  20. package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +60 -11
  21. package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +13 -9
  22. package/cpp/rnwgpu/api/descriptors/GPUSharedFenceDescriptor.h +58 -0
  23. package/cpp/rnwgpu/api/descriptors/GPUSharedFenceState.h +51 -0
  24. package/cpp/rnwgpu/async/AsyncTaskHandle.cpp +55 -23
  25. package/cpp/rnwgpu/async/AsyncTaskHandle.h +8 -5
  26. package/cpp/rnwgpu/async/RuntimeContext.cpp +193 -0
  27. package/cpp/rnwgpu/async/RuntimeContext.h +122 -0
  28. package/lib/commonjs/Canvas.js.map +1 -1
  29. package/lib/commonjs/Offscreen.js +1 -1
  30. package/lib/commonjs/WebPolyfillGPUModule.js +2 -0
  31. package/lib/commonjs/WebPolyfillGPUModule.js.map +1 -1
  32. package/lib/commonjs/constants.js +40 -0
  33. package/lib/commonjs/constants.js.map +1 -0
  34. package/lib/commonjs/index.js +22 -0
  35. package/lib/commonjs/index.js.map +1 -1
  36. package/lib/commonjs/install.js +63 -0
  37. package/lib/commonjs/install.js.map +1 -0
  38. package/lib/module/Canvas.js.map +1 -1
  39. package/lib/module/Offscreen.js +1 -1
  40. package/lib/module/WebPolyfillGPUModule.js +2 -0
  41. package/lib/module/WebPolyfillGPUModule.js.map +1 -1
  42. package/lib/module/constants.js +34 -0
  43. package/lib/module/constants.js.map +1 -0
  44. package/lib/module/index.js +2 -0
  45. package/lib/module/index.js.map +1 -1
  46. package/lib/module/install.js +57 -0
  47. package/lib/module/install.js.map +1 -0
  48. package/lib/typescript/lib/commonjs/constants.d.ts +3 -0
  49. package/lib/typescript/lib/commonjs/constants.d.ts.map +1 -0
  50. package/lib/typescript/lib/commonjs/install.d.ts +35 -0
  51. package/lib/typescript/lib/commonjs/install.d.ts.map +1 -0
  52. package/lib/typescript/lib/module/constants.d.ts +6 -0
  53. package/lib/typescript/lib/module/constants.d.ts.map +1 -0
  54. package/lib/typescript/lib/module/index.d.ts +2 -0
  55. package/lib/typescript/lib/module/install.d.ts +2 -0
  56. package/lib/typescript/lib/module/install.d.ts.map +1 -0
  57. package/lib/typescript/src/Canvas.d.ts +9 -0
  58. package/lib/typescript/src/Canvas.d.ts.map +1 -1
  59. package/lib/typescript/src/constants.d.ts +6 -0
  60. package/lib/typescript/src/constants.d.ts.map +1 -0
  61. package/lib/typescript/src/index.d.ts +5 -2
  62. package/lib/typescript/src/index.d.ts.map +1 -1
  63. package/lib/typescript/src/install.d.ts +34 -0
  64. package/lib/typescript/src/install.d.ts.map +1 -0
  65. package/lib/typescript/src/types.d.ts +34 -2
  66. package/lib/typescript/src/types.d.ts.map +1 -1
  67. package/package.json +1 -1
  68. package/src/Canvas.tsx +9 -0
  69. package/src/Offscreen.ts +1 -1
  70. package/src/WebPolyfillGPUModule.ts +3 -3
  71. package/src/constants.ts +37 -0
  72. package/src/index.tsx +13 -2
  73. package/src/install.ts +61 -0
  74. package/src/types.ts +69 -3
  75. package/cpp/rnwgpu/async/AsyncDispatcher.h +0 -28
  76. package/cpp/rnwgpu/async/AsyncRunner.cpp +0 -215
  77. package/cpp/rnwgpu/async/AsyncRunner.h +0 -53
  78. package/cpp/rnwgpu/async/JSIMicrotaskDispatcher.cpp +0 -23
  79. package/cpp/rnwgpu/async/JSIMicrotaskDispatcher.h +0 -22
package/README.md CHANGED
@@ -15,7 +15,7 @@ npm install react-native-webgpu
15
15
  ## With Expo
16
16
 
17
17
  Expo provides a React Native WebGPU template that works with React Three Fiber.
18
- The works on iOS, Android, and Web.
18
+ This works on iOS, Android, and Web.
19
19
 
20
20
  ```
21
21
  npx create-expo-app@latest -e with-webgpu
@@ -174,8 +174,7 @@ ctx.canvas.height = ctx.canvas.clientHeight * PixelRatio.get();
174
174
 
175
175
  ### Frame Scheduling
176
176
 
177
- In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
178
- This means that when you are ready to present a frame, you need to call `present` on the context.
177
+ In React Native, frame presentation is a manual operation: when you are ready to present a frame, call `present()` on the context after submitting your commands to the queue. This works the same on every runtime: the main JS runtime, the Reanimated UI runtime, and dedicated worklet runtimes (`createWorkletRuntime` / `runOnRuntime`, or a Vision Camera frame processor). `present()` runs synchronously on the calling thread, so the frame is presented from whichever thread did the rendering.
179
178
 
180
179
  ```tsx
181
180
  // draw
@@ -185,6 +184,13 @@ device.queue.submit([commandEncoder.finish()]);
185
184
  context.present();
186
185
  ```
187
186
 
187
+ ### Threading model
188
+
189
+ react-native-webgpu can drive WebGPU from more than one JavaScript runtime: the main JS runtime, the Reanimated UI runtime, and dedicated worklet runtimes (`createWorkletRuntime` / `runOnRuntime`, or a Vision Camera frame processor).
190
+ This module also works well with [Bundle Mode](https://docs.swmansion.com/react-native-worklets/docs/bundleMode/) and lets you run complex Three.js scenes on the UI thread or dedicated worklet threads.
191
+
192
+ There is a caveat with `device.lost` and `uncapturederror`: they are only delivered on the main JS runtime. This is usually fine because the GPU device is typically created on the main JS thread and then sent to the UI or a dedicated worklet thread. However, if for some reason you create the device outside the main JS thread, beware that `device.lost` and `uncapturederror` won't fire.
193
+
188
194
  ### Canvas Transparency
189
195
 
190
196
  On Android, the `alphaMode` property is ignored when configuring the canvas.
@@ -293,10 +299,10 @@ const render = () => {
293
299
 
294
300
  // ... encode a pass that samples `externalTexture`, then:
295
301
  device.queue.submit([encoder.finish()]);
302
+ context.present();
296
303
 
297
304
  // Release the surface's access window right after the submit that sampled it.
298
305
  externalTexture.destroy();
299
- context.present();
300
306
  };
301
307
  ```
302
308
 
@@ -316,14 +322,21 @@ First, install the optional peer dependencies:
316
322
  npm install react-native-reanimated react-native-worklets
317
323
  ```
318
324
 
319
- WebGPU objects are automatically registered for Worklets serialization when the module loads. You can pass WebGPU objects like `GPUDevice` and `GPUCanvasContext` directly to worklets:
325
+ WebGPU objects are automatically registered for Worklets serialization when the module loads. You can pass WebGPU objects like `GPUDevice` and `GPUCanvasContext` directly to worklets.
326
+ Call `installWebGPU()` once at the top of the worklet to install flag constants like `GPUBufferUsage`, `GPUTextureUsage`, and so on.
320
327
 
321
328
  ```tsx
322
- import { Canvas } from "react-native-webgpu";
329
+ import { Canvas, installWebGPU } from "react-native-webgpu";
323
330
  import { runOnUI } from "react-native-reanimated";
324
331
 
325
332
  const renderFrame = (device: GPUDevice, context: GPUCanvasContext) => {
326
333
  "worklet";
334
+ installWebGPU();
335
+ // WebGPU constants are now available on this worklet thread
336
+ const buffer = device.createBuffer({
337
+ size,
338
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
339
+ });
327
340
  // WebGPU rendering code runs on the UI thread
328
341
  const commandEncoder = device.createCommandEncoder();
329
342
  // ... render ...
@@ -37,6 +37,7 @@ add_library(${PACKAGE_NAME} SHARED
37
37
  ../cpp/rnwgpu/api/GPUCommandEncoder.cpp
38
38
  ../cpp/rnwgpu/api/GPUQuerySet.cpp
39
39
  ../cpp/rnwgpu/api/GPUTexture.cpp
40
+ ../cpp/rnwgpu/api/GPUSharedFence.cpp
40
41
  ../cpp/rnwgpu/api/GPUSharedTextureMemory.cpp
41
42
  ../cpp/rnwgpu/api/GPUExternalTexture.cpp
42
43
  ../cpp/rnwgpu/api/GPURenderBundleEncoder.cpp
@@ -50,9 +51,8 @@ add_library(${PACKAGE_NAME} SHARED
50
51
  ../cpp/jsi/Promise.cpp
51
52
  ../cpp/jsi/RuntimeLifecycleMonitor.cpp
52
53
  ../cpp/jsi/RuntimeAwareCache.cpp
53
- ../cpp/rnwgpu/async/AsyncRunner.cpp
54
+ ../cpp/rnwgpu/async/RuntimeContext.cpp
54
55
  ../cpp/rnwgpu/async/AsyncTaskHandle.cpp
55
- ../cpp/rnwgpu/async/JSIMicrotaskDispatcher.cpp
56
56
  )
57
57
 
58
58
  target_include_directories(
@@ -439,6 +439,29 @@ protected:
439
439
  prototype.setProperty(runtime, name, func);
440
440
  }
441
441
 
442
+ /**
443
+ * Install a method whose native implementation needs the calling jsi::Runtime
444
+ * as its first parameter. Used by entry points that must act per-runtime
445
+ * (e.g. GPU::requestAdapter, which creates a per-runtime RuntimeContext).
446
+ */
447
+ template <typename ReturnType, typename... Args>
448
+ static void
449
+ installMethodWithRuntime(jsi::Runtime &runtime, jsi::Object &prototype,
450
+ const char *name,
451
+ ReturnType (Derived::*method)(jsi::Runtime &,
452
+ Args...)) {
453
+ auto func = jsi::Function::createFromHostFunction(
454
+ runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args),
455
+ [method](jsi::Runtime &rt, const jsi::Value &thisVal,
456
+ const jsi::Value *args, size_t count) -> jsi::Value {
457
+ auto native = Derived::fromValue(rt, thisVal);
458
+ return callMethodWithRuntime(native.get(), method, rt, args,
459
+ std::index_sequence_for<Args...>{},
460
+ count);
461
+ });
462
+ prototype.setProperty(runtime, name, func);
463
+ }
464
+
442
465
  /**
443
466
  * Install a getter on the prototype.
444
467
  */
@@ -574,6 +597,22 @@ protected:
574
597
  }
575
598
 
576
599
  private:
600
+ // Helper to call a method that takes the calling jsi::Runtime as its first
601
+ // parameter, with JSI argument conversion for the rest and JSI conversion of
602
+ // the result.
603
+ template <typename ReturnType, typename... Args, size_t... Is>
604
+ static jsi::Value
605
+ callMethodWithRuntime(Derived *obj,
606
+ ReturnType (Derived::*method)(jsi::Runtime &, Args...),
607
+ jsi::Runtime &runtime, const jsi::Value *args,
608
+ std::index_sequence<Is...>, size_t count) {
609
+ ReturnType result = (obj->*method)(
610
+ runtime, rnwgpu::JSIConverter<std::decay_t<Args>>::fromJSI(
611
+ runtime, args[Is], Is >= count)...);
612
+ return rnwgpu::JSIConverter<std::decay_t<ReturnType>>::toJSI(
613
+ runtime, std::move(result));
614
+ }
615
+
577
616
  // Helper to call a method with JSI argument conversion
578
617
  template <typename ReturnType, typename... Args, size_t... Is>
579
618
  static jsi::Value callMethod(Derived *obj,
@@ -31,6 +31,7 @@
31
31
  #include "GPURenderPassEncoder.h"
32
32
  #include "GPURenderPipeline.h"
33
33
  #include "GPUSampler.h"
34
+ #include "GPUSharedFence.h"
34
35
  #include "GPUSharedTextureMemory.h"
35
36
  #include "GPUShaderModule.h"
36
37
  #include "GPUSupportedLimits.h"
@@ -63,6 +64,12 @@ RNWebGPUManager::RNWebGPUManager(
63
64
  // Register main runtime for RuntimeAwareCache
64
65
  BaseRuntimeAwareCache::setMainJsRuntime(_jsRuntime);
65
66
 
67
+ // Register the main runtime + its CallInvoker so spontaneous events
68
+ // (device.lost / uncapturederror) on main-runtime devices can be delivered to
69
+ // the JS thread without the ProcessEvents pump. Worklet-runtime devices have
70
+ // no invoker (best-effort; see README "Threading model").
71
+ async::RuntimeContext::registerMainRuntime(_jsRuntime, _jsCallInvoker);
72
+
66
73
  auto gpu = std::make_shared<GPU>(*_jsRuntime);
67
74
  auto rnWebGPU =
68
75
  std::make_shared<RNWebGPU>(gpu, _platformContext, _jsCallInvoker);
@@ -106,6 +113,7 @@ RNWebGPUManager::RNWebGPUManager(
106
113
  GPURenderPassEncoder::installConstructor(*_jsRuntime);
107
114
  GPURenderPipeline::installConstructor(*_jsRuntime);
108
115
  GPUSampler::installConstructor(*_jsRuntime);
116
+ GPUSharedFence::installConstructor(*_jsRuntime);
109
117
  GPUSharedTextureMemory::installConstructor(*_jsRuntime);
110
118
  GPUShaderModule::installConstructor(*_jsRuntime);
111
119
  GPUSupportedLimits::installConstructor(*_jsRuntime);
@@ -7,6 +7,12 @@
7
7
 
8
8
  #include "webgpu/webgpu_cpp.h"
9
9
 
10
+ #ifdef __APPLE__
11
+ namespace dawn::native::metal {
12
+ void WaitForCommandsToBeScheduled(WGPUDevice device);
13
+ } // namespace dawn::native::metal
14
+ #endif
15
+
10
16
  namespace rnwgpu {
11
17
 
12
18
  struct NativeInfo {
@@ -113,7 +119,27 @@ public:
113
119
  height = newHeight;
114
120
  }
115
121
 
116
- void present() {
122
+ // Present the current surface texture. Called synchronously from the thread
123
+ // that did getCurrentTexture / submit (via GPUCanvasContext::present), so it
124
+ // preserves Dawn surface thread-affinity. No-op when offscreen / unconfigured
125
+ // (no surface).
126
+ void presentFrame() {
127
+ #ifdef __APPLE__
128
+ // Ensure command buffers are scheduled before presenting. Read the device
129
+ // under a shared lock, then wait without holding it (the wait can block).
130
+ // The device may be reconfigured between the two locks; that is safe because
131
+ // present() is called on the rendering thread right after submit(), the wait
132
+ // just flushes that thread's already-submitted work, and the Present() below
133
+ // re-checks `surface` under the unique lock before touching it.
134
+ wgpu::Device device;
135
+ {
136
+ std::shared_lock<std::shared_mutex> lock(_mutex);
137
+ device = config.device;
138
+ }
139
+ if (device) {
140
+ dawn::native::metal::WaitForCommandsToBeScheduled(device.Get());
141
+ }
142
+ #endif
117
143
  std::unique_lock<std::shared_mutex> lock(_mutex);
118
144
  if (surface) {
119
145
  surface.Present();
@@ -131,6 +157,12 @@ public:
131
157
  }
132
158
  }
133
159
 
160
+ // True when an on-screen wgpu::Surface is attached (vs offscreen texture).
161
+ bool hasSurface() {
162
+ std::shared_lock<std::shared_mutex> lock(_mutex);
163
+ return surface != nullptr;
164
+ }
165
+
134
166
  NativeInfo getNativeInfo() {
135
167
  std::shared_lock<std::shared_mutex> lock(_mutex);
136
168
  return {.nativeSurface = nativeSurface, .width = width, .height = height};
@@ -9,11 +9,11 @@
9
9
 
10
10
  #include "Convertors.h"
11
11
  #include "JSIConverter.h"
12
- #include "rnwgpu/async/JSIMicrotaskDispatcher.h"
12
+ #include "rnwgpu/async/RuntimeContext.h"
13
13
 
14
14
  namespace rnwgpu {
15
15
 
16
- GPU::GPU(jsi::Runtime &runtime) : NativeObject(CLASS_NAME) {
16
+ GPU::GPU(jsi::Runtime & /*runtime*/) : NativeObject(CLASS_NAME) {
17
17
  static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
18
18
  wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
19
19
  .requiredFeatures = &kTimedWaitAny};
@@ -48,12 +48,10 @@ GPU::GPU(jsi::Runtime &runtime) : NativeObject(CLASS_NAME) {
48
48
  instanceDesc.nextInChain = &toggles;
49
49
 
50
50
  _instance = wgpu::CreateInstance(&instanceDesc);
51
-
52
- auto dispatcher = std::make_shared<async::JSIMicrotaskDispatcher>(runtime);
53
- _async = async::AsyncRunner::getOrCreate(runtime, _instance, dispatcher);
54
51
  }
55
52
 
56
53
  async::AsyncTaskHandle GPU::requestAdapter(
54
+ jsi::Runtime &runtime,
57
55
  std::optional<std::shared_ptr<GPURequestAdapterOptions>> options) {
58
56
  wgpu::RequestAdapterOptions aOptions;
59
57
  Convertor conv;
@@ -66,12 +64,17 @@ async::AsyncTaskHandle GPU::requestAdapter(
66
64
  constexpr auto kDefaultBackendType = wgpu::BackendType::Vulkan;
67
65
  #endif
68
66
  aOptions.backendType = kDefaultBackendType;
69
- return _async->postTask(
70
- [this, aOptions](const async::AsyncTaskHandle::ResolveFunction &resolve,
71
- const async::AsyncTaskHandle::RejectFunction &reject) {
67
+
68
+ // Per-runtime context: async ops requested on this runtime resolve on this
69
+ // runtime's own thread (via its ProcessEvents pump).
70
+ auto context = async::RuntimeContext::getOrCreate(runtime, _instance);
71
+ return context->postTask(
72
+ [this, aOptions,
73
+ context](const async::AsyncTaskHandle::ResolveFunction &resolve,
74
+ const async::AsyncTaskHandle::RejectFunction &reject) {
72
75
  _instance.RequestAdapter(
73
76
  &aOptions, wgpu::CallbackMode::AllowProcessEvents,
74
- [asyncRunner = _async, resolve,
77
+ [context, resolve,
75
78
  reject](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter,
76
79
  wgpu::StringView message) {
77
80
  if (message.length) {
@@ -79,8 +82,8 @@ async::AsyncTaskHandle GPU::requestAdapter(
79
82
  }
80
83
 
81
84
  if (status == wgpu::RequestAdapterStatus::Success && adapter) {
82
- auto adapterHost = std::make_shared<GPUAdapter>(
83
- std::move(adapter), asyncRunner);
85
+ auto adapterHost =
86
+ std::make_shared<GPUAdapter>(std::move(adapter), context);
84
87
  auto result =
85
88
  std::variant<std::nullptr_t, std::shared_ptr<GPUAdapter>>(
86
89
  adapterHost);
@@ -9,8 +9,8 @@
9
9
 
10
10
  #include "NativeObject.h"
11
11
 
12
- #include "rnwgpu/async/AsyncRunner.h"
13
12
  #include "rnwgpu/async/AsyncTaskHandle.h"
13
+ #include "rnwgpu/async/RuntimeContext.h"
14
14
 
15
15
  #include "webgpu/webgpu_cpp.h"
16
16
 
@@ -32,7 +32,10 @@ public:
32
32
  public:
33
33
  std::string getBrand() { return CLASS_NAME; }
34
34
 
35
+ // requestAdapter needs the calling runtime so each runtime gets its own
36
+ // RuntimeContext (and ProcessEvents pump on its own thread).
35
37
  async::AsyncTaskHandle requestAdapter(
38
+ jsi::Runtime &runtime,
36
39
  std::optional<std::shared_ptr<GPURequestAdapterOptions>> options);
37
40
  wgpu::TextureFormat getPreferredCanvasFormat();
38
41
 
@@ -40,7 +43,8 @@ public:
40
43
 
41
44
  static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
42
45
  installGetter(runtime, prototype, "__brand", &GPU::getBrand);
43
- installMethod(runtime, prototype, "requestAdapter", &GPU::requestAdapter);
46
+ installMethodWithRuntime(runtime, prototype, "requestAdapter",
47
+ &GPU::requestAdapter);
44
48
  installMethod(runtime, prototype, "getPreferredCanvasFormat",
45
49
  &GPU::getPreferredCanvasFormat);
46
50
  installGetter(runtime, prototype, "wgslLanguageFeatures",
@@ -48,11 +52,9 @@ public:
48
52
  }
49
53
 
50
54
  inline const wgpu::Instance get() { return _instance; }
51
- inline std::shared_ptr<async::AsyncRunner> getAsyncRunner() { return _async; }
52
55
 
53
56
  private:
54
57
  wgpu::Instance _instance;
55
- std::shared_ptr<async::AsyncRunner> _async;
56
58
  };
57
59
 
58
60
  } // namespace rnwgpu
@@ -164,10 +164,9 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
164
164
  }
165
165
  _instance.RequestDevice(
166
166
  &deviceDesc, wgpu::CallbackMode::AllowProcessEvents,
167
- [asyncRunner = _async, resolve, reject, label, creationRuntime,
167
+ [context = _async, resolve, reject, label, creationRuntime,
168
168
  deviceLostBinding](wgpu::RequestDeviceStatus status,
169
- wgpu::Device device,
170
- wgpu::StringView message) {
169
+ wgpu::Device device, wgpu::StringView message) {
171
170
  if (message.length) {
172
171
  fprintf(stderr, "%s", message.data);
173
172
  }
@@ -191,14 +190,12 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
191
190
  case wgpu::LoggingType::Warning:
192
191
  logLevel = "Warning";
193
192
  Logger::warnToJavascriptConsole(
194
- *creationRuntime,
195
- std::string(msg.data, msg.length));
193
+ *creationRuntime, std::string(msg.data, msg.length));
196
194
  break;
197
195
  case wgpu::LoggingType::Error:
198
196
  logLevel = "Error";
199
197
  Logger::errorToJavascriptConsole(
200
- *creationRuntime,
201
- std::string(msg.data, msg.length));
198
+ *creationRuntime, std::string(msg.data, msg.length));
202
199
  break;
203
200
  case wgpu::LoggingType::Verbose:
204
201
  logLevel = "Verbose";
@@ -216,7 +213,7 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
216
213
  creationRuntime);
217
214
 
218
215
  auto deviceHost = std::make_shared<GPUDevice>(std::move(device),
219
- asyncRunner, label);
216
+ context, label);
220
217
  *deviceLostBinding = deviceHost;
221
218
 
222
219
  // Register the device in the static registry so the uncaptured
@@ -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
 
@@ -27,7 +27,7 @@ public:
27
27
  static constexpr const char *CLASS_NAME = "GPUAdapter";
28
28
 
29
29
  explicit GPUAdapter(wgpu::Adapter instance,
30
- std::shared_ptr<async::AsyncRunner> async)
30
+ std::shared_ptr<async::RuntimeContext> async)
31
31
  : NativeObject(CLASS_NAME), _instance(instance), _async(async) {}
32
32
 
33
33
  public:
@@ -53,7 +53,7 @@ public:
53
53
 
54
54
  private:
55
55
  wgpu::Adapter _instance;
56
- std::shared_ptr<async::AsyncRunner> _async;
56
+ std::shared_ptr<async::RuntimeContext> _async;
57
57
  };
58
58
 
59
59
  } // namespace rnwgpu
@@ -55,30 +55,29 @@ async::AsyncTaskHandle GPUBuffer::mapAsync(uint64_t modeIn,
55
55
  [bufferHandle, mode, resolvedOffset,
56
56
  rangeSize](const async::AsyncTaskHandle::ResolveFunction &resolve,
57
57
  const async::AsyncTaskHandle::RejectFunction &reject) {
58
- bufferHandle.MapAsync(mode, resolvedOffset, rangeSize,
59
- wgpu::CallbackMode::AllowProcessEvents,
60
- [resolve, reject](wgpu::MapAsyncStatus status,
61
- wgpu::StringView message) {
62
- switch (status) {
63
- case wgpu::MapAsyncStatus::Success:
64
- resolve(nullptr);
65
- break;
66
- case wgpu::MapAsyncStatus::CallbackCancelled:
67
- reject("MapAsyncStatus::CallbackCancelled");
68
- break;
69
- case wgpu::MapAsyncStatus::Error:
70
- reject("MapAsyncStatus::Error");
71
- break;
72
- case wgpu::MapAsyncStatus::Aborted:
73
- reject("MapAsyncStatus::Aborted");
74
- break;
75
- default:
76
- reject(
77
- "MapAsyncStatus: " +
78
- std::to_string(static_cast<int>(status)));
79
- break;
80
- }
81
- });
58
+ bufferHandle.MapAsync(
59
+ mode, resolvedOffset, rangeSize, wgpu::CallbackMode::AllowProcessEvents,
60
+ [resolve, reject](wgpu::MapAsyncStatus status,
61
+ wgpu::StringView message) {
62
+ switch (status) {
63
+ case wgpu::MapAsyncStatus::Success:
64
+ resolve(nullptr);
65
+ break;
66
+ case wgpu::MapAsyncStatus::CallbackCancelled:
67
+ reject("MapAsyncStatus::CallbackCancelled");
68
+ break;
69
+ case wgpu::MapAsyncStatus::Error:
70
+ reject("MapAsyncStatus::Error");
71
+ break;
72
+ case wgpu::MapAsyncStatus::Aborted:
73
+ reject("MapAsyncStatus::Aborted");
74
+ break;
75
+ default:
76
+ reject("MapAsyncStatus: " +
77
+ std::to_string(static_cast<int>(status)));
78
+ break;
79
+ }
80
+ });
82
81
  });
83
82
  }
84
83
 
@@ -9,8 +9,8 @@
9
9
 
10
10
  #include "NativeObject.h"
11
11
 
12
- #include "rnwgpu/async/AsyncRunner.h"
13
12
  #include "rnwgpu/async/AsyncTaskHandle.h"
13
+ #include "rnwgpu/async/RuntimeContext.h"
14
14
 
15
15
  #include "webgpu/webgpu_cpp.h"
16
16
 
@@ -25,7 +25,7 @@ public:
25
25
  static constexpr const char *CLASS_NAME = "GPUBuffer";
26
26
 
27
27
  explicit GPUBuffer(wgpu::Buffer instance,
28
- std::shared_ptr<async::AsyncRunner> async,
28
+ std::shared_ptr<async::RuntimeContext> async,
29
29
  std::string label)
30
30
  : NativeObject(CLASS_NAME), _instance(instance), _async(async),
31
31
  _label(label) {}
@@ -71,7 +71,7 @@ public:
71
71
 
72
72
  private:
73
73
  wgpu::Buffer _instance;
74
- std::shared_ptr<async::AsyncRunner> _async;
74
+ std::shared_ptr<async::RuntimeContext> _async;
75
75
  std::string _label;
76
76
  struct Mapping {
77
77
  uint64_t start;
@@ -3,14 +3,6 @@
3
3
  #include "RNWebGPUManager.h"
4
4
  #include <memory>
5
5
 
6
- #ifdef __APPLE__
7
- namespace dawn::native::metal {
8
-
9
- void WaitForCommandsToBeScheduled(WGPUDevice device);
10
-
11
- }
12
- #endif
13
-
14
6
  namespace rnwgpu {
15
7
 
16
8
  void GPUCanvasContext::configure(
@@ -47,21 +39,26 @@ std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
47
39
  if (sizeHasChanged) {
48
40
  _surfaceInfo->reconfigure(width, height);
49
41
  }
42
+
50
43
  auto texture = _surfaceInfo->getCurrentTexture();
44
+
45
+ auto size = _surfaceInfo->getSize();
46
+ _canvas->setClientWidth(size.width);
47
+ _canvas->setClientHeight(size.height);
48
+
51
49
  // Pass reportsMemoryPressure=false to avoid triggering spurious Hermes GC
52
50
  // cycles every frame since the canvas texture doesn't own the buffer.
53
51
  return std::make_shared<GPUTexture>(texture, "", false);
54
52
  }
55
53
 
56
54
  void GPUCanvasContext::present() {
57
- #ifdef __APPLE__
58
- dawn::native::metal::WaitForCommandsToBeScheduled(
59
- _surfaceInfo->getDevice().Get());
60
- #endif
61
- auto size = _surfaceInfo->getSize();
62
- _canvas->setClientWidth(size.width);
63
- _canvas->setClientHeight(size.height);
64
- _surfaceInfo->present();
55
+ // Present runs synchronously on the calling thread (the one that did
56
+ // getCurrentTexture / submit), preserving Dawn surface thread-affinity.
57
+ // Required on every runtime (main JS, Reanimated UI, dedicated worklet);
58
+ // offscreen surfaces have no wgpu::Surface so they no-op.
59
+ if (_surfaceInfo->hasSurface()) {
60
+ _surfaceInfo->presentFrame();
61
+ }
65
62
  }
66
63
 
67
64
  } // namespace rnwgpu
@@ -55,6 +55,9 @@ public:
55
55
  void configure(std::shared_ptr<GPUCanvasConfiguration> configuration);
56
56
  void unconfigure();
57
57
  std::shared_ptr<GPUTexture> getCurrentTexture();
58
+ // Present is explicit on every runtime (main JS, Reanimated UI, and dedicated
59
+ // worklet runtimes). It runs synchronously on the calling thread, preserving
60
+ // Dawn surface thread-affinity; offscreen surfaces no-op.
58
61
  void present();
59
62
 
60
63
  private: