Game Development Community

dev|Pro Game Development Curriculum

A PIX debugging session

by Brian Richardson · 02/05/2008 (9:50 pm) · 13 comments

Here's an article I've been meaning to write for a little while. It's about PIX, the DirectX utility that ships with the DirectX SDK. It's a great utility for tracking down rendering issues. The point of this article isn't really to explain it in great detail, because it's easy to use. It's mostly to expose people to the tool and encourage you to check it out! This is a debugging session I did after tracking down a crash bug in dynamic cubemaps in the upcoming TGEA release. The crash bug was pretty easy to find. But when I fixed it, I got this result:

knowhere.net/gg/pix-article/thumbnails/pix000.png

The "spider ball" in this image should be reflecting the tower it is next to, what gives? Let's use PIX to figure it out. First, to launch PIX, find it under DirectX Utilities under the DX SDK folder. You'll get a blank screen. Next, select File->New Experiment. Then for "Program Path", enter the path to your executable. Then, select the "A single-frame capture to Direct3D whenever F12 is pressed." radio button. Finally, hit the "Start Experiment". This will launch the app. The next step is to get your app to render your bug. In this example, I moved the camera so I was looking at the spider ball again and hit the F12 key. The game will pause while PIX records all of the DirectX API calls and state into a log file. Then quit. You'll end up with a screen like this:

knowhere.net/gg/pix-article/thumbnails/pix001.png

The treeview in the lower left pane is a list of all DirectX calls made during the frame that was recorded. If you click on the render tab in the right pane PIX will show you the scene up to the API call selected in the left pane. You can learn a lot about how an app renders by just hitting the "D down" button and watching the scene draw step by step in the render pane.

knowhere.net/gg/pix-article/thumbnails/pix002.png

It's cool to have all of this information at our fingertips, but it'd be cooler if we could filter it down to what we're interested in. By right-clicking on a pixel in the scene and selecting "Debug This Pixel" we can do just that.

knowhere.net/gg/pix-article/thumbnails/pix003.png

This will replace the render view with a list of all of the API calls that changed the color of that pixel. It's a life story of that pixel for that frame. You can click on each of the links displayed to move the current selection in the treeview in the left hand pane to that API call.

knowhere.net/gg/pix-article/thumbnails/pix004.png

In this next image, I just picked the first pixel that was black, which should get me to the API call I feel is generating the wrong image. It's event 2893.

knowhere.net/gg/pix-article/thumbnails/pix005.png

Now we can examine the state of D3D at the time of this call. This is awesome stuff! Rendering with DirectX (and OpenGL) basically involves setting up a large state machine. The types of states are rendering states, textures, texture sampling states, shaders, shader constants, and the list goes on. If any one of these states is incorrect, you'll get rendering bugs. They can be hard to track down without a tool which allows one to examine the state of D3D. Often we have to do ad-hoc state checks by dumping textures to disk or querying the device and logging the state info out to a file. PIX provides the ability to check state without modifying your code. Getting back to my dynamic cubemap problem, the first question we must answer is: Is the dynamic cubemaptexture being rendered correctly? If it is being generated correctly, are we actually using it during the draw call? Let's find out! Double click on the blue hexadecimal address to the left of the API call in the treeview in the left hand pane. In this example, it's 0x056150D8. This will change the right hand pane to a view of the device. We can see a lot of state here. I want to look at the textures, so I choose "Pixel State" from the tabs and scroll down to the "Sampler" section. This section allows one to look at all of the current textures that are active during a draw call. Many bugs are just due to the wrong texture being bound, or no texture is bound at all!

knowhere.net/gg/pix-article/thumbnails/pix006.png

To actually look at the texture, just double click on the blue hexadecimal address in the "texture" column. In the next picture, you can see the cubemap texture has data in it! This is good news, that means I can ignore all of the code that deals with generating the cubemap.

knowhere.net/gg/pix-article/thumbnails/pix007.png

Next, I wanted to look at the pixel shader code that is used. Mostly because I forgot how the cubemap was working at the time. I wanted to see what other dependencies it might have: other textures, shader constants, etc. To do this, go back to the debugger frame that showed the history of the pixel and click on "Debug pixel (xx,xx)". This will bring up the pixel shader that was used.

knowhere.net/gg/pix-article/thumbnails/pix008.png

