Created
July 8, 2024 07:29
-
-
Save greggman/3c7b8482850252ec1d8b0b848393913f to your computer and use it in GitHub Desktop.
WebGPU: Points from mousemove
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
html, body { margin: 0; height: 100% } | |
canvas { width: 100%; height: 100%; display: block; } |
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
/* global GPUBufferUsage */ | |
/* global GPUTextureUsage */ | |
async function main() { | |
const adapter = await navigator.gpu?.requestAdapter(); | |
const device = await adapter?.requestDevice(); | |
if (!device) { | |
fail('need a browser that supports WebGPU'); | |
return; | |
} | |
const canvas = document.querySelector('canvas'); | |
const context = canvas.getContext('webgpu'); | |
const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); | |
context.configure({ | |
device, | |
format: presentationFormat, | |
alphaMode: 'premultiplied', | |
}); | |
const code = ` | |
struct MyVSInput { | |
@location(0) position: vec4f, | |
}; | |
@vertex | |
fn myVSMain(v: MyVSInput) -> @builtin(position) vec4f { | |
return v.position; | |
} | |
@fragment | |
fn myFSMain() -> @location(0) vec4f { | |
return vec4f(1, 0, 0, 1); | |
} | |
`; | |
const module = device.createShaderModule({code}); | |
const pipeline = device.createRenderPipeline({ | |
layout: 'auto', | |
vertex: { | |
module, | |
buffers: [ | |
{ | |
arrayStride: 2 * 4, | |
attributes: [ | |
{ shaderLocation: 0, offset: 0, format: 'float32x2' }, | |
], | |
}, | |
], | |
}, | |
fragment: { | |
module, | |
targets: [ | |
{format: presentationFormat}, | |
], | |
}, | |
primitive: { | |
topology: 'point-list', | |
}, | |
}); | |
const buffers = []; | |
const pointsPerBuffer = 128; // should make this much larger (16k) but keeping it small for testing. | |
let numPoints = 0; | |
const addPoint = (x, y) => { | |
const bufferNdx = numPoints / pointsPerBuffer | 0; | |
const ndx = numPoints % pointsPerBuffer; | |
if (ndx === 0) { | |
const buffer = device.createBuffer({ | |
size: pointsPerBuffer * 2 * 4, | |
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, | |
}); | |
buffers.push(buffer); | |
} | |
const buffer = buffers[bufferNdx]; | |
device.queue.writeBuffer(buffer, ndx * 2 * 4, new Float32Array([x, y])); | |
++numPoints; | |
}; | |
const renderPassDescriptor = { | |
colorAttachments: [ | |
{ | |
// view: undefined, // Assigned later | |
clearValue: [ 0.2, 0.2, 0.2, 1.0 ], | |
loadOp: 'clear', | |
storeOp: 'store', | |
}, | |
], | |
}; | |
function render() { | |
const canvasTexture = context.getCurrentTexture(); | |
renderPassDescriptor.colorAttachments[0].view = canvasTexture.createView(); | |
const encoder = device.createCommandEncoder(); | |
const pass = encoder.beginRenderPass(renderPassDescriptor); | |
pass.setPipeline(pipeline); | |
buffers.forEach((buffer, i) => { | |
pass.setVertexBuffer(0, buffer); | |
const base = i * pointsPerBuffer; | |
const numToDraw = Math.min(numPoints - base, pointsPerBuffer); | |
pass.draw(numToDraw); | |
}) | |
pass.end(); | |
device.queue.submit([encoder.finish()]); | |
requestAnimationFrame(render); | |
} | |
requestAnimationFrame(render); | |
window.addEventListener('mousemove', e => { | |
const rect = canvas.getBoundingClientRect(); | |
const x = (e.clientX - rect.left) / rect.width * 2 - 1; | |
const y = (e.clientY - rect.top) / rect.height * -2 + 1; | |
addPoint(x, y); | |
}) | |
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)); | |
} | |
}); | |
observer.observe(canvas); | |
} | |
function fail(msg) { | |
const elem = document.querySelector('#fail'); | |
elem.style.display = ''; | |
elem.children[0].textContent = 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: Points from mousemove ","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