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
|
@@ -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
|
|
|
@@ -104,6 +104,7 @@ namespace rnwgpu {
|
|
|
104
104
|
V(AdapterPropertiesWGPU, "adapter-properties-wgpu") \
|
|
105
105
|
V(SharedBufferMemoryFromWindowsHandle, \
|
|
106
106
|
"shared-buffer-memory-from-windows-handle") \
|
|
107
|
+
V(SharedBufferMemoryHostPointer, "shared-buffer-memory-host-pointer") \
|
|
107
108
|
V(SharedTextureMemoryD3D12Resource, "shared-texture-memory-d3d12-resource") \
|
|
108
109
|
V(ChromiumExperimentalSamplingResourceTable, \
|
|
109
110
|
"chromium-experimental-sampling-resource-table") \
|
|
@@ -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",
|
|
@@ -21,8 +21,8 @@ namespace jsi = facebook::jsi;
|
|
|
21
21
|
class VideoFrame : public NativeObject<VideoFrame> {
|
|
22
22
|
public:
|
|
23
23
|
// JS-facing brand. installConstructor() exposes this as
|
|
24
|
-
// globalThis[CLASS_NAME] and
|
|
25
|
-
//
|
|
24
|
+
// globalThis[CLASS_NAME], and the NativeObjectRegistry / worklet serializer
|
|
25
|
+
// are keyed off it, so it must NOT be "VideoFrame": that would install our
|
|
26
26
|
// constructor over the WebCodecs `VideoFrame` global and shadow it at
|
|
27
27
|
// runtime. "NativeVideoFrame" matches the public TypeScript type (see
|
|
28
28
|
// src/types.ts) and keeps the two namespaces distinct. The C++ class stays
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#include "webgpu/webgpu_cpp.h"
|
|
7
7
|
|
|
8
|
+
#include "CanvasColorSpace.h"
|
|
8
9
|
#include "JSIConverter.h"
|
|
9
10
|
|
|
10
11
|
#include "GPUDevice.h"
|
|
@@ -20,6 +21,7 @@ struct GPUCanvasConfiguration {
|
|
|
20
21
|
std::optional<std::vector<wgpu::TextureFormat>>
|
|
21
22
|
viewFormats; // Iterable<GPUTextureFormat>
|
|
22
23
|
wgpu::CompositeAlphaMode alphaMode = wgpu::CompositeAlphaMode::Opaque;
|
|
24
|
+
CanvasColorSpace colorSpace = CanvasColorSpace::SRGB;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
} // namespace rnwgpu
|
|
@@ -63,6 +65,22 @@ struct JSIConverter<std::shared_ptr<rnwgpu::GPUCanvasConfiguration>> {
|
|
|
63
65
|
result->alphaMode = wgpu::CompositeAlphaMode::Premultiplied;
|
|
64
66
|
}
|
|
65
67
|
}
|
|
68
|
+
if (value.hasProperty(runtime, "colorSpace")) {
|
|
69
|
+
auto prop = value.getProperty(runtime, "colorSpace");
|
|
70
|
+
if (!prop.isUndefined()) {
|
|
71
|
+
auto name = prop.asString(runtime).utf8(runtime);
|
|
72
|
+
if (name == "display-p3") {
|
|
73
|
+
result->colorSpace = CanvasColorSpace::DisplayP3;
|
|
74
|
+
} else if (name != "srgb") {
|
|
75
|
+
throw jsi::JSError(
|
|
76
|
+
runtime, "Failed to execute 'configure' on 'GPUCanvasContext': "
|
|
77
|
+
"The provided value '" +
|
|
78
|
+
name +
|
|
79
|
+
"' is not a valid enum value of type "
|
|
80
|
+
"PredefinedColorSpace.");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
66
84
|
}
|
|
67
85
|
|
|
68
86
|
return result;
|