Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best practice for per-draw uniform data #733

Closed
math4tots opened this issue Jun 18, 2020 · 2 comments
Closed

Best practice for per-draw uniform data #733

math4tots opened this issue Jun 18, 2020 · 2 comments
Labels
type: question Further information is requested

Comments

@math4tots
Copy link

math4tots commented Jun 18, 2020

I'm afraid this may be a noobie question given how common this use case must be, but for the same reason I figured it would be an easy question for some wgpu gurus.

What's the best practice for updating per-draw uniform data? In particular, I really just want to pass a 4x4 transformation matrix for each draw call.

Some possibilities:

  1. Tutorial here suggests that using a staging buffer is the way to go, but this path involves creating a new staging buffer for each draw, which seems kind of expensive. But if a second person were to tell me that this is the way to go, I'd be happy to accept this for the time being.
  2. Push constants: I think this would be exactly what I'd want if it were available, but thread here seems to suggest it might not be available anytime soon...
  3. The same thread above suggests dynamic offsets as a workaround. I might not be understanding this correctly, but it's not clear to me how changing offsets into the buffer will allow me to pass in arbitrary 4x4 matrices to my shaders.
  4. I've also come across this which compares push-constants with mapped buffers. I would guess that this would be like using the Buffer::map_read/map_write methods? I was kind of hoping to avoid them if I could for now, because of the situation with event loops described in the docs, but if someone tells me that that's the recommended way to do this, I might try it anyway.

Are any of the above possibilities the recommended way to update per-draw data with wgpu today?

Are there other better ways to update per-draw uniform data?

@math4tots math4tots added the type: question Further information is requested label Jun 18, 2020
@kvark
Copy link
Member

kvark commented Jun 18, 2020

That's a great question! It comes up regularly, and there is a reason for it - WebGPU doesn't offer a solution that would be obvious to use. The closest thing you can get to an obvious solution is push constants. I filed #734 for us to try implementing them as a native extension, but this wouldn't be portable to the web.

Here is how you'd do it in a portable way. First important fact: you can't do any transfers in the middle of a render pass. Transfers are done on command encoder outside of a render pass. Therefore, each separate object/entity that you draw in a render pass needs to use a separate area in a GPU buffer somewhere (if it needs its own matrix). That means, all your data has to be ready by the time a render pass starts executing (but not necessarily recording!). Here is some pseudo-code:

fn init() {
  let uniform_buffer = device.create_buffer(size = <big enough for a frame>).
  // this will allow us to specify the offsets at binding time
  let uniform_bind_group = device.create_bind_group(uniform_buffer, dynamic_offset = true).
}
fn render(queue: &wgpu::Queue) {
  let encoder = device.create_command_encoder();
  let mut offset = 0;
  let mut pass = encoder.begin_render_pass();
  for entity in world.entities {
    // this is an operation on the queue, it will be scheduled before anything in this render pass
    queue.write_buffer(&self.uniform_buffer, offset, entity.matrix, size_of::<Matrix>());
    // here comes handy the dynamic offset
    pass.set_bind_group(1, &self.uniform_bind_group, &[offset]);
    // we don't want to overwrite any existing data this frame
    offset += size_of::<Matrix>();
  }
  // our draw commands are scheduled after the updates
  queue.submit(iter::once(encoder.finish()));
}

@kvark
Copy link
Member

kvark commented Jul 1, 2020

Also worth noting that we landed an experimental staging belt in wgpu-rs, as an alternative to write_buffer.
Closing this. Please free to re-open and/or continue discussion!

@kvark kvark closed this as completed Jul 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants