From fb987ef353c92fe48775eda29c815819a6271a42 Mon Sep 17 00:00:00 2001 From: Milosz Filimowski Date: Wed, 16 Sep 2026 10:52:40 +0200 Subject: [PATCH 1/2] fix(react-native): use own WebGPU flag constants react-native-webgpu >= 0.8.4 evaluates its constants module before the native install runs, so GPUTextureUsage, GPUShaderStage and GPUBufferUsage imported from it are undefined and every camera effect failed with "Cannot read property 'TEXTURE_BINDING' of undefined". The values are fixed by the WebGPU spec, so use the package's own constants (core/constants.ts) like the web path already does. --- CHANGELOG.md | 4 ++++ src/core/constants.ts | 6 ++++++ src/react-native/webgpu/cameraPassthroughPipeline.ts | 6 +++--- src/react-native/webgpu/cameraShaderBindings.ts | 7 ++++--- src/react-native/webgpu/cameraTextureResolver.ts | 5 ++--- src/react-native/webgpu/webGpuFrameRenderLoop.ts | 10 ++++------ 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2672d9b..de29a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- React Native: the WebGPU usage and shader-stage flags are now the package's own constants instead of the `GPUTextureUsage` / `GPUShaderStage` / `GPUBufferUsage` re-exports of `react-native-webgpu`. Since react-native-webgpu 0.8.4 those re-exports are `undefined` (the module reads the globals before the native install runs), which made every camera effect fail with `Cannot read property 'TEXTURE_BINDING' of undefined`. + ## 0.1.4 - Background image: the image loads on React Native. React Native's `Blob` cannot wrap an `ArrayBuffer`, so decoding threw and the effect silently stayed a passthrough; the bytes now go to `createImageBitmap` directly there. diff --git a/src/core/constants.ts b/src/core/constants.ts index b61e3e6..9acaa3d 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -13,6 +13,12 @@ export const BUFFER_USAGE = { UNIFORM: 0x40, } as const; +export const SHADER_STAGE = { + VERTEX: 0x1, + FRAGMENT: 0x2, + COMPUTE: 0x4, +} as const; + export const IDENTITY_UV_TRANSFORM = new Float32Array([ 1, 0, 0, 0, 1, 0, 0, 0, 1, ]); diff --git a/src/react-native/webgpu/cameraPassthroughPipeline.ts b/src/react-native/webgpu/cameraPassthroughPipeline.ts index 5c4b30d..3530870 100644 --- a/src/react-native/webgpu/cameraPassthroughPipeline.ts +++ b/src/react-native/webgpu/cameraPassthroughPipeline.ts @@ -1,8 +1,8 @@ -import { GPUBufferUsage, GPUShaderStage } from "react-native-webgpu"; import tgpu from "typegpu"; import * as d from "typegpu/data"; import { add, div, mul, sub } from "typegpu/std"; +import { BUFFER_USAGE, SHADER_STAGE } from "../../core/constants"; import { type CameraPixelLayout, type CameraShaderBindings, @@ -145,7 +145,7 @@ export function createCameraPassthroughPipeline( entries: [ { binding: 0, - visibility: GPUShaderStage.FRAGMENT, + visibility: SHADER_STAGE.FRAGMENT, buffer: { type: "uniform" }, }, ], @@ -153,7 +153,7 @@ export function createCameraPassthroughPipeline( const cropParamsBuffer = device.createBuffer({ label: "fishjam-camera-passthrough-crop-params", size: FRAME_CROP_BUFFER_BYTES, - usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + usage: BUFFER_USAGE.UNIFORM | BUFFER_USAGE.COPY_DST, }); const cropBindGroup = device.createBindGroup({ layout: cropBindGroupLayout, diff --git a/src/react-native/webgpu/cameraShaderBindings.ts b/src/react-native/webgpu/cameraShaderBindings.ts index edeb804..a3c98de 100644 --- a/src/react-native/webgpu/cameraShaderBindings.ts +++ b/src/react-native/webgpu/cameraShaderBindings.ts @@ -1,7 +1,8 @@ -import { GPUShaderStage } from "react-native-webgpu"; import tgpu, { type TgpuFn } from "typegpu"; import * as d from "typegpu/data"; +import { SHADER_STAGE } from "../../core/constants"; + // The camera arrives as a `texture_external`, which TypeGPU 0.11 cannot resolve inside a TGSL // function (its type only resolves as a declaration, not as a sampled value in codegen). So // `sampleCamera` is authored as a WGSL-bodied `tgpu.fn`: it is a first-class TypeGPU function that @@ -141,8 +142,8 @@ export function createCameraShaderBindings( const bindGroupLayout = device.createBindGroupLayout({ label: "fishjam-camera-shader-bindings", entries: [ - { binding: 0, visibility: GPUShaderStage.FRAGMENT, externalTexture: {} }, - { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} }, + { binding: 0, visibility: SHADER_STAGE.FRAGMENT, externalTexture: {} }, + { binding: 1, visibility: SHADER_STAGE.FRAGMENT, sampler: {} }, ], }); const sampler = device.createSampler({ diff --git a/src/react-native/webgpu/cameraTextureResolver.ts b/src/react-native/webgpu/cameraTextureResolver.ts index 30057c0..f73989f 100644 --- a/src/react-native/webgpu/cameraTextureResolver.ts +++ b/src/react-native/webgpu/cameraTextureResolver.ts @@ -1,5 +1,4 @@ -import { GPUTextureUsage } from "react-native-webgpu"; - +import { TEXTURE_USAGE } from "../../core/constants"; import { createCameraPassthroughPipeline, encodeCameraPassthrough, @@ -50,7 +49,7 @@ export function createCameraTextureResolver( label: "fishjam-resolved-camera", format: "rgba8unorm", size: [size.width, size.height], - usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT, + usage: TEXTURE_USAGE.TEXTURE_BINDING | TEXTURE_USAGE.RENDER_ATTACHMENT, }); return { texture, diff --git a/src/react-native/webgpu/webGpuFrameRenderLoop.ts b/src/react-native/webgpu/webGpuFrameRenderLoop.ts index be6d77f..0756ce7 100644 --- a/src/react-native/webgpu/webGpuFrameRenderLoop.ts +++ b/src/react-native/webgpu/webGpuFrameRenderLoop.ts @@ -3,11 +3,9 @@ import { type PooledTrack, pushFrame, } from "@fishjam-cloud/react-native-webrtc"; -import { - type GPUSharedTextureMemory, - GPUTextureUsage, -} from "react-native-webgpu"; +import type { GPUSharedTextureMemory } from "react-native-webgpu"; +import { TEXTURE_USAGE } from "../../core/constants"; import type { WorkletBufferDescriptor } from "../internal/pooledTrackAllocation"; import { type CameraShaderBindings, @@ -84,8 +82,8 @@ export function createWebGpuFrameRenderer( const runtime: WebGpuRuntime = getWebGpuRuntime(); const outputSurfaceFormat = getOutputSurfaceFormat(); - // Captured as a plain number: the worklet must not close over the GPUTextureUsage namespace. - const renderAttachmentUsage = GPUTextureUsage.RENDER_ATTACHMENT; + // Captured as a plain number so the worklet does not close over an object. + const renderAttachmentUsage = TEXTURE_USAGE.RENDER_ATTACHMENT; const state: RendererState = { poolCursor: 0, importedByIndex: {} }; const renderFrame = ( From 9d32c6ddc680b437924f3d88d8931276d436babe Mon Sep 17 00:00:00 2001 From: Milosz Filimowski Date: Wed, 16 Sep 2026 11:10:24 +0200 Subject: [PATCH 2/2] chore: release 0.1.5 --- CHANGELOG.md | 2 +- package.json | 4 ++-- yarn.lock | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de29a0e..974ac26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.1.5 - React Native: the WebGPU usage and shader-stage flags are now the package's own constants instead of the `GPUTextureUsage` / `GPUShaderStage` / `GPUBufferUsage` re-exports of `react-native-webgpu`. Since react-native-webgpu 0.8.4 those re-exports are `undefined` (the module reads the globals before the native install runs), which made every camera effect fail with `Cannot read property 'TEXTURE_BINDING' of undefined`. diff --git a/package.json b/package.json index ee090be..c4b79df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fishjam-cloud/video-effects", - "version": "0.1.4", + "version": "0.1.5", "description": "WebGPU video effects and person-segmentation providers for Fishjam video sources", "license": "Apache-2.0", "author": "Fishjam Team", @@ -145,7 +145,7 @@ "react": "19.1.0", "react-native": "0.86.3", "react-native-builder-bob": "^0.18.2", - "react-native-webgpu": "0.5.15", + "react-native-webgpu": "0.10.1", "typescript": "^5.8.3", "unplugin-typegpu": "^0.12.0" }, diff --git a/yarn.lock b/yarn.lock index 1df5fd5..bb1c6e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1461,7 +1461,7 @@ __metadata: react: "npm:19.1.0" react-native: "npm:0.86.3" react-native-builder-bob: "npm:^0.18.2" - react-native-webgpu: "npm:0.5.15" + react-native-webgpu: "npm:0.10.1" typed-binary: "npm:^4.3.3" typegpu: "npm:^0.12.0" typescript: "npm:^5.8.3" @@ -5335,9 +5335,9 @@ __metadata: languageName: node linkType: hard -"react-native-webgpu@npm:0.5.15": - version: 0.5.15 - resolution: "react-native-webgpu@npm:0.5.15" +"react-native-webgpu@npm:0.10.1": + version: 0.10.1 + resolution: "react-native-webgpu@npm:0.10.1" peerDependencies: react: "*" react-native: ">=0.81.0" @@ -5348,7 +5348,7 @@ __metadata: optional: true react-native-worklets: optional: true - checksum: 10c0/91430eb36a5ff5f7e43519a79b384bc9648776c2b4788900e8f9154a706cc8c60c101decc3ef32631cef300f2f636e9aeb038ccf90b15dd8cbb7f8381f39fa4b + checksum: 10c0/fd72b41e19819008d6966159a153589fd04f06c1d8ef1665ed37f3ba76af1096ff9e8eff0bbbf1f4d0ee2f59377e675e95d41286166f4e58488e845a038b93ca languageName: node linkType: hard