@shopify/react-native-skia 2.6.3 → 2.6.5

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 (107) hide show
  1. package/android/CMakeLists.txt +7 -4
  2. package/android/build.gradle +22 -3
  3. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +62 -0
  4. package/apple/RNSkApplePlatformContext.h +2 -0
  5. package/apple/RNSkApplePlatformContext.mm +71 -0
  6. package/apple/RNWebGPUAppleNativeBuffer.mm +33 -0
  7. package/cpp/api/JsiNativeBuffer.h +9 -1
  8. package/cpp/api/JsiSkAnimatedImageFactory.h +1 -1
  9. package/cpp/api/JsiSkApi.h +2 -2
  10. package/cpp/api/JsiSkCanvas.h +1 -1
  11. package/cpp/api/JsiSkDataFactory.h +1 -1
  12. package/cpp/api/JsiSkFont.h +1 -1
  13. package/cpp/api/JsiSkFontMgr.h +1 -1
  14. package/cpp/api/JsiSkHostObjects.h +3 -3
  15. package/cpp/api/JsiSkImage.h +2 -2
  16. package/cpp/api/JsiSkImageFactory.h +2 -2
  17. package/cpp/api/JsiSkPath.h +1 -1
  18. package/cpp/api/JsiSkPathFactory.h +1 -1
  19. package/cpp/api/JsiSkSurface.h +13 -5
  20. package/cpp/api/JsiSkTypeface.h +1 -1
  21. package/cpp/api/JsiSkTypefaceFontProvider.h +1 -1
  22. package/cpp/api/JsiSkiaContext.h +2 -2
  23. package/cpp/api/JsiTextureInfo.h +1 -1
  24. package/cpp/api/JsiVideo.h +2 -2
  25. package/cpp/api/recorder/Drawings.h +1 -1
  26. package/cpp/api/recorder/JsiRecorder.h +4 -4
  27. package/cpp/api/recorder/RNRecorder.h +1 -1
  28. package/cpp/jsi/JsiHostObject.cpp +5 -7
  29. package/cpp/jsi/ViewProperty.h +1 -1
  30. package/cpp/rnskia/RNDawnContext.h +13 -0
  31. package/cpp/rnskia/RNDawnUtils.h +11 -1
  32. package/cpp/rnskia/RNSkJsiViewApi.h +2 -2
  33. package/cpp/rnskia/RNSkManager.cpp +88 -2
  34. package/cpp/rnskia/RNSkPictureView.h +4 -4
  35. package/cpp/rnskia/RNSkPlatformContext.h +7 -0
  36. package/cpp/rnskia/RNSkView.h +9 -6
  37. package/cpp/rnwgpu/ArrayBuffer.h +51 -7
  38. package/cpp/rnwgpu/api/AppleNativeBuffer.h +22 -0
  39. package/cpp/rnwgpu/api/Convertors.h +33 -11
  40. package/cpp/rnwgpu/api/GPUAdapter.cpp +28 -2
  41. package/cpp/rnwgpu/api/GPUBuffer.h +1 -1
  42. package/cpp/rnwgpu/api/GPUDevice.cpp +29 -2
  43. package/cpp/rnwgpu/api/GPUDevice.h +6 -0
  44. package/cpp/rnwgpu/api/GPUExternalTexture.cpp +139 -0
  45. package/cpp/rnwgpu/api/GPUExternalTexture.h +52 -2
  46. package/cpp/rnwgpu/api/GPUQueue.cpp +50 -45
  47. package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +82 -0
  48. package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +70 -0
  49. package/cpp/rnwgpu/api/ImageBitmap.h +62 -0
  50. package/cpp/rnwgpu/api/NativeBufferUtils.h +87 -0
  51. package/cpp/rnwgpu/api/descriptors/GPUBindGroupEntry.h +4 -1
  52. package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +1 -1
  53. package/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h +56 -0
  54. package/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +10 -0
  55. package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +43 -24
  56. package/cpp/rnwgpu/api/descriptors/GPUImageCopyExternalImage.h +9 -9
  57. package/cpp/rnwgpu/api/descriptors/GPUImageCopyTexture.h +1 -1
  58. package/cpp/rnwgpu/api/descriptors/GPUImageCopyTextureTagged.h +2 -2
  59. package/cpp/rnwgpu/api/descriptors/GPUSharedTextureMemoryDescriptor.h +73 -0
  60. package/cpp/rnwgpu/api/descriptors/GPUTextureDescriptor.h +1 -1
  61. package/cpp/rnwgpu/api/descriptors/GPUUncapturedErrorEventInit.h +1 -1
  62. package/cpp/skia/include/core/SkRegion.h +10 -5
  63. package/cpp/skia/include/effects/SkRuntimeEffect.h +2 -2
  64. package/cpp/skia/include/gpu/graphite/Context.h +1 -1
  65. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +10 -1
  66. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  67. package/lib/commonjs/skia/types/WebGPU.d.ts +88 -0
  68. package/lib/commonjs/skia/types/WebGPU.js +6 -0
  69. package/lib/commonjs/skia/types/WebGPU.js.map +1 -0
  70. package/lib/commonjs/skia/types/index.d.ts +1 -0
  71. package/lib/commonjs/skia/types/index.js +11 -0
  72. package/lib/commonjs/skia/types/index.js.map +1 -1
  73. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +1 -0
  74. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js +19 -0
  75. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
  76. package/lib/commonjs/skia/web/JsiSkPath.js.map +1 -1
  77. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +10 -1
  78. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  79. package/lib/module/skia/types/WebGPU.d.ts +88 -0
  80. package/lib/module/skia/types/WebGPU.js +2 -0
  81. package/lib/module/skia/types/WebGPU.js.map +1 -0
  82. package/lib/module/skia/types/index.d.ts +1 -0
  83. package/lib/module/skia/types/index.js +1 -0
  84. package/lib/module/skia/types/index.js.map +1 -1
  85. package/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +1 -0
  86. package/lib/module/skia/web/JsiSkNativeBufferFactory.js +19 -0
  87. package/lib/module/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
  88. package/lib/module/skia/web/JsiSkPath.js.map +1 -1
  89. package/lib/module/web/LoadSkiaWeb.js +1 -2
  90. package/lib/module/web/LoadSkiaWeb.js.map +1 -1
  91. package/lib/typescript/lib/commonjs/skia/types/WebGPU.d.ts +1 -0
  92. package/lib/typescript/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +1 -0
  93. package/lib/typescript/lib/module/skia/types/WebGPU.d.ts +1 -0
  94. package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
  95. package/lib/typescript/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +1 -0
  96. package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +10 -1
  97. package/lib/typescript/src/skia/types/WebGPU.d.ts +88 -0
  98. package/lib/typescript/src/skia/types/index.d.ts +1 -0
  99. package/lib/typescript/src/skia/web/JsiSkNativeBufferFactory.d.ts +1 -0
  100. package/package.json +12 -10
  101. package/react-native-skia.podspec +59 -7
  102. package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +10 -1
  103. package/src/skia/types/WebGPU.ts +108 -0
  104. package/src/skia/types/index.ts +1 -0
  105. package/src/skia/web/JsiSkNativeBufferFactory.ts +20 -0
  106. package/src/skia/web/JsiSkPath.ts +8 -2
  107. package/scripts/install-libs.js +0 -133
