@fishjam-cloud/react-native-custom-video-source 0.29.0

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 (58) hide show
  1. package/README.md +70 -0
  2. package/dist/module/index.js +22 -0
  3. package/dist/module/index.js.map +1 -0
  4. package/dist/module/internal/stopStreamTracks.js +9 -0
  5. package/dist/module/internal/stopStreamTracks.js.map +1 -0
  6. package/dist/module/internal/toError.js +5 -0
  7. package/dist/module/internal/toError.js.map +1 -0
  8. package/dist/module/internal/useManagedForwardTrack.js +63 -0
  9. package/dist/module/internal/useManagedForwardTrack.js.map +1 -0
  10. package/dist/module/internal/useManagedPooledTrack.js +120 -0
  11. package/dist/module/internal/useManagedPooledTrack.js.map +1 -0
  12. package/dist/module/webgpu/cameraPassthroughPipeline.js +237 -0
  13. package/dist/module/webgpu/cameraPassthroughPipeline.js.map +1 -0
  14. package/dist/module/webgpu/cameraShaderBindings.js +126 -0
  15. package/dist/module/webgpu/cameraShaderBindings.js.map +1 -0
  16. package/dist/module/webgpu/cameraTextureResolver.js +52 -0
  17. package/dist/module/webgpu/cameraTextureResolver.js.map +1 -0
  18. package/dist/module/webgpu/cropUtilities.js +121 -0
  19. package/dist/module/webgpu/cropUtilities.js.map +1 -0
  20. package/dist/module/webgpu/frameRenderContext.js +2 -0
  21. package/dist/module/webgpu/frameRenderContext.js.map +1 -0
  22. package/dist/module/webgpu/requiredFeatures.js +46 -0
  23. package/dist/module/webgpu/requiredFeatures.js.map +1 -0
  24. package/dist/module/webgpu/useCameraWebGpuDevice.js +114 -0
  25. package/dist/module/webgpu/useCameraWebGpuDevice.js.map +1 -0
  26. package/dist/module/webgpu/webGpuRuntime.js +25 -0
  27. package/dist/module/webgpu/webGpuRuntime.js.map +1 -0
  28. package/dist/module/webgpu.js +25 -0
  29. package/dist/module/webgpu.js.map +1 -0
  30. package/dist/typescript/index.d.ts +21 -0
  31. package/dist/typescript/index.d.ts.map +1 -0
  32. package/dist/typescript/internal/stopStreamTracks.d.ts +4 -0
  33. package/dist/typescript/internal/stopStreamTracks.d.ts.map +1 -0
  34. package/dist/typescript/internal/toError.d.ts +3 -0
  35. package/dist/typescript/internal/toError.d.ts.map +1 -0
  36. package/dist/typescript/internal/useManagedForwardTrack.d.ts +17 -0
  37. package/dist/typescript/internal/useManagedForwardTrack.d.ts.map +1 -0
  38. package/dist/typescript/internal/useManagedPooledTrack.d.ts +26 -0
  39. package/dist/typescript/internal/useManagedPooledTrack.d.ts.map +1 -0
  40. package/dist/typescript/webgpu/cameraPassthroughPipeline.d.ts +46 -0
  41. package/dist/typescript/webgpu/cameraPassthroughPipeline.d.ts.map +1 -0
  42. package/dist/typescript/webgpu/cameraShaderBindings.d.ts +69 -0
  43. package/dist/typescript/webgpu/cameraShaderBindings.d.ts.map +1 -0
  44. package/dist/typescript/webgpu/cameraTextureResolver.d.ts +39 -0
  45. package/dist/typescript/webgpu/cameraTextureResolver.d.ts.map +1 -0
  46. package/dist/typescript/webgpu/cropUtilities.d.ts +67 -0
  47. package/dist/typescript/webgpu/cropUtilities.d.ts.map +1 -0
  48. package/dist/typescript/webgpu/frameRenderContext.d.ts +61 -0
  49. package/dist/typescript/webgpu/frameRenderContext.d.ts.map +1 -0
  50. package/dist/typescript/webgpu/requiredFeatures.d.ts +25 -0
  51. package/dist/typescript/webgpu/requiredFeatures.d.ts.map +1 -0
  52. package/dist/typescript/webgpu/useCameraWebGpuDevice.d.ts +27 -0
  53. package/dist/typescript/webgpu/useCameraWebGpuDevice.d.ts.map +1 -0
  54. package/dist/typescript/webgpu/webGpuRuntime.d.ts +14 -0
  55. package/dist/typescript/webgpu/webGpuRuntime.d.ts.map +1 -0
  56. package/dist/typescript/webgpu.d.ts +24 -0
  57. package/dist/typescript/webgpu.d.ts.map +1 -0
  58. package/package.json +109 -0
