I've read Unity3D
draw call batching documentation.
I understood it, and I want to use it (or something similar) in order to optimize my application.
My situation is the following:
- I'm drawing hundreds of 3d buildings. Each building can be represented using a
Mesh
(or a SubMesh for each building, but I don't thing this will affect performances) - Each building can be textured with several combinations of texture patterns(walls, windows,..). Textures are stored into an
Atlas
for optimizaztion (see Texture2d.PackTextures) - Texture mapping and facade pattern generation is done in fragment shader. The shader can be the same (except for few values) for all buildings, so I'd like to use a
sharedMaterial
in order to optimize parameters passed to the GPU.
The main problem is that, even if I use an Atlas, share the material, and declare the objects as static to use static batching, there are few parameters(very fews, it could be just even a float I guess) that should be different for every draw call.
I don't know exactly how to manage this situation using Unity3D
. I'm trying 2 different solutions, none of them completely implemented.
Solution 1
- Build a GameObject for each building building (I don't like very much the overhead of a GameObject, anyway..)
- Prepare each GameObject to be static batched with StaticBatchingUtility.Combine.
- Pack all texture into an atlas
- Assign the parent game object of combined batched objects the Material (basically the shader and the atlas)
- Change some properties in the material before drawing an Object
The problem is the point 5. Let's say I have to assign a different id to an object before drawing it, how can I do this?
- If I use a different material for each object I can't benefit of static batching.
- If I use a sharedMaterial and I modify a material property, all GameObjects will reference the same modified variable
Solution 2
- Build a Mesh for every building (sounds better, no GameObject overhead)
- Pack all textures into an Atlas
- Draw each mesh manually using Graphics.DrawMesh
- Customize each DrawMesh call using a MaterialPropertyBlock
This would solve the issue related to slightly modify material properties for each draw call, but the documentation isn't clear on the following point:
Does several consecutive calls to Graphic.DrawMesh
with a different MaterialPropertyBlock would cause a new material to be instanced?
Or Unity can understand that I'm modifying just few parameters while using the same material and is able to optimize that (in such a way that the big atlas is passed just once to the GPU)?
Answer
- Textures are stored in vram and don't get deleted even between frames, draw calls just bind these textures to the texture units. Changing a texture has always the same cost, regardless of the texture size. Each state change is expansive. DrawMesh looks pretty good, it should sort all meshes using this material and limit the state changes to the property block and transformations. I expect it to not create a new material, it is a little bit lower than that and only set the MaterialPropertyBlock's properties in addition to the material. Still, unity's batching will no longer work which means one draw call peer subject.
- As soon as you want to change any material properties per instance static and dynamic batching will not be possible, at least unity's batching methods look that way. Still, if you meshes contain submeshes that may use shared materials, unity should combine these at least.
I don't think unity will give you access to the low level rendering APIs to implement real hardware instancing eg using per instance vertex streams? If the amount of data is really low, you can implement your own static batching, by cloning the meshes and adding the data to each vertex, eg as color attribute, then unity's static batching should work. What a waste of memory... :-)
No comments:
Post a Comment