react-native-webgpu 0.19.2 → 0.20.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/Package.swift +19 -7
- package/android/build.gradle +7 -0
- package/apple/ApplePlatformContext.h +6 -1
- package/apple/ApplePlatformContext.mm +7 -8
- package/apple/MetalLayerColorSpace.mm +17 -1
- package/apple/WebGPUModule.mm +15 -1
- package/cpp/jsi/Promise.cpp +4 -0
- package/cpp/jsi/Promise.h +3 -0
- package/cpp/rnwgpu/CanvasColorSpace.h +5 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +2 -4
- package/cpp/rnwgpu/SurfaceRegistry.h +16 -7
- package/cpp/rnwgpu/WebGPURuntime.cpp +2 -1
- package/cpp/rnwgpu/api/GPU.cpp +6 -6
- package/cpp/rnwgpu/api/GPU.h +1 -1
- package/cpp/rnwgpu/api/GPUAdapter.cpp +28 -2
- package/cpp/rnwgpu/api/GPUBuffer.cpp +5 -6
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +2 -1
- package/cpp/rnwgpu/api/GPUDevice.cpp +38 -47
- package/cpp/rnwgpu/api/GPUDevice.h +4 -5
- package/cpp/rnwgpu/api/GPUQueue.cpp +4 -2
- package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -2
- package/cpp/rnwgpu/api/RNWebGPU.h +9 -9
- package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +24 -0
- package/cpp/rnwgpu/async/AsyncTaskHandle.cpp +33 -45
- package/cpp/rnwgpu/async/AsyncTaskHandle.h +5 -6
- package/cpp/rnwgpu/async/RuntimeContext.cpp +321 -112
- package/cpp/rnwgpu/async/RuntimeContext.h +90 -82
- package/lib/commonjs/Canvas.js +1 -0
- package/lib/commonjs/Canvas.js.map +1 -1
- package/lib/commonjs/WebGPUViewNativeComponent.js.map +1 -1
- package/lib/commonjs/WebGPUViewNativeComponent.web.js.map +1 -1
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js +19 -1
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/commonjs/install.js +6 -5
- package/lib/commonjs/install.js.map +1 -1
- package/lib/module/Canvas.js +1 -0
- package/lib/module/Canvas.js.map +1 -1
- package/lib/module/WebGPUViewNativeComponent.js.map +1 -1
- package/lib/module/WebGPUViewNativeComponent.web.js.map +1 -1
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js +19 -1
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/module/install.js +6 -5
- package/lib/module/install.js.map +1 -1
- package/lib/typescript/lib/commonjs/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/install.d.ts +6 -5
- package/lib/typescript/lib/commonjs/install.d.ts.map +1 -1
- package/lib/typescript/lib/module/Canvas.d.ts.map +1 -1
- package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/lib/module/install.d.ts.map +1 -1
- package/lib/typescript/src/Canvas.d.ts.map +1 -1
- package/lib/typescript/src/WebGPUViewNativeComponent.d.ts +6 -7
- package/lib/typescript/src/WebGPUViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/WebGPUViewNativeComponent.web.d.ts +2 -3
- package/lib/typescript/src/WebGPUViewNativeComponent.web.d.ts.map +1 -1
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts +1 -0
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/install.d.ts +6 -5
- package/lib/typescript/src/install.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/Canvas.tsx +4 -2
- package/src/WebGPUViewNativeComponent.ts +6 -10
- package/src/WebGPUViewNativeComponent.web.ts +2 -3
- package/src/external/reanimated/registerWebGPUForReanimated.ts +21 -1
- package/src/install.ts +6 -5
package/Package.swift
CHANGED
|
@@ -56,21 +56,33 @@ let dawnVersion =
|
|
|
56
56
|
// changing either package's Dawn reset the package caches (File > Packages >
|
|
57
57
|
// Reset Package Caches) if this does not re-run.
|
|
58
58
|
//
|
|
59
|
-
// A react-native-skia that depends on react-native-webgpu-dawn itself must
|
|
60
|
-
// the same version as this package
|
|
61
|
-
//
|
|
62
|
-
//
|
|
59
|
+
// A react-native-skia that depends on react-native-webgpu-dawn itself must
|
|
60
|
+
// resolve the same version as this package: the copy nested under it when the
|
|
61
|
+
// package manager could not hoist it, else the one next to it (Node's
|
|
62
|
+
// resolution, which react-native-skia's podspec compares too). A workspace
|
|
63
|
+
// that resolves the package to one version for both (yarn `resolutions`, npm
|
|
64
|
+
// `overrides`) passes while react-native-skia's own pin lags behind a Dawn
|
|
65
|
+
// bump: the app then contains that one Dawn. Older Graphite builds
|
|
66
|
+
// (react-native-skia 3.0.x) vendor their own Dawn and record its release tag
|
|
67
|
+
// at libs/.dawn-version in their binary packages; this package's `dawn` field
|
|
63
68
|
// names the release that Dawn is interchangeable with.
|
|
64
69
|
if let skiaPath = nodePackage("react-native-skia"),
|
|
65
70
|
let skiaPackage = readJSON("\(skiaPath)/package.json"),
|
|
66
|
-
let
|
|
71
|
+
let skiaDawnPin = (skiaPackage["dependencies"] as? [String: String])?["react-native-webgpu-dawn"]
|
|
67
72
|
{
|
|
73
|
+
let skiaDawnPath = [
|
|
74
|
+
"\(skiaPath)/node_modules/react-native-webgpu-dawn",
|
|
75
|
+
"\(skiaPath)/../react-native-webgpu-dawn",
|
|
76
|
+
].first { FileManager.default.fileExists(atPath: "\($0)/package.json") }
|
|
77
|
+
let skiaDawn =
|
|
78
|
+
skiaDawnPath.flatMap { readJSON("\($0)/package.json")?["version"] as? String } ?? skiaDawnPin
|
|
68
79
|
if skiaDawn != dawnVersion {
|
|
69
80
|
fatalError(
|
|
70
81
|
"""
|
|
71
82
|
react-native-webgpu: Dawn version mismatch. This package links \
|
|
72
|
-
react-native-webgpu-dawn \(dawnVersion) but react-native-skia
|
|
73
|
-
\(skiaDawn). Align the two packages so the
|
|
83
|
+
react-native-webgpu-dawn \(dawnVersion) but react-native-skia resolves \
|
|
84
|
+
\(skiaDawn) (it depends on \(skiaDawnPin)). Align the two packages so the \
|
|
85
|
+
app contains exactly one Dawn.
|
|
74
86
|
""")
|
|
75
87
|
}
|
|
76
88
|
} else {
|
package/android/build.gradle
CHANGED
|
@@ -97,6 +97,13 @@ def supportsNamespace() {
|
|
|
97
97
|
android {
|
|
98
98
|
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
99
99
|
|
|
100
|
+
if (rootProject.hasProperty("ndkPath")) {
|
|
101
|
+
ndkPath = rootProject.ext.ndkPath
|
|
102
|
+
}
|
|
103
|
+
if (rootProject.hasProperty("ndkVersion")) {
|
|
104
|
+
ndkVersion = rootProject.ext.ndkVersion
|
|
105
|
+
}
|
|
106
|
+
|
|
100
107
|
defaultConfig {
|
|
101
108
|
minSdkVersion resolveMinSdkVersion()
|
|
102
109
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
@@ -13,8 +13,12 @@ public:
|
|
|
13
13
|
// UIView* / NSView* bridged to void*, or nullptr). Called on the main thread
|
|
14
14
|
// only. Provided by WebGPUModule from React Native's view registry.
|
|
15
15
|
using ViewLookup = std::function<void *(int)>;
|
|
16
|
+
// Returns React Native's RCTBlobManager (unretained, bridged to void*), or
|
|
17
|
+
// nullptr. Provided by WebGPUModule from React Native's module registry.
|
|
18
|
+
using BlobManagerLookup = std::function<void *()>;
|
|
16
19
|
|
|
17
|
-
explicit ApplePlatformContext(ViewLookup viewLookup
|
|
20
|
+
explicit ApplePlatformContext(ViewLookup viewLookup,
|
|
21
|
+
BlobManagerLookup blobManagerLookup);
|
|
18
22
|
~ApplePlatformContext() = default;
|
|
19
23
|
|
|
20
24
|
void snapshotView(const ViewSnapshotRequest &request,
|
|
@@ -52,6 +56,7 @@ public:
|
|
|
52
56
|
|
|
53
57
|
private:
|
|
54
58
|
ViewLookup _viewLookup;
|
|
59
|
+
BlobManagerLookup _blobManagerLookup;
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
} // namespace rnwgpu
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#import <CoreVideo/CoreVideo.h>
|
|
7
7
|
#import <ImageIO/ImageIO.h>
|
|
8
8
|
#import <React/RCTBlobManager.h>
|
|
9
|
-
#import <React/RCTBridge+Private.h>
|
|
10
9
|
#import <ReactCommon/RCTTurboModule.h>
|
|
11
10
|
|
|
12
11
|
#include "AppleVideoPlayer.h"
|
|
@@ -35,8 +34,10 @@ void checkIfUsingSimulatorWithAPIValidation() {
|
|
|
35
34
|
#endif
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
ApplePlatformContext::ApplePlatformContext(ViewLookup viewLookup
|
|
39
|
-
|
|
37
|
+
ApplePlatformContext::ApplePlatformContext(ViewLookup viewLookup,
|
|
38
|
+
BlobManagerLookup blobManagerLookup)
|
|
39
|
+
: _viewLookup(std::move(viewLookup)),
|
|
40
|
+
_blobManagerLookup(std::move(blobManagerLookup)) {
|
|
40
41
|
checkIfUsingSimulatorWithAPIValidation();
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -360,8 +361,7 @@ static std::span<const uint8_t> nsDataToSpan(NSData *data) {
|
|
|
360
361
|
|
|
361
362
|
ImageData ApplePlatformContext::createImageBitmap(std::string blobId,
|
|
362
363
|
double offset, double size) {
|
|
363
|
-
RCTBlobManager *blobManager =
|
|
364
|
-
[[RCTBridge currentBridge] moduleForClass:RCTBlobManager.class];
|
|
364
|
+
RCTBlobManager *blobManager = (__bridge RCTBlobManager *)_blobManagerLookup();
|
|
365
365
|
NSData *blobData =
|
|
366
366
|
[blobManager resolve:[NSString stringWithUTF8String:blobId.c_str()]
|
|
367
367
|
offset:(long)offset
|
|
@@ -378,9 +378,8 @@ void ApplePlatformContext::createImageBitmapAsync(
|
|
|
378
378
|
std::string blobId, double offset, double size,
|
|
379
379
|
std::function<void(ImageData)> onSuccess,
|
|
380
380
|
std::function<void(std::string)> onError) {
|
|
381
|
-
// Resolve blob on current thread
|
|
382
|
-
RCTBlobManager *blobManager =
|
|
383
|
-
[[RCTBridge currentBridge] moduleForClass:RCTBlobManager.class];
|
|
381
|
+
// Resolve blob on current thread
|
|
382
|
+
RCTBlobManager *blobManager = (__bridge RCTBlobManager *)_blobManagerLookup();
|
|
384
383
|
NSData *blobData =
|
|
385
384
|
[blobManager resolve:[NSString stringWithUTF8String:blobId.c_str()]
|
|
386
385
|
offset:(long)offset
|
|
@@ -19,9 +19,18 @@ namespace rnwgpu {
|
|
|
19
19
|
// "display-p3" the layer is tagged Display P3 instead, or its extended range
|
|
20
20
|
// variant on a float format, so the values reach a wide-gamut display without
|
|
21
21
|
// being converted from sRGB first.
|
|
22
|
+
//
|
|
23
|
+
// GPUCanvasConfiguration.toneMapping.mode says what happens to values above
|
|
24
|
+
// 1.0 on a float canvas. Core Animation keeps an extended colorspace inside
|
|
25
|
+
// the display's SDR range unless the layer asks for extended dynamic range
|
|
26
|
+
// content, so without it the values clip at white: that is "standard". For
|
|
27
|
+
// "extended" the layer opts in, and the values use the display's headroom
|
|
28
|
+
// (EDR) as they do in a browser. An 8-bit canvas cannot hold values above 1.0,
|
|
29
|
+
// so the flag only applies to a float format.
|
|
22
30
|
void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
23
31
|
wgpu::TextureFormat format,
|
|
24
|
-
CanvasColorSpace canvasColorSpace
|
|
32
|
+
CanvasColorSpace canvasColorSpace,
|
|
33
|
+
CanvasToneMappingMode toneMappingMode) {
|
|
25
34
|
CALayer *layer = (__bridge CALayer *)nativeSurface;
|
|
26
35
|
if (![layer isKindOfClass:[CAMetalLayer class]]) {
|
|
27
36
|
return;
|
|
@@ -43,6 +52,13 @@ void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
|
43
52
|
// 8-bit sRGB canvas.
|
|
44
53
|
metalLayer.colorspace = nil;
|
|
45
54
|
}
|
|
55
|
+
const bool extendedRange =
|
|
56
|
+
isFloat && toneMappingMode == CanvasToneMappingMode::Extended;
|
|
57
|
+
if (@available(iOS 16.0, tvOS 16.0, *)) {
|
|
58
|
+
if (metalLayer.wantsExtendedDynamicRangeContent != extendedRange) {
|
|
59
|
+
metalLayer.wantsExtendedDynamicRangeContent = extendedRange;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
46
62
|
// The change must be set synchronously so the first present already sees
|
|
47
63
|
// it, and it must reach the render server. On a non-main thread (RN JS or
|
|
48
64
|
// worklet runtime) the property lands in that thread's implicit
|
package/apple/WebGPUModule.mm
CHANGED
|
@@ -37,6 +37,10 @@ static std::shared_ptr<rnwgpu::RNWebGPUManager> webgpuManager;
|
|
|
37
37
|
// (Fabric component views included), which drawElementImageToTexture needs.
|
|
38
38
|
@synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED;
|
|
39
39
|
|
|
40
|
+
// Synthesize the module registry so the TurboModule infra injects it too. It
|
|
41
|
+
// reaches React Native's BlobModule in both Bridge and Bridgeless mode.
|
|
42
|
+
@synthesize moduleRegistry = _moduleRegistry;
|
|
43
|
+
|
|
40
44
|
+ (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager {
|
|
41
45
|
return webgpuManager;
|
|
42
46
|
}
|
|
@@ -93,8 +97,18 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
|
|
|
93
97
|
[strongSelf.viewRegistry_DEPRECATED viewForReactTag:@(tag)];
|
|
94
98
|
return (__bridge void *)view;
|
|
95
99
|
};
|
|
100
|
+
rnwgpu::ApplePlatformContext::BlobManagerLookup blobManagerLookup =
|
|
101
|
+
[weakSelf]() -> void * {
|
|
102
|
+
WebGPUModule *strongSelf = weakSelf;
|
|
103
|
+
if (strongSelf == nil) {
|
|
104
|
+
return nullptr;
|
|
105
|
+
}
|
|
106
|
+
return (__bridge void *)[strongSelf.moduleRegistry
|
|
107
|
+
moduleForName:"BlobModule"];
|
|
108
|
+
};
|
|
96
109
|
std::shared_ptr<rnwgpu::PlatformContext> platformContext =
|
|
97
|
-
std::make_shared<rnwgpu::ApplePlatformContext>(
|
|
110
|
+
std::make_shared<rnwgpu::ApplePlatformContext>(
|
|
111
|
+
std::move(viewLookup), std::move(blobManagerLookup));
|
|
98
112
|
webgpuManager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, jsInvoker,
|
|
99
113
|
platformContext);
|
|
100
114
|
return @true;
|
package/cpp/jsi/Promise.cpp
CHANGED
package/cpp/jsi/Promise.h
CHANGED
|
@@ -15,7 +15,10 @@ public:
|
|
|
15
15
|
Promise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter);
|
|
16
16
|
|
|
17
17
|
void resolve(jsi::Value&& result);
|
|
18
|
+
/** Rejects with a new Error carrying `error` as its message. */
|
|
18
19
|
void reject(std::string error);
|
|
20
|
+
/** Rejects with the JS error `error` carries, as thrown. */
|
|
21
|
+
void reject(const jsi::JSError& error);
|
|
19
22
|
|
|
20
23
|
public:
|
|
21
24
|
jsi::Runtime& runtime;
|
|
@@ -6,4 +6,9 @@ namespace rnwgpu {
|
|
|
6
6
|
// values are interpreted when it is composited.
|
|
7
7
|
enum class CanvasColorSpace { SRGB, DisplayP3 };
|
|
8
8
|
|
|
9
|
+
// GPUCanvasConfiguration.toneMapping.mode (GPUCanvasToneMappingMode): whether
|
|
10
|
+
// values above 1.0 on a float canvas are clamped to the display's SDR range
|
|
11
|
+
// ("standard", the default) or reach its extended range ("extended").
|
|
12
|
+
enum class CanvasToneMappingMode { Standard, Extended };
|
|
13
|
+
|
|
9
14
|
} // namespace rnwgpu
|
|
@@ -64,10 +64,8 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
64
64
|
: _jsRuntime(jsRuntime), _jsCallInvoker(jsCallInvoker),
|
|
65
65
|
_platformContext(platformContext) {
|
|
66
66
|
|
|
67
|
-
// Register the main runtime + its CallInvoker
|
|
68
|
-
//
|
|
69
|
-
// the JS thread without the ProcessEvents pump. Worklet-runtime devices have
|
|
70
|
-
// no invoker (best-effort; see README "Threading model").
|
|
67
|
+
// Register the main runtime + its CallInvoker: how async results reach the
|
|
68
|
+
// main runtime, and the way to the UI runtime (see async::RuntimeContext).
|
|
71
69
|
async::RuntimeContext::registerMainRuntime(_jsRuntime, _jsCallInvoker);
|
|
72
70
|
|
|
73
71
|
// Expose the platform context to API objects created later without a
|
|
@@ -39,11 +39,13 @@ namespace rnwgpu {
|
|
|
39
39
|
|
|
40
40
|
#ifdef __APPLE__
|
|
41
41
|
// Tags the CAMetalLayer with the colorspace matching the configured texture
|
|
42
|
-
// format and the canvas's colorSpace
|
|
42
|
+
// format and the canvas's colorSpace, and opts it into the display's extended
|
|
43
|
+
// dynamic range for an "extended" toneMapping mode. Implemented in
|
|
43
44
|
// apple/MetalLayerColorSpace.mm.
|
|
44
45
|
void applyCAMetalLayerColorSpace(void *nativeSurface,
|
|
45
46
|
wgpu::TextureFormat format,
|
|
46
|
-
CanvasColorSpace colorSpace
|
|
47
|
+
CanvasColorSpace colorSpace,
|
|
48
|
+
CanvasToneMappingMode toneMappingMode);
|
|
47
49
|
#endif
|
|
48
50
|
|
|
49
51
|
struct NativeInfo {
|
|
@@ -297,7 +299,8 @@ public:
|
|
|
297
299
|
}
|
|
298
300
|
configureSurfaceLocked(config);
|
|
299
301
|
#ifdef __APPLE__
|
|
300
|
-
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace
|
|
302
|
+
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace,
|
|
303
|
+
_toneMappingMode);
|
|
301
304
|
#endif
|
|
302
305
|
if (blit) {
|
|
303
306
|
presentBlit = blitOffscreenToSurfaceLocked();
|
|
@@ -333,13 +336,16 @@ public:
|
|
|
333
336
|
// --- Rendering thread
|
|
334
337
|
// -------------------------------------------------------
|
|
335
338
|
|
|
336
|
-
void configure(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
+
void configure(
|
|
340
|
+
wgpu::SurfaceConfiguration &newConfig,
|
|
341
|
+
std::vector<wgpu::TextureFormat> viewFormats,
|
|
342
|
+
CanvasColorSpace colorSpace = CanvasColorSpace::SRGB,
|
|
343
|
+
CanvasToneMappingMode toneMappingMode = CanvasToneMappingMode::Standard) {
|
|
339
344
|
applyPendingAttach(/* supersedeInFlightFrame = */ true);
|
|
340
345
|
std::unique_lock<std::shared_mutex> lock(_mutex);
|
|
341
346
|
_viewFormats = std::move(viewFormats);
|
|
342
347
|
_colorSpace = colorSpace;
|
|
348
|
+
_toneMappingMode = toneMappingMode;
|
|
343
349
|
_config = newConfig;
|
|
344
350
|
// The caller's viewFormats storage dies with the call; point the stored
|
|
345
351
|
// configuration at our own copy.
|
|
@@ -405,6 +411,7 @@ public:
|
|
|
405
411
|
_texture = nullptr;
|
|
406
412
|
_config = {};
|
|
407
413
|
_colorSpace = CanvasColorSpace::SRGB;
|
|
414
|
+
_toneMappingMode = CanvasToneMappingMode::Standard;
|
|
408
415
|
_viewFormats.clear();
|
|
409
416
|
_acquiredFromSurface = false;
|
|
410
417
|
_frameEpoch++;
|
|
@@ -911,7 +918,8 @@ private:
|
|
|
911
918
|
if (_surface) {
|
|
912
919
|
configureSurfaceLocked(_config);
|
|
913
920
|
#ifdef __APPLE__
|
|
914
|
-
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace
|
|
921
|
+
applyCAMetalLayerColorSpace(_nativeSurface, _config.format, _colorSpace,
|
|
922
|
+
_toneMappingMode);
|
|
915
923
|
#endif
|
|
916
924
|
} else {
|
|
917
925
|
_texture = createOffscreenTextureLocked();
|
|
@@ -1276,6 +1284,7 @@ private:
|
|
|
1276
1284
|
wgpu::SurfaceConfiguration _config;
|
|
1277
1285
|
std::vector<wgpu::TextureFormat> _viewFormats;
|
|
1278
1286
|
CanvasColorSpace _colorSpace = CanvasColorSpace::SRGB;
|
|
1287
|
+
CanvasToneMappingMode _toneMappingMode = CanvasToneMappingMode::Standard;
|
|
1279
1288
|
// Keeps the Dawn instance alive for as long as any canvas exists.
|
|
1280
1289
|
wgpu::Instance _gpu;
|
|
1281
1290
|
// Native view size in dp (surfaced as clientWidth/clientHeight on the JS
|
|
@@ -18,7 +18,8 @@ wgpu::Instance createInstance() {
|
|
|
18
18
|
// react-native-webgpu installs (see the RTLD_NOLOAD lookup below), so the
|
|
19
19
|
// native-module init order decides whether the instance is shared.
|
|
20
20
|
// RNWebGPU.importDevice does not depend on it: an imported device's async
|
|
21
|
-
// work is
|
|
21
|
+
// work is waited for on the instance that owns it (see
|
|
22
|
+
// async::RuntimeContext).
|
|
22
23
|
// Both packages must link the same single Dawn copy (they vendor the same
|
|
23
24
|
// artifact) for the handles they exchange to be meaningful.
|
|
24
25
|
using GetInstanceFn = WGPUInstance (*)();
|
package/cpp/rnwgpu/api/GPU.cpp
CHANGED
|
@@ -33,17 +33,17 @@ async::AsyncTaskHandle GPU::requestAdapter(
|
|
|
33
33
|
aOptions.backendType = kDefaultBackendType;
|
|
34
34
|
|
|
35
35
|
// Per-runtime context: async ops requested on this runtime resolve on this
|
|
36
|
-
// runtime's own thread (
|
|
36
|
+
// runtime's own thread (see RuntimeContext).
|
|
37
37
|
auto context = async::RuntimeContext::getOrCreate(runtime, _instance);
|
|
38
38
|
return context->postTask(
|
|
39
39
|
[this, aOptions,
|
|
40
40
|
context](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
41
41
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
42
|
-
_instance.RequestAdapter(
|
|
43
|
-
&aOptions, wgpu::CallbackMode::
|
|
44
|
-
[context, resolve,
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
return _instance.RequestAdapter(
|
|
43
|
+
&aOptions, wgpu::CallbackMode::WaitAnyOnly,
|
|
44
|
+
[context, resolve, reject](wgpu::RequestAdapterStatus status,
|
|
45
|
+
wgpu::Adapter adapter,
|
|
46
|
+
wgpu::StringView message) {
|
|
47
47
|
if (message.length) {
|
|
48
48
|
fprintf(stderr, "%s", message.data);
|
|
49
49
|
}
|
package/cpp/rnwgpu/api/GPU.h
CHANGED
|
@@ -33,7 +33,7 @@ public:
|
|
|
33
33
|
std::string getBrand() { return CLASS_NAME; }
|
|
34
34
|
|
|
35
35
|
// requestAdapter needs the calling runtime so each runtime gets its own
|
|
36
|
-
// RuntimeContext
|
|
36
|
+
// RuntimeContext, which settles the promise on that runtime's thread.
|
|
37
37
|
async::AsyncTaskHandle requestAdapter(
|
|
38
38
|
jsi::Runtime &runtime,
|
|
39
39
|
std::optional<std::shared_ptr<GPURequestAdapterOptions>> options);
|
|
@@ -271,6 +271,32 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
271
271
|
enabledToggles.push_back(name);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
+
#endif
|
|
275
|
+
#if defined(__APPLE__)
|
|
276
|
+
// iOS refuses GPU work from an app in the background, and Dawn loses
|
|
277
|
+
// the device over a refused command buffer. With this toggle Dawn
|
|
278
|
+
// holds the commits while the app is in the background, from
|
|
279
|
+
// UIApplication's own notifications, and commits them when it comes
|
|
280
|
+
// back: nothing is lost, nothing is refused, and a promise on the
|
|
281
|
+
// held work settles once the app is back (the same toggle Skia
|
|
282
|
+
// requests on the device it shares, see react-native-skia's
|
|
283
|
+
// DawnUtils::requestDevice). Dawn applies it on iOS and tvOS only, and
|
|
284
|
+
// a Dawn without it (react-native-webgpu-dawn before 157.1.0) drops
|
|
285
|
+
// the name. The caller opts out through disabledToggles.
|
|
286
|
+
{
|
|
287
|
+
static const char *const kName =
|
|
288
|
+
"metal_suspend_submissions_in_background";
|
|
289
|
+
const auto named = [](const char *t) {
|
|
290
|
+
return std::strcmp(t, kName) == 0;
|
|
291
|
+
};
|
|
292
|
+
const bool explicitlyDisabled = std::any_of(
|
|
293
|
+
disabledToggles.begin(), disabledToggles.end(), named);
|
|
294
|
+
const bool alreadyEnabled =
|
|
295
|
+
std::any_of(enabledToggles.begin(), enabledToggles.end(), named);
|
|
296
|
+
if (!explicitlyDisabled && !alreadyEnabled) {
|
|
297
|
+
enabledToggles.push_back(kName);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
274
300
|
#endif
|
|
275
301
|
if (!enabledToggles.empty() || !disabledToggles.empty()) {
|
|
276
302
|
toggles.enabledToggleCount = enabledToggles.size();
|
|
@@ -279,8 +305,8 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
|
|
|
279
305
|
toggles.disabledToggles = disabledToggles.data();
|
|
280
306
|
deviceDesc.nextInChain = &toggles;
|
|
281
307
|
}
|
|
282
|
-
_instance.RequestDevice(
|
|
283
|
-
&deviceDesc, wgpu::CallbackMode::
|
|
308
|
+
return _instance.RequestDevice(
|
|
309
|
+
&deviceDesc, wgpu::CallbackMode::WaitAnyOnly,
|
|
284
310
|
[context, resolve, reject, label, creationRuntime,
|
|
285
311
|
deviceLostBinding](wgpu::RequestDeviceStatus status,
|
|
286
312
|
wgpu::Device device, wgpu::StringView message) {
|
|
@@ -55,18 +55,17 @@ async::AsyncTaskHandle GPUBuffer::mapAsync(jsi::Runtime &runtime,
|
|
|
55
55
|
// Post to the CALLING runtime's context, not the one captured at buffer
|
|
56
56
|
// creation (_async): the buffer may have been created on another runtime and
|
|
57
57
|
// boxed across (e.g. device created on the main JS runtime, mapAsync called
|
|
58
|
-
//
|
|
59
|
-
// must be settled from that runtime's own thread
|
|
60
|
-
//
|
|
61
|
-
// only safe for the runtime we are currently executing on.
|
|
58
|
+
// The returned Promise lives on the calling runtime, so it
|
|
59
|
+
// must be settled from that runtime's own thread, which its context knows
|
|
60
|
+
// how to reach (see RuntimeContext).
|
|
62
61
|
auto context =
|
|
63
62
|
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
64
63
|
return context->postTask(
|
|
65
64
|
[bufferHandle, mode, resolvedOffset,
|
|
66
65
|
rangeSize](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
67
66
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
68
|
-
bufferHandle.MapAsync(
|
|
69
|
-
mode, resolvedOffset, rangeSize, wgpu::CallbackMode::
|
|
67
|
+
return bufferHandle.MapAsync(
|
|
68
|
+
mode, resolvedOffset, rangeSize, wgpu::CallbackMode::WaitAnyOnly,
|
|
70
69
|
[resolve, reject](wgpu::MapAsyncStatus status,
|
|
71
70
|
wgpu::StringView message) {
|
|
72
71
|
switch (status) {
|
|
@@ -30,7 +30,8 @@ void GPUCanvasContext::configure(
|
|
|
30
30
|
#endif
|
|
31
31
|
surfaceConfiguration.presentMode = wgpu::PresentMode::Fifo;
|
|
32
32
|
_surfaceInfo->configure(surfaceConfiguration, std::move(viewFormats),
|
|
33
|
-
configuration->colorSpace
|
|
33
|
+
configuration->colorSpace,
|
|
34
|
+
configuration->toneMappingMode);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
void GPUCanvasContext::unconfigure() { _surfaceInfo->unconfigure(); }
|
|
@@ -68,41 +68,35 @@ void GPUDevice::notifyDeviceLost(wgpu::DeviceLostReason reason,
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
std::make_shared<std::vector<PendingLostPromise>>(std::move(toResolve));
|
|
86
|
-
invoker->invokeAsync([pending, info]() {
|
|
87
|
-
for (auto &entry : *pending) {
|
|
88
|
-
auto &runtime = *entry.runtime;
|
|
89
|
-
auto locked = entry.promise.lock(runtime);
|
|
71
|
+
// Settle each promise on the thread of the runtime that read it, through
|
|
72
|
+
// that runtime's context. The promises are held weakly: if the device graph
|
|
73
|
+
// was collected in the meantime there is nothing to do, since nobody could
|
|
74
|
+
// observe the resolution anyway. The shared_ptr is only there because
|
|
75
|
+
// jsi::WeakObject is move-only and std::function requires a copyable
|
|
76
|
+
// closure.
|
|
77
|
+
for (auto &entry : toResolve) {
|
|
78
|
+
auto context = entry.context.lock();
|
|
79
|
+
if (context == nullptr) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
auto pending = std::make_shared<jsi::WeakObject>(std::move(entry.promise));
|
|
83
|
+
context->post([pending, info](jsi::Runtime &runtime) {
|
|
84
|
+
auto locked = pending->lock(runtime);
|
|
90
85
|
if (!locked.isObject()) {
|
|
91
|
-
|
|
86
|
+
return;
|
|
92
87
|
}
|
|
93
88
|
auto promiseObj = locked.getObject(runtime);
|
|
94
89
|
auto resolveProp = promiseObj.getProperty(runtime, kLostResolveProp);
|
|
95
90
|
if (!resolveProp.isObject() ||
|
|
96
91
|
!resolveProp.getObject(runtime).isFunction(runtime)) {
|
|
97
|
-
|
|
92
|
+
return;
|
|
98
93
|
}
|
|
99
94
|
auto resolveFn = resolveProp.getObject(runtime).getFunction(runtime);
|
|
100
95
|
resolveFn.call(runtime,
|
|
101
96
|
JSIConverter<std::shared_ptr<GPUDeviceLostInfo>>::toJSI(
|
|
102
97
|
runtime, info));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
void GPUDevice::forceLossForTesting() {
|
|
@@ -534,8 +528,8 @@ async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
|
534
528
|
const async::AsyncTaskHandle::RejectFunction
|
|
535
529
|
&reject) {
|
|
536
530
|
(void)descriptor;
|
|
537
|
-
device.CreateComputePipelineAsync(
|
|
538
|
-
&desc, wgpu::CallbackMode::
|
|
531
|
+
return device.CreateComputePipelineAsync(
|
|
532
|
+
&desc, wgpu::CallbackMode::WaitAnyOnly,
|
|
539
533
|
[pipelineHolder, resolve,
|
|
540
534
|
reject](wgpu::CreatePipelineAsyncStatus status,
|
|
541
535
|
wgpu::ComputePipeline pipeline, wgpu::StringView msg) {
|
|
@@ -581,8 +575,8 @@ async::AsyncTaskHandle GPUDevice::createRenderPipelineAsync(
|
|
|
581
575
|
const async::AsyncTaskHandle::RejectFunction
|
|
582
576
|
&reject) {
|
|
583
577
|
(void)descriptor;
|
|
584
|
-
device.CreateRenderPipelineAsync(
|
|
585
|
-
&desc, wgpu::CallbackMode::
|
|
578
|
+
return device.CreateRenderPipelineAsync(
|
|
579
|
+
&desc, wgpu::CallbackMode::WaitAnyOnly,
|
|
586
580
|
[pipelineHolder, resolve,
|
|
587
581
|
reject](wgpu::CreatePipelineAsyncStatus status,
|
|
588
582
|
wgpu::RenderPipeline pipeline, wgpu::StringView msg) {
|
|
@@ -617,8 +611,8 @@ async::AsyncTaskHandle GPUDevice::popErrorScope(jsi::Runtime &runtime) {
|
|
|
617
611
|
&resolve,
|
|
618
612
|
const async::AsyncTaskHandle::RejectFunction
|
|
619
613
|
&reject) {
|
|
620
|
-
device.PopErrorScope(
|
|
621
|
-
wgpu::CallbackMode::
|
|
614
|
+
return device.PopErrorScope(
|
|
615
|
+
wgpu::CallbackMode::WaitAnyOnly,
|
|
622
616
|
[resolve, reject](wgpu::PopErrorScopeStatus status,
|
|
623
617
|
wgpu::ErrorType type, wgpu::StringView message) {
|
|
624
618
|
if (status == wgpu::PopErrorScopeStatus::Error ||
|
|
@@ -731,12 +725,12 @@ jsi::Value GPUDevice::getLost(jsi::Runtime &runtime,
|
|
|
731
725
|
*capturedResolve);
|
|
732
726
|
|
|
733
727
|
// Register a WEAK reference so notifyDeviceLost() can settle the promise
|
|
734
|
-
// if it is still alive
|
|
735
|
-
//
|
|
736
|
-
// through that invoker. A promise created on another runtime stays
|
|
737
|
-
// pending, matching the previous best-effort behavior.
|
|
728
|
+
// if it is still alive, on the thread of the runtime that read it,
|
|
729
|
+
// through that runtime's context (see RuntimeContext::post).
|
|
738
730
|
bool settledMeanwhile = false;
|
|
739
|
-
if (_async
|
|
731
|
+
if (_async) {
|
|
732
|
+
auto context =
|
|
733
|
+
async::RuntimeContext::getOrCreate(runtime, _async->instance());
|
|
740
734
|
std::lock_guard<std::mutex> lock(_lostMutex);
|
|
741
735
|
if (_lostSettled) {
|
|
742
736
|
// The device was lost between the check above and now.
|
|
@@ -744,7 +738,7 @@ jsi::Value GPUDevice::getLost(jsi::Runtime &runtime,
|
|
|
744
738
|
settledInfo = _lostInfo;
|
|
745
739
|
} else {
|
|
746
740
|
_lostPromises.push_back(
|
|
747
|
-
PendingLostPromise{
|
|
741
|
+
PendingLostPromise{jsi::WeakObject(runtime, promiseObj), context});
|
|
748
742
|
}
|
|
749
743
|
}
|
|
750
744
|
if (settledMeanwhile) {
|
|
@@ -775,20 +769,17 @@ void GPUDevice::removeEventListener(std::string type, jsi::Function callback) {
|
|
|
775
769
|
|
|
776
770
|
void GPUDevice::notifyUncapturedError(wgpu::ErrorType type,
|
|
777
771
|
std::string message) {
|
|
778
|
-
// Dawn
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
|
|
782
|
-
// worklet runtime does not deliver uncaptured errors to JS (best-effort; see
|
|
783
|
-
// README "Threading model").
|
|
784
|
-
auto invoker = _async ? _async->callInvoker() : nullptr;
|
|
785
|
-
if (!invoker) {
|
|
772
|
+
// Dawn reports an uncaptured error from whichever thread detects it. The
|
|
773
|
+
// listeners run on the thread of the runtime the device was created on,
|
|
774
|
+
// through its context (see RuntimeContext::post).
|
|
775
|
+
if (!_async) {
|
|
786
776
|
return;
|
|
787
777
|
}
|
|
788
778
|
auto self = shared_from_this();
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
779
|
+
_async->post(
|
|
780
|
+
[self, type, message = std::move(message)](jsi::Runtime &) mutable {
|
|
781
|
+
self->deliverUncapturedError(type, std::move(message));
|
|
782
|
+
});
|
|
792
783
|
}
|
|
793
784
|
|
|
794
785
|
void GPUDevice::deliverUncapturedError(wgpu::ErrorType type,
|
|
@@ -302,7 +302,7 @@ private:
|
|
|
302
302
|
friend class GPUAdapter;
|
|
303
303
|
|
|
304
304
|
// Runs the uncapturederror listeners on the creation runtime's JS thread.
|
|
305
|
-
// Invoked from notifyUncapturedError
|
|
305
|
+
// Invoked from notifyUncapturedError through the runtime's context.
|
|
306
306
|
void deliverUncapturedError(wgpu::ErrorType type, std::string message);
|
|
307
307
|
|
|
308
308
|
wgpu::Device _instance;
|
|
@@ -327,12 +327,11 @@ private:
|
|
|
327
327
|
// resolve function lives as a hidden property on the promise object itself,
|
|
328
328
|
// so if the promise is still alive when the device is lost we can settle
|
|
329
329
|
// it; if it was collected, nobody could have observed the resolution.
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
// spontaneous events.
|
|
330
|
+
// Each entry is settled on the thread of the runtime that read `lost`,
|
|
331
|
+
// through that runtime's context; a runtime gone since is skipped.
|
|
333
332
|
struct PendingLostPromise {
|
|
334
|
-
jsi::Runtime *runtime;
|
|
335
333
|
jsi::WeakObject promise;
|
|
334
|
+
std::weak_ptr<async::RuntimeContext> context;
|
|
336
335
|
};
|
|
337
336
|
std::vector<PendingLostPromise> _lostPromises;
|
|
338
337
|
|
|
@@ -94,8 +94,8 @@ async::AsyncTaskHandle GPUQueue::onSubmittedWorkDone(jsi::Runtime &runtime) {
|
|
|
94
94
|
return context->postTask(
|
|
95
95
|
[queue](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
96
96
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
97
|
-
queue.OnSubmittedWorkDone(
|
|
98
|
-
wgpu::CallbackMode::
|
|
97
|
+
return queue.OnSubmittedWorkDone(
|
|
98
|
+
wgpu::CallbackMode::WaitAnyOnly,
|
|
99
99
|
[resolve, reject](wgpu::QueueWorkDoneStatus status,
|
|
100
100
|
wgpu::StringView message) {
|
|
101
101
|
if (status == wgpu::QueueWorkDoneStatus::Success) {
|
|
@@ -531,6 +531,8 @@ async::AsyncTaskHandle GPUQueue::drawElementImageToTexture(
|
|
|
531
531
|
});
|
|
532
532
|
},
|
|
533
533
|
[reject](std::string error) { reject(std::move(error)); });
|
|
534
|
+
// Not Dawn's work: nothing to wait for, the snapshot calls back.
|
|
535
|
+
return std::nullopt;
|
|
534
536
|
});
|
|
535
537
|
}
|
|
536
538
|
|
|
@@ -19,8 +19,8 @@ GPUShaderModule::getCompilationInfo(jsi::Runtime &runtime) {
|
|
|
19
19
|
[module](const async::AsyncTaskHandle::ResolveFunction &resolve,
|
|
20
20
|
const async::AsyncTaskHandle::RejectFunction &reject) {
|
|
21
21
|
auto result = std::make_shared<GPUCompilationInfo>();
|
|
22
|
-
module.GetCompilationInfo(
|
|
23
|
-
wgpu::CallbackMode::
|
|
22
|
+
return module.GetCompilationInfo(
|
|
23
|
+
wgpu::CallbackMode::WaitAnyOnly,
|
|
24
24
|
[result, resolve,
|
|
25
25
|
reject](wgpu::CompilationInfoRequestStatus status,
|
|
26
26
|
const wgpu::CompilationInfo *compilationInfo) {
|