Debugging a captured frame is usually a real challenge in comparison with debugging C++ code. We are dealing with hundreds of thousands of more, pixels that are produced, and in addition, there might be several functions being processed by the GPU. Typically, in modern games, there are different passes on a frame constructing it; also, there are many post-process renderings that will be applied on the final result to increase the quality of the frame. All these processes make it quite difficult to find why a specific pixel is drawn with an unexpected color during debugging! Visual Studio 2012 comes with a series of tools that intend to assist game developers. The new DirectX graphics diagnostics tools are a set of development tools integrated with Microsoft Visual Studio 2012, which can help us to analyze and debug the frames captured from a Direct3D application. Some of the functionalities of these tools come from the PIX for a Windows tool, which is a part of DirectX SDK.
note that the DirectX graphics diagnostics tools are not supported by Visual Studio 2012 Express at the time of writing this article.
In this article, we are going to explain a complete example that shows how to use graphics diagnostics to capture and analyze the graphics information of a frame. Open the final project of this article, DirectX Graphics Diagnostics, and let's see what is going on with the GPU.
Intel Graphics Performance Analyzer (Intel GPA) is another suite of graphics analysis and optimization tools that are supported by Windows Store applications. At the time of writing this article, the final release of this suite (Intel GPA 2013 R2) is able to analyze Windows 8 Store applications, but tracing the captured frames is not supported yet. Also, Nvidia Nsight(TM) Visual Studio Edition 3.1 is another option which supports Visual Studio 2012 and Direct3D 11.1 for debugging, profiling, and tracing heterogeneous compute and graphics application.
Capturing the frame
To start debugging the application, press Alt + F5 or select the Start Diagnostics command from Debug | Graphics | Start Diagnostics, as shown in the following screenshot:
You can capture graphics information by using the application in two ways. The first way is to use Visual Studio to manually capture the frame while it is running, and the second way is to use the programmatic capture API. The latter is useful when the application is about to run on a computer that does not have Visual Studio installed or when you would like to capture the graphics information from the Windows RT devices.
For in the first way, when the application starts, press the Print Screen key (Prt Scr).
For in the second way, for preparing the application to use the programmatic capture, you need to use the CaptureCurrentFrame API. So, make sure to add the following header to the pch.h file:
For Windows Store applications, the location of the temp directory is specific to each user and application, and can be found at C:\users\username\AppData\Local\Packages\package family name\TempState\.
Now you can capture your frame by calling the g_pVsgDbg->CaptureCurrentFrame() function. By default, the name of the captured file is default.vsglog.
Remember, do not start the graphics diagnostics when using the programmatic capture API, just run or debug your application.
The Graphics Experiment window
After a frame is captured, it is displayed in Visual Studio as Graphics Experiment.vsglog. Each captured frame will be added to the Frame List and is presented as a thumbnail image at the bottom of the Graphics Experiment tab. This logfile contains all the information needed for debugging and tracing. As you can see in the following screenshot, there are three subwindows: the left one displays the captured frames, the right one, which is named Graphics Events List, demonstrates the list of all DirectX events, and finally, the Graphics Pixel History subwindow in the middle is responsible for displaying the activities of the selected pixel in the running frame:
Let's start with the Graphics Pixel History subwindow. As you can see in the preceding screenshot, we selected one of the pixels on the spaceship model. Now let us take a look at the Graphics Pixel History subwindow of that pixel, as shown in the following screenshot:
The preceding screenshot shows how this pixel has been modified by each DirectX event; first it is initialized with a specific color, then it is changed to blue by the ClearRenderTargetView function and after this, it is changed to the color of our model by drawing indexed primitives. Open the collapsed DrawIndexed function to see what really happens in the Vertex Shader and Pixel Shader pipelines. The following screenshot shows the information about each of the vertices:
The input layout of the vertex buffer is VertexPositionNormalTangentColorTexture. In this article, you can see each vertex's value of the model's triangle. Now, we would like to debug the Pixel Shader of this pixel, so just press the green triangular icon to start debugging. As you can see in the following screenshot, when the debug process is started, Visual Studio will navigate to the source code of the Pixel Shader:
Now you can easily debug the Pixel Shader code of the specific pixel in the DrawIndexed stage. You can also right-click on each pixel of the captured frame and select Graphics Object Table to check the Direct3D object's data.
Following screenshot shows the Graphics Event List subwindow. Draw calls in this list are typically more important events:
The event that is displayed with the icon marks a draw event, the one that is displayed with the icon marks an event that occurs before the captured frame, and the user-defined event marker or the group shown with the icon can be defined inside the application code. In this example, we mark an event (Start rendering the model) before rendering the model and mark another event (The model has been rendered) after the model is rendered. You can create these events by using the ID3DUserDefinedAnnotation:: BeginEvent, ID3DUserDefinedAnnotation:: EndEvent, and ID3DUserDefinedAnnotation:: SetMarker interfaces.
Investigating a missing object
This section will demonstrate how to investigate an error that occurs in the Vertex Shader stage so we can find out why an object is missing from our scene. First we need to find the draw call for the missing geometry. Select the Draw Event from the event list then select Graphics Pipeline Stage by navigating to Debug | Graphics | Pipeline Stages.First select Input Assembler and view the object's geometry inside the Model Editor. If we make sure that it is the missing geometry, we must find out why it is not shown in our scene. The following are some of the common reasons:
- Incompatible input layout: When you click on the DrawIndexed event, you will see a window that shows the device context information. Make sure the input element description of the vertex buffer is the same as the Vertex Shader input structure.
- Error in the Vertex Shader code: The input assembler provides the right data for the Vertex Shader, so we need to see what is going on inside the Vertex Shader. Use the HLSL debugger to examine the state of variables in the Vertex Shader, especially for the output position of the Vertex Shader.
- Error in the application source code: If neither of the previous two solutions solve the rendering issue, then we must find out where the constant buffer is being set in the source code of the program. Open the Graphics Event Call Stack window by navigating to Debug | Graphics | Event Call Stack and check the source code.
- Let's check the device state: Open the Graphics Object Table tool and examine the device state, especially the depth stencil view, to see whether the primitives have failed the depth test or not.