Created
June 28, 2023 02:57
-
-
Save greggman/22fd7c51223918bf2af49f3e311395c0 to your computer and use it in GitHub Desktop.
WebGPU Simple Triangle - write to storage buffer
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); |
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
// WebGPU Simple Triangle | |
// from https://webgpufundamentals.org/webgpu/webgpu-simple-triangle.html | |
async function main() { | |
const adapter = await navigator.gpu?.requestAdapter(); | |
const device = await adapter?.requestDevice(); | |
if (!device) { | |
fail('need a browser that supports WebGPU'); | |
return; | |
} | |
// Get a WebGPU context from the canvas and configure it | |
const canvas = document.querySelector('canvas'); | |
const context = canvas.getContext('webgpu'); | |
const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); | |
context.configure({ | |
device, | |
format: presentationFormat, | |
}); | |
const module = device.createShaderModule({ | |
label: 'our hardcoded red triangle shaders', | |
code: ` | |
@vertex fn vs( | |
@builtin(vertex_index) vertexIndex : u32 | |
) -> @builtin(position) vec4f { | |
let pos = array( | |
vec2f( 0.0, 0.5), // top center | |
vec2f(-0.5, -0.5), // bottom left | |
vec2f( 0.5, -0.5) // bottom right | |
); | |
return vec4f(pos[vertexIndex], 0.0, 1.0); | |
} | |
@group(0) @binding(0) var<storage, read_write> s: array<vec2f>; | |
@fragment fn fs(@builtin(position) pos: vec4f) -> @location(0) vec4f { | |
let offset = i32(pos.x) + i32(pos.y) * 300; | |
s[offset] = pos.xy; | |
return vec4f(1, 0, 0, 1); | |
} | |
`, | |
}); | |
const pipeline = device.createRenderPipeline({ | |
label: 'our hardcoded red triangle pipeline', | |
layout: 'auto', | |
vertex: { | |
module, | |
entryPoint: 'vs', | |
}, | |
fragment: { | |
module, | |
entryPoint: 'fs', | |
targets: [{ format: presentationFormat }], | |
}, | |
}); | |
const storageBuffer = device.createBuffer({ | |
size: 300 * 150 * 4 * 2, | |
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE, | |
}); | |
const bindGroup = device.createBindGroup({ | |
layout: pipeline.getBindGroupLayout(0), | |
entries: [ | |
{ binding: 0, resource: { buffer: storageBuffer } }, | |
], | |
}); | |
const resultBuffer = device.createBuffer({ | |
size: 300 * 150 * 4 * 2, | |
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, | |
}); | |
const renderPassDescriptor = { | |
label: 'our basic canvas renderPass', | |
colorAttachments: [ | |
{ | |
// view: <- to be filled out when we render | |
clearValue: [0.3, 0.3, 0.3, 1], | |
loadOp: 'clear', | |
storeOp: 'store', | |
}, | |
], | |
}; | |
async function render() { | |
// Get the current texture from the canvas context and | |
// set it as the texture to render to. | |
renderPassDescriptor.colorAttachments[0].view = | |
context.getCurrentTexture().createView(); | |
// make a command encoder to start encoding commands | |
const encoder = device.createCommandEncoder({ label: 'our encoder' }); | |
// make a render pass encoder to encode render specific commands | |
const pass = encoder.beginRenderPass(renderPassDescriptor); | |
pass.setPipeline(pipeline); | |
pass.setBindGroup(0, bindGroup); | |
pass.draw(3); // call our vertex shader 3 times. | |
pass.end(); | |
encoder.copyBufferToBuffer(storageBuffer, 0, resultBuffer, 0, storageBuffer.size); | |
const commandBuffer = encoder.finish(); | |
device.queue.submit([commandBuffer]); | |
await resultBuffer.mapAsync(GPUMapMode.READ); | |
const results = new Float32Array(resultBuffer.getMappedRange()); | |
// show the results in a 2D canvas | |
const ctx = document.createElement('canvas').getContext('2d'); | |
document.body.appendChild(ctx.canvas); | |
const rgb = (r, g, b) => `rgb(${r}, ${g}, ${b})`; | |
for (let y = 0; y < 150; ++y) { | |
for (let x = 0; x < 300; ++x) { | |
const off = (y * 300 + x) * 2; | |
ctx.fillStyle = rgb(results[off], results[off + 1], 0); | |
ctx.fillRect(x, y, 1, 1); | |
} | |
} | |
} | |
render(); | |
} | |
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 Simple Triangle - write to storage buffer","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