I am trying to grasp the difference between uniform buffers created with create_buffer_with_data() and those created by copying the contents of a staging buffer?
I assume that those created with create_buffer_with_data() are placed in host-visible memory. _is it true?_
According to vulkan-tutorial.com, shader access to a GPU-only buffer is faster but setting it up is more complicated and possibly longer due to the need to setup a staging buffer and copy data from it to the final buffer. So for buffers that need to be updated every frame, it is probably not worth the effort. _Is it true?_
Besides, when using this method, one should probably foresee multiple instances of the buffer (one per swapchain image?) to avoid changing the data in while it is being used to draw another frame.
So I am tempted to conclude that the best approach for a uniform that needs to be updated at every frame is to create a new buffer and binding group for each frame with create_buffer_with_data(). When the frame is complete the buffer is dropped and the memory released. _Is it correct?_
Thanks beforehand for your help!
Hi! These are all great questions!
The story of data transfers in WebGPU is evolving, and the desired semantics is slightly different from what we currently implement. The desired behavior for CreateBufferMapped, which is what we use for create_buffer_with_data, is to have the staging (host-visible) area allocated only once. The buffer itself, unless you specified any MAP_READ or MAP_WRITE usage, goes into the private GPU memory. Once the mapping is over, we copy the staging to it.
What we are currently doing is just allocating the buffer in CPU-visible memory, which isn't great for sure! This needs to be fixed, and it will be, hopefully soon ;)
So for buffers that need to be updated every frame, it is probably not worth the effort. Is it true?
It depends on how many times a buffer is used by the GPU. Say, if it's a vertex buffer used for a single draw call, then you aren't going to save anything by moving it to GPU private memory. If, on the other hand, it's a uniform buffer that is read by each invocation of a pixel shader, then it's worth having in private memory.
Besides, when using this method, one should probably foresee multiple instances of the buffer (one per swapchain image?) to avoid changing the data in while it is being used to draw another frame.
If you copy over the contents from some staging buffers into the GPU buffer each frame, there is no need to have multiple instances of that GPU buffer.
create a new buffer and binding group for each frame with create_buffer_with_data()
That's not convenient, and not the best. Just keep a single GPU buffer with the bind group. Whenever you need to update, create some buffer with data and issue a copy_buffer_to_buffer into your buffer.
Thanks a lot for your clear answers! I learned something important today :-)
Most helpful comment
Hi! These are all great questions!
The story of data transfers in WebGPU is evolving, and the desired semantics is slightly different from what we currently implement. The desired behavior for CreateBufferMapped, which is what we use for
create_buffer_with_data, is to have the staging (host-visible) area allocated only once. The buffer itself, unless you specified any MAP_READ or MAP_WRITE usage, goes into the private GPU memory. Once the mapping is over, we copy the staging to it.What we are currently doing is just allocating the buffer in CPU-visible memory, which isn't great for sure! This needs to be fixed, and it will be, hopefully soon ;)
It depends on how many times a buffer is used by the GPU. Say, if it's a vertex buffer used for a single draw call, then you aren't going to save anything by moving it to GPU private memory. If, on the other hand, it's a uniform buffer that is read by each invocation of a pixel shader, then it's worth having in private memory.
If you copy over the contents from some staging buffers into the GPU buffer each frame, there is no need to have multiple instances of that GPU buffer.
That's not convenient, and not the best. Just keep a single GPU buffer with the bind group. Whenever you need to update, create some buffer with data and issue a
copy_buffer_to_bufferinto your buffer.