react-native-webgpu 0.17.0 → 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/apple/ApplePlatformContext.h +2 -0
- package/apple/ApplePlatformContext.mm +12 -0
- package/apple/MetalLayerColorSpace.mm +20 -8
- package/cpp/rnwgpu/CanvasColorSpace.h +9 -0
- package/cpp/rnwgpu/PlatformContext.h +6 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +3 -3
- 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/RNWebGPU.h +63 -5
- 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 +3 -4
- package/lib/commonjs/Canvas.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/module/Canvas.js +3 -4
- package/lib/module/Canvas.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/typescript/lib/commonjs/Canvas.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/module/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/module/importDevice.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.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/package.json +1 -1
- package/src/Canvas.tsx +3 -4
- package/src/WebPolyfillGPUModule.ts +3 -0
- package/src/importDevice.ts +3 -2
- package/src/index.tsx +12 -1
|
@@ -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");
|
|
@@ -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
|
|
@@ -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.
|
|
@@ -167,9 +167,9 @@ void RNWebGPUManager::flushPendingSurfaceTransition(
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
RNWebGPUManager::~RNWebGPUManager() {
|
|
170
|
-
// Drop
|
|
171
|
-
//
|
|
172
|
-
//
|
|
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).
|
|
173
173
|
SurfaceRegistry::getInstance().clear();
|
|
174
174
|
if (PlatformContext::current() == _platformContext) {
|
|
175
175
|
PlatformContext::setCurrent(nullptr);
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
#include "webgpu/webgpu_cpp.h"
|
|
21
21
|
|
|
22
|
+
#include "CanvasColorSpace.h"
|
|
23
|
+
#include "WebGPURuntime.h"
|
|
24
|
+
|
|
22
25
|
#if defined(__ANDROID__)
|
|
23
26
|
#include <android/hardware_buffer.h>
|
|
24
27
|
#include <android/log.h>
|
|
@@ -36,9 +39,11 @@ namespace rnwgpu {
|
|
|
36
39
|
|
|
37
40
|
#ifdef __APPLE__
|
|
38
41
|
// Tags the CAMetalLayer with the colorspace matching the configured texture
|
|
39
|
-
// format. Implemented in
|
|
42
|
+
// format and the canvas's colorSpace. Implemented in
|
|
43
|
+
// apple/MetalLayerColorSpace.mm.
|
|
40
44
|
void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
41
|
-
wgpu::TextureFormat format
|
|
45
|
+
wgpu::TextureFormat format,
|
|
46
|
+
CanvasColorSpace colorSpace);
|
|
42
47
|
#endif
|
|
43
48
|
|
|
44
49
|
struct NativeInfo {
|
|
@@ -123,12 +128,13 @@ struct AHBPool {
|
|
|
123
128
|
// Ownership & threading model:
|
|
124
129
|
// - A registry entry is created on first use (by whichever of JS/native gets
|
|
125
130
|
// there first) and lives exactly as long as its JS Canvas: contextIds are
|
|
126
|
-
// never reused
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// entry; it only
|
|
131
|
+
// never reused, not even across a reload (see allocateContextId). It is
|
|
132
|
+
// removed by the native view's teardown (MetalView dealloc /
|
|
133
|
+
// WebGPUViewManager.onDropViewInstance) when a surface is attached, or by
|
|
134
|
+
// RNWebGPU.destroyContext (the Canvas unmount cleanup) when none ever was —
|
|
135
|
+
// see RNWebGPU::destroyContext for why the split. Surface destruction alone
|
|
136
|
+
// (backgrounding, TextureView teardown) never removes an entry; it only
|
|
137
|
+
// detaches the surface.
|
|
132
138
|
// - Attaching a surface is LATCHED: the UI thread stores it as pending
|
|
133
139
|
// (attachSurface) and it is adopted at the next frame boundary — start of
|
|
134
140
|
// getCurrentTexture or end of presentFrame — on whichever thread renders
|
|
@@ -147,8 +153,20 @@ struct AHBPool {
|
|
|
147
153
|
// native surface exists.
|
|
148
154
|
class SurfaceInfo {
|
|
149
155
|
public:
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
// releaseSurfaceOnUnconfigure is for a surface on a window the caller
|
|
157
|
+
// handed in (see SurfaceRegistry::makeUnregisteredSurfaceInfo), which no
|
|
158
|
+
// view attaches or detaches: unconfigure() then destroys the Dawn surface
|
|
159
|
+
// rather than only unconfiguring it, and configure() makes a new one for
|
|
160
|
+
// the same window. On Android, a window keeps the buffers it handed to the
|
|
161
|
+
// swapchain until the VkSurfaceKHR is destroyed, which Surface::Unconfigure
|
|
162
|
+
// does not do (it parks the swapchain for the next Configure): a MediaCodec
|
|
163
|
+
// input surface would otherwise keep them until the context is garbage
|
|
164
|
+
// collected. The window itself is retained until the SurfaceInfo is
|
|
165
|
+
// destroyed.
|
|
166
|
+
SurfaceInfo(wgpu::Instance gpu, int width, int height,
|
|
167
|
+
bool releaseSurfaceOnUnconfigure = false)
|
|
168
|
+
: _gpu(std::move(gpu)), _width(width), _height(height),
|
|
169
|
+
_releaseSurfaceOnUnconfigure(releaseSurfaceOnUnconfigure) {}
|
|
152
170
|
|
|
153
171
|
~SurfaceInfo() {
|
|
154
172
|
#if defined(__ANDROID__)
|
|
@@ -279,7 +297,7 @@ public:
|
|
|
279
297
|
}
|
|
280
298
|
configureSurfaceLocked(config);
|
|
281
299
|
#ifdef __APPLE__
|
|
282
|
-
applyCAMetalLayerColorSpace(_nativeSurface, _config.format);
|
|
300
|
+
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace);
|
|
283
301
|
#endif
|
|
284
302
|
if (blit) {
|
|
285
303
|
presentBlit = blitOffscreenToSurfaceLocked();
|
|
@@ -316,10 +334,12 @@ public:
|
|
|
316
334
|
// -------------------------------------------------------
|
|
317
335
|
|
|
318
336
|
void configure(wgpu::SurfaceConfiguration &newConfig,
|
|
319
|
-
std::vector<wgpu::TextureFormat> viewFormats
|
|
337
|
+
std::vector<wgpu::TextureFormat> viewFormats,
|
|
338
|
+
CanvasColorSpace colorSpace = CanvasColorSpace::SRGB) {
|
|
320
339
|
applyPendingAttach(/* supersedeInFlightFrame = */ true);
|
|
321
340
|
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
322
341
|
_viewFormats = std::move(viewFormats);
|
|
342
|
+
_colorSpace = colorSpace;
|
|
323
343
|
_config = newConfig;
|
|
324
344
|
// The caller's viewFormats storage dies with the call; point the stored
|
|
325
345
|
// configuration at our own copy.
|
|
@@ -346,6 +366,10 @@ public:
|
|
|
346
366
|
_poolUnsupported = false;
|
|
347
367
|
}
|
|
348
368
|
#endif
|
|
369
|
+
if (_releaseSurfaceOnUnconfigure && !_surface && _nativeSurface) {
|
|
370
|
+
// unconfigure() destroyed the surface; make a new one for the window.
|
|
371
|
+
recreateSurfaceLocked();
|
|
372
|
+
}
|
|
349
373
|
_configureLocked();
|
|
350
374
|
}
|
|
351
375
|
|
|
@@ -365,10 +389,22 @@ public:
|
|
|
365
389
|
void unconfigure() {
|
|
366
390
|
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
367
391
|
if (_surface) {
|
|
368
|
-
|
|
392
|
+
if (_releaseSurfaceOnUnconfigure) {
|
|
393
|
+
// Destroy the surface, and so its swapchain, while their device is
|
|
394
|
+
// still alive. The window is kept: configure() makes a new surface
|
|
395
|
+
// for it (see the constructor).
|
|
396
|
+
_surface = nullptr;
|
|
397
|
+
#if defined(__ANDROID__)
|
|
398
|
+
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
399
|
+
_surfaceDevice = nullptr;
|
|
400
|
+
#endif
|
|
401
|
+
} else {
|
|
402
|
+
_surface.Unconfigure();
|
|
403
|
+
}
|
|
369
404
|
}
|
|
370
405
|
_texture = nullptr;
|
|
371
406
|
_config = {};
|
|
407
|
+
_colorSpace = CanvasColorSpace::SRGB;
|
|
372
408
|
_viewFormats.clear();
|
|
373
409
|
_acquiredFromSurface = false;
|
|
374
410
|
_frameEpoch++;
|
|
@@ -875,7 +911,7 @@ private:
|
|
|
875
911
|
if (_surface) {
|
|
876
912
|
configureSurfaceLocked(_config);
|
|
877
913
|
#ifdef __APPLE__
|
|
878
|
-
applyCAMetalLayerColorSpace(_nativeSurface, _config.format);
|
|
914
|
+
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace);
|
|
879
915
|
#endif
|
|
880
916
|
} else {
|
|
881
917
|
_texture = createOffscreenTextureLocked();
|
|
@@ -906,26 +942,24 @@ private:
|
|
|
906
942
|
#endif
|
|
907
943
|
}
|
|
908
944
|
|
|
909
|
-
#if defined(__ANDROID__)
|
|
910
|
-
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
911
945
|
// Replace _surface with a new, unconfigured surface for the same native
|
|
912
|
-
// window
|
|
913
|
-
//
|
|
946
|
+
// window: after unconfigure() destroyed it (see the constructor), or on
|
|
947
|
+
// Android for DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE, where
|
|
948
|
+
// dropping the old one runs ~Surface, which detaches both the active and
|
|
949
|
+
// the recycled swapchain against their still-alive device.
|
|
914
950
|
void recreateSurfaceLocked() {
|
|
915
951
|
_surface = nullptr;
|
|
952
|
+
#if defined(__ANDROID__)
|
|
953
|
+
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
916
954
|
_surfaceDevice = nullptr;
|
|
955
|
+
#endif
|
|
917
956
|
// A frame acquired from the old surface can no longer be presented.
|
|
918
957
|
_acquiredFromSurface = false;
|
|
919
958
|
_frameEpoch++;
|
|
920
959
|
if (_nativeSurface) {
|
|
921
|
-
|
|
922
|
-
source.window = _nativeSurface;
|
|
923
|
-
wgpu::SurfaceDescriptor descriptor;
|
|
924
|
-
descriptor.nextInChain = &source;
|
|
925
|
-
_surface = _gpu.CreateSurface(&descriptor);
|
|
960
|
+
_surface = WebGPURuntime::makeSurface(_gpu, _nativeSurface);
|
|
926
961
|
}
|
|
927
962
|
}
|
|
928
|
-
#endif
|
|
929
963
|
|
|
930
964
|
#if defined(__ANDROID__)
|
|
931
965
|
// Drop every pool generation and the ready / in-flight slot bookkeeping.
|
|
@@ -1241,12 +1275,15 @@ private:
|
|
|
1241
1275
|
// that _config.viewFormats points at.
|
|
1242
1276
|
wgpu::SurfaceConfiguration _config;
|
|
1243
1277
|
std::vector<wgpu::TextureFormat> _viewFormats;
|
|
1278
|
+
CanvasColorSpace _colorSpace = CanvasColorSpace::SRGB;
|
|
1244
1279
|
// Keeps the Dawn instance alive for as long as any canvas exists.
|
|
1245
1280
|
wgpu::Instance _gpu;
|
|
1246
1281
|
// Native view size in dp (surfaced as clientWidth/clientHeight on the JS
|
|
1247
1282
|
// canvas).
|
|
1248
1283
|
int _width;
|
|
1249
1284
|
int _height;
|
|
1285
|
+
// See the constructor.
|
|
1286
|
+
bool _releaseSurfaceOnUnconfigure = false;
|
|
1250
1287
|
|
|
1251
1288
|
#if defined(__ANDROID__)
|
|
1252
1289
|
// Pool state. Guarded by _poolMutex (never nested under _mutex while
|
|
@@ -1296,6 +1333,15 @@ public:
|
|
|
1296
1333
|
SurfaceRegistry(const SurfaceRegistry &) = delete;
|
|
1297
1334
|
SurfaceRegistry &operator=(const SurfaceRegistry &) = delete;
|
|
1298
1335
|
|
|
1336
|
+
// The id of a Canvas about to mount. Ids are handed out here, once for the
|
|
1337
|
+
// life of the process, rather than counted by each JS runtime from the
|
|
1338
|
+
// same start: this registry outlives a reload, and the views of the bundle
|
|
1339
|
+
// being replaced are torn down (removeSurfaceInfo) on the UI thread with
|
|
1340
|
+
// no ordering against the new bundle's first canvases. An id counted from
|
|
1341
|
+
// the same start as its predecessor would let the predecessor's teardown
|
|
1342
|
+
// detach and erase the entry of a live canvas.
|
|
1343
|
+
int allocateContextId() { return _nextContextId.fetch_add(1); }
|
|
1344
|
+
|
|
1299
1345
|
std::shared_ptr<SurfaceInfo> getSurfaceInfo(int id) {
|
|
1300
1346
|
std::shared_lock<std::shared_mutex> lock(_mutex);
|
|
1301
1347
|
auto it = _registry.find(id);
|
|
@@ -1357,6 +1403,19 @@ public:
|
|
|
1357
1403
|
_registry.erase(it);
|
|
1358
1404
|
}
|
|
1359
1405
|
|
|
1406
|
+
// A SurfaceInfo that is not in the registry, for a context presenting to a
|
|
1407
|
+
// native window of the caller's (see
|
|
1408
|
+
// RNWebGPU::MakeWebGPUCanvasContextFromNativeWindow): the context owns it,
|
|
1409
|
+
// no view attaches to it, and it releases its surface on unconfigure (see
|
|
1410
|
+
// the SurfaceInfo constructor). It is still tracked for
|
|
1411
|
+
// releaseSurfacesForDevice.
|
|
1412
|
+
std::shared_ptr<SurfaceInfo>
|
|
1413
|
+
makeUnregisteredSurfaceInfo(wgpu::Instance gpu, int width, int height) {
|
|
1414
|
+
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
1415
|
+
return makeSurfaceInfoLocked(std::move(gpu), width, height,
|
|
1416
|
+
/* releaseSurfaceOnUnconfigure = */ true);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1360
1419
|
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
1361
1420
|
// See SurfaceInfo::releaseSurfaceForDevice. Lock order is registry ->
|
|
1362
1421
|
// SurfaceInfo, matching every other path.
|
|
@@ -1382,8 +1441,9 @@ public:
|
|
|
1382
1441
|
}
|
|
1383
1442
|
|
|
1384
1443
|
// Drops all entries. Called when the RN instance tears down (dev reload):
|
|
1385
|
-
//
|
|
1386
|
-
//
|
|
1444
|
+
// the canvases of the old bundle never run their JS cleanup, and nothing
|
|
1445
|
+
// reaches their ids again (see allocateContextId), so the entries would
|
|
1446
|
+
// otherwise stay for the life of the process.
|
|
1387
1447
|
void clear() {
|
|
1388
1448
|
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
1389
1449
|
_registry.clear();
|
|
@@ -1410,15 +1470,25 @@ private:
|
|
|
1410
1470
|
_retired.erase(retired);
|
|
1411
1471
|
}
|
|
1412
1472
|
if (!info) {
|
|
1413
|
-
info = std::
|
|
1414
|
-
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
1415
|
-
pruneAllSurfacesLocked();
|
|
1416
|
-
_allSurfaces.push_back(info);
|
|
1473
|
+
info = makeSurfaceInfoLocked(std::move(gpu), width, height);
|
|
1417
1474
|
}
|
|
1418
1475
|
_registry[id] = info;
|
|
1419
1476
|
return info;
|
|
1420
1477
|
}
|
|
1421
1478
|
|
|
1479
|
+
// Every SurfaceInfo is made here, so releaseSurfacesForDevice reaches it
|
|
1480
|
+
// whether or not it is in _registry.
|
|
1481
|
+
std::shared_ptr<SurfaceInfo>
|
|
1482
|
+
makeSurfaceInfoLocked(wgpu::Instance gpu, int width, int height,
|
|
1483
|
+
bool releaseSurfaceOnUnconfigure = false) {
|
|
1484
|
+
auto info = std::make_shared<SurfaceInfo>(std::move(gpu), width, height,
|
|
1485
|
+
releaseSurfaceOnUnconfigure);
|
|
1486
|
+
// DAWN_WORKAROUND_DEVICE_DESTROY_BEFORE_SURFACE_RELEASE
|
|
1487
|
+
pruneAllSurfacesLocked();
|
|
1488
|
+
_allSurfaces.push_back(info);
|
|
1489
|
+
return info;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1422
1492
|
void pruneRetiredLocked() {
|
|
1423
1493
|
for (auto it = _retired.begin(); it != _retired.end();) {
|
|
1424
1494
|
it = it->second.expired() ? _retired.erase(it) : ++it;
|
|
@@ -1436,6 +1506,7 @@ private:
|
|
|
1436
1506
|
}
|
|
1437
1507
|
|
|
1438
1508
|
mutable std::shared_mutex _mutex;
|
|
1509
|
+
std::atomic<int> _nextContextId{1};
|
|
1439
1510
|
std::unordered_map<int, std::shared_ptr<SurfaceInfo>> _registry;
|
|
1440
1511
|
// The entries JS retired while a GPUCanvasContext still held their
|
|
1441
1512
|
// SurfaceInfo (see removeSurfaceInfoIfDetached). Non-owning; pruned on
|
|
@@ -9,18 +9,32 @@ namespace rnwgpu {
|
|
|
9
9
|
namespace {
|
|
10
10
|
|
|
11
11
|
wgpu::Instance createInstance() {
|
|
12
|
-
// If a Graphite build of react-native-skia is loaded in this
|
|
12
|
+
// If a Graphite build of react-native-skia is already loaded in this
|
|
13
13
|
// process, adopt its wgpu::Instance instead of creating our own, so
|
|
14
|
-
// Graphite's device and every JS-created device live on one instance.
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
14
|
+
// Graphite's device and every JS-created device live on one instance.
|
|
15
|
+
// Discovered at runtime via dlsym so there is no build-time coupling. The
|
|
16
|
+
// adoption is best-effort and decided once, when the instance is first
|
|
17
|
+
// used: on Android it only succeeds when librnskia.so is loaded before
|
|
18
|
+
// react-native-webgpu installs (see the RTLD_NOLOAD lookup below), so the
|
|
19
|
+
// native-module init order decides whether the instance is shared.
|
|
20
|
+
// RNWebGPU.importDevice does not depend on it: an imported device's async
|
|
21
|
+
// work is pumped on the instance that owns it (see async::RuntimeContext).
|
|
22
|
+
// Both packages must link the same single Dawn copy (they vendor the same
|
|
23
|
+
// artifact) for the handles they exchange to be meaningful.
|
|
21
24
|
using GetInstanceFn = WGPUInstance (*)();
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
void *getter = dlsym(RTLD_DEFAULT, "rnskia_getWGPUInstance");
|
|
26
|
+
#ifdef __ANDROID__
|
|
27
|
+
// System.loadLibrary loads librnskia.so with RTLD_LOCAL, so RTLD_DEFAULT
|
|
28
|
+
// does not search it. RTLD_NOLOAD returns its handle only if it is already
|
|
29
|
+
// loaded, so this never loads Skia.
|
|
30
|
+
if (getter == nullptr) {
|
|
31
|
+
if (void *skia = dlopen("librnskia.so", RTLD_NOW | RTLD_NOLOAD)) {
|
|
32
|
+
getter = dlsym(skia, "rnskia_getWGPUInstance");
|
|
33
|
+
dlclose(skia);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
#endif
|
|
37
|
+
if (auto getSkiaInstance = reinterpret_cast<GetInstanceFn>(getter)) {
|
|
24
38
|
if (WGPUInstance external = getSkiaInstance()) {
|
|
25
39
|
wgpuInstanceAddRef(external);
|
|
26
40
|
return wgpu::Instance::Acquire(external);
|
|
@@ -76,6 +90,11 @@ wgpu::Instance WebGPURuntime::instance() {
|
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
wgpu::Surface WebGPURuntime::makeSurface(void *nativeSurface) {
|
|
93
|
+
return makeSurface(instance(), nativeSurface);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
wgpu::Surface WebGPURuntime::makeSurface(const wgpu::Instance &gpu,
|
|
97
|
+
void *nativeSurface) {
|
|
79
98
|
#if defined(__ANDROID__)
|
|
80
99
|
wgpu::SurfaceSourceAndroidNativeWindow source;
|
|
81
100
|
source.window = nativeSurface;
|
|
@@ -85,7 +104,7 @@ wgpu::Surface WebGPURuntime::makeSurface(void *nativeSurface) {
|
|
|
85
104
|
#endif
|
|
86
105
|
wgpu::SurfaceDescriptor descriptor;
|
|
87
106
|
descriptor.nextInChain = &source;
|
|
88
|
-
return
|
|
107
|
+
return gpu.CreateSurface(&descriptor);
|
|
89
108
|
}
|
|
90
109
|
|
|
91
110
|
} // namespace rnwgpu
|
|
@@ -15,14 +15,19 @@ class WebGPURuntime {
|
|
|
15
15
|
public:
|
|
16
16
|
static WebGPURuntime &shared();
|
|
17
17
|
|
|
18
|
-
// The process-wide instance: Skia Graphite's when that is loaded
|
|
19
|
-
//
|
|
18
|
+
// The process-wide instance: Skia Graphite's when that is already loaded on
|
|
19
|
+
// first use, otherwise our own. Safe to call from any thread.
|
|
20
20
|
wgpu::Instance instance();
|
|
21
21
|
|
|
22
22
|
// Creates a surface on the shared instance for a CAMetalLayer (Apple) or an
|
|
23
23
|
// ANativeWindow (Android). Safe to call from any thread.
|
|
24
24
|
wgpu::Surface makeSurface(void *nativeSurface);
|
|
25
25
|
|
|
26
|
+
// The same on a given instance: a SurfaceInfo recreates its surface on the
|
|
27
|
+
// instance it was made with.
|
|
28
|
+
static wgpu::Surface makeSurface(const wgpu::Instance &gpu,
|
|
29
|
+
void *nativeSurface);
|
|
30
|
+
|
|
26
31
|
WebGPURuntime(const WebGPURuntime &) = delete;
|
|
27
32
|
WebGPURuntime &operator=(const WebGPURuntime &) = delete;
|
|
28
33
|
|
|
@@ -29,7 +29,8 @@ void GPUCanvasContext::configure(
|
|
|
29
29
|
surfaceConfiguration.alphaMode = configuration->alphaMode;
|
|
30
30
|
#endif
|
|
31
31
|
surfaceConfiguration.presentMode = wgpu::PresentMode::Fifo;
|
|
32
|
-
_surfaceInfo->configure(surfaceConfiguration, std::move(viewFormats)
|
|
32
|
+
_surfaceInfo->configure(surfaceConfiguration, std::move(viewFormats),
|
|
33
|
+
configuration->colorSpace);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
void GPUCanvasContext::unconfigure() { _surfaceInfo->unconfigure(); }
|
|
@@ -35,6 +35,16 @@ public:
|
|
|
35
35
|
registry.getSurfaceInfoOrCreate(contextId, _gpu->get(), width, height);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// A context presenting to a surface of its own rather than a view's, outside
|
|
39
|
+
// of the registry (see RNWebGPU::MakeWebGPUCanvasContextFromNativeWindow).
|
|
40
|
+
GPUCanvasContext(std::shared_ptr<GPU> gpu,
|
|
41
|
+
std::shared_ptr<SurfaceInfo> surfaceInfo, int width,
|
|
42
|
+
int height)
|
|
43
|
+
: NativeObject(CLASS_NAME), _surfaceInfo(std::move(surfaceInfo)),
|
|
44
|
+
_gpu(std::move(gpu)) {
|
|
45
|
+
_canvas = std::make_shared<Canvas>(nullptr, width, height);
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
public:
|
|
39
49
|
std::string getBrand() { return CLASS_NAME; }
|
|
40
50
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <cmath>
|
|
3
4
|
#include <memory>
|
|
4
5
|
#include <string>
|
|
5
6
|
#include <utility>
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
#include "PlatformContext.h"
|
|
19
20
|
#include "VideoFrame.h"
|
|
20
21
|
#include "VideoPlayer.h"
|
|
22
|
+
#include "WebGPURuntime.h"
|
|
21
23
|
|
|
22
24
|
#include <ReactCommon/CallInvoker.h>
|
|
23
25
|
|
|
@@ -83,13 +85,58 @@ public:
|
|
|
83
85
|
return ctx;
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
// A canvas context presenting to a native window that is not a WebGPU
|
|
89
|
+
// view, e.g. the input Surface of a MediaCodec encoder: an ANativeWindow*
|
|
90
|
+
// (Android) or a CAMetalLayer* (Apple), passed as a BigInt. Unlike a view's,
|
|
91
|
+
// the context owns its surface, outside of the SurfaceRegistry, and the
|
|
92
|
+
// caller drives it from one thread at a time. The context retains the
|
|
93
|
+
// window for as long as it lives; unconfigure() releases the surface (and
|
|
94
|
+
// on Android the buffers the window handed to it) and configure() makes a
|
|
95
|
+
// new one for the window, see the SurfaceInfo constructor.
|
|
96
|
+
std::shared_ptr<GPUCanvasContext>
|
|
97
|
+
MakeWebGPUCanvasContextFromNativeWindow(void *window, float width,
|
|
98
|
+
float height) {
|
|
99
|
+
if (window == nullptr) {
|
|
100
|
+
throw std::runtime_error(
|
|
101
|
+
"MakeWebGPUCanvasContextFromNativeWindow: expected a non-null "
|
|
102
|
+
"ANativeWindow (Android) or CAMetalLayer (Apple) pointer (BigInt)");
|
|
103
|
+
}
|
|
104
|
+
if (!(std::isfinite(width) && std::isfinite(height) && width > 0 &&
|
|
105
|
+
height > 0)) {
|
|
106
|
+
throw std::runtime_error(
|
|
107
|
+
"MakeWebGPUCanvasContextFromNativeWindow: expected a positive "
|
|
108
|
+
"width and height");
|
|
109
|
+
}
|
|
110
|
+
auto &runtime = WebGPURuntime::shared();
|
|
111
|
+
// Unlike a view's, this surface has no offscreen fallback worth having:
|
|
112
|
+
// nothing would ever show up, so fail here.
|
|
113
|
+
auto surface = runtime.makeSurface(window);
|
|
114
|
+
if (surface == nullptr) {
|
|
115
|
+
throw std::runtime_error(
|
|
116
|
+
"MakeWebGPUCanvasContextFromNativeWindow: could not create a surface "
|
|
117
|
+
"for the window; is it a valid ANativeWindow (Android) or "
|
|
118
|
+
"CAMetalLayer (Apple)?");
|
|
119
|
+
}
|
|
120
|
+
auto info = SurfaceRegistry::getInstance().makeUnregisteredSurfaceInfo(
|
|
121
|
+
runtime.instance(), static_cast<int>(width), static_cast<int>(height));
|
|
122
|
+
// The window is retained last, once nothing left here can throw: the
|
|
123
|
+
// SurfaceInfo owns the reference from here on. Adopted by the context's
|
|
124
|
+
// configure() (see SurfaceInfo::configure).
|
|
125
|
+
info->attachSurface(window, std::move(surface),
|
|
126
|
+
_platformContext->retainNativeWindow(window));
|
|
127
|
+
return std::make_shared<GPUCanvasContext>(_gpu, std::move(info), width,
|
|
128
|
+
height);
|
|
129
|
+
}
|
|
130
|
+
|
|
86
131
|
// Wrap an externally created WGPUDevice (passed as a BigInt pointer, e.g.
|
|
87
132
|
// Skia's Graphite device from Skia.getNativeDevice()) in a GPUDevice.
|
|
88
133
|
// AddRefs the handle, so the original owner keeps its reference. Only sound
|
|
89
|
-
// because the process links a single Dawn copy
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
134
|
+
// because the process links a single Dawn copy. The device's async
|
|
135
|
+
// callbacks are pumped on the instance that owns it (RuntimeContext is per
|
|
136
|
+
// runtime and per instance), which is not necessarily the shared one:
|
|
137
|
+
// WebGPURuntime adopts Skia's instance only when Skia's getter is reachable
|
|
138
|
+
// as the instance is first used (on Android, only once librnskia.so is
|
|
139
|
+
// loaded), and other exporters have their own.
|
|
93
140
|
std::shared_ptr<GPUDevice> importDevice(jsi::Runtime &runtime,
|
|
94
141
|
void *pointer) {
|
|
95
142
|
if (pointer == nullptr) {
|
|
@@ -99,7 +146,8 @@ public:
|
|
|
99
146
|
auto raw = reinterpret_cast<WGPUDevice>(pointer);
|
|
100
147
|
wgpuDeviceAddRef(raw);
|
|
101
148
|
wgpu::Device device = wgpu::Device::Acquire(raw);
|
|
102
|
-
auto ctx = async::RuntimeContext::getOrCreate(
|
|
149
|
+
auto ctx = async::RuntimeContext::getOrCreate(
|
|
150
|
+
runtime, device.GetAdapter().GetInstance());
|
|
103
151
|
return std::make_shared<GPUDevice>(device, ctx, "Imported Device");
|
|
104
152
|
}
|
|
105
153
|
|
|
@@ -282,6 +330,12 @@ public:
|
|
|
282
330
|
return _platformContext->writeTestVideoFile();
|
|
283
331
|
}
|
|
284
332
|
|
|
333
|
+
// The id for a Canvas about to mount, unique for the life of the process
|
|
334
|
+
// (see SurfaceRegistry::allocateContextId).
|
|
335
|
+
int allocateContextId() {
|
|
336
|
+
return rnwgpu::SurfaceRegistry::getInstance().allocateContextId();
|
|
337
|
+
}
|
|
338
|
+
|
|
285
339
|
std::shared_ptr<Canvas> getNativeSurface(int contextId) {
|
|
286
340
|
auto ®istry = rnwgpu::SurfaceRegistry::getInstance();
|
|
287
341
|
auto info = registry.getSurfaceInfo(contextId);
|
|
@@ -319,12 +373,16 @@ public:
|
|
|
319
373
|
installGetter(runtime, prototype, "gpu", &RNWebGPU::getGPU);
|
|
320
374
|
installMethod(runtime, prototype, "createImageBitmap",
|
|
321
375
|
&RNWebGPU::createImageBitmap);
|
|
376
|
+
installMethod(runtime, prototype, "allocateContextId",
|
|
377
|
+
&RNWebGPU::allocateContextId);
|
|
322
378
|
installMethod(runtime, prototype, "getNativeSurface",
|
|
323
379
|
&RNWebGPU::getNativeSurface);
|
|
324
380
|
installMethod(runtime, prototype, "destroyContext",
|
|
325
381
|
&RNWebGPU::destroyContext);
|
|
326
382
|
installMethod(runtime, prototype, "MakeWebGPUCanvasContext",
|
|
327
383
|
&RNWebGPU::MakeWebGPUCanvasContext);
|
|
384
|
+
installMethod(runtime, prototype, "MakeWebGPUCanvasContextFromNativeWindow",
|
|
385
|
+
&RNWebGPU::MakeWebGPUCanvasContextFromNativeWindow);
|
|
328
386
|
installMethod(runtime, prototype, "loadVideoFrame",
|
|
329
387
|
&RNWebGPU::loadVideoFrame);
|
|
330
388
|
installMethod(runtime, prototype, "createTestVideoFrame",
|