From 2cacfe558752965ba4e4e7004f574002cb895833 Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Sun, 10 May 2026 14:05:12 +0200 Subject: [PATCH 1/2] =?UTF-8?q?chore(=F0=9F=90=99):=20update=20to=20TypeGP?= =?UTF-8?q?U=200.11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/example/babel.config.js | 4 +- apps/example/package.json | 6 +- .../src/GradientTiles/GradientTiles.tsx | 166 +++----------- .../example/src/GradientTiles/gradientWgsl.ts | 39 ---- yarn.lock | 207 ++++++++++++++++-- 5 files changed, 230 insertions(+), 192 deletions(-) delete mode 100644 apps/example/src/GradientTiles/gradientWgsl.ts diff --git a/apps/example/babel.config.js b/apps/example/babel.config.js index 77ed5950b..2fbb5703a 100644 --- a/apps/example/babel.config.js +++ b/apps/example/babel.config.js @@ -2,6 +2,8 @@ module.exports = { presets: ['module:@react-native/babel-preset'], plugins: [ 'react-native-reanimated/plugin', - "transform-inline-environment-variables", + 'transform-inline-environment-variables', + '@babel/plugin-transform-class-static-block', + 'unplugin-typegpu/babel', ], }; diff --git a/apps/example/package.json b/apps/example/package.json index bc1e36625..071126535 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -25,6 +25,7 @@ "@tensorflow/tfjs": "^4.22.0", "@tensorflow/tfjs-backend-webgpu": "^4.22.0", "@tensorflow/tfjs-vis": "^1.5.1", + "@typegpu/react": "^0.11.0", "async-mutex": "^0.5.0", "babel-plugin-transform-inline-environment-variables": "^0.4.4", "fast-text-encoding": "^1.0.6", @@ -40,7 +41,7 @@ "react-native-worklets": "0.7.2", "teapot": "^1.0.0", "three": "0.172.0", - "typegpu": "^0.3.2", + "typegpu": "^0.11.4", "wgpu-matrix": "^3.0.2" }, "devDependencies": { @@ -66,7 +67,8 @@ "prettier": "2.8.8", "react-native-test-app": "4.4.10", "react-test-renderer": "18.2.0", - "typescript": "5.0.4" + "typescript": "5.0.4", + "unplugin-typegpu": "https://pkg.pr.new/unplugin-typegpu@bbce6b7.tgz" }, "engines": { "node": ">=18" diff --git a/apps/example/src/GradientTiles/GradientTiles.tsx b/apps/example/src/GradientTiles/GradientTiles.tsx index 3268cb7f4..16b542a2d 100644 --- a/apps/example/src/GradientTiles/GradientTiles.tsx +++ b/apps/example/src/GradientTiles/GradientTiles.tsx @@ -1,132 +1,42 @@ -import { useEffect, useMemo, useRef, useState } from "react"; -import { Button, PixelRatio, StyleSheet, Text, View } from "react-native"; -import type { CanvasRef } from "react-native-wgpu"; -import { Canvas, useDevice } from "react-native-wgpu"; -import * as d from "typegpu/data"; -import tgpu, { type TgpuBindGroup, type TgpuBuffer } from "typegpu"; - -import { vertWGSL, fragWGSL } from "./gradientWgsl"; - -const Span = d.struct({ - x: d.u32, - y: d.u32, -}); - -const bindGroupLayout = tgpu.bindGroupLayout({ - span: { uniform: Span }, -}); - -interface RenderingState { - pipeline: GPURenderPipeline; - spanBuffer: TgpuBuffer; - bindGroup: TgpuBindGroup<(typeof bindGroupLayout)["entries"]>; -} - -function useRoot() { - const { device } = useDevice(); - - return useMemo( - () => (device ? tgpu.initFromDevice({ device }) : null), - [device], - ); -} +import { useMemo, useState } from "react"; +import { Button, StyleSheet, Text, View } from "react-native"; +import { Canvas } from "react-native-wgpu"; +import { common, d, std } from "typegpu"; +import { useConfigureContext, useFrame, useMirroredUniform, useRoot } from "@typegpu/react"; export function GradientTiles() { - const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); - const [state, setState] = useState(null); - const [spanX, setSpanX] = useState(4); - const [spanY, setSpanY] = useState(4); const root = useRoot(); - const { device = null } = root ?? {}; - const ref = useRef(null); - useEffect(() => { - if (!device || !root || state !== null) { - return; - } - const context = ref.current?.getContext("webgpu")!; - - const canvas = context.canvas as HTMLCanvasElement; - canvas.width = canvas.clientWidth * PixelRatio.get(); - canvas.height = canvas.clientHeight * PixelRatio.get(); - context.configure({ - device, - format: presentationFormat, - }); - - const spanBuffer = root - .createBuffer(Span, { x: 10, y: 10 }) - .$usage("uniform"); - - const shader = device.createShaderModule({ - code: tgpu.resolve({ - template: `${vertWGSL} ${fragWGSL}`, - externals: { - _EXT_: { - span: bindGroupLayout.bound.span, - }, - }, - }), - }); + const [spanX, setSpanX] = useState(4); + const [spanY, setSpanY] = useState(4); - const pipeline = device.createRenderPipeline({ - layout: device.createPipelineLayout({ - bindGroupLayouts: [root.unwrap(bindGroupLayout)], - }), - vertex: { - module: shader, + // Mirroring React state on the GPU as a uniform + const span = useMirroredUniform(d.vec2u, d.vec2u(spanX, spanY)); + + const pipeline = useMemo(() => { + // Defining a full-screen shader + return root.createRenderPipeline({ + vertex: common.fullScreenTriangle, + fragment: ({ uv }) => { + "use gpu"; + const red = std.floor(uv.x * d.f32(span.$.x)) / d.f32(span.$.x); + const green = std.floor(uv.y * d.f32(span.$.y)) / d.f32(span.$.y); + return d.vec4f(red, green, 0.5, 1.0); }, - fragment: { - module: shader, - targets: [ - { - format: presentationFormat, - }, - ], - }, - primitive: { - topology: "triangle-strip", - }, - }); - - const bindGroup = root.createBindGroup(bindGroupLayout, { - span: spanBuffer, }); + }, [root, span]); - setState({ bindGroup, pipeline, spanBuffer }); - }, [ref, device, root, presentationFormat, state]); - - useEffect(() => { - if (!device || !root || !state) { - return; - } - - const { bindGroup, pipeline, spanBuffer } = state; - const context = ref.current?.getContext("webgpu")!; - const textureView = context.getCurrentTexture().createView(); - const renderPassDescriptor: GPURenderPassDescriptor = { - colorAttachments: [ - { - view: textureView, - clearValue: [0, 0, 0, 0], - loadOp: "clear", - storeOp: "store", - }, - ], - }; + const { ref, ctxRef } = useConfigureContext(); - spanBuffer.write({ x: spanX, y: spanY }); + useFrame(() => { + const ctx = ctxRef.current; + if (!ctx) return; - const commandEncoder = device.createCommandEncoder(); - const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); - passEncoder.setPipeline(pipeline); - passEncoder.setBindGroup(0, root.unwrap(bindGroup)); - passEncoder.draw(4); - passEncoder.end(); + // Drawing to the canvas each frame + pipeline.withColorAttachment({ view: ctx }).draw(3); - device.queue.submit([commandEncoder.finish()]); - context.present(); - }, [ref, device, root, spanX, spanY, state]); + ctx.present?.(); + }); return ( @@ -134,26 +44,14 @@ export function GradientTiles() { span x: -