From 35afa7ca01f2d75801642c6254234b7cec4edbe4 Mon Sep 17 00:00:00 2001 From: Rezmason Date: Wed, 3 Nov 2021 01:19:22 -0700 Subject: [PATCH] Pulled the rain render out of the render bundle, I won't likely benefit from it. Threw together renderToCanvas, which renders a texture to the canvas texture. --- js/webgpu/main.js | 86 +++++++++++++++++++++++--------- js/webgpu/utils.js | 8 ++- shaders/wgsl/renderToCanvas.wgsl | 17 +++++++ 3 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 shaders/wgsl/renderToCanvas.wgsl diff --git a/js/webgpu/main.js b/js/webgpu/main.js index a9969a3..693a9c6 100644 --- a/js/webgpu/main.js +++ b/js/webgpu/main.js @@ -1,5 +1,5 @@ import std140 from "./std140.js"; -import { getCanvasSize, loadTexture, makeUniformBuffer } from "./utils.js"; +import { getCanvasSize, loadTexture, loadShaderModule, makeUniformBuffer } from "./utils.js"; const { mat4, vec3 } = glMatrix; const rippleTypes = { @@ -30,8 +30,11 @@ export default async (canvas, config) => { canvasContext.configure(canvasConfig); - const msdfTexturePromise = loadTexture(device, config.glyphTexURL); - const rainShaderPromise = fetch("shaders/wgsl/rainPass.wgsl").then((response) => response.text()); + const assets = [ + loadTexture(device, config.glyphTexURL), + loadShaderModule(device, "shaders/wgsl/rainPass.wgsl"), + loadShaderModule(device, "shaders/wgsl/renderToCanvas.wgsl"), + ]; // The volumetric mode multiplies the number of columns // to reach the desired density, and then overlaps them @@ -126,9 +129,7 @@ export default async (canvas, config) => { minFilter: "linear", }); - const [msdfTexture, rainShader] = await Promise.all([msdfTexturePromise, rainShaderPromise]); - - const rainShaderModule = device.createShaderModule({ code: rainShader }); + const [msdfTexture, rainShaderModule, renderToCanvasShaderModule] = await Promise.all(assets); const rainComputePipeline = device.createComputePipeline({ compute: { @@ -163,7 +164,23 @@ export default async (canvas, config) => { }, }); - const computeBindGroup = device.createBindGroup({ + const renderToCanvasPipeline = device.createRenderPipeline({ + vertex: { + module: renderToCanvasShaderModule, + entryPoint: "vertMain", + }, + fragment: { + module: renderToCanvasShaderModule, + entryPoint: "fragMain", + targets: [ + { + format: presentationFormat, + }, + ], + }, + }); + + const rainComputeBindGroup = device.createBindGroup({ layout: rainComputePipeline.getBindGroupLayout(0), entries: [configBuffer, timeBuffer, cellsBuffer] .map((resource) => (resource instanceof GPUBuffer ? { buffer: resource } : resource)) @@ -173,7 +190,7 @@ export default async (canvas, config) => { })), }); - const renderBindGroup = device.createBindGroup({ + const rainRenderBindGroup = device.createBindGroup({ layout: rainRenderPipeline.getBindGroupLayout(0), entries: [configBuffer, timeBuffer, sceneBuffer, msdfSampler, msdfTexture.createView(), cellsBuffer] .map((resource) => (resource instanceof GPUBuffer ? { buffer: resource } : resource)) @@ -183,16 +200,27 @@ export default async (canvas, config) => { })), }); - const bundleEncoder = device.createRenderBundleEncoder({ - colorFormats: [presentationFormat], + const renderToCanvasBindGroup = device.createBindGroup({ + layout: renderToCanvasPipeline.getBindGroupLayout(0), + entries: [msdfSampler, msdfTexture.createView()] + .map((resource) => (resource instanceof GPUBuffer ? { buffer: resource } : resource)) + .map((resource, binding) => ({ + binding, + resource, + })), }); - bundleEncoder.setPipeline(rainRenderPipeline); - bundleEncoder.setBindGroup(0, renderBindGroup); - bundleEncoder.draw(numVerticesPerQuad * numQuads, 1, 0, 0); - const renderBundles = [bundleEncoder.finish()]; + const rainRenderPassConfig = { + colorAttachments: [ + { + view: canvasContext.getCurrentTexture().createView(), + loadValue: { r: 0, g: 0, b: 0, a: 1 }, + storeOp: "store", + }, + ], + }; - const renderPassConfig = { + const renderToCanvasPassConfig = { colorAttachments: [ { view: canvasContext.getCurrentTexture().createView(), @@ -220,16 +248,26 @@ export default async (canvas, config) => { const encoder = device.createCommandEncoder(); - const computePass = encoder.beginComputePass(); - computePass.setPipeline(rainComputePipeline); - computePass.setBindGroup(0, computeBindGroup); - computePass.dispatch(Math.ceil(gridSize[0] / 32), gridSize[1], 1); - computePass.endPass(); + const rainComputePass = encoder.beginComputePass(); + rainComputePass.setPipeline(rainComputePipeline); + rainComputePass.setBindGroup(0, rainComputeBindGroup); + rainComputePass.dispatch(Math.ceil(gridSize[0] / 32), gridSize[1], 1); + rainComputePass.endPass(); + + rainRenderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView(); + const rainRenderPass = encoder.beginRenderPass(rainRenderPassConfig); + rainRenderPass.setPipeline(rainRenderPipeline); + rainRenderPass.setBindGroup(0, rainRenderBindGroup); + rainRenderPass.draw(numVerticesPerQuad * numQuads, 1, 0, 0); + rainRenderPass.endPass(); + + // renderToCanvasPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView(); + // const renderToCanvasPass = encoder.beginRenderPass(renderToCanvasPassConfig); + // renderToCanvasPass.setPipeline(renderToCanvasPipeline); + // renderToCanvasPass.setBindGroup(0, renderToCanvasBindGroup); + // renderToCanvasPass.draw(numVerticesPerQuad, 1, 0, 0); + // renderToCanvasPass.endPass(); - renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView(); - const renderPass = encoder.beginRenderPass(renderPassConfig); - renderPass.executeBundles(renderBundles); - renderPass.endPass(); const commandBuffer = encoder.finish(); device.queue.submit([commandBuffer]); diff --git a/js/webgpu/utils.js b/js/webgpu/utils.js index 884c0df..eeffeb5 100644 --- a/js/webgpu/utils.js +++ b/js/webgpu/utils.js @@ -27,6 +27,12 @@ const loadTexture = async (device, url) => { return texture; }; +const loadShaderModule = async (device, url) => { + const response = await fetch(url); + const code = await response.text(); + return device.createShaderModule({ code }); +}; + const makeUniformBuffer = (device, structLayout, values = null) => { const buffer = device.createBuffer({ size: structLayout.size, @@ -40,4 +46,4 @@ const makeUniformBuffer = (device, structLayout, values = null) => { return buffer; }; -export { getCanvasSize, loadTexture, makeUniformBuffer }; +export { getCanvasSize, loadTexture, loadShaderModule, makeUniformBuffer }; diff --git a/shaders/wgsl/renderToCanvas.wgsl b/shaders/wgsl/renderToCanvas.wgsl new file mode 100644 index 0000000..263207a --- /dev/null +++ b/shaders/wgsl/renderToCanvas.wgsl @@ -0,0 +1,17 @@ +[[group(0), binding(0)]] var inputSampler : sampler; +[[group(0), binding(1)]] var inputTexture : texture_2d; + +struct VertOutput { + [[builtin(position)]] Position : vec4; + [[location(0)]] uv : vec2; +}; + +[[stage(vertex)]] fn vertMain([[builtin(vertex_index)]] index : u32) -> VertOutput { + var uv = vec2(f32(index % 2u), f32((index + 1u) % 6u / 3u)); + var position = vec4(uv * 2.0 - 1.0, 1.0, 1.0); + return VertOutput(position, uv); +} + +[[stage(fragment)]] fn fragMain(input : VertOutput) -> [[location(0)]] vec4 { + return textureSample(inputTexture, inputSampler, input.uv); +}