11import { oklabGamutClip , oklabToLinearRgb } from '@typegpu/color' ;
2+ import type { Fft2d } from '@typegpu/sort' ;
23import {
34 createFft2d ,
45 createStockhamRadix2LineStrategy ,
56 createStockhamRadix4LineStrategy ,
6- type Fft2d ,
77} from '@typegpu/sort' ;
8+ import type { TgpuBindGroup } from 'typegpu' ;
89import tgpu , { common , d , std } from 'typegpu' ;
910import { defineControls } from '../../common/defineControls.ts' ;
1011
@@ -73,7 +74,7 @@ function decodedFrameSize(
7374 return { width : w , height : h } ;
7475}
7576
76- const fillParamsType = d . struct ( {
77+ const FillParams = d . struct ( {
7778 videoW : d . u32 ,
7879 videoH : d . u32 ,
7980 padW : d . u32 ,
@@ -88,7 +89,7 @@ const fillParamsType = d.struct({
8889
8990const fillLayout = tgpu . bindGroupLayout ( {
9091 video : { texture : d . texture2d ( ) } ,
91- params : { uniform : fillParamsType } ,
92+ params : { uniform : FillParams } ,
9293 out : { storage : d . arrayOf ( d . vec2f ) , access : 'mutable' } ,
9394} ) ;
9495
@@ -164,6 +165,7 @@ const filterKernel = tgpu.computeFn({
164165 numWorkgroups : d . builtin . numWorkgroups ,
165166 } ,
166167} ) ( ( input ) => {
168+ 'use gpu' ;
167169 const wg = d . u32 ( WORKGROUP ) ;
168170 const spanX = input . numWorkgroups . x * wg ;
169171 const spanY = input . numWorkgroups . y * spanX ;
@@ -203,8 +205,7 @@ const filterKernel = tgpu.computeFn({
203205 const highMaskInner = std . select ( d . f32 ( 0 ) , d . f32 ( 1 ) , r > highC ) ;
204206 const highMask = std . select ( highMaskInner , d . f32 ( 1 ) , highPassOff ) ;
205207 const mask = lowMask * highMask ;
206- const c = filterLayout . $ . spectrum [ tid ] ;
207- filterLayout . $ . spectrum [ tid ] = d . vec2f ( c . x * mask , c . y * mask ) ;
208+ filterLayout . $ . spectrum [ tid ] *= mask ;
208209} ) ;
209210
210211const magParamsType = d . struct ( {
@@ -257,10 +258,10 @@ const magKernel = tgpu.computeFn({
257258 magLayout . $ . params . swapSpectrumAxes !== d . u32 ( 0 ) ,
258259 ) ;
259260 const cShift = magLayout . $ . spectrum [ srcTid ] ;
260- const lenRaw = std . sqrt ( cShift . x * cShift . x + cShift . y * cShift . y ) ;
261+ const lenRaw = std . length ( cShift ) ;
261262 /** Log stretch tuned on these magnitudes (`log(1+|·|) * 0.2`). */
262263 const logv = std . log ( 1.0 + lenRaw ) * 0.2 * std . exp2 ( magLayout . $ . params . exposure ) ;
263- const cv = std . clamp ( logv , 0.0 , 1.0 ) ;
264+ const cv = std . saturate ( logv ) ;
264265 /** `cv` from log-magnitude; L ∈ [0.04, 1]; chroma → 0 at max `cv` so peaks go neutral white. */
265266 const L = 0.04 + cv * ( 1.0 - 0.04 ) ;
266267 const chroma = cv * ( 1.0 - cv ) * 0.32 ;
@@ -295,6 +296,7 @@ const spatialKernel = tgpu.computeFn({
295296 numWorkgroups : d . builtin . numWorkgroups ,
296297 } ,
297298} ) ( ( input ) => {
299+ 'use gpu' ;
298300 const wg = d . u32 ( WORKGROUP ) ;
299301 const spanX = input . numWorkgroups . x * wg ;
300302 const spanY = input . numWorkgroups . y * spanX ;
@@ -308,7 +310,7 @@ const spatialKernel = tgpu.computeFn({
308310
309311 const c = spatialLayout . $ . spectrum [ tid ] ;
310312 const inv = spatialLayout . $ . params . invSize ;
311- const g = std . clamp ( c . x * inv * std . exp2 ( spatialLayout . $ . params . exposure ) , 0.0 , 1.0 ) ;
313+ const g = std . saturate ( c . x * inv * std . exp2 ( spatialLayout . $ . params . exposure ) ) ;
312314 const padWLog2 = spatialLayout . $ . params . padWLog2 ;
313315 const padWMask = spatialLayout . $ . params . padWMask ;
314316 const x = tid & padWMask ;
@@ -377,15 +379,10 @@ const spectrumFrag = tgpu.fragmentFn({
377379 return d . vec4f ( col . rgb , 1 ) ;
378380} ) ;
379381
380- const videoBlitTargetDimsType = d . struct ( {
381- w : d . f32 ,
382- h : d . f32 ,
383- } ) ;
384-
385382const videoBlitLayout = tgpu . bindGroupLayout ( {
386383 inputTexture : { externalTexture : d . textureExternal ( ) } ,
387384 /** Render-target size (pixels); `fullScreenTriangle` uv is not linear in pixel space for offscreen passes. */
388- targetPx : { uniform : videoBlitTargetDimsType } ,
385+ targetPx : { uniform : d . vec2f } ,
389386} ) ;
390387
391388const canvas = document . querySelector ( 'canvas' ) as HTMLCanvasElement ;
@@ -419,15 +416,14 @@ const sampler = root['~unstable'].createSampler({
419416 minFilter : 'linear' ,
420417} ) ;
421418
422- const videoBlitTargetPx = root . createBuffer ( videoBlitTargetDimsType ) . $usage ( 'uniform' ) ;
419+ const videoBlitTargetPx = root . createBuffer ( d . vec2f ) . $usage ( 'uniform' ) ;
423420
424421const videoBlitFrag = tgpu . fragmentFn ( {
425422 in : { position : d . builtin . position } ,
426423 out : d . vec4f ,
427424} ) ( ( input ) => {
428- const w = d . f32 ( videoBlitLayout . $ . targetPx . w ) ;
429- const rtH = d . f32 ( videoBlitLayout . $ . targetPx . h ) ;
430- const st = d . vec2f ( input . position . x / w , input . position . y / rtH ) ;
425+ 'use gpu' ;
426+ const st = input . position . xy / videoBlitLayout . $ . targetPx ;
431427 return std . textureSampleBaseClampToEdge ( videoBlitLayout . $ . inputTexture , sampler . $ , st ) ;
432428} ) ;
433429
@@ -470,7 +466,7 @@ let displayTexture: ReturnType<typeof createPaddedDisplayTexture> | undefined;
470466let displaySampleView : ReturnType < typeof displaySampleViewOf > | undefined ;
471467let displayStorageView : ReturnType < typeof displayStorageViewOf > | undefined ;
472468
473- const fillParams = root . createBuffer ( fillParamsType ) . $usage ( 'uniform' ) ;
469+ const fillParams = root . createBuffer ( FillParams ) . $usage ( 'uniform' ) ;
474470const filterParams = root . createBuffer ( filterParamsType ) . $usage ( 'uniform' ) ;
475471const magParams = root . createBuffer ( magParamsType ) . $usage ( 'uniform' ) ;
476472const spatialParams = root . createBuffer ( spatialParamsType ) . $usage ( 'uniform' ) ;
@@ -516,18 +512,12 @@ function effectiveFrameSize(frameW: number, frameH: number): { effW: number; eff
516512 return { effW, effH } ;
517513}
518514
519- let fillBindGroup : ReturnType < typeof root . createBindGroup > | undefined ;
515+ let fillBindGroup : TgpuBindGroup | undefined ;
520516/** One bind group per ping-pong buffer; use `fft.outputIndex()` after each transform to pick the right one. */
521- let magBindSlots :
522- | [ ReturnType < typeof root . createBindGroup > , ReturnType < typeof root . createBindGroup > ]
523- | undefined ;
524- let filterBindSlots :
525- | [ ReturnType < typeof root . createBindGroup > , ReturnType < typeof root . createBindGroup > ]
526- | undefined ;
527- let spatialBindSlots :
528- | [ ReturnType < typeof root . createBindGroup > , ReturnType < typeof root . createBindGroup > ]
529- | undefined ;
530- let renderBindGroup : ReturnType < typeof root . createBindGroup > | undefined ;
517+ let magBindSlots : [ TgpuBindGroup , TgpuBindGroup ] | undefined ;
518+ let filterBindSlots : [ TgpuBindGroup , TgpuBindGroup ] | undefined ;
519+ let spatialBindSlots : [ TgpuBindGroup , TgpuBindGroup ] | undefined ;
520+ let renderBindGroup : TgpuBindGroup | undefined ;
531521
532522function invalidateBindGroups ( all : boolean ) {
533523 fillBindGroup = undefined ;
@@ -708,7 +698,7 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
708698 const videoBlitKey = `${ effW } x${ effH } ` ;
709699 if ( videoBlitKey !== lastVideoBlitKey ) {
710700 lastVideoBlitKey = videoBlitKey ;
711- videoBlitTargetPx . write ( { w : effW , h : effH } ) ;
701+ videoBlitTargetPx . write ( d . vec2f ( effW , effH ) ) ;
712702 }
713703 const videoBlitBindGroup = root . createBindGroup ( videoBlitLayout , {
714704 inputTexture : device . importExternalTexture ( { source : video } ) ,
0 commit comments