Search code examples
performanceunity-game-engine2dgpu-overdraw

Unity 2d, multilayer background overdraw


The definition of the problem is simple yet I wasn't able to find a simple or straightforward solution.

I have a background, made of several opaque layers:

  1. Gradient sky (1pic, static);
  2. Bright big mountain (1pic, moves a little);
  3. Darker [closer] mountains (a few pics, randomly generated, moves faster);
  4. Even darker [closer] mountains with some details (a few pics, randomly generated, moves even faster).

I experience overdraw: every picture is drawing on top of each other. Every layer is dynamic so I can't just cut half of the sky or cut half of the mountain. However, 50% of the pictures are just one solid color + full transparency.

Question 1: what is the best practice of preventing background overdraw? Consider the simplest example: background sky (100% screen) + moving mountain (80% screen).

Question 2: some of the mountains are simple pictures - one opaque color, curved shape and the rest is full transparency. Should I keep using them as textures in sprites or I have to involve some practices to use it differently?

Background's look: Background's look


Solution

  • Answers:

    1. There is no overdraw preventing solution unless something will be written from the ground up. Most likely it will involve meshes and smart runtime adaptation depending on layers positions.
    2. Unity now has SVG support. I tested this and it's better (depending on the number of vertices) than having individual textures. Properly imported SVG images have no textures in them which prevents increasing of SetPassCalls and overall texture loading/unloading.

    Overdraw is the problem for low-end devices and the best practice is to minimize the number of layers. However, something additional can be done.

    • First of all, the empty scene rendering should cost as little as possible. That means no post-processing, no unoptimized scripts, etc. Simple FPS counter and temperature measures of a low-end device will help here.
    • Then, the choice of shaders matters the most. The simplest forms of shaders are Unlit/*. However, they aren't as flexible as default shaders (which also can be considered as lightweight).
    • SetPassCalls is a probably important subject, but it wasn't a device crashing for me. Essentially, all the background is better to be included in one atlas, share the same shader, and do not have anything else in between. This way you can have as many layers as you want and everything will be drawn in one draw pass call.
    • And lastly, avoid full-screen dynamic lighting. Light sources should lit as little layers as possible, as smaller area, as possible. The quality of lighting can be toggled as well. I am still fighting with the lighting and trying to apply baking to my task, but so far getting rid of background full-screen lights was a huge FPS boost.