@@ -5,7 +5,7 @@
5
5
  #include "dawn/dawn_proc.h"
6
6
  #include "dawn/native/DawnNative.h"
7
7
 
8
- #include "RNSkLog.h"
8
+ #include "utils/RNSkLog.h"
9
9
  #include "include/core/SkColorType.h"
10
10
  #include "include/gpu/graphite/dawn/DawnBackendContext.h"
11
11
 
@@ -206,12 +206,22 @@ createDawnBackendContext(dawn::native::Instance *instance) {
206
206
  wgpu::FeatureName::ImplicitDeviceSynchronization,
207
207
  #ifdef __APPLE__
208
208
  wgpu::FeatureName::SharedTextureMemoryIOSurface,
209
+ // Required to call SharedTextureMemory::EndAccess on Metal (it exports a
210
+ // MTLSharedEvent fence). importExternalTexture / importSharedTextureMemory
211
+ // end the access window after submit; without this EndAccess errors with
212
+ // "Required feature (SharedFenceMTLSharedEvent) is missing". Safe here
213
+ // because we always queue.submit() before EndAccess (the secondary device
214
+ // omits it on purpose — its camera path doesn't commit first).
215
+ wgpu::FeatureName::SharedFenceMTLSharedEvent,
209
216
  wgpu::FeatureName::DawnMultiPlanarFormats,
210
217
  wgpu::FeatureName::MultiPlanarFormatP010,
211
218
  wgpu::FeatureName::MultiPlanarFormatP210,
212
219
  wgpu::FeatureName::MultiPlanarFormatExtendedUsages,
213
220
  #else
214
221
  wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer,
222
+ // Vulkan equivalent of the above: EndAccess exports a sync-fd fence.
223
+ wgpu::FeatureName::SharedFenceSyncFD,
224
+ wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD,
215
225
  #endif
216
226
  };
217
227
 
@@ -9,11 +9,11 @@
9
9
  #include <utility>
10
10
  #include <vector>
11
11
 
12
- #include "JsiHostObject.h"
12
+ #include "jsi/JsiHostObject.h"
13
13
  #include "RNSkPictureView.h"
14
14
  #include "RNSkPlatformContext.h"
15
15
  #include "RNSkView.h"
16
- #include "ViewProperty.h"
16
+ #include "jsi/ViewProperty.h"
17
17
  #include <jsi/jsi.h>
18
18
 
