diff --git a/TODO.txt b/TODO.txt index f77b9cb..30718f4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,5 +1,19 @@ TODO: +Bloom comparison: WebGPU vs REGL + Why are they different? + Create a project that tests them side-by-side + That's right, two canvases, one regl and one webgpu + program them both to do the same basic ops in a floating point texture + display that texture + Retrieve the texture from the GPU and spit it out + Possible causes of difference + Color space + Intermediate texture formats around high pass filter + Floating point math + Texture interpolation + Blur implementation + Audio system Toggle (or number representing frequency) Load the sound effect @@ -25,17 +39,6 @@ Support Resurrections SDF bevel and "lights" Anomaly mode toggles between this and anomaly streaks WebGPU - Why is it brighter than the regl version? - Create a project that tests them side-by-side - That's right, two canvases, one regl and one webgpu - program them both to do the same basic ops in a floating point texture - display that texture - Retrieve the texture from the GPU and spit it out - Possible causes of difference - Color space - Floating point math - Texture interpolation - Blur implementation Try https://github.com/brendan-duncan/wgsl_reflect Get rid of end pass once it's possible to copy a bgra8unorm to a canvas texture Switch to rgba32float somehow? diff --git a/js/webgpu/rainPass.js b/js/webgpu/rainPass.js index 24bb035..89a5f2a 100644 --- a/js/webgpu/rainPass.js +++ b/js/webgpu/rainPass.js @@ -83,8 +83,10 @@ export default ({ config, device, timeBuffer }) => { let configBuffer; let sceneUniforms; let sceneBuffer; + let introPipeline; let computePipeline; let renderPipeline; + let introBindGroup; let computeBindGroup; let renderBindGroup; let output; @@ -115,7 +117,15 @@ export default ({ config, device, timeBuffer }) => { dstFactor: "one", }; - [computePipeline, renderPipeline] = await Promise.all([ + [introPipeline, computePipeline, renderPipeline] = await Promise.all([ + device.createComputePipelineAsync({ + layout: "auto", + compute: { + module: rainShader.module, + entryPoint: "computeIntro", + }, + }), + device.createComputePipelineAsync({ layout: "auto", compute: { @@ -153,6 +163,7 @@ export default ({ config, device, timeBuffer }) => { }), ]); + introBindGroup = makeBindGroup(device, introPipeline, 0, [configBuffer, timeBuffer, introCellsBuffer]); computeBindGroup = makeBindGroup(device, computePipeline, 0, [configBuffer, timeBuffer, cellsBuffer, introCellsBuffer]); renderBindGroup = makeBindGroup(device, renderPipeline, 0, [ configBuffer, @@ -198,6 +209,12 @@ export default ({ config, device, timeBuffer }) => { const run = (encoder) => { // We render the code into an Target using MSDFs: https://github.com/Chlumsky/msdfgen + const introPass = encoder.beginComputePass(); + introPass.setPipeline(introPipeline); + introPass.setBindGroup(0, introBindGroup); + introPass.dispatchWorkgroups(Math.ceil(gridSize[0] / 32), 1, 1); + introPass.end(); + const computePass = encoder.beginComputePass(); computePass.setPipeline(computePipeline); computePass.setBindGroup(0, computeBindGroup); diff --git a/shaders/wgsl/rainPass.wgsl b/shaders/wgsl/rainPass.wgsl index 4dba63a..1db791f 100644 --- a/shaders/wgsl/rainPass.wgsl +++ b/shaders/wgsl/rainPass.wgsl @@ -84,9 +84,12 @@ struct IntroCellData { @group(0) @binding(0) var config : Config; @group(0) @binding(1) var time : Time; +// Intro-specific bindings +@group(0) @binding(2) var introCells_RW : IntroCellData; + // Compute-specific bindings @group(0) @binding(2) var cells_RW : CellData; -@group(0) @binding(3) var introCells_RW : IntroCellData; +@group(0) @binding(3) var introCells_RO : IntroCellData; // Render-specific bindings @group(0) @binding(2) var scene : Scene; @@ -216,7 +219,7 @@ fn getRipple(simTime : f32, screenPos : vec2) -> f32 { // Compute shader main functions -fn computeIntro (simTime : f32, isFirstFrame : bool, glyphPos : vec2, screenPos : vec2, previous : vec4) -> vec4 { +fn computeIntroProgress (simTime : f32, isFirstFrame : bool, glyphPos : vec2, screenPos : vec2, previous : vec4) -> vec4 { if (bool(config.skipIntro)) { return vec4(2.0, 0.0, 0.0, 0.0); } @@ -298,6 +301,27 @@ fn computeEffect (simTime : f32, isFirstFrame : bool, glyphPos : vec2, scre return result; } +@compute @workgroup_size(32, 1, 1) fn computeIntro(input : ComputeInput) { + + // Resolve the invocation ID to an intro cell coordinate + var column = i32(input.id.x); + + if (column >= i32(config.gridSize.x)) { + return; + } + + var simTime = time.seconds * config.animationSpeed; + var isFirstFrame = time.frames == 0; + + // Update the cell + var glyphPos = vec2(f32(column), 0.0); + var screenPos = glyphPos / config.gridSize; + + var introCell = introCells_RW.cells[column]; + introCell.progress = computeIntroProgress(simTime, isFirstFrame, glyphPos, screenPos, introCell.progress); + introCells_RW.cells[column] = introCell; +} + @compute @workgroup_size(32, 1, 1) fn computeMain(input : ComputeInput) { // Resolve the invocation ID to a cell coordinate @@ -317,14 +341,8 @@ fn computeEffect (simTime : f32, isFirstFrame : bool, glyphPos : vec2, scre var glyphPos = vec2(f32(column), f32(row)); var screenPos = glyphPos / config.gridSize; - var introCell = introCells_RW.cells[column]; - - if (row == i32(config.gridSize.y - 1)) { - introCell.progress = computeIntro(simTime, isFirstFrame, glyphPos, screenPos, introCell.progress); - introCells_RW.cells[column] = introCell; - } - var cell = cells_RW.cells[i]; + var introCell = introCells_RO.cells[column]; cell.raindrop = computeRaindrop(simTime, isFirstFrame, glyphPos, screenPos, cell.raindrop, introCell.progress); cell.symbol = computeSymbol(simTime, isFirstFrame, glyphPos, screenPos, cell.symbol, cell.raindrop); cell.effect = computeEffect(simTime, isFirstFrame, glyphPos, screenPos, cell.effect, cell.raindrop);