Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions components/PatternDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,11 @@ export const PatternDisplay: React.FC<PatternDisplayProps> = ({
...(playbackStateRef ? { playbackStateRef } : {}),
}, setDebugInfo);

// WebGPU render hook
// WebGPU render hook — matrix and padTopChannel passed directly so React tracks them as
// explicit deps, guaranteeing the cells buffer is rebuilt when a new module is loaded.
const { gpuReady, render, deviceRef: gpuDevRef } = useWebGPURender(
canvasRef, glCanvasRef, shaderFile,
syncCanvasSize, renderParamsRef, setDebugInfo, setWebgpuAvailable
syncCanvasSize, renderParamsRef, matrix, padTopChannel, setDebugInfo, setWebgpuAvailable
);

// Keep resize reconfiguration refs in sync
Expand Down
20 changes: 14 additions & 6 deletions hooks/useWebGPURender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
shaderFile: string,
syncCanvasSize: (canvas: HTMLCanvasElement, gl: HTMLCanvasElement | null) => void,
renderParamsRef: React.MutableRefObject<WebGPURenderParams>,
matrix: import('../types').PatternMatrix | null,
padTopChannel: boolean,
setDebugInfo: React.Dispatch<React.SetStateAction<DebugInfo>>,
setWebgpuAvailable: (v: boolean) => void
) {
Expand Down Expand Up @@ -362,20 +364,26 @@
};
}, [shaderFile, syncCanvasSize]); // eslint-disable-line react-hooks/exhaustive-deps

// Update cells buffer when matrix changes
// Update cells buffer when matrix changes.
// Uses `matrix` and `padTopChannel` as direct React deps (not via renderParamsRef) so React
// reliably detects new-module loads even if the ref mutation timing is ambiguous.
useEffect(() => {
const device = deviceRef.current;
if (!device || !gpuReady) return;
const p = renderParamsRef.current;
console.log(`[PatternDisplay] Updating cells buffer: matrix=${p.matrix ? 'yes' : 'null'}, rows=${p.matrix?.numRows}, channels=${p.matrix?.numChannels}`);
console.log(`[PatternDisplay] Updating cells buffer: matrix=${matrix ? 'yes' : 'null'}, rows=${matrix?.numRows}, channels=${matrix?.numChannels}`);
if (cellsBufferRef.current) cellsBufferRef.current.destroy();
const isHighPrec = shaderFile.includes('v0.36') || shaderFile.includes('v0.37') || shaderFile.includes('v0.38') || shaderFile.includes('v0.39') || shaderFile.includes('v0.40') || shaderFile.includes('v0.42') || shaderFile.includes('v0.43') || shaderFile.includes('v0.44') || shaderFile.includes('v0.45') || shaderFile.includes('v0.46') || shaderFile.includes('v0.47') || shaderFile.includes('v0.48') || shaderFile.includes('v0.49') || shaderFile.includes('v0.50');
const packFunc = isHighPrec ? packPatternMatrixHighPrecision : packPatternMatrix;
const { packedData, noteCount } = packFunc(p.matrix, p.padTopChannel);

Check failure on line 377 in hooks/useWebGPURender.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Cannot find name 'p'.

Check failure on line 377 in hooks/useWebGPURender.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Cannot find name 'p'.

Check failure on line 377 in hooks/useWebGPURender.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Cannot redeclare block-scoped variable 'noteCount'.
let noteCount = 0;

Check failure on line 378 in hooks/useWebGPURender.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Cannot redeclare block-scoped variable 'noteCount'.
for (let i = 0; i < packedData.length; i += 2) {
const note = ((packedData[i] ?? 0) >> 24) & 0xFF;
if (note > 0) noteCount++;

Check failure on line 381 in hooks/useWebGPURender.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

Cannot assign to 'noteCount' because it is a constant.
}
console.log(`[PatternDisplay] Packed data contains ${noteCount} notes in ${packedData.length / 2} cells`);
cellsBufferRef.current = createBufferWithData(device, packedData, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST);
if (layoutTypeRef.current === 'extended') {
const numRows = p.matrix?.numRows ?? DEFAULT_ROWS;
const numRows = matrix?.numRows ?? DEFAULT_ROWS;
const flags = buildRowFlags(numRows);
if (!rowFlagsBufferRef.current || rowFlagsBufferRef.current.size < flags.byteLength) {
rowFlagsBufferRef.current?.destroy();
Expand All @@ -385,7 +393,7 @@
}
}
refreshBindGroup(device);
}, [renderParamsRef.current.matrix, gpuReady, shaderFile, refreshBindGroup]); // eslint-disable-line react-hooks/exhaustive-deps
}, [matrix, padTopChannel, gpuReady, shaderFile, refreshBindGroup]); // eslint-disable-line react-hooks/exhaustive-deps

// Update channel states buffer
useEffect(() => {
Expand All @@ -411,7 +419,7 @@
}
if (recreated) refreshBindGroup(device);
}
}, [renderParamsRef.current.channels, renderParamsRef.current.matrix?.numChannels, gpuReady, refreshBindGroup]); // eslint-disable-line react-hooks/exhaustive-deps
}, [renderParamsRef.current.channels, matrix?.numChannels, padTopChannel, gpuReady, refreshBindGroup]); // eslint-disable-line react-hooks/exhaustive-deps

// Stable render function — reads from renderParamsRef to avoid stale closures
const render = useCallback(() => {
Expand Down
Loading