19
19
  namespace RNSkia {
@@ -5,22 +5,29 @@
5
5
 
6
6
  #include <jsi/jsi.h>
7
7
 
8
- #include "JsiSkApi.h"
8
+ #include "api/JsiSkApi.h"
9
9
  #include "RNSkJsiViewApi.h"
10
10
  #include "RNSkView.h"
11
11
 
12
- #include "RuntimeAwareCache.h"
12
+ #include "jsi/RuntimeAwareCache.h"
13
13
 
14
14
  #ifdef SK_GRAPHITE
15
15
  #include "RNDawnContext.h"
16
+ #include "rnwgpu/ArrayBuffer.h"
16
17
  #include "rnwgpu/api/GPU.h"
17
18
  #include "rnwgpu/api/GPUUncapturedErrorEvent.h"
19
+ #include "rnwgpu/api/ImageBitmap.h"
18
20
  #include "rnwgpu/api/RNWebGPU.h"
19
21
  #include "rnwgpu/api/descriptors/GPUBufferUsage.h"
20
22
  #include "rnwgpu/api/descriptors/GPUColorWrite.h"
21
23
  #include "rnwgpu/api/descriptors/GPUMapMode.h"
22
24
  #include "rnwgpu/api/descriptors/GPUShaderStage.h"
23
25
  #include "rnwgpu/api/descriptors/GPUTextureUsage.h"
26
+ #include "jsi2/Promise.h"
27
+
28
+ #include "include/core/SkData.h"
29
+ #include "include/core/SkImage.h"
30
+ #include "include/core/SkImageInfo.h"
24
31
  #endif
25
32
 
26
33
  namespace RNSkia {
@@ -114,6 +121,85 @@ void RNSkManager::installBindings() {
114
121
  auto rnWebGPU = std::make_shared<rnwgpu::RNWebGPU>(gpu, nullptr);
115
122
  _jsRuntime->global().setProperty(
116
123
  *_jsRuntime, "RNWebGPU", rnwgpu::RNWebGPU::create(*_jsRuntime, rnWebGPU));
124
+
125
+ // DRAFT — compile-unverified. Install the ImageBitmap constructor (so
126
+ // `instanceof ImageBitmap` works) and a global createImageBitmap() that
127
+ // accepts the non-standard encoded-BufferSource overload.
128
+ //
129
+ // The BufferSource is run through the shared rnwgpu::ArrayBuffer converter,
130
+ // which validates byteOffset/byteLength against the backing buffer and throws
131
+ // synchronously on a spoofed / out-of-bounds view — so createImageBitmap()
132
+ // rejects rather than reading out of bounds (see ArrayBufferBounds /
133
+ // ImageBitmapBounds specs). Decoding uses Skia's own codec; no platform image
134
+ // decoder is needed.
135
+ rnwgpu::ImageBitmap::installConstructor(*_jsRuntime);
136
+ _jsRuntime->global().setProperty(
137
+ *_jsRuntime, "createImageBitmap",
138
+ jsi::Function::createFromHostFunction(
139
+ *_jsRuntime,
140
+ jsi::PropNameID::forAscii(*_jsRuntime, "createImageBitmap"), 1,
141
+ [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/,
142
+ const jsi::Value *args, size_t count) -> jsi::Value {
143
+ if (count < 1 || !args[0].isObject()) {
144
+ throw jsi::JSError(
145
+ rt, "createImageBitmap requires a BufferSource argument");
146
+ }
147
+ // Only the encoded ArrayBuffer / ArrayBufferView overload is
148
+ // supported here. Anything else (Blob, ImageData, …) is rejected.
149
+ auto obj = args[0].getObject(rt);
150
+ bool isBufferSource = obj.isArrayBuffer(rt);
151
+ if (!isBufferSource && obj.hasProperty(rt, "buffer")) {
152
+ auto bufferProp = obj.getProperty(rt, "buffer");
153
+ isBufferSource =
154
+ bufferProp.isObject() &&
155
+ bufferProp.getObject(rt).isArrayBuffer(rt);
156
+ }
157
+ if (!isBufferSource) {
158
+ throw jsi::JSError(rt, "createImageBitmap: unsupported source "
159
+ "(expected an ArrayBuffer or TypedArray "
160
+ "of encoded image bytes)");
161
+ }
162
+ // Validates bounds and THROWS synchronously on a spoofed view, so
163
+ // the bad pointer never reaches the copy below.
164
+ auto buffer =
165
+ rnwgpu::JSIConverter<std::shared_ptr<rnwgpu::ArrayBuffer>>::
166
+ fromJSI(rt, args[0], false);
167
+ // Copy the encoded bytes off the JS-owned ArrayBuffer.
168
+ const uint8_t *bytes = buffer->data();
169
+ std::vector<uint8_t> encoded(bytes, bytes + buffer->size());
170
+
171
+ return rnwgpu::Promise::createPromise(
172
+ rt, [encoded = std::move(encoded)](
173
+ jsi::Runtime &runtime,
174
+ std::shared_ptr<rnwgpu::Promise> promise) mutable {
175
+ auto skData =
176
+ SkData::MakeWithCopy(encoded.data(), encoded.size());
177
+ auto image = SkImages::DeferredFromEncodedData(skData);
178
+ if (image == nullptr) {
179
+ promise->reject(
180
+ "createImageBitmap: failed to decode image data");
181
+ return;
182
+ }
183
+ const int w = image->width();
184
+ const int h = image->height();
185
+ auto info =
186
+ SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
187
+ kUnpremul_SkAlphaType);
188
+ std::vector<uint8_t> pixels(info.computeMinByteSize());
189
+ // nullptr context: decode/read on the CPU (raster).
190
+ if (!image->readPixels(nullptr, info, pixels.data(),
191
+ info.minRowBytes(), 0, 0)) {
192
+ promise->reject(
193
+ "createImageBitmap: failed to read decoded pixels");
194
+ return;
195
+ }
196
+ auto bitmap = std::make_shared<rnwgpu::ImageBitmap>(
197
+ std::move(pixels), static_cast<size_t>(w),
198
+ static_cast<size_t>(h));
199
+ promise->resolve(
200
+ rnwgpu::ImageBitmap::create(runtime, bitmap));
201
+ });
202
+ }));
117
203
  #endif
118
204
  }
119
205
  } // namespace RNSkia
