react-native-wgpu 0.4.1 → 0.5.0

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 (197) hide show
  1. package/README.md +59 -14
  2. package/android/CMakeLists.txt +3 -3
  3. package/cpp/jsi/{RNFEnumMapper.h → EnumMapper.h} +2 -6
  4. package/cpp/jsi/{RNFJSIConverter.h → JSIConverter.h} +27 -110
  5. package/cpp/jsi/NativeObject.h +607 -0
  6. package/cpp/jsi/{RNFPromise.cpp → Promise.cpp} +3 -6
  7. package/cpp/jsi/{RNFPromise.h → Promise.h} +2 -5
  8. package/cpp/jsi/RuntimeAwareCache.cpp +7 -0
  9. package/cpp/jsi/RuntimeAwareCache.h +100 -0
  10. package/cpp/jsi/RuntimeLifecycleMonitor.cpp +72 -0
  11. package/cpp/jsi/RuntimeLifecycleMonitor.h +32 -0
  12. package/cpp/rnwgpu/ArrayBuffer.h +8 -12
  13. package/cpp/rnwgpu/RNWebGPUManager.cpp +187 -28
  14. package/cpp/rnwgpu/RNWebGPUManager.h +7 -0
  15. package/cpp/rnwgpu/api/Canvas.h +14 -12
  16. package/cpp/rnwgpu/api/GPU.cpp +4 -6
  17. package/cpp/rnwgpu/api/GPU.h +13 -11
  18. package/cpp/rnwgpu/api/GPUAdapter.cpp +9 -9
  19. package/cpp/rnwgpu/api/GPUAdapter.h +14 -11
  20. package/cpp/rnwgpu/api/GPUAdapterInfo.h +17 -15
  21. package/cpp/rnwgpu/api/GPUBindGroup.h +11 -10
  22. package/cpp/rnwgpu/api/GPUBindGroupLayout.h +12 -11
  23. package/cpp/rnwgpu/api/GPUBuffer.h +19 -16
  24. package/cpp/rnwgpu/api/GPUCanvasContext.h +17 -13
  25. package/cpp/rnwgpu/api/GPUCommandBuffer.h +12 -10
  26. package/cpp/rnwgpu/api/GPUCommandEncoder.h +35 -32
  27. package/cpp/rnwgpu/api/GPUCompilationInfo.h +19 -19
  28. package/cpp/rnwgpu/api/GPUCompilationMessage.h +10 -7
  29. package/cpp/rnwgpu/api/GPUComputePassEncoder.h +28 -27
  30. package/cpp/rnwgpu/api/GPUComputePipeline.h +14 -13
  31. package/cpp/rnwgpu/api/GPUDevice.cpp +111 -95
  32. package/cpp/rnwgpu/api/GPUDevice.h +51 -43
  33. package/cpp/rnwgpu/api/GPUDeviceLostInfo.h +12 -10
  34. package/cpp/rnwgpu/api/GPUError.h +19 -29
  35. package/cpp/rnwgpu/api/GPUExtent3D.h +7 -10
  36. package/cpp/rnwgpu/api/GPUExternalTexture.h +12 -11
  37. package/cpp/rnwgpu/api/GPUInternalError.h +31 -0
  38. package/cpp/rnwgpu/api/GPUOrigin2D.h +6 -10
  39. package/cpp/rnwgpu/api/GPUOrigin3D.h +6 -10
  40. package/cpp/rnwgpu/api/GPUOutOfMemoryError.h +33 -0
  41. package/cpp/rnwgpu/api/GPUPipelineLayout.h +12 -10
  42. package/cpp/rnwgpu/api/GPUQuerySet.h +14 -12
  43. package/cpp/rnwgpu/api/GPUQueue.h +18 -17
  44. package/cpp/rnwgpu/api/GPURenderBundle.h +11 -10
  45. package/cpp/rnwgpu/api/GPURenderBundleEncoder.h +36 -33
  46. package/cpp/rnwgpu/api/GPURenderPassEncoder.h +49 -47
  47. package/cpp/rnwgpu/api/GPURenderPipeline.h +14 -12
  48. package/cpp/rnwgpu/api/GPUSampler.h +11 -10
  49. package/cpp/rnwgpu/api/GPUShaderModule.cpp +7 -8
  50. package/cpp/rnwgpu/api/GPUShaderModule.h +13 -12
  51. package/cpp/rnwgpu/api/GPUSupportedLimits.h +73 -93
  52. package/cpp/rnwgpu/api/GPUTexture.h +24 -20
  53. package/cpp/rnwgpu/api/GPUTextureView.h +11 -10
  54. package/cpp/rnwgpu/api/GPUValidationError.h +32 -0
  55. package/cpp/rnwgpu/api/ImageBitmap.h +10 -6
  56. package/cpp/rnwgpu/api/RNWebGPU.h +21 -21
  57. package/cpp/rnwgpu/api/descriptors/GPUBindGroupDescriptor.h +3 -7
  58. package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +7 -11
  59. package/cpp/rnwgpu/api/descriptors/GPUBindGroupLayoutDescriptor.h +3 -7
  60. package/cpp/rnwgpu/api/descriptors/GPUBindGroupLayoutEntry.h +3 -7
  61. package/cpp/rnwgpu/api/descriptors/GPUBlendComponent.h +3 -8
  62. package/cpp/rnwgpu/api/descriptors/GPUBlendState.h +3 -7
  63. package/cpp/rnwgpu/api/descriptors/GPUBufferBinding.h +3 -7
  64. package/cpp/rnwgpu/api/descriptors/GPUBufferBindingLayout.h +3 -8
  65. package/cpp/rnwgpu/api/descriptors/GPUBufferDescriptor.h +3 -8
  66. package/cpp/rnwgpu/api/descriptors/GPUBufferUsage.h +28 -32
  67. package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +3 -7
  68. package/cpp/rnwgpu/api/descriptors/GPUColor.h +3 -8
  69. package/cpp/rnwgpu/api/descriptors/GPUColorTargetState.h +3 -7
  70. package/cpp/rnwgpu/api/descriptors/GPUColorWrite.h +18 -20
  71. package/cpp/rnwgpu/api/descriptors/GPUCommandBufferDescriptor.h +3 -8
  72. package/cpp/rnwgpu/api/descriptors/GPUCommandEncoderDescriptor.h +3 -8
  73. package/cpp/rnwgpu/api/descriptors/GPUComputePassDescriptor.h +3 -7
  74. package/cpp/rnwgpu/api/descriptors/GPUComputePassTimestampWrites.h +3 -7
  75. package/cpp/rnwgpu/api/descriptors/GPUComputePipelineDescriptor.h +3 -7
  76. package/cpp/rnwgpu/api/descriptors/GPUDepthStencilState.h +3 -7
  77. package/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +3 -7
  78. package/cpp/rnwgpu/api/descriptors/GPUExternalTextureBindingLayout.h +3 -8
  79. package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +3 -7
  80. package/cpp/rnwgpu/api/descriptors/GPUFragmentState.h +3 -7
  81. package/cpp/rnwgpu/api/descriptors/GPUImageCopyBuffer.h +3 -7
  82. package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +3 -7
  83. package/cpp/rnwgpu/api/descriptors/GPUImageCopyTexture.h +3 -7
  84. package/cpp/rnwgpu/api/descriptors/GPUImageCopyTextureTagged.h +3 -7
  85. package/cpp/rnwgpu/api/descriptors/GPUImageDataLayout.h +3 -8
  86. package/cpp/rnwgpu/api/descriptors/GPUMapMode.h +11 -14
  87. package/cpp/rnwgpu/api/descriptors/GPUMultisampleState.h +3 -8
  88. package/cpp/rnwgpu/api/descriptors/GPUPipelineLayoutDescriptor.h +3 -7
  89. package/cpp/rnwgpu/api/descriptors/GPUPrimitiveState.h +3 -8
  90. package/cpp/rnwgpu/api/descriptors/GPUProgrammableStage.h +3 -7
  91. package/cpp/rnwgpu/api/descriptors/GPUQuerySetDescriptor.h +3 -8
  92. package/cpp/rnwgpu/api/descriptors/GPUQueueDescriptor.h +3 -8
  93. package/cpp/rnwgpu/api/descriptors/GPURenderBundleDescriptor.h +3 -8
  94. package/cpp/rnwgpu/api/descriptors/GPURenderBundleEncoderDescriptor.h +3 -8
  95. package/cpp/rnwgpu/api/descriptors/GPURenderPassColorAttachment.h +3 -7
  96. package/cpp/rnwgpu/api/descriptors/GPURenderPassDepthStencilAttachment.h +3 -7
  97. package/cpp/rnwgpu/api/descriptors/GPURenderPassDescriptor.h +3 -7
  98. package/cpp/rnwgpu/api/descriptors/GPURenderPassTimestampWrites.h +3 -7
  99. package/cpp/rnwgpu/api/descriptors/GPURenderPipelineDescriptor.h +3 -7
  100. package/cpp/rnwgpu/api/descriptors/GPURequestAdapterOptions.h +3 -8
  101. package/cpp/rnwgpu/api/descriptors/GPUSamplerBindingLayout.h +3 -8
  102. package/cpp/rnwgpu/api/descriptors/GPUSamplerDescriptor.h +3 -8
  103. package/cpp/rnwgpu/api/descriptors/GPUShaderModuleCompilationHint.h +3 -7
  104. package/cpp/rnwgpu/api/descriptors/GPUShaderModuleDescriptor.h +3 -7
  105. package/cpp/rnwgpu/api/descriptors/GPUShaderStage.h +14 -16
  106. package/cpp/rnwgpu/api/descriptors/GPUStencilFaceState.h +3 -8
  107. package/cpp/rnwgpu/api/descriptors/GPUStorageTextureBindingLayout.h +3 -8
  108. package/cpp/rnwgpu/api/descriptors/GPUTextureBindingLayout.h +3 -8
  109. package/cpp/rnwgpu/api/descriptors/GPUTextureDescriptor.h +3 -7
  110. package/cpp/rnwgpu/api/descriptors/GPUTextureUsage.h +18 -29
  111. package/cpp/rnwgpu/api/descriptors/GPUTextureViewDescriptor.h +3 -8
  112. package/cpp/rnwgpu/api/descriptors/GPUUncapturedErrorEventInit.h +3 -7
  113. package/cpp/rnwgpu/api/descriptors/GPUVertexAttribute.h +3 -8
  114. package/cpp/rnwgpu/api/descriptors/GPUVertexBufferLayout.h +3 -7
  115. package/cpp/rnwgpu/api/descriptors/GPUVertexState.h +3 -7
  116. package/cpp/rnwgpu/api/descriptors/Unions.h +3 -3
  117. package/cpp/rnwgpu/async/AsyncTaskHandle.cpp +10 -10
  118. package/cpp/rnwgpu/async/AsyncTaskHandle.h +2 -2
  119. package/lib/commonjs/Canvas.js +9 -14
  120. package/lib/commonjs/Canvas.js.map +1 -1
  121. package/lib/commonjs/external/ModuleProxy.js +36 -0
  122. package/lib/commonjs/external/ModuleProxy.js.map +1 -0
  123. package/lib/commonjs/external/index.js +17 -0
  124. package/lib/commonjs/external/index.js.map +1 -0
  125. package/lib/commonjs/external/reanimated/ReanimatedProxy.js +18 -0
  126. package/lib/commonjs/external/reanimated/ReanimatedProxy.js.map +1 -0
  127. package/lib/commonjs/external/reanimated/index.js +21 -0
  128. package/lib/commonjs/external/reanimated/index.js.map +1 -0
  129. package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js +50 -0
  130. package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js.map +1 -0
  131. package/lib/commonjs/main/index.js +2 -142
  132. package/lib/commonjs/main/index.js.map +1 -1
  133. package/lib/module/Canvas.js +10 -16
  134. package/lib/module/Canvas.js.map +1 -1
  135. package/lib/module/external/ModuleProxy.js +28 -0
  136. package/lib/module/external/ModuleProxy.js.map +1 -0
  137. package/lib/module/external/index.js +2 -0
  138. package/lib/module/external/index.js.map +1 -0
  139. package/lib/module/external/reanimated/ReanimatedProxy.js +12 -0
  140. package/lib/module/external/reanimated/ReanimatedProxy.js.map +1 -0
  141. package/lib/module/external/reanimated/index.js +3 -0
  142. package/lib/module/external/reanimated/index.js.map +1 -0
  143. package/lib/module/external/reanimated/registerWebGPUForReanimated.js +43 -0
  144. package/lib/module/external/reanimated/registerWebGPUForReanimated.js.map +1 -0
  145. package/lib/module/main/index.js +2 -141
  146. package/lib/module/main/index.js.map +1 -1
  147. package/lib/typescript/babel.config.d.ts +1 -0
  148. package/lib/typescript/lib/commonjs/Canvas.d.ts +5 -1
  149. package/lib/typescript/lib/commonjs/Canvas.d.ts.map +1 -1
  150. package/lib/typescript/lib/commonjs/external/ModuleProxy.d.ts +12 -0
  151. package/lib/typescript/lib/commonjs/external/ModuleProxy.d.ts.map +1 -0
  152. package/lib/typescript/lib/commonjs/external/index.d.ts +2 -0
  153. package/lib/typescript/lib/commonjs/external/index.d.ts.map +1 -0
  154. package/lib/typescript/lib/commonjs/external/reanimated/ReanimatedProxy.d.ts +6 -0
  155. package/lib/typescript/lib/commonjs/external/reanimated/ReanimatedProxy.d.ts.map +1 -0
  156. package/lib/typescript/lib/commonjs/external/reanimated/index.d.ts +4 -0
  157. package/lib/typescript/lib/commonjs/external/reanimated/index.d.ts.map +1 -0
  158. package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts +9 -0
  159. package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -0
  160. package/lib/typescript/lib/module/Canvas.d.ts +6 -1
  161. package/lib/typescript/lib/module/Canvas.d.ts.map +1 -1
  162. package/lib/typescript/lib/module/external/ModuleProxy.d.ts +7 -0
  163. package/lib/typescript/lib/module/external/ModuleProxy.d.ts.map +1 -0
  164. package/lib/typescript/lib/module/external/index.d.ts +2 -0
  165. package/lib/typescript/lib/module/external/index.d.ts.map +1 -0
  166. package/lib/typescript/lib/module/external/reanimated/ReanimatedProxy.d.ts +5 -0
  167. package/lib/typescript/lib/module/external/reanimated/ReanimatedProxy.d.ts.map +1 -0
  168. package/lib/typescript/lib/module/external/reanimated/index.d.ts +3 -0
  169. package/lib/typescript/lib/module/external/reanimated/index.d.ts.map +1 -0
  170. package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts +2 -0
  171. package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -0
  172. package/lib/typescript/src/Canvas.d.ts +4 -2
  173. package/lib/typescript/src/Canvas.d.ts.map +1 -1
  174. package/lib/typescript/src/external/ModuleProxy.d.ts +11 -0
  175. package/lib/typescript/src/external/ModuleProxy.d.ts.map +1 -0
  176. package/lib/typescript/src/external/index.d.ts +2 -0
  177. package/lib/typescript/src/external/index.d.ts.map +1 -0
  178. package/lib/typescript/src/external/reanimated/ReanimatedProxy.d.ts +4 -0
  179. package/lib/typescript/src/external/reanimated/ReanimatedProxy.d.ts.map +1 -0
  180. package/lib/typescript/src/external/reanimated/index.d.ts +3 -0
  181. package/lib/typescript/src/external/reanimated/index.d.ts.map +1 -0
  182. package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts +8 -0
  183. package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -0
  184. package/package.json +15 -2
  185. package/src/Canvas.tsx +16 -22
  186. package/src/external/ModuleProxy.ts +30 -0
  187. package/src/external/index.ts +1 -0
  188. package/src/external/reanimated/ReanimatedProxy.ts +19 -0
  189. package/src/external/reanimated/index.ts +2 -0
  190. package/src/external/reanimated/registerWebGPUForReanimated.ts +43 -0
  191. package/src/main/index.tsx +3 -170
  192. package/cpp/jsi/RNFHybridObject.cpp +0 -150
  193. package/cpp/jsi/RNFHybridObject.h +0 -181
  194. package/cpp/jsi/RNFJSIHelper.h +0 -51
  195. package/cpp/jsi/RNFPointerHolder.h +0 -95
  196. package/cpp/jsi/RNFRuntimeState.cpp +0 -18
  197. package/cpp/jsi/RNFRuntimeState.h +0 -106
