react-native-webgpu 0.13.1 → 0.14.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/CMakeLists.txt +1 -0
- package/android/cpp/AndroidPlatformContext.h +0 -9
- package/android/cpp/cpp-adapter.cpp +6 -4
- package/android/src/main/java/com/webgpu/WebGPUView.java +8 -0
- package/apple/ApplePlatformContext.h +0 -3
- package/apple/ApplePlatformContext.mm +0 -10
- package/apple/MetalView.mm +4 -3
- package/cpp/jsi/JSIConverter.h +6 -11
- package/cpp/jsi/NativeObject.h +130 -18
- package/cpp/rnwgpu/EnforceRange.h +68 -0
- package/cpp/rnwgpu/PlatformContext.h +0 -2
- package/cpp/rnwgpu/RNWebGPUManager.cpp +7 -95
- package/cpp/rnwgpu/RNWebGPUManager.h +4 -3
- package/cpp/rnwgpu/WebGPURuntime.cpp +91 -0
- package/cpp/rnwgpu/WebGPURuntime.h +36 -0
- package/cpp/rnwgpu/api/Convertors.h +10 -1
- package/cpp/rnwgpu/api/DestroyedError.h +26 -0
- package/cpp/rnwgpu/api/GPU.cpp +3 -57
- package/cpp/rnwgpu/api/GPUDevice.cpp +17 -9
- package/cpp/rnwgpu/api/GPUError.h +15 -0
- package/cpp/rnwgpu/api/GPUInternalError.h +2 -0
- package/cpp/rnwgpu/api/GPUOutOfMemoryError.h +2 -0
- package/cpp/rnwgpu/api/GPUQueue.cpp +7 -21
- package/cpp/rnwgpu/api/GPUSharedFence.cpp +39 -12
- package/cpp/rnwgpu/api/GPUSharedFence.h +21 -4
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +80 -4
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +29 -6
- package/cpp/rnwgpu/api/GPUTexture.h +6 -0
- package/cpp/rnwgpu/api/GPUValidationError.h +2 -0
- package/cpp/rnwgpu/api/descriptors/Unions.h +80 -0
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js +13 -3
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js +13 -3
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts +12 -0
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/external/reanimated/registerWebGPUForReanimated.ts +29 -7
- package/src/types.ts +14 -1
package/android/CMakeLists.txt
CHANGED
|
@@ -54,6 +54,7 @@ add_library(${PACKAGE_NAME} SHARED
|
|
|
54
54
|
../cpp/rnwgpu/api/GPUComputePipeline.cpp
|
|
55
55
|
../cpp/rnwgpu/api/GPUCanvasContext.cpp
|
|
56
56
|
../cpp/rnwgpu/RNWebGPUManager.cpp
|
|
57
|
+
../cpp/rnwgpu/WebGPURuntime.cpp
|
|
57
58
|
../cpp/jsi/Promise.cpp
|
|
58
59
|
../cpp/jsi/JSICache.cpp
|
|
59
60
|
../cpp/rnwgpu/async/RuntimeContext.cpp
|
|
@@ -131,15 +131,6 @@ public:
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
wgpu::Surface makeSurface(wgpu::Instance instance, void *window, int width,
|
|
135
|
-
int height) override {
|
|
136
|
-
wgpu::SurfaceSourceAndroidNativeWindow androidSurfaceDesc;
|
|
137
|
-
androidSurfaceDesc.window = reinterpret_cast<ANativeWindow *>(window);
|
|
138
|
-
wgpu::SurfaceDescriptor surfaceDescriptor;
|
|
139
|
-
surfaceDescriptor.nextInChain = &androidSurfaceDesc;
|
|
140
|
-
return instance.CreateSurface(&surfaceDescriptor);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
134
|
ImageData createImageBitmap(std::string blobId, double offset,
|
|
144
135
|
double size) override {
|
|
145
136
|
jni::Environment::ensureCurrentThreadIsAttached();
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#include "AndroidPlatformContext.h"
|
|
18
18
|
#include "GPUCanvasContext.h"
|
|
19
19
|
#include "RNWebGPUManager.h"
|
|
20
|
+
#include "WebGPURuntime.h"
|
|
20
21
|
|
|
21
22
|
#define LOG_TAG "WebGPUModule"
|
|
22
23
|
|
|
@@ -116,9 +117,9 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceCreate(
|
|
|
116
117
|
return;
|
|
117
118
|
}
|
|
118
119
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
119
|
-
auto
|
|
120
|
-
auto
|
|
121
|
-
|
|
120
|
+
auto &runtime = rnwgpu::WebGPURuntime::shared();
|
|
121
|
+
auto gpu = runtime.instance();
|
|
122
|
+
auto surface = runtime.makeSurface(window);
|
|
122
123
|
// Find-or-create + attach runs atomically under the registry lock so a
|
|
123
124
|
// concurrent destroyContext cannot orphan this surface.
|
|
124
125
|
auto info = registry.attachSurface(
|
|
@@ -212,7 +213,8 @@ Java_com_webgpu_WebGPUHardwareBufferView_nEnablePool(JNIEnv *env, jobject thiz,
|
|
|
212
213
|
jint dpH) {
|
|
213
214
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
214
215
|
auto info = registry.getSurfaceInfoOrCreate(
|
|
215
|
-
contextId,
|
|
216
|
+
contextId, rnwgpu::WebGPURuntime::shared().instance(),
|
|
217
|
+
static_cast<int>(dpW), static_cast<int>(dpH));
|
|
216
218
|
auto waker = std::make_shared<HardwareBufferViewWaker>(env, thiz);
|
|
217
219
|
info->setPoolFrameReadyCallback([waker]() { (*waker)(); });
|
|
218
220
|
info->enablePool(static_cast<int>(dpW), static_cast<int>(dpH));
|
|
@@ -27,6 +27,14 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
27
27
|
|
|
28
28
|
WebGPUView(Context context) {
|
|
29
29
|
super(context);
|
|
30
|
+
// ReactViewGroup disables clipChildren (RN overflow is handled with canvas
|
|
31
|
+
// clipping instead). Android copies the parent's clipChildren into each
|
|
32
|
+
// child's RenderNode.clipToBounds, and HWUI treats a damaged node without
|
|
33
|
+
// it as unbounded damage (RenderNode::damageSelf). The TextureView and
|
|
34
|
+
// HardwareBufferView children invalidate on every frame, so without this
|
|
35
|
+
// every non-opaque canvas frame repaints the whole window. Our only child
|
|
36
|
+
// exactly fills this view, so clipping it changes nothing visually.
|
|
37
|
+
setClipChildren(true);
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
public void setContextId(int contextId) {
|
|
@@ -20,9 +20,6 @@ public:
|
|
|
20
20
|
std::function<void(ImageData)> onSuccess,
|
|
21
21
|
std::function<void(std::string)> onError) override;
|
|
22
22
|
|
|
23
|
-
wgpu::Surface makeSurface(wgpu::Instance instance, void *surface, int width,
|
|
24
|
-
int height) override;
|
|
25
|
-
|
|
26
23
|
ImageData createImageBitmap(std::string blobId, double offset,
|
|
27
24
|
double size) override;
|
|
28
25
|
|
|
@@ -209,16 +209,6 @@ void ApplePlatformContext::snapshotView(
|
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
wgpu::Surface ApplePlatformContext::makeSurface(wgpu::Instance instance,
|
|
213
|
-
void *surface, int width,
|
|
214
|
-
int height) {
|
|
215
|
-
wgpu::SurfaceSourceMetalLayer metalSurfaceDesc;
|
|
216
|
-
metalSurfaceDesc.layer = surface;
|
|
217
|
-
wgpu::SurfaceDescriptor surfaceDescriptor;
|
|
218
|
-
surfaceDescriptor.nextInChain = &metalSurfaceDesc;
|
|
219
|
-
return instance.CreateSurface(&surfaceDescriptor);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
212
|
static std::span<const uint8_t> nsDataToSpan(NSData *data) {
|
|
223
213
|
return {static_cast<const uint8_t *>(data.bytes), data.length};
|
|
224
214
|
}
|
package/apple/MetalView.mm
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "MetalView.h"
|
|
2
|
+
#import "WebGPURuntime.h"
|
|
2
3
|
#import "webgpu/webgpu_cpp.h"
|
|
3
4
|
|
|
4
5
|
@implementation MetalView {
|
|
@@ -32,9 +33,9 @@
|
|
|
32
33
|
// dealloc runs. Balanced by the releaser below.
|
|
33
34
|
void *nativeSurface = (void *)CFBridgingRetain(self.layer);
|
|
34
35
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
35
|
-
auto
|
|
36
|
-
auto
|
|
37
|
-
|
|
36
|
+
auto &runtime = rnwgpu::WebGPURuntime::shared();
|
|
37
|
+
auto gpu = runtime.instance();
|
|
38
|
+
auto surface = runtime.makeSurface(nativeSurface);
|
|
38
39
|
// Find-or-create + attach runs atomically under the registry lock so a
|
|
39
40
|
// concurrent destroyContext cannot orphan this surface.
|
|
40
41
|
auto info = registry.attachSurface(
|
package/cpp/jsi/JSIConverter.h
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
#include "WGPULogger.h"
|
|
15
15
|
|
|
16
|
+
#include "EnforceRange.h"
|
|
16
17
|
#include "EnumMapper.h"
|
|
17
18
|
#include "Promise.h"
|
|
18
19
|
|
|
@@ -49,7 +50,7 @@ private:
|
|
|
49
50
|
// int <> number
|
|
50
51
|
template <> struct JSIConverter<int> {
|
|
51
52
|
static int fromJSI(jsi::Runtime&, const jsi::Value& arg, bool outOfBound) {
|
|
52
|
-
return
|
|
53
|
+
return enforceRange<int>(arg.asNumber());
|
|
53
54
|
}
|
|
54
55
|
static jsi::Value toJSI(jsi::Runtime&, int arg) {
|
|
55
56
|
return jsi::Value(arg);
|
|
@@ -89,8 +90,7 @@ template <typename T>
|
|
|
89
90
|
struct JSIConverter<T, std::enable_if_t<!std::is_same_v<T, uint64_t> && std::is_same_v<T, size_t>>> {
|
|
90
91
|
static size_t fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
|
|
91
92
|
if (arg.isNumber()) {
|
|
92
|
-
|
|
93
|
-
return static_cast<size_t>(value);
|
|
93
|
+
return enforceRange<size_t>(arg.asNumber());
|
|
94
94
|
} else {
|
|
95
95
|
return arg.asBigInt(runtime).asInt64(runtime);
|
|
96
96
|
}
|
|
@@ -103,9 +103,8 @@ struct JSIConverter<T, std::enable_if_t<!std::is_same_v<T, uint64_t> && std::is_
|
|
|
103
103
|
|
|
104
104
|
// uint32_t <> double
|
|
105
105
|
template <> struct JSIConverter<uint32_t> {
|
|
106
|
-
static
|
|
107
|
-
|
|
108
|
-
return static_cast<uint32_t>(value);
|
|
106
|
+
static uint32_t fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
|
|
107
|
+
return enforceRange<uint32_t>(arg.asNumber());
|
|
109
108
|
}
|
|
110
109
|
static jsi::Value toJSI(jsi::Runtime& runtime, uint32_t arg) {
|
|
111
110
|
return jsi::Value(static_cast<double>(arg));
|
|
@@ -116,11 +115,7 @@ template <> struct JSIConverter<uint32_t> {
|
|
|
116
115
|
template <> struct JSIConverter<uint64_t> {
|
|
117
116
|
static uint64_t fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
|
|
118
117
|
if (arg.isNumber()) {
|
|
119
|
-
|
|
120
|
-
if (value < 0 || value > MAX_SAFE_INTEGER) {
|
|
121
|
-
throw jsi::JSError(runtime, "Number out of range for uint64_t");
|
|
122
|
-
}
|
|
123
|
-
return static_cast<uint64_t>(value);
|
|
118
|
+
return enforceRange<uint64_t>(arg.asNumber());
|
|
124
119
|
} else {
|
|
125
120
|
return arg.asBigInt(runtime).getUint64(runtime);
|
|
126
121
|
}
|
package/cpp/jsi/NativeObject.h
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
#include <unordered_map>
|
|
16
16
|
#include <utility>
|
|
17
17
|
|
|
18
|
+
#include "EnforceRange.h"
|
|
18
19
|
#include "JSICache.h"
|
|
19
20
|
#include "WGPULogger.h"
|
|
20
21
|
|
|
@@ -33,6 +34,22 @@ namespace jsi = facebook::jsi;
|
|
|
33
34
|
// Forward declaration
|
|
34
35
|
template <typename Derived> class NativeObject;
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Non-template base of every NativeObject. It is what lets code that does not
|
|
39
|
+
* know the concrete class (the worklet serializer helper below) tell a WebGPU
|
|
40
|
+
* object from any other object carrying jsi::NativeState, and read its brand.
|
|
41
|
+
*/
|
|
42
|
+
class NativeObjectBase : public jsi::NativeState {
|
|
43
|
+
public:
|
|
44
|
+
/** The class brand (Derived::CLASS_NAME), e.g. "GPUBuffer". */
|
|
45
|
+
const char *getClassName() const { return _name; }
|
|
46
|
+
|
|
47
|
+
protected:
|
|
48
|
+
explicit NativeObjectBase(const char *name) : _name(name) {}
|
|
49
|
+
|
|
50
|
+
const char *_name;
|
|
51
|
+
};
|
|
52
|
+
|
|
36
53
|
/**
|
|
37
54
|
* Registry mapping a NativeObject class brand (CLASS_NAME) to a reconstructor
|
|
38
55
|
* that rebuilds a fully functional JS object (prototype + native state) on
|
|
@@ -92,8 +109,9 @@ private:
|
|
|
92
109
|
* - The NativeState from the original object
|
|
93
110
|
* - The brand name for prototype reconstruction
|
|
94
111
|
*
|
|
95
|
-
* Usage pattern with registerCustomSerializable
|
|
96
|
-
*
|
|
112
|
+
* Usage pattern with registerCustomSerializable (see
|
|
113
|
+
* src/external/reanimated/registerWebGPUForReanimated.ts):
|
|
114
|
+
* - pack(): WebGPUWorkletHelpers::box(obj) creates a BoxedWebGPUObject
|
|
97
115
|
* - The HostObject is serialized by Worklets and transferred to UI runtime
|
|
98
116
|
* - unpack(): Call boxed.unbox() to get the object's wrapper on that runtime
|
|
99
117
|
* (cached per runtime, see NativeObject::create), prototype included
|
|
@@ -149,6 +167,75 @@ private:
|
|
|
149
167
|
std::string _brand;
|
|
150
168
|
};
|
|
151
169
|
|
|
170
|
+
/**
|
|
171
|
+
* The native half of the worklet serializer registered in
|
|
172
|
+
* src/external/reanimated/registerWebGPUForReanimated.ts:
|
|
173
|
+
*
|
|
174
|
+
* - isNativeObject(value): true only for objects whose NativeState is a
|
|
175
|
+
* NativeObjectBase. Other native-state objects (Skia objects, worklets' own
|
|
176
|
+
* serializable refs) and objects that merely inherit from a WebGPU prototype
|
|
177
|
+
* (Object.create(device)) are rejected, and no JS property is ever read, so
|
|
178
|
+
* getters and Proxy traps of unrelated objects never run.
|
|
179
|
+
* - box(value): wraps the object into a BoxedWebGPUObject, branded with its
|
|
180
|
+
* class name, for unbox() on the target runtime.
|
|
181
|
+
*
|
|
182
|
+
* One instance is installed on the main runtime as
|
|
183
|
+
* globalThis.__webgpuWorkletHelpers and captured by the serializer's
|
|
184
|
+
* determine/pack worklets. Worklets hands a HostObject to every runtime by
|
|
185
|
+
* reference, so the helper is available wherever a WebGPU object gets captured
|
|
186
|
+
* (Reanimated UI runtime, createWorkletRuntime, Vision Camera frame
|
|
187
|
+
* processors) without installing anything on those runtimes.
|
|
188
|
+
*/
|
|
189
|
+
class WebGPUWorkletHelpers : public jsi::HostObject {
|
|
190
|
+
public:
|
|
191
|
+
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override {
|
|
192
|
+
auto propName = name.utf8(runtime);
|
|
193
|
+
if (propName == "isNativeObject") {
|
|
194
|
+
return jsi::Function::createFromHostFunction(
|
|
195
|
+
runtime, name, 1,
|
|
196
|
+
[](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
|
|
197
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
198
|
+
return jsi::Value(
|
|
199
|
+
count > 0 && args[0].isObject() &&
|
|
200
|
+
args[0].getObject(rt).hasNativeState<NativeObjectBase>(rt));
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (propName == "box") {
|
|
204
|
+
return jsi::Function::createFromHostFunction(
|
|
205
|
+
runtime, name, 1,
|
|
206
|
+
[](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
|
|
207
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
208
|
+
if (count < 1 || !args[0].isObject()) {
|
|
209
|
+
throw jsi::JSError(rt, "box() expects a WebGPU object");
|
|
210
|
+
}
|
|
211
|
+
auto obj = args[0].getObject(rt);
|
|
212
|
+
if (!obj.hasNativeState<NativeObjectBase>(rt)) {
|
|
213
|
+
throw jsi::JSError(rt, "box() expects a WebGPU object");
|
|
214
|
+
}
|
|
215
|
+
auto state = obj.getNativeState<NativeObjectBase>(rt);
|
|
216
|
+
return jsi::Object::createFromHostObject(
|
|
217
|
+
rt, std::make_shared<BoxedWebGPUObject>(state,
|
|
218
|
+
state->getClassName()));
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return jsi::Value::undefined();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
void set(jsi::Runtime &runtime, const jsi::PropNameID & /*name*/,
|
|
225
|
+
const jsi::Value & /*value*/) override {
|
|
226
|
+
throw jsi::JSError(runtime, "__webgpuWorkletHelpers is read-only");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
std::vector<jsi::PropNameID>
|
|
230
|
+
getPropertyNames(jsi::Runtime &runtime) override {
|
|
231
|
+
std::vector<jsi::PropNameID> names;
|
|
232
|
+
names.reserve(2);
|
|
233
|
+
names.push_back(jsi::PropNameID::forUtf8(runtime, "isNativeObject"));
|
|
234
|
+
names.push_back(jsi::PropNameID::forUtf8(runtime, "box"));
|
|
235
|
+
return names;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
152
239
|
/**
|
|
153
240
|
* Base class for native objects using the NativeState pattern.
|
|
154
241
|
*
|
|
@@ -178,7 +265,7 @@ private:
|
|
|
178
265
|
* ```
|
|
179
266
|
*/
|
|
180
267
|
template <typename Derived>
|
|
181
|
-
class NativeObject : public
|
|
268
|
+
class NativeObject : public NativeObjectBase,
|
|
182
269
|
public std::enable_shared_from_this<Derived> {
|
|
183
270
|
public:
|
|
184
271
|
// Marker type for SFINAE detection in JSIConverter
|
|
@@ -450,7 +537,7 @@ public:
|
|
|
450
537
|
jsi::Runtime *getCreationRuntime() const { return _creationRuntime; }
|
|
451
538
|
|
|
452
539
|
protected:
|
|
453
|
-
explicit NativeObject(const char *name) :
|
|
540
|
+
explicit NativeObject(const char *name) : NativeObjectBase(name) {
|
|
454
541
|
#if DEBUG && RNF_ENABLE_LOGS
|
|
455
542
|
Logger::logToConsole("NativeObject", "(MEMORY) Creating %s... ✅", _name);
|
|
456
543
|
#endif
|
|
@@ -462,13 +549,30 @@ protected:
|
|
|
462
549
|
#endif
|
|
463
550
|
}
|
|
464
551
|
|
|
465
|
-
const char *_name;
|
|
466
552
|
jsi::Runtime *_creationRuntime = nullptr;
|
|
467
553
|
|
|
468
554
|
// ============================================================
|
|
469
555
|
// Helper methods for definePrototype() implementations
|
|
470
556
|
// ============================================================
|
|
471
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Runs a host function body, turning a RangeTypeError (a JS number outside
|
|
560
|
+
* its WebIDL [EnforceRange] integer range, see EnforceRange.h) into a JS
|
|
561
|
+
* TypeError, as browsers throw for the same input. Everything else
|
|
562
|
+
* propagates unchanged.
|
|
563
|
+
*/
|
|
564
|
+
template <typename Body>
|
|
565
|
+
static jsi::Value withWebIDLConversions(jsi::Runtime &runtime, Body &&body) {
|
|
566
|
+
try {
|
|
567
|
+
return body();
|
|
568
|
+
} catch (const RangeTypeError &error) {
|
|
569
|
+
auto typeError = runtime.global().getPropertyAsFunction(runtime, "TypeError");
|
|
570
|
+
throw jsi::JSError(runtime,
|
|
571
|
+
typeError.callAsConstructor(
|
|
572
|
+
runtime, jsi::String::createFromUtf8(runtime, error.what())));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
472
576
|
/**
|
|
473
577
|
* Install a method on the prototype.
|
|
474
578
|
*/
|
|
@@ -481,8 +585,10 @@ protected:
|
|
|
481
585
|
[method](jsi::Runtime &rt, const jsi::Value &thisVal,
|
|
482
586
|
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
483
587
|
auto native = Derived::fromValue(rt, thisVal);
|
|
484
|
-
return
|
|
485
|
-
|
|
588
|
+
return withWebIDLConversions(rt, [&] {
|
|
589
|
+
return callMethod(native.get(), method, rt, args,
|
|
590
|
+
std::index_sequence_for<Args...>{}, count);
|
|
591
|
+
});
|
|
486
592
|
});
|
|
487
593
|
prototype.setProperty(runtime, name, func);
|
|
488
594
|
}
|
|
@@ -503,9 +609,11 @@ protected:
|
|
|
503
609
|
[method](jsi::Runtime &rt, const jsi::Value &thisVal,
|
|
504
610
|
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
505
611
|
auto native = Derived::fromValue(rt, thisVal);
|
|
506
|
-
return
|
|
507
|
-
|
|
508
|
-
|
|
612
|
+
return withWebIDLConversions(rt, [&] {
|
|
613
|
+
return callMethodWithRuntime(native.get(), method, rt, args,
|
|
614
|
+
std::index_sequence_for<Args...>{},
|
|
615
|
+
count);
|
|
616
|
+
});
|
|
509
617
|
});
|
|
510
618
|
prototype.setProperty(runtime, name, func);
|
|
511
619
|
}
|
|
@@ -563,10 +671,12 @@ protected:
|
|
|
563
671
|
throw jsi::JSError(rt, "Setter requires a value argument");
|
|
564
672
|
}
|
|
565
673
|
auto native = Derived::fromValue(rt, thisVal);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
674
|
+
return withWebIDLConversions(rt, [&] {
|
|
675
|
+
auto value =
|
|
676
|
+
rnwgpu::JSIConverter<std::decay_t<ValueType>>::fromJSI(rt, args[0], false);
|
|
677
|
+
(native.get()->*setter)(std::move(value));
|
|
678
|
+
return jsi::Value::undefined();
|
|
679
|
+
});
|
|
570
680
|
});
|
|
571
681
|
|
|
572
682
|
// Use Object.defineProperty to create a proper setter
|
|
@@ -624,10 +734,12 @@ protected:
|
|
|
624
734
|
throw jsi::JSError(rt, "Setter requires a value argument");
|
|
625
735
|
}
|
|
626
736
|
auto native = Derived::fromValue(rt, thisVal);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
737
|
+
return withWebIDLConversions(rt, [&] {
|
|
738
|
+
auto value =
|
|
739
|
+
rnwgpu::JSIConverter<std::decay_t<ValueType>>::fromJSI(rt, args[0], false);
|
|
740
|
+
(native.get()->*setter)(std::move(value));
|
|
741
|
+
return jsi::Value::undefined();
|
|
742
|
+
});
|
|
631
743
|
});
|
|
632
744
|
|
|
633
745
|
auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cmath>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <limits>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <type_traits>
|
|
10
|
+
|
|
11
|
+
namespace rnwgpu {
|
|
12
|
+
|
|
13
|
+
// Thrown when a JS number cannot be converted to a WebIDL [EnforceRange]
|
|
14
|
+
// integer. The host function wrappers in NativeObject rethrow it as a JS
|
|
15
|
+
// TypeError, which is what browsers throw for the same input.
|
|
16
|
+
class RangeTypeError : public std::invalid_argument {
|
|
17
|
+
public:
|
|
18
|
+
using std::invalid_argument::invalid_argument;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
namespace detail {
|
|
22
|
+
|
|
23
|
+
// The WebIDL name of the integer type, for the same message Chrome produces.
|
|
24
|
+
template <typename T> constexpr const char *webIDLIntegerName() {
|
|
25
|
+
if constexpr (sizeof(T) == 1) {
|
|
26
|
+
return std::is_signed_v<T> ? "byte" : "octet";
|
|
27
|
+
} else if constexpr (sizeof(T) == 2) {
|
|
28
|
+
return std::is_signed_v<T> ? "short" : "unsigned short";
|
|
29
|
+
} else if constexpr (sizeof(T) == 4) {
|
|
30
|
+
return std::is_signed_v<T> ? "long" : "unsigned long";
|
|
31
|
+
} else {
|
|
32
|
+
return std::is_signed_v<T> ? "long long" : "unsigned long long";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
} // namespace detail
|
|
37
|
+
|
|
38
|
+
// WebIDL [EnforceRange] conversion of a JS number to an integer
|
|
39
|
+
// (https://webidl.spec.whatwg.org/#abstract-opdef-converttoint): non-finite
|
|
40
|
+
// values and values outside the type's range are rejected, fractions are
|
|
41
|
+
// truncated toward zero. 64-bit types are limited to the integers a double
|
|
42
|
+
// holds exactly, [-(2^53 - 1), 2^53 - 1], as the spec requires.
|
|
43
|
+
//
|
|
44
|
+
// WebGPU marks nearly every integer it takes [EnforceRange] (GPUSize32,
|
|
45
|
+
// GPUIndex32, GPUSize64, GPUIntegerCoordinate, GPUSignedOffset32,
|
|
46
|
+
// GPUStencilValue, GPUSampleMask, GPUDepthBias). The flag constants
|
|
47
|
+
// (GPUBufferUsageFlags and friends) are plain `unsigned long` and are not
|
|
48
|
+
// converted here.
|
|
49
|
+
template <typename T> T enforceRange(double value) {
|
|
50
|
+
static_assert(std::is_integral_v<T> && !std::is_same_v<T, bool>,
|
|
51
|
+
"enforceRange converts to integer types only");
|
|
52
|
+
constexpr double kMaxSafeInteger = 9007199254740991.0;
|
|
53
|
+
constexpr double kLower = std::max(
|
|
54
|
+
static_cast<double>(std::numeric_limits<T>::min()), -kMaxSafeInteger);
|
|
55
|
+
constexpr double kUpper = std::min(
|
|
56
|
+
static_cast<double>(std::numeric_limits<T>::max()), kMaxSafeInteger);
|
|
57
|
+
if (!std::isfinite(value)) {
|
|
58
|
+
throw RangeTypeError("The provided double value is non-finite.");
|
|
59
|
+
}
|
|
60
|
+
const double integer = std::trunc(value);
|
|
61
|
+
if (integer < kLower || integer > kUpper) {
|
|
62
|
+
throw RangeTypeError(std::string("Value is outside the '") +
|
|
63
|
+
detail::webIDLIntegerName<T>() + "' value range.");
|
|
64
|
+
}
|
|
65
|
+
return static_cast<T>(integer);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
} // namespace rnwgpu
|
|
@@ -142,8 +142,6 @@ public:
|
|
|
142
142
|
std::function<void(ImageData)> onSuccess,
|
|
143
143
|
std::function<void(std::string)> onError) = 0;
|
|
144
144
|
|
|
145
|
-
virtual wgpu::Surface makeSurface(wgpu::Instance instance, void *surface,
|
|
146
|
-
int width, int height) = 0;
|
|
147
145
|
virtual ImageData createImageBitmap(std::string blobId, double offset,
|
|
148
146
|
double size) = 0;
|
|
149
147
|
|
|
@@ -141,101 +141,13 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
void RNWebGPUManager::installWebGPUWorkletHelpers(jsi::Runtime &runtime) {
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
auto obj = args[0].getObject(rt);
|
|
153
|
-
|
|
154
|
-
// Check if it has native state
|
|
155
|
-
if (!obj.hasNativeState(rt)) {
|
|
156
|
-
return jsi::Value(false);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Check if it has Symbol.toStringTag on its prototype (WebGPU objects
|
|
160
|
-
// do)
|
|
161
|
-
auto objectCtor = rt.global().getPropertyAsObject(rt, "Object");
|
|
162
|
-
auto getPrototypeOf =
|
|
163
|
-
objectCtor.getPropertyAsFunction(rt, "getPrototypeOf");
|
|
164
|
-
auto proto = getPrototypeOf.call(rt, obj);
|
|
165
|
-
|
|
166
|
-
if (!proto.isObject()) {
|
|
167
|
-
return jsi::Value(false);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
auto protoObj = proto.getObject(rt);
|
|
171
|
-
auto symbolCtor = rt.global().getPropertyAsObject(rt, "Symbol");
|
|
172
|
-
auto toStringTag = symbolCtor.getProperty(rt, "toStringTag");
|
|
173
|
-
if (toStringTag.isUndefined()) {
|
|
174
|
-
return jsi::Value(false);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
auto getOwnPropertyDescriptor =
|
|
178
|
-
objectCtor.getPropertyAsFunction(rt, "getOwnPropertyDescriptor");
|
|
179
|
-
auto desc = getOwnPropertyDescriptor.call(rt, protoObj, toStringTag);
|
|
180
|
-
return jsi::Value(desc.isObject());
|
|
181
|
-
});
|
|
182
|
-
runtime.global().setProperty(runtime, "__webgpuIsWebGPUObject",
|
|
183
|
-
std::move(isWebGPUObjectFunc));
|
|
184
|
-
|
|
185
|
-
// __webgpuBox - boxes a WebGPU object for Worklets serialization
|
|
186
|
-
auto boxFunc = jsi::Function::createFromHostFunction(
|
|
187
|
-
runtime, jsi::PropNameID::forUtf8(runtime, "__webgpuBox"), 1,
|
|
188
|
-
[](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
|
|
189
|
-
const jsi::Value *args, size_t count) -> jsi::Value {
|
|
190
|
-
if (count < 1 || !args[0].isObject()) {
|
|
191
|
-
throw jsi::JSError(rt,
|
|
192
|
-
"__webgpuBox() requires a WebGPU object argument");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
auto obj = args[0].getObject(rt);
|
|
196
|
-
|
|
197
|
-
// Check if it has native state
|
|
198
|
-
if (!obj.hasNativeState(rt)) {
|
|
199
|
-
throw jsi::JSError(
|
|
200
|
-
rt, "Object has no native state - not a WebGPU object");
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Get the brand name from Symbol.toStringTag on the prototype
|
|
204
|
-
auto objectCtor = rt.global().getPropertyAsObject(rt, "Object");
|
|
205
|
-
auto getPrototypeOf =
|
|
206
|
-
objectCtor.getPropertyAsFunction(rt, "getPrototypeOf");
|
|
207
|
-
auto proto = getPrototypeOf.call(rt, obj);
|
|
208
|
-
|
|
209
|
-
std::string brand;
|
|
210
|
-
if (proto.isObject()) {
|
|
211
|
-
auto protoObj = proto.getObject(rt);
|
|
212
|
-
auto symbolCtor = rt.global().getPropertyAsObject(rt, "Symbol");
|
|
213
|
-
auto toStringTag = symbolCtor.getProperty(rt, "toStringTag");
|
|
214
|
-
if (!toStringTag.isUndefined()) {
|
|
215
|
-
auto getOwnPropertyDescriptor = objectCtor.getPropertyAsFunction(
|
|
216
|
-
rt, "getOwnPropertyDescriptor");
|
|
217
|
-
auto desc =
|
|
218
|
-
getOwnPropertyDescriptor.call(rt, protoObj, toStringTag);
|
|
219
|
-
if (desc.isObject()) {
|
|
220
|
-
auto descObj = desc.getObject(rt);
|
|
221
|
-
auto value = descObj.getProperty(rt, "value");
|
|
222
|
-
if (value.isString()) {
|
|
223
|
-
brand = value.getString(rt).utf8(rt);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (brand.empty()) {
|
|
230
|
-
throw jsi::JSError(rt, "Cannot determine WebGPU object type - no "
|
|
231
|
-
"Symbol.toStringTag found");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
auto nativeState = obj.getNativeState(rt);
|
|
235
|
-
auto boxed = std::make_shared<BoxedWebGPUObject>(nativeState, brand);
|
|
236
|
-
return jsi::Object::createFromHostObject(rt, boxed);
|
|
237
|
-
});
|
|
238
|
-
runtime.global().setProperty(runtime, "__webgpuBox", std::move(boxFunc));
|
|
144
|
+
// The serializer worklets capture this object by closure; worklets passes a
|
|
145
|
+
// HostObject to every runtime by reference, so installing it here, on the
|
|
146
|
+
// main runtime, is enough (see WebGPUWorkletHelpers).
|
|
147
|
+
runtime.global().setProperty(
|
|
148
|
+
runtime, "__webgpuWorkletHelpers",
|
|
149
|
+
jsi::Object::createFromHostObject(
|
|
150
|
+
runtime, std::make_shared<WebGPUWorkletHelpers>()));
|
|
239
151
|
}
|
|
240
152
|
|
|
241
153
|
void RNWebGPUManager::flushPendingSurfaceTransition(
|
|
@@ -28,9 +28,10 @@ public:
|
|
|
28
28
|
~RNWebGPUManager();
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Installs globalThis.__webgpuWorkletHelpers, the HostObject the worklet
|
|
32
|
+
* serializer (registerWebGPUForReanimated) captures to detect and box
|
|
33
|
+
* WebGPU objects. Only needed on the main runtime: the serializer worklets
|
|
34
|
+
* carry it to every other runtime by reference. See WebGPUWorkletHelpers.
|
|
34
35
|
*/
|
|
35
36
|
static void installWebGPUWorkletHelpers(jsi::Runtime &runtime);
|
|
36
37
|
|