@@ -11,12 +11,12 @@
11
11
  #include <jsi/jsi.h>
12
12
 
13
13
  #include "RNSkView.h"
14
- #include "ViewProperty.h"
14
+ #include "jsi/ViewProperty.h"
15
15
 
16
- #include "JsiSkPicture.h"
17
- #include "RNSkLog.h"
16
+ #include "api/JsiSkPicture.h"
17
+ #include "utils/RNSkLog.h"
18
18
  #include "RNSkPlatformContext.h"
19
- #include "RNSkTimingInfo.h"
19
+ #include "utils/RNSkTimingInfo.h"
20
20
 
21
21
  #pragma clang diagnostic push
22
22
  #pragma clang diagnostic ignored "-Wdocumentation"
@@ -141,6 +141,13 @@ public:
141
141
 
142
142
  virtual uint64_t makeNativeBuffer(sk_sp<SkImage> image) = 0;
143
143
 
144
+ // Allocate a platform native buffer (IOSurface on Apple, AHardwareBuffer on
145
+ // Android) of the given size filled with a procedural test pattern (RGB
146
+ // gradient + diagonal stripes), entirely on the CPU. Intended for examples
147
+ // and tests that need a buffer to feed into importExternalTexture without a
148
+ // camera/video source. Release it with releaseNativeBuffer().
149
+ virtual uint64_t makeTestNativeBuffer(int width, int height) = 0;
150
+
144
151
  virtual std::shared_ptr<RNSkVideo> createVideo(const std::string &url) = 0;
145
152
 