Looking at the shader, it only samples the cubemap and doesn't rely on any shader constants. It's time to look at the vertex shader to see what it passes to the pixel shader. To do this, just select "Debug Vertex X" in the history view.

knowhere.net/gg/pix-article/thumbnails/pix009.png

Looking at this dump really fast, I could see that it needed to have these vertex shader constants: $modelview @ position 0, $cubeTrans @ position 16, and $cubeEyePos @ position 19. Let's check the vertex shader constants and see if the data is good. To do this, select "Vertex State" from the tabs and scroll down to the vertex shader constants.

knowhere.net/gg/pix-article/thumbnails/pix010.png

Oh crap! Look at positions 16-19, it's full of zeros. So the cubemap transform isn't getting set! I quickly searched the code to see what sets them up and found out that they'll only be set if the material has "dynamicCubemap" set to true! D'oh! I fixed the material and got the following screen:

knowhere.net/gg/pix-article/thumbnails/pix012.png

That was literally my debugging session, it took about 5 minutes. It could have taken a lot longer if I didn't have the right tool. Hopefully, this was a good introduction to PIX. There are other uses for it: performance monitoring, resource usage, but this is what I use it for the most. I hope this article makes you reach for PIX the next time you have a rendering bug.

#1
02/05/2008 (11:05 pm)
That has got to be one of the best blogs I've ever seen! Especially since this is one of my bugs ;)

Now to figure out how to add in the environmental cubeMap as well, so it renders the sky as well...think I'll open up Pix :)
#2
02/05/2008 (11:30 pm)
Awesome post Brian. Really. That's super quality.
#3
02/06/2008 (4:52 am)
Excellent, thanks a lot for this blog!
#4
02/06/2008 (6:36 am)
Agreed, Pix is great (at least the xbox one is, the windows one isn't just as good).
For those big bugs it's an excellent debugging tool. And this is a very informative blog, thank you.

One shouldn't trust PIX too much though, especially when it comes to DepthStencil surfaces and similar things.... that never worked for me. Just out of curiosity, does it work for you? I get a lot of garbage in all of my DepthStencil's (the data is actually right because it works, but PIX shows meaningless stuff).

And for performance monitoring... I believe that PIX is a bit useless compared to nvPerfHud (yes, that's a very different, lower-level app... but so much better). Just my opinion though.
#5
02/06/2008 (8:06 am)
Using PIX and NvPerfHud together is an awesome combination. I just wish I could wrap my head around vTune, which is frankly a bit of a mess :)
#6
02/06/2008 (8:20 am)
Great .plan, Brian.

Hadoken: PIX for windows doesn't seem to do depth-stencil correctly, in my experience. The 360 version does very well though.
#7
02/06/2008 (9:14 am)
eXellant resource.

Randy
#8
02/06/2008 (10:16 am)
@all: Thanks for the comments!

@Hadoken: I have noticed that fairly recent versions of PIX for Windows (August07) fixed some bugs. I just quickly looked at a depth buffer and saw some stuff in it, I'm not sure if it covers everything. Most depth buffer bugs I've encountered so far have been simple things like the buffer being the wrong size. The DX Debug Runtime has always yelled at me about that and made it easy to fix.
#9
02/06/2008 (4:06 pm)
I just checked out nvPerfHUD 5 the other day and wow! They added a lot of new, very useful things, and of course it runs in realtime as well. It has access to a lot of data that PIX doesn't. It's been a while since I've run PIX, but the new nvPerfHUD had just about everything that PIX did the last time I used it. Both ATI and nVIDIA also have PIX plugins worth checking out as well (expose more counters from card). Both of them are great tools, really cool stuff.
#10
02/06/2008 (11:58 pm)
Great post! Thanks for the info on PIX! I never realized this tool was part of DX SDK.
#11
02/10/2008 (12:02 pm)
The only issue with NvPerfHUD 5 is that it doesn't support the 8800GT yet. :(

Other than that its another great tool when your needing to fix graphics bugs or optimize rendering performance.
#12
04/18/2008 (3:53 am)
This is a great blog entry. Very useful information.
#13
04/30/2008 (1:09 am)
I've been trying to use PIX to debug my TGEA application, but whenever I try and launch it with a "single frame capture" trigger the game runs at about 0.5 fps--it takes 10 minutes just to get past the loading screen.

Has anyone gotten PIX working with TGEA? I've used PIX with other applications and it hasn't behaved this way..