react-native-webgpu 0.14.1 → 0.15.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.
- package/android/cpp/AndroidColorSpace.h +107 -0
- package/android/cpp/AndroidPlatformContext.h +100 -9
- package/android/cpp/AndroidVideoPlayer.h +46 -1
- package/android/cpp/cpp-adapter.cpp +53 -1
- package/android/src/main/java/com/webgpu/SyncFences.java +88 -0
- package/android/src/main/java/com/webgpu/ViewSnapshot.java +66 -23
- package/android/src/main/java/com/webgpu/WebGPUModule.java +69 -5
- package/android/src/main/java/com/webgpu/WebGPUVideoPlayer.java +109 -0
- package/apple/ApplePlatformContext.h +5 -5
- package/apple/ApplePlatformContext.mm +75 -3
- package/apple/AppleVideoPlayer.mm +68 -35
- package/cpp/dawn/common/ExternalTextureParams.h +67 -0
- package/cpp/rnwgpu/PlatformContext.h +65 -16
- package/cpp/rnwgpu/SurfaceRegistry.h +39 -5
- package/cpp/rnwgpu/api/GPUAdapter.cpp +35 -0
- package/cpp/rnwgpu/api/GPUExternalTexture.cpp +172 -156
- package/cpp/rnwgpu/api/GPUExternalTexture.h +2 -19
- package/cpp/rnwgpu/api/GPUQueue.cpp +98 -3
- package/cpp/rnwgpu/api/RNWebGPU.h +12 -3
- package/cpp/rnwgpu/api/VideoFrameBlit.h +39 -24
- package/cpp/rnwgpu/api/descriptors/GPUExternalTextureDescriptor.h +10 -3
- package/lib/commonjs/Canvas.js +36 -6
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/WebPolyfillGPUModule.js +6 -3
- package/lib/commonjs/WebPolyfillGPUModule.js.map +1 -1
- package/lib/commonjs/constants.js +6 -3
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/index.js +1 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/install.js +9 -22
- package/lib/commonjs/install.js.map +1 -1
- package/lib/commonjs/main/index.js +8 -7
- package/lib/commonjs/main/index.js.map +1 -1
- package/lib/commonjs/main/index.web.js +1 -1
- package/lib/commonjs/main/installNative.js +20 -0
- package/lib/commonjs/main/installNative.js.map +1 -0
- package/lib/commonjs/main/installNative.web.js +4 -0
- package/lib/commonjs/main/installNative.web.js.map +1 -0
- package/lib/commonjs/mock.js +60 -13
- package/lib/commonjs/mock.js.map +1 -1
- package/lib/module/Canvas.js +37 -7
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/WebPolyfillGPUModule.js +6 -3
- package/lib/module/WebPolyfillGPUModule.js.map +1 -1
- package/lib/module/constants.js +6 -3
- package/lib/module/constants.js.map +1 -1
- package/lib/module/index.js +8 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/install.js +8 -20
- package/lib/module/install.js.map +1 -1
- package/lib/module/main/index.js +6 -7
- package/lib/module/main/index.js.map +1 -1
- package/lib/module/main/index.web.js +1 -1
- package/lib/module/main/index.web.js.map +1 -1
- package/lib/module/main/installNative.js +18 -0
- package/lib/module/main/installNative.js.map +1 -0
- package/lib/module/main/installNative.web.js +6 -0
- package/lib/module/main/installNative.web.js.map +1 -0
- package/lib/module/mock.js +55 -1
- package/lib/module/mock.js.map +1 -1
- package/lib/typescript/lib/commonjs/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/install.d.ts +41 -3
- package/lib/typescript/lib/commonjs/install.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/main/installNative.d.ts +2 -0
- package/lib/typescript/lib/commonjs/main/installNative.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/main/installNative.web.d.ts +2 -0
- package/lib/typescript/lib/commonjs/main/installNative.web.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/mock.d.ts +39 -0
- package/lib/typescript/lib/commonjs/mock.d.ts.map +1 -1
- package/lib/typescript/lib/module/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/module/install.d.ts +0 -1
- package/lib/typescript/lib/module/install.d.ts.map +1 -1
- package/lib/typescript/lib/module/main/installNative.d.ts +2 -0
- package/lib/typescript/lib/module/main/installNative.d.ts.map +1 -0
- package/lib/typescript/lib/module/main/installNative.web.d.ts +2 -0
- package/lib/typescript/lib/module/main/installNative.web.d.ts.map +1 -0
- package/lib/typescript/lib/module/mock.d.ts +39 -1
- package/lib/typescript/lib/module/mock.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/constants.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/install.d.ts +0 -5
- package/lib/typescript/src/install.d.ts.map +1 -1
- package/lib/typescript/src/main/index.d.ts +1 -0
- package/lib/typescript/src/main/index.d.ts.map +1 -1
- package/lib/typescript/src/main/index.web.d.ts +1 -1
- package/lib/typescript/src/main/index.web.d.ts.map +1 -1
- package/lib/typescript/src/main/installNative.d.ts +2 -0
- package/lib/typescript/src/main/installNative.d.ts.map +1 -0
- package/lib/typescript/src/main/installNative.web.d.ts +2 -0
- package/lib/typescript/src/main/installNative.web.d.ts.map +1 -0
- package/lib/typescript/src/mock.d.ts +5 -1
- package/lib/typescript/src/mock.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Canvas.tsx +48 -29
- package/src/WebPolyfillGPUModule.ts +3 -1
- package/src/constants.ts +6 -3
- package/src/index.tsx +18 -10
- package/src/install.ts +9 -18
- package/src/main/index.tsx +7 -10
- package/src/main/index.web.tsx +1 -1
- package/src/main/installNative.ts +20 -0
- package/src/main/installNative.web.ts +5 -0
- package/src/mock.ts +60 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package com.webgpu;
|
|
2
2
|
|
|
3
3
|
import android.graphics.Bitmap;
|
|
4
|
+
import android.hardware.HardwareBuffer;
|
|
5
|
+
import android.os.Build;
|
|
4
6
|
import android.util.Log;
|
|
5
7
|
import android.view.View;
|
|
6
8
|
|
|
@@ -57,9 +59,12 @@ public class WebGPUModule extends NativeWebGPUModuleSpec {
|
|
|
57
59
|
* Called from C++ (AndroidPlatformContext::snapshotView) on an arbitrary
|
|
58
60
|
* thread for GPUQueue.drawElementImageToTexture. Hops to the UI thread,
|
|
59
61
|
* resolves the view for {@code tag}, rasterizes the requested rectangle and
|
|
60
|
-
* always reports back through {@link #
|
|
61
|
-
*
|
|
62
|
-
*
|
|
62
|
+
* always reports back exactly once: through {@link #onViewSnapshotBuffer}
|
|
63
|
+
* with the buffer the hardware renderer drew into, when the caller allows
|
|
64
|
+
* it and the platform can tell when the drawing is done (API 33), or else
|
|
65
|
+
* through {@link #onViewSnapshot} with a bitmap or an error message.
|
|
66
|
+
* {@code callback} is an opaque native pointer that must be handed back
|
|
67
|
+
* exactly once.
|
|
63
68
|
*
|
|
64
69
|
* The source rectangle is in density-independent points (React Native
|
|
65
70
|
* layout units) and is converted to pixels here; a non-positive
|
|
@@ -75,11 +80,15 @@ public class WebGPUModule extends NativeWebGPUModuleSpec {
|
|
|
75
80
|
final double sourceHeight,
|
|
76
81
|
final int width,
|
|
77
82
|
final int height,
|
|
83
|
+
final boolean allowHardwareBuffer,
|
|
78
84
|
final long callback) {
|
|
79
85
|
UiThreadUtil.runOnUiThread(
|
|
80
86
|
() -> {
|
|
81
87
|
Bitmap bitmap = null;
|
|
82
88
|
String error = null;
|
|
89
|
+
// Set once the callback was handed to onViewSnapshotBuffer; nothing
|
|
90
|
+
// may report again after that.
|
|
91
|
+
final boolean[] handedOver = {false};
|
|
83
92
|
try {
|
|
84
93
|
ReactApplicationContext context = getReactApplicationContext();
|
|
85
94
|
UIManager uiManager = UIManagerHelper.getUIManagerForReactTag(context, tag);
|
|
@@ -105,17 +114,72 @@ public class WebGPUModule extends NativeWebGPUModuleSpec {
|
|
|
105
114
|
throw new IllegalArgumentException(
|
|
106
115
|
"drawElementImageToTexture: the source rectangle is empty");
|
|
107
116
|
}
|
|
108
|
-
|
|
117
|
+
// The zero-copy path: the renderer's buffer goes to the GPU with
|
|
118
|
+
// the fence that says when the drawing is done. The fence is
|
|
119
|
+
// exposed from API 33; before that, and when the hardware render
|
|
120
|
+
// fails, the pixels are read back into a bitmap as usual.
|
|
121
|
+
if (allowHardwareBuffer
|
|
122
|
+
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
|
123
|
+
&& view.isHardwareAccelerated()) {
|
|
124
|
+
try {
|
|
125
|
+
ViewSnapshot.renderHardware(
|
|
126
|
+
view,
|
|
127
|
+
sx,
|
|
128
|
+
sy,
|
|
129
|
+
sw,
|
|
130
|
+
sh,
|
|
131
|
+
w,
|
|
132
|
+
h,
|
|
133
|
+
image -> {
|
|
134
|
+
HardwareBuffer buffer = image.getHardwareBuffer();
|
|
135
|
+
if (buffer == null) {
|
|
136
|
+
throw new IllegalStateException(
|
|
137
|
+
"HardwareRenderer image has no HardwareBuffer");
|
|
138
|
+
}
|
|
139
|
+
int fenceFd = SyncFences.acquireFenceFd(image);
|
|
140
|
+
// Native acquires its own reference to the buffer and
|
|
141
|
+
// adopts the fence descriptor.
|
|
142
|
+
handedOver[0] = true;
|
|
143
|
+
onViewSnapshotBuffer(callback, buffer, fenceFd);
|
|
144
|
+
buffer.close();
|
|
145
|
+
return null;
|
|
146
|
+
});
|
|
147
|
+
} catch (Throwable t) {
|
|
148
|
+
if (handedOver[0]) {
|
|
149
|
+
throw t;
|
|
150
|
+
}
|
|
151
|
+
Log.w(NAME, "HardwareRenderer snapshot failed, reading the pixels back instead", t);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!handedOver[0]) {
|
|
155
|
+
bitmap = ViewSnapshot.snapshot(view, sx, sy, sw, sh, w, h);
|
|
156
|
+
}
|
|
109
157
|
} catch (Throwable t) {
|
|
158
|
+
if (handedOver[0]) {
|
|
159
|
+
// The result is already on its way; only the cleanup failed.
|
|
160
|
+
Log.w(NAME, "drawElementImageToTexture: cleanup after the snapshot failed", t);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
110
163
|
error = t.getMessage() != null ? t.getMessage() : t.toString();
|
|
111
164
|
}
|
|
112
|
-
|
|
165
|
+
if (!handedOver[0]) {
|
|
166
|
+
onViewSnapshot(callback, bitmap, error);
|
|
167
|
+
}
|
|
113
168
|
});
|
|
114
169
|
}
|
|
115
170
|
|
|
116
171
|
@DoNotStrip
|
|
117
172
|
private native void onViewSnapshot(long callback, Bitmap bitmap, String error);
|
|
118
173
|
|
|
174
|
+
/**
|
|
175
|
+
* The zero-copy completion of {@link #snapshotView}: the buffer the hardware
|
|
176
|
+
* renderer drew the view into and the fence that signals when the drawing
|
|
177
|
+
* is done (a sync file descriptor native takes ownership of, -1 for none).
|
|
178
|
+
* Native acquires its own reference to the buffer before returning.
|
|
179
|
+
*/
|
|
180
|
+
@DoNotStrip
|
|
181
|
+
private native void onViewSnapshotBuffer(long callback, HardwareBuffer buffer, int fenceFd);
|
|
182
|
+
|
|
119
183
|
/**
|
|
120
184
|
* Called from C++ (AndroidPlatformContext::createVideoPlayer) for
|
|
121
185
|
* RNWebGPU.createVideoPlayer. Throws when the source cannot be opened; the
|
|
@@ -61,6 +61,23 @@ public class WebGPUVideoPlayer {
|
|
|
61
61
|
private volatile int rotation = 0;
|
|
62
62
|
private volatile double frameRate = 0;
|
|
63
63
|
private volatile double durationSeconds = 0;
|
|
64
|
+
// The color space of the video track as the container declares it, packed
|
|
65
|
+
// like a data space (see dataSpaceFromContainer); 0 when it says nothing.
|
|
66
|
+
private volatile int containerDataSpace = 0;
|
|
67
|
+
|
|
68
|
+
// The layout of android.hardware.DataSpace (API 33), which is also the
|
|
69
|
+
// ADataSpace of the NDK: standard << 16, transfer << 22, range << 27. Spelled
|
|
70
|
+
// out so that the mapping compiles and runs on every supported API level.
|
|
71
|
+
private static final int DATASPACE_STANDARD_BT709 = 1 << 16;
|
|
72
|
+
private static final int DATASPACE_STANDARD_BT601_625 = 2 << 16;
|
|
73
|
+
private static final int DATASPACE_STANDARD_BT601_525 = 4 << 16;
|
|
74
|
+
private static final int DATASPACE_STANDARD_BT2020 = 6 << 16;
|
|
75
|
+
private static final int DATASPACE_TRANSFER_LINEAR = 1 << 22;
|
|
76
|
+
private static final int DATASPACE_TRANSFER_SMPTE_170M = 3 << 22;
|
|
77
|
+
private static final int DATASPACE_TRANSFER_ST2084 = 7 << 22;
|
|
78
|
+
private static final int DATASPACE_TRANSFER_HLG = 8 << 22;
|
|
79
|
+
private static final int DATASPACE_RANGE_FULL = 1 << 27;
|
|
80
|
+
private static final int DATASPACE_RANGE_LIMITED = 2 << 27;
|
|
64
81
|
|
|
65
82
|
WebGPUVideoPlayer(Context context, String source) {
|
|
66
83
|
Uri uri = parseSource(source);
|
|
@@ -160,6 +177,17 @@ public class WebGPUVideoPlayer {
|
|
|
160
177
|
if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
|
|
161
178
|
frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
|
|
162
179
|
}
|
|
180
|
+
containerDataSpace =
|
|
181
|
+
dataSpaceFromContainer(
|
|
182
|
+
format.containsKey(MediaFormat.KEY_COLOR_STANDARD)
|
|
183
|
+
? format.getInteger(MediaFormat.KEY_COLOR_STANDARD)
|
|
184
|
+
: 0,
|
|
185
|
+
format.containsKey(MediaFormat.KEY_COLOR_TRANSFER)
|
|
186
|
+
? format.getInteger(MediaFormat.KEY_COLOR_TRANSFER)
|
|
187
|
+
: 0,
|
|
188
|
+
format.containsKey(MediaFormat.KEY_COLOR_RANGE)
|
|
189
|
+
? format.getInteger(MediaFormat.KEY_COLOR_RANGE)
|
|
190
|
+
: 0);
|
|
163
191
|
break;
|
|
164
192
|
}
|
|
165
193
|
}
|
|
@@ -185,6 +213,77 @@ public class WebGPUVideoPlayer {
|
|
|
185
213
|
}
|
|
186
214
|
}
|
|
187
215
|
|
|
216
|
+
// The MediaFormat color keys of the video track (KEY_COLOR_STANDARD,
|
|
217
|
+
// KEY_COLOR_TRANSFER, KEY_COLOR_RANGE; 0 when absent) packed like a data
|
|
218
|
+
// space, so that the native side reads one representation whatever its
|
|
219
|
+
// source.
|
|
220
|
+
private static int dataSpaceFromContainer(int standard, int transfer, int range) {
|
|
221
|
+
int dataSpace = 0;
|
|
222
|
+
switch (standard) {
|
|
223
|
+
case MediaFormat.COLOR_STANDARD_BT709:
|
|
224
|
+
dataSpace |= DATASPACE_STANDARD_BT709;
|
|
225
|
+
break;
|
|
226
|
+
case MediaFormat.COLOR_STANDARD_BT601_PAL:
|
|
227
|
+
dataSpace |= DATASPACE_STANDARD_BT601_625;
|
|
228
|
+
break;
|
|
229
|
+
case MediaFormat.COLOR_STANDARD_BT601_NTSC:
|
|
230
|
+
dataSpace |= DATASPACE_STANDARD_BT601_525;
|
|
231
|
+
break;
|
|
232
|
+
case MediaFormat.COLOR_STANDARD_BT2020:
|
|
233
|
+
dataSpace |= DATASPACE_STANDARD_BT2020;
|
|
234
|
+
break;
|
|
235
|
+
default:
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
switch (transfer) {
|
|
239
|
+
case MediaFormat.COLOR_TRANSFER_LINEAR:
|
|
240
|
+
dataSpace |= DATASPACE_TRANSFER_LINEAR;
|
|
241
|
+
break;
|
|
242
|
+
case MediaFormat.COLOR_TRANSFER_SDR_VIDEO:
|
|
243
|
+
dataSpace |= DATASPACE_TRANSFER_SMPTE_170M;
|
|
244
|
+
break;
|
|
245
|
+
case MediaFormat.COLOR_TRANSFER_ST2084:
|
|
246
|
+
dataSpace |= DATASPACE_TRANSFER_ST2084;
|
|
247
|
+
break;
|
|
248
|
+
case MediaFormat.COLOR_TRANSFER_HLG:
|
|
249
|
+
dataSpace |= DATASPACE_TRANSFER_HLG;
|
|
250
|
+
break;
|
|
251
|
+
default:
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
switch (range) {
|
|
255
|
+
case MediaFormat.COLOR_RANGE_FULL:
|
|
256
|
+
dataSpace |= DATASPACE_RANGE_FULL;
|
|
257
|
+
break;
|
|
258
|
+
case MediaFormat.COLOR_RANGE_LIMITED:
|
|
259
|
+
dataSpace |= DATASPACE_RANGE_LIMITED;
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
return dataSpace;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* The color space of a frame returned by {@link #copyLatestFrame}, as a data space (the
|
|
269
|
+
* packing of {@code android.hardware.DataSpace}): the one the decoder stamps on the frame
|
|
270
|
+
* (API 33), or what the container declares for the video track. 0 when neither says anything.
|
|
271
|
+
*/
|
|
272
|
+
@DoNotStrip
|
|
273
|
+
public int frameDataSpace(Image image) {
|
|
274
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
275
|
+
try {
|
|
276
|
+
int dataSpace = image.getDataSpace();
|
|
277
|
+
if (dataSpace != 0) {
|
|
278
|
+
return dataSpace;
|
|
279
|
+
}
|
|
280
|
+
} catch (IllegalStateException e) {
|
|
281
|
+
// The frame was closed under us; fall back to the container.
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return containerDataSpace;
|
|
285
|
+
}
|
|
286
|
+
|
|
188
287
|
// The decoder reports the frame size (possibly more than once for adaptive
|
|
189
288
|
// streams); the ImageReader that receives the frames is sized accordingly.
|
|
190
289
|
private void onVideoSizeChanged(int w, int h) {
|
|
@@ -273,6 +372,16 @@ public class WebGPUVideoPlayer {
|
|
|
273
372
|
return image;
|
|
274
373
|
}
|
|
275
374
|
|
|
375
|
+
/**
|
|
376
|
+
* The acquire fence of a frame returned by {@link #copyLatestFrame}, as a sync file descriptor
|
|
377
|
+
* the caller owns, or -1 when there is none. The decoder may still be writing the frame when it
|
|
378
|
+
* is acquired; the GPU work that samples it has to wait on this fence (see {@link SyncFences}).
|
|
379
|
+
*/
|
|
380
|
+
@DoNotStrip
|
|
381
|
+
public static int acquireFenceFd(Image image) {
|
|
382
|
+
return SyncFences.acquireFenceFd(image);
|
|
383
|
+
}
|
|
384
|
+
|
|
276
385
|
/** Hands the buffer of a frame returned by {@link #copyLatestFrame} back to the decoder. */
|
|
277
386
|
@DoNotStrip
|
|
278
387
|
public void closeImage(Image image) {
|
|
@@ -17,7 +17,8 @@ public:
|
|
|
17
17
|
~ApplePlatformContext() = default;
|
|
18
18
|
|
|
19
19
|
void snapshotView(const ViewSnapshotRequest &request,
|
|
20
|
-
std::function<void(ImageData)>
|
|
20
|
+
std::function<void(ImageData)> onPixels,
|
|
21
|
+
std::function<void(VideoFrameHandle)> onFrame,
|
|
21
22
|
std::function<void(std::string)> onError) override;
|
|
22
23
|
|
|
23
24
|
ImageData createImageBitmap(std::string blobId, double offset,
|
|
@@ -36,12 +37,11 @@ public:
|
|
|
36
37
|
|
|
37
38
|
VideoFrameHandle loadVideoFrame(const std::string &path) override;
|
|
38
39
|
|
|
39
|
-
VideoFrameHandle createTestVideoFrame(uint32_t width,
|
|
40
|
-
|
|
40
|
+
VideoFrameHandle createTestVideoFrame(uint32_t width, uint32_t height,
|
|
41
|
+
VideoPixelFormat format) override;
|
|
41
42
|
|
|
42
43
|
std::unique_ptr<IVideoPlayer>
|
|
43
|
-
createVideoPlayer(const std::string &path,
|
|
44
|
-
VideoPixelFormat format) override;
|
|
44
|
+
createVideoPlayer(const std::string &path, VideoPixelFormat format) override;
|
|
45
45
|
|
|
46
46
|
std::string writeTestVideoFile() override;
|
|
47
47
|
|
|
@@ -189,9 +189,12 @@ ImageData snapshotViewOnMainThread(const ApplePlatformContext::ViewLookup &looku
|
|
|
189
189
|
|
|
190
190
|
} // namespace
|
|
191
191
|
|
|
192
|
+
// Always delivers pixels: the view is drawn into a CPU bitmap, so there is no
|
|
193
|
+
// GPU frame to hand over (onFrame is unused).
|
|
192
194
|
void ApplePlatformContext::snapshotView(
|
|
193
195
|
const ViewSnapshotRequest &request,
|
|
194
|
-
std::function<void(ImageData)>
|
|
196
|
+
std::function<void(ImageData)> onPixels,
|
|
197
|
+
std::function<void(VideoFrameHandle)> /* onFrame */,
|
|
195
198
|
std::function<void(std::string)> onError) {
|
|
196
199
|
// Copies: the block outlives this call.
|
|
197
200
|
ViewLookup lookup = _viewLookup;
|
|
@@ -199,7 +202,7 @@ void ApplePlatformContext::snapshotView(
|
|
|
199
202
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
200
203
|
@autoreleasepool {
|
|
201
204
|
try {
|
|
202
|
-
|
|
205
|
+
onPixels(snapshotViewOnMainThread(lookup, snapshotRequest));
|
|
203
206
|
} catch (const std::exception &error) {
|
|
204
207
|
onError(error.what());
|
|
205
208
|
} catch (...) {
|
|
@@ -414,8 +417,77 @@ VideoFrameHandle ApplePlatformContext::wrapNativeBuffer(void *pointer) {
|
|
|
414
417
|
return wrapCVPixelBuffer(static_cast<CVPixelBufferRef>(pointer));
|
|
415
418
|
}
|
|
416
419
|
|
|
420
|
+
// The NV12 variant of createTestVideoFrame: a biplanar BT.709 limited-range
|
|
421
|
+
// frame, red on the left half and green on the right, whose attachments
|
|
422
|
+
// declare its color space the way the decoder's frames do, so that the
|
|
423
|
+
// conversion of importExternalTexture is fully determined.
|
|
424
|
+
static VideoFrameHandle createTestYuvFrame(uint32_t width, uint32_t height) {
|
|
425
|
+
NSDictionary *attrs = @{
|
|
426
|
+
(NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{},
|
|
427
|
+
(NSString *)kCVPixelBufferMetalCompatibilityKey : @YES,
|
|
428
|
+
};
|
|
429
|
+
CVPixelBufferRef pixelBuffer = NULL;
|
|
430
|
+
CVReturn err =
|
|
431
|
+
CVPixelBufferCreate(kCFAllocatorDefault, width, height,
|
|
432
|
+
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
|
433
|
+
(__bridge CFDictionaryRef)attrs, &pixelBuffer);
|
|
434
|
+
if (err != kCVReturnSuccess || !pixelBuffer) {
|
|
435
|
+
throw std::runtime_error("createTestVideoFrame: unsupported: "
|
|
436
|
+
"CVPixelBufferCreate failed for a biplanar "
|
|
437
|
+
"YUV buffer");
|
|
438
|
+
}
|
|
439
|
+
CVBufferSetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey,
|
|
440
|
+
kCVImageBufferYCbCrMatrix_ITU_R_709_2,
|
|
441
|
+
kCVAttachmentMode_ShouldPropagate);
|
|
442
|
+
CVBufferSetAttachment(pixelBuffer, kCVImageBufferColorPrimariesKey,
|
|
443
|
+
kCVImageBufferColorPrimaries_ITU_R_709_2,
|
|
444
|
+
kCVAttachmentMode_ShouldPropagate);
|
|
445
|
+
CVBufferSetAttachment(pixelBuffer, kCVImageBufferTransferFunctionKey,
|
|
446
|
+
kCVImageBufferTransferFunction_ITU_R_709_2,
|
|
447
|
+
kCVAttachmentMode_ShouldPropagate);
|
|
448
|
+
|
|
449
|
+
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
|
450
|
+
uint8_t *luma = static_cast<uint8_t *>(
|
|
451
|
+
CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
|
|
452
|
+
size_t lumaRowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
|
|
453
|
+
for (uint32_t y = 0; y < height; ++y) {
|
|
454
|
+
uint8_t *row = luma + y * lumaRowBytes;
|
|
455
|
+
for (uint32_t x = 0; x < width; ++x) {
|
|
456
|
+
row[x] = x < width / 2 ? test_yuv_frame::kRedY : test_yuv_frame::kGreenY;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// The chroma plane is subsampled by two on both axes and interleaves Cb, Cr.
|
|
460
|
+
uint8_t *chroma = static_cast<uint8_t *>(
|
|
461
|
+
CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1));
|
|
462
|
+
size_t chromaRowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
|
|
463
|
+
size_t chromaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
|
|
464
|
+
size_t chromaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
|
|
465
|
+
for (size_t y = 0; y < chromaHeight; ++y) {
|
|
466
|
+
uint8_t *row = chroma + y * chromaRowBytes;
|
|
467
|
+
for (size_t x = 0; x < chromaWidth; ++x) {
|
|
468
|
+
bool red = x < chromaWidth / 2;
|
|
469
|
+
row[x * 2 + 0] = red ? test_yuv_frame::kRedCb : test_yuv_frame::kGreenCb;
|
|
470
|
+
row[x * 2 + 1] = red ? test_yuv_frame::kRedCr : test_yuv_frame::kGreenCr;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
auto handle = wrapCVPixelBuffer(pixelBuffer);
|
|
477
|
+
CFRelease(pixelBuffer); // wrapCVPixelBuffer retained its own reference
|
|
478
|
+
return handle;
|
|
479
|
+
} catch (...) {
|
|
480
|
+
CFRelease(pixelBuffer);
|
|
481
|
+
throw;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
417
485
|
VideoFrameHandle
|
|
418
|
-
ApplePlatformContext::createTestVideoFrame(uint32_t width, uint32_t height
|
|
486
|
+
ApplePlatformContext::createTestVideoFrame(uint32_t width, uint32_t height,
|
|
487
|
+
VideoPixelFormat format) {
|
|
488
|
+
if (format == VideoPixelFormat::NV12) {
|
|
489
|
+
return createTestYuvFrame(width, height);
|
|
490
|
+
}
|
|
419
491
|
NSDictionary *attrs = @{
|
|
420
492
|
(NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{},
|
|
421
493
|
(NSString *)kCVPixelBufferMetalCompatibilityKey : @YES,
|
|
@@ -12,47 +12,80 @@ namespace rnwgpu {
|
|
|
12
12
|
|
|
13
13
|
namespace {
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
1.164383f, 2.112402f, 0.000000f, -1.133402f, //
|
|
25
|
-
};
|
|
26
|
-
static constexpr float kBT601LimitedToRgb[12] = {
|
|
27
|
-
1.164383f, 0.000000f, 1.596027f, -0.874202f, //
|
|
28
|
-
1.164383f, -0.391762f, -0.812968f, 0.531668f, //
|
|
29
|
-
1.164383f, 2.017232f, 0.000000f, -1.085631f, //
|
|
30
|
-
};
|
|
31
|
-
static constexpr float kBT2020LimitedToRgb[12] = {
|
|
32
|
-
1.164383f, 0.000000f, 1.678674f, -0.915688f, //
|
|
33
|
-
1.164383f, -0.187326f, -0.650424f, 0.347459f, //
|
|
34
|
-
1.164383f, 2.141772f, 0.000000f, -1.148145f, //
|
|
35
|
-
};
|
|
15
|
+
// The string attachment of a pixel buffer, or null when absent or not a string.
|
|
16
|
+
static CFStringRef stringAttachment(CVPixelBufferRef pixelBuffer,
|
|
17
|
+
CFStringRef key) {
|
|
18
|
+
CFTypeRef value = CVBufferGetAttachment(pixelBuffer, key, nullptr);
|
|
19
|
+
if (value == nullptr || CFGetTypeID(value) != CFStringGetTypeID()) {
|
|
20
|
+
return nullptr;
|
|
21
|
+
}
|
|
22
|
+
return static_cast<CFStringRef>(value);
|
|
23
|
+
}
|
|
36
24
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
25
|
+
// The color space of a YUV pixel buffer, from its attachments: the YCbCr matrix
|
|
26
|
+
// (kCVImageBufferYCbCrMatrixKey), the range (the full-range pixel formats),
|
|
27
|
+
// the primaries and the transfer function. What the buffer does not declare
|
|
28
|
+
// stays unset and resolves at import time to BT.709 limited range, the norm
|
|
29
|
+
// for HD video (GPUExternalTexture.cpp).
|
|
30
|
+
static VideoFrameColorSpace
|
|
31
|
+
colorSpaceFromCVPixelBuffer(CVPixelBufferRef pixelBuffer) {
|
|
32
|
+
VideoFrameColorSpace colorSpace;
|
|
33
|
+
if (CFStringRef matrix =
|
|
34
|
+
stringAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey)) {
|
|
46
35
|
if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_601_4) ||
|
|
47
36
|
CFEqual(matrix, kCVImageBufferYCbCrMatrix_SMPTE_240M_1995)) {
|
|
48
|
-
|
|
37
|
+
colorSpace.matrix = wgpu::ColorSpaceYCbCrMatrixDawn::Rec601;
|
|
38
|
+
} else if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) {
|
|
39
|
+
colorSpace.matrix = wgpu::ColorSpaceYCbCrMatrixDawn::Rec709;
|
|
49
40
|
} else if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_2020)) {
|
|
50
|
-
|
|
41
|
+
colorSpace.matrix = wgpu::ColorSpaceYCbCrMatrixDawn::Rec2020;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
switch (CVPixelBufferGetPixelFormatType(pixelBuffer)) {
|
|
45
|
+
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
46
|
+
colorSpace.range = wgpu::ColorSpaceYCbCrRangeDawn::Full;
|
|
47
|
+
break;
|
|
48
|
+
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
49
|
+
colorSpace.range = wgpu::ColorSpaceYCbCrRangeDawn::Narrow;
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (CFStringRef primaries =
|
|
55
|
+
stringAttachment(pixelBuffer, kCVImageBufferColorPrimariesKey)) {
|
|
56
|
+
if (CFEqual(primaries, kCVImageBufferColorPrimaries_ITU_R_709_2)) {
|
|
57
|
+
// BT.709 shares the sRGB primaries (Dawn's helper only knows them under
|
|
58
|
+
// that name).
|
|
59
|
+
colorSpace.primaries = wgpu::ColorSpacePrimariesDawn::SRGB;
|
|
60
|
+
} else if (CFEqual(primaries, kCVImageBufferColorPrimaries_EBU_3213) ||
|
|
61
|
+
CFEqual(primaries, kCVImageBufferColorPrimaries_SMPTE_C)) {
|
|
62
|
+
colorSpace.primaries = wgpu::ColorSpacePrimariesDawn::Rec601;
|
|
63
|
+
} else if (CFEqual(primaries, kCVImageBufferColorPrimaries_ITU_R_2020)) {
|
|
64
|
+
colorSpace.primaries = wgpu::ColorSpacePrimariesDawn::Rec2020;
|
|
65
|
+
} else if (CFEqual(primaries, kCVImageBufferColorPrimaries_P3_D65)) {
|
|
66
|
+
colorSpace.primaries = wgpu::ColorSpacePrimariesDawn::DisplayP3;
|
|
51
67
|
}
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
if (CFStringRef transfer =
|
|
70
|
+
stringAttachment(pixelBuffer, kCVImageBufferTransferFunctionKey)) {
|
|
71
|
+
if (CFEqual(transfer, kCVImageBufferTransferFunction_ITU_R_709_2) ||
|
|
72
|
+
CFEqual(transfer, kCVImageBufferTransferFunction_SMPTE_240M_1995) ||
|
|
73
|
+
CFEqual(transfer, kCVImageBufferTransferFunction_ITU_R_2020)) {
|
|
74
|
+
// The SDR video OETF; SMPTE 170M is Dawn's name for its inverse.
|
|
75
|
+
colorSpace.transfer = wgpu::ColorSpaceTransferDawn::SMPTE_170M;
|
|
76
|
+
} else if (CFEqual(transfer, kCVImageBufferTransferFunction_sRGB)) {
|
|
77
|
+
colorSpace.transfer = wgpu::ColorSpaceTransferDawn::SRGB;
|
|
78
|
+
} else if (CFEqual(transfer, kCVImageBufferTransferFunction_Linear)) {
|
|
79
|
+
colorSpace.transfer = wgpu::ColorSpaceTransferDawn::Identity;
|
|
80
|
+
} else if (CFEqual(transfer,
|
|
81
|
+
kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ)) {
|
|
82
|
+
colorSpace.transfer = wgpu::ColorSpaceTransferDawn::PQ;
|
|
83
|
+
} else if (CFEqual(transfer,
|
|
84
|
+
kCVImageBufferTransferFunction_ITU_R_2100_HLG)) {
|
|
85
|
+
colorSpace.transfer = wgpu::ColorSpaceTransferDawn::HLG;
|
|
86
|
+
}
|
|
55
87
|
}
|
|
88
|
+
return colorSpace;
|
|
56
89
|
}
|
|
57
90
|
|
|
58
91
|
// Map a CVPixelBuffer's pixel format to our VideoPixelFormat enum.
|
|
@@ -257,7 +290,7 @@ VideoFrameHandle wrapCVPixelBuffer(CVPixelBufferRef pixelBuffer) {
|
|
|
257
290
|
handle.height = static_cast<uint32_t>(CVPixelBufferGetHeight(pixelBuffer));
|
|
258
291
|
handle.pixelFormat = pixelFormatFromCVPixelBuffer(pixelBuffer);
|
|
259
292
|
if (handle.pixelFormat == VideoPixelFormat::NV12) {
|
|
260
|
-
|
|
293
|
+
handle.colorSpace = colorSpaceFromCVPixelBuffer(pixelBuffer);
|
|
261
294
|
}
|
|
262
295
|
handle.deleter = [pixelBuffer]() { CFRelease(pixelBuffer); };
|
|
263
296
|
return handle;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Copyright 2026 The Dawn & Tint Authors
|
|
2
|
+
//
|
|
3
|
+
// Redistribution and use in source and binary forms, with or without
|
|
4
|
+
// modification, are permitted provided that the following conditions are met:
|
|
5
|
+
//
|
|
6
|
+
// 1. Redistributions of source code must retain the above copyright notice, this
|
|
7
|
+
// list of conditions and the following disclaimer.
|
|
8
|
+
//
|
|
9
|
+
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
// this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
// and/or other materials provided with the distribution.
|
|
12
|
+
//
|
|
13
|
+
// 3. Neither the name of the copyright holder nor the names of its
|
|
14
|
+
// contributors may be used to endorse or promote products derived from
|
|
15
|
+
// this software without specific prior written permission.
|
|
16
|
+
//
|
|
17
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
18
|
+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
19
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
20
|
+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
24
|
+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
25
|
+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
26
|
+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
|
|
28
|
+
// DAWN_VENDORED_EXTERNAL_TEXTURE_PARAMS
|
|
29
|
+
//
|
|
30
|
+
// Vendored from Dawn's src/dawn/common/ExternalTextureParams.h, unchanged. The
|
|
31
|
+
// functions it declares are compiled into the Dawn binaries that
|
|
32
|
+
// react-native-webgpu-dawn ships (libwebgpu_dawn.so, libwebgpu_dawn.a), which
|
|
33
|
+
// export them, but the package only ships Dawn's public headers. The copy is
|
|
34
|
+
// pinned to the Dawn commit of the package in scripts/check-dawn.ts: on a Dawn
|
|
35
|
+
// bump, diff it against the new commit's file and re-sync it.
|
|
36
|
+
|
|
37
|
+
#ifndef SRC_DAWN_COMMON_EXTERNALTEXTUREPARAMS_H_
|
|
38
|
+
#define SRC_DAWN_COMMON_EXTERNALTEXTUREPARAMS_H_
|
|
39
|
+
|
|
40
|
+
#include <webgpu/webgpu_cpp.h>
|
|
41
|
+
|
|
42
|
+
#include <array>
|
|
43
|
+
|
|
44
|
+
namespace dawn {
|
|
45
|
+
|
|
46
|
+
// Color space parameters that can be passed to an ExternalTexture.
|
|
47
|
+
struct ExternalTextureColorSpaceParams {
|
|
48
|
+
// Stored row-major to save uniform buffer space. (so one full row after another)
|
|
49
|
+
// TODO(https://crbug.com/496604550): That's an implementation detail that should not be exposed
|
|
50
|
+
// in the API, Dawn can transpose internally.
|
|
51
|
+
std::array<float, 12> yuvToRgbConversionMatrix{};
|
|
52
|
+
std::array<float, 7> srcTransferFunction = {};
|
|
53
|
+
std::array<float, 7> dstTransferFunction = {}; // Really the inverse transfer function.
|
|
54
|
+
// Stored column major to match WGSL. (so one full column after another)
|
|
55
|
+
std::array<float, 9> gamutConversionMatrix{};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Helper function to make parameters for an ExternalTextureDescriptor
|
|
59
|
+
wgpu::Status ComputeExternalTextureParams(const wgpu::ColorSpaceDawn& srcColorSpace,
|
|
60
|
+
wgpu::PredefinedColorSpace dstColorSpace,
|
|
61
|
+
ExternalTextureColorSpaceParams* out);
|
|
62
|
+
|
|
63
|
+
ExternalTextureColorSpaceParams GetNoopColorSpaceParams();
|
|
64
|
+
|
|
65
|
+
} // namespace dawn
|
|
66
|
+
|
|
67
|
+
#endif // SRC_DAWN_COMMON_EXTERNALTEXTUREPARAMS_H_
|