Last active
February 8, 2024 22:09
-
-
Save greggman/2bd2907d602aced28e0a6ca9d0d20eed to your computer and use it in GitHub Desktop.
WebGPU Test - Mess up indirect draw by updating indirect buffers
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
<p>This doesn't work because WebGPU complains you can't read/write in same scope</p> | |
<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: '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(); | |
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 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