Optimizing Draw Calls In 2D Games

Understanding Draw Calls and Why They Matter

A draw call is a function call made to the graphics card to command it to render an object or model on screen. Each sprite, image, or geometry that needs to be drawn requires at least one draw call. An excessive amount of draw calls can bog down the game with unnecessary overhead, resulting in lower FPS and slower overall performance.

Every draw call requires the CPU to stop what it’s doing and hand over data like textures, positions, and transformations to the GPU for rendering. That context switching negatively impacts FPS and throughput. Some benchmarks indicate that each draw call introduces roughly 0.1ms of frame delay in many game engines. So minimizing them where possible is key for achieving smooth 60 FPS performance.

For example, a 2D game with thousands of independently moving sprites could easily generate that many draw calls every frame, bringing even powerful hardware to its knees. By batching and optimizing them properly, developers can reduce the quantity substantially while still rendering the same assets to the player’s view.

Techniques to Optimize Draw Calls

Batching Sprites

Batching refers to collecting together multiple sprites or pieces of geometry and rendering them in one draw call as a single, combined mesh. This drastically reduces the overhead of sending all those objects to the GPU separately.

Most 2D game engines provide mechanisms for the programmer to define sprite batches – logical groups of sprites that share enough commonality to have their vertices and indices combined. The specifics depend on the engine, but generally batching sprites reduces draw calls in proportion to the number of batches.

Using Texture Atlases

A texture atlas packs together many smaller texture images into a single large one. This allows sprites that were previously on separate texture sheets to now access different texture coordinates from the same big texture.

This matters for draw call optimization because switching textures mid-scene requires flushing the rendering pipeline, causing extra draw calls. Combining textures into a unified atlas enables more sprites to share vertices within a batch.

Minimizing Overdraw

Overdraw refers to pixels getting drawn over more times than necessary per frame, wasting fill rate. This happens often in 2D games when backgrounds, sprites, and UI elements overlap.

Engine profiling tools can visualize overdraw to help the developer rearrange scene elements and sorting layers so pixels aren’t over-written unnecessarily. Since overdrawn pixels still cost draw calls, reducing them improves batching and FPS.

Reducing Transparency

Transparent textures require special handling compared to opaque rendering. Many engines have to split and sort transparent elements into separate batches, forcing extra draw calls.

When feasible, reducing transparency by baking lighting into textures or through other creative solutions can enable more sprites to be packed together through batching.

Implementing Draw Call Optimization in Practice

Before and After Benchmarks

It always helps to use profiling tools to understand the starting point for draw calls and FPS in a scene. Then specific optimizations can be measured after the fact to demonstrate their impact.

For example, one scene might start out with 7500 draw calls and 45 FPS initially. After a day spent combining textures and adding batching, it could reduced to 2500 draw calls and 58 FPS through measurable progress.

Sprite Batching Code Example

Here is some example code for batching sprites in a hypothetical engine:

SpriteBatch batch = new SpriteBatch();

batch.Begin(); 

foreach (Sprite sprite in activeSprites) {
   batch.Draw(sprite); 
}

batch.End();

//Batched draw call replaces thousands of per-sprite calls!

Atlas Creation Guidelines

The texture atlas creation process varies across engines, but generally follows this workflow:

  1. Export individual textures as PNGs
  2. Use atlas packing tool to arrange them efficiently
  3. Set max texture size based on hardware limits (often 2048 or 4096px)
  4. Export packed atlas sheet plus data file with mappings
  5. Reference atlas instead of separate texture assets in game

Testing Overdraw with Profiling Tools

Most game engines provide real-time profiling views within the editor that can visualize overdraw. Look for red overlays marking areas of high over-render.

Alternatively, using GPU profilers like RenderDoc or gDebugger can pinpoint exact pixel overdraw in detail across specific frames to see where fill rate is being wasted.

Advanced Optimization Considerations

Multi-threading Rendering

Some game engines support jobifying or parallelizing rendering work across multiple threads. When done right, this can effectively hide the latency of draw calls since they execute asynchronously.

However, effective multi-threading requires its own set of performance considerations like avoiding race conditions between threads.

Custom Shader Programming

For ultimate control, developers can code their own shaders tailored precisely to their specific rendering needs. Combined with batching, this removes any overhead from generalized engine shader programs.

But it requires graphics programming expertise. Pre-built shaders provided by the engine may be sufficient for most use cases.

Using Object Pools

Object pools allow sprite and other drawable game object instances to be reused instead of destroyed and recreated every frame. This prevents costly memory allocation and deallocation each frame.

Combined with batching, this reduces per-frame overhead further by keeping references to pooled objects persistent across frames instead of requiring lookups each frame.

Achieving 60 FPS Through Draw Call Discipline

For high-performance 2D games targeting 60 FPS, carefully managing draw calls through batching, atlasing, culling, and other optimizations remains vital even on modern hardware. Profilers help identify optimization targets, but developers should architect scenes and rendering code with an emphasis on draw call reduction throughout development.

The demand for new features and content will always push the limits of hardware. Butby keeping the raw number of draws per frame low through mindful programming, developers give themselves headroom to evolve their games without compromising responsiveness for players.

Leave a Reply

Your email address will not be published. Required fields are marked *