In project I'm working on I ran into a case that TypeGPU api doesn't natively support. I'm mostly sharing this out of curiosity and in case it's useful, the unwrap escape hatch is great and everything works.
1. GPUBuffer
I need to use a raw GPUBuffer as a vertex source to get sub-buffer writes at specified byte offsets, like this:
device.queue.writeBuffer(page, alloc.offset, someVertexData);
if I understand correctly, TypeGPU write() replaces the entire buffer, and for my case it would possibly have support something like
buffer.writeAt(offset,data). Alternatively, a way to use or wrap GPUBuffer so it can be accessed directly without unwrapping
2. Binding sub region of a buffer - draw indexed with baseVertex and firstInstance
My buffers hold many objects' geometry packed together, but I need each draw call to read only one objects vertices and indices - not the whole buffer.
setIndexBuffer has built-in offset and size params:
pass.setIndexBuffer(
fullBuffer,
"uint16",
indexOffset,
indexSize,
);
For vertex buffer I have to set the entire buffer in the pass:
pass.setVertexBuffer(0, fullBuffer);
but then I can use drawIndexed like this:
pass.drawIndexed(
draw.indexCount, // 36 indices for a cube
1, // 1 instance
0, // firstIndex
draw.baseVertex, // added to every index before vertex fetch acts as an offset
draw.modelSlot, // firstInstance - becomes instance_index in shader
);
Now when the GPU processes index 0, it actually fetches vertex 0 + 24 = 24. Index 1 fetches vertex 25. And so on. The indices stay [0,..,23] but they're shifted to read from the right region of the buffer.
This is the other half of why I have to unwrap. TypeGPU's draw abstractions don't expose baseVertex or firstInstance.
My draw code currently looks like this:
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
clearValue: [0.04, 0.04, 0.07, 1],
loadOp: "clear" as const,
storeOp: "store" as const,
},
],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1,
depthLoadOp: "clear" as const,
depthStoreOp: "store" as const,
},
});
const rawPipeline = root.unwrap(renderPipeline);
pass.setPipeline(rawPipeline);
pass.setBindGroup(0, rawCameraBindGroup);
pass.setBindGroup(1, rawModelsBindGroup);
for (const [vertPage, draws] of drawsByPage) {
pass.setVertexBuffer(0, vertexPool.getBuffer(vertPage));
for (const draw of draws) {
pass.setIndexBuffer(
indexPool.getBuffer(draw.idxPage),
"uint16",
draw.idxOffset,
draw.idxSize,
);
pass.drawIndexed(draw.indexCount, 1, 0, draw.baseVertex, draw.modelSlot);
}
}
pass.end();
device.queue.submit([encoder.finish()]);
In project I'm working on I ran into a case that TypeGPU api doesn't natively support. I'm mostly sharing this out of curiosity and in case it's useful, the
unwrapescape hatch is great and everything works.1. GPUBuffer
I need to use a raw
GPUBufferas a vertex source to get sub-buffer writes at specified byte offsets, like this:if I understand correctly, TypeGPU
write()replaces the entire buffer, and for my case it would possibly have support something likebuffer.writeAt(offset,data). Alternatively, a way to use or wrap GPUBuffer so it can be accessed directly without unwrapping2. Binding sub region of a buffer - draw indexed with baseVertex and firstInstance
My buffers hold many objects' geometry packed together, but I need each draw call to read only one objects vertices and indices - not the whole buffer.
setIndexBufferhas built-in offset and size params:For vertex buffer I have to set the entire buffer in the pass:
but then I can use
drawIndexedlike this:Now when the GPU processes index 0, it actually fetches vertex 0 + 24 = 24. Index 1 fetches vertex 25. And so on. The indices stay [0,..,23] but they're shifted to read from the right region of the buffer.
This is the other half of why I have to unwrap. TypeGPU's draw abstractions don't expose baseVertex or firstInstance.
My draw code currently looks like this: