Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active February 8, 2024 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greggman/2bd2907d602aced28e0a6ca9d0d20eed to your computer and use it in GitHub Desktop.
Save greggman/2bd2907d602aced28e0a6ca9d0d20eed to your computer and use it in GitHub Desktop.
WebGPU Test - Mess up indirect draw by updating indirect buffers

WebGPU Test - Mess up indirect draw by updating indirect buffers

view on jsgist

@import url(https://webgpufundamentals.org/webgpu/resources/webgpu-lesson.css);
<p>This doesn't work because WebGPU complains you can't read/write in same scope</p>
<canvas></canvas>
// 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: 'draw indirect',
code: `
struct Vertex {
@location(0) pos: vec4f,
@location(1) color: vec4f,
};
struct VOut {
@builtin(position) pos: vec4f,
@location(0) color: vec4f,
};
@vertex fn vs(vin: Vertex, @builtin(vertex_index) vNdx: u32, @builtin(instance_index) iNdx: u32) -> VOut {
let corner = array(
vec4f(-1 , 1 , 0, 1),
vec4f(-1 , 0.5, 0, 1),
vec4f(-0.5, 1 , 0, 1)
);
var vout: VOut;
let c = corner[vNdx % 3];
let pos = select(vin.pos, c, iNdx > 0);
let color = select(vin.color, vec4f(1), iNdx > 0);
vout.pos = pos;
vout.color = color;
return vout;
}
@group(0) @binding(0) var<storage, read> u: array<u32, 5>;
@group(0) @binding(1) var<storage, read_write> s: array<u32, 5>;
@fragment fn fs(vout: VOut) -> @location(0) vec4f {
let index = i32(vout.pos.x) + 5 * i32(vout.pos.y);
s[index] = u[index];
return vout.color;
}
`,
});
const pipeline = device.createRenderPipeline({
label: 'set indirect pipeline',
layout: 'auto',
vertex: {
module,
buffers: [
{
arrayStride: 6 * 4,
attributes: [
{shaderLocation: 0, offset: 0, format: 'float32x2'},
{shaderLocation: 1, offset: 8, format: 'float32x4'},
],
},
],
},
fragment: {
module,
targets: [{ format: presentationFormat }],
},
});
const bufferSets = [6, 12, 8, 2].map(triangleNdx => {
const srcBuffer = device.createBuffer({
size: 5 * 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const storageIndirectBuffer = device.createBuffer({
size: 5 * 4,
usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.STORAGE,
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: srcBuffer } },
{ binding: 1, resource: { buffer: storageIndirectBuffer } },
],
});
return { srcBuffer, storageIndirectBuffer, bindGroup, triangleNdx };
});
const renderPassDescriptor = {
label: 'our basic canvas renderPass',
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
loadOp: 'load',
storeOp: 'store',
},
],
};
const encoder = device.createCommandEncoder({ label: 'our encoder' });
const across = 4;
const down = 4;
const vertices = [];
{
const bitsToValue = (a, b) => ((a ? 2 : 0) + (b ? 1 : 0)) / 3;
const addVert = (...args) => {
//console.log(...args);
vertices.push(...args);
};
const width = 2 / across;
const height = 2 / down;
for (let y = 0; y < down; ++y) {;
const yy = y / down * 2 - 1;
for (let x = 0; x < across; ++x) {
const xx = x / across * 2 - 1
const c = (y * across + x) + 1;
const color = [
bitsToValue(c & 1, c & 8),
bitsToValue(c & 2, c & 16),
bitsToValue(c & 4, c & 32),
1,
];
addVert(xx, yy, ...color);
addVert(xx + width, yy, ...color);
addVert(xx, yy + height, ...color);
}
}
}
const vertexBuffer = device.createBuffer({
size: vertices.length * 4,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
});
device.queue.writeBuffer(vertexBuffer, 0, new Float32Array(vertices));
const firstIndirectBuffer = device.createBuffer({
size: 5 * 4,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDIRECT,
});
const ind = new Uint32Array([3, 2, 15, 0, 0]);
console.log(ind);
device.queue.writeBuffer(firstIndirectBuffer, 0, ind);
const pass = encoder.beginRenderPass(renderPassDescriptor);
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
// make a render pass encoder to encode render specific commands
let indirectBuffer = firstIndirectBuffer;
bufferSets.forEach(({ srcBuffer, storageIndirectBuffer, bindGroup, triangleNdx }, i) => {
const ind = new Uint32Array([3, 2, triangleNdx * 3, 0, 0]);
console.log(`#${i + 1}:`, ind);
device.queue.writeBuffer(srcBuffer, 0, ind);
pass.setBindGroup(0, bindGroup);
pass.drawIndirect(indirectBuffer, 0);
indirectBuffer = storageIndirectBuffer;
});
pass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
}
function fail(msg) {
// eslint-disable-next-line no-alert
alert(msg);
}
main();
{"name":"WebGPU Test - Mess up indirect draw by updating indirect buffers","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