Track Optimization
Note
This page covers some of the core principles of 3D rendering to ensure you have solid grasp of what’s happening under the hood. The concepts are being explained at a high level and the goal is that understanding these concepts will help make the process of optimizing your tracks clearer.
The quick rundown
Modern graphics cards can render a staggering amount of polygons very fast, and because of BallisticNGs low shader complexity, it’s even easier for your graphics card to churn out pixels.
Unity has many of it’s own rendering optimization systems in place, however internal BallisticNG tracks don’t need to lean into them as much as other games due to the atlased textures, low material counts and aggressive merging of meshes in the modeling software. This means Unity has less work to do when processing objects for rendering.
A little bit about how rendering works
Rendering a game is a process that involves both the CPU and GPU. First the CPU must figure out what we want to draw, then it sends all of that information off to the GPU to finally draw what we tell it to.
On a standard desktop PC with a dedicated GPU the slowest part of this rendering process is almost always the initial gathering of objects. If you’ve played a game that isn’t visually impressive but runs bad, this will usally be the culprit. A modern graphics card can handle millions of polygons being thrown at it, but the CPU will struggle to keep up if those millions of polygons are split over many, many objects.
So then this brings us to the core point of discussion for this page: Bandwidth
Bandwidth
The CPU is normally going to be the bottleneck in rendering because while it can push a lot of data in a single stream pretty fast, it can’t push a lot of data in multiple streams anywhere near as fast.
This then means we need to be clever about how we’re getting our scene from the CPU to the GPU, and luckily for us BallisticNG scenes are static and relatively small. This is perfect because since we know information about every object ahead of time, and where the player will be with certainty, we can apply some aggressive optimization techniques to our tracks.
Draw Calls
This process of sending rendering information from the CPU to the GPU is called a draw call, which we perform for each object in the scene. If the object has multiple materials, that object will generate multiple draw calls since the material splits the mesh into sub-meshes which require individual drawing.
It’s best practice to use as little materials as possible. Because BallisticNG tracks are made with texture atlases, this means the entire scene can usually be assigned just a handful of materials depending on the effects desired.
Quad Overdraw
Your graphics card renders games in blocks of 2x2 pixels (a quad). Quad overdraw is when any of the pixels in each 2x2 quad is drawn more then once.
The most common cause for this is transparent objects. As transparent pixels are not writing depth information, the GPU can’t determine if any other pixels that might be drawn in the same place would be visible or not, so it will always redraw the pixel. This can drain performance if you have a lot of transparent surface taking up large portions of the screen. The most common place to run into issues with this is when using dense particle systems.
Quad overdraw can also happen when you have a lot of dense opaque geometry in the far distance. Dense geometry at a distance can result in multiple triangles occupying the same 2x2 quad and your GPU putting in a lot of extra work.
It’s important to note that BallisticNGs shaders are quite simple and easy to render. These are issues that you should be aware of so you don’t overdo it, but a little bit of quad overdraw while using BallisticNGs shaders is not going to tank your performance.
Fill Rate
FIll rate determines how fast your graphics card can draw triangles (filling them with pixels). There are a few factors that play into how fast a triangle can be fully rendered:
The speed of your graphics card
How many pixels on your screen the triangle is taking up
The complexity of the shader drawing the triangles pixels
This means that lots of geometry in the distance is actually faster to render then the same geometry taking up the entire screen, assuming that the same number of triangles is visible at both distances. Keep in mind that this does not mean you should make high detailed objects in the background, as this is still a waste of processing time which could be spent on objects closer to the camera, or not at all.
BallisticNGs shaders are not very complex, so lots of triangles can be drawn quickly on appropriate hardware.
Frustum Culling
This is automatically handled behind the scenes and you don’t need to do anything for this, but it’s included here just so you know it’s happening
Frustum Culling figures out what the camera can see so we’re only working with objects that are visible to the player. Let’s say we have a really dense city and we’re at the edge of it. If we’re facing away from the city then we don’t care about all of those buildings because we can’t see them. This means we can skip render pre-processing on these objects and save ourselves a lot of time preparing them. Note that this can’t determine when an object is behind another object entirely and will render anything that is inside of the cameras frustrum, regardless of if the object is actually visible or not.
Batching
Batching is the process of taking multiple objects which share a material and bundling them into a single draw call. This can very dramatically increase performance if lots of objects are involved, but does still have an overhead in and of itself. Unity has two types of batching: static and dynamic.
Static batching performs the batching ahead of time so the game doesn’t have to figure it out at runtime. This however has a major caveat, which is it only works with very low poly objects.
Dynamic batching performs the batching for each rendered frame. It’s slower then static batching, but still faster then no batching at all. This works with much larger meshes, which makes it the preferable method of batching.
However do keep in mind that as previously said, batching does itself have an overhead which we can actually eliminate ourselves by merging objects that should be rendered at the same time in our modeling software.
Baked Occlusion Culling
TODO
Render Zones (BallistiNG specific feature)
Not available yet