react-native-webgpu 0.5.11 → 0.5.13

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 (57) hide show
  1. package/README.md +84 -0
  2. package/android/CMakeLists.txt +2 -0
  3. package/android/cpp/AndroidPlatformContext.h +121 -0
  4. package/apple/ApplePlatformContext.h +13 -0
  5. package/apple/ApplePlatformContext.mm +145 -0
  6. package/apple/AppleVideoPlayer.h +31 -0
  7. package/apple/AppleVideoPlayer.mm +314 -0
  8. package/cpp/rnwgpu/ArrayBuffer.h +51 -7
  9. package/cpp/rnwgpu/PlatformContext.h +81 -0
  10. package/cpp/rnwgpu/RNWebGPUManager.cpp +12 -0
  11. package/cpp/rnwgpu/api/Convertors.h +33 -11
  12. package/cpp/rnwgpu/api/GPU.cpp +27 -0
  13. package/cpp/rnwgpu/api/GPUAdapter.cpp +109 -33
  14. package/cpp/rnwgpu/api/GPUDevice.cpp +58 -5
  15. package/cpp/rnwgpu/api/GPUDevice.h +6 -0
  16. package/cpp/rnwgpu/api/GPUExternalTexture.cpp +335 -0
  17. package/cpp/rnwgpu/api/GPUExternalTexture.h +47 -2
  18. package/cpp/rnwgpu/api/GPUFeatures.h +4 -1
  19. package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -3
  20. package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +80 -0
  21. package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +71 -0
  22. package/cpp/rnwgpu/api/ImageBitmap.h +8 -0
  23. package/cpp/rnwgpu/api/RNWebGPU.h +63 -21
  24. package/cpp/rnwgpu/api/RnFeatures.h +53 -0
  25. package/cpp/rnwgpu/api/VideoFrame.h +76 -0
  26. package/cpp/rnwgpu/api/VideoPlayer.h +69 -0
  27. package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +4 -1
  28. package/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h +57 -0
  29. package/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +18 -3
  30. package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +35 -33
  31. package/cpp/rnwgpu/api/descriptors/GPUSharedTextureMemoryDescriptor.h +62 -0
  32. package/cpp/rnwgpu/api/descriptors/Unions.h +10 -0
  33. package/cpp/webgpu/webgpu.h +78 -17
  34. package/cpp/webgpu/webgpu_cpp.h +1014 -1249
  35. package/cpp/webgpu/webgpu_cpp_print.h +99 -10
  36. package/lib/commonjs/Canvas.js.map +1 -1
  37. package/lib/commonjs/index.js.map +1 -1
  38. package/lib/module/Canvas.js.map +1 -1
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/typescript/src/Canvas.d.ts +0 -10
  41. package/lib/typescript/src/Canvas.d.ts.map +1 -1
  42. package/lib/typescript/src/index.d.ts +20 -1
  43. package/lib/typescript/src/index.d.ts.map +1 -1
  44. package/lib/typescript/src/types.d.ts +32 -1
  45. package/lib/typescript/src/types.d.ts.map +1 -1
  46. package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
  47. package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
  48. package/libs/android/x86/libwebgpu_dawn.so +0 -0
  49. package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
  50. package/libs/apple/libwebgpu_dawn.xcframework/Info.plist +6 -6
  51. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64/libwebgpu_dawn.a +0 -0
  52. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
  53. package/libs/apple/libwebgpu_dawn.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
  54. package/package.json +3 -3
  55. package/src/Canvas.tsx +0 -15
  56. package/src/index.tsx +62 -2
  57. package/src/types.ts +83 -1
@@ -1,10 +1,13 @@
1
1
  #pragma once
2
2
 
3
+ #include <memory>
3
4
  #include <string>
5
+ #include <utility>
4
6
 
5
7
  #include "Unions.h"
6
8
 
7
9
  #include "NativeObject.h"
10
+ #include "VideoFrame.h"
8
11
 
9
12
  #include "webgpu/webgpu_cpp.h"
10
13
 
