diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock index 5b1dc5148..773ce50bf 100644 --- a/apps/example/ios/Podfile.lock +++ b/apps/example/ios/Podfile.lock @@ -937,7 +937,7 @@ PODS: - React-debug - react-native-safe-area-context (4.11.0): - React-Core - - react-native-wgpu (0.1.18): + - react-native-wgpu (0.1.19): - DoubleConversion - glog - hermes-engine @@ -1528,7 +1528,7 @@ SPEC CHECKSUMS: React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d React-Mapbuffer: bf56147c9775491e53122a94c423ac201417e326 react-native-safe-area-context: 851c62c48dce80ccaa5637b6aa5991a1bc36eca9 - react-native-wgpu: 0f842ae9566fdf3b6ff05034bf9cbe99e90c2b5e + react-native-wgpu: 5fd8cb5fd7bd00c88831d29438697d897fb680e8 React-nativeconfig: 9f223cd321823afdecf59ed00861ab2d69ee0fc1 React-NativeModulesApple: ff7efaff7098639db5631236cfd91d60abff04c0 React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994 diff --git a/apps/example/src/Cube/Cube.tsx b/apps/example/src/Cube/Cube.tsx index cbc8f9415..63c49b5ac 100644 --- a/apps/example/src/Cube/Cube.tsx +++ b/apps/example/src/Cube/Cube.tsx @@ -176,7 +176,7 @@ export function Cube() { return ( - + ); } diff --git a/apps/example/src/GradientTiles/GradientTiles.tsx b/apps/example/src/GradientTiles/GradientTiles.tsx index ef9379897..701cd3159 100644 --- a/apps/example/src/GradientTiles/GradientTiles.tsx +++ b/apps/example/src/GradientTiles/GradientTiles.tsx @@ -120,7 +120,7 @@ export function GradientTiles() { return ( - + span x: diff --git a/apps/example/src/Resize/Resize.tsx b/apps/example/src/Resize/Resize.tsx index d1ff685b0..467f5aaea 100644 --- a/apps/example/src/Resize/Resize.tsx +++ b/apps/example/src/Resize/Resize.tsx @@ -1,20 +1,41 @@ -import React, { useEffect, useRef } from "react"; -import { Animated, Dimensions, PixelRatio, View } from "react-native"; +import React, { useEffect } from "react"; +import { Dimensions, PixelRatio, View } from "react-native"; import { Canvas } from "react-native-wgpu"; +import Animated, { + cancelAnimation, + Easing, + useAnimatedStyle, + useDerivedValue, + useSharedValue, + withRepeat, + withTiming, +} from "react-native-reanimated"; import { redFragWGSL, triangleVertWGSL } from "../Triangle/triangle"; import { useWebGPU } from "../components/useWebGPU"; -const window = Dimensions.get("window"); +const win = Dimensions.get("window"); -export const Resize = () => { - const width = useRef(new Animated.Value(20)); - const widthRef = useRef(20); +export const useLoop = ({ duration }: { duration: number }) => { + const progress = useSharedValue(0); useEffect(() => { - width.current.addListener(({ value }) => { - widthRef.current = value; - }); - }, []); + progress.value = withRepeat( + withTiming(1, { duration, easing: Easing.inOut(Easing.ease) }), + -1, + true, + ); + return () => { + cancelAnimation(progress); + }; + }, [duration, progress]); + return progress; +}; + +export const Resize = () => { + const progress = useLoop({ duration: 4000 }); + const width = useDerivedValue(() => { + return 20 + progress.value * (win.width - 20); + }); const ref = useWebGPU(({ context, device, presentationFormat, canvas }) => { const sampleCount = 4; const pipeline = device.createRenderPipeline({ @@ -75,12 +96,13 @@ export const Resize = () => { } if (renderTargetView) { const commandEncoder = device.createCommandEncoder(); + const a = 0.7; const renderPassDescriptor: GPURenderPassDescriptor = { colorAttachments: [ { view: renderTargetView, resolveTarget: context.getCurrentTexture().createView(), - clearValue: [0.5, 0.5, 0.5, 1], + clearValue: [a, a, a, a], loadOp: "clear", storeOp: "store", }, @@ -97,30 +119,13 @@ export const Resize = () => { } }; }); - - useEffect(() => { - Animated.loop( - Animated.sequence([ - Animated.timing(width.current, { - toValue: window.width, - duration: 4000, - useNativeDriver: false, - }), - Animated.timing(width.current, { - toValue: 20, - duration: 4000, - useNativeDriver: false, - }), - ]), - ).start(); - }, []); - + const style = useAnimatedStyle(() => { + return { width: width.value, flex: 1, backgroundColor: "cyan" }; + }); return ( - - + + ); diff --git a/apps/example/src/Triangle/HelloTriangle.tsx b/apps/example/src/Triangle/HelloTriangle.tsx index 421546f30..db93dc6f2 100644 --- a/apps/example/src/Triangle/HelloTriangle.tsx +++ b/apps/example/src/Triangle/HelloTriangle.tsx @@ -80,11 +80,7 @@ export function HelloTriangle() { return ( - + ); } diff --git a/apps/example/src/Triangle/HelloTriangleMSAA.tsx b/apps/example/src/Triangle/HelloTriangleMSAA.tsx index e83001222..6736d6fa5 100644 --- a/apps/example/src/Triangle/HelloTriangleMSAA.tsx +++ b/apps/example/src/Triangle/HelloTriangleMSAA.tsx @@ -91,7 +91,7 @@ export function HelloTriangleMSAA() { return ( - + ); } diff --git a/packages/webgpu/android/build.gradle b/packages/webgpu/android/build.gradle index 07d786ef4..7f9ce0e73 100644 --- a/packages/webgpu/android/build.gradle +++ b/packages/webgpu/android/build.gradle @@ -47,7 +47,7 @@ static def findNodeModules(baseDir) { basePath = basePath.getParent() } - throw new GradleException("react-native-filament: Failed to find node_modules/ path!") + throw new GradleException("react-native-wgpu: Failed to find node_modules/ path!") } def nodeModules = findNodeModules(projectDir) diff --git a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUAHBView.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUAHBView.java index 1db902289..073b7351e 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUAHBView.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUAHBView.java @@ -15,14 +15,12 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import java.util.LinkedList; -import java.util.Queue; - @SuppressLint("ViewConstructor") @RequiresApi(api = Build.VERSION_CODES.Q) -public class WebGPUAHBView extends View { +public class WebGPUAHBView extends View implements ImageReader.OnImageAvailableListener { + + private ImageReader mReader; - private final Queue mImageReaders = new LinkedList<>(); private Bitmap mBitmap = null; private final Matrix matrix = new Matrix(); @@ -34,48 +32,58 @@ public WebGPUAHBView(Context context, WebGPUAPI api) { mApi = api; } + private ImageReader createReader() { + ImageReader reader = ImageReader.newInstance(getWidth(), getHeight(), PixelFormat.RGBA_8888, 2, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | + HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + reader.setOnImageAvailableListener(this, null); + return reader; + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | - HardwareBuffer.USAGE_GPU_COLOR_OUTPUT; - ImageReader imageReader = ImageReader.newInstance(getWidth(), getHeight(), PixelFormat.RGBA_8888, 2, usage); - if (mImageReaders.isEmpty()) { - mApi.surfaceCreated(imageReader.getSurface()); + if (mReader == null) { + mReader = createReader(); + mApi.surfaceCreated(mReader.getSurface()); } else { - mApi.surfaceChanged(imageReader.getSurface()); + mApi.surfaceChanged(mReader.getSurface()); } - imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { - @Override - public void onImageAvailable(ImageReader reader) { - try (Image image = reader.acquireLatestImage()) { - if (image != null) { - HardwareBuffer hb = image.getHardwareBuffer(); - if (hb != null) { - Bitmap bitmap = Bitmap.wrapHardwareBuffer(hb, null); - if (bitmap != null) { - mBitmap = bitmap; - hb.close(); - invalidate(); - ImageReader imageReader = mImageReaders.poll(); - ImageReader ir; - while((ir = mImageReaders.poll()) != null) { - ir.close(); - } - mImageReaders.add(imageReader); - } - } + } + + @Override + public void onImageAvailable(ImageReader reader) { + try (Image image = reader.acquireLatestImage()) { + if (image != null) { + HardwareBuffer hb = image.getHardwareBuffer(); + if (hb != null) { + Bitmap bitmap = Bitmap.wrapHardwareBuffer(hb, null); + if (bitmap != null) { + mBitmap = bitmap; + hb.close(); + invalidate(); } } } - }, null); - mImageReaders.add(imageReader); + } } @Override protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); if (mBitmap != null) { + float viewWidth = getWidth(); + float viewHeight = getHeight(); + float bitmapWidth = mBitmap.getWidth(); + float bitmapHeight = mBitmap.getHeight(); + + // Calculate the scale factors + float scaleX = viewWidth / bitmapWidth; + float scaleY = viewHeight / bitmapHeight; + + // Reset the matrix and apply scaling + matrix.reset(); + matrix.setScale(scaleX, scaleY); + canvas.drawBitmap(mBitmap, matrix, null); } } diff --git a/packages/webgpu/android/src/main/java/com/webgpu/SurfaceView2.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUSurfaceViewWithSC.java similarity index 92% rename from packages/webgpu/android/src/main/java/com/webgpu/SurfaceView2.java rename to packages/webgpu/android/src/main/java/com/webgpu/WebGPUSurfaceViewWithSC.java index 77106685e..e999e098d 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/SurfaceView2.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUSurfaceViewWithSC.java @@ -15,13 +15,13 @@ @SuppressLint("ViewConstructor") @RequiresApi(api = Build.VERSION_CODES.Q) -public class SurfaceView2 extends SurfaceView implements SurfaceHolder.Callback { +public class WebGPUSurfaceViewWithSC extends SurfaceView implements SurfaceHolder.Callback { WebGPUAPI mApi; SurfaceControl mSurfaceControl; Surface mSurface; - public SurfaceView2(Context context, WebGPUAPI api) { + public WebGPUSurfaceViewWithSC(Context context, WebGPUAPI api) { super(context); mApi = api; getHolder().addCallback(this); @@ -48,7 +48,7 @@ public void surfaceCreated(@NonNull SurfaceHolder holder) { } else { SurfaceControl.Builder scb = new SurfaceControl.Builder(); scb.setName("WebGPUView"); - scb.setOpaque(false); + scb.setOpaque(true); scb.setBufferSize(getWidth(), getHeight()); scb.setParent(getSurfaceControl()); scb.setFormat(PixelFormat.RGBA_8888); diff --git a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUView.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUView.java index 6aa9565c7..1531befac 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUView.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUView.java @@ -5,37 +5,17 @@ import android.view.Surface; import android.view.View; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringDef; - import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.views.view.ReactViewGroup; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - public class WebGPUView extends ReactViewGroup implements WebGPUAPI { - public static final String SURFACE_VIEW = "SurfaceView"; - public static final String TEXTURE_VIEW = "TextureView"; - public static final String HARDWARE_BUFFER = "HardwareBuffer"; - public static final String SURFACE_VIEW2 = "SurfaceView2"; - - @Retention(RetentionPolicy.SOURCE) - @StringDef({ - SURFACE_VIEW, - TEXTURE_VIEW, - HARDWARE_BUFFER, - SURFACE_VIEW2 - }) - public @interface ViewType {} private int mContextId; - private @ViewType String mName = null; + private boolean mTransparent = false; private WebGPUModule mModule; - private View mView; + private View mView = null; WebGPUView(Context context) { super(context); @@ -51,28 +31,21 @@ public void setContextId(int contextId) { mContextId = contextId; } - public void setView(@NonNull @ViewType String name) { + public void setTransparent(boolean value) { Context ctx = getContext(); - if (mName == null || !mName.equals(name)) { - removeView(mView); - mName = name; - switch (name) { - case TEXTURE_VIEW -> mView = new WebGPUTextureView(ctx, this); - case HARDWARE_BUFFER -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - mView = new WebGPUAHBView(ctx, this); - } else { - throw new RuntimeException("HardwareBuffer Canvas implementation is only available on API Level 29 and above"); - } - } - case SURFACE_VIEW2 -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - mView = new SurfaceView2(ctx, this); - } else { - throw new RuntimeException("HardwareBuffer Canvas implementation is only available on API Level 29 and above"); - } + if (value != mTransparent || mView == null) { + if (mView != null) { + removeView(mView); + } + mTransparent = value; + if (mTransparent) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + mView = new WebGPUAHBView(ctx, this); + } else { + mView = new WebGPUTextureView(ctx, this); } - default -> mView = new WebGPUSurfaceView(ctx, this); + } else { + mView = new WebGPUSurfaceView(ctx, this); } addView(mView); } diff --git a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUViewManager.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUViewManager.java index dd4b83183..71041b326 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUViewManager.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUViewManager.java @@ -25,9 +25,9 @@ public WebGPUView createViewInstance(@NonNull ThemedReactContext context) { } @Override - @ReactProp(name = "androidView") - public void setAndroidView(WebGPUView view, @Nullable String value) { - view.setView(value == null ? WebGPUView.SURFACE_VIEW : value); + @ReactProp(name = "transparent") + public void setTransparent(WebGPUView view, boolean value) { + view.setTransparent(value); } @Override diff --git a/packages/webgpu/android/src/oldarch/com/webgpu/WebGPUViewManagerSpec.java b/packages/webgpu/android/src/oldarch/com/webgpu/WebGPUViewManagerSpec.java index d154ac4f3..cd7a0fdb4 100644 --- a/packages/webgpu/android/src/oldarch/com/webgpu/WebGPUViewManagerSpec.java +++ b/packages/webgpu/android/src/oldarch/com/webgpu/WebGPUViewManagerSpec.java @@ -8,5 +8,5 @@ public abstract class WebGPUViewManagerSpec extends SimpleViewManager { public abstract void setContextId(T view, int contextId); - public abstract void setAndroidView(T view, @Nullable String name); + public abstract void setTransparent(T view, boolean transparency); } diff --git a/packages/webgpu/apple/WebGPUModule.mm b/packages/webgpu/apple/WebGPUModule.mm index 7b77cefff..35c7459e4 100644 --- a/packages/webgpu/apple/WebGPUModule.mm +++ b/packages/webgpu/apple/WebGPUModule.mm @@ -46,19 +46,19 @@ - (void)invalidate { } RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge]; if (!cxxBridge.runtime) { - NSLog(@"Failed to install react-native-filament: RCTBridge is not a " + NSLog(@"Failed to install react-native-wgpu: RCTBridge is not a " @"RCTCxxBridge!"); return [NSNumber numberWithBool:NO]; } jsi::Runtime *runtime = (jsi::Runtime *)cxxBridge.runtime; if (!runtime) { - NSLog(@"Failed to install react-native-filament: jsi::Runtime* was null!"); + NSLog(@"Failed to install react-native-wgpu: jsi::Runtime* was null!"); return [NSNumber numberWithBool:NO]; } std::shared_ptr jsInvoker = cxxBridge.jsCallInvoker; if (!jsInvoker) { - NSLog(@"Failed to install react-native-filament: react::CallInvoker was " + NSLog(@"Failed to install react-native-wgpu: react::CallInvoker was " @"null!"); return [NSNumber numberWithBool:NO]; } @@ -74,15 +74,6 @@ - (void)invalidate { return @true; } -// RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(createSurfaceContext -// : (double)contextId) { -// int contextIdInt = contextId; -// RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; -// auto runtime = (jsi::Runtime *)cxxBridge.runtime; - -// return @true; -// } - #ifdef RCT_NEW_ARCH_ENABLED - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params { diff --git a/packages/webgpu/cpp/rnwgpu/api/WebGPUCanvasContextFactory.h b/packages/webgpu/cpp/rnwgpu/api/WebGPUCanvasContextFactory.h deleted file mode 100644 index 001bbdf9a..000000000 --- a/packages/webgpu/cpp/rnwgpu/api/WebGPUCanvasContextFactory.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "Unions.h" - -#include "webgpu/webgpu_cpp.h" - -#include "RNFHybridObject.h" - -#include "AsyncRunner.h" - -#include "GPUCanvasContext.h" - -#ifdef __APPLE__ -#include "WebGPUModule.h" -#endif - -namespace rnwgpu { - -namespace m = margelo; - -class WebGPUCanvasContextFactory : public m::HybridObject { -public: - GPUCanvasContextFactory() : HybridObject("GPUCanvasContextFactory") {} - -public: - std::shared_ptr Make(uint64_t surface, int width, - int height) { -#ifdef __APPLE__ - wgpu::SurfaceDescriptorFromMetalLayer metalSurfaceDesc; - metalSurfaceDesc.layer = reinterpret_cast(surface); - - wgpu::SurfaceDescriptor surfaceDescriptor; - surfaceDescriptor.nextInChain = &metalSurfaceDesc; - - auto surfaceGpu = std::make_unique( - WebGPUModule::getManager()->getGPU()->get().CreateSurface( - &surfaceDescriptor)); - const float scaleFactor = 1; - const float scaledWidth = width * scaleFactor; - const float scaledHeight = height * scaleFactor; - - rnwgpu::SurfaceData surfaceData{scaledWidth, scaledHeight, - std::move(surfaceGpu)}; -#elif __ANDROID__ - throw std::runtime_error("Not implemented"); -#endif - return std::make_shared(surfaceData); - } - - void loadHybridMethods() override { - registerHybridGetter("Make", &GPUCanvasContextFactory::Make, this); - } -}; - -} // namespace rnwgpu diff --git a/packages/webgpu/package.json b/packages/webgpu/package.json index db36c5979..b13e6be84 100644 --- a/packages/webgpu/package.json +++ b/packages/webgpu/package.json @@ -1,6 +1,6 @@ { "name": "react-native-wgpu", - "version": "0.1.18", + "version": "0.1.19", "description": "React Native WebGPU", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/packages/webgpu/src/Canvas.tsx b/packages/webgpu/src/Canvas.tsx index 8dc8ee08b..65e0c3965 100644 --- a/packages/webgpu/src/Canvas.tsx +++ b/packages/webgpu/src/Canvas.tsx @@ -92,69 +92,55 @@ const useSizePaper = (_ref: RefObject) => { export const Canvas = forwardRef< CanvasRef, - ViewProps & { androidTransparency?: boolean; androidExperimental?: boolean } ->( - ( - { onLayout: _onLayout, androidTransparency, androidExperimental, ...props }, - ref, - ) => { - const viewRef = useRef(null); - const FABRIC = RNWebGPU.fabric; - const useSize = FABRIC ? useSizeFabric : useSizePaper; - const [contextId, _] = useState(() => generateContextId()); - const cb = useRef<() => void>(); - const { size, onLayout } = useSize(viewRef); - useEffect(() => { - if (size && cb.current) { - cb.current(); + ViewProps & { transparent?: boolean } +>(({ onLayout: _onLayout, transparent, ...props }, ref) => { + const viewRef = useRef(null); + const FABRIC = RNWebGPU.fabric; + const useSize = FABRIC ? useSizeFabric : useSizePaper; + const [contextId, _] = useState(() => generateContextId()); + const cb = useRef<() => void>(); + const { size, onLayout } = useSize(viewRef); + useEffect(() => { + if (size && cb.current) { + cb.current(); + } + }, [size]); + useImperativeHandle(ref, () => ({ + getContextId: () => contextId, + getNativeSurface: () => { + if (size === null) { + throw new Error("[WebGPU] Canvas size is not available yet"); } - }, [size]); - useImperativeHandle(ref, () => ({ - getContextId: () => contextId, - getNativeSurface: () => { - if (size === null) { - throw new Error("[WebGPU] Canvas size is not available yet"); - } - return RNWebGPU.getNativeSurface(contextId); - }, - whenReady(callback: () => void) { - if (size === null) { - cb.current = callback; - } else { - callback(); - } - }, - getContext(contextName: "webgpu"): RNCanvasContext | null { - if (contextName !== "webgpu") { - throw new Error(`[WebGPU] Unsupported context: ${contextName}`); - } - if (size === null) { - throw new Error("[WebGPU] Canvas size is not available yet"); - } - return RNWebGPU.MakeWebGPUCanvasContext( - contextId, - size.width, - size.height, - ); - }, - })); - return ( - - - - ); - }, -); + return RNWebGPU.getNativeSurface(contextId); + }, + whenReady(callback: () => void) { + if (size === null) { + cb.current = callback; + } else { + callback(); + } + }, + getContext(contextName: "webgpu"): RNCanvasContext | null { + if (contextName !== "webgpu") { + throw new Error(`[WebGPU] Unsupported context: ${contextName}`); + } + if (size === null) { + throw new Error("[WebGPU] Canvas size is not available yet"); + } + return RNWebGPU.MakeWebGPUCanvasContext( + contextId, + size.width, + size.height, + ); + }, + })); + return ( + + + + ); +}); diff --git a/packages/webgpu/src/WebGPUViewNativeComponent.ts b/packages/webgpu/src/WebGPUViewNativeComponent.ts index 8826faad8..fc582de10 100644 --- a/packages/webgpu/src/WebGPUViewNativeComponent.ts +++ b/packages/webgpu/src/WebGPUViewNativeComponent.ts @@ -3,8 +3,8 @@ import type { ViewProps } from "react-native"; import type { Int32 } from "react-native/Libraries/Types/CodegenTypes"; interface NativeProps extends ViewProps { - contextId?: Int32; - androidView?: string; + contextId: Int32; + transparent: boolean; } // eslint-disable-next-line import/no-default-export