-
Notifications
You must be signed in to change notification settings - Fork 329
Description
Consider a Render Pipeline that includes two vertex buffers and three bind groups.
In WebGPU, vertex buffers are attached to the pass by calling void setVertexBuffers(u32 startSlot ...);
. This startSlot
parameter refers to the inputSlot
parameter inside WebGPUVertexInputDescriptor
.
In Metal, sequence<WebGPUVertexInputDescriptor>
corresponds to MTLVertexBufferLayoutDescriptorArray
, which is parallel to the buffers in the vertex shader's argument table. Therefore, a call to WebGPURenderPassEncoder.setVertexBuffers(x, {buf}, {0})
will result in a call to -[MTLRenderCommandEncoder setVertexBuffer:buf offset:0 atIndex:x]
. So far so good.
In WebGPU, bind groups are attached to the pass by calling void setBindGroup(u32 index, WebGPUBindGroup bindGroup, optional sequence<u32> dynamicOffsets);
. This index
parameter refers to the index into the sequence<WebGPUBindGroupLayout> bindGroupLayouts
inside the WebGPUPipelineLayoutDescriptor
.
In Metal, WebGPUBindGroup
corresponds to a MTLBuffer
that has been populated by using a MTLArgumentEncoder
. These get attached to the MTLRenderCommandEncoder
by setting values in that same vertex shader's argument table. Therefore, a call to WebGPUProgrammablePassEncoder.setBindGroup(y, bindGroup)
will result in a call to -[MTLRenderCommandEncoder setVertexBuffer:argumentBuffer offset:0 atIndex:y]
So, as you can see above, we have a collision. If a WebGPU programmer tries to use a vertex buffer at slot x
and a bind group at index x
, a naive Metal implementation would clobber one in favor of the other.
I think there are three possible solutions that we should choose among:
- Forbid WebGPU programmers from using the same vertex buffer slot IDs as bind group indices.
- Metal implementations can internally separate and tightly pack the vertex and argument buffers. Doing this is only possible if the runtime knows how many argument buffers there will be at the time
WebGPUProgrammablePassEncoder.setBindGroup()
is called (or vice versa), which requires WebGPU programmers to callWebGPUProgrammablePassEncoder.setPipeline()
before callingWebGPUProgrammablePassEncoder.setBindGroup()
. - Instead of tightly packing the buffers, we can add a hardcoded constant to the bind group indices. This causes the vertex argument table to be sparse, and requires WebGPU to place hard limits on the number of vertex buffers and bind groups that may ever occur together. If the limits are high, this sparseness could hurt performance-wise, but if they're small, it shouldn't be a problem.