@@ -0,0 +1,607 @@
1
+ //
2
+ // NativeObject base class for JSI NativeState pattern
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include <functional>
8
+ #include <jsi/jsi.h>
9
+ #include <memory>
10
+ #include <mutex>
11
+ #include <optional>
12
+ #include <string>
13
+ #include <type_traits>
14
+ #include <unordered_map>
15
+ #include <utility>
16
+
17
+ #include "RuntimeAwareCache.h"
18
+ #include "WGPULogger.h"
19
+
20
+ // Forward declare to avoid circular dependency
21
+ namespace rnwgpu {
22
+ template <typename ArgType, typename SFINAE> struct JSIConverter;
23
+ } // namespace rnwgpu
24
+
25
+ // Include the converter - must come after forward declaration
26
+ #include "JSIConverter.h"
27
+
28
+ namespace rnwgpu {
29
+
30
+ namespace jsi = facebook::jsi;
31
+
32
+ // Forward declaration
33
+ template <typename Derived> class NativeObject;
34
+
35
+ /**
36
+ * Registry for NativeObject prototype installers.
37
+ * This allows BoxedWebGPUObject::unbox() to install prototypes on any runtime
38
+ * by looking up the brand name and calling the appropriate installer.
39
+ */
40
+ class NativeObjectRegistry {
41
+ public:
42
+ using InstallerFunc = std::function<void(jsi::Runtime &)>;
43
+
44
+ static NativeObjectRegistry &getInstance() {
45
+ static NativeObjectRegistry instance;
46
+ return instance;
47
+ }
48
+
49
+ void registerInstaller(const std::string &brand, InstallerFunc installer) {
50
+ std::lock_guard<std::mutex> lock(_mutex);
51
+ _installers[brand] = std::move(installer);
52
+ }
53
+
54
+ bool installPrototype(jsi::Runtime &runtime, const std::string &brand) {
55
+ std::lock_guard<std::mutex> lock(_mutex);
56
+ auto it = _installers.find(brand);
57
+ if (it != _installers.end()) {
58
+ it->second(runtime);
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ private:
65
+ NativeObjectRegistry() = default;
66
+ std::mutex _mutex;
67
+ std::unordered_map<std::string, InstallerFunc> _installers;
68
+ };
69
+
70
+ /**
71
+ * Per-runtime cache entry for a prototype object.
72
+ * Uses std::optional<jsi::Object> so the prototype is stored directly
73
+ * without extra indirection.
74
+ */
75
+ struct PrototypeCacheEntry {
76
+ std::optional<jsi::Object> prototype;
77
+ };
78
+
79
+ /**
80
+ * Wrapper for static RuntimeAwareCache that handles hot reload.
81
+ *
82
+ * When used with static storage (like prototype caches), the cache persists
83
+ * across hot reloads. But the JSI objects inside become invalid when the
84
+ * runtime is destroyed. This wrapper tracks which runtime the cache was
85
+ * created for and allocates a new cache when the runtime changes.
86
+ *
87
+ * The old cache is intentionally leaked - we cannot safely destroy JSI
88
+ * objects after their runtime is gone.
89
+ */
90
+ template <typename T> struct StaticRuntimeAwareCache {
91
+ RuntimeAwareCache<T> *cache = nullptr;
92
+ jsi::Runtime *cacheRuntime = nullptr;
93
+
94
+ RuntimeAwareCache<T> &get(jsi::Runtime &rt) {
95
+ auto mainRuntime = BaseRuntimeAwareCache::getMainJsRuntime();
96
+ if (&rt == mainRuntime && cacheRuntime != mainRuntime) {
97
+ // Main runtime changed (hot reload) - allocate new cache, leak old one
98
+ cache = new RuntimeAwareCache<T>();
99
+ cacheRuntime = mainRuntime;
100
+ }
101
+ if (cache == nullptr) {
102
+ cache = new RuntimeAwareCache<T>();
103
+ cacheRuntime = mainRuntime;
104
+ }
105
+ return *cache;
106
+ }
107
+ };
108
+
109
+ /**
110
+ * BoxedWebGPUObject is a HostObject wrapper that holds a reference to ANY
111
+ * WebGPU NativeObject. This is used for Reanimated/Worklets serialization.
112
+ *
113
+ * Since NativeObject uses NativeState (not HostObject), Worklets can't
114
+ * serialize them directly. But Worklets CAN serialize HostObjects.
115
+ *
116
+ * This class stores:
117
+ * - The NativeState from the original object
118
+ * - The brand name for prototype reconstruction
119
+ *
120
+ * Usage pattern with registerCustomSerializable:
121
+ * - pack(): Call WebGPU.box(obj) to create a BoxedWebGPUObject (HostObject)
122
+ * - The HostObject is serialized by Worklets and transferred to UI runtime
123
+ * - unpack(): Call boxed.unbox() to get back the original object with prototype
124
+ *
125
+ * This is similar to NitroModules.box()/unbox() pattern.
126
+ */
127
+ class BoxedWebGPUObject : public jsi::HostObject {
128
+ public:
129
+ BoxedWebGPUObject(std::shared_ptr<jsi::NativeState> nativeState,
130
+ const std::string &brand)
131
+ : _nativeState(std::move(nativeState)), _brand(brand) {}
132
+
133
+ jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override {
134
+ auto propName = name.utf8(runtime);
135
+ if (propName == "unbox") {
136
+ return jsi::Function::createFromHostFunction(
137
+ runtime, jsi::PropNameID::forUtf8(runtime, "unbox"), 0,
138
+ [this](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
139
+ const jsi::Value * /*args*/,
140
+ size_t /*count*/) -> jsi::Value {
141
+ // Try to get the prototype from the global constructor
142
+ auto ctor = rt.global().getProperty(rt, _brand.c_str());
143
+ if (!ctor.isObject()) {
144
+ // Constructor doesn't exist on this runtime - install it
145
+ NativeObjectRegistry::getInstance().installPrototype(rt, _brand);
146
+ ctor = rt.global().getProperty(rt, _brand.c_str());
147
+ }
148
+
149
+ // Create a new object and attach the native state
150
+ jsi::Object obj(rt);
151
+ obj.setNativeState(rt, _nativeState);
152
+
153
+ // Set the prototype if constructor exists
154
+ if (ctor.isObject()) {
155
+ auto ctorObj = ctor.getObject(rt);
156
+ auto proto = ctorObj.getProperty(rt, "prototype");
157
+ if (proto.isObject()) {
158
+ auto objectCtor =
159
+ rt.global().getPropertyAsObject(rt, "Object");
160
+ auto setPrototypeOf =
161
+ objectCtor.getPropertyAsFunction(rt, "setPrototypeOf");
162
+ setPrototypeOf.call(rt, obj, proto);
163
+ }
164
+ }
165
+
166
+ return std::move(obj);
167
+ });
168
+ }
169
+ if (propName == "__boxedWebGPU") {
170
+ return jsi::Value(true);
171
+ }
172
+ if (propName == "__brand") {
173
+ return jsi::String::createFromUtf8(runtime, _brand);
174
+ }
175
+ return jsi::Value::undefined();
176
+ }
177
+
178
+ void set(jsi::Runtime &runtime, const jsi::PropNameID &name,
179
+ const jsi::Value &value) override {
180
+ throw jsi::JSError(runtime, "BoxedWebGPUObject is read-only");
181
+ }
182
+
183
+ std::vector<jsi::PropNameID>
184
+ getPropertyNames(jsi::Runtime &runtime) override {
185
+ std::vector<jsi::PropNameID> names;
186
+ names.reserve(3);
187
+ names.push_back(jsi::PropNameID::forUtf8(runtime, "unbox"));
188
+ names.push_back(jsi::PropNameID::forUtf8(runtime, "__boxedWebGPU"));
189
+ names.push_back(jsi::PropNameID::forUtf8(runtime, "__brand"));
190
+ return names;
191
+ }
192
+
193
+ private:
194
+ std::shared_ptr<jsi::NativeState> _nativeState;
195
+ std::string _brand;
196
+ };
197
+
198
+ /**
199
+ * Base class for native objects using the NativeState pattern.
200
+ *
201
+ * Instead of using HostObject (which intercepts all property access),
202
+ * this pattern:
203
+ * 1. Stores native data via jsi::Object::setNativeState()
204
+ * 2. Installs methods on a shared prototype object (once per runtime)
205
+ * 3. Creates plain JS objects that use the prototype chain
206
+ *
207
+ * Usage:
208
+ * ```cpp
209
+ * class MyClass : public NativeObject<MyClass> {
210
+ * public:
211
+ * static constexpr const char* CLASS_NAME = "MyClass";
212
+ *
213
+ * MyClass(...) : NativeObject(CLASS_NAME), ... {}
214
+ *
215
+ * std::string getValue() { return _value; }
216
+ *
217
+ * static void definePrototype(jsi::Runtime& rt, jsi::Object& proto) {
218
+ * installGetter(rt, proto, "value", &MyClass::getValue);
219
+ * }
220
+ *
221
+ * private:
222
+ * std::string _value;
223
+ * };
224
+ * ```
225
+ */
226
+ template <typename Derived>
227
+ class NativeObject : public jsi::NativeState,
228
+ public std::enable_shared_from_this<Derived> {
229
+ public:
230
+ // Marker type for SFINAE detection in JSIConverter
231
+ using IsNativeObject = std::true_type;
232
+
233
+ /**
234
+ * Get the prototype cache for this type.
235
+ * Each NativeObject<Derived> type has its own static cache.
236
+ * Uses StaticRuntimeAwareCache to properly handle runtime lifecycle
237
+ * and hot reload (where the main runtime is destroyed and recreated).
238
+ */
239
+ static RuntimeAwareCache<PrototypeCacheEntry> &
240
+ getPrototypeCache(jsi::Runtime &runtime) {
241
+ static StaticRuntimeAwareCache<PrototypeCacheEntry> cache;
242
+ return cache.get(runtime);
243
+ }
244
+
245
+ /**
246
+ * Ensure the prototype is installed for this runtime.
247
+ * Called automatically by create(), but can be called manually.
248
+ */
249
+ static void installPrototype(jsi::Runtime &runtime) {
250
+ auto &entry = getPrototypeCache(runtime).get(runtime);
251
+ if (entry.prototype.has_value()) {
252
+ return; // Already installed
253
+ }
254
+
255
+ // Create prototype object
256
+ jsi::Object prototype(runtime);
257
+
258
+ // Let derived class define its methods/properties
259
+ Derived::definePrototype(runtime, prototype);
260
+
261
+ // Add Symbol.toStringTag for proper object identification in console.log
262
+ auto symbolCtor = runtime.global().getPropertyAsObject(runtime, "Symbol");
263
+ auto toStringTag = symbolCtor.getProperty(runtime, "toStringTag");
264
+ if (!toStringTag.isUndefined()) {
265
+ // Use Object.defineProperty to set symbol property since setProperty
266
+ // doesn't support symbols directly
267
+ auto objectCtor =
268
+ runtime.global().getPropertyAsObject(runtime, "Object");
269
+ auto defineProperty =
270
+ objectCtor.getPropertyAsFunction(runtime, "defineProperty");
271
+ jsi::Object descriptor(runtime);
272
+ descriptor.setProperty(
273
+ runtime, "value",
274
+ jsi::String::createFromUtf8(runtime, Derived::CLASS_NAME));
275
+ descriptor.setProperty(runtime, "writable", false);
276
+ descriptor.setProperty(runtime, "enumerable", false);
277
+ descriptor.setProperty(runtime, "configurable", true);
278
+ defineProperty.call(runtime, prototype, toStringTag, descriptor);
279
+ }
280
+
281
+ // Cache the prototype
282
+ entry.prototype = std::move(prototype);
283
+ }
284
+
285
+ /**
286
+ * Install a constructor function on the global object.
287
+ * This enables `instanceof` checks: `obj instanceof ClassName`
288
+ *
289
+ * The constructor throws if called directly (these objects are only
290
+ * created internally by the native code).
291
+ *
292
+ * Also registers this class with NativeObjectRegistry so that
293
+ * BoxedWebGPUObject::unbox() can install prototypes on secondary runtimes.
294
+ */
295
+ static void installConstructor(jsi::Runtime &runtime) {
296
+ // Register this class's installer in the registry (only needs to happen once)
297
+ static std::once_flag registryFlag;
298
+ std::call_once(registryFlag, []() {
299
+ NativeObjectRegistry::getInstance().registerInstaller(
300
+ Derived::CLASS_NAME,
301
+ [](jsi::Runtime &rt) { Derived::installConstructor(rt); });
302
+ });
303
+
304
+ installPrototype(runtime);
305
+
306
+ auto &entry = getPrototypeCache(runtime).get(runtime);
307
+ if (!entry.prototype.has_value()) {
308
+ return;
309
+ }
310
+
311
+ // Create a constructor function that throws when called directly
312
+ auto ctor = jsi::Function::createFromHostFunction(
313
+ runtime, jsi::PropNameID::forUtf8(runtime, Derived::CLASS_NAME), 0,
314
+ [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
315
+ const jsi::Value * /*args*/, size_t /*count*/) -> jsi::Value {
316
+ throw jsi::JSError(
317
+ rt, std::string("Illegal constructor: ") + Derived::CLASS_NAME +
318
+ " objects are created by the WebGPU API");
319
+ });
320
+
321
+ // Set the prototype property on the constructor
322
+ // This is what makes `instanceof` work
323
+ ctor.setProperty(runtime, "prototype", *entry.prototype);
324
+
325
+ // Set constructor property on prototype pointing back to constructor
326
+ entry.prototype->setProperty(runtime, "constructor", ctor);
327
+
328
+ // Install on global
329
+ runtime.global().setProperty(runtime, Derived::CLASS_NAME, std::move(ctor));
330
+ }
331
+
332
+ /**
333
+ * Create a JS object with native state attached.
334
+ */
335
+ static jsi::Value create(jsi::Runtime &runtime,
336
+ std::shared_ptr<Derived> instance) {
337
+ installPrototype(runtime);
338
+
339
+ // Store creation runtime for logging etc.
340
+ instance->setCreationRuntime(&runtime);
341
+
342
+ // Create a new object
343
+ jsi::Object obj(runtime);
344
+
345
+ // Attach native state
346
+ obj.setNativeState(runtime, instance);
347
+
348
+ // Set prototype
349
+ auto &entry = getPrototypeCache(runtime).get(runtime);
350
+ if (entry.prototype.has_value()) {
351
+ // Use Object.setPrototypeOf to set the prototype
352
+ auto objectCtor =
353
+ runtime.global().getPropertyAsObject(runtime, "Object");
354
+ auto setPrototypeOf =
355
+ objectCtor.getPropertyAsFunction(runtime, "setPrototypeOf");
356
+ setPrototypeOf.call(runtime, obj, *entry.prototype);
357
+ }
358
+
359
+ // Set memory pressure hint for GC
360
+ auto pressure = instance->getMemoryPressure();
361
+ if (pressure > 0) {
362
+ obj.setExternalMemoryPressure(runtime, pressure);
363
+ }
364
+
365
+ return std::move(obj);
366
+ }
367
+
368
+ /**
369
+ * Get the native state from a JS value.
370
+ * Throws if the value doesn't have the expected native state.
371
+ */
372
+ static std::shared_ptr<Derived> fromValue(jsi::Runtime &runtime,
373
+ const jsi::Value &value) {
374
+ if (!value.isObject()) {
375
+ throw jsi::JSError(runtime, std::string("Expected ") +
376
+ Derived::CLASS_NAME +
377
+ " but got non-object");
378
+ }
379
+ jsi::Object obj = value.getObject(runtime);
380
+ if (!obj.hasNativeState<Derived>(runtime)) {
381
+ throw jsi::JSError(runtime, std::string("Expected ") +
382
+ Derived::CLASS_NAME +
383
+ " but got different type");
384
+ }
385
+ return obj.getNativeState<Derived>(runtime);
386
+ }
387
+
388
+ /**
389
+ * Memory pressure for GC hints. Override in derived classes.
390
+ */
391
+ virtual size_t getMemoryPressure() { return 1024; }
392
+
393
+ /**
394
+ * Set the creation runtime. Called during create().
395
+ */
396
+ void setCreationRuntime(jsi::Runtime *runtime) { _creationRuntime = runtime; }
397
+
398
+ /**
399
+ * Get the creation runtime.
400
+ * WARNING: This pointer may become invalid if the runtime is destroyed.
401
+ */
402
+ jsi::Runtime *getCreationRuntime() const { return _creationRuntime; }
403
+
404
+ protected:
405
+ explicit NativeObject(const char *name) : _name(name) {
406
+ #if DEBUG && RNF_ENABLE_LOGS
407
+ Logger::logToConsole("NativeObject", "(MEMORY) Creating %s... ✅", _name);
408
+ #endif
409
+ }
410
+
411
+ virtual ~NativeObject() {
412
+ #if DEBUG && RNF_ENABLE_LOGS
413
+ Logger::log("NativeObject", "(MEMORY) Deleting %s... ❌", _name);
414
+ #endif
415
+ }
416
+
417
+ const char *_name;
418
+ jsi::Runtime *_creationRuntime = nullptr;
419
+
420
+ // ============================================================
421
+ // Helper methods for definePrototype() implementations
422
+ // ============================================================
423
+
424
+ /**
425
+ * Install a method on the prototype.
426
+ */
427
+ template <typename ReturnType, typename... Args>
428
+ static void installMethod(jsi::Runtime &runtime, jsi::Object &prototype,
429
+ const char *name,
430
+ ReturnType (Derived::*method)(Args...)) {
431
+ auto func = jsi::Function::createFromHostFunction(
432
+ runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args),
433
+ [method](jsi::Runtime &rt, const jsi::Value &thisVal,
434
+ const jsi::Value *args, size_t count) -> jsi::Value {
435
+ auto native = Derived::fromValue(rt, thisVal);
436
+ return callMethod(native.get(), method, rt, args,
437
+ std::index_sequence_for<Args...>{}, count);
438
+ });
439
+ prototype.setProperty(runtime, name, func);
440
+ }
441
+
442
+ /**
443
+ * Install a getter on the prototype.
444
+ */
445
+ template <typename ReturnType>
446
+ static void installGetter(jsi::Runtime &runtime, jsi::Object &prototype,
447
+ const char *name, ReturnType (Derived::*getter)()) {
448
+ // Create a getter function
449
+ auto getterFunc = jsi::Function::createFromHostFunction(
450
+ runtime, jsi::PropNameID::forUtf8(runtime, std::string("get_") + name),
451
+ 0,
452
+ [getter](jsi::Runtime &rt, const jsi::Value &thisVal,
453
+ const jsi::Value *args, size_t count) -> jsi::Value {
454
+ auto native = Derived::fromValue(rt, thisVal);
455
+ if constexpr (std::is_same_v<ReturnType, void>) {
456
+ (native.get()->*getter)();
457
+ return jsi::Value::undefined();
458
+ } else {
459
+ ReturnType result = (native.get()->*getter)();
460
+ return rnwgpu::JSIConverter<std::decay_t<ReturnType>>::toJSI(
461
+ rt, std::move(result));
462
+ }
463
+ });
464
+
465
+ // Use Object.defineProperty to create a proper getter
466
+ auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
467
+ auto defineProperty =
468
+ objectCtor.getPropertyAsFunction(runtime, "defineProperty");
469
+
470
+ jsi::Object descriptor(runtime);
471
+ descriptor.setProperty(runtime, "get", getterFunc);
472
+ descriptor.setProperty(runtime, "enumerable", true);
473
+ descriptor.setProperty(runtime, "configurable", true);
474
+
475
+ defineProperty.call(runtime, prototype,
476
+ jsi::String::createFromUtf8(runtime, name), descriptor);
477
+ }
478
+
479
+ /**
480
+ * Install a setter on the prototype.
481
+ */
482
+ template <typename ValueType>
483
+ static void installSetter(jsi::Runtime &runtime, jsi::Object &prototype,
484
+ const char *name,
485
+ void (Derived::*setter)(ValueType)) {
486
+ auto setterFunc = jsi::Function::createFromHostFunction(
487
+ runtime, jsi::PropNameID::forUtf8(runtime, std::string("set_") + name),
488
+ 1,
489
+ [setter](jsi::Runtime &rt, const jsi::Value &thisVal,
490
+ const jsi::Value *args, size_t count) -> jsi::Value {
491
+ if (count < 1) {
492
+ throw jsi::JSError(rt, "Setter requires a value argument");
493
+ }
494
+ auto native = Derived::fromValue(rt, thisVal);
495
+ auto value =
496
+ rnwgpu::JSIConverter<std::decay_t<ValueType>>::fromJSI(rt, args[0], false);
497
+ (native.get()->*setter)(std::move(value));
498
+ return jsi::Value::undefined();
499
+ });
500
+
501
+ // Use Object.defineProperty to create a proper setter
502
+ auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
503
+ auto defineProperty =
504
+ objectCtor.getPropertyAsFunction(runtime, "defineProperty");
505
+
506
+ // Check if property already has a getter
507
+ auto getOwnPropertyDescriptor =
508
+ objectCtor.getPropertyAsFunction(runtime, "getOwnPropertyDescriptor");
509
+ auto existingDesc = getOwnPropertyDescriptor.call(
510
+ runtime, prototype, jsi::String::createFromUtf8(runtime, name));
511
+
512
+ jsi::Object descriptor(runtime);
513
+ if (existingDesc.isObject()) {
514
+ auto existingDescObj = existingDesc.getObject(runtime);
515
+ if (existingDescObj.hasProperty(runtime, "get")) {
516
+ descriptor.setProperty(
517
+ runtime, "get", existingDescObj.getProperty(runtime, "get"));
518
+ }
519
+ }
520
+ descriptor.setProperty(runtime, "set", setterFunc);
521
+ descriptor.setProperty(runtime, "enumerable", true);
522
+ descriptor.setProperty(runtime, "configurable", true);
523
+
524
+ defineProperty.call(runtime, prototype,
525
+ jsi::String::createFromUtf8(runtime, name), descriptor);
526
+ }
527
+
528
+ /**
529
+ * Install both getter and setter for a property.
530
+ */
531
+ template <typename ReturnType, typename ValueType>
532
+ static void installGetterSetter(jsi::Runtime &runtime, jsi::Object &prototype,
533
+ const char *name,
534
+ ReturnType (Derived::*getter)(),
535
+ void (Derived::*setter)(ValueType)) {
536
+ auto getterFunc = jsi::Function::createFromHostFunction(
537
+ runtime, jsi::PropNameID::forUtf8(runtime, std::string("get_") + name),
538
+ 0,
539
+ [getter](jsi::Runtime &rt, const jsi::Value &thisVal,
540
+ const jsi::Value *args, size_t count) -> jsi::Value {
541
+ auto native = Derived::fromValue(rt, thisVal);
542
+ ReturnType result = (native.get()->*getter)();
543
+ return rnwgpu::JSIConverter<std::decay_t<ReturnType>>::toJSI(rt,
544
+ std::move(result));
545
+ });
546
+
547
+ auto setterFunc = jsi::Function::createFromHostFunction(
548
+ runtime, jsi::PropNameID::forUtf8(runtime, std::string("set_") + name),
549
+ 1,
550
+ [setter](jsi::Runtime &rt, const jsi::Value &thisVal,
551
+ const jsi::Value *args, size_t count) -> jsi::Value {
552
+ if (count < 1) {
553
+ throw jsi::JSError(rt, "Setter requires a value argument");
554
+ }
555
+ auto native = Derived::fromValue(rt, thisVal);
556
+ auto value =
557
+ rnwgpu::JSIConverter<std::decay_t<ValueType>>::fromJSI(rt, args[0], false);
558
+ (native.get()->*setter)(std::move(value));
559
+ return jsi::Value::undefined();
560
+ });
561
+
562
+ auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
563
+ auto defineProperty =
564
+ objectCtor.getPropertyAsFunction(runtime, "defineProperty");
565
+
566
+ jsi::Object descriptor(runtime);
567
+ descriptor.setProperty(runtime, "get", getterFunc);
568
+ descriptor.setProperty(runtime, "set", setterFunc);
569
+ descriptor.setProperty(runtime, "enumerable", true);
570
+ descriptor.setProperty(runtime, "configurable", true);
571
+
572
+ defineProperty.call(runtime, prototype,
573
+ jsi::String::createFromUtf8(runtime, name), descriptor);
574
+ }
575
+
576
+ private:
577
+ // Helper to call a method with JSI argument conversion
578
+ template <typename ReturnType, typename... Args, size_t... Is>
579
+ static jsi::Value callMethod(Derived *obj,
580
+ ReturnType (Derived::*method)(Args...),
581
+ jsi::Runtime &runtime, const jsi::Value *args,
582
+ std::index_sequence<Is...>, size_t count) {
583
+ if constexpr (std::is_same_v<ReturnType, void>) {
584
+ (obj->*method)(rnwgpu::JSIConverter<std::decay_t<Args>>::fromJSI(
585
+ runtime, args[Is], Is >= count)...);
586
+ return jsi::Value::undefined();
587
+ } else if constexpr (std::is_same_v<ReturnType, jsi::Value>) {
588
+ // Special case: if return type is jsi::Value, method has full control
589
+ // This requires the method signature to match HostFunction
590
+ return (obj->*method)(runtime, jsi::Value::undefined(), args, count);
591
+ } else {
592
+ ReturnType result = (obj->*method)(rnwgpu::JSIConverter<std::decay_t<Args>>::fromJSI(
593
+ runtime, args[Is], Is >= count)...);
594
+ return rnwgpu::JSIConverter<std::decay_t<ReturnType>>::toJSI(runtime,
595
+ std::move(result));
596
+ }
597
+ }
598
+ };
599
+
600
+ // Type trait to detect NativeObject-derived classes
601
+ template <typename T> struct is_native_object : std::false_type {};
602
+
603
+ template <typename T>
604
+ struct is_native_object<std::shared_ptr<T>>
605
+ : std::bool_constant<std::is_base_of_v<NativeObject<T>, T>> {};
606
+
607
+ } // namespace rnwgpu
@@ -1,7 +1,4 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #include "RNFPromise.h"
1
+ #include "Promise.h"
5
2
  #include <future>
6
3
  #include <jsi/jsi.h>
7
4
  #include <memory>
@@ -9,7 +6,7 @@
9
6
  #include <utility>
10
7
  #include <vector>
11
8
 
12
- namespace margelo {
9
+ namespace rnwgpu {
13
10
 
14
11
  namespace jsi = facebook::jsi;
15
12
 
@@ -44,4 +41,4 @@ void Promise::reject(std::string message) {
44
41
  _rejecter.call(runtime, error.value());
45
42
  }
46
43
 
47
- } // namespace margelo
44
+ } // namespace rnwgpu
@@ -1,6 +1,3 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
1
  #pragma once
5
2
 
6
3
  #include <jsi/jsi.h>
@@ -9,7 +6,7 @@
9
6
  #include <string>
10
7
  #include <memory>
11
8
 
12
- namespace margelo {
9
+ namespace rnwgpu {
13
10
 
14
11
  namespace jsi = facebook::jsi;
15
12
 
@@ -35,4 +32,4 @@ public:
35
32
  static jsi::Value createPromise(jsi::Runtime& runtime, RunPromise run);
36
33
  };
37
34
 
38
- } // namespace margelo
35
+ } // namespace rnwgpu
@@ -0,0 +1,7 @@
1
+ #include "RuntimeAwareCache.h"
2
+
3
+ namespace rnwgpu {
4
+
5
+ jsi::Runtime *BaseRuntimeAwareCache::_mainRuntime = nullptr;
6
+
7
+ } // namespace rnwgpu