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.
- package/README.md +19 -6
- package/android/CMakeLists.txt +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/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/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/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/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/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
package/src/install.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GPUBufferUsage,
|
|
3
|
+
GPUColorWrite,
|
|
4
|
+
GPUMapMode,
|
|
5
|
+
GPUShaderStage,
|
|
6
|
+
GPUTextureUsage,
|
|
7
|
+
} from "./constants";
|
|
8
|
+
|
|
9
|
+
// Globals that this function installs on the calling runtime. These are the
|
|
10
|
+
// native-derived flag constants re-exported from `./constants` (a single source
|
|
11
|
+
// of truth, matching the native `wgpu::*Usage` enums), so they are safe to set
|
|
12
|
+
// on any runtime.
|
|
13
|
+
const constants = {
|
|
14
|
+
GPUBufferUsage,
|
|
15
|
+
GPUTextureUsage,
|
|
16
|
+
GPUShaderStage,
|
|
17
|
+
GPUColorWrite,
|
|
18
|
+
GPUMapMode,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Install WebGPU on the runtime that calls it.
|
|
23
|
+
*
|
|
24
|
+
* The native module installs the WebGPU flag constants (`GPUBufferUsage`,
|
|
25
|
+
* `GPUTextureUsage`, `GPUShaderStage`, `GPUColorWrite`, `GPUMapMode`) as globals
|
|
26
|
+
* on the main JS runtime, but worklet runtimes (Reanimated UI, dedicated worklet
|
|
27
|
+
* runtimes, Vision Camera frame processors) start without them, so referencing
|
|
28
|
+
* the bare global inside a worklet yields `undefined`.
|
|
29
|
+
*
|
|
30
|
+
* Call `installWebGPU()` once at the top of a worklet to make those globals
|
|
31
|
+
* available there, instead of importing each constant by hand:
|
|
32
|
+
*
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import { installWebGPU } from "react-native-webgpu";
|
|
35
|
+
*
|
|
36
|
+
* const work = (device: GPUDevice) => {
|
|
37
|
+
* "worklet";
|
|
38
|
+
* installWebGPU();
|
|
39
|
+
* device.createBuffer({
|
|
40
|
+
* usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
41
|
+
* });
|
|
42
|
+
* };
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* The constants are captured into the worklet by closure (the same way a shader
|
|
46
|
+
* string is), so they work on every runtime. Calling it on a runtime that
|
|
47
|
+
* already has the globals (e.g. the main JS runtime) is a safe no-op.
|
|
48
|
+
*
|
|
49
|
+
* This is the explicit entry point for runtime setup; for now it only installs
|
|
50
|
+
* the flag constants, but it is the place where other per-runtime WebGPU setup
|
|
51
|
+
* (e.g. `navigator.gpu`) can be wired in later.
|
|
52
|
+
*/
|
|
53
|
+
export const installWebGPU = () => {
|
|
54
|
+
"worklet";
|
|
55
|
+
const g = globalThis as unknown as Record<string, unknown>;
|
|
56
|
+
for (const [key, value] of Object.entries(constants)) {
|
|
57
|
+
if (g[key] === undefined) {
|
|
58
|
+
g[key] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -9,6 +9,15 @@ export interface NativeCanvas {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export type RNCanvasContext = GPUCanvasContext & {
|
|
12
|
+
/**
|
|
13
|
+
* Present the current frame.
|
|
14
|
+
*
|
|
15
|
+
* Call this after `queue.submit()` on every runtime: the main JS runtime, the
|
|
16
|
+
* Reanimated UI runtime, and dedicated worklet runtimes (e.g.
|
|
17
|
+
* `createWorkletRuntime` / `runOnRuntime`, or a Vision Camera frame
|
|
18
|
+
* processor). It runs synchronously on the calling thread, so the frame is
|
|
19
|
+
* presented from whichever thread did the rendering.
|
|
20
|
+
*/
|
|
12
21
|
present: () => void;
|
|
13
22
|
};
|
|
14
23
|
|
|
@@ -75,6 +84,55 @@ export interface GPUSharedTextureMemoryDescriptor {
|
|
|
75
84
|
label?: string;
|
|
76
85
|
}
|
|
77
86
|
|
|
87
|
+
// The kind of native synchronization primitive a GPUSharedFence wraps, matching
|
|
88
|
+
// the shared-fence-* device feature names. Limited to the kinds react-native-webgpu
|
|
89
|
+
// targets (iOS/Metal and Android/Vulkan); importSharedFence accepts these and
|
|
90
|
+
// export() reports them.
|
|
91
|
+
export type GPUSharedFenceType =
|
|
92
|
+
| "mtl-shared-event"
|
|
93
|
+
| "sync-fd"
|
|
94
|
+
| "vk-semaphore-opaque-fd";
|
|
95
|
+
|
|
96
|
+
export interface GPUSharedFenceDescriptor {
|
|
97
|
+
// The fence kind to import. Must match a shared-fence-* feature enabled on
|
|
98
|
+
// the device.
|
|
99
|
+
type: GPUSharedFenceType;
|
|
100
|
+
// The raw native handle as a BigInt: an id<MTLSharedEvent> pointer for
|
|
101
|
+
// "mtl-shared-event", or an OS file descriptor for the *-fd kinds.
|
|
102
|
+
handle: bigint;
|
|
103
|
+
label?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface GPUSharedFenceExportInfo {
|
|
107
|
+
type: GPUSharedFenceType;
|
|
108
|
+
// An id<MTLSharedEvent> pointer (Apple) or file descriptor (Android), as a
|
|
109
|
+
// BigInt. The caller takes ownership; e.g. an exported sync-fd must be
|
|
110
|
+
// closed once consumed.
|
|
111
|
+
handle: bigint;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// A native GPU synchronization primitive shared across queues/APIs. Produced by
|
|
115
|
+
// GPUSharedTextureMemory.endAccess(), consumed by beginAccess(), or imported
|
|
116
|
+
// from a consumer's fence with GPUDevice.importSharedFence().
|
|
117
|
+
export interface GPUSharedFence {
|
|
118
|
+
readonly __brand: "GPUSharedFence";
|
|
119
|
+
label: string;
|
|
120
|
+
export(): GPUSharedFenceExportInfo;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// A fence and the timeline value to wait for (0n for binary sync-fd fences).
|
|
124
|
+
export interface GPUSharedFenceState {
|
|
125
|
+
fence: GPUSharedFence;
|
|
126
|
+
signaledValue: bigint;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// The result of GPUSharedTextureMemory.endAccess(): each fence is signaled at
|
|
130
|
+
// its signaledValue once Dawn's GPU work for the access completes.
|
|
131
|
+
export interface GPUSharedTextureMemoryEndAccessState {
|
|
132
|
+
initialized: boolean;
|
|
133
|
+
fences: GPUSharedFenceState[];
|
|
134
|
+
}
|
|
135
|
+
|
|
78
136
|
// Non-standard, Dawn-only device toggles. Mirrors Dawn's DawnTogglesDescriptor
|
|
79
137
|
// and is chained onto the native device descriptor at requestDevice time.
|
|
80
138
|
// Pass it via the (augmented) GPUDeviceDescriptor: adapter.requestDevice({
|
|
@@ -96,7 +154,15 @@ export interface GPUSharedTextureMemory {
|
|
|
96
154
|
createTexture(descriptor?: GPUTextureDescriptor): GPUTexture;
|
|
97
155
|
// `initialized` declares whether the surface already holds meaningful pixels
|
|
98
156
|
// (true for an incoming video/camera frame, false if the next pass will fully
|
|
99
|
-
// overwrite it).
|
|
100
|
-
|
|
101
|
-
|
|
157
|
+
// overwrite it). Optional `fences` are wait fences: Dawn waits for each to
|
|
158
|
+
// reach its signaledValue before writing the surface. Throws if the access
|
|
159
|
+
// could not begin.
|
|
160
|
+
beginAccess(
|
|
161
|
+
texture: GPUTexture,
|
|
162
|
+
initialized: boolean,
|
|
163
|
+
fences?: GPUSharedFenceState[],
|
|
164
|
+
): void;
|
|
165
|
+
// Ends the access and returns the fences Dawn produced for it. Throws if the
|
|
166
|
+
// access could not be ended.
|
|
167
|
+
endAccess(texture: GPUTexture): GPUSharedTextureMemoryEndAccessState;
|
|
102
168
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <functional>
|
|
4
|
-
#include <memory>
|
|
5
|
-
|
|
6
|
-
#include <jsi/jsi.h>
|
|
7
|
-
|
|
8
|
-
namespace rnwgpu::async {
|
|
9
|
-
|
|
10
|
-
namespace jsi = facebook::jsi;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Abstract dispatcher used by the AsyncRunner to enqueue work back onto the
|
|
14
|
-
* JavaScript thread.
|
|
15
|
-
*/
|
|
16
|
-
class AsyncDispatcher {
|
|
17
|
-
public:
|
|
18
|
-
using Work = std::function<void(jsi::Runtime &)>;
|
|
19
|
-
|
|
20
|
-
virtual ~AsyncDispatcher() = default;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Enqueue a unit of work that will be executed on the JavaScript thread.
|
|
24
|
-
*/
|
|
25
|
-
virtual void post(Work work) = 0;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
} // namespace rnwgpu::async
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
#include "AsyncRunner.h"
|
|
2
|
-
|
|
3
|
-
#include <chrono>
|
|
4
|
-
#include <stdexcept>
|
|
5
|
-
#include <utility>
|
|
6
|
-
|
|
7
|
-
#include "AsyncTaskHandle.h"
|
|
8
|
-
#include "WGPULogger.h"
|
|
9
|
-
|
|
10
|
-
namespace rnwgpu::async {
|
|
11
|
-
|
|
12
|
-
namespace {
|
|
13
|
-
struct RuntimeData {
|
|
14
|
-
std::shared_ptr<AsyncRunner> runner;
|
|
15
|
-
};
|
|
16
|
-
constexpr const char *TAG = "AsyncRunner";
|
|
17
|
-
} // namespace
|
|
18
|
-
|
|
19
|
-
AsyncRunner::AsyncRunner(wgpu::Instance instance,
|
|
20
|
-
std::shared_ptr<AsyncDispatcher> dispatcher)
|
|
21
|
-
: _instance(std::move(instance)), _dispatcher(std::move(dispatcher)),
|
|
22
|
-
_pendingTasks(0), _pumpTasks(0), _tickScheduled(false),
|
|
23
|
-
_lastTickTimeNs(0) {
|
|
24
|
-
if (!_dispatcher) {
|
|
25
|
-
throw std::runtime_error("AsyncRunner requires a valid dispatcher.");
|
|
26
|
-
}
|
|
27
|
-
Logger::logToConsole("[%s] Created runner (dispatcher=%p)", TAG,
|
|
28
|
-
_dispatcher.get());
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
std::shared_ptr<AsyncRunner> AsyncRunner::get(jsi::Runtime &runtime) {
|
|
32
|
-
auto data = runtime.getRuntimeData(runtimeDataUUID());
|
|
33
|
-
if (!data) {
|
|
34
|
-
return nullptr;
|
|
35
|
-
}
|
|
36
|
-
auto stored = std::static_pointer_cast<RuntimeData>(data);
|
|
37
|
-
return stored->runner;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
std::shared_ptr<AsyncRunner>
|
|
41
|
-
AsyncRunner::getOrCreate(jsi::Runtime &runtime, wgpu::Instance instance,
|
|
42
|
-
std::shared_ptr<AsyncDispatcher> dispatcher) {
|
|
43
|
-
auto existing = get(runtime);
|
|
44
|
-
if (existing) {
|
|
45
|
-
return existing;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
auto runner =
|
|
49
|
-
std::make_shared<AsyncRunner>(std::move(instance), std::move(dispatcher));
|
|
50
|
-
auto data = std::make_shared<RuntimeData>();
|
|
51
|
-
data->runner = runner;
|
|
52
|
-
runtime.setRuntimeData(runtimeDataUUID(), data);
|
|
53
|
-
return runner;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
AsyncTaskHandle AsyncRunner::postTask(const TaskCallback &callback,
|
|
57
|
-
bool keepPumping) {
|
|
58
|
-
auto handle = AsyncTaskHandle::create(shared_from_this(), keepPumping);
|
|
59
|
-
if (!handle.valid()) {
|
|
60
|
-
throw std::runtime_error("Failed to create AsyncTaskHandle.");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
_pendingTasks.fetch_add(1, std::memory_order_acq_rel);
|
|
64
|
-
if (keepPumping) {
|
|
65
|
-
_pumpTasks.fetch_add(1, std::memory_order_acq_rel);
|
|
66
|
-
}
|
|
67
|
-
requestTick();
|
|
68
|
-
|
|
69
|
-
Logger::logToConsole(
|
|
70
|
-
"[%s] postTask (keepPumping=%s, pending=%zu, pumping=%zu)", TAG,
|
|
71
|
-
keepPumping ? "true" : "false",
|
|
72
|
-
_pendingTasks.load(std::memory_order_acquire),
|
|
73
|
-
_pumpTasks.load(std::memory_order_acquire));
|
|
74
|
-
|
|
75
|
-
auto resolve = handle.createResolveFunction();
|
|
76
|
-
auto reject = handle.createRejectFunction();
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
callback(resolve, reject);
|
|
80
|
-
} catch (const std::exception &exception) {
|
|
81
|
-
reject(exception.what());
|
|
82
|
-
} catch (...) {
|
|
83
|
-
reject("Unknown native error in AsyncRunner::postTask.");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return handle;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
void AsyncRunner::requestTick() {
|
|
90
|
-
bool expected = false;
|
|
91
|
-
if (!_tickScheduled.compare_exchange_strong(expected, true,
|
|
92
|
-
std::memory_order_acq_rel)) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
auto self = shared_from_this();
|
|
97
|
-
_dispatcher->post([self](jsi::Runtime &runtime) {
|
|
98
|
-
auto tickCallback = jsi::Function::createFromHostFunction(
|
|
99
|
-
runtime, jsi::PropNameID::forAscii(runtime, "AsyncRunnerTick"), 0,
|
|
100
|
-
[self](jsi::Runtime &runtime, const jsi::Value & /*thisValue*/,
|
|
101
|
-
const jsi::Value * /*args*/, size_t /*count*/) -> jsi::Value {
|
|
102
|
-
self->tick(runtime);
|
|
103
|
-
return jsi::Value::undefined();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
#if defined(ANDROID) || defined(__ANDROID__)
|
|
107
|
-
auto global = runtime.global();
|
|
108
|
-
auto setImmediateValue = global.getProperty(runtime, "setImmediate");
|
|
109
|
-
constexpr auto kMinTickInterval = std::chrono::milliseconds(4);
|
|
110
|
-
const int64_t nowNs =
|
|
111
|
-
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
112
|
-
std::chrono::steady_clock::now().time_since_epoch())
|
|
113
|
-
.count();
|
|
114
|
-
const int64_t lastNs =
|
|
115
|
-
self->_lastTickTimeNs.load(std::memory_order_acquire);
|
|
116
|
-
int delayMs = 0;
|
|
117
|
-
if (lastNs > 0) {
|
|
118
|
-
const int64_t elapsedNs = nowNs - lastNs;
|
|
119
|
-
const int64_t minIntervalNs = kMinTickInterval.count() * 1000000LL;
|
|
120
|
-
if (elapsedNs < minIntervalNs) {
|
|
121
|
-
const int64_t remainingNs = minIntervalNs - elapsedNs;
|
|
122
|
-
delayMs = static_cast<int>((remainingNs + 999999) / 1000000);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
auto tryScheduleTimeout = [&](int ms) {
|
|
127
|
-
auto setTimeoutValue = global.getProperty(runtime, "setTimeout");
|
|
128
|
-
if (!setTimeoutValue.isObject()) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
auto setTimeoutObj = setTimeoutValue.asObject(runtime);
|
|
132
|
-
if (!setTimeoutObj.isFunction(runtime)) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
Logger::logToConsole("[%s] requestTick scheduled via setTimeout(%d)", TAG,
|
|
136
|
-
ms);
|
|
137
|
-
auto setTimeoutFn = setTimeoutObj.asFunction(runtime);
|
|
138
|
-
jsi::Value callbackArg(runtime, tickCallback);
|
|
139
|
-
jsi::Value delayArg(static_cast<double>(ms));
|
|
140
|
-
setTimeoutFn.call(runtime, callbackArg, delayArg);
|
|
141
|
-
return true;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
if (delayMs > 0) {
|
|
145
|
-
if (tryScheduleTimeout(delayMs)) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
// If setTimeout unavailable fall through to immediate scheduling.
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (setImmediateValue.isObject()) {
|
|
152
|
-
auto setImmediateObj = setImmediateValue.asObject(runtime);
|
|
153
|
-
if (setImmediateObj.isFunction(runtime)) {
|
|
154
|
-
Logger::logToConsole("[%s] requestTick scheduled via setImmediate",
|
|
155
|
-
TAG);
|
|
156
|
-
auto setImmediateFn = setImmediateObj.asFunction(runtime);
|
|
157
|
-
jsi::Value callbackArg(runtime, tickCallback);
|
|
158
|
-
setImmediateFn.call(runtime, callbackArg);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
int timeoutDelayMs = delayMs > 0 ? delayMs : 0;
|
|
164
|
-
if (tryScheduleTimeout(timeoutDelayMs)) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
Logger::logToConsole("[%s] requestTick scheduled via microtask fallback",
|
|
169
|
-
TAG);
|
|
170
|
-
runtime.queueMicrotask(std::move(tickCallback));
|
|
171
|
-
#else
|
|
172
|
-
Logger::logToConsole("[%s] requestTick scheduled microtask (non-Android)",
|
|
173
|
-
TAG);
|
|
174
|
-
runtime.queueMicrotask(std::move(tickCallback));
|
|
175
|
-
#endif
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
void AsyncRunner::tick(jsi::Runtime & /*runtime*/) {
|
|
180
|
-
_tickScheduled.store(false, std::memory_order_release);
|
|
181
|
-
_instance.ProcessEvents();
|
|
182
|
-
const auto nowNs = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
183
|
-
std::chrono::steady_clock::now().time_since_epoch())
|
|
184
|
-
.count();
|
|
185
|
-
_lastTickTimeNs.store(nowNs, std::memory_order_release);
|
|
186
|
-
Logger::logToConsole("[%s] tick processed events (pending=%zu, pumping=%zu)",
|
|
187
|
-
TAG, _pendingTasks.load(std::memory_order_acquire),
|
|
188
|
-
_pumpTasks.load(std::memory_order_acquire));
|
|
189
|
-
if (_pumpTasks.load(std::memory_order_acquire) > 0) {
|
|
190
|
-
requestTick();
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
void AsyncRunner::onTaskSettled(bool keepPumping) {
|
|
195
|
-
_pendingTasks.fetch_sub(1, std::memory_order_acq_rel);
|
|
196
|
-
if (keepPumping) {
|
|
197
|
-
_pumpTasks.fetch_sub(1, std::memory_order_acq_rel);
|
|
198
|
-
}
|
|
199
|
-
Logger::logToConsole(
|
|
200
|
-
"[%s] onTaskSettled (keepPumping=%s, pending=%zu, pumping=%zu)", TAG,
|
|
201
|
-
keepPumping ? "true" : "false",
|
|
202
|
-
_pendingTasks.load(std::memory_order_acquire),
|
|
203
|
-
_pumpTasks.load(std::memory_order_acquire));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
std::shared_ptr<AsyncDispatcher> AsyncRunner::dispatcher() const {
|
|
207
|
-
return _dispatcher;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
jsi::UUID AsyncRunner::runtimeDataUUID() {
|
|
211
|
-
static const auto uuid = jsi::UUID();
|
|
212
|
-
return uuid;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
} // namespace rnwgpu::async
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <atomic>
|
|
4
|
-
#include <cstdint>
|
|
5
|
-
#include <functional>
|
|
6
|
-
#include <memory>
|
|
7
|
-
|
|
8
|
-
#include <jsi/jsi.h>
|
|
9
|
-
|
|
10
|
-
#include "AsyncDispatcher.h"
|
|
11
|
-
#include "AsyncTaskHandle.h"
|
|
12
|
-
|
|
13
|
-
#include "webgpu/webgpu_cpp.h"
|
|
14
|
-
|
|
15
|
-
namespace jsi = facebook::jsi;
|
|
16
|
-
|
|
17
|
-
namespace rnwgpu::async {
|
|
18
|
-
|
|
19
|
-
class AsyncRunner : public std::enable_shared_from_this<AsyncRunner> {
|
|
20
|
-
public:
|
|
21
|
-
using TaskCallback =
|
|
22
|
-
std::function<void(const AsyncTaskHandle::ResolveFunction &,
|
|
23
|
-
const AsyncTaskHandle::RejectFunction &)>;
|
|
24
|
-
|
|
25
|
-
AsyncRunner(wgpu::Instance instance,
|
|
26
|
-
std::shared_ptr<AsyncDispatcher> dispatcher);
|
|
27
|
-
|
|
28
|
-
static std::shared_ptr<AsyncRunner> get(jsi::Runtime &runtime);
|
|
29
|
-
static std::shared_ptr<AsyncRunner>
|
|
30
|
-
getOrCreate(jsi::Runtime &runtime, wgpu::Instance instance,
|
|
31
|
-
std::shared_ptr<AsyncDispatcher> dispatcher);
|
|
32
|
-
|
|
33
|
-
AsyncTaskHandle postTask(const TaskCallback &callback,
|
|
34
|
-
bool keepPumping = true);
|
|
35
|
-
|
|
36
|
-
void requestTick();
|
|
37
|
-
void tick(jsi::Runtime &runtime);
|
|
38
|
-
void onTaskSettled(bool keepPumping);
|
|
39
|
-
|
|
40
|
-
std::shared_ptr<AsyncDispatcher> dispatcher() const;
|
|
41
|
-
|
|
42
|
-
private:
|
|
43
|
-
static jsi::UUID runtimeDataUUID();
|
|
44
|
-
|
|
45
|
-
wgpu::Instance _instance;
|
|
46
|
-
std::shared_ptr<AsyncDispatcher> _dispatcher;
|
|
47
|
-
std::atomic<size_t> _pendingTasks;
|
|
48
|
-
std::atomic<size_t> _pumpTasks;
|
|
49
|
-
std::atomic<bool> _tickScheduled;
|
|
50
|
-
std::atomic<int64_t> _lastTickTimeNs;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
} // namespace rnwgpu::async
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#include "JSIMicrotaskDispatcher.h"
|
|
2
|
-
|
|
3
|
-
#include <utility>
|
|
4
|
-
|
|
5
|
-
namespace rnwgpu::async {
|
|
6
|
-
|
|
7
|
-
JSIMicrotaskDispatcher::JSIMicrotaskDispatcher(jsi::Runtime &runtime)
|
|
8
|
-
: _runtime(runtime) {}
|
|
9
|
-
|
|
10
|
-
void JSIMicrotaskDispatcher::post(Work work) {
|
|
11
|
-
auto microtask = jsi::Function::createFromHostFunction(
|
|
12
|
-
_runtime, jsi::PropNameID::forAscii(_runtime, "AsyncMicrotask"), 0,
|
|
13
|
-
[work = std::move(work)](
|
|
14
|
-
jsi::Runtime &runtime, const jsi::Value & /*thisValue*/,
|
|
15
|
-
const jsi::Value * /*args*/, size_t /*count*/) -> jsi::Value {
|
|
16
|
-
work(runtime);
|
|
17
|
-
return jsi::Value::undefined();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
_runtime.queueMicrotask(std::move(microtask));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
} // namespace rnwgpu::async
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include "AsyncDispatcher.h"
|
|
4
|
-
|
|
5
|
-
namespace rnwgpu::async {
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Dispatcher implementation backed by `jsi::Runtime::queueMicrotask`.
|
|
9
|
-
*/
|
|
10
|
-
class JSIMicrotaskDispatcher final
|
|
11
|
-
: public AsyncDispatcher,
|
|
12
|
-
public std::enable_shared_from_this<JSIMicrotaskDispatcher> {
|
|
13
|
-
public:
|
|
14
|
-
explicit JSIMicrotaskDispatcher(jsi::Runtime &runtime);
|
|
15
|
-
|
|
16
|
-
void post(Work work) override;
|
|
17
|
-
|
|
18
|
-
private:
|
|
19
|
-
jsi::Runtime &_runtime;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
} // namespace rnwgpu::async
|