react-native-webgpu 0.16.1 → 0.19.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/AndroidPlatformContext.h +8 -0
- package/android/src/main/java/com/webgpu/BackingViewKind.java +38 -0
- package/android/src/main/java/com/webgpu/WebGPUSurfaceView.java +19 -2
- package/android/src/main/java/com/webgpu/WebGPUView.java +78 -76
- package/android/src/main/java/com/webgpu/WebGPUViewManager.java +13 -6
- package/apple/ApplePlatformContext.h +2 -0
- package/apple/ApplePlatformContext.mm +12 -0
- package/apple/MetalLayerColorSpace.mm +20 -8
- package/cpp/jsi/NativeObject.h +42 -32
- package/cpp/rnwgpu/CanvasColorSpace.h +9 -0
- package/cpp/rnwgpu/PlatformContext.h +6 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +5 -8
- package/cpp/rnwgpu/SurfaceRegistry.h +101 -30
- package/cpp/rnwgpu/WebGPURuntime.cpp +30 -11
- package/cpp/rnwgpu/WebGPURuntime.h +7 -2
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +2 -1
- package/cpp/rnwgpu/api/GPUCanvasContext.h +10 -0
- package/cpp/rnwgpu/api/GPUFeatures.h +1 -0
- package/cpp/rnwgpu/api/RNWebGPU.h +63 -5
- package/cpp/rnwgpu/api/VideoFrame.h +2 -2
- package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +18 -0
- package/cpp/rnwgpu/async/RuntimeContext.cpp +43 -27
- package/cpp/rnwgpu/async/RuntimeContext.h +19 -11
- package/lib/commonjs/Canvas.js +88 -43
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/WebGPUViewNativeComponent.js.map +1 -1
- package/lib/commonjs/WebGPUViewNativeComponent.web.js +3 -5
- package/lib/commonjs/WebGPUViewNativeComponent.web.js.map +1 -1
- package/lib/commonjs/WebPolyfillGPUModule.js +2 -0
- package/lib/commonjs/WebPolyfillGPUModule.js.map +1 -1
- package/lib/commonjs/importDevice.js +3 -2
- package/lib/commonjs/importDevice.js.map +1 -1
- package/lib/commonjs/mock.js +0 -1
- package/lib/commonjs/mock.js.map +1 -1
- package/lib/module/Canvas.js +88 -43
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/WebGPUViewNativeComponent.js.map +1 -1
- package/lib/module/WebGPUViewNativeComponent.web.js +3 -5
- package/lib/module/WebGPUViewNativeComponent.web.js.map +1 -1
- package/lib/module/WebPolyfillGPUModule.js +2 -0
- package/lib/module/WebPolyfillGPUModule.js.map +1 -1
- package/lib/module/importDevice.js +3 -2
- package/lib/module/importDevice.js.map +1 -1
- package/lib/module/mock.js +0 -1
- package/lib/module/mock.js.map +1 -1
- package/lib/typescript/lib/commonjs/Canvas.d.ts +1 -2
- package/lib/typescript/lib/commonjs/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/WebGPUViewNativeComponent.web.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/importDevice.d.ts +3 -2
- package/lib/typescript/lib/commonjs/importDevice.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/mock.d.ts +1 -2
- package/lib/typescript/lib/commonjs/mock.d.ts.map +1 -1
- package/lib/typescript/lib/module/Canvas.d.ts +1 -2
- package/lib/typescript/lib/module/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/module/WebGPUViewNativeComponent.web.d.ts.map +1 -1
- package/lib/typescript/lib/module/importDevice.d.ts.map +1 -1
- package/lib/typescript/lib/module/mock.d.ts +1 -2
- package/lib/typescript/lib/module/mock.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.d.ts +61 -29
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/WebGPUViewNativeComponent.d.ts +3 -2
- package/lib/typescript/src/WebGPUViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/WebGPUViewNativeComponent.web.d.ts +3 -2
- package/lib/typescript/src/WebGPUViewNativeComponent.web.d.ts.map +1 -1
- package/lib/typescript/src/importDevice.d.ts +3 -2
- package/lib/typescript/src/importDevice.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mock.d.ts +2 -2
- package/lib/typescript/src/mock.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Canvas.tsx +156 -81
- package/src/WebGPUViewNativeComponent.ts +6 -4
- package/src/WebGPUViewNativeComponent.web.ts +7 -5
- package/src/WebPolyfillGPUModule.ts +3 -0
- package/src/importDevice.ts +3 -2
- package/src/index.tsx +15 -4
- package/src/mock.ts +5 -7
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <android/bitmap.h>
|
|
4
4
|
#include <android/data_space.h>
|
|
5
5
|
#include <android/hardware_buffer.h>
|
|
6
|
+
#include <android/native_window.h>
|
|
6
7
|
#include <dlfcn.h>
|
|
7
8
|
#include <jni.h>
|
|
8
9
|
|
|
@@ -469,6 +470,13 @@ public:
|
|
|
469
470
|
"writeTestVideoFile is not yet implemented on Android.");
|
|
470
471
|
}
|
|
471
472
|
|
|
473
|
+
std::function<void(void *)> retainNativeWindow(void *window) override {
|
|
474
|
+
ANativeWindow_acquire(static_cast<ANativeWindow *>(window));
|
|
475
|
+
return [](void *nativeWindow) {
|
|
476
|
+
ANativeWindow_release(static_cast<ANativeWindow *>(nativeWindow));
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
472
480
|
VideoFrameHandle wrapNativeBuffer(void *pointer) override {
|
|
473
481
|
if (!pointer) {
|
|
474
482
|
throw std::runtime_error("wrapNativeBuffer: pointer is null");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.webgpu;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Nullable;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The Android view a WebGPUView draws into, see WebGPUView.updateView(), and
|
|
7
|
+
* the rules the props resolve by. The JS side (Canvas.tsx) mirrors them to
|
|
8
|
+
* warn in development.
|
|
9
|
+
*/
|
|
10
|
+
enum BackingViewKind {
|
|
11
|
+
SURFACE_VIEW,
|
|
12
|
+
TEXTURE_VIEW,
|
|
13
|
+
HARDWARE_BUFFER_VIEW;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The kind the androidBackingView prop names; a TextureView by default. A
|
|
17
|
+
* HardwareBufferView (a plain View drawing AHardwareBuffers inline) needs
|
|
18
|
+
* Android 10: below it, the request falls back to a TextureView, which
|
|
19
|
+
* composites the same way, and the JS side warns about it in development.
|
|
20
|
+
*/
|
|
21
|
+
static BackingViewKind fromBackingView(@Nullable String backingView, boolean hardwareBufferSupported) {
|
|
22
|
+
if ("SurfaceView".equals(backingView)) {
|
|
23
|
+
return SURFACE_VIEW;
|
|
24
|
+
}
|
|
25
|
+
if ("HardwareBufferView".equals(backingView) && hardwareBufferSupported) {
|
|
26
|
+
return HARDWARE_BUFFER_VIEW;
|
|
27
|
+
}
|
|
28
|
+
return TEXTURE_VIEW;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether the canvas composites as opaque: what the prop asks for, except
|
|
33
|
+
* that a HardwareBufferView always composites with alpha.
|
|
34
|
+
*/
|
|
35
|
+
static boolean resolveOpaque(BackingViewKind kind, boolean opaque) {
|
|
36
|
+
return opaque && kind != HARDWARE_BUFFER_VIEW;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -13,10 +13,12 @@ public class WebGPUSurfaceView extends SurfaceView implements SurfaceHolder.Call
|
|
|
13
13
|
|
|
14
14
|
WebGPUAPI mApi;
|
|
15
15
|
|
|
16
|
-
public WebGPUSurfaceView(Context context, WebGPUAPI api, boolean zOrderOnTop,
|
|
16
|
+
public WebGPUSurfaceView(Context context, WebGPUAPI api, boolean zOrderOnTop,
|
|
17
|
+
boolean zOrderMediaOverlay, boolean opaque) {
|
|
17
18
|
super(context);
|
|
18
19
|
mApi = api;
|
|
19
|
-
|
|
20
|
+
// Both are read when the surface is created.
|
|
21
|
+
setZOrder(zOrderOnTop, zOrderMediaOverlay);
|
|
20
22
|
setOpaque(opaque);
|
|
21
23
|
getHolder().addCallback(this);
|
|
22
24
|
}
|
|
@@ -27,6 +29,21 @@ public class WebGPUSurfaceView extends SurfaceView implements SurfaceHolder.Call
|
|
|
27
29
|
getHolder().setFormat(opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
// The sublayer the surface is composited at, relative to the window: above
|
|
33
|
+
// it (on top), or below it, above the other SurfaceViews there (media
|
|
34
|
+
// overlay) or among them (the default). setZOrderOnTop(false) resets the
|
|
35
|
+
// request to the default, so it goes first. Before Android 11 the request
|
|
36
|
+
// is read when the surface is created; from Android 11 on, SurfaceView
|
|
37
|
+
// applies a changed request with the window's next frame (its pre-draw
|
|
38
|
+
// listener runs updateSurface()), which invalidate() schedules.
|
|
39
|
+
public void setZOrder(boolean onTop, boolean mediaOverlay) {
|
|
40
|
+
setZOrderOnTop(onTop);
|
|
41
|
+
if (!onTop) {
|
|
42
|
+
setZOrderMediaOverlay(mediaOverlay);
|
|
43
|
+
}
|
|
44
|
+
invalidate();
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
@Override
|
|
31
48
|
protected void onDetachedFromWindow() {
|
|
32
49
|
super.onDetachedFromWindow();
|
|
@@ -6,31 +6,36 @@ import android.util.Log;
|
|
|
6
6
|
import android.view.Surface;
|
|
7
7
|
import android.view.View;
|
|
8
8
|
|
|
9
|
+
import androidx.annotation.Nullable;
|
|
10
|
+
|
|
9
11
|
import com.facebook.proguard.annotations.DoNotStrip;
|
|
10
12
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
11
13
|
import com.facebook.react.views.view.ReactViewGroup;
|
|
12
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The Android view behind <Canvas>. It is backed by a TextureView, a
|
|
17
|
+
* SurfaceView or a HardwareBufferView, named by the androidBackingView prop
|
|
18
|
+
* and created once, from the props of the first transaction, for the life of
|
|
19
|
+
* the view (see updateView()).
|
|
20
|
+
*/
|
|
13
21
|
public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
14
22
|
private static final String TAG = "WebGPUView";
|
|
15
23
|
|
|
16
|
-
// Backing view kinds, see updateView().
|
|
17
|
-
private static final int KIND_SURFACE_VIEW = 0;
|
|
18
|
-
private static final int KIND_TEXTURE_VIEW = 1;
|
|
19
|
-
private static final int KIND_HARDWARE_BUFFER_VIEW = 2;
|
|
20
|
-
|
|
21
24
|
private int mContextId;
|
|
22
|
-
// Props. opaque is applied in place, and so
|
|
23
|
-
// allows it (see
|
|
25
|
+
// Props. opaque is applied in place, and so are the z-order props where
|
|
26
|
+
// Android allows it (see takesZOrder); backingView is read once, when the
|
|
24
27
|
// backing view is created (see updateView()).
|
|
25
|
-
private
|
|
26
|
-
private
|
|
28
|
+
private BackingViewKind mBackingView = BackingViewKind.TEXTURE_VIEW;
|
|
29
|
+
private boolean mOpaque = false;
|
|
27
30
|
private boolean mZOrderOnTop = false;
|
|
31
|
+
private boolean mZOrderMediaOverlay = false;
|
|
28
32
|
private WebGPUModule mModule;
|
|
29
33
|
// The backing view, created by the first prop transaction and kept, with
|
|
30
|
-
// the kind it was created with. The kind is
|
|
34
|
+
// the kind it was created with. The kind is null until then: it is what
|
|
31
35
|
// tells a mounted canvas apart.
|
|
32
36
|
private View mView = null;
|
|
33
|
-
|
|
37
|
+
@Nullable
|
|
38
|
+
private BackingViewKind mKind = null;
|
|
34
39
|
// Only a SurfaceView votes: a TextureView or HardwareBufferView is
|
|
35
40
|
// composited into the window, which votes for it.
|
|
36
41
|
private final SurfaceFrameRateVote mFrameRateVote = new SurfaceFrameRateVote(
|
|
@@ -58,11 +63,7 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
58
63
|
mContextId = contextId;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
mOpaque = value;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// The backing view is chosen from the props of the first transaction and
|
|
66
|
+
// The backing view is created from the props of the first transaction and
|
|
66
67
|
// kept (see updateView()). React calls a setter again only when its prop
|
|
67
68
|
// changed, so a call on a mounted canvas is a change: the setters below
|
|
68
69
|
// apply it in place where the view can take it, else they ignore it and
|
|
@@ -70,98 +71,99 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
70
71
|
// new key). The JS side warns about the same changes in development (see
|
|
71
72
|
// Canvas.tsx), mirroring these rules.
|
|
72
73
|
|
|
73
|
-
public void
|
|
74
|
-
if (mKind !=
|
|
75
|
-
warnIgnoredOnceMounted("android.
|
|
74
|
+
public void setBackingView(@Nullable String value) {
|
|
75
|
+
if (mKind != null) {
|
|
76
|
+
warnIgnoredOnceMounted("android.backingView");
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
boolean hardwareBufferSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
|
|
80
|
+
mBackingView = BackingViewKind.fromBackingView(value, hardwareBufferSupported);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public void setOpaque(boolean value) {
|
|
84
|
+
mOpaque = value;
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
public void setZOrderOnTop(boolean value) {
|
|
82
|
-
if (
|
|
88
|
+
if (takesZOrder("android.zOrderOnTop")) {
|
|
83
89
|
mZOrderOnTop = value;
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
// Only a SurfaceView has a z-order of its own: elsewhere the prop never
|
|
87
|
-
// applied, and the change is moot.
|
|
88
|
-
if (mKind != KIND_SURFACE_VIEW) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (!zOrderChangesInPlace()) {
|
|
92
|
-
warnIgnoredOnceMounted("android.zOrderOnTop");
|
|
93
|
-
return;
|
|
90
|
+
applyZOrder();
|
|
94
91
|
}
|
|
95
|
-
mZOrderOnTop = value;
|
|
96
|
-
((WebGPUSurfaceView) mView).setZOrderOnTop(value);
|
|
97
92
|
}
|
|
98
93
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
public void setZOrderMediaOverlay(boolean value) {
|
|
95
|
+
if (takesZOrder("android.zOrderMediaOverlay")) {
|
|
96
|
+
mZOrderMediaOverlay = value;
|
|
97
|
+
applyZOrder();
|
|
98
|
+
}
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
// Whether a z-order prop is taken. Before the backing view exists it is
|
|
102
|
+
// read when the view is created. After that only a SurfaceView has a
|
|
103
|
+
// z-order of its own (elsewhere the prop never applied, and the change is
|
|
104
|
+
// moot), and SurfaceView moves a live surface since Android 11, for an app
|
|
105
|
+
// targeting it; before that, the surface reads it when it is created.
|
|
106
|
+
private boolean takesZOrder(String prop) {
|
|
107
|
+
if (mKind == null) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (mKind != BackingViewKind.SURFACE_VIEW) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
boolean inPlace = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
|
110
114
|
&& getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R;
|
|
115
|
+
if (!inPlace) {
|
|
116
|
+
warnIgnoredOnceMounted(prop);
|
|
117
|
+
}
|
|
118
|
+
return inPlace;
|
|
111
119
|
}
|
|
112
120
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// surfaceType until it gets a runtime fallback to TextureView when the
|
|
117
|
-
// AHardwareBuffer import fails on a device; below API 29 the request itself
|
|
118
|
-
// falls back to TextureView.
|
|
119
|
-
private int resolveKind() {
|
|
120
|
-
if ("SurfaceView".equals(mSurfaceType)) {
|
|
121
|
-
return KIND_SURFACE_VIEW;
|
|
122
|
-
}
|
|
123
|
-
if ("TextureView".equals(mSurfaceType)) {
|
|
124
|
-
return KIND_TEXTURE_VIEW;
|
|
121
|
+
private void applyZOrder() {
|
|
122
|
+
if (mKind == BackingViewKind.SURFACE_VIEW) {
|
|
123
|
+
((WebGPUSurfaceView) mView).setZOrder(mZOrderOnTop, mZOrderMediaOverlay);
|
|
125
124
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private void warnIgnoredOnceMounted(String prop) {
|
|
128
|
+
Log.w(TAG, "Ignoring a change of " + prop + " on a mounted canvas: the backing view"
|
|
129
|
+
+ " is chosen when the canvas mounts. Remount the canvas (give it a new key)"
|
|
130
|
+
+ " to change it.");
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// Apply the complete prop transaction once, after contextId and all rendering
|
|
134
134
|
// options have arrived. The first one creates the backing view, which is
|
|
135
135
|
// then kept: a SurfaceView must know its z-order before it attaches (before
|
|
136
|
-
// Android 11), and
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
136
|
+
// Android 11), and keeping a canvas on screen while its backing view is
|
|
137
|
+
// replaced takes hold-and-retire logic specific to each Android release (see
|
|
138
|
+
// PR #563, and react-native-skia's #4189 and #4192), which this view does
|
|
139
|
+
// not carry. A canvas that needs another backing view is remounted instead,
|
|
140
|
+
// and is off screen for a frame or two until the new view draws its first
|
|
141
|
+
// frame. Later transactions apply the opacity in place (the setters decide
|
|
141
142
|
// what else is taken); WebGPUHardwareBufferView always composites with
|
|
142
143
|
// alpha, so there it is a no-op.
|
|
143
144
|
public void updateView() {
|
|
144
|
-
if (mKind !=
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
if (mKind != null) {
|
|
146
|
+
boolean opaque = BackingViewKind.resolveOpaque(mKind, mOpaque);
|
|
147
|
+
if (mKind == BackingViewKind.TEXTURE_VIEW) {
|
|
148
|
+
((WebGPUTextureView) mView).setOpaque(opaque);
|
|
149
|
+
} else if (mKind == BackingViewKind.SURFACE_VIEW) {
|
|
150
|
+
((WebGPUSurfaceView) mView).setOpaque(opaque);
|
|
149
151
|
}
|
|
150
152
|
return;
|
|
151
153
|
}
|
|
152
|
-
|
|
153
|
-
boolean zOrderOnTop = kind == KIND_SURFACE_VIEW && mZOrderOnTop;
|
|
154
|
+
BackingViewKind kind = mBackingView;
|
|
154
155
|
mKind = kind;
|
|
156
|
+
boolean opaque = BackingViewKind.resolveOpaque(kind, mOpaque);
|
|
155
157
|
Context ctx = getContext();
|
|
156
158
|
switch (kind) {
|
|
157
|
-
case
|
|
159
|
+
case HARDWARE_BUFFER_VIEW:
|
|
158
160
|
mView = new WebGPUHardwareBufferView(ctx, this);
|
|
159
161
|
break;
|
|
160
|
-
case
|
|
161
|
-
mView = new WebGPUTextureView(ctx, this,
|
|
162
|
+
case TEXTURE_VIEW:
|
|
163
|
+
mView = new WebGPUTextureView(ctx, this, opaque);
|
|
162
164
|
break;
|
|
163
165
|
default:
|
|
164
|
-
mView = new WebGPUSurfaceView(ctx, this,
|
|
166
|
+
mView = new WebGPUSurfaceView(ctx, this, mZOrderOnTop, mZOrderMediaOverlay, opaque);
|
|
165
167
|
break;
|
|
166
168
|
}
|
|
167
169
|
addView(mView);
|
|
@@ -188,7 +190,7 @@ public class WebGPUView extends ReactViewGroup implements WebGPUAPI {
|
|
|
188
190
|
float density = getResources().getDisplayMetrics().density;
|
|
189
191
|
float width = getWidth() / density;
|
|
190
192
|
float height = getHeight() / density;
|
|
191
|
-
boolean votesFrameRate = mKind ==
|
|
193
|
+
boolean votesFrameRate = mKind == BackingViewKind.SURFACE_VIEW;
|
|
192
194
|
if (votesFrameRate) {
|
|
193
195
|
mFrameRateVote.attach(surface);
|
|
194
196
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.webgpu;
|
|
2
2
|
|
|
3
3
|
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
4
5
|
|
|
5
6
|
import com.facebook.react.module.annotations.ReactModule;
|
|
6
7
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
@@ -30,15 +31,15 @@ public class WebGPUViewManager extends WebGPUViewManagerSpec<WebGPUView> {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
@Override
|
|
33
|
-
@ReactProp(name = "
|
|
34
|
-
public void
|
|
35
|
-
view.
|
|
34
|
+
@ReactProp(name = "androidBackingView")
|
|
35
|
+
public void setAndroidBackingView(WebGPUView view, @Nullable String value) {
|
|
36
|
+
view.setBackingView(value);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
@Override
|
|
39
|
-
@ReactProp(name = "
|
|
40
|
-
public void
|
|
41
|
-
view.
|
|
40
|
+
@ReactProp(name = "androidOpaque")
|
|
41
|
+
public void setAndroidOpaque(WebGPUView view, boolean value) {
|
|
42
|
+
view.setOpaque(value);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
@Override
|
|
@@ -47,6 +48,12 @@ public class WebGPUViewManager extends WebGPUViewManagerSpec<WebGPUView> {
|
|
|
47
48
|
view.setZOrderOnTop(value);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
@Override
|
|
52
|
+
@ReactProp(name = "androidZOrderMediaOverlay")
|
|
53
|
+
public void setAndroidZOrderMediaOverlay(WebGPUView view, boolean value) {
|
|
54
|
+
view.setZOrderMediaOverlay(value);
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
@Override
|
|
51
58
|
protected void onAfterUpdateTransaction(@NonNull WebGPUView view) {
|
|
52
59
|
super.onAfterUpdateTransaction(view);
|
|
@@ -556,6 +556,18 @@ VideoFrameHandle ApplePlatformContext::wrapNativeBuffer(void *pointer) {
|
|
|
556
556
|
return wrapCVPixelBuffer(static_cast<CVPixelBufferRef>(pointer));
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
std::function<void(void *)>
|
|
560
|
+
ApplePlatformContext::retainNativeWindow(void *window) {
|
|
561
|
+
CFRetain(window);
|
|
562
|
+
return [](void *layer) {
|
|
563
|
+
// As MetalView's: the releaser can run on the rendering thread, and
|
|
564
|
+
// CALayer teardown belongs on the main thread.
|
|
565
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
566
|
+
CFRelease(layer);
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
559
571
|
// The NV12 variant of createTestVideoFrame: a biplanar BT.709 limited-range
|
|
560
572
|
// frame, red on the left half and green on the right, whose attachments
|
|
561
573
|
// declare its color space the way the decoder's frames do, so that the
|
|
@@ -3,32 +3,44 @@
|
|
|
3
3
|
#import <QuartzCore/CAMetalLayer.h>
|
|
4
4
|
#import <QuartzCore/CATransaction.h>
|
|
5
5
|
|
|
6
|
+
#include "CanvasColorSpace.h"
|
|
6
7
|
#include "webgpu/webgpu_cpp.h"
|
|
7
8
|
|
|
8
9
|
namespace rnwgpu {
|
|
9
10
|
|
|
10
|
-
// WebGPU canvas values are
|
|
11
|
-
//
|
|
11
|
+
// WebGPU canvas values are encoded with the sRGB transfer function regardless
|
|
12
|
+
// of the texture format, and GPUCanvasConfiguration.colorSpace says which
|
|
13
|
+
// primaries they are in: "srgb" (the default) or "display-p3". The same shader
|
|
12
14
|
// output must display identically on bgra8unorm and rgba16float surfaces.
|
|
13
15
|
// Core Animation interprets a float-format layer with a nil colorspace as
|
|
14
16
|
// extended *linear* sRGB, which displays the same values noticeably brighter.
|
|
15
17
|
// Tagging the layer as (gamma-encoded) extended sRGB matches the browser
|
|
16
|
-
// behavior: identical colors, with the extra precision of float16.
|
|
18
|
+
// behavior: identical colors, with the extra precision of float16. For
|
|
19
|
+
// "display-p3" the layer is tagged Display P3 instead, or its extended range
|
|
20
|
+
// variant on a float format, so the values reach a wide-gamut display without
|
|
21
|
+
// being converted from sRGB first.
|
|
17
22
|
void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
18
|
-
wgpu::TextureFormat format
|
|
23
|
+
wgpu::TextureFormat format,
|
|
24
|
+
CanvasColorSpace canvasColorSpace) {
|
|
19
25
|
CALayer *layer = (__bridge CALayer *)nativeSurface;
|
|
20
26
|
if (![layer isKindOfClass:[CAMetalLayer class]]) {
|
|
21
27
|
return;
|
|
22
28
|
}
|
|
23
29
|
auto metalLayer = static_cast<CAMetalLayer *>(layer);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
const bool isFloat = format == wgpu::TextureFormat::RGBA16Float;
|
|
31
|
+
CFStringRef name = nullptr;
|
|
32
|
+
if (canvasColorSpace == CanvasColorSpace::DisplayP3) {
|
|
33
|
+
name = isFloat ? kCGColorSpaceExtendedDisplayP3 : kCGColorSpaceDisplayP3;
|
|
34
|
+
} else if (isFloat) {
|
|
35
|
+
name = kCGColorSpaceExtendedSRGB;
|
|
36
|
+
}
|
|
37
|
+
if (name != nullptr) {
|
|
38
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(name);
|
|
27
39
|
metalLayer.colorspace = colorSpace;
|
|
28
40
|
CGColorSpaceRelease(colorSpace);
|
|
29
41
|
} else if (metalLayer.colorspace != nil) {
|
|
30
42
|
// Restore the default (no color matching) when reconfiguring back to an
|
|
31
|
-
// 8-bit
|
|
43
|
+
// 8-bit sRGB canvas.
|
|
32
44
|
metalLayer.colorspace = nil;
|
|
33
45
|
}
|
|
34
46
|
// The change must be set synchronously so the first present already sees
|
package/cpp/jsi/NativeObject.h
CHANGED
|
@@ -328,6 +328,11 @@ public:
|
|
|
328
328
|
return; // Already installed on this runtime
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// Every wrapper is made by create(), which installs the prototype first,
|
|
332
|
+
// so the class is registered before any of its objects can be boxed,
|
|
333
|
+
// whether or not its constructor is ever installed as a global.
|
|
334
|
+
registerReconstructor();
|
|
335
|
+
|
|
331
336
|
// Create prototype object
|
|
332
337
|
jsi::Object prototype(runtime);
|
|
333
338
|
|
|
@@ -411,40 +416,10 @@ public:
|
|
|
411
416
|
* Idempotent per runtime: once the global exists there, later calls return
|
|
412
417
|
* without touching it, so the constructor object keeps its identity.
|
|
413
418
|
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
419
|
+
* Not needed for objects to cross runtimes: installPrototype() registers
|
|
420
|
+
* the class with NativeObjectRegistry.
|
|
416
421
|
*/
|
|
417
422
|
static void installConstructor(jsi::Runtime &runtime) {
|
|
418
|
-
// Register the reconstructor used by BoxedWebGPUObject::unbox() to
|
|
419
|
-
// rebuild this object (prototype + native state) on another runtime.
|
|
420
|
-
static std::once_flag registryFlag;
|
|
421
|
-
std::call_once(registryFlag, []() {
|
|
422
|
-
NativeObjectRegistry::getInstance().registerClass(
|
|
423
|
-
Derived::CLASS_NAME,
|
|
424
|
-
[](jsi::Runtime &rt,
|
|
425
|
-
std::shared_ptr<jsi::NativeState> state) -> jsi::Value {
|
|
426
|
-
auto instance = std::dynamic_pointer_cast<Derived>(state);
|
|
427
|
-
if (instance == nullptr) {
|
|
428
|
-
throw jsi::JSError(rt, "Invalid boxed native object state");
|
|
429
|
-
}
|
|
430
|
-
// The constructor makes `instanceof` work on this runtime too
|
|
431
|
-
// (a no-op once it is installed there).
|
|
432
|
-
Derived::installConstructor(rt);
|
|
433
|
-
// Unboxing hands back the wrapper cached for the target runtime,
|
|
434
|
-
// creating it on first use. Keep the runtime the object was
|
|
435
|
-
// originally created on: async native code (GPUDevice error/lost
|
|
436
|
-
// events, GPUAdapter::requestDevice) delivers into the creation
|
|
437
|
-
// runtime, and rebinding it to a worklet runtime would invoke
|
|
438
|
-
// main-runtime jsi::Functions on the wrong runtime and thread.
|
|
439
|
-
auto *originalRuntime = instance->getCreationRuntime();
|
|
440
|
-
auto value = Derived::create(rt, instance);
|
|
441
|
-
if (originalRuntime != nullptr) {
|
|
442
|
-
instance->setCreationRuntime(originalRuntime);
|
|
443
|
-
}
|
|
444
|
-
return value;
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
|
|
448
423
|
installPrototype(runtime);
|
|
449
424
|
|
|
450
425
|
// Once per runtime. installWebGPU() runs this at the top of every worklet
|
|
@@ -481,6 +456,41 @@ public:
|
|
|
481
456
|
runtime.global().setProperty(runtime, Derived::CLASS_NAME, std::move(ctor));
|
|
482
457
|
}
|
|
483
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Registers the reconstructor BoxedWebGPUObject::unbox() uses to rebuild
|
|
461
|
+
* this class's objects (prototype + native state) on another runtime.
|
|
462
|
+
* Once per process: the registry outlives every runtime.
|
|
463
|
+
*/
|
|
464
|
+
static void registerReconstructor() {
|
|
465
|
+
static std::once_flag registryFlag;
|
|
466
|
+
std::call_once(registryFlag, []() {
|
|
467
|
+
NativeObjectRegistry::getInstance().registerClass(
|
|
468
|
+
Derived::CLASS_NAME,
|
|
469
|
+
[](jsi::Runtime &rt,
|
|
470
|
+
std::shared_ptr<jsi::NativeState> state) -> jsi::Value {
|
|
471
|
+
auto instance = std::dynamic_pointer_cast<Derived>(state);
|
|
472
|
+
if (instance == nullptr) {
|
|
473
|
+
throw jsi::JSError(rt, "Invalid boxed native object state");
|
|
474
|
+
}
|
|
475
|
+
// The constructor makes `instanceof` work on this runtime too
|
|
476
|
+
// (a no-op once it is installed there).
|
|
477
|
+
Derived::installConstructor(rt);
|
|
478
|
+
// Unboxing hands back the wrapper cached for the target runtime,
|
|
479
|
+
// creating it on first use. Keep the runtime the object was
|
|
480
|
+
// originally created on: async native code (GPUDevice error/lost
|
|
481
|
+
// events, GPUAdapter::requestDevice) delivers into the creation
|
|
482
|
+
// runtime, and rebinding it to a worklet runtime would invoke
|
|
483
|
+
// main-runtime jsi::Functions on the wrong runtime and thread.
|
|
484
|
+
auto *originalRuntime = instance->getCreationRuntime();
|
|
485
|
+
auto value = Derived::create(rt, instance);
|
|
486
|
+
if (originalRuntime != nullptr) {
|
|
487
|
+
instance->setCreationRuntime(originalRuntime);
|
|
488
|
+
}
|
|
489
|
+
return value;
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
484
494
|
/**
|
|
485
495
|
* Returns the JS wrapper of `instance` on `runtime`, creating it on first
|
|
486
496
|
* use.
|
|
@@ -243,6 +243,12 @@ public:
|
|
|
243
243
|
// safely release their own reference immediately after.
|
|
244
244
|
virtual VideoFrameHandle wrapNativeBuffer(void *pointer) = 0;
|
|
245
245
|
|
|
246
|
+
// Retain a native window obtained from another library, to present to it
|
|
247
|
+
// (an ANativeWindow* on Android, a CAMetalLayer* on Apple platforms), and
|
|
248
|
+
// return the releaser that balances that reference once the surface is done
|
|
249
|
+
// with it. The releaser can run on any thread.
|
|
250
|
+
virtual std::function<void(void *)> retainNativeWindow(void *window) = 0;
|
|
251
|
+
|
|
246
252
|
// Write a small procedurally-generated test video to a temporary location
|
|
247
253
|
// and return its absolute path. Lets the SharedTextureMemory example play
|
|
248
254
|
// a real decoded video without bundling an asset.
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
#include "GPUTextureView.h"
|
|
42
42
|
#include "GPUUncapturedErrorEvent.h"
|
|
43
43
|
#include "GPUValidationError.h"
|
|
44
|
+
#include "ImageBitmap.h"
|
|
44
45
|
#include "VideoFrame.h"
|
|
45
46
|
#include "VideoPlayer.h"
|
|
46
47
|
|
|
@@ -78,11 +79,6 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
78
79
|
std::make_shared<RNWebGPU>(gpu, _platformContext, _jsCallInvoker);
|
|
79
80
|
_gpu = gpu->get();
|
|
80
81
|
|
|
81
|
-
// RNWebGPU needs its brand registered in NativeObjectRegistry so the boxing
|
|
82
|
-
// path can install the prototype on worklet runtimes. installConstructor
|
|
83
|
-
// does that registration but also sets globalThis.RNWebGPU = ctor, so we
|
|
84
|
-
// call it FIRST and then overwrite the global with the actual instance.
|
|
85
|
-
RNWebGPU::installConstructor(*_jsRuntime);
|
|
86
82
|
_jsRuntime->global().setProperty(*_jsRuntime, "RNWebGPU",
|
|
87
83
|
RNWebGPU::create(*_jsRuntime, rnWebGPU));
|
|
88
84
|
|
|
@@ -144,6 +140,7 @@ void RNWebGPUManager::installConstructors(jsi::Runtime &runtime) {
|
|
|
144
140
|
GPUSupportedLimits::installConstructor(runtime);
|
|
145
141
|
GPUTexture::installConstructor(runtime);
|
|
146
142
|
GPUTextureView::installConstructor(runtime);
|
|
143
|
+
ImageBitmap::installConstructor(runtime);
|
|
147
144
|
VideoFrame::installConstructor(runtime);
|
|
148
145
|
VideoPlayer::installConstructor(runtime);
|
|
149
146
|
}
|
|
@@ -170,9 +167,9 @@ void RNWebGPUManager::flushPendingSurfaceTransition(
|
|
|
170
167
|
}
|
|
171
168
|
|
|
172
169
|
RNWebGPUManager::~RNWebGPUManager() {
|
|
173
|
-
// Drop
|
|
174
|
-
//
|
|
175
|
-
//
|
|
170
|
+
// Drop the canvas registry entries of this runtime's bundle: its canvases
|
|
171
|
+
// never run their JS cleanup, and their ids are not handed out again (see
|
|
172
|
+
// SurfaceRegistry::allocateContextId).
|
|
176
173
|
SurfaceRegistry::getInstance().clear();
|
|
177
174
|
if (PlatformContext::current() == _platformContext) {
|
|
178
175
|
PlatformContext::setCurrent(nullptr);
|