@@ -12,16 +15,54 @@ namespace rnwgpu {
12
15
 
13
16
  namespace jsi = facebook::jsi;
14
17
 
18
+ struct GPUExternalTextureDescriptor;
19
+
15
20
  class GPUExternalTexture : public NativeObject<GPUExternalTexture> {
16
21
  public:
17
22
  static constexpr const char *CLASS_NAME = "GPUExternalTexture";
18
23
 
19
- explicit GPUExternalTexture(wgpu::ExternalTexture instance, std::string label)
20
- : NativeObject(CLASS_NAME), _instance(instance), _label(label) {}
24
+ // Import a VideoFrame (via descriptor.source) as a GPUExternalTexture on
25
+ // `device`: imports the native surface as SharedTextureMemory, begins access,
26
+ // and wraps the resulting wgpu::ExternalTexture together with the resources
27
+ // whose lifetime it owns. The matching EndAccess runs in destroy() / the
28
+ // destructor. Defined in GPUExternalTexture.cpp.
29
+ static std::shared_ptr<GPUExternalTexture>
30
+ Create(wgpu::Device device,
31
+ std::shared_ptr<GPUExternalTextureDescriptor> descriptor);
32
+
33
+ // Construct from an already-built wgpu::ExternalTexture plus the underlying
34
+ // shared-memory resources we need to keep alive. The wrapper takes ownership
35
+ // of the SharedTextureMemory + Texture and calls EndAccess on destruction so
36
+ // the producer (e.g. AVPlayer) can reclaim the IOSurface.
37
+ GPUExternalTexture(wgpu::ExternalTexture instance,
38
+ wgpu::SharedTextureMemory memory, wgpu::Texture texture,
39
+ std::shared_ptr<VideoFrame> source, std::string label)
40
+ : NativeObject(CLASS_NAME), _instance(std::move(instance)),
41
+ _memory(std::move(memory)), _texture(std::move(texture)),
42
+ _source(std::move(source)), _label(std::move(label)) {}
43
+
44
+ ~GPUExternalTexture() override { destroy(); }
21
45
 
22
46
  public:
23
47
  std::string getBrand() { return CLASS_NAME; }
24
48
 
49
+ // End the shared-memory access window and release the underlying resources.
50
+ // Idempotent: safe to call more than once, and the destructor calls it as a
51
+ // garbage-collection fallback. Call it right after the queue.submit() that
52
+ // sampled this texture (never before): a GPUExternalTexture's access window
53
+ // is owned by this wrapper's lifetime, not by submit, so without an explicit
54
+ // destroy() the producer's surface (e.g. an AVPlayer IOSurface) stays claimed
55
+ // until GC runs. EndAccess is the designed post-submit call: Dawn keeps the
56
+ // texture alive for in-flight GPU work via the fences it returns.
57
+ void destroy() {
58
+ if (_memory && _texture) {
59
+ wgpu::SharedTextureMemoryEndAccessState state{};
60
+ (void)_memory.EndAccess(_texture, &state);
61
+ }
62
+ _texture = nullptr;
63
+ _memory = nullptr;
64
+ }
65
+
25
66
  std::string getLabel() { return _label; }
26
67
  void setLabel(const std::string &label) {
27
68
  _label = label;
@@ -33,12 +74,16 @@ public:
33
74
  installGetterSetter(runtime, prototype, "label",
34
75
  &GPUExternalTexture::getLabel,
35
76
  &GPUExternalTexture::setLabel);
77
+ installMethod(runtime, prototype, "destroy", &GPUExternalTexture::destroy);
36
78
  }
37
79
 
38
80
  inline const wgpu::ExternalTexture get() { return _instance; }
39
81
 
40
82
  private:
41
83
  wgpu::ExternalTexture _instance;
84
+ wgpu::SharedTextureMemory _memory;
85
+ wgpu::Texture _texture;
86
+ std::shared_ptr<VideoFrame> _source;
42
87
  std::string _label;
43
88
  };
44
89
 
@@ -168,7 +168,7 @@ static void convertEnumToJSUnion(wgpu::FeatureName inEnum,
168
168
  *outUnion = "shared-fence-vk-semaphore-opaque-fd";
169
169
  break;
170
170
  case wgpu::FeatureName::SharedFenceSyncFD:
171
- *outUnion = "shared-fence-vk-semaphore-sync-fd";
171
+ *outUnion = "shared-fence-sync-fd";
172
172
  break;
173
173
  case wgpu::FeatureName::SharedFenceVkSemaphoreZirconHandle:
174
174
  *outUnion = "shared-fence-vk-semaphore-zircon-handle";
@@ -188,6 +188,9 @@ static void convertEnumToJSUnion(wgpu::FeatureName inEnum,
188
188
  case wgpu::FeatureName::YCbCrVulkanSamplers:
189
189
  *outUnion = "ycbcr-vulkan-samplers";
190
190
  break;
191
+ case wgpu::FeatureName::OpaqueYCbCrAndroidForExternalTexture:
192
+ *outUnion = "opaque-ycbcr-android-for-external-texture";
193
+ break;
191
194
  case wgpu::FeatureName::ShaderModuleCompilationOptions:
192
195
  *outUnion = "shader-module-compilation-options";
193
196
  break;
@@ -18,7 +18,7 @@ async::AsyncTaskHandle GPUShaderModule::getCompilationInfo() {
18
18
  wgpu::CallbackMode::AllowProcessEvents,
19
19
  [result, resolve,
20
20
  reject](wgpu::CompilationInfoRequestStatus status,
21
- const wgpu::CompilationInfo *compilationInfo) mutable {
21
+ const wgpu::CompilationInfo *compilationInfo) {
22
22
  if (status != wgpu::CompilationInfoRequestStatus::Success ||
23
23
  compilationInfo == nullptr) {
24
24
  reject("Failed to get compilation info");
@@ -39,8 +39,7 @@ async::AsyncTaskHandle GPUShaderModule::getCompilationInfo() {
39
39
  result->_messages.push_back(std::move(message));
40
40
  }
41
41
 
42
- resolve([result =
43
- std::move(result)](jsi::Runtime &runtime) mutable {
42
+ resolve([result](jsi::Runtime &runtime) mutable {
44
43
  return JSIConverter<std::shared_ptr<GPUCompilationInfo>>::toJSI(
45
44
  runtime, result);
46
45
  });
@@ -0,0 +1,80 @@
1
+ #include "GPUSharedTextureMemory.h"
2
+
3
+ #include <memory>
4
+ #include <stdexcept>
5
+ #include <string>
6
+
7
+ #include "Convertors.h"
8
+
9
+ namespace rnwgpu {
10
+
11
+ std::shared_ptr<GPUTexture> GPUSharedTextureMemory::createTexture(
12
+ std::optional<std::shared_ptr<GPUTextureDescriptor>> descriptor) {
13
+ if (!descriptor.has_value() || descriptor.value() == nullptr) {
14
+ auto texture = _instance.CreateTexture();
15
+ return std::make_shared<GPUTexture>(texture, "");
16
+ }
17
+
18
+ wgpu::TextureDescriptor desc{};
19
+ Convertor conv;
20
+ if (!conv(desc, descriptor.value())) {
21
+ throw std::runtime_error(
22
+ "GPUSharedTextureMemory::createTexture(): Error with "
23
+ "GPUTextureDescriptor");
24
+ }
25
+ auto texture = _instance.CreateTexture(&desc);
26
+ return std::make_shared<GPUTexture>(texture,
27
+ descriptor.value()->label.value_or(""));
28
+ }
29
+
30
+ bool GPUSharedTextureMemory::beginAccess(std::shared_ptr<GPUTexture> texture,
31
+ bool initialized) {
32
+ if (!texture) {
33
+ throw std::runtime_error(
34
+ "GPUSharedTextureMemory::beginAccess(): texture is null");
35
+ }
36
+ wgpu::SharedTextureMemoryBeginAccessDescriptor desc{};
37
+ desc.initialized = initialized;
38
+ desc.concurrentRead = false;
39
+ desc.fenceCount = 0;
40
+ desc.fences = nullptr;
41
+ desc.signaledValues = nullptr;
42
+
43
+ #if defined(__ANDROID__)
44
+ // Dawn's Vulkan backend (AHardwareBuffer) validates that the begin-access
45
+ // descriptor chains a SharedTextureMemoryVkImageLayoutBeginState specifying
46
+ // the VkImageLayout to acquire the image into. UNDEFINED (= 0) on both ends
47
+ // is the canonical "no prior GPU producer" pattern: Dawn performs an
48
+ // external-queue acquire from VK_QUEUE_FAMILY_EXTERNAL which preserves the
49
+ // AHB contents, then transitions to whatever layout the texture's actual
50
+ // usage requires.
51
+ wgpu::SharedTextureMemoryVkImageLayoutBeginState vkLayout{};
52
+ vkLayout.oldLayout = 0;
53
+ vkLayout.newLayout = 0;
54
+ desc.nextInChain = &vkLayout;
55
+ #endif
56
+
57
+ auto status = _instance.BeginAccess(texture->get(), &desc);
58
+ return static_cast<bool>(status);
59
+ }
60
+
61
+ bool GPUSharedTextureMemory::endAccess(std::shared_ptr<GPUTexture> texture) {
62
+ if (!texture) {
63
+ throw std::runtime_error(
64
+ "GPUSharedTextureMemory::endAccess(): texture is null");
65
+ }
66
+ wgpu::SharedTextureMemoryEndAccessState state{};
67
+
68
+ #if defined(__ANDROID__)
69
+ // Dawn's Vulkan backend writes the released old/new VkImageLayouts back into
70
+ // a chained SharedTextureMemoryVkImageLayoutEndState; validation requires
71
+ // the chain even when the caller doesn't read the values.
72
+ wgpu::SharedTextureMemoryVkImageLayoutEndState vkLayout{};
73
+ state.nextInChain = &vkLayout;
74
+ #endif
75
+
76
+ auto status = _instance.EndAccess(texture->get(), &state);
77
+ return static_cast<bool>(status);
78
+ }
79
+
80
+ } // namespace rnwgpu
@@ -0,0 +1,71 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <optional>
5
+ #include <string>
6
+
7
+ #include "NativeObject.h"
8
+
9
+ #include "webgpu/webgpu_cpp.h"
10
+
11
+ #include "GPUTexture.h"
12
+ #include "GPUTextureDescriptor.h"
13
+
14
+ namespace rnwgpu {
15
+
16
+ namespace jsi = facebook::jsi;
17
+
18
+ class GPUSharedTextureMemory : public NativeObject<GPUSharedTextureMemory> {
19
+ public:
20
+ static constexpr const char *CLASS_NAME = "GPUSharedTextureMemory";
21
+
22
+ explicit GPUSharedTextureMemory(wgpu::SharedTextureMemory instance,
23
+ std::string label)
24
+ : NativeObject(CLASS_NAME), _instance(std::move(instance)),
25
+ _label(std::move(label)) {}
26
+
27
+ public:
28
+ std::string getBrand() { return CLASS_NAME; }
29
+
30
+ std::shared_ptr<GPUTexture>
31
+ createTexture(std::optional<std::shared_ptr<GPUTextureDescriptor>> descriptor);
32
+
33
+ // Returns true on success. Marks the shared memory as initialized so the
34
+ // texture's content is preserved (or not). Callers that want fence-based
35
+ // synchronization should pass fences via beginAccess descriptor (not yet
36
+ // exposed - we currently take the implicit/no-fence path that matches the
37
+ // most common RN use cases: still images, single-producer video frames).
38
+ bool beginAccess(std::shared_ptr<GPUTexture> texture, bool initialized);
39
+
40
+ // Returns true on success. Drops any fences produced by end-access (we do
41
+ // not yet surface them to JS).
42
+ bool endAccess(std::shared_ptr<GPUTexture> texture);
43
+
44
+ std::string getLabel() { return _label; }
45
+ void setLabel(const std::string &label) {
46
+ _label = label;
47
+ _instance.SetLabel(_label.c_str());
48
+ }
49
+
50
+ static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
51
+ installGetter(runtime, prototype, "__brand",
52
+ &GPUSharedTextureMemory::getBrand);
53
+ installMethod(runtime, prototype, "createTexture",
54
+ &GPUSharedTextureMemory::createTexture);
55
+ installMethod(runtime, prototype, "beginAccess",
56
+ &GPUSharedTextureMemory::beginAccess);
57
+ installMethod(runtime, prototype, "endAccess",
58
+ &GPUSharedTextureMemory::endAccess);
59
+ installGetterSetter(runtime, prototype, "label",
60
+ &GPUSharedTextureMemory::getLabel,
61
+ &GPUSharedTextureMemory::setLabel);
62
+ }
63
+
64
+ inline const wgpu::SharedTextureMemory get() { return _instance; }
65
+
66
+ private:
67
+ wgpu::SharedTextureMemory _instance;
68
+ std::string _label;
69
+ };
70
+
71
+ } // namespace rnwgpu
@@ -26,9 +26,17 @@ public:
26
26
 
27
27
  size_t getSize() { return _imageData.data.size(); }
28
28
 
29
+ void close() {
30
+ _imageData.data.clear();
31
+ _imageData.data.shrink_to_fit();
32
+ _imageData.width = 0;
33
+ _imageData.height = 0;
34
+ }
35
+
29
36
  static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
30
37
  installGetter(runtime, prototype, "width", &ImageBitmap::getWidth);
31
38
  installGetter(runtime, prototype, "height", &ImageBitmap::getHeight);
39
+ installMethod(runtime, prototype, "close", &ImageBitmap::close);
32
40
  }
33
41
 
34
42
  size_t getMemoryPressure() override { return getSize(); }
@@ -5,11 +5,14 @@
5
5
 
6
6
  #include "NativeObject.h"
7
7
 
8
+ #include "ArrayBuffer.h"
8
9
  #include "Canvas.h"
9
10
  #include "GPU.h"
10
11
  #include "GPUCanvasContext.h"
11
12
  #include "ImageBitmap.h"
12
13
  #include "PlatformContext.h"
14
+ #include "VideoFrame.h"
15
+ #include "VideoPlayer.h"
13
16
 
14
17
  #include <ReactCommon/CallInvoker.h>
15
18
 
@@ -88,32 +91,29 @@ public:
88
91
  auto callInvoker = _callInvoker;
89
92
 
90
93
  // Check if the argument is an ArrayBuffer or ArrayBufferView
91
- // (TypedArray / DataView)
94
+ // (TypedArray / DataView). Only a real buffer source is run through the
95
+ // ArrayBuffer converter, which validates byteOffset/byteLength against the
96
+ // backing buffer and throws on an out-of-bounds (or spoofed) view. Anything
97
+ // else (e.g. a Blob) is left to the fall-through path below, so its errors
98
+ // are not misreported as bounds errors here.
92
99
  if (args[0].isObject()) {
93
100
  auto obj = args[0].getObject(runtime);
94
101
 
95
- std::span<const uint8_t> data;
96
-
97
- if (obj.isArrayBuffer(runtime)) {
98
- // Plain ArrayBuffer — use the full buffer
99
- const auto &ab = obj.getArrayBuffer(runtime);
100
- data = {ab.data(runtime), ab.size(runtime)};
101
- } else if (obj.hasProperty(runtime, "buffer")) {
102
- // TypedArray or DataView — respect byteOffset/byteLength
103
- auto bufferVal = obj.getProperty(runtime, "buffer");
104
- if (bufferVal.isObject() &&
105
- bufferVal.getObject(runtime).isArrayBuffer(runtime)) {
106
- const auto &ab = bufferVal.getObject(runtime).getArrayBuffer(runtime);
107
- auto byteOffset = static_cast<size_t>(
108
- obj.getProperty(runtime, "byteOffset").asNumber());
109
- auto byteLength = static_cast<size_t>(
110
- obj.getProperty(runtime, "byteLength").asNumber());
111
- data = {ab.data(runtime) + byteOffset, byteLength};
112
- }
102
+ bool isBufferSource = obj.isArrayBuffer(runtime);
103
+ if (!isBufferSource && obj.hasProperty(runtime, "buffer")) {
104
+ auto bufferProp = obj.getProperty(runtime, "buffer");
105
+ isBufferSource = bufferProp.isObject() &&
106
+ bufferProp.getObject(runtime).isArrayBuffer(runtime);
113
107
  }
114
108
 
115
- if (!data.empty()) {
116
- // Copy bytes on the JS thread the ArrayBuffer pointer is into
109
+ if (isBufferSource) {
110
+ // Bounds violations propagate out of fromJSI so the call rejects
111
+ // rather than reading out of bounds below.
112
+ auto buffer = JSIConverter<std::shared_ptr<ArrayBuffer>>::fromJSI(
113
+ runtime, args[0], false);
114
+ std::span<const uint8_t> data{buffer->data(), buffer->size()};
115
+
116
+ // Copy bytes on the JS thread: the ArrayBuffer pointer is into
117
117
  // JS-owned memory that can be GC'd
118
118
  std::vector<uint8_t> dataCopy(data.begin(), data.end());
119
119
 
@@ -168,6 +168,38 @@ public:
168
168
  });
169
169
  }
170
170
 
171
+ std::shared_ptr<VideoFrame> loadVideoFrame(std::string path) {
172
+ auto frame = _platformContext->loadVideoFrame(path);
173
+ return std::make_shared<VideoFrame>(std::move(frame));
174
+ }
175
+
176
+ std::shared_ptr<VideoFrame> createTestVideoFrame(double width, double height) {
177
+ auto frame = _platformContext->createTestVideoFrame(
178
+ static_cast<uint32_t>(width), static_cast<uint32_t>(height));
179
+ return std::make_shared<VideoFrame>(std::move(frame));
180
+ }
181
+
182
+ // Wrap a CVPixelBufferRef / AHardwareBuffer* pointer (typed as void* via
183
+ // BigInt on the JS side) into one of our VideoFrames. The native side
184
+ // CFRetains / acquires so the caller can release immediately.
185
+ std::shared_ptr<VideoFrame> createVideoFrameFromNativeBuffer(void *pointer) {
186
+ auto handle = _platformContext->wrapNativeBuffer(pointer);
187
+ return std::make_shared<VideoFrame>(std::move(handle));
188
+ }
189
+
190
+ std::shared_ptr<VideoPlayer>
191
+ createVideoPlayer(std::string path, std::optional<std::string> pixelFormat) {
192
+ auto format = (pixelFormat && pixelFormat.value() == "nv12")
193
+ ? VideoPixelFormat::NV12
194
+ : VideoPixelFormat::BGRA8;
195
+ auto impl = _platformContext->createVideoPlayer(path, format);
196
+ return std::make_shared<VideoPlayer>(std::move(impl));
197
+ }
198
+
199
+ std::string writeTestVideoFile() {
200
+ return _platformContext->writeTestVideoFile();
201
+ }
202
+
171
203
  std::shared_ptr<Canvas> getNativeSurface(int contextId) {
172
204
  auto &registry = rnwgpu::SurfaceRegistry::getInstance();
173
205
  auto info = registry.getSurfaceInfo(contextId);
@@ -188,6 +220,16 @@ public:
188
220
  &RNWebGPU::getNativeSurface);
189
221
  installMethod(runtime, prototype, "MakeWebGPUCanvasContext",
190
222
  &RNWebGPU::MakeWebGPUCanvasContext);
223
+ installMethod(runtime, prototype, "loadVideoFrame",
224
+ &RNWebGPU::loadVideoFrame);
225
+ installMethod(runtime, prototype, "createTestVideoFrame",
226
+ &RNWebGPU::createTestVideoFrame);
227
+ installMethod(runtime, prototype, "createVideoFrameFromNativeBuffer",
228
+ &RNWebGPU::createVideoFrameFromNativeBuffer);
229
+ installMethod(runtime, prototype, "createVideoPlayer",
230
+ &RNWebGPU::createVideoPlayer);
231
+ installMethod(runtime, prototype, "writeTestVideoFile",
232
+ &RNWebGPU::writeTestVideoFile);
191
233
  }
192
234
 
193
235
  private:
@@ -0,0 +1,53 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <unordered_set>
5
+ #include <vector>
6
+
7
+ #include "webgpu/webgpu_cpp.h"
8
+
9
+ namespace rnwgpu {
10
+
11
+ // Umbrella feature name owned by react-native-wgpu. Backed by a
12
+ // platform-specific pair of Dawn features. The prefix is intentional: this
13
+ // string is not part of the WebGPU spec, it is our API surface for the
14
+ // "import a native surface as a sampleable texture" capability.
15
+ inline constexpr const char *kRnNativeTextureFeature =
16
+ "rnwebgpu/native-texture";
17
+
18
+ // Dawn features that back the umbrella on the current platform. Empty on
19
+ // platforms where the capability is not available, in which case the umbrella
20
+ // behaves as a no-op (it won't appear in adapter.features and asking for it
21
+ // in requiredFeatures expands to nothing).
22
+ inline std::vector<wgpu::FeatureName> rnNativeTextureBackingFeatures() {
23
+ #if defined(__APPLE__)
24
+ return {wgpu::FeatureName::SharedTextureMemoryIOSurface,
25
+ wgpu::FeatureName::SharedFenceMTLSharedEvent};
26
+ #elif defined(__ANDROID__)
27
+ return {wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer,
28
+ wgpu::FeatureName::SharedFenceSyncFD};
29
+ #else
30
+ return {};
31
+ #endif
32
+ }
33
+
34
+ // If every Dawn feature backing the umbrella is in `enabled`, add the
35
+ // umbrella name to `out`. Used by adapter.features / device.features so JS
36
+ // callers can see (and call .has on) the same name they pass in.
37
+ inline void
38
+ maybeSynthesizeRnNativeTextureFeature(
39
+ const std::unordered_set<wgpu::FeatureName> &enabled,
40
+ std::unordered_set<std::string> &out) {
41
+ auto backing = rnNativeTextureBackingFeatures();
42
+ if (backing.empty()) {
43
+ return;
44
+ }
45
+ for (auto f : backing) {
46
+ if (enabled.find(f) == enabled.end()) {
47
+ return;
48
+ }
49
+ }
50
+ out.insert(kRnNativeTextureFeature);
51
+ }
52
+
53
+ } // namespace rnwgpu
@@ -0,0 +1,76 @@
1
+ #pragma once
2
+
3
+ #include <functional>
4
+ #include <memory>
5
+ #include <string>
6
+ #include <utility>
7
+
8
+ #include "JSIConverter.h"
9
+ #include "NativeObject.h"
10
+ #include "PlatformContext.h"
11
+
12
+ namespace rnwgpu {
13
+
14
+ namespace jsi = facebook::jsi;
15
+
16
+ // VideoFrame is a small RAII wrapper around a platform-specific native handle
17
+ // (IOSurfaceRef-backed CVPixelBuffer on Apple, AHardwareBuffer on Android).
18
+ // It surfaces the raw handle as a BigInt to JS so callers can hand it to
19
+ // GPUDevice.importSharedTextureMemory, and owns a deleter so the underlying
20
+ // object stays alive until the JS object is GC'd (or release() is called).
21
+ class VideoFrame : public NativeObject<VideoFrame> {
22
+ public:
23
+ // JS-facing brand. installConstructor() exposes this as globalThis[CLASS_NAME]
24
+ // and keys the NativeObjectRegistry / worklet serializer off it, so it must
25
+ // NOT be "VideoFrame": that would install our constructor over the WebCodecs
26
+ // `VideoFrame` global and shadow it at runtime. "NativeVideoFrame" matches the
27
+ // public TypeScript type (see src/types.ts) and keeps the two namespaces
28
+ // distinct. The C++ class stays `VideoFrame` for brevity; only the brand is
29
+ // namespaced.
30
+ static constexpr const char *CLASS_NAME = "NativeVideoFrame";
31
+
32
+ explicit VideoFrame(VideoFrameHandle handle)
33
+ : NativeObject(CLASS_NAME), _handle(std::move(handle)) {}
34
+
35
+ ~VideoFrame() override { release(); }
36
+
37
+ std::string getBrand() { return CLASS_NAME; }
38
+
39
+ // The native handle (IOSurfaceRef / AHardwareBuffer*) as a uintptr_t value.
40
+ // Exposed as a BigInt on the JS side.
41
+ void *getHandle() { return _handle.handle; }
42
+ uint32_t getWidth() { return _handle.width; }
43
+ uint32_t getHeight() { return _handle.height; }
44
+
45
+ // Pixel format as a JS-visible string: "bgra8" | "nv12".
46
+ std::string getPixelFormat() {
47
+ return _handle.pixelFormat == VideoPixelFormat::NV12 ? "nv12" : "bgra8";
48
+ }
49
+
50
+ // Direct access to the underlying handle, for use by importExternalTexture /
51
+ // importSharedTextureMemory inside the C++ layer (not exposed to JS).
52
+ const VideoFrameHandle &handle() const { return _handle; }
53
+
54
+ void release() {
55
+ if (_handle.deleter) {
56
+ _handle.deleter();
57
+ _handle.deleter = nullptr;
58
+ }
59
+ _handle.handle = nullptr;
60
+ }
61
+
62
+ static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
63
+ installGetter(runtime, prototype, "__brand", &VideoFrame::getBrand);
64
+ installGetter(runtime, prototype, "handle", &VideoFrame::getHandle);
65
+ installGetter(runtime, prototype, "width", &VideoFrame::getWidth);
66
+ installGetter(runtime, prototype, "height", &VideoFrame::getHeight);
67
+ installGetter(runtime, prototype, "pixelFormat",
68
+ &VideoFrame::getPixelFormat);
69
+ installMethod(runtime, prototype, "release", &VideoFrame::release);
70
+ }
71
+
72
+ private:
73
+ VideoFrameHandle _handle;
74
+ };
75
+
76
+ } // namespace rnwgpu
@@ -0,0 +1,69 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <string>
5
+ #include <utility>
6
+ #include <variant>
7
+
8
+ #include "NativeObject.h"
9
+ #include "PlatformContext.h"
10
+ #include "VideoFrame.h"
11
+
12
+ namespace rnwgpu {
13
+
14
+ namespace jsi = facebook::jsi;
15
+
16
+ // JSI wrapper around a platform-specific IVideoPlayer. Hands out fresh
17
+ // VideoFrame handles each time the underlying decoder produces a new frame.
18
+ class VideoPlayer : public NativeObject<VideoPlayer> {
19
+ public:
20
+ static constexpr const char *CLASS_NAME = "VideoPlayer";
21
+
22
+ explicit VideoPlayer(std::unique_ptr<IVideoPlayer> impl)
23
+ : NativeObject(CLASS_NAME), _impl(std::move(impl)) {}
24
+
25
+ std::string getBrand() { return CLASS_NAME; }
26
+
27
+ // Returns the latest decoded frame, or null if no new frame is ready yet.
28
+ // Callers should poll this from their render loop and skip rendering (or
29
+ // reuse the last frame's texture) when null.
30
+ std::variant<std::nullptr_t, std::shared_ptr<VideoFrame>>
31
+ copyLatestFrame() {
32
+ if (!_impl) {
33
+ return nullptr;
34
+ }
35
+ auto handle = _impl->copyLatestFrame();
36
+ if (handle.handle == nullptr) {
37
+ return nullptr;
38
+ }
39
+ return std::make_shared<VideoFrame>(std::move(handle));
40
+ }
41
+
42
+ void play() {
43
+ if (_impl) {
44
+ _impl->play();
45
+ }
46
+ }
47
+
48
+ void pause() {
49
+ if (_impl) {
50
+ _impl->pause();
51
+ }
52
+ }
53
+
54
+ void release() { _impl.reset(); }
55
+
56
+ static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
57
+ installGetter(runtime, prototype, "__brand", &VideoPlayer::getBrand);
58
+ installMethod(runtime, prototype, "copyLatestFrame",
59
+ &VideoPlayer::copyLatestFrame);
60
+ installMethod(runtime, prototype, "play", &VideoPlayer::play);
61
+ installMethod(runtime, prototype, "pause", &VideoPlayer::pause);
62
+ installMethod(runtime, prototype, "release", &VideoPlayer::release);
63
+ }
64
+
65
+ private:
66
+ std::unique_ptr<IVideoPlayer> _impl;
67
+ };
68
+
69
+ } // namespace rnwgpu
@@ -21,7 +21,7 @@ struct GPUBindGroupEntry {
21
21
  std::shared_ptr<GPUSampler> sampler = nullptr;
22
22
  std::shared_ptr<GPUTextureView> textureView = nullptr;
23
23
  std::shared_ptr<GPUBufferBinding> buffer = nullptr;
24
- // external textures
24
+ std::shared_ptr<GPUExternalTexture> externalTexture = nullptr;
25
25
  };
26
26
 
27
27
  } // namespace rnwgpu
@@ -46,6 +46,9 @@ template <> struct JSIConverter<std::shared_ptr<rnwgpu::GPUBindGroupEntry>> {
46
46
  } else if (obj.hasNativeState<rnwgpu::GPUTextureView>(runtime)) {
47
47
  result->textureView =
48
48
  obj.getNativeState<rnwgpu::GPUTextureView>(runtime);
49
+ } else if (obj.hasNativeState<rnwgpu::GPUExternalTexture>(runtime)) {
50
+ result->externalTexture =
51
+ obj.getNativeState<rnwgpu::GPUExternalTexture>(runtime);
49
52
  } else if (obj.hasNativeState<rnwgpu::GPUBuffer>(runtime)) {
50
53
  // Support passing GPUBuffer directly as resource (auto-wrap in
51
54
  // GPUBufferBinding)