Last active
May 7, 2024 01:22
-
-
Save greggman/295e38eeedf5957ac50179308666d98b to your computer and use it in GitHub Desktop.
WebGPU write to canvas texture in compute shader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url(https://webgpufundamentals.org/webgpu/resources/webgpu-lesson.css); | |
html, body { | |
margin: 0; /* remove the default margin */ | |
height: 100%; /* make the html,body fill the page */ | |
} | |
canvas { | |
display: block; /* make the canvas act like a block */ | |
width: 100%; /* make the canvas fill its container */ | |
height: 100%; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<canvas></canvas> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function main() { | |
const adapter = await navigator.gpu?.requestAdapter(); | |
if (!adapter) { | |
fail('need a browser that supports WebGPU'); | |
} | |
// bgra8unorm as a storage texture is an optional feature so | |
// if it's supported then we don't care if presentationFormat is | |
// bgra8unorm or rgba8unorm but if the feature does not exist | |
// then we must use rgba8unorm | |
let presentationFormat = adapter.features.has('bgra8unorm-storage') | |
? navigator.gpu.getPreferredCanvasFormat() | |
: 'rgba8unorm'; | |
const device = await adapter?.requestDevice({ | |
requiredFeatures: presentationFormat === 'bgra8unorm' | |
? ['bgra8unorm-storage'] | |
: [], | |
}); | |
if (!device) { | |
return; | |
} | |
// Get a WebGPU context from the canvas and configure it | |
const canvas = document.querySelector('canvas'); | |
const context = canvas.getContext('webgpu'); | |
context.configure({ | |
device, | |
format: presentationFormat, | |
// This is what's required to be able to write to a texture from a compute shader | |
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING, | |
}); | |
const module = device.createShaderModule({ | |
code: ` | |
@group(0) @binding(0) var tex: texture_storage_2d<${presentationFormat}, write>; | |
@compute @workgroup_size(1) fn cs( | |
@builtin(global_invocation_id) id: vec3u | |
) { | |
let color = vec4f(fract(vec2f(id.xy) / 32.0), 0, 1); | |
textureStore(tex, id.xy, color); | |
} | |
`, | |
}); | |
const pipeline = device.createComputePipeline({ | |
label: 'checkboard pipeline', | |
layout: 'auto', | |
compute: { | |
module, | |
entryPoint: 'cs', | |
}, | |
}); | |
function render() { | |
// Get the current texture from the canvas context | |
const canvasTexture = context.getCurrentTexture(); | |
const bindGroup = device.createBindGroup({ | |
layout: pipeline.getBindGroupLayout(0), | |
entries: [ | |
{ binding: 0, resource: canvasTexture.createView() }, | |
], | |
}); | |
const encoder = device.createCommandEncoder({ label: 'our encoder' }); | |
const pass = encoder.beginComputePass(); | |
pass.setPipeline(pipeline); | |
pass.setBindGroup(0, bindGroup); | |
pass.dispatchWorkgroups(canvasTexture.width, canvasTexture.height); | |
pass.end(); | |
const commandBuffer = encoder.finish(); | |
device.queue.submit([commandBuffer]); | |
} | |
const observer = new ResizeObserver(entries => { | |
for (const entry of entries) { | |
const canvas = entry.target; | |
const width = entry.contentBoxSize[0].inlineSize; | |
const height = entry.contentBoxSize[0].blockSize; | |
canvas.width = Math.max(1, Math.min(width, device.limits.maxTextureDimension2D)); | |
canvas.height = Math.max(1, Math.min(height, device.limits.maxTextureDimension2D)); | |
// re-render | |
render(); | |
} | |
}); | |
observer.observe(canvas); | |
} | |
function fail(msg) { | |
// eslint-disable-next-line no-alert | |
alert(msg); | |
} | |
main(); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"name":"WebGPU write to canvas texture in compute shader","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment