Skip to content

Commit df7bdcc

Browse files
kessenmaclaudewcandillon
authored
fix(🍏): support Bridgeless mode via self.bridge and RCTCallInvokerModule (#326)
* fix(ios): support Bridgeless mode by using self.bridge and RCTCallInvokerModule In Bridgeless mode (forced in RN 0.84+), [RCTBridge currentBridge] returns nil, causing install() to silently fail and the RNWebGPU JSI global to never be set. Replace the class method with self.bridge.runtime (works in both Legacy and Bridgeless via RCTBridgeProxy) and conform to RCTCallInvokerModule so the TurboModule infra injects the CallInvoker. Also guard the RNWebGPU global access in JS so a failed install() doesn't crash the module require. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update WebGPUModule.mm --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: William Candillon <wcandillon@gmail.com>
1 parent f6cd720 commit df7bdcc

3 files changed

Lines changed: 48 additions & 36 deletions

File tree

‎packages/webgpu/apple/WebGPUModule.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
#import "RNWebGPUManager.h"
44
#import <RNWgpuViewSpec/RNWgpuViewSpec.h>
5+
#import <React/RCTCallInvokerModule.h>
56
#import <React/RCTEventEmitter.h>
67

7-
@interface WebGPUModule : RCTEventEmitter <NativeWebGPUModuleSpec>
8+
@interface WebGPUModule : RCTEventEmitter <NativeWebGPUModuleSpec, RCTCallInvokerModule>
89

910
+ (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager;
1011

‎packages/webgpu/apple/WebGPUModule.mm‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#import "GPUCanvasContext.h"
44

55
#import <React/RCTBridge+Private.h>
6+
#import <React/RCTCallInvoker.h>
67
#import <React/RCTLog.h>
78
#import <ReactCommon/RCTTurboModule.h>
89
#import <jsi/jsi.h>
@@ -11,12 +12,25 @@
1112
namespace jsi = facebook::jsi;
1213
namespace react = facebook::react;
1314

15+
// Category to declare the runtime property on RCTBridge/RCTBridgeProxy.
16+
// In Bridgeless mode, self.bridge is an RCTBridgeProxy which implements
17+
// -(void *)runtime. In Legacy mode, self.bridge is the real RCTBridge
18+
// (backed by RCTCxxBridge) which also implements it.
19+
@interface RCTBridge (JSIRuntime)
20+
- (void *)runtime;
21+
@end
22+
1423
@implementation WebGPUModule
1524

1625
RCT_EXPORT_MODULE(WebGPUModule)
1726

1827
static std::shared_ptr<rnwgpu::RNWebGPUManager> webgpuManager;
1928

29+
// Synthesize callInvoker so RCTTurboModuleManager injects the JS CallInvoker.
30+
// When the module conforms to RCTCallInvokerModule, the TurboModule infra
31+
// calls setCallInvoker: during module initialization.
32+
@synthesize callInvoker = _callInvoker;
33+
2034
+ (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager {
2135
return webgpuManager;
2236
}
@@ -28,9 +42,6 @@ + (BOOL)requiresMainQueueSetup {
2842
}
2943

3044
- (void)invalidate {
31-
// if (_webgpuManager != nil) {
32-
// [_webgpuManager invalidate];
33-
// }
3445
webgpuManager = nil;
3546
}
3647

@@ -43,31 +54,26 @@ - (void)invalidate {
4354
// Already initialized, ignore call.
4455
return @true;
4556
}
46-
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge];
47-
if (!cxxBridge.runtime) {
48-
NSLog(@"Failed to install react-native-wgpu: RCTBridge is not a "
49-
@"RCTCxxBridge!");
50-
return [NSNumber numberWithBool:NO];
51-
}
5257

53-
jsi::Runtime *runtime = (jsi::Runtime *)cxxBridge.runtime;
58+
// self.bridge works in both Legacy (RCTBridge) and Bridgeless (RCTBridgeProxy).
59+
jsi::Runtime *runtime = (jsi::Runtime *)self.bridge.runtime;
5460
if (!runtime) {
55-
NSLog(@"Failed to install react-native-wgpu: jsi::Runtime* was null!");
61+
NSLog(@"Failed to install react-native-wgpu: jsi::Runtime* was null! "
62+
@"(self.bridge=%@)", self.bridge);
5663
return [NSNumber numberWithBool:NO];
5764
}
58-
std::shared_ptr<react::CallInvoker> jsInvoker = cxxBridge.jsCallInvoker;
65+
66+
// _callInvoker is injected by RCTTurboModuleManager because we conform to
67+
// RCTCallInvokerModule. Works in both Legacy and Bridgeless.
68+
std::shared_ptr<react::CallInvoker> jsInvoker = _callInvoker.callInvoker;
5969
if (!jsInvoker) {
6070
NSLog(@"Failed to install react-native-wgpu: react::CallInvoker was "
6171
@"null!");
6272
return [NSNumber numberWithBool:NO];
6373
}
6474

65-
if (!jsInvoker) {
66-
jsInvoker = cxxBridge.jsCallInvoker;
67-
}
6875
std::shared_ptr<rnwgpu::PlatformContext> platformContext =
6976
std::make_shared<rnwgpu::ApplePlatformContext>();
70-
// TODO: remove allocation here
7177
webgpuManager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, jsInvoker,
7278
platformContext);
7379
return @true;

‎packages/webgpu/src/main/index.tsx‎

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,33 @@ export * from "../hooks";
88

99
export { default as WebGPUModule } from "../NativeWebGPUModule";
1010

11-
WebGPUModule.install();
11+
const _installOk = WebGPUModule.install();
1212

1313
registerWebGPUForReanimated();
1414

15-
if (!navigator) {
16-
// @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property
17-
navigator = {
18-
gpu: RNWebGPU.gpu,
19-
userAgent: "react-native",
20-
};
21-
} else {
22-
navigator.gpu = RNWebGPU.gpu;
23-
if (typeof navigator.userAgent !== "string") {
24-
try {
25-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
26-
// @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed
27-
navigator.userAgent = "react-native";
28-
} catch {
29-
// navigator.userAgent can be read-only; ignore if assignment fails
15+
if (typeof RNWebGPU !== "undefined") {
16+
if (!navigator) {
17+
// @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property
18+
navigator = {
19+
gpu: RNWebGPU.gpu,
20+
userAgent: "react-native",
21+
};
22+
} else {
23+
navigator.gpu = RNWebGPU.gpu;
24+
if (typeof navigator.userAgent !== "string") {
25+
try {
26+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
27+
// @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed
28+
navigator.userAgent = "react-native";
29+
} catch {
30+
// navigator.userAgent can be read-only; ignore if assignment fails
31+
}
3032
}
3133
}
34+
global.createImageBitmap =
35+
global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU);
36+
} else {
37+
console.warn(
38+
`[react-native-wgpu] install() returned ${_installOk} but RNWebGPU global is not available`
39+
);
3240
}
33-
34-
global.createImageBitmap =
35-
global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU);

0 commit comments

Comments
 (0)