-
-
Save bacher/67fa38ee638423c304962ff087685b6e to your computer and use it in GitHub Desktop.
WebGPU Simple Triangle - write to storage buffer
This file contains 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 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 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; | |
let offset2 = 300 * 150 - offset; | |
s[offset] = pos.yx; | |
s[offset2] = 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 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