146
153
  /**
@@ -7,11 +7,11 @@
7
7
  #include <vector>
8
8
 
9
9
  #include "RNSkPlatformContext.h"
10
- #include "ViewProperty.h"
10
+ #include "jsi/ViewProperty.h"
11
11
 
12
- #include "JsiSkImage.h"
13
- #include "JsiSkPoint.h"
14
- #include "JsiSkRect.h"
12
+ #include "api/JsiSkImage.h"
13
+ #include "api/JsiSkPoint.h"
14
+ #include "api/JsiSkRect.h"
15
15
 
16
16
  #pragma clang diagnostic push
17
17
  #pragma clang diagnostic ignored "-Wdocumentation"
@@ -94,8 +94,11 @@ public:
94
94
  image = _surface->makeImageSnapshot();
95
95
  }
96
96
  #if defined(SK_GRAPHITE)
97
- DawnContext::getInstance().submitRecording(
98
- _surface->recorder()->snap().get());
97
+ // Only Graphite-backed surfaces have a recorder to snap/submit; a raster
98
+ // surface's snapshot is already a valid CPU image.
99
+ if (auto *recorder = _surface->recorder()) {
100
+ DawnContext::getInstance().submitRecording(recorder->snap().get());
101
+ }
99
102
  return DawnContext::getInstance().MakeRasterImage(image);
100
103
  #else
101
104
  auto grContext = _context->getDirectContext();
@@ -1,6 +1,8 @@
1
1
  #pragma once
2
2
  #include <jsi/jsi.h>
3
3
 
4
+ #include <cmath>
5
+ #include <cstdint>
4
6
  #include <memory>
5
7
 
6
8
  #include "jsi2/JSIConverter.h"
@@ -47,16 +49,58 @@ template <> struct JSIConverter<std::shared_ptr<ArrayBuffer>> {
47
49
  if (bufferProp.isObject() &&
48
50
  bufferProp.getObject(runtime).isArrayBuffer(runtime)) {
49
51
  auto buff = bufferProp.getObject(runtime);
50
- auto bytesPerElements =
51
- obj.getProperty(runtime, "BYTES_PER_ELEMENT").asNumber();
52
52
  auto arrayBuffer = buff.getArrayBuffer(runtime);
53
- auto byteOffset = static_cast<size_t>(
54
- obj.getProperty(runtime, "byteOffset").asNumber());
55
- auto byteLength = static_cast<size_t>(
56
- obj.getProperty(runtime, "byteLength").asNumber());
53
+ const size_t bufferSize = arrayBuffer.size(runtime);
54
+
55
+ // byteOffset / byteLength are user-readable JS properties, not values
56
+ // the engine guarantees (unlike Dawn's node binding, which reads them
57
+ // off the engine's typed-array view). Read them as doubles so we can
58
+ // reject negative, non-integral, NaN/Inf, or oversized values before
59
+ // they wrap around when cast to size_t.
60
+ const double byteOffsetValue =
61
+ obj.getProperty(runtime, "byteOffset").asNumber();
62
+ const double byteLengthValue =
63
+ obj.getProperty(runtime, "byteLength").asNumber();
64
+
65
+ auto isValidByteIndex = [](double value) {
66
+ return std::isfinite(value) && value >= 0.0 &&
67
+ value <= static_cast<double>(SIZE_MAX) &&
68
+ std::floor(value) == value;
69
+ };
70
+ if (!isValidByteIndex(byteOffsetValue) ||
71
+ !isValidByteIndex(byteLengthValue)) {
72
+ throw std::runtime_error(
73
+ "ArrayBuffer::fromJSI: invalid byteOffset/byteLength");
74
+ }
75
+
76
+ const size_t byteOffset = static_cast<size_t>(byteOffsetValue);
77
+ const size_t byteLength = static_cast<size_t>(byteLengthValue);
78
+
79
+ // Overflow-safe bounds check: byteOffset + byteLength <= bufferSize.
80
+ if (byteOffset > bufferSize ||
81
+ byteLength > bufferSize - byteOffset) {
82
+ throw std::runtime_error(
83
+ "ArrayBuffer::fromJSI: view bounds [byteOffset, byteOffset + "
84
+ "byteLength) exceed the backing ArrayBuffer size");
85
+ }
86
+
87
+ // BYTES_PER_ELEMENT is absent on a DataView; default to 1. A spoofed
88
+ // object could report 0 (or a negative/NaN value), so clamp to a
89
+ // minimum of 1 to avoid a later division by zero in writeBuffer.
90
+ size_t bytesPerElements = 1;
91
+ if (obj.hasProperty(runtime, "BYTES_PER_ELEMENT")) {
92
+ auto bpe = obj.getProperty(runtime, "BYTES_PER_ELEMENT");
93
+ if (bpe.isNumber()) {
94
+ const double value = bpe.asNumber();
95
+ if (std::isfinite(value) && value >= 1.0) {
96
+ bytesPerElements = static_cast<size_t>(value);
97
+ }
98
+ }
99
+ }
100
+
57
101
  return std::make_shared<ArrayBuffer>(
58
102
  arrayBuffer.data(runtime) + byteOffset, byteLength,
59
- static_cast<size_t>(bytesPerElements));
103
+ bytesPerElements);
60
104
  }
61
105
  }
62
106
  }
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+
3
+ #if defined(__APPLE__)
4
+
5
+ #include <cstdint>
6
+
7
+ namespace rnwgpu {
8
+
9
+ // Extract the backing IOSurface and dimensions from a CVPixelBufferRef pointer
10
+ // (the value Skia's NativeBuffer.MakeFromImage returns on Apple). The returned
11
+ // IOSurfaceRef is owned by the CVPixelBuffer; the caller must keep the
12
+ // CVPixelBuffer alive while the IOSurface is in use. Returns nullptr (and
13
+ // leaves the out-params at 0) if the buffer has no IOSurface.
14
+ //
15
+ // Defined in apple/RNWebGPUAppleNativeBuffer.mm (Objective-C++ so it can use
16
+ // CoreVideo, which isn't available from the cpp/ translation units).
17
+ void *GetIOSurfaceFromNativeBuffer(void *cvPixelBuffer, uint32_t *outWidth,
18
+ uint32_t *outHeight);
19
+
20
+ } // namespace rnwgpu
21
+
22
+ #endif // __APPLE__
@@ -252,13 +252,24 @@ public:
252
252
 
253
253
  [[nodiscard]] bool Convert(wgpu::BindGroupLayoutEntry &out,
254
254
  const GPUBindGroupLayoutEntry &in) {
255
- return Convert(out.binding, in.binding) &&
256
- Convert(out.visibility, in.visibility) &&
257
- Convert(out.buffer, in.buffer) && Convert(out.sampler, in.sampler) &&
258
- Convert(out.texture, in.texture) &&
259
- Convert(out.storageTexture, in.storageTexture);
260
- // no external textures here
261
- //&& Convert(out.externalTexture, in.externalTexture);
255
+ out = {};
256
+ if (!Convert(out.binding, in.binding) ||
257
+ !Convert(out.visibility, in.visibility) ||
258
+ !Convert(out.buffer, in.buffer) || !Convert(out.sampler, in.sampler) ||
259
+ !Convert(out.texture, in.texture) ||
260
+ !Convert(out.storageTexture, in.storageTexture)) {
261
+ return false;
262
+ }
263
+ if (in.externalTexture.has_value() &&
264
+ in.externalTexture.value() != nullptr) {
265
+ // External texture layouts bind via a chained struct rather than a
266
+ // direct field on BindGroupLayoutEntry. The chained struct must outlive
267
+ // the BindGroupLayoutEntry until Device::CreateBindGroupLayout returns,
268
+ // so we allocate it on the Convertor's arena.
269
+ auto *chain = Allocate<wgpu::ExternalTextureBindingLayout>();
270
+ out.nextInChain = chain;
271
+ }
272
+ return true;
262
273
  }
263
274
 
264
275
  [[nodiscard]] bool Convert(wgpu::BlendComponent &out,
@@ -422,9 +433,11 @@ public:
422
433
  }
423
434
 
424
435
  [[nodiscard]] bool Convert(wgpu::ExternalTextureBindingLayout &out,
425
- const GPUExternalTextureBindingLayout &in) {
426
- // no external textures at the moment
427
- return false;
436
+ const GPUExternalTextureBindingLayout & /*in*/) {
437
+ // ExternalTextureBindingLayout carries no fields of its own; its presence
438
+ // (as a chained struct) is what marks the entry as an external texture.
439
+ out = {};
440
+ return true;
428
441
  }
