Reputation: 226
I am trying to organize draw call for my scene asset with many materials and meshes. The strategy I thought:
However, there are so many different meshes with different dynamic transformations
, thus they are not able to be organize into one instance draw call. Instead, each mesh with different dynamic transformation need a draw call, resuling into a number of draw calls.
dynamic transformation
: the translation, rotation and scale of a mesh are updated per frameIf I use a storage buffer to save each mesh's transformation and use the unique object index
of mesh to index into the storage buffer, getting corresponding transformations. But the object index can not be instanced, because the meshes with different dynamic transformations can not be instanced. Therefore, an additional vertex attribute is required for each vertex for implementing the method. At the same time, it will occupy some memeory usage for redundancy primitive index data, because the primitive index of one mesh is same.
object index
: an index assigned to a mesh, which can be used to index into storage buffer for getting the transformation of the mesh.the reason why object index should be per vertex
: each mesh only need one object index, but I can not pass the mesh's object index per mesh/instance. Because I want to group meshes with different transformations into one draw call and these different meshes can not be instanced in one draw call, thus the object index data can not be passed by instance data.Is it worthy to implement this method? Or is there a more practical way to minimize the count of draw call?
Upvotes: 1
Views: 1651
Reputation: 474386
So you have a number of objects you want to draw. Some of those objects use different meshes from different objects, but sometimes they share the same underlying mesh. But regardless, a particular collection of objects all share the same vertex format and source buffers (VAO), as well as the same shader logic and shader resources.
But these objects do have different, per-object information. This is what distinguishes each object within such a collection. You cite transform information, but really, it could be all kinds of per-object state (textures, etc).
And your goal is to draw all of the objects in a collection with a single draw call. So you need a way to distinguish between different objects in the shader, as well as a way to use those distinct.
What you ultimately need is to give each object an index and provide an array to the shader that the shader will use to fetch that index and get its per-object information. All vertices for a particular object need to get the same index, and if two different objects draw from the same mesh, the shaders for those two objects need different indices.
This is what multi-draw and gl_DrawID
are for. gl_DrawID
is core in GL 4.6, but it was available as the extension ARB_shader_draw_parameters
for some time before being adopted into core. It's pretty widely available.
Multi-draw rendering commands allow you to issue multiple draw calls with a single function call. These draw calls all have to use the same primitive type, but you already had that limitation before, so that's no problem. Each draw in the array of drawing commands is executed in order, first to last.
As such, each draw has an index, from 0 to the number of draws. You can access this index in a vertex shader via the gl_DrawID
predefined input. You can therefore use this index to fetch from your per-object SSBO array(s) to get whatever per-object information you want.
Plus, gl_DrawID
, and any subsequent derivatives thereof, is always a dynamically uniform expression. As such, if you have an array of SSBOs (not an array within an SSBO; an array of SSBOs), array of sampler uniforms, or an array of bindless textures, you can use gl_DrawID
or some (dynamically uniform) expression based on it to access such arrays. So you can switch textures with this index.
So what happens to instancing in this process? While multi-draw indirect does in fact allow each draw to have its own instance count and base instance, instancing makes accessing per-object information much more complicated. Each draw index originally only used one piece of per-object information, so every draw command could just directly use its own index. But if a draw command can consume multiple pieces of per-object data, then each draw command has to know how many instances were consumed by previous draw commands.
That's not too difficult to do, actually, and shader_draw_parameters contains the solution: gl_BaseInstance
. A multi-draw indirect command has both an instance count and a base instance value. The instance count maps directly to the gl_InstanceID
field, but the base instance value is not added to this. Instead, you can access it directly with gl_BaseInstance
.
You can calculate a particular index by taking advantage of this. The base instance value for each draw in the multi-draw command needs to be the sum total of all instance counts of prior drawing commands (including the ones that only draw 1 instance). Therefore, your per-object index is no longer gl_DrawID
; it is gl_BaseInstance + gl_InstanceID
.
However, if you do this, you lose the advantages of gl_DrawID
being dynamically uniform. gl_InstanceID
and gl_BaseInstance
are not required to be dynamically uniform, so you're unable to use them to index arrays of SSBOs or samplers or even bindless textures. So if you want to switch textures based on the draw call, you're limited to using array textures.
Upvotes: 5