react-native-wgpu 0.1.12 → 0.1.14

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.
Files changed (67) hide show
  1. package/android/CMakeLists.txt +7 -0
  2. package/android/build.gradle +4 -4
  3. package/android/cpp/cpp-adapter.cpp +10 -4
  4. package/android/cpp/platform/ThreadUtils.cpp +41 -0
  5. package/apple/ApplePlatformContext.h +19 -0
  6. package/apple/ApplePlatformContext.mm +86 -0
  7. package/apple/MetalView.h +13 -0
  8. package/apple/MetalView.mm +58 -0
  9. package/apple/WebGPUModule.h +19 -0
  10. package/apple/WebGPUModule.mm +93 -0
  11. package/apple/WebGPUView.h +15 -0
  12. package/apple/WebGPUView.mm +68 -0
  13. package/apple/WebGPUViewManager.mm +23 -0
  14. package/apple/platform/ThreadUtils.cpp +33 -0
  15. package/cpp/jsi/RNFJSIConverter.h +47 -28
  16. package/cpp/platform/ThreadUtils.h +30 -0
  17. package/cpp/rnwgpu/RNWebGPUManager.cpp +8 -0
  18. package/cpp/rnwgpu/api/Convertors.h +13 -14
  19. package/cpp/rnwgpu/api/GPU.cpp +4 -4
  20. package/cpp/rnwgpu/api/GPUAdapter.cpp +15 -14
  21. package/cpp/rnwgpu/api/GPUAdapterInfo.h +25 -4
  22. package/cpp/rnwgpu/api/GPUCanvasContext.cpp +6 -4
  23. package/cpp/rnwgpu/api/GPUDevice.cpp +5 -5
  24. package/cpp/rnwgpu/api/GPUDevice.h +7 -1
  25. package/cpp/rnwgpu/api/GPUFeatures.h +4 -4
  26. package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -1
  27. package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +9 -0
  28. package/cpp/threading/CallInvokerDispatcher.h +37 -0
  29. package/cpp/threading/Dispatcher.cpp +54 -0
  30. package/cpp/threading/Dispatcher.h +93 -0
  31. package/cpp/threading/ThreadPool.cpp +86 -0
  32. package/cpp/threading/ThreadPool.h +53 -0
  33. package/cpp/webgpu/webgpu.h +762 -758
  34. package/cpp/webgpu/webgpu_cpp.h +1827 -1626
  35. package/cpp/webgpu/webgpu_cpp_chained_struct.h +2 -0
  36. package/lib/commonjs/hooks.js +4 -2
  37. package/lib/commonjs/hooks.js.map +1 -1
  38. package/lib/module/hooks.js +4 -2
  39. package/lib/module/hooks.js.map +1 -1
  40. package/lib/typescript/lib/commonjs/hooks.d.ts.map +1 -1
  41. package/lib/typescript/lib/module/hooks.d.ts.map +1 -1
  42. package/lib/typescript/src/__tests__/Alpha.spec.d.ts +2 -0
  43. package/lib/typescript/src/__tests__/Alpha.spec.d.ts.map +1 -0
  44. package/lib/typescript/src/hooks.d.ts.map +1 -1
  45. package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
  46. package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
  47. package/libs/android/x86/libwebgpu_dawn.so +0 -0
  48. package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
  49. package/libs/apple/arm64_iphoneos/libwebgpu_dawn.a +0 -0
  50. package/libs/apple/arm64_iphonesimulator/libwebgpu_dawn.a +0 -0
  51. package/libs/apple/arm64_xros/libwebgpu_dawn.a +0 -0
  52. package/libs/apple/arm64_xrsimulator/libwebgpu_dawn.a +0 -0
  53. package/libs/apple/iphonesimulator/libwebgpu_dawn.a +0 -0
  54. package/libs/apple/libwebgpu_dawn.xcframework/Info.plist +10 -10
  55. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64/libwebgpu_dawn.a +0 -0
  56. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
  57. package/libs/apple/libwebgpu_dawn.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
  58. package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64/libwebgpu_dawn.a +0 -0
  59. package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64-simulator/libwebgpu_dawn.a +0 -0
  60. package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
  61. package/libs/apple/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
  62. package/libs/dawn.json +270 -251
  63. package/package.json +2 -2
  64. package/src/__tests__/Alpha.spec.ts +28 -0
  65. package/src/__tests__/Device.spec.ts +31 -0
  66. package/src/__tests__/snapshots/semi-opaque-cyan.png +0 -0
  67. package/src/hooks.tsx +3 -2
