Animated Objects Best Practice and Buffer (Re-)Alloc

14 views
Skip to first unread message

Jean-Colas Prunier

unread,
Jan 27, 2023, 6:04:57 AM1/27/23
to Dawn Graphics
Continuing doing tests with Dawn. It's really enjoyable working with this API. Thanks for the great work again. There are only so many samples, but I found them super useful as well as digging into the tests to search for how you guys intend to use certain functions.

I had a couple of questions regarding animated meshes and allocating buffers (vertex/index).

1) Regarding animation, what's the best practice or the one you recommend? Say you have animated other 10 "frames". So far, I have packed all data for all 10 frames for all objects into a vertex buffer. And then, in the render loop, I do something like this:

```
// data in the Vertex Buffer are packed as follow
// - all vertex data for all objects are packed in a block
// - there are as many blocks as they are frames 
 pass.SetVertexBuffer(0, vertexBuffer, numVertices * sizeof(Vertex) * frame);
 uint32_t offset = 0;
 for (const auto& mesh : meshes) {
     pass.SetIndexBuffer(index_buffer, wgpu::IndexFormat::Uint32, 0);
     pass.DrawIndexed(mesh.numTriangles * 3, 1, offset);
     offset += mesh.numTriangles * 3;
  }
```
I use the offset in SetVertexBuffer to indicate where the vertex data are for the current frame, and then, for now, I am doing as many draw calls as there are objects in the scene (this version doesn't use the bundle option).

I just wanted to know if this is how you would be doing it or if you have a better way to suggest. Also, I haven't looked into the tests yet to find a possible answer to what the `slot` parameter does in:

```
void SetVertexBuffer(uint32_t slot, Buffer const& buffer, uint64_t offset = 0, uint64_t size = WGPU_WHOLE_SIZE) const;
```

How can it be used?

2) I am looking into dynamic scenes, where the number of objects can change from time to time quite abruptly. Some objects would stay, but some objects would disappear, and new ones would be added. Is there a "recommended" way for updating an existing buffer size like realloc or should I destroy the current buffer, reallocate a new one at the current size and then populate it with the updated data? Would you recommend a large buffer or potentially several smaller buffers? I will benchmark all these options, but I was curious whether you could share your views and experience.

Many thanks again. I will write a series of intro docs on how to get started with Dawn asap.

Cheers, and keep up the excellent work.

Kai Ninomiya

unread,
Jan 27, 2023, 7:28:29 PM1/27/23
to Jean-Colas Prunier, Dawn Graphics

-Kai (he/they)


On Fri, Jan 27, 2023 at 3:04 AM Jean-Colas Prunier <j...@jean-colas.com> wrote:
Continuing doing tests with Dawn. It's really enjoyable working with this API. Thanks for the great work again. There are only so many samples, but I found them super useful as well as digging into the tests to search for how you guys intend to use certain functions.

I had a couple of questions regarding animated meshes and allocating buffers (vertex/index).

1) Regarding animation, what's the best practice or the one you recommend? Say you have animated other 10 "frames". So far, I have packed all data for all 10 frames for all objects into a vertex buffer. And then, in the render loop, I do something like this:

```
// data in the Vertex Buffer are packed as follow
// - all vertex data for all objects are packed in a block
// - there are as many blocks as they are frames 
 pass.SetVertexBuffer(0, vertexBuffer, numVertices * sizeof(Vertex) * frame);
 uint32_t offset = 0;
 for (const auto& mesh : meshes) {
     pass.SetIndexBuffer(index_buffer, wgpu::IndexFormat::Uint32, 0);
     pass.DrawIndexed(mesh.numTriangles * 3, 1, offset);
     offset += mesh.numTriangles * 3;
  }
```
I use the offset in SetVertexBuffer to indicate where the vertex data are for the current frame, and then, for now, I am doing as many draw calls as there are objects in the scene (this version doesn't use the bundle option).

I just wanted to know if this is how you would be doing it or if you have a better way to suggest.
I think this is a good approach. 
 
Also, I haven't looked into the tests yet to find a possible answer to what the `slot` parameter does in:

```
void SetVertexBuffer(uint32_t slot, Buffer const& buffer, uint64_t offset = 0, uint64_t size = WGPU_WHOLE_SIZE) const;
```

How can it be used?
A render pipeline can use multiple vertex buffers, by passing multiple vertex buffer layouts here: https://gpuweb.github.io/gpuweb/#dom-gpuvertexstate-buffers
How this function is roughly described in the spec:

vertex buffer is, conceptually, a view into buffer memory as an array of structuresarrayStride is the stride, in bytes, between elements of that array. Each element of a vertex buffer is like a structure with a memory layout defined by its attributes, which describe the members of the structure.

Each GPUVertexAttribute describes its format and its offset, in bytes, within the structure.

Each attribute appears as a separate input in a vertex shader, each bound by a numeric location, which is specified by shaderLocation. Every location must be unique within the GPUVertexState.

2) I am looking into dynamic scenes, where the number of objects can change from time to time quite abruptly. Some objects would stay, but some objects would disappear, and new ones would be added. Is there a "recommended" way for updating an existing buffer size like realloc or should I destroy the current buffer, reallocate a new one at the current size and then populate it with the updated data? Would you recommend a large buffer or potentially several smaller buffers? I will benchmark all these options, but I was curious whether you could share your views and experience.
There's no way to resize or reallocate a buffer - this is a common design of post-OpenGL graphics APIs, done so that the underlying implementations of other objects like BindGroups and CommandBuffers don't have to be spookily reinitialized at a distance if they bake in a pointer to the original allocation.
The best technique here very much depends on your workload, but generally I'd avoid having buffers whose size depends on anything that dynamic. For example for culling geometry that isn't visible, you should cull whole draw calls, not parts of vertex buffers. If you can't avoid it, better to have a little wasted space at the end of a buffer than to reallocate down to a smaller one.

I'll note in the general case allocations shouldn't be too expensive, because Dawn generally actually suballocates memory out of larger allocations rather than making an allocation for every GPUBuffer. However over time, doing too many allocations may create fragmentation/wasted memory in those larger allocations, and of course will cause more of those larger allocations to hit the GPU.

Many thanks again. I will write a series of intro docs on how to get started with Dawn asap.

Cheers, and keep up the excellent work.

--
You received this message because you are subscribed to the Google Groups "Dawn Graphics" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dawn-graphic...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dawn-graphics/79c1c2d3-840e-47a4-ab67-a5976ea396cdn%40googlegroups.com.

Jean-Colas Prunier

unread,
Jan 30, 2023, 5:23:30 AM1/30/23
to Dawn Graphics

Thanks a lot Kai. Very useful!
Reply all
Reply to author
Forward
0 new messages