react-native-wgpu 0.1.12 → 0.1.13

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.
@@ -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
@@ -280,15 +280,6 @@ public:
280
280
  Convert(out.label, in.label);
281
281
  }
282
282
 
283
- // [[nodiscard]] bool Convert(wgpu::CanvasConfiguration &out,
284
- // const GPUCanvasConfiguration &in) {
285
- // return Convert(out.device, in.device) && Convert(out.format, in.format)
286
- // &&
287
- // Convert(out.usage, in.usage) && Convert(out.viewFormats,
288
- // in.viewFormats) && Convert(out.colorSpace, in.colorSpace) &&
289
- // Convert(out.alphaMode, in.alphaMode);
290
- // }
291
-
292
283
  [[nodiscard]] bool Convert(wgpu::Color &out, const GPUColor &in) {
293
284
  return Convert(out.r, in.r) && Convert(out.g, in.g) &&
294
285
  Convert(out.b, in.b) && Convert(out.a, in.a);
@@ -16,12 +16,13 @@ void GPUCanvasContext::configure(
16
16
  throw std::runtime_error("Error with SurfaceConfiguration");
17
17
  }
18
18
  }
19
- if (!conv(surfaceConfiguration.format, configuration->format)) {
20
- throw std::runtime_error("Error with SurfaceConfiguration");
21
- }
22
- if (!conv(surfaceConfiguration.usage, configuration->usage)) {
23
- throw std::runtime_error("Error with SurfaceConfiguration");
19
+ if (!conv(surfaceConfiguration.usage, configuration->usage) || !conv(surfaceConfiguration.format, configuration->format)) {
20
+ throw std::runtime_error("Error with SurfaceConfiguration");
24
21
  }
22
+
23
+ #ifdef __APPLE__
24
+ surfaceConfiguration.alphaMode = configuration->alphaMode;
25
+ #endif
25
26
  _surfaceInfo->configure(surfaceConfiguration);
26
27
  }
27
28
 
@@ -21,6 +21,7 @@ struct GPUCanvasConfiguration {
21
21
  std::optional<double> usage; // GPUTextureUsageFlags
22
22
  std::optional<std::vector<wgpu::TextureFormat>>
23
23
  viewFormats; // Iterable<GPUTextureFormat>
24
+ wgpu::CompositeAlphaMode alphaMode = wgpu::CompositeAlphaMode::Opaque;
24
25
  };
25
26
 
26
27
  } // namespace rnwgpu
@@ -58,6 +59,12 @@ struct JSIConverter<std::shared_ptr<rnwgpu::GPUCanvasConfiguration>> {
58
59
  prop,
59
60
  false);
60
61
  }
62
+ if (value.hasProperty(runtime, "alphaMode")) {
63
+ auto prop = value.getProperty(runtime, "alphaMode").asString(runtime).utf8(runtime);
64
+ if (prop == "premultiplied") {
65
+ result->alphaMode = wgpu::CompositeAlphaMode::Premultiplied;
66
+ }
67
+ }
61
68
  }
62
69
 
63
70
  return result;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Alpha.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alpha.spec.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/Alpha.spec.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-wgpu",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "React Native WebGPU",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -15,7 +15,7 @@
15
15
  "android/cpp/**",
16
16
  "android/src/**",
17
17
  "cpp/**/*.{h,cpp}",
18
- "ios/**",
18
+ "apple/**",
19
19
  "libs/**",
20
20
  "*.podspec"
21
21
  ],
@@ -0,0 +1,28 @@
1
+ import { checkImage, client, encodeImage } from "./setup";
2
+
3
+ describe("Alpha", () => {
4
+ it("Premultiplied Color", async () => {
5
+ const result = await client.eval(({ device, ctx, canvas }) => {
6
+ const commandEncoder = device.createCommandEncoder();
7
+ const textureView = ctx.getCurrentTexture().createView();
8
+ const alpha = 0.5;
9
+ const renderPassDescriptor: GPURenderPassDescriptor = {
10
+ colorAttachments: [
11
+ {
12
+ view: textureView,
13
+ clearValue: [0.3 * alpha, 0.6 * alpha, 1 * alpha, alpha],
14
+ loadOp: "clear",
15
+ storeOp: "store",
16
+ },
17
+ ],
18
+ };
19
+
20
+ const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
21
+ passEncoder.end();
22
+ device.queue.submit([commandEncoder.finish()]);
23
+ return canvas.getImageData();
24
+ });
25
+ const image = encodeImage(result);
26
+ checkImage(image, "snapshots/semi-opaque-cyan.png");
27
+ });
28
+ });