@@ -0,0 +1,126 @@
1
+ import { Platform } from 'react-native';
2
+ import { GPUShaderStage } from 'react-native-webgpu';
3
+ import tgpu from 'typegpu';
4
+ import * as d from 'typegpu/data';
5
+
6
+ // The camera arrives as a `texture_external`, which TypeGPU 0.11 cannot resolve inside a TGSL
7
+ // function (its type only resolves as a declaration, not as a sampled value in codegen). So
8
+ // `sampleCamera` is authored as a WGSL-bodied `tgpu.fn`: it is a first-class TypeGPU function that
9
+ // TGSL shaders can call, but its body is WGSL and it references the external texture + sampler by
10
+ // name — those bindings are declared separately by {@link CameraShaderBindings.bindingDeclarations}
11
+ // (also WGSL, for the same reason). This is the one part of the shader that is not authored in TGSL.
12
+ //
13
+ // On Android, the camera arrives as an opaque YCbCr AHardwareBuffer and Dawn's Vulkan path forces
14
+ // an identity sampler conversion, so sampling the external texture returns RAW [Y, Cb, Cr] — the
15
+ // BT.709 limited-range decode below must run in-shader. iOS (NV12 IOSurface) samples as
16
+ // ready-to-use RGB.
17
+ const SAMPLE_CAMERA_ANDROID = /* wgsl */`(uv: vec2f) -> vec4f {
18
+ let rawSample = textureSampleBaseClampToEdge(fishjamCameraTexture, fishjamCameraSampler, uv);
19
+ let luma = rawSample.r - 0.0627451;
20
+ let chromaBlue = rawSample.g - 0.5;
21
+ let chromaRed = rawSample.b - 0.5;
22
+ let rgb = vec3f(
23
+ 1.164384 * luma + 1.792741 * chromaRed,
24
+ 1.164384 * luma - 0.213249 * chromaBlue - 0.532909 * chromaRed,
25
+ 1.164384 * luma + 2.112402 * chromaBlue,
26
+ );
27
+ return vec4f(clamp(rgb, vec3f(0.0), vec3f(1.0)), 1.0);
28
+ }`;
29
+ const SAMPLE_CAMERA_IOS = /* wgsl */`(uv: vec2f) -> vec4f {
30
+ return textureSampleBaseClampToEdge(fishjamCameraTexture, fishjamCameraSampler, uv);
31
+ }`;
32
+
33
+ /**
34
+ * Samples the live camera and returns upright RGB on both platforms (the Android in-shader BT.709
35
+ * YUV decode is included automatically). A TypeGPU function you can call from your own TGSL
36
+ * fragment shaders. It reads the camera texture + sampler declared by
37
+ * {@link CameraShaderBindings.bindingDeclarations}, which you must prepend to the resolved shader.
38
+ *
39
+ * @group WebGPU
40
+ */
41
+ export const sampleCamera = tgpu.fn([d.vec2f], d.vec4f)(Platform.OS === 'android' ? SAMPLE_CAMERA_ANDROID : SAMPLE_CAMERA_IOS).$name('sampleCamera');
42
+
43
+ /** Options for {@link createCameraShaderBindings}. */
44
+
45
+ /**
46
+ * Everything a fragment shader needs to sample the live camera. Build once at setup with
47
+ * {@link createCameraShaderBindings}; the fields are safe to capture into the frame worklet.
48
+ *
49
+ * @group WebGPU
50
+ */
51
+
52
+ function buildBindingDeclarations(bindGroupIndex) {
53
+ return /* wgsl */`@group(${bindGroupIndex}) @binding(0) var fishjamCameraTexture: texture_external;
54
+ @group(${bindGroupIndex}) @binding(1) var fishjamCameraSampler: sampler;
55
+ `;
56
+ }
57
+
58
+ /**
59
+ * Builds the camera-sampling bindings against the device your pipelines use. Pass the result to
60
+ * the source hook's `cameraShaderBindings` option and the render context delivers a ready-made
61
+ * `cameraBindGroup` every frame — set it at {@link CameraShaderBindings.bindGroupIndex} and call
62
+ * `sampleCamera(uv)` in your fragment shader.
63
+ *
64
+ * ```ts
65
+ * const cam = createCameraShaderBindings(device);
66
+ * const fragment = tgpu.fragmentFn({ in: { uv: d.location(0, d.vec2f) }, out: d.vec4f })((input) => {
67
+ * return cam.sampleCamera(input.uv);
68
+ * });
69
+ * const wgsl = cam.bindingDeclarations + tgpu.resolve({ externals: { fragment } });
70
+ * ```
71
+ *
72
+ * @group WebGPU
73
+ */
74
+ export function createCameraShaderBindings(device, options = {}) {
75
+ const bindGroupIndex = options.bindGroupIndex ?? 0;
76
+ const bindGroupLayout = device.createBindGroupLayout({
77
+ label: 'fishjam-camera-shader-bindings',
78
+ entries: [{
79
+ binding: 0,
80
+ visibility: GPUShaderStage.FRAGMENT,
81
+ externalTexture: {}
82
+ }, {
83
+ binding: 1,
84
+ visibility: GPUShaderStage.FRAGMENT,
85
+ sampler: {}
86
+ }]
87
+ });
88
+ const sampler = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(device.createSampler({
89
+ magFilter: 'linear',
90
+ minFilter: 'linear'
91
+ }), "sampler");
92
+ return {
93
+ sampleCamera,
94
+ bindingDeclarations: buildBindingDeclarations(bindGroupIndex),
95
+ bindGroupLayout,
96
+ sampler,
97
+ bindGroupIndex
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Builds the per-frame bind group for the live camera texture. The source hook already does this
103
+ * for you when you pass `cameraShaderBindings` in its options (see the render context's
104
+ * `cameraBindGroup`); call it yourself only for advanced multi-layout setups. Worklet-safe.
105
+ *
106
+ * A camera texture expires when the frame ends, so a bind group referencing it must be rebuilt
107
+ * every frame — never cache the result.
108
+ *
109
+ * @group WebGPU
110
+ */
111
+ export function createCameraBindGroup(device, cameraShaderBindings, cameraTexture) {
112
+ 'worklet';
113
+
114
+ return device.createBindGroup({
115
+ label: 'fishjam-camera-frame',
116
+ layout: cameraShaderBindings.bindGroupLayout,
117
+ entries: [{
118
+ binding: 0,
119
+ resource: cameraTexture
120
+ }, {
121
+ binding: 1,
122
+ resource: cameraShaderBindings.sampler
123
+ }]
124
+ });
125
+ }
126
+ //# sourceMappingURL=cameraShaderBindings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Platform","GPUShaderStage","tgpu","d","SAMPLE_CAMERA_ANDROID","SAMPLE_CAMERA_IOS","sampleCamera","fn","vec2f","vec4f","OS","$name","buildBindingDeclarations","bindGroupIndex","createCameraShaderBindings","device","options","bindGroupLayout","createBindGroupLayout","label","entries","binding","visibility","FRAGMENT","externalTexture","sampler","globalThis","__TYPEGPU_AUTONAME__","a","createSampler","magFilter","minFilter","bindingDeclarations","createCameraBindGroup","cameraShaderBindings","cameraTexture","createBindGroup","layout","resource"],"sources":["cameraShaderBindings.ts"],"sourcesContent":["import { Platform } from 'react-native';\nimport { GPUShaderStage } from 'react-native-webgpu';\nimport tgpu, { type TgpuFn } from 'typegpu';\nimport * as d from 'typegpu/data';\n\n// The camera arrives as a `texture_external`, which TypeGPU 0.11 cannot resolve inside a TGSL\n// function (its type only resolves as a declaration, not as a sampled value in codegen). So\n// `sampleCamera` is authored as a WGSL-bodied `tgpu.fn`: it is a first-class TypeGPU function that\n// TGSL shaders can call, but its body is WGSL and it references the external texture + sampler by\n// name — those bindings are declared separately by {@link CameraShaderBindings.bindingDeclarations}\n// (also WGSL, for the same reason). This is the one part of the shader that is not authored in TGSL.\n//\n// On Android, the camera arrives as an opaque YCbCr AHardwareBuffer and Dawn's Vulkan path forces\n// an identity sampler conversion, so sampling the external texture returns RAW [Y, Cb, Cr] — the\n// BT.709 limited-range decode below must run in-shader. iOS (NV12 IOSurface) samples as\n// ready-to-use RGB.\nconst SAMPLE_CAMERA_ANDROID = /* wgsl */ `(uv: vec2f) -> vec4f {\n let rawSample = textureSampleBaseClampToEdge(fishjamCameraTexture, fishjamCameraSampler, uv);\n let luma = rawSample.r - 0.0627451;\n let chromaBlue = rawSample.g - 0.5;\n let chromaRed = rawSample.b - 0.5;\n let rgb = vec3f(\n 1.164384 * luma + 1.792741 * chromaRed,\n 1.164384 * luma - 0.213249 * chromaBlue - 0.532909 * chromaRed,\n 1.164384 * luma + 2.112402 * chromaBlue,\n );\n return vec4f(clamp(rgb, vec3f(0.0), vec3f(1.0)), 1.0);\n}`;\n\nconst SAMPLE_CAMERA_IOS = /* wgsl */ `(uv: vec2f) -> vec4f {\n return textureSampleBaseClampToEdge(fishjamCameraTexture, fishjamCameraSampler, uv);\n}`;\n\n/**\n * Samples the live camera and returns upright RGB on both platforms (the Android in-shader BT.709\n * YUV decode is included automatically). A TypeGPU function you can call from your own TGSL\n * fragment shaders. It reads the camera texture + sampler declared by\n * {@link CameraShaderBindings.bindingDeclarations}, which you must prepend to the resolved shader.\n *\n * @group WebGPU\n */\nexport const sampleCamera: TgpuFn<(uv: d.Vec2f) => d.Vec4f> = tgpu\n .fn(\n [d.vec2f],\n d.vec4f,\n )(Platform.OS === 'android' ? SAMPLE_CAMERA_ANDROID : SAMPLE_CAMERA_IOS)\n .$name('sampleCamera');\n\n/** Options for {@link createCameraShaderBindings}. */\nexport interface CreateCameraShaderBindingsOptions {\n /** Bind group index the camera texture + sampler are declared at. Defaults to `0`. */\n bindGroupIndex?: number;\n}\n\n/**\n * Everything a fragment shader needs to sample the live camera. Build once at setup with\n * {@link createCameraShaderBindings}; the fields are safe to capture into the frame worklet.\n *\n * @group WebGPU\n */\nexport interface CameraShaderBindings {\n /**\n * The camera sampler as a TypeGPU function — call `sampleCamera(uv)` from your TGSL fragment\n * shader. Same value as the exported {@link sampleCamera}.\n */\n readonly sampleCamera: typeof sampleCamera;\n /**\n * WGSL declaring the camera `texture_external` and `sampler` at {@link bindGroupIndex}. TypeGPU\n * cannot emit an external-texture binding, so prepend this to the WGSL your shader resolves to.\n */\n readonly bindingDeclarations: string;\n /** Layout of the camera bind group; place it at {@link bindGroupIndex} in your pipeline layout. */\n readonly bindGroupLayout: GPUBindGroupLayout;\n /** The linear-filtering sampler bound at binding 1. */\n readonly sampler: GPUSampler;\n /** The group index the bindings are declared at. */\n readonly bindGroupIndex: number;\n}\n\nfunction buildBindingDeclarations(bindGroupIndex: number): string {\n return /* wgsl */ `@group(${bindGroupIndex}) @binding(0) var fishjamCameraTexture: texture_external;\n@group(${bindGroupIndex}) @binding(1) var fishjamCameraSampler: sampler;\n`;\n}\n\n/**\n * Builds the camera-sampling bindings against the device your pipelines use. Pass the result to\n * the source hook's `cameraShaderBindings` option and the render context delivers a ready-made\n * `cameraBindGroup` every frame — set it at {@link CameraShaderBindings.bindGroupIndex} and call\n * `sampleCamera(uv)` in your fragment shader.\n *\n * ```ts\n * const cam = createCameraShaderBindings(device);\n * const fragment = tgpu.fragmentFn({ in: { uv: d.location(0, d.vec2f) }, out: d.vec4f })((input) => {\n * return cam.sampleCamera(input.uv);\n * });\n * const wgsl = cam.bindingDeclarations + tgpu.resolve({ externals: { fragment } });\n * ```\n *\n * @group WebGPU\n */\nexport function createCameraShaderBindings(\n device: GPUDevice,\n options: CreateCameraShaderBindingsOptions = {},\n): CameraShaderBindings {\n const bindGroupIndex = options.bindGroupIndex ?? 0;\n const bindGroupLayout = device.createBindGroupLayout({\n label: 'fishjam-camera-shader-bindings',\n entries: [\n { binding: 0, visibility: GPUShaderStage.FRAGMENT, externalTexture: {} },\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n ],\n });\n const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' });\n return {\n sampleCamera,\n bindingDeclarations: buildBindingDeclarations(bindGroupIndex),\n bindGroupLayout,\n sampler,\n bindGroupIndex,\n };\n}\n\n/**\n * Builds the per-frame bind group for the live camera texture. The source hook already does this\n * for you when you pass `cameraShaderBindings` in its options (see the render context's\n * `cameraBindGroup`); call it yourself only for advanced multi-layout setups. Worklet-safe.\n *\n * A camera texture expires when the frame ends, so a bind group referencing it must be rebuilt\n * every frame — never cache the result.\n *\n * @group WebGPU\n */\nexport function createCameraBindGroup(\n device: GPUDevice,\n cameraShaderBindings: CameraShaderBindings,\n cameraTexture: GPUExternalTexture,\n): GPUBindGroup {\n 'worklet';\n return device.createBindGroup({\n label: 'fishjam-camera-frame',\n layout: cameraShaderBindings.bindGroupLayout,\n entries: [\n { binding: 0, resource: cameraTexture },\n { binding: 1, resource: cameraShaderBindings.sampler },\n ],\n });\n}\n"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,SAASC,cAAc,QAAQ,qBAAqB;AACpD,OAAOC,IAAI,MAAuB,SAAS;AAC3C,OAAO,KAAKC,CAAC,MAAM,cAAc;;AAEjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,qBAAqB,GAAG,UAAW;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;AAEF,MAAMC,iBAAiB,GAAG,UAAW;AACrC;AACA,EAAE;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,YAA8C,GAAGJ,IAAI,CAC/DK,EAAE,CACD,CAACJ,CAAC,CAACK,KAAK,CAAC,EACTL,CAAC,CAACM,KACJ,CAAC,CAACT,QAAQ,CAACU,EAAE,KAAK,SAAS,GAAGN,qBAAqB,GAAGC,iBAAiB,CAAC,CACvEM,KAAK,CAAC,cAAc,CAAC;;AAExB;;AAMA;AACA;AACA;AACA;AACA;AACA;;AAoBA,SAASC,wBAAwBA,CAACC,cAAsB,EAAU;EAChE,OAAO,UAAW,UAAUA,cAAc;AAC5C,SAASA,cAAc;AACvB,CAAC;AACD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,0BAA0BA,CACxCC,MAAiB,EACjBC,OAA0C,GAAG,CAAC,CAAC,EACzB;EACtB,MAAMH,cAAc,GAAGG,OAAO,CAACH,cAAc,IAAI,CAAC;EAClD,MAAMI,eAAe,GAAGF,MAAM,CAACG,qBAAqB,CAAC;IACnDC,KAAK,EAAE,gCAAgC;IACvCC,OAAO,EAAE,CACP;MAAEC,OAAO,EAAE,CAAC;MAAEC,UAAU,EAAErB,cAAc,CAACsB,QAAQ;MAAEC,eAAe,EAAE,CAAC;IAAE,CAAC,EACxE;MAAEH,OAAO,EAAE,CAAC;MAAEC,UAAU,EAAErB,cAAc,CAACsB,QAAQ;MAAEE,OAAO,EAAE,CAAC;IAAE,CAAC;EAEpE,CAAC,CAAC;EACF,MAAMA,OAAO,iBAAAC,UAAA,CAAAC,oBAAA,KAAAC,CAAA,IAAAA,CAAA,GAAGb,MAAM,CAACc,aAAa,CAAC;IAAEC,SAAS,EAAE,QAAQ;IAAEC,SAAS,EAAE;EAAS,CAAC,CAAC;EAClF,OAAO;IACLzB,YAAY;IACZ0B,mBAAmB,EAAEpB,wBAAwB,CAACC,cAAc,CAAC;IAC7DI,eAAe;IACfQ,OAAO;IACPZ;EACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASoB,qBAAqBA,CACnClB,MAAiB,EACjBmB,oBAA0C,EAC1CC,aAAiC,EACnB;EACd,SAAS;;EACT,OAAOpB,MAAM,CAACqB,eAAe,CAAC;IAC5BjB,KAAK,EAAE,sBAAsB;IAC7BkB,MAAM,EAAEH,oBAAoB,CAACjB,eAAe;IAC5CG,OAAO,EAAE,CACP;MAAEC,OAAO,EAAE,CAAC;MAAEiB,QAAQ,EAAEH;IAAc,CAAC,EACvC;MAAEd,OAAO,EAAE,CAAC;MAAEiB,QAAQ,EAAEJ,oBAAoB,CAACT;IAAQ,CAAC;EAE1D,CAAC,CAAC;AACJ","ignoreList":[]}
@@ -0,0 +1,52 @@
1
+ import { GPUTextureUsage } from 'react-native-webgpu';
2
+ import { createCameraPassthroughPipeline, encodeCameraPassthrough } from './cameraPassthroughPipeline';
3
+ import { computeAspectFillCrop } from './cropUtilities';
4
+
5
+ /**
6
+ * An owned `rgba8unorm` texture the camera frame is resolved into each frame — for pipelines that
7
+ * want a plain `texture_2d` camera instead of `texture_external`. Build once at setup with
8
+ * {@link createCameraTextureResolver}; every field is safe to capture into the frame worklet.
9
+ *
10
+ * Prefer sampling the camera directly via {@link createCameraShaderBindings} when you can: the
11
+ * resolver costs one extra render pass per frame.
12
+ *
13
+ * @group WebGPU
14
+ */
15
+
16
+ /**
17
+ * Creates a {@link CameraTextureResolver} with an owned `rgba8unorm` texture of the given size.
18
+ *
19
+ * @group WebGPU
20
+ */
21
+ export function createCameraTextureResolver(device, size) {
22
+ const texture = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(device.createTexture({
23
+ label: 'fishjam-resolved-camera',
24
+ format: 'rgba8unorm',
25
+ size: [size.width, size.height],
26
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
27
+ }), "texture");
28
+ return {
29
+ texture,
30
+ view: /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(texture.createView(), "view"),
31
+ width: size.width,
32
+ height: size.height,
33
+ resolvePass: createCameraPassthroughPipeline(device, {
34
+ outputFormat: 'rgba8unorm'
35
+ })
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Encodes one pass resolving the live camera texture into `resolver.texture`, aspect-filled to
41
+ * the resolver's size (platform YUV decode included). Worklet-safe; call it inside your render
42
+ * callback before the passes that sample `resolver.texture`.
43
+ *
44
+ * @group WebGPU
45
+ */
46
+ export function resolveCameraTexture(device, resolver, cameraTexture, cameraWidth, cameraHeight, commandEncoder) {
47
+ 'worklet';
48
+
49
+ const crop = computeAspectFillCrop(cameraWidth, cameraHeight, resolver.width / resolver.height);
50
+ encodeCameraPassthrough(device, resolver.resolvePass, cameraTexture, resolver.view, commandEncoder, crop);
51
+ }
52
+ //# sourceMappingURL=cameraTextureResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["GPUTextureUsage","createCameraPassthroughPipeline","encodeCameraPassthrough","computeAspectFillCrop","createCameraTextureResolver","device","size","texture","globalThis","__TYPEGPU_AUTONAME__","a","createTexture","label","format","width","height","usage","TEXTURE_BINDING","RENDER_ATTACHMENT","view","createView","resolvePass","outputFormat","resolveCameraTexture","resolver","cameraTexture","cameraWidth","cameraHeight","commandEncoder","crop"],"sources":["cameraTextureResolver.ts"],"sourcesContent":["import { GPUTextureUsage } from 'react-native-webgpu';\n\nimport { createCameraPassthroughPipeline, encodeCameraPassthrough } from './cameraPassthroughPipeline';\nimport { computeAspectFillCrop } from './cropUtilities';\n\n/**\n * An owned `rgba8unorm` texture the camera frame is resolved into each frame — for pipelines that\n * want a plain `texture_2d` camera instead of `texture_external`. Build once at setup with\n * {@link createCameraTextureResolver}; every field is safe to capture into the frame worklet.\n *\n * Prefer sampling the camera directly via {@link createCameraShaderBindings} when you can: the\n * resolver costs one extra render pass per frame.\n *\n * @group WebGPU\n */\nexport interface CameraTextureResolver {\n /** The resolved camera texture (`rgba8unorm`, sampled + render-attachment usage). */\n readonly texture: GPUTexture;\n /** A reusable default view of {@link texture}. */\n readonly view: GPUTextureView;\n readonly width: number;\n readonly height: number;\n /** @internal The pass used to resolve into {@link texture}. */\n readonly resolvePass: ReturnType<typeof createCameraPassthroughPipeline>;\n}\n\n/**\n * Creates a {@link CameraTextureResolver} with an owned `rgba8unorm` texture of the given size.\n *\n * @group WebGPU\n */\nexport function createCameraTextureResolver(\n device: GPUDevice,\n size: { width: number; height: number },\n): CameraTextureResolver {\n const texture = device.createTexture({\n label: 'fishjam-resolved-camera',\n format: 'rgba8unorm',\n size: [size.width, size.height],\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,\n });\n return {\n texture,\n view: texture.createView(),\n width: size.width,\n height: size.height,\n resolvePass: createCameraPassthroughPipeline(device, { outputFormat: 'rgba8unorm' }),\n };\n}\n\n/**\n * Encodes one pass resolving the live camera texture into `resolver.texture`, aspect-filled to\n * the resolver's size (platform YUV decode included). Worklet-safe; call it inside your render\n * callback before the passes that sample `resolver.texture`.\n *\n * @group WebGPU\n */\nexport function resolveCameraTexture(\n device: GPUDevice,\n resolver: CameraTextureResolver,\n cameraTexture: GPUExternalTexture,\n cameraWidth: number,\n cameraHeight: number,\n commandEncoder: GPUCommandEncoder,\n): void {\n 'worklet';\n const crop = computeAspectFillCrop(cameraWidth, cameraHeight, resolver.width / resolver.height);\n encodeCameraPassthrough(device, resolver.resolvePass, cameraTexture, resolver.view, commandEncoder, crop);\n}\n"],"mappings":"AAAA,SAASA,eAAe,QAAQ,qBAAqB;AAErD,SAASC,+BAA+B,EAAEC,uBAAuB,QAAQ,6BAA6B;AACtG,SAASC,qBAAqB,QAAQ,iBAAiB;;AAEvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,2BAA2BA,CACzCC,MAAiB,EACjBC,IAAuC,EAChB;EACvB,MAAMC,OAAO,iBAAAC,UAAA,CAAAC,oBAAA,KAAAC,CAAA,IAAAA,CAAA,GAAGL,MAAM,CAACM,aAAa,CAAC;IACnCC,KAAK,EAAE,yBAAyB;IAChCC,MAAM,EAAE,YAAY;IACpBP,IAAI,EAAE,CAACA,IAAI,CAACQ,KAAK,EAAER,IAAI,CAACS,MAAM,CAAC;IAC/BC,KAAK,EAAEhB,eAAe,CAACiB,eAAe,GAAGjB,eAAe,CAACkB;EAC3D,CAAC,CAAC;EACF,OAAO;IACLX,OAAO;IACPY,IAAI,gBAAAX,UAAA,CAAAC,oBAAA,KAAAC,CAAA,IAAAA,CAAA,GAAEH,OAAO,CAACa,UAAU,CAAC,CAAC;IAC1BN,KAAK,EAAER,IAAI,CAACQ,KAAK;IACjBC,MAAM,EAAET,IAAI,CAACS,MAAM;IACnBM,WAAW,EAAEpB,+BAA+B,CAACI,MAAM,EAAE;MAAEiB,YAAY,EAAE;IAAa,CAAC;EACrF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAClClB,MAAiB,EACjBmB,QAA+B,EAC/BC,aAAiC,EACjCC,WAAmB,EACnBC,YAAoB,EACpBC,cAAiC,EAC3B;EACN,SAAS;;EACT,MAAMC,IAAI,GAAG1B,qBAAqB,CAACuB,WAAW,EAAEC,YAAY,EAAEH,QAAQ,CAACV,KAAK,GAAGU,QAAQ,CAACT,MAAM,CAAC;EAC/Fb,uBAAuB,CAACG,MAAM,EAAEmB,QAAQ,CAACH,WAAW,EAAEI,aAAa,EAAED,QAAQ,CAACL,IAAI,EAAES,cAAc,EAAEC,IAAI,CAAC;AAC3G","ignoreList":[]}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Crop math + a worklet-safe byte packer for the {@link FrameCropParams} uniform used by
3
+ * {@link createCameraPassthroughPipeline} (and reusable by your own shaders).
4
+ */
5
+
6
+ import * as d from 'typegpu/data';
7
+
8
+ /**
9
+ * The TypeGPU schema for the crop uniform. This is the single source of truth for both the WGSL
10
+ * `struct FrameCropParams` (emitted when the passthrough shader is resolved) and the byte layout
11
+ * {@link packFrameCropParams} writes. Its std140 layout is 40 bytes:
12
+ *
13
+ * sourceSize: vec2u @0 cropOrigin: vec2f @8 cropSize: vec2f @16 uvTransform: mat2x2f @24
14
+ *
15
+ * @group WebGPU
16
+ */
17
+ export const FrameCropParams = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({
18
+ sourceSize: d.vec2u,
19
+ cropOrigin: d.vec2f,
20
+ cropSize: d.vec2f,
21
+ uvTransform: d.mat2x2f
22
+ }), "FrameCropParams");
23
+
24
+ /**
25
+ * A center-crop of a source frame plus a UV-space orientation transform, in source pixels.
26
+ * Produce one with {@link computeAspectFillCrop} or {@link computeSquareCrop}.
27
+ *
28
+ * @group WebGPU
29
+ */
30
+
31
+ // Derived from the schema so the buffer size can never drift from the WGSL struct. The manual
32
+ // offsets below must match the schema's std140 layout by hand (see the layout table on
33
+ // FrameCropParams); we keep a hand-written packer rather than a schema-driven writer because this
34
+ // runs per-frame inside a worklet and must stay off the tgpu root/proxy.
35
+ const FRAME_CROP_BYTES = d.sizeOf(FrameCropParams);
36
+
37
+ /**
38
+ * Packs a {@link FrameCrop} into the {@link FrameCropParams} uniform byte layout.
39
+ * Worklet-safe; upload the result with `device.queue.writeBuffer(buffer, 0, bytes)`.
40
+ *
41
+ * @group WebGPU
42
+ */
43
+ export function packFrameCropParams(crop) {
44
+ 'worklet';
45
+
46
+ const buffer = new ArrayBuffer(FRAME_CROP_BYTES);
47
+ const asUint32 = new Uint32Array(buffer);
48
+ const asFloat32 = new Float32Array(buffer);
49
+ asUint32[0] = crop.sourceWidth >>> 0;
50
+ asUint32[1] = crop.sourceHeight >>> 0;
51
+ asFloat32[2] = crop.cropOriginX;
52
+ asFloat32[3] = crop.cropOriginY;
53
+ asFloat32[4] = crop.cropSizeX;
54
+ asFloat32[5] = crop.cropSizeY;
55
+ asFloat32[6] = crop.uv00;
56
+ asFloat32[7] = crop.uv01;
57
+ asFloat32[8] = crop.uv10;
58
+ asFloat32[9] = crop.uv11;
59
+ return buffer;
60
+ }
61
+
62
+ /**
63
+ * Center-crop of a (sourceWidth × sourceHeight) frame to the target aspect ratio (width/height).
64
+ * A no-op full-frame crop when the source already has the target aspect. Worklet-safe.
65
+ *
66
+ * Feed it the upright camera size from the render context:
67
+ * `computeAspectFillCrop(context.cameraWidth, context.cameraHeight, context.outputWidth / context.outputHeight)`.
68
+ *
69
+ * The optional `uv00..uv11` scalars form a UV-space transform applied around the frame center
70
+ * (identity by default).
71
+ *
72
+ * @group WebGPU
73
+ */
74
+ export function computeAspectFillCrop(sourceWidth, sourceHeight, targetAspect, uv00 = 1, uv01 = 0, uv10 = 0, uv11 = 1) {
75
+ 'worklet';
76
+
77
+ let cropWidth = sourceWidth;
78
+ let cropHeight = sourceHeight;
79
+ if (sourceWidth / sourceHeight > targetAspect) {
80
+ cropWidth = sourceHeight * targetAspect;
81
+ } else {
82
+ cropHeight = sourceWidth / targetAspect;
83
+ }
84
+ return {
85
+ sourceWidth,
86
+ sourceHeight,
87
+ cropOriginX: Math.floor((sourceWidth - cropWidth) / 2),
88
+ cropOriginY: Math.floor((sourceHeight - cropHeight) / 2),
89
+ cropSizeX: cropWidth,
90
+ cropSizeY: cropHeight,
91
+ uv00,
92
+ uv01,
93
+ uv10,
94
+ uv11
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Square center-crop of a (sourceWidth × sourceHeight) frame — the usual shape for square model
100
+ * inputs. Worklet-safe. The optional `uv00..uv11` scalars are as in {@link computeAspectFillCrop}.
101
+ *
102
+ * @group WebGPU
103
+ */
104
+ export function computeSquareCrop(sourceWidth, sourceHeight, uv00 = 1, uv01 = 0, uv10 = 0, uv11 = 1) {
105
+ 'worklet';
106
+
107
+ const size = Math.min(sourceWidth, sourceHeight);
108
+ return {
109
+ sourceWidth,
110
+ sourceHeight,
111
+ cropOriginX: Math.floor((sourceWidth - size) / 2),
112
+ cropOriginY: Math.floor((sourceHeight - size) / 2),
113
+ cropSizeX: size,
114
+ cropSizeY: size,
115
+ uv00,
116
+ uv01,
117
+ uv10,
118
+ uv11
119
+ };
120
+ }
121
+ //# sourceMappingURL=cropUtilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["d","FrameCropParams","globalThis","__TYPEGPU_AUTONAME__","a","struct","sourceSize","vec2u","cropOrigin","vec2f","cropSize","uvTransform","mat2x2f","FRAME_CROP_BYTES","sizeOf","packFrameCropParams","crop","buffer","ArrayBuffer","asUint32","Uint32Array","asFloat32","Float32Array","sourceWidth","sourceHeight","cropOriginX","cropOriginY","cropSizeX","cropSizeY","uv00","uv01","uv10","uv11","computeAspectFillCrop","targetAspect","cropWidth","cropHeight","Math","floor","computeSquareCrop","size","min"],"sources":["cropUtilities.ts"],"sourcesContent":["/**\n * Crop math + a worklet-safe byte packer for the {@link FrameCropParams} uniform used by\n * {@link createCameraPassthroughPipeline} (and reusable by your own shaders).\n */\n\nimport * as d from 'typegpu/data';\n\n/**\n * The TypeGPU schema for the crop uniform. This is the single source of truth for both the WGSL\n * `struct FrameCropParams` (emitted when the passthrough shader is resolved) and the byte layout\n * {@link packFrameCropParams} writes. Its std140 layout is 40 bytes:\n *\n * sourceSize: vec2u @0 cropOrigin: vec2f @8 cropSize: vec2f @16 uvTransform: mat2x2f @24\n *\n * @group WebGPU\n */\nexport const FrameCropParams = d.struct({\n sourceSize: d.vec2u,\n cropOrigin: d.vec2f,\n cropSize: d.vec2f,\n uvTransform: d.mat2x2f,\n});\n\n/**\n * A center-crop of a source frame plus a UV-space orientation transform, in source pixels.\n * Produce one with {@link computeAspectFillCrop} or {@link computeSquareCrop}.\n *\n * @group WebGPU\n */\nexport interface FrameCrop {\n sourceWidth: number;\n sourceHeight: number;\n cropOriginX: number;\n cropOriginY: number;\n cropSizeX: number;\n cropSizeY: number;\n /** uvTransform matrix scalars in (m00, m01, m10, m11) order; identity when omitted at compute time. */\n uv00: number;\n uv01: number;\n uv10: number;\n uv11: number;\n}\n\n// Derived from the schema so the buffer size can never drift from the WGSL struct. The manual\n// offsets below must match the schema's std140 layout by hand (see the layout table on\n// FrameCropParams); we keep a hand-written packer rather than a schema-driven writer because this\n// runs per-frame inside a worklet and must stay off the tgpu root/proxy.\nconst FRAME_CROP_BYTES = d.sizeOf(FrameCropParams);\n\n/**\n * Packs a {@link FrameCrop} into the {@link FrameCropParams} uniform byte layout.\n * Worklet-safe; upload the result with `device.queue.writeBuffer(buffer, 0, bytes)`.\n *\n * @group WebGPU\n */\nexport function packFrameCropParams(crop: FrameCrop): ArrayBuffer {\n 'worklet';\n const buffer = new ArrayBuffer(FRAME_CROP_BYTES);\n const asUint32 = new Uint32Array(buffer);\n const asFloat32 = new Float32Array(buffer);\n asUint32[0] = crop.sourceWidth >>> 0;\n asUint32[1] = crop.sourceHeight >>> 0;\n asFloat32[2] = crop.cropOriginX;\n asFloat32[3] = crop.cropOriginY;\n asFloat32[4] = crop.cropSizeX;\n asFloat32[5] = crop.cropSizeY;\n asFloat32[6] = crop.uv00;\n asFloat32[7] = crop.uv01;\n asFloat32[8] = crop.uv10;\n asFloat32[9] = crop.uv11;\n return buffer;\n}\n\n/**\n * Center-crop of a (sourceWidth × sourceHeight) frame to the target aspect ratio (width/height).\n * A no-op full-frame crop when the source already has the target aspect. Worklet-safe.\n *\n * Feed it the upright camera size from the render context:\n * `computeAspectFillCrop(context.cameraWidth, context.cameraHeight, context.outputWidth / context.outputHeight)`.\n *\n * The optional `uv00..uv11` scalars form a UV-space transform applied around the frame center\n * (identity by default).\n *\n * @group WebGPU\n */\nexport function computeAspectFillCrop(\n sourceWidth: number,\n sourceHeight: number,\n targetAspect: number,\n uv00: number = 1,\n uv01: number = 0,\n uv10: number = 0,\n uv11: number = 1,\n): FrameCrop {\n 'worklet';\n let cropWidth = sourceWidth;\n let cropHeight = sourceHeight;\n if (sourceWidth / sourceHeight > targetAspect) {\n cropWidth = sourceHeight * targetAspect;\n } else {\n cropHeight = sourceWidth / targetAspect;\n }\n return {\n sourceWidth,\n sourceHeight,\n cropOriginX: Math.floor((sourceWidth - cropWidth) / 2),\n cropOriginY: Math.floor((sourceHeight - cropHeight) / 2),\n cropSizeX: cropWidth,\n cropSizeY: cropHeight,\n uv00,\n uv01,\n uv10,\n uv11,\n };\n}\n\n/**\n * Square center-crop of a (sourceWidth × sourceHeight) frame — the usual shape for square model\n * inputs. Worklet-safe. The optional `uv00..uv11` scalars are as in {@link computeAspectFillCrop}.\n *\n * @group WebGPU\n */\nexport function computeSquareCrop(\n sourceWidth: number,\n sourceHeight: number,\n uv00: number = 1,\n uv01: number = 0,\n uv10: number = 0,\n uv11: number = 1,\n): FrameCrop {\n 'worklet';\n const size = Math.min(sourceWidth, sourceHeight);\n return {\n sourceWidth,\n sourceHeight,\n cropOriginX: Math.floor((sourceWidth - size) / 2),\n cropOriginY: Math.floor((sourceHeight - size) / 2),\n cropSizeX: size,\n cropSizeY: size,\n uv00,\n uv01,\n uv10,\n uv11,\n };\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;;AAEA,OAAO,KAAKA,CAAC,MAAM,cAAc;;AAEjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,eAAe,iBAAAC,UAAA,CAAAC,oBAAA,KAAAC,CAAA,IAAAA,CAAA,GAAGJ,CAAC,CAACK,MAAM,CAAC;EACtCC,UAAU,EAAEN,CAAC,CAACO,KAAK;EACnBC,UAAU,EAAER,CAAC,CAACS,KAAK;EACnBC,QAAQ,EAAEV,CAAC,CAACS,KAAK;EACjBE,WAAW,EAAEX,CAAC,CAACY;AACjB,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;;AAeA;AACA;AACA;AACA;AACA,MAAMC,gBAAgB,GAAGb,CAAC,CAACc,MAAM,CAACb,eAAe,CAAC;;AAElD;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASc,mBAAmBA,CAACC,IAAe,EAAe;EAChE,SAAS;;EACT,MAAMC,MAAM,GAAG,IAAIC,WAAW,CAACL,gBAAgB,CAAC;EAChD,MAAMM,QAAQ,GAAG,IAAIC,WAAW,CAACH,MAAM,CAAC;EACxC,MAAMI,SAAS,GAAG,IAAIC,YAAY,CAACL,MAAM,CAAC;EAC1CE,QAAQ,CAAC,CAAC,CAAC,GAAGH,IAAI,CAACO,WAAW,KAAK,CAAC;EACpCJ,QAAQ,CAAC,CAAC,CAAC,GAAGH,IAAI,CAACQ,YAAY,KAAK,CAAC;EACrCH,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACS,WAAW;EAC/BJ,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACU,WAAW;EAC/BL,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACW,SAAS;EAC7BN,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACY,SAAS;EAC7BP,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACa,IAAI;EACxBR,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACc,IAAI;EACxBT,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACe,IAAI;EACxBV,SAAS,CAAC,CAAC,CAAC,GAAGL,IAAI,CAACgB,IAAI;EACxB,OAAOf,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASgB,qBAAqBA,CACnCV,WAAmB,EACnBC,YAAoB,EACpBU,YAAoB,EACpBL,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EACL;EACX,SAAS;;EACT,IAAIG,SAAS,GAAGZ,WAAW;EAC3B,IAAIa,UAAU,GAAGZ,YAAY;EAC7B,IAAID,WAAW,GAAGC,YAAY,GAAGU,YAAY,EAAE;IAC7CC,SAAS,GAAGX,YAAY,GAAGU,YAAY;EACzC,CAAC,MAAM;IACLE,UAAU,GAAGb,WAAW,GAAGW,YAAY;EACzC;EACA,OAAO;IACLX,WAAW;IACXC,YAAY;IACZC,WAAW,EAAEY,IAAI,CAACC,KAAK,CAAC,CAACf,WAAW,GAAGY,SAAS,IAAI,CAAC,CAAC;IACtDT,WAAW,EAAEW,IAAI,CAACC,KAAK,CAAC,CAACd,YAAY,GAAGY,UAAU,IAAI,CAAC,CAAC;IACxDT,SAAS,EAAEQ,SAAS;IACpBP,SAAS,EAAEQ,UAAU;IACrBP,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC;EACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASO,iBAAiBA,CAC/BhB,WAAmB,EACnBC,YAAoB,EACpBK,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EAChBC,IAAY,GAAG,CAAC,EACL;EACX,SAAS;;EACT,MAAMQ,IAAI,GAAGH,IAAI,CAACI,GAAG,CAAClB,WAAW,EAAEC,YAAY,CAAC;EAChD,OAAO;IACLD,WAAW;IACXC,YAAY;IACZC,WAAW,EAAEY,IAAI,CAACC,KAAK,CAAC,CAACf,WAAW,GAAGiB,IAAI,IAAI,CAAC,CAAC;IACjDd,WAAW,EAAEW,IAAI,CAACC,KAAK,CAAC,CAACd,YAAY,GAAGgB,IAAI,IAAI,CAAC,CAAC;IAClDb,SAAS,EAAEa,IAAI;IACfZ,SAAS,EAAEY,IAAI;IACfX,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC;EACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=frameRenderContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["frameRenderContext.ts"],"sourcesContent":["/**\n * Everything your render callback needs for one frame. Handed to the function you pass to\n * `render(...)` inside the source hook's `onFrame` worklet; every field is valid only until that\n * function returns.\n *\n * @group WebGPU\n */\nexport interface WebGpuFrameRenderContext {\n /** The device in use (the shared device, or your override). */\n device: GPUDevice;\n /** Shortcut for `device.queue`. */\n queue: GPUQueue;\n /**\n * The command encoder for this frame. Encode your render/compute passes into it — the hook\n * submits it (a single submit) and delivers the frame after your callback returns. Do not call\n * `queue.submit` yourself for work targeting {@link outputTexture}.\n */\n commandEncoder: GPUCommandEncoder;\n /**\n * The live camera frame as a `texture_external`, already rotated upright (and mirrored per the\n * camera's mirroring). On Android it samples as raw YCbCr — sample it through\n * `createCameraShaderBindings` (or the ready-made {@link cameraBindGroup}) for\n * platform-correct RGB.\n */\n cameraTexture: GPUExternalTexture;\n /**\n * Ready-made bind group for {@link cameraTexture}, present when the hook's\n * `cameraShaderBindings` option was provided: set it at the bindings' group index and\n * `sampleCamera(uv)` works.\n */\n cameraBindGroup?: GPUBindGroup;\n /** The output texture your passes draw into; its content becomes the published video frame. */\n outputTexture: GPUTexture;\n /**\n * A cached, reusable default `createView()` of {@link outputTexture}. Prefer this over calling\n * `outputTexture.createView()` yourself every frame: `GPUTextureView` has no `destroy()`/`release()`\n * in react-native-webgpu, so a per-frame view accumulates native (malloc) wrappers on the frame\n * runtime until GC — a steady leak in a render loop. The output textures are a small fixed pool, so\n * the hook builds one view per slot and reuses it.\n */\n outputView: GPUTextureView;\n /** Size of {@link outputTexture}, in pixels. */\n outputWidth: number;\n outputHeight: number;\n /**\n * Upright (post-rotation) camera frame size, in pixels — feed it to `computeAspectFillCrop`\n * together with the output aspect.\n */\n cameraWidth: number;\n cameraHeight: number;\n /** Whether the camera feed is mirrored (front cameras usually are). */\n cameraIsMirrored: boolean;\n}\n\n/**\n * The `render` function handed to the source hook's `onFrame` worklet. Call it at most once per\n * frame with your encode function; skipping it drops the frame (nothing is published for it).\n *\n * @group WebGPU\n */\nexport type WebGpuFrameRenderFunction = (encode: (context: WebGpuFrameRenderContext) => void) => void;\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,46 @@
1
+ import { Platform } from 'react-native';
2
+
3
+ // rnwebgpu/native-texture gates the zero-copy shared-surface + external-texture path.
4
+ // The camera-import feature differs by platform: iOS delivers biplanar NV12 IOSurfaces
5
+ // (dawn-multi-planar-formats); Android delivers YUV_420 AHardwareBuffers imported through an
6
+ // external YCbCr Vulkan sampler (opaque-ycbcr-android-for-external-texture). Without the right
7
+ // one, importing the camera fails on every frame on a real device.
8
+ const REQUIRED_FEATURES = Platform.OS === 'android' ? ['rnwebgpu/native-texture', 'opaque-ycbcr-android-for-external-texture'] : ['rnwebgpu/native-texture', 'dawn-multi-planar-formats'];
9
+
10
+ /**
11
+ * The GPU features a device must have to import camera frames and Fishjam output surfaces on this
12
+ * platform. {@link useCameraWebGpuDevice} requests them for you; pass them yourself as
13
+ * `requiredFeatures` in `GPUDeviceDescriptor` when you bring your own device.
14
+ *
15
+ * @group WebGPU
16
+ */
17
+ export function getRequiredWebGpuCameraFeatures() {
18
+ return [...REQUIRED_FEATURES];
19
+ }
20
+
21
+ /**
22
+ * Throws a descriptive error when `device` is missing any feature required to import camera
23
+ * frames or Fishjam output surfaces on this platform. Called automatically on devices passed as
24
+ * an override; call it yourself to validate a device early.
25
+ *
26
+ * @group WebGPU
27
+ */
28
+ export function assertWebGpuDeviceSupportsCameraImport(device) {
29
+ const missingFeatures = REQUIRED_FEATURES.filter(feature => !device.features.has(feature));
30
+ if (missingFeatures.length === 0) {
31
+ return;
32
+ }
33
+ throw new Error(`This GPUDevice cannot import camera frames on ${Platform.OS}: it is missing the ` + `${missingFeatures.map(feature => `'${feature}'`).join(', ')} feature(s). ` + `Request your device with getRequiredWebGpuCameraFeatures() in GPUDeviceDescriptor.requiredFeatures, ` + `or omit the device option to use the shared device from useCameraWebGpuDevice().`);
34
+ }
35
+
36
+ /**
37
+ * The pixel format of Fishjam output surfaces on this platform: `'rgba8unorm'` on Android
38
+ * (AHardwareBuffer), `'bgra8unorm'` on iOS (IOSurface). Use it as the render-target format of any
39
+ * pipeline that draws into the output texture — a mismatched format renders wrong or black.
40
+ *
41
+ * @group WebGPU
42
+ */
43
+ export function getOutputSurfaceFormat() {
44
+ return Platform.OS === 'android' ? 'rgba8unorm' : 'bgra8unorm';
45
+ }
46
+ //# sourceMappingURL=requiredFeatures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Platform","REQUIRED_FEATURES","OS","getRequiredWebGpuCameraFeatures","assertWebGpuDeviceSupportsCameraImport","device","missingFeatures","filter","feature","features","has","length","Error","map","join","getOutputSurfaceFormat"],"sources":["requiredFeatures.ts"],"sourcesContent":["import { Platform } from 'react-native';\n\n// rnwebgpu/native-texture gates the zero-copy shared-surface + external-texture path.\n// The camera-import feature differs by platform: iOS delivers biplanar NV12 IOSurfaces\n// (dawn-multi-planar-formats); Android delivers YUV_420 AHardwareBuffers imported through an\n// external YCbCr Vulkan sampler (opaque-ycbcr-android-for-external-texture). Without the right\n// one, importing the camera fails on every frame on a real device.\nconst REQUIRED_FEATURES: GPUFeatureName[] =\n Platform.OS === 'android'\n ? ['rnwebgpu/native-texture' as GPUFeatureName, 'opaque-ycbcr-android-for-external-texture' as GPUFeatureName]\n : ['rnwebgpu/native-texture' as GPUFeatureName, 'dawn-multi-planar-formats' as GPUFeatureName];\n\n/**\n * The GPU features a device must have to import camera frames and Fishjam output surfaces on this\n * platform. {@link useCameraWebGpuDevice} requests them for you; pass them yourself as\n * `requiredFeatures` in `GPUDeviceDescriptor` when you bring your own device.\n *\n * @group WebGPU\n */\nexport function getRequiredWebGpuCameraFeatures(): GPUFeatureName[] {\n return [...REQUIRED_FEATURES];\n}\n\n/**\n * Throws a descriptive error when `device` is missing any feature required to import camera\n * frames or Fishjam output surfaces on this platform. Called automatically on devices passed as\n * an override; call it yourself to validate a device early.\n *\n * @group WebGPU\n */\nexport function assertWebGpuDeviceSupportsCameraImport(device: GPUDevice): void {\n const missingFeatures = REQUIRED_FEATURES.filter((feature) => !device.features.has(feature));\n if (missingFeatures.length === 0) {\n return;\n }\n throw new Error(\n `This GPUDevice cannot import camera frames on ${Platform.OS}: it is missing the ` +\n `${missingFeatures.map((feature) => `'${feature}'`).join(', ')} feature(s). ` +\n `Request your device with getRequiredWebGpuCameraFeatures() in GPUDeviceDescriptor.requiredFeatures, ` +\n `or omit the device option to use the shared device from useCameraWebGpuDevice().`,\n );\n}\n\n/**\n * The pixel format of Fishjam output surfaces on this platform: `'rgba8unorm'` on Android\n * (AHardwareBuffer), `'bgra8unorm'` on iOS (IOSurface). Use it as the render-target format of any\n * pipeline that draws into the output texture — a mismatched format renders wrong or black.\n *\n * @group WebGPU\n */\nexport function getOutputSurfaceFormat(): GPUTextureFormat {\n return Platform.OS === 'android' ? 'rgba8unorm' : 'bgra8unorm';\n}\n"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,cAAc;;AAEvC;AACA;AACA;AACA;AACA;AACA,MAAMC,iBAAmC,GACvCD,QAAQ,CAACE,EAAE,KAAK,SAAS,GACrB,CAAC,yBAAyB,EAAoB,2CAA2C,CAAmB,GAC5G,CAAC,yBAAyB,EAAoB,2BAA2B,CAAmB;;AAElG;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,+BAA+BA,CAAA,EAAqB;EAClE,OAAO,CAAC,GAAGF,iBAAiB,CAAC;AAC/B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASG,sCAAsCA,CAACC,MAAiB,EAAQ;EAC9E,MAAMC,eAAe,GAAGL,iBAAiB,CAACM,MAAM,CAAEC,OAAO,IAAK,CAACH,MAAM,CAACI,QAAQ,CAACC,GAAG,CAACF,OAAO,CAAC,CAAC;EAC5F,IAAIF,eAAe,CAACK,MAAM,KAAK,CAAC,EAAE;IAChC;EACF;EACA,MAAM,IAAIC,KAAK,CACb,iDAAiDZ,QAAQ,CAACE,EAAE,sBAAsB,GAChF,GAAGI,eAAe,CAACO,GAAG,CAAEL,OAAO,IAAK,IAAIA,OAAO,GAAG,CAAC,CAACM,IAAI,CAAC,IAAI,CAAC,eAAe,GAC7E,sGAAsG,GACtG,kFACJ,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,sBAAsBA,CAAA,EAAqB;EACzD,OAAOf,QAAQ,CAACE,EAAE,KAAK,SAAS,GAAG,YAAY,GAAG,YAAY;AAChE","ignoreList":[]}
@@ -0,0 +1,114 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { toError } from '../internal/toError';
3
+ import { assertWebGpuDeviceSupportsCameraImport, getRequiredWebGpuCameraFeatures } from './requiredFeatures';
4
+ import { getWebGpuRuntime } from './webGpuRuntime';
5
+
6
+ // One app-wide device: every hook instance shares it, so per-device resources (pipelines,
7
+ // shared-texture imports) built by different callers stay valid for each other. Cleared on
8
+ // device loss or acquisition failure so a later caller retries.
9
+ let sharedDevicePromise = null;
10
+ async function acquireSharedCameraWebGpuDevice() {
11
+ getWebGpuRuntime();
12
+ const adapter = await navigator.gpu.requestAdapter();
13
+ if (adapter == null) {
14
+ throw new Error('WebGPU is unavailable: navigator.gpu.requestAdapter() returned no adapter.');
15
+ }
16
+ return adapter.requestDevice({
17
+ requiredFeatures: getRequiredWebGpuCameraFeatures()
18
+ });
19
+ }
20
+ function getSharedCameraWebGpuDevice() {
21
+ if (sharedDevicePromise == null) {
22
+ // Only this promise may clear the slot: by the time a loss (or failure) callback fires, a
23
+ // replacement device may already occupy it, and unconditionally nulling would discard that
24
+ // replacement.
25
+ const clearIfCurrent = () => {
26
+ if (sharedDevicePromise === devicePromise) {
27
+ sharedDevicePromise = null;
28
+ }
29
+ };
30
+ const devicePromise = acquireSharedCameraWebGpuDevice().then(device => {
31
+ device.lost.then(clearIfCurrent, clearIfCurrent);
32
+ return device;
33
+ }, cause => {
34
+ clearIfCurrent();
35
+ throw cause;
36
+ });
37
+ sharedDevicePromise = devicePromise;
38
+ }
39
+ return sharedDevicePromise;
40
+ }
41
+
42
+ /** Result of {@link useCameraWebGpuDevice}. */
43
+
44
+ /**
45
+ * The app-wide GPUDevice used for camera work, configured with
46
+ * {@link getRequiredWebGpuCameraFeatures}. All callers share one device, so pipelines you build
47
+ * against it work with the textures the source hook hands your render callback.
48
+ *
49
+ * Build your pipelines once the device arrives:
50
+ * ```tsx
51
+ * const { device } = useCameraWebGpuDevice();
52
+ * const effect = useMemo(() => (device ? buildMyEffect(device) : null), [device]);
53
+ * ```
54
+ *
55
+ * @group WebGPU
56
+ */
57
+ export function useCameraWebGpuDevice() {
58
+ const [result, setResult] = useState({
59
+ device: null,
60
+ error: null
61
+ });
62
+ useEffect(() => {
63
+ let cancelled = false;
64
+ getSharedCameraWebGpuDevice().then(device => {
65
+ if (!cancelled) {
66
+ setResult({
67
+ device,
68
+ error: null
69
+ });
70
+ }
71
+ }).catch(cause => {
72
+ if (!cancelled) {
73
+ setResult({
74
+ device: null,
75
+ error: toError(cause)
76
+ });
77
+ }
78
+ });
79
+ return () => {
80
+ cancelled = true;
81
+ };
82
+ }, []);
83
+ return result;
84
+ }
85
+
86
+ /**
87
+ * Device resolution for the source hook: the user-provided override (validated) when present,
88
+ * otherwise the shared device. Always called, so hook order stays stable either way.
89
+ */
90
+ export function useCameraWebGpuDeviceWithOverride(override) {
91
+ const shared = useCameraWebGpuDevice();
92
+ const overrideValidationError = useMemo(() => {
93
+ if (override == null) {
94
+ return null;
95
+ }
96
+ try {
97
+ assertWebGpuDeviceSupportsCameraImport(override);
98
+ return null;
99
+ } catch (cause) {
100
+ return toError(cause);
101
+ }
102
+ }, [override]);
103
+ if (override != null) {
104
+ return overrideValidationError != null ? {
105
+ device: null,
106
+ error: overrideValidationError
107
+ } : {
108
+ device: override,
109
+ error: null
110
+ };
111
+ }
112
+ return shared;
113
+ }
114
+ //# sourceMappingURL=useCameraWebGpuDevice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useMemo","useState","toError","assertWebGpuDeviceSupportsCameraImport","getRequiredWebGpuCameraFeatures","getWebGpuRuntime","sharedDevicePromise","acquireSharedCameraWebGpuDevice","adapter","navigator","gpu","requestAdapter","Error","requestDevice","requiredFeatures","getSharedCameraWebGpuDevice","clearIfCurrent","devicePromise","then","device","lost","cause","useCameraWebGpuDevice","result","setResult","error","cancelled","catch","useCameraWebGpuDeviceWithOverride","override","shared","overrideValidationError"],"sources":["useCameraWebGpuDevice.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport { toError } from '../internal/toError';\nimport { assertWebGpuDeviceSupportsCameraImport, getRequiredWebGpuCameraFeatures } from './requiredFeatures';\nimport { getWebGpuRuntime } from './webGpuRuntime';\n\n// One app-wide device: every hook instance shares it, so per-device resources (pipelines,\n// shared-texture imports) built by different callers stay valid for each other. Cleared on\n// device loss or acquisition failure so a later caller retries.\nlet sharedDevicePromise: Promise<GPUDevice> | null = null;\n\nasync function acquireSharedCameraWebGpuDevice(): Promise<GPUDevice> {\n getWebGpuRuntime();\n const adapter = await navigator.gpu.requestAdapter();\n if (adapter == null) {\n throw new Error('WebGPU is unavailable: navigator.gpu.requestAdapter() returned no adapter.');\n }\n return adapter.requestDevice({\n requiredFeatures: getRequiredWebGpuCameraFeatures(),\n });\n}\n\nfunction getSharedCameraWebGpuDevice(): Promise<GPUDevice> {\n if (sharedDevicePromise == null) {\n // Only this promise may clear the slot: by the time a loss (or failure) callback fires, a\n // replacement device may already occupy it, and unconditionally nulling would discard that\n // replacement.\n const clearIfCurrent = () => {\n if (sharedDevicePromise === devicePromise) {\n sharedDevicePromise = null;\n }\n };\n const devicePromise: Promise<GPUDevice> = acquireSharedCameraWebGpuDevice().then(\n (device) => {\n device.lost.then(clearIfCurrent, clearIfCurrent);\n return device;\n },\n (cause: unknown) => {\n clearIfCurrent();\n throw cause;\n },\n );\n sharedDevicePromise = devicePromise;\n }\n return sharedDevicePromise;\n}\n\n/** Result of {@link useCameraWebGpuDevice}. */\nexport interface UseCameraWebGpuDeviceResult {\n /** The shared GPUDevice; `null` until acquisition resolves. */\n device: GPUDevice | null;\n /** Acquisition failure (no adapter, missing platform features), if any. */\n error: Error | null;\n}\n\n/**\n * The app-wide GPUDevice used for camera work, configured with\n * {@link getRequiredWebGpuCameraFeatures}. All callers share one device, so pipelines you build\n * against it work with the textures the source hook hands your render callback.\n *\n * Build your pipelines once the device arrives:\n * ```tsx\n * const { device } = useCameraWebGpuDevice();\n * const effect = useMemo(() => (device ? buildMyEffect(device) : null), [device]);\n * ```\n *\n * @group WebGPU\n */\nexport function useCameraWebGpuDevice(): UseCameraWebGpuDeviceResult {\n const [result, setResult] = useState<UseCameraWebGpuDeviceResult>({ device: null, error: null });\n\n useEffect(() => {\n let cancelled = false;\n getSharedCameraWebGpuDevice()\n .then((device) => {\n if (!cancelled) {\n setResult({ device, error: null });\n }\n })\n .catch((cause: unknown) => {\n if (!cancelled) {\n setResult({ device: null, error: toError(cause) });\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n return result;\n}\n\n/**\n * Device resolution for the source hook: the user-provided override (validated) when present,\n * otherwise the shared device. Always called, so hook order stays stable either way.\n */\nexport function useCameraWebGpuDeviceWithOverride(override: GPUDevice | undefined): UseCameraWebGpuDeviceResult {\n const shared = useCameraWebGpuDevice();\n\n const overrideValidationError = useMemo(() => {\n if (override == null) {\n return null;\n }\n try {\n assertWebGpuDeviceSupportsCameraImport(override);\n return null;\n } catch (cause) {\n return toError(cause);\n }\n }, [override]);\n\n if (override != null) {\n return overrideValidationError != null\n ? { device: null, error: overrideValidationError }\n : { device: override, error: null };\n }\n return shared;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,25 @@
1
+ import { WebGPUModule } from 'react-native-webgpu';
2
+
3
+ /** The react-native-webgpu runtime singleton (the `RNWebGPU` JSI global). */
4
+
5
+ /**
6
+ * Returns the react-native-webgpu runtime singleton, self-healing when the `RNWebGPU` global is
7
+ * missing on the calling runtime. The library binds the global once at module load, but that
8
+ * binding can be absent after a crash-recovery runtime reload or an install-ordering flake;
9
+ * `WebGPUModule.install()` binds it to the calling runtime and is safe to repeat.
10
+ *
11
+ * @throws When the runtime cannot be installed (react-native-webgpu missing or its native module
12
+ * not linked) — returning the still-undefined global would only defer the failure to an opaque
13
+ * per-frame TypeError.
14
+ */
15
+ export function getWebGpuRuntime() {
16
+ if (typeof RNWebGPU === 'undefined') {
17
+ const installedOk = WebGPUModule.install();
18
+ if (!installedOk || typeof RNWebGPU === 'undefined') {
19
+ throw new Error('react-native-custom-video-source: the RNWebGPU global is missing and WebGPUModule.install() ' + 'could not bind it. Make sure react-native-webgpu is installed and its native module is linked ' + '(rebuild the app after adding it).');
20
+ }
21
+ console.warn('react-native-custom-video-source: the RNWebGPU global was missing on this runtime; re-ran install()');
22
+ }
23
+ return RNWebGPU;
24
+ }
25
+ //# sourceMappingURL=webGpuRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["WebGPUModule","getWebGpuRuntime","RNWebGPU","installedOk","install","Error","console","warn"],"sources":["webGpuRuntime.ts"],"sourcesContent":["import { WebGPUModule } from 'react-native-webgpu';\n\n/** The react-native-webgpu runtime singleton (the `RNWebGPU` JSI global). */\nexport type WebGpuRuntime = typeof RNWebGPU;\n\n/**\n * Returns the react-native-webgpu runtime singleton, self-healing when the `RNWebGPU` global is\n * missing on the calling runtime. The library binds the global once at module load, but that\n * binding can be absent after a crash-recovery runtime reload or an install-ordering flake;\n * `WebGPUModule.install()` binds it to the calling runtime and is safe to repeat.\n *\n * @throws When the runtime cannot be installed (react-native-webgpu missing or its native module\n * not linked) — returning the still-undefined global would only defer the failure to an opaque\n * per-frame TypeError.\n */\nexport function getWebGpuRuntime(): WebGpuRuntime {\n if (typeof RNWebGPU === 'undefined') {\n const installedOk = WebGPUModule.install();\n if (!installedOk || typeof RNWebGPU === 'undefined') {\n throw new Error(\n 'react-native-custom-video-source: the RNWebGPU global is missing and WebGPUModule.install() ' +\n 'could not bind it. Make sure react-native-webgpu is installed and its native module is linked ' +\n '(rebuild the app after adding it).',\n );\n }\n console.warn('react-native-custom-video-source: the RNWebGPU global was missing on this runtime; re-ran install()');\n }\n return RNWebGPU;\n}\n"],"mappings":"AAAA,SAASA,YAAY,QAAQ,qBAAqB;;AAElD;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAAA,EAAkB;EAChD,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE;IACnC,MAAMC,WAAW,GAAGH,YAAY,CAACI,OAAO,CAAC,CAAC;IAC1C,IAAI,CAACD,WAAW,IAAI,OAAOD,QAAQ,KAAK,WAAW,EAAE;MACnD,MAAM,IAAIG,KAAK,CACb,8FAA8F,GAC5F,gGAAgG,GAChG,oCACJ,CAAC;IACH;IACAC,OAAO,CAACC,IAAI,CAAC,qGAAqG,CAAC;EACrH;EACA,OAAOL,QAAQ;AACjB","ignoreList":[]}
@@ -0,0 +1,25 @@
1
+ /// <reference types="@webgpu/types" />
2
+ /**
3
+ * WebGPU camera toolkit — sample the live camera and render your own content into the video you
4
+ * publish with `useManagedPooledTrack` (from the package's main entry point). Requires `react-native-webgpu` (an optional peer of
5
+ * this package; only this entry point loads it).
6
+ *
7
+ * - {@link createCameraShaderBindings} — sample the live camera from your own shaders via
8
+ * `sampleCamera(uv)`, with the platform's YUV decode handled for you.
9
+ * - {@link createCameraPassthroughPipeline} / {@link encodeCameraPassthrough} — a ready-made
10
+ * camera→output pass to publish the camera with zero WGSL, or to build overlays on.
11
+ * - {@link createCameraTextureResolver} — opt-in plain-texture camera for pipelines that can't
12
+ * sample `texture_external`.
13
+ * - {@link useCameraWebGpuDevice} — the shared, camera-import-capable GPUDevice.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+
18
+ export { createCameraPassthroughPipeline, encodeCameraPassthrough } from './webgpu/cameraPassthroughPipeline';
19
+ export { createCameraBindGroup, createCameraShaderBindings, sampleCamera } from './webgpu/cameraShaderBindings';
20
+ export { createCameraTextureResolver, resolveCameraTexture } from './webgpu/cameraTextureResolver';
21
+ export { computeAspectFillCrop, computeSquareCrop, FrameCropParams, packFrameCropParams } from './webgpu/cropUtilities';
22
+ export { assertWebGpuDeviceSupportsCameraImport, getOutputSurfaceFormat, getRequiredWebGpuCameraFeatures } from './webgpu/requiredFeatures';
23
+ export { useCameraWebGpuDevice, useCameraWebGpuDeviceWithOverride } from './webgpu/useCameraWebGpuDevice';
24
+ export { getWebGpuRuntime } from './webgpu/webGpuRuntime';
25
+ //# sourceMappingURL=webgpu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createCameraPassthroughPipeline","encodeCameraPassthrough","createCameraBindGroup","createCameraShaderBindings","sampleCamera","createCameraTextureResolver","resolveCameraTexture","computeAspectFillCrop","computeSquareCrop","FrameCropParams","packFrameCropParams","assertWebGpuDeviceSupportsCameraImport","getOutputSurfaceFormat","getRequiredWebGpuCameraFeatures","useCameraWebGpuDevice","useCameraWebGpuDeviceWithOverride","getWebGpuRuntime"],"sources":["webgpu.ts"],"sourcesContent":["/// <reference types=\"@webgpu/types\" />\n/**\n * WebGPU camera toolkit — sample the live camera and render your own content into the video you\n * publish with `useManagedPooledTrack` (from the package's main entry point). Requires `react-native-webgpu` (an optional peer of\n * this package; only this entry point loads it).\n *\n * - {@link createCameraShaderBindings} — sample the live camera from your own shaders via\n * `sampleCamera(uv)`, with the platform's YUV decode handled for you.\n * - {@link createCameraPassthroughPipeline} / {@link encodeCameraPassthrough} — a ready-made\n * camera→output pass to publish the camera with zero WGSL, or to build overlays on.\n * - {@link createCameraTextureResolver} — opt-in plain-texture camera for pipelines that can't\n * sample `texture_external`.\n * - {@link useCameraWebGpuDevice} — the shared, camera-import-capable GPUDevice.\n *\n * @packageDocumentation\n */\n\nexport {\n type CameraPassthroughPipeline,\n type CameraPassthroughPipelineOptions,\n createCameraPassthroughPipeline,\n encodeCameraPassthrough,\n} from './webgpu/cameraPassthroughPipeline';\nexport {\n type CameraShaderBindings,\n createCameraBindGroup,\n createCameraShaderBindings,\n type CreateCameraShaderBindingsOptions,\n sampleCamera,\n} from './webgpu/cameraShaderBindings';\nexport {\n type CameraTextureResolver,\n createCameraTextureResolver,\n resolveCameraTexture,\n} from './webgpu/cameraTextureResolver';\nexport {\n computeAspectFillCrop,\n computeSquareCrop,\n type FrameCrop,\n FrameCropParams,\n packFrameCropParams,\n} from './webgpu/cropUtilities';\nexport type { WebGpuFrameRenderContext, WebGpuFrameRenderFunction } from './webgpu/frameRenderContext';\nexport {\n assertWebGpuDeviceSupportsCameraImport,\n getOutputSurfaceFormat,\n getRequiredWebGpuCameraFeatures,\n} from './webgpu/requiredFeatures';\nexport {\n useCameraWebGpuDevice,\n type UseCameraWebGpuDeviceResult,\n useCameraWebGpuDeviceWithOverride,\n} from './webgpu/useCameraWebGpuDevice';\nexport { getWebGpuRuntime, type WebGpuRuntime } from './webgpu/webGpuRuntime';\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAGEA,+BAA+B,EAC/BC,uBAAuB,QAClB,oCAAoC;AAC3C,SAEEC,qBAAqB,EACrBC,0BAA0B,EAE1BC,YAAY,QACP,+BAA+B;AACtC,SAEEC,2BAA2B,EAC3BC,oBAAoB,QACf,gCAAgC;AACvC,SACEC,qBAAqB,EACrBC,iBAAiB,EAEjBC,eAAe,EACfC,mBAAmB,QACd,wBAAwB;AAE/B,SACEC,sCAAsC,EACtCC,sBAAsB,EACtBC,+BAA+B,QAC1B,2BAA2B;AAClC,SACEC,qBAAqB,EAErBC,iCAAiC,QAC5B,gCAAgC;AACvC,SAASC,gBAAgB,QAA4B,wBAAwB","ignoreList":[]}