429
442
 
430
443
  [[nodiscard]] bool Convert(wgpu::ConstantEntry &out, const std::string &key,
@@ -729,7 +742,16 @@ public:
729
742
  out.buffer = buffer->get();
730
743
  return true;
731
744
  }
732
- // Not external textures at the moment
745
+ if (in.externalTexture != nullptr) {
746
+ // External textures bind via a chained struct rather than a direct field
747
+ // on BindGroupEntry. The chained struct must outlive the BindGroupEntry
748
+ // until Device::CreateBindGroup returns, so we allocate it on the
749
+ // Convertor's arena.
750
+ auto *chain = Allocate<wgpu::ExternalTextureBindingEntry>();
751
+ chain->externalTexture = in.externalTexture->get();
752
+ out.nextInChain = chain;
753
+ return true;
754
+ }
733
755
  return false;
734
756
  }
735
757
 
@@ -109,9 +109,35 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
109
109
  deviceLostBinding,
110
110
  creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve,
111
111
  const async::AsyncTaskHandle::RejectFunction &reject) {
112
- (void)descriptor;
112
+ // Build a local mutable copy so we can chain Dawn's device toggles.
113
+ // The toggle name strings are owned by `descriptor` (captured above),
114
+ // and the const char* / DawnTogglesDescriptor locals live for the
115
+ // whole synchronous RequestDevice call below, which is when Dawn reads
116
+ // the chained struct.
117
+ wgpu::DeviceDescriptor deviceDesc = aDescriptor;
118
+ wgpu::DawnTogglesDescriptor toggles{};
119
+ std::vector<const char *> enabledToggles;
120
+ std::vector<const char *> disabledToggles;
121
+ if (descriptor.has_value() && descriptor.value()->dawnToggles) {
122
+ const auto &dawnToggles = descriptor.value()->dawnToggles.value();
123
+ if (dawnToggles->enabledToggles) {
124
+ for (const auto &t : dawnToggles->enabledToggles.value()) {
125
+ enabledToggles.push_back(t.c_str());
126
+ }
127
+ toggles.enabledToggleCount = enabledToggles.size();
128
+ toggles.enabledToggles = enabledToggles.data();
129
+ }
130
+ if (dawnToggles->disabledToggles) {
131
+ for (const auto &t : dawnToggles->disabledToggles.value()) {
132
+ disabledToggles.push_back(t.c_str());
133
+ }
134
+ toggles.disabledToggleCount = disabledToggles.size();
135
+ toggles.disabledToggles = disabledToggles.data();
136
+ }
137
+ deviceDesc.nextInChain = &toggles;
138
+ }
113
139
  _instance.RequestDevice(
114
- &aDescriptor, wgpu::CallbackMode::AllowProcessEvents,
140
+ &deviceDesc, wgpu::CallbackMode::AllowProcessEvents,
115
141
  [asyncRunner = _async, resolve, reject, label, creationRuntime,
116
142
  deviceLostBinding](wgpu::RequestDeviceStatus status,
117
143
  wgpu::Device device,
@@ -14,7 +14,7 @@
14
14
 
15
15
  #include "webgpu/webgpu_cpp.h"
16
16
 
17
- #include "ArrayBuffer.h"
17
+ #include "rnwgpu/ArrayBuffer.h"
18
18
 
19
19
  namespace rnwgpu {
20
20
 
@@ -7,6 +7,7 @@
7
7
  #include <vector>
8
8
 
9
9
  #include "Convertors.h"
10
+ #include "NativeBufferUtils.h"
10
11
  #include "jsi2/JSIConverter.h"
11
12
 
12
13
  #include "GPUFeatures.h"
@@ -234,8 +235,34 @@ std::shared_ptr<GPUPipelineLayout> GPUDevice::createPipelineLayout(
234
235
 
235
236
  std::shared_ptr<GPUExternalTexture> GPUDevice::importExternalTexture(
236
237
  std::shared_ptr<GPUExternalTextureDescriptor> descriptor) {
237
- throw std::runtime_error(
238
- "GPUDevice::importExternalTexture(): Not implemented");
238
+ // The import / begin-access / descriptor-build logic, plus the matching
239
+ // EndAccess, all live on GPUExternalTexture so the begin/end lifecycle stays
240
+ // in one translation unit (see GPUExternalTexture.cpp).
241
+ return GPUExternalTexture::Create(_instance, std::move(descriptor));
242
+ }
243
+
244
+ std::shared_ptr<GPUSharedTextureMemory> GPUDevice::importSharedTextureMemory(
245
+ std::shared_ptr<GPUSharedTextureMemoryDescriptor> descriptor) {
246
+ if (!descriptor || descriptor->handle == 0) {
247
+ throw std::runtime_error(
248
+ "GPUDevice::importSharedTextureMemory(): handle must be a non-null "
249
+ "native buffer pointer (from Skia.NativeBuffer.MakeFromImage)");
250
+ }
251
+ void *bufferPtr =
252
+ reinterpret_cast<void *>(static_cast<uintptr_t>(descriptor->handle));
253
+ std::string label = descriptor->label.value_or("");
254
+
255
+ auto memory = importNativeBufferAsSharedTextureMemory(
256
+ _instance, bufferPtr, label, /*outWidth=*/nullptr, /*outHeight=*/nullptr);
257
+ if (memory == nullptr) {
258
+ throw std::runtime_error(
259
+ "GPUDevice::importSharedTextureMemory(): ImportSharedTextureMemory "
260
+ "returned null - is the 'shared-texture-memory-iosurface' (Apple) or "
261
+ "'shared-texture-memory-ahardware-buffer' (Android) feature enabled on "
262
+ "the device?");
263
+ }
264
+ return std::make_shared<GPUSharedTextureMemory>(std::move(memory),
265
+ std::move(label));
239
266
  }
240
267
 
241
268
  async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
@@ -37,6 +37,7 @@
37
37
  #include "GPURenderPipeline.h"
38
38
  #include "GPUSampler.h"
39
39
  #include "GPUShaderModule.h"
40
+ #include "GPUSharedTextureMemory.h"
40
41
  #include "GPUSupportedLimits.h"
41
42
  #include "GPUTexture.h"
42
43
  #include "descriptors/GPUBindGroupDescriptor.h"
@@ -51,6 +52,7 @@
51
52
  #include "descriptors/GPURenderPipelineDescriptor.h"
52
53
  #include "descriptors/GPUSamplerDescriptor.h"
53
54
  #include "descriptors/GPUShaderModuleDescriptor.h"
55
+ #include "descriptors/GPUSharedTextureMemoryDescriptor.h"
54
56
  #include "descriptors/GPUTextureDescriptor.h"
55
57
 
56
58
  namespace rnwgpu {
@@ -120,6 +122,8 @@ public:
120
122
  std::optional<std::shared_ptr<GPUSamplerDescriptor>> descriptor);
121
123
  std::shared_ptr<GPUExternalTexture> importExternalTexture(
122
124
  std::shared_ptr<GPUExternalTextureDescriptor> descriptor);
125
+ std::shared_ptr<GPUSharedTextureMemory> importSharedTextureMemory(
126
+ std::shared_ptr<GPUSharedTextureMemoryDescriptor> descriptor);
123
127
  std::shared_ptr<GPUBindGroupLayout> createBindGroupLayout(
124
128
  std::shared_ptr<GPUBindGroupLayoutDescriptor> descriptor);
125
129
  std::shared_ptr<GPUPipelineLayout>
@@ -173,6 +177,8 @@ public:
173
177
  &GPUDevice::createSampler);
174
178
  installMethod(runtime, prototype, "importExternalTexture",
175
179
  &GPUDevice::importExternalTexture);
180
+ installMethod(runtime, prototype, "importSharedTextureMemory",
181
+ &GPUDevice::importSharedTextureMemory);
176
182
  installMethod(runtime, prototype, "createBindGroupLayout",
177
183
  &GPUDevice::createBindGroupLayout);
178
184
  installMethod(runtime, prototype, "createPipelineLayout",
@@ -0,0 +1,139 @@
1
+ #include "GPUExternalTexture.h"
2
+
3
+ #include <cmath>
4
+ #include <memory>
5
+ #include <string>
6
+ #include <utility>
7
+
8
+ #include "NativeBufferUtils.h"
9
+ #include "descriptors/GPUExternalTextureDescriptor.h"
10
+
11
+ namespace rnwgpu {
12
+
13
+ // Identity gamut (same primaries) as a 3x3 column-major matrix.
14
+ static const float kIdentityGamutMatrix[9] = {
15
+ 1.0f, 0.0f, 0.0f, //
16
+ 0.0f, 1.0f, 0.0f, //
17
+ 0.0f, 0.0f, 1.0f, //
18
+ };
19
+
20
+ // Identity transfer (y = x). The native buffers produced by Skia's NativeBuffer
21
+ // API are single-plane BGRA/RGBA already in the render target's color space, so
22
+ // no conversion is wanted. Dawn dereferences the transfer-function arrays
23
+ // unconditionally (ComputeExternalTextureParams), so these must be non-null.
24
+ static const float kIdentityTransferParams[7] = {
25
+ 1.0f, // G
26
+ 1.0f, // A
27
+ 0.0f, // B
28
+ 0.0f, // C
29
+ 0.0f, // D
30
+ 0.0f, // E
31
+ 0.0f, // F
32
+ };
33
+
34
+ // Map a rotation in degrees (0 / 90 / 180 / 270) to Dawn's enum. Anything that
35
+ // isn't a clean multiple of 90 snaps to the nearest quadrant; Dawn only
36
+ // supports those four steps for external textures.
37
+ static wgpu::ExternalTextureRotation toExternalTextureRotation(double degrees) {
38
+ int quadrant = static_cast<int>(std::lround(degrees / 90.0));
39
+ quadrant = ((quadrant % 4) + 4) % 4;
40
+ switch (quadrant) {
41
+ case 1:
42
+ return wgpu::ExternalTextureRotation::Rotate90Degrees;
43
+ case 2:
44
+ return wgpu::ExternalTextureRotation::Rotate180Degrees;
45
+ case 3:
46
+ return wgpu::ExternalTextureRotation::Rotate270Degrees;
47
+ default:
48
+ return wgpu::ExternalTextureRotation::Rotate0Degrees;
49
+ }
50
+ }
51
+
52
+ std::shared_ptr<GPUExternalTexture> GPUExternalTexture::Create(
53
+ wgpu::Device device,
54
+ std::shared_ptr<GPUExternalTextureDescriptor> descriptor) {
55
+ if (!descriptor || descriptor->source == 0) {
56
+ throw std::runtime_error(
57
+ "GPUExternalTexture::Create(): descriptor.source (a native buffer "
58
+ "pointer from Skia.NativeBuffer.MakeFromImage) is required");
59
+ }
60
+ void *bufferPtr =
61
+ reinterpret_cast<void *>(static_cast<uintptr_t>(descriptor->source));
62
+ std::string label = descriptor->label.value_or("external-texture");
63
+
64
+ // 1. Import the native buffer as SharedTextureMemory and read its dimensions.
65
+ uint32_t width = 0;
66
+ uint32_t height = 0;
67
+ wgpu::SharedTextureMemory memory = importNativeBufferAsSharedTextureMemory(
68
+ device, bufferPtr, label, &width, &height);
69
+ if (memory == nullptr) {
70
+ throw std::runtime_error(
71
+ "GPUExternalTexture::Create(): ImportSharedTextureMemory returned "
72
+ "null");
73
+ }
74
+
75
+ // 2. Create the texture from the surface (Dawn picks the single-plane
76
+ // BGRA/RGBA format).
77
+ auto texture = memory.CreateTexture();
78
+ if (texture == nullptr) {
79
+ throw std::runtime_error(
80
+ "GPUExternalTexture::Create(): CreateTexture returned null");
81
+ }
82
+
83
+ // 3. Begin access. The matching EndAccess runs when the GPUExternalTexture is
84
+ // destroyed (explicitly via destroy() or at GC).
85
+ wgpu::SharedTextureMemoryBeginAccessDescriptor begin{};
86
+ begin.initialized = true;
87
+ begin.concurrentRead = false;
88
+ #if defined(__ANDROID__)
89
+ // Dawn's Vulkan backend requires the acquired VkImageLayout to be chained.
90
+ // UNDEFINED (= 0) on both ends is the canonical "no prior GPU producer"
91
+ // pattern (matches GPUSharedTextureMemory::beginAccess).
92
+ wgpu::SharedTextureMemoryVkImageLayoutBeginState vkBegin{};
93
+ vkBegin.oldLayout = 0;
94
+ vkBegin.newLayout = 0;
95
+ begin.nextInChain = &vkBegin;
96
+ #endif
97
+ if (!memory.BeginAccess(texture, &begin)) {
98
+ throw std::runtime_error(
99
+ "GPUExternalTexture::Create(): BeginAccess failed");
100
+ }
101
+
102
+ // 4. Single-plane view (the whole BGRA/RGBA surface).
103
+ auto plane0 = texture.CreateView();
104
+
105
+ // 5. Build the ExternalTextureDescriptor. The surface is already RGB in the
106
+ // target color space, so pass it through with identity transfer/gamut.
107
+ wgpu::ExternalTextureDescriptor extDesc{};
108
+ if (!label.empty()) {
109
+ extDesc.label = wgpu::StringView(label.c_str(), label.size());
110
+ }
111
+ extDesc.plane0 = plane0;
112
+ extDesc.gamutConversionMatrix = kIdentityGamutMatrix;
113
+ extDesc.srcTransferFunctionParameters = kIdentityTransferParams;
114
+ extDesc.dstTransferFunctionParameters = kIdentityTransferParams;
115
+ extDesc.cropOrigin = {0, 0};
116
+ extDesc.cropSize = {width, height};
117
+ extDesc.apparentSize = {width, height};
118
+ extDesc.mirrored = descriptor->mirrored.value_or(false);
119
+ extDesc.rotation =
120
+ toExternalTextureRotation(descriptor->rotation.value_or(0));
121
+
122
+ auto external = device.CreateExternalTexture(&extDesc);
123
+ if (external == nullptr) {
124
+ wgpu::SharedTextureMemoryEndAccessState state{};
125
+ #if defined(__ANDROID__)
126
+ wgpu::SharedTextureMemoryVkImageLayoutEndState vkEnd{};
127
+ state.nextInChain = &vkEnd;
128
+ #endif
129
+ (void)memory.EndAccess(texture, &state);
130
+ throw std::runtime_error(
131
+ "GPUExternalTexture::Create(): CreateExternalTexture returned null");
132
+ }
133
+
134
+ return std::make_shared<GPUExternalTexture>(
135
+ std::move(external), std::move(memory), std::move(texture),
136
+ std::move(label));
137
+ }
138
+
139
+ } // namespace rnwgpu