@@ -25,6 +25,7 @@ find_package(fbjni REQUIRED CONFIG)
25
25
 
26
26
  add_library(${PACKAGE_NAME} SHARED
27
27
  ./cpp/cpp-adapter.cpp
28
+ ./cpp/platform/ThreadUtils.cpp
28
29
  ../cpp/rnwgpu/api/GPU.cpp
29
30
  ../cpp/rnwgpu/api/GPUAdapter.cpp
30
31
  ../cpp/rnwgpu/api/GPUSupportedLimits.cpp
@@ -48,6 +49,8 @@ add_library(${PACKAGE_NAME} SHARED
48
49
  ../cpp/jsi/RNFHybridObject.cpp
49
50
  ../cpp/jsi/RNFRuntimeCache.cpp
50
51
  ../cpp/jsi/RNFWorkletRuntimeRegistry.cpp
52
+ ../cpp/threading/Dispatcher.cpp
53
+ ../cpp/threading/ThreadPool.cpp
51
54
  )
52
55
 
53
56
  target_include_directories(
@@ -61,6 +64,7 @@ target_include_directories(
61
64
  "${NODE_MODULES_DIR}/react-native/ReactCommon/runtimeexecutor"
62
65
  "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni"
63
66
  "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"
67
+ "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule"
64
68
 
65
69
  ../cpp
66
70
  ../cpp/rnwgpu
@@ -68,6 +72,8 @@ target_include_directories(
68
72
  ../cpp/rnwgpu/api/descriptors
69
73
  ../cpp/jsi
70
74
  ../cpp/webgpu
75
+ ../cpp/threading
76
+ ../cpp/platform
71
77
 
72
78
  ${libfbjni_include_DIRS}
73
79
  )
@@ -84,5 +90,6 @@ target_link_libraries(
84
90
  android
85
91
  fbjni::fbjni
86
92
  ReactAndroid::jsi
93
+ ReactAndroid::reactnativejni
87
94
  webgpu_dawn
88
95
  )
@@ -87,10 +87,10 @@ android {
87
87
  }
88
88
 
89
89
  packagingOptions {
90
- exclude "lib/arm64-v8a/libjsi.so"
91
- exclude "lib/x86_64/libjsi.so"
92
- exclude "lib/x86/libjsi.so"
93
- exclude "lib/armeabi-v7a/libjsi.so"
90
+ excludes = [
91
+ "**/libjsi.so",
92
+ "**/libreactnativejni.so",
93
+ ]
94
94
  }
95
95
 
96
96
  buildFeatures {
@@ -5,6 +5,7 @@
5
5
  #include <jni.h>
6
6
  #include <jsi/jsi.h>
7
7
 
8
+ #include <ReactCommon/CallInvokerHolder.h>
8
9
  #include <android/native_window_jni.h>
9
10
  #include <webgpu/webgpu_cpp.h>
10
11
 
@@ -17,13 +18,18 @@
17
18
  std::shared_ptr<rnwgpu::RNWebGPUManager> manager;
18
19
 
19
20
  extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative(
20
- JNIEnv *env, jobject /* this */, jlong jsRuntime, jobject jsInvokerHolder,
21
- jobject blobModule) {
21
+ JNIEnv *env, jobject /* this */, jlong jsRuntime,
22
+ jobject jsCallInvokerHolder, jobject blobModule) {
22
23
  auto runtime = reinterpret_cast<facebook::jsi::Runtime *>(jsRuntime);
23
24
  jobject globalBlobModule = env->NewGlobalRef(blobModule);
25
+ auto jsCallInvoker{
26
+ facebook::jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>{
27
+ reinterpret_cast<facebook::react::CallInvokerHolder::javaobject>(
28
+ jsCallInvokerHolder)} -> cthis()
29
+ ->getCallInvoker()};
24
30
  auto platformContext =
25
31
  std::make_shared<rnwgpu::AndroidPlatformContext>(globalBlobModule);
26
- manager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, nullptr,
32
+ manager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, jsCallInvoker,
27
33
  platformContext);
28
34
  }
29
35
 
@@ -55,7 +61,7 @@ Java_com_webgpu_WebGPUView_switchToOffscreenSurface(JNIEnv *env, jobject thiz,
55
61
  jint contextId) {
56
62
  auto &registry = rnwgpu::SurfaceRegistry::getInstance();
57
63
  auto nativeSurface = registry.getSurfaceInfo(contextId)->switchToOffscreen();
58
- ANativeWindow_release(reinterpret_cast<ANativeWindow *>(nativeSurface));
64
+ // ANativeWindow_release(reinterpret_cast<ANativeWindow *>(nativeSurface));
59
65
  }
60
66
 
61
67
  extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceDestroy(
@@ -0,0 +1,41 @@
1
+ //
2
+ // ThreadUtils.cpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #include "ThreadUtils.h"
9
+ #include <pthread.h>
10
+ #include <sstream>
11
+ #include <string>
12
+ #include <sys/prctl.h>
13
+ #include <thread>
14
+
15
+ namespace margelo {
16
+
17
+ std::string ThreadUtils::getThreadName() {
18
+ #ifdef HAVE_ANDROID_PTHREAD_SETNAME_NP
19
+ // Try using pthread APIs
20
+ pthread_t this_thread = pthread_self();
21
+ char thread_name[16]; // Thread name length limit in Android is 16 characters
22
+
23
+ int result =
24
+ pthread_getname_np(this_thread, thread_name, sizeof(thread_name));
25
+ if (result == 0) {
26
+ return std::string(thread_name);
27
+ }
28
+ #endif
29
+
30
+ // Fall back to this_thread ID
31
+ std::stringstream stream;
32
+ stream << std::this_thread::get_id();
33
+ std::string threadId = stream.str();
34
+ return "Thread #" + threadId;
35
+ }
36
+
37
+ void ThreadUtils::setThreadName(const std::string &name) {
38
+ prctl(PR_SET_NAME, name.c_str(), 0, 0, 0);
39
+ }
40
+
41
+ } // namespace margelo
@@ -0,0 +1,19 @@
1
+ #pragma once
2
+
3
+ #include "PlatformContext.h"
4
+
5
+ namespace rnwgpu {
6
+
7
+ class ApplePlatformContext : public PlatformContext {
8
+ public:
9
+ ApplePlatformContext();
10
+ ~ApplePlatformContext() = default;
11
+
12
+ wgpu::Surface makeSurface(wgpu::Instance instance, void *surface, int width,
13
+ int height) override;
14
+
15
+ ImageData createImageBitmap(std::string blobId, double offset,
16
+ double size) override;
17
+ };
18
+
19
+ } // namespace rnwgpu
@@ -0,0 +1,86 @@
1
+ #include "ApplePlatformContext.h"
2
+
3
+ #include <TargetConditionals.h>
4
+
5
+ #import <React/RCTBlobManager.h>
6
+ #import <React/RCTBridge+Private.h>
7
+ #import <ReactCommon/RCTTurboModule.h>
8
+
9
+ #include "RNWebGPUManager.h"
10
+ #include "WebGPUModule.h"
11
+
12
+ namespace rnwgpu {
13
+
14
+ void isSimulatorWithAPIValidation() {
15
+ #if TARGET_OS_SIMULATOR
16
+ NSDictionary *environment = [[NSProcessInfo processInfo] environment];
17
+ NSString *metalDeviceWrapperType = environment[@"METAL_DEVICE_WRAPPER_TYPE"];
18
+
19
+ if ([metalDeviceWrapperType isEqualToString:@"1"]) {
20
+ throw std::runtime_error(
21
+ "To use React Native WebGPU project on the iOS simulator, you need to "
22
+ "disable the Metal validation API. In 'Edit Scheme,' uncheck 'Metal "
23
+ "Validation.'");
24
+ }
25
+ #endif
26
+ }
27
+
28
+ ApplePlatformContext::ApplePlatformContext() { isSimulatorWithAPIValidation(); }
29
+
30
+ wgpu::Surface ApplePlatformContext::makeSurface(wgpu::Instance instance,
31
+ void *surface, int width,
32
+ int height) {
33
+ wgpu::SurfaceDescriptorFromMetalLayer metalSurfaceDesc;
34
+ metalSurfaceDesc.layer = surface;
35
+ wgpu::SurfaceDescriptor surfaceDescriptor;
36
+ surfaceDescriptor.nextInChain = &metalSurfaceDesc;
37
+ return instance.CreateSurface(&surfaceDescriptor);
38
+ }
39
+
40
+ ImageData ApplePlatformContext::createImageBitmap(std::string blobId,
41
+ double offset, double size) {
42
+ RCTBlobManager *blobManager =
43
+ [[RCTBridge currentBridge] moduleForClass:RCTBlobManager.class];
44
+ NSData *blobData =
45
+ [blobManager resolve:[NSString stringWithUTF8String:blobId.c_str()]
46
+ offset:(long)offset
47
+ size:(long)size];
48
+
49
+ if (!blobData) {
50
+ throw std::runtime_error("Couldn't retrive blob data");
51
+ }
52
+
53
+ UIImage *image = [UIImage imageWithData:blobData];
54
+ if (!image) {
55
+ throw std::runtime_error("Couldn't decode image");
56
+ }
57
+
58
+ CGImageRef cgImage = image.CGImage;
59
+ size_t width = CGImageGetWidth(cgImage);
60
+ size_t height = CGImageGetHeight(cgImage);
61
+ size_t bitsPerComponent = 8;
62
+ size_t bytesPerRow = width * 4;
63
+ std::vector<uint8_t> imageData(height * bytesPerRow);
64
+
65
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
66
+ CGContextRef context = CGBitmapContextCreate(
67
+ imageData.data(), width, height, bitsPerComponent, bytesPerRow,
68
+ colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
69
+
70
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
71
+
72
+ // Now imageData contains a copy of the bitmap data
73
+
74
+ CGContextRelease(context);
75
+ CGColorSpaceRelease(colorSpace);
76
+
77
+ // Use the copied data
78
+ ImageData result;
79
+ result.width = static_cast<int>(width);
80
+ result.height = static_cast<int>(height);
81
+ result.data = imageData;
82
+ result.format = wgpu::TextureFormat::RGBA8Unorm;
83
+ return result;
84
+ }
85
+
86
+ } // namespace rnwgpu
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #import "WebGPUModule.h"
4
+ #import <UIKit/UIKit.h>
5
+
6
+ @interface MetalView : UIView
7
+
8
+ @property NSNumber *contextId;
9
+
10
+ - (void)configure;
11
+ - (void)update;
12
+
13
+ @end
@@ -0,0 +1,58 @@
1
+ #import "MetalView.h"
2
+ #import "webgpu_cpp.h"
3
+
4
+ #ifndef RCT_NEW_ARCH_ENABLED
5
+ #import <React/RCTViewManager.h>
6
+ #endif // RCT_NEW_ARCH_ENABLED
7
+
8
+ @implementation MetalView {
9
+ BOOL _isConfigured;
10
+ }
11
+
12
+ + (Class)layerClass {
13
+ return [CAMetalLayer class];
14
+ }
15
+
16
+ - (void)configure {
17
+ auto size = self.frame.size;
18
+ std::shared_ptr<rnwgpu::RNWebGPUManager> manager = [WebGPUModule getManager];
19
+ void *nativeSurface = (__bridge void *)self.layer;
20
+ auto &registry = rnwgpu::SurfaceRegistry::getInstance();
21
+ auto gpu = manager->_gpu;
22
+ auto surface = manager->_platformContext->makeSurface(
23
+ gpu, nativeSurface, size.width, size.height);
24
+ registry
25
+ .getSurfaceInfoOrCreate([_contextId intValue], gpu, size.width,
26
+ size.height)
27
+ ->switchToOnscreen(nativeSurface, surface);
28
+ }
29
+
30
+ - (void)update {
31
+ auto size = self.frame.size;
32
+ auto &registry = rnwgpu::SurfaceRegistry::getInstance();
33
+ registry.getSurfaceInfo([_contextId intValue])
34
+ ->resize(size.width, size.height);
35
+ }
36
+
37
+ - (void)dealloc {
38
+ auto &registry = rnwgpu::SurfaceRegistry::getInstance();
39
+ // Remove the surface info from the registry
40
+ registry.removeSurfaceInfo([_contextId intValue]);
41
+ }
42
+
43
+ #ifndef RCT_NEW_ARCH_ENABLED
44
+ // Paper only method
45
+ // TODO: this method is wrong, it appears to call configureSurface instead of
46
+ // updateSurface sometimes
47
+ - (void)reactSetFrame:(CGRect)frame {
48
+ [super reactSetFrame:frame];
49
+ if (!_isConfigured) {
50
+ [self configure];
51
+ _isConfigured = YES;
52
+ } else {
53
+ [self update];
54
+ }
55
+ }
56
+ #endif // RCT_NEW_ARCH_ENABLED
57
+
58
+ @end
@@ -0,0 +1,19 @@
1
+ #pragma once
2
+
3
+ #import "RNWebGPUManager.h"
4
+ #import <React/RCTBridgeModule.h>
5
+ #import <React/RCTEventEmitter.h>
6
+
7
+ #ifdef RCT_NEW_ARCH_ENABLED
8
+ #import <RNWgpuViewSpec/RNWgpuViewSpec.h>
9
+ @interface WebGPUModule : RCTEventEmitter <NativeWebGPUModuleSpec>
10
+ #else
11
+ @interface WebGPUModule : RCTEventEmitter <RCTBridgeModule>
12
+ #endif
13
+
14
+ @property(nonatomic, weak) RCTBridge *bridge;
15
+ @property(nonatomic, weak) RCTModuleRegistry *moduleRegistry;
16
+
17
+ + (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager;
18
+
19
+ @end
@@ -0,0 +1,93 @@
1
+ #import "WebGPUModule.h"
2
+ #include "ApplePlatformContext.h"
3
+ #import "GPUCanvasContext.h"
4
+
5
+ #import <React/RCTBridge+Private.h>
6
+ #import <React/RCTLog.h>
7
+ #import <React/RCTUIManagerUtils.h>
8
+ #import <ReactCommon/RCTTurboModule.h>
9
+ #import <jsi/jsi.h>
10
+ #import <memory>
11
+
12
+ namespace jsi = facebook::jsi;
13
+ namespace react = facebook::react;
14
+
15
+ @implementation WebGPUModule
16
+
17
+ RCT_EXPORT_MODULE(WebGPUModule)
18
+
19
+ static std::shared_ptr<rnwgpu::RNWebGPUManager> webgpuManager;
20
+
21
+ + (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager {
22
+ return webgpuManager;
23
+ }
24
+
25
+ #pragma Setup and invalidation
26
+
27
+ + (BOOL)requiresMainQueueSetup {
28
+ return YES;
29
+ }
30
+
31
+ - (void)invalidate {
32
+ // if (_webgpuManager != nil) {
33
+ // [_webgpuManager invalidate];
34
+ // }
35
+ webgpuManager = nil;
36
+ }
37
+
38
+ - (std::shared_ptr<rnwgpu::RNWebGPUManager>)getManager {
39
+ return webgpuManager;
40
+ }
41
+
42
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
43
+ if (webgpuManager != nil) {
44
+ // Already initialized, ignore call.
45
+ return @true;
46
+ }
47
+ RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge];
48
+ if (!cxxBridge.runtime) {
49
+ NSLog(@"Failed to install react-native-filament: RCTBridge is not a "
50
+ @"RCTCxxBridge!");
51
+ return [NSNumber numberWithBool:NO];
52
+ }
53
+
54
+ jsi::Runtime *runtime = (jsi::Runtime *)cxxBridge.runtime;
55
+ if (!runtime) {
56
+ NSLog(@"Failed to install react-native-filament: jsi::Runtime* was null!");
57
+ return [NSNumber numberWithBool:NO];
58
+ }
59
+ std::shared_ptr<react::CallInvoker> jsInvoker = cxxBridge.jsCallInvoker;
60
+ if (!jsInvoker) {
61
+ NSLog(@"Failed to install react-native-filament: react::CallInvoker was "
62
+ @"null!");
63
+ return [NSNumber numberWithBool:NO];
64
+ }
65
+
66
+ if (!jsInvoker) {
67
+ jsInvoker = cxxBridge.jsCallInvoker;
68
+ }
69
+ std::shared_ptr<rnwgpu::PlatformContext> platformContext =
70
+ std::make_shared<rnwgpu::ApplePlatformContext>();
71
+ // TODO: remove allocation here
72
+ webgpuManager = std::make_shared<rnwgpu::RNWebGPUManager>(runtime, jsInvoker,
73
+ platformContext);
74
+ return @true;
75
+ }
76
+
77
+ // RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(createSurfaceContext
78
+ // : (double)contextId) {
79
+ // int contextIdInt = contextId;
80
+ // RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
81
+ // auto runtime = (jsi::Runtime *)cxxBridge.runtime;
82
+
83
+ // return @true;
84
+ // }
85
+
86
+ #ifdef RCT_NEW_ARCH_ENABLED
87
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
88
+ (const facebook::react::ObjCTurboModule::InitParams &)params {
89
+ return std::make_shared<facebook::react::NativeWebGPUModuleSpecJSI>(params);
90
+ }
91
+ #endif
92
+
93
+ @end
@@ -0,0 +1,15 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+ #pragma once
3
+
4
+ #import "MetalView.h"
5
+ #import <React/RCTViewComponentView.h>
6
+ #import <UIKit/UIKit.h>
7
+
8
+ NS_ASSUME_NONNULL_BEGIN
9
+
10
+ @interface WebGPUView : RCTViewComponentView
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
14
+
15
+ #endif /* RCT_NEW_ARCH_ENABLED */
@@ -0,0 +1,68 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+ #import "WebGPUView.h"
3
+
4
+ #import <react/renderer/components/RNWgpuViewSpec/ComponentDescriptors.h>
5
+ #import <react/renderer/components/RNWgpuViewSpec/EventEmitters.h>
6
+ #import <react/renderer/components/RNWgpuViewSpec/Props.h>
7
+ #import <react/renderer/components/RNWgpuViewSpec/RCTComponentViewHelpers.h>
8
+
9
+ #import "MetalView.h"
10
+ #import "RCTFabricComponentsPlugins.h"
11
+ #import "Utils.h"
12
+
13
+ using namespace facebook::react;
14
+
15
+ @implementation WebGPUView
16
+
17
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
18
+ return concreteComponentDescriptorProvider<WebGPUViewComponentDescriptor>();
19
+ }
20
+
21
+ - (void)prepareForRecycle {
22
+ [super prepareForRecycle];
23
+ /*
24
+ It's important to destroy the Metal Layer before releasing a view
25
+ to the recycled pool to prevent displaying outdated content from
26
+ the last usage in the new context.
27
+ */
28
+ self.contentView = nil;
29
+ }
30
+
31
+ - (MetalView *)getContentView {
32
+ if (!self.contentView) {
33
+ self.contentView = [MetalView new];
34
+ }
35
+ return (MetalView *)self.contentView;
36
+ }
37
+
38
+ - (void)updateProps:(const Props::Shared &)props
39
+ oldProps:(const Props::Shared &)oldProps {
40
+ const auto &oldViewProps =
41
+ *std::static_pointer_cast<const WebGPUViewProps>(_props);
42
+ const auto &newViewProps =
43
+ *std::static_pointer_cast<const WebGPUViewProps>(props);
44
+
45
+ if (newViewProps.contextId != oldViewProps.contextId) {
46
+ /*
47
+ The context is set only once during mounting the component
48
+ and never changes because it isn't available for users to modify.
49
+ */
50
+ MetalView *metalView = [MetalView new];
51
+ self.contentView = metalView;
52
+ [metalView setContextId:@(newViewProps.contextId)];
53
+ [metalView configure];
54
+ }
55
+
56
+ [super updateProps:props oldProps:oldProps];
57
+ }
58
+
59
+ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
60
+ oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
61
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
62
+ [(MetalView *)self.contentView update];
63
+ }
64
+
65
+ Class<RCTComponentViewProtocol> WebGPUViewCls(void) { return WebGPUView.class; }
66
+
67
+ @end
68
+ #endif
@@ -0,0 +1,23 @@
1
+ #import "MetalView.h"
2
+ #import "RCTBridge.h"
3
+ #import "WebGPUModule.h"
4
+ #import <React/RCTUIManager.h>
5
+ #import <React/RCTViewManager.h>
6
+
7
+ @interface WebGPUViewManager : RCTViewManager
8
+ @end
9
+
10
+ @implementation WebGPUViewManager
11
+
12
+ RCT_EXPORT_MODULE(WebGPUView)
13
+
14
+ - (UIView *)view {
15
+ return [MetalView new];
16
+ }
17
+
18
+ RCT_CUSTOM_VIEW_PROPERTY(contextId, NSNumber, UIView) {
19
+ NSNumber *contextId = [RCTConvert NSNumber:json];
20
+ [(MetalView *)view setContextId:contextId];
21
+ }
22
+
23
+ @end
@@ -0,0 +1,33 @@
1
+ //
2
+ // ThreadUtils.cpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #include "ThreadUtils.h"
9
+ #include <pthread.h>
10
+ #include <sstream>
11
+ #include <thread>
12
+
13
+ namespace margelo {
14
+
15
+ std::string ThreadUtils::getThreadName() {
16
+ // Try using pthread APIs
17
+ char name[256];
18
+ if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0) {
19
+ return std::string(name);
20
+ }
21
+
22
+ // Fall back to this_thread ID
23
+ std::stringstream stream;
24
+ stream << std::this_thread::get_id();
25
+ std::string threadId = stream.str();
26
+ return "Thread #" + threadId;
27
+ }
28
+
29
+ void ThreadUtils::setThreadName(const std::string &name) {
30
+ pthread_setname_np(name.c_str());
31
+ }
32
+
33
+ } // namespace margelo
@@ -25,6 +25,9 @@
25
25
  #include "RNFPromise.h"
26
26
  #include "RNFWorkletRuntimeRegistry.h"
27
27
 
28
+ #include "Dispatcher.h"
29
+ #include "ThreadPool.h"
30
+
28
31
  #if __has_include(<cxxabi.h>)
29
32
  #include <cxxabi.h>
30
33
  #endif
@@ -207,39 +210,55 @@ template <typename TResult> struct JSIConverter<std::future<TResult>> {
207
210
  }
208
211
  static jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
209
212
  auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(arg));
210
- return Promise::createPromise(runtime, [sharedFuture = std::move(sharedFuture)](jsi::Runtime& runtime,
213
+ std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
214
+ std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
215
+
216
+ return Promise::createPromise(runtime, [sharedFuture = std::move(sharedFuture), weakDispatcher](jsi::Runtime& runtime,
211
217
  std::shared_ptr<Promise> promise) {
212
- try {
213
- // wait until the future completes.
218
+ // Spawn new async thread to synchronously wait for the `future<T>` to complete
219
+ std::shared_ptr<ThreadPool> pool = ThreadPool::getSharedPool();
220
+ pool->run([promise, &runtime, weakDispatcher, sharedFuture]() {
221
+ // synchronously wait until the `future<T>` completes. we are running on a background task here.
214
222
  sharedFuture->wait();
215
223
 
216
- if constexpr (std::is_same_v<TResult, void>) {
217
- // it's returning void, just return undefined to JS
218
- sharedFuture->get();
219
- promise->resolve(jsi::Value::undefined());
220
- } else {
221
- // it's returning a custom type, convert it to a jsi::Value
222
- TResult result = sharedFuture->get();
223
- jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, result);
224
- promise->resolve(std::move(jsResult));
224
+ std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
225
+ if (!dispatcher) {
226
+ throw std::runtime_error("Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed!");
227
+ return;
225
228
  }
226
- } catch (const std::exception& exception) {
227
- // the async function threw an error, reject the promise
228
- std::string what = exception.what();
229
- promise->reject(what);
230
- } catch (...) {
231
- // the async function threw a non-std error, try getting it
232
- #if __has_include(<cxxabi.h>)
233
- std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
234
- #else
235
- std::string name = "<unknown>";
236
- #endif
237
- promise->reject("Unknown non-std exception: " + name);
238
- }
239
229
 
240
- // This lambda owns the promise shared pointer, and we need to call its
241
- // destructor correctly here - ensuring it's properly handled.
242
- promise = nullptr;
230
+ dispatcher->runAsync([&runtime, promise, sharedFuture]() mutable {
231
+ try {
232
+ if constexpr (std::is_same_v<TResult, void>) {
233
+ // it's returning void, just return undefined to JS
234
+ sharedFuture->get();
235
+ promise->resolve(jsi::Value::undefined());
236
+ } else {
237
+ // it's returning a custom type, convert it to a jsi::Value
238
+ TResult result = sharedFuture->get();
239
+ jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, result);
240
+ promise->resolve(std::move(jsResult));
241
+ }
242
+ } catch (const std::exception& exception) {
243
+ // the async function threw an error, reject the promise
244
+ std::string what = exception.what();
245
+ promise->reject(what);
246
+ } catch (...) {
247
+ // the async function threw a non-std error, try getting it
248
+ #if __has_include(<cxxabi.h>)
249
+ std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
250
+ #else
251
+ std::string name = "<unknown>";
252
+ #endif
253
+ promise->reject("Unknown non-std exception: " + name);
254
+ }
255
+
256
+ // This lambda owns the promise shared pointer, and we need to call its
257
+ // destructor correctly here - otherwise it might be called
258
+ // from the threadPool thread.
259
+ promise = nullptr;
260
+ });
261
+ });
243
262
  });
244
263
  }
245
264
  };