Since version 5, Unity has shipped a new tool for visually debugging your frames: the Frame Debugger. This allows you to find out the reason for many graphic issues you’ll face: Z-fighting, strange GPU states, wrong render queues, incorrect blending operations, high number of draw calls, low performance, etc.. It offers a much more detailed information in comparison to the stats layout from the game’s view. By interacting with it and checking the render events/steps you’ll also massively learn about the GPU pipeline. Really, every developer should get to know it!
In this entry I’ll briefly describe how to use the frame debugger and afterwards I’ll point out the drawbacks that can be addressed through other tools. It is not my goal to give a thorough description about every point, but rather, to present an overview of the aforementioned technologies.
1. Frame debugger: usage
Open our main window through Window->Frame Debugger. I recommend seeing both the frame debugger and the game view at the same time, so you might need to adjust your layout a bit.
Get your game in play mode and get to the area that is causing you headaches. In the frame debugger window, click on enable so you get the details about the frame that was lastly rendered.
There are two main areas: the events and, to its right, its detailed information. The events are basically the commands that the CPU are sending to the GPU. They are surely way simplified, since you can not really see the API functions that are being called. But it is simple enough to keep the whole process timeboxed. Most of them are about clearing and drawing (opaque/transparent) geometry, but you might see more information like GUI rendering, shadows, image effects, etc. along the section’s number of callbacks.
Whenever you select an event you’ll instantly get more detailed information about it on the details panel. Among that information: the bound shader and its flags, the event rendered output and the shader properties. At the same time, the game view will display what has been rendered until that event (inclusive). An example is shown below:
2. What for?
There are a few useful hints that the frame debugger is giving us for free. The order of draw events (i.e. the rendering queues) are crucial for a correct rendering. If you have elements that are not being correctly displayed, you might discover that it is being rendered in the wrong render queue and therefore too early/late, especially if you are playing with custom shaders and parameters like Z writes and tests. I had many issues fixed by using this tool; for instance, I discovered that the skybox was overwriting the output of my shaders because I forgot to set the right flags and render queues.
Secondly, you relatively see how many draw calls are there and indirectly how “expensive” they are by looking at vertices/index counts. The amount of shader passes does also help, but their complexity is not shown here so you’ll have to use some common sense. Having this information will help you improving performance in your scenes; e.g. you might discover that some meshes are not statically/dynamically batching for some reason. In my example you might have noticed two draw calls for rendering two sprites that can be easily merged into one just by using texture atlas.
Thirdly and by interacting with the frame debugger, you quickly get to know the GPU architecture and discover how Unity handles the rendering process. You may use the keypad to navigate through the different events and see how the scene is being rendered step by step. In the aforementioned example you see that the scene starts with clearing three buffers (color, z, stencil), followed by rendering the opaque geometry (front-to-back), skybox and transparent geometry (back-to-front).
Lastly, you may access the shader properties for more information about the material and shaders. You also have a reference to the data that is being used by that object, like textures. Not many people do know about the advanced views in Unity; you can access them in the inspector by clicking on the paragraph icon in the top-right area and selecting “Debug”.
While working for one of our clients, I experienced some drawbacks caused by the extreme simplicity of the frame debugger. I talked to a few Unity developers at Unite 2016 Europe and they seem to know about them, although they didn’t reveal if they had further plans to extend it. From my experience, some of them are:
- You don’t get low-level information about the API calls behind those events. Unity is, in this context, a black box that you can not (easily) improve in specific situations.
- It is hard to get an overview of the state of the GPU pipeline in every event. You just get information about the geometry and the textures but no data about the different stages like vertex/geometry/fragment shaders and rasterizer.
- You can’t really debug shaders. Either you output colors for visually debugging your code (even more complicated than prints) or you use external software like GLSL-Debugger.
- It is hard to associate resulting pixels with the responsible events that wrote them. You have to go step by step and check which event is writing the wrong information in your pixel; that can take a long time if you have a huge number of draw calls.
- No accurate indicators for measuring performance per draw-call. The number of vertices/indices is not necessarily the best one. Ok, sending more vertices require more memory bandwidth, but a complex pixel shader executed on that geometry or expensive state changes might be even worse.
If you are (still) reading this, you might be as well looking for answers. So, what about experimenting with complementary other tools?
After testing several software, I can definitely recommend a few ones. The main one you should have in your toolchain is RenderDoc, a free tool originally developed at Crytek for solving lower-level issues. It is a tool that uses a classic approach used by many cheaters: it creates a virtual graphic driver and acts as a Man-in-the-Middle (MITM) to catch the application DX/GL/Vulkan calls so as to offer detailed debugging information. The good news are: there’s already a Unity native integration. You just need to install RenderDoc in your windows machine and restart the Unity editor.
My objective in this section is to give you an overview about the features of this tool so you feel intrigued enough to start playing with it and making your players happier both by playing smoothly and paying less electricity bills.
Let’s get down to business. If the installation went successfully, you should be able to right-click on Game and select Load RenderDoc:
After a few seconds, get into play mode and whenever you are happy with the frame you lastly rendered click on the new icon that looks like a bald person to the left of Maximize on Play:
Then switch to the newly opened RenderDoc window and double click the automatically captured log to analyze it:
Wow WTF! So much information. Calm down, everything’s gonna be ok. By now you will have all the frame’s gathered data in thousand windows. No worries, we’ll be highlighting a few points that I considered really interesting in the next paragraphs.
4.1. Event browser + API calls.
One advantage of this tool is being able to get lower-level information about what is going on in each event. The event browser is pretty much like an extended Unity’s frame debugger. It includes timings (in microseconds) and other hints that pretty much help measuring performance. RenderDoc is context-sensitive, meaning that, any time you select a certain event, most of the other windows and areas will only display the relevant information to that event. The API calls window under it lists the functions called for processing that event.
What a good thing to get these timings! Surely, they might not be absolutely precise, but they are relatively accurate, meaning, that by comparing the figures you will figure out which draw calls are the most expensive.
4.2. Pipeline state.
I always wondered how a pipeline looks like. How little cute geometries enter it and gently get transformed to pixels in your 50″ screen. Well, you’re not going to get such a user-friendly animation out of this tool, sorry.
There’re anyway great free resources for that. But anyway, this tool will give you access to a wide range of facts about the pipeline states in each event: input assembler, vertex/hull/domain/geometry/fragment/compute shaders, rasterizer and output merger. And trust me there, that is good stuff.