Optimizing Shader Binding
One common bottleneck in the graphics pipeline is the binding of shaders during a draw call. In a basic implementation, we might simply bind shaders for every mesh draw call. However, this can be very expensive and creates a ton of unnecessary work. One demonstration of this is a scene where every mesh is drawn with the same shader. In that scenario, we would be wasting time rebinding the same shader for every mesh! Ideally, we would simply bind the shader once for the entire draw call. By using render commands, we can implement that behavior.
Render commands are a technique used to encode draw calls into a uint64_t. This is done by setting specific bits in the integer to indicate the categorization and prioritization of a draw call. First, we can categorize draw calls by the shaders they are using. Then, we can prioritize draw calls that are closer to the camera. By encoding these two properties into an integer, we can then sort our draw calls!
Sorted Draw Calls
Below is an animation demonstrating what sorted draw calls will look like. Notice how the meshes with the white shader are drawn before the meshes with the red shader. This means the draw calls have been sorted by shader! Something else to note is that in group of meshes with the white shader, they are also being drawn from closest to farthest away from the camera.
Here's also the same scene from a different camera angle. We can see the same sorted draw call behavior, which means our draw calls are independent from when they are submitted!
With draw calls sorted, we can address the original problem of needing to rebind a shader for every draw call. This is done by keeping track of the last shader bound while rendering a frame. If it's the same shader as the one we want to use for the current draw call, we can skip binding it! We can see this in the image below, which displays two green draw calls in a row with only one yellow shader binding.