A basic performance investigation around a variety of rendering techniques within Unity.
Every mesh gets its own GameObject, MeshFilter, and Renderer components.
Transform matrices and materials are cached and all meshes are drawn using the Graphics.DrawMesh()
function.
No overhead and management of Renderers and MeshFilters.
Same as above, but one material is used and each mesh gets its own MaterialPropertyBlock to augment that material.
MaterialPropertyBlock is supposed to be more efficient, but seems to take more time render.
It's possible that this is just more memory efficient. Results in the same number of set pass calls and batches.
Each mesh is converted into two ComputeBuffers for both indices and attributes which are referenced in the vertex shader. A material and matrix are cached for each mesh and rendered using the Graphics.DrawProcedural()
function and GL.PushMatrix()
to set the transform of the draw.
Each material takes both that "points" and "attributes" compute buffers as parameters.
Despite many draws, the Unity stats window only displays that two "draw calls" are being made.
Every draw requires a new set pass call.
Because the mesh is procedural this will by-pass all of Unity's internal calculations rendering logic like frustum culling.
Attributes can be stored in custom formats and unpacked in the vertex shader to save on memory.
Same as above, but the attributes for the mesh are unpacked into a single ComputeBuffer with three vertices per triangle.
Only pro might be that there is less array access in the vertex shader.
This approach takes more memory and transforms more vertices.
Runs an occlusion pass, counts the triangles, generates an array of visible triangles, and renders only the visible triangles using DrawProcedural.
Requires a few compute shader and prepasses to run. These passes can be done over multiple frames:
More memory is required for the extra textures and buffers.
This limits the amount of triangles that have to be drawn to the primary buffer every frame to a minimal and possibly consistent count.
Moving a mesh requires updating a buffer with the model attributes.
One compute buffer could be used to store all the attributes for all meshes with an offset buffer to help address a specific point in the attribute buffer to render. Multiple meshes could then be drawn by instancing and the instanceId can be used to address the specific mesh to draw.
The biggest issue is that when using DrawProcedural()
, you have to specify the amount of vertices, which means every mesh must be the same size.
Decide which triangles should be drawn and generate the data for the compute buffer that will be drawn.