1- import { oklabGamutClipSlot , oklabToLinearRgb } from '@typegpu/color' ;
1+ import { oklabGamutClip , oklabToLinearRgb } from '@typegpu/color' ;
22import {
33 createFft2d ,
44 createStockhamRadix2LineStrategy ,
@@ -196,7 +196,7 @@ const filterKernel = tgpu.computeFn({
196196 const cutoff = filterLayout . $ . params . cutoffRadius * rMax ;
197197 const r = std . sqrt ( r2 ) ;
198198 const mask = std . select ( d . f32 ( 0 ) , d . f32 ( 1 ) , r <= cutoff ) ;
199- const c = filterLayout . $ . spectrum [ tid ] as d . v2f ;
199+ const c = filterLayout . $ . spectrum [ tid ] ;
200200 filterLayout . $ . spectrum [ tid ] = d . vec2f ( c . x * mask , c . y * mask ) ;
201201} ) ;
202202
@@ -248,17 +248,17 @@ const magKernel = tgpu.computeFn({
248248 srcX * padH + srcY ,
249249 magLayout . $ . params . swapSpectrumAxes !== d . u32 ( 0 ) ,
250250 ) ;
251- const cShift = magLayout . $ . spectrum [ srcTid ] as d . v2f ;
251+ const cShift = magLayout . $ . spectrum [ srcTid ] ;
252252 const len = std . sqrt ( cShift . x * cShift . x + cShift . y * cShift . y ) ;
253253 const logv = std . log ( 1.0 + len ) * magLayout . $ . params . gain ;
254254 const cv = std . clamp ( logv , 0.0 , 1.0 ) ;
255- /** Perceptual lightness from log-magnitude; chroma scales with magnitude; phase → hue in the `a,b` plane . */
256- const eps = 1e-8 ;
257- const hue = std . atan2 ( cShift . y , cShift . x ) ;
258- const chroma = std . select ( cv * 0.16 , d . f32 ( 0 ) , len < eps ) ;
259- const L = 0.04 + cv * 0.88 ;
260- const lab = d . vec3f ( L , chroma * std . cos ( hue ) , chroma * std . sin ( hue ) ) ;
261- const rgb = oklabToLinearRgb ( oklabGamutClipSlot . $ ( lab ) ) ;
255+ /** `cv` from log-magnitude; L ∈ [0.04, 1]; chroma → 0 at max `cv` so peaks go neutral white . */
256+ const L = 0.04 + cv * ( 1.0 - 0.04 ) ;
257+ const chroma = cv * ( 1.0 - cv ) * 0.32 ;
258+ /** Oklab a,b = chroma × unit phase — same as chroma·(cos θ, sin θ) with θ = atan2(im, re ), without trig. */
259+ const invLen = 1.0 / std . max ( len , 1e-20 ) ;
260+ const lab = d . vec3f ( L , chroma * invLen * cShift . x , chroma * invLen * cShift . y ) ;
261+ const rgb = oklabToLinearRgb ( oklabGamutClip . adaptiveL05 ( lab ) ) ;
262262 std . textureStore ( magLayout . $ . outTex , d . vec2u ( xLin , yLin ) , d . vec4f ( rgb , 1 ) ) ;
263263} ) ;
264264
@@ -295,7 +295,7 @@ const spatialKernel = tgpu.computeFn({
295295 return ;
296296 }
297297
298- const c = spatialLayout . $ . spectrum [ tid ] as d . v2f ;
298+ const c = spatialLayout . $ . spectrum [ tid ] ;
299299 const inv = spatialLayout . $ . params . invSize ;
300300 const g = std . clamp ( c . x * inv , 0.0 , 1.0 ) ;
301301 const padWLog2 = spatialLayout . $ . params . padWLog2 ;
@@ -481,9 +481,12 @@ let gainValue = 0.2;
481481let cutoffRadiusNorm = 1 ;
482482/** Separable Hann window on camera ROI before FFT (reduces periodic-boundary cross in spectrum). */
483483let applyEdgeWindow = false ;
484+ let lastFillUniformKey = '' ;
484485let lastMagUniformKey = '' ;
485486let lastSpatialUniformKey = '' ;
486487let lastFilterUniformKey = '' ;
488+ let lastDisplayFbKey = '' ;
489+ let lastVideoBlitKey = '' ;
487490
488491function effectiveFrameSize ( frameW : number , frameH : number ) : { effW : number ; effH : number } {
489492 let effW : number ;
@@ -553,16 +556,22 @@ function ensureResources(frameW: number, frameH: number) {
553556 padW = nextPadW ;
554557 padH = nextPadH ;
555558 const lineFftStrategyFactory =
556- lineFftMode === 'radix-2' ? createStockhamRadix2LineStrategy : createStockhamRadix4LineStrategy ;
559+ lineFftMode === 'radix-2'
560+ ? createStockhamRadix2LineStrategy
561+ : createStockhamRadix4LineStrategy ;
557562 fft = createFft2d ( root , {
558563 width : padW ,
559564 height : padH ,
560565 skipFinalTranspose : SKIP_FINAL_FFT_TRANSPOSE ,
561566 lineFftStrategyFactory,
562567 } ) ;
563568 fftLineFftMode = lineFftMode ;
569+ lastFillUniformKey = '' ;
564570 lastMagUniformKey = '' ;
571+ lastSpatialUniformKey = '' ;
565572 lastFilterUniformKey = '' ;
573+ lastDisplayFbKey = '' ;
574+ lastVideoBlitKey = '' ;
566575
567576 displayTexture = createPaddedDisplayTexture ( padW , padH ) ;
568577
@@ -682,7 +691,11 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
682691
683692 const { effW, effH } = effectiveFrameSize ( frameWidth , frameHeight ) ;
684693
685- videoBlitTargetPx . write ( { w : effW , h : effH } ) ;
694+ const videoBlitKey = `${ effW } x${ effH } ` ;
695+ if ( videoBlitKey !== lastVideoBlitKey ) {
696+ lastVideoBlitKey = videoBlitKey ;
697+ videoBlitTargetPx . write ( { w : effW , h : effH } ) ;
698+ }
686699 const videoBlitBindGroup = root . createBindGroup ( videoBlitLayout , {
687700 inputTexture : device . importExternalTexture ( { source : video } ) ,
688701 targetPx : videoBlitTargetPx ,
@@ -696,15 +709,19 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
696709 . draw ( 3 ) ;
697710
698711 const padWLog2 = log2Int ( padW ) ;
699- fillParams . write ( {
700- videoW : effW ,
701- videoH : effH ,
702- padW,
703- padH,
704- padWLog2,
705- padWMask : padW - 1 ,
706- edgeWindow : applyEdgeWindow ? 1 : 0 ,
707- } ) ;
712+ const fillUniformKey = `${ effW } x${ effH } x${ padW } x${ padH } x${ applyEdgeWindow } ` ;
713+ if ( fillUniformKey !== lastFillUniformKey ) {
714+ lastFillUniformKey = fillUniformKey ;
715+ fillParams . write ( {
716+ videoW : effW ,
717+ videoH : effH ,
718+ padW,
719+ padH,
720+ padWLog2,
721+ padWMask : padW - 1 ,
722+ edgeWindow : applyEdgeWindow ? 1 : 0 ,
723+ } ) ;
724+ }
708725
709726 const magUniformKey = `${ padW } x${ padH } x${ gainValue } x${ activeFft . skipFinalTranspose } ` ;
710727 if ( magUniformKey !== lastMagUniformKey ) {
@@ -752,15 +769,19 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
752769 const gpuCanvas = context . canvas ;
753770 const fbW = Math . max ( 1 , gpuCanvas . width ) ;
754771 const fbH = Math . max ( 1 , gpuCanvas . height ) ;
755- displayFb . write ( {
756- fbW,
757- fbH,
758- padW,
759- padH,
760- effW,
761- effH,
762- viewMode : applyInverseFft ? 1 : 0 ,
763- } ) ;
772+ const displayFbKey = `${ fbW } x${ fbH } x${ padW } x${ padH } x${ effW } x${ effH } x${ applyInverseFft } ` ;
773+ if ( displayFbKey !== lastDisplayFbKey ) {
774+ lastDisplayFbKey = displayFbKey ;
775+ displayFb . write ( {
776+ fbW,
777+ fbH,
778+ padW,
779+ padH,
780+ effW,
781+ effH,
782+ viewMode : applyInverseFft ? 1 : 0 ,
783+ } ) ;
784+ }
764785
765786 const enc = device . createCommandEncoder ( { label : 'camera-fft frame' } ) ;
766787 {
@@ -806,12 +827,14 @@ export const controls = defineControls({
806827 initial : false ,
807828 onToggleChange : ( value ) => {
808829 applyInverseFft = value ;
830+ lastDisplayFbKey = '' ;
809831 } ,
810832 } ,
811833 edgeWindow : {
812834 initial : false ,
813835 onToggleChange : ( value ) => {
814836 applyEdgeWindow = value ;
837+ lastFillUniformKey = '' ;
815838 } ,
816839 } ,
817840 fftMaxSide : {
0 commit comments