-
Notifications
You must be signed in to change notification settings - Fork 18
Merging and Batching Draw Calls
A draw call is when Tetra3D sends triangle data to the GPU to render. This happens for each renderable portion (or MeshPart
) in a Mesh. Meshes generally are split apart into MeshParts according to material slots. If a single material slot is used across an entire Mesh, then all its triangles will belong to a single MeshPart, and so will render in a single draw call (which is good, because it's much faster to batch many triangles together into fewer draw calls than to send fewer triangles to the GPU in more draw calls).
Depth testing / intersection has been implemented, but only works between draw calls. However, by using multiple materials on an individual model, you can make it render in multiple draw calls, thereby allowing depth testing between portions of the model, if necessary.
You can lower draw calls by statically merging Models together using Model.Merge()
. When statically merging models together, the merged Models' mesh data gets combined with the calling Model's mesh. This means that you wouldn't be able to move or otherwise alter models after static merging.
When merging Models together using Model.Merge()
, the receiving Model will reuse its existing MeshParts to merge any passed Models' MeshParts as necessary if:
- a MeshPart with the needed Material already exists, and
- Merging another MeshPart into it won't exceed the triangle limit (21845) / vertex limit (65535).
Otherwise, it will make a new MeshPart as necessary, which means another draw call. In other words, it will be as efficient as possible, and you won't generally have to worry about the results after calling Model.Merge()
. However, to get any improvement out of static merging, you want the objects merged to share Materials (see the stress demo). If all of the merged Models have unique materials, then they will all necessitate their own MeshParts and render individually, which means you won't get very much benefit from merging them together.
Dynamic batching is another tool in the belt of rendering optimization. While not as flexible as static merging, it's still rather useful.
First, we'll walk through the way rendering Models in Tetra3D works. Essentially, after we've fully prepared all data for rendering in Render()
, we use Ebitengine to actually render the triangles for our object - this is done through the Flush()
function. The rendering flow for individual objects, then, would look like this:
Object A:
MeshPart 0:
Render()
Flush()
Object B:
MeshPart 0:
Render()
Flush()
MeshPart 1:
Render()
Flush()
Object C:
MeshPart 0:
Render()
Flush()
... And so on. However, if we're rendering a lot of objects with a single, shared Material, we can avoid flushing until all such objects are rendered, saving a lot of time in the process:
Object A:
MeshPart 0:
Render()
Object B:
MeshPart 0:
Render()
MeshPart 1:
Render()
Object C:
MeshPart 0:
Render()
Flush() - (Using some common Material setup)
While not as fast as static merging, dynamic batching is very good for saving time if you have a lot of models that you want to render with a single Material while maintaining their dynamic nature (i.e. they can move around individually, they can be animated individually, can change color individually, and so on).
Note, though, that there are restrictions to dynamic batching:
-
Dynamically batched Models can't have individual material properties (like individual textures, material blending modes, or texture filtering modes). Object and vertex colors still work for coloring triangles in a batched Model, though.
-
Dynamically batched objects all count up to a single vertex count, so we can't have more than the max number of vertices after batching all objects together into a single Model (which at this stage is 65535 vertices, or 21845 triangles).
-
Because we're batching together dynamically batched objects, they can't write to the depth texture individually. This means dynamically batched objects cannot visually intersect with one another - they simply draw behind or in front of one another, and so are sorted according to their objects' depths in comparison to the camera.
-
After adding models to a model to dynamic batch, the owning object will no longer render its own model, even if all models are removed from its batch.
All of these batching and merging tools are very useful, but can be unwieldly, so it's often easier to automatically batch relevant objects together and let Tetra3D handle it. Mesh objects can be automatically batched by changing the Automatic Batching option under the Object panel (or by setting a Model's AutoBatchMode
property). When set to either Dynamic Batching or Static Merging, objects will be batched or merged automatically when their parenting changes (i.e. when added to a scene tree). Rendering of these objects will also be automatically handled.
This makes creating efficient levels simpler, as you can automatically merge objects that share a material (like from a tileset), or batch together objects that are similar, but might need to retain some individuality, movement, or interactivity (like destructible trees, NPCs, randomly colored scene elements, etc).