?/TD> |
Microsoft DirectX 9.0 |
Microsoft?Visual Studio?.NET supports debugging assembly level and high-level language vertex and pixel shaders.
The debugger works with the debug version of the runtime, as well as with certain device types. Any vertex or pixel shader can be debugged when using a reference device. Vertex shaders can be debugged, providing they are running on a hardware abstraction layer (HAL) device created for software vertex processing. No debugging support is provided for hardware vertex processing or "pure" HAL devices. For more information about device types, see D3DDEVTYPE.
To do source-level debugging of a shader, the shader must be built with debug information and it must have an on-disk source file associated with it. You can create such a shader in the following two ways.
To debug an application, the application must be using the debug version of the Microsoft Direct3D?runtime and must have shader debugging enabled. You can configure these options through the Direct3D tab in the Microsoft DirectX?control panel applet. The DirectX debug service must also be running on the target computer. This service is installed automatically when you install the DirectX extensions for Visual Studio .NET.
There are two ways to start the debugger:
Visual Studio .NET also supports remote debugging of Direct3D applications. To attach to a Direct3D process on a remote computer, in the Processes dialog box, enter the name of the remote computer in the Name box and then attach to the process the same as for a local process.
The module window can be used to view all shaders that are currently loaded. When debugging native code, right-click on the window and choose Show Modules for All Programs to view the shaders that are loaded.
To set a breakpoint in a vertex or pixel shader source file, the shader must have debug information present (see Building with Debug Information). You can set a source code breakpoint in a shader the same ways you would set a source code breakpoint in C++ source code, that is:
Type the case-sensitive name of the shader function as the name, and set the language to D3D Shader. The function name is the name of the shader function stored in the debug information, the name of the file containing the shader, or the shader handle number preceded by the shader type (for example, VS1). The module window lists all currently loaded shaders and their names. IDirect3DDevice9::BeginScene and IDirect3DDevice9::EndScene breakpoints work as before.
Any of the following types of breakpoints can have a condition, as long as the breakpoint is set to fire when the condition is true (as opposed to when the condition changes):
The expression must evaluate to a single-element bool result.
A memory breakpoint triggers at the first instruction of any pixel shader that is about to modify a pixel within a given rectangle. Set a memory breakpoint by going to the Memory tab in the Breakpoint dialog, selecting D3D Shader as the language, and entering a region in one of the following formats:
Setting a breakpoint on a specific instruction of a shader does not require debug information. You can set an address breakpoint in the following ways.
The format of an address is <Shader Type>ShaderHandle:InstructionIndex, where Shader Type is either "v" for Vertex Shader or "p" for Pixel Shaders, ShaderHandle is the value of the handle returned by IDirect3DDevice9::CreateVertexShader or IDirect3DDevice9::CreatePixelShader, and InstructionIndex is the one-based byte offset for the shader instruction. For example, to set a breakpoint on the first instruction of vertex shader number 2, you would enter the address as v2:1.
To set a breakpoint when the application you are debugging reaches IDirect3DDevice9::BeginScene or IDirect3DDevice9::EndScene, select the New Breakpoint command (CTRL+B by default), select the Function tab, select Direct3D Shader from the Language combo box, and type either BeginScene or EndScene as the name of the function.
You can step through shader code using the Debug menu's Step Over command (F10 by default). Stepping in Direct3D code will cause the application you are debugging to stop at the next vertex or pixel shader instruction it runs.
When the application you are debugging is stopped in a vertex or pixel shader, or at a BeginScene or EndScene breakpoint, you can use the Watch window to inspect the state of the application you are debugging. You can also quickly view the contents of a register by briefly resting the mouse pointer on the register in the Disassembly window or in your shader source code.
The expression syntax supports register names, swizzles, and the following operators, which all operate per component.
+ - * / == != < <= > >= [] ! || &&
In addition, the following two intrinsic functions are supported.
All types must match exactly. Floating-point literals must have a decimal point; otherwise, they are treated as integers.
This expression returns true if all of the first three components of r0 are less than the corresponding components of c4.
all( r0.xyz < c4.xyz )
Constant register are indexed by one plus the x-component of a0.
c[a0.x+1]
To view the contents of a register in the watch window, use the following expression syntax.
registerName[.swizzle][,format][,w]
Where:
To view the current values of render states and texture stage states on the device, enter $DeviceState in the Watch window.
The coordinates of the pixel currently being rendered by a pixel shader are contained in register vPos.
Viewing the render target - To view the contents of the current render target surface, open the Render Target window by selecting the Debug menu's Direct3D, Render Target menu options. This window will be updated with the current contents of the render target whenever the application you are debugging enters "break" mode.
Viewing textures - To view the contents of the texture currently selected into a texture stage, open a Texture window by selecting the appropriate texture stage from the Debug menu's Direct3D, Textures menu. The texture windows will be updated with the current contents of the corresponding texture stage whenever the application you are debugging enters "break" mode.
If the contents of a surface (either the render target or a texture) are not available, the surface's window will show the text as "Unavailable." Possible reasons for a surface being unavailable include:
The Visual Studio .NET debugger supports debugging of multiple program types at the same time. Typical C++ debugging uses the "Native" program type. Direct3D debugging uses the "Direct3D" program type. When the application you are debugging stops in C++ code, the current program will be set to Native, and the call stack, watch window, and other debug windows will reflect the state of the Native program. Similarly, when the application you are debugging stops in shader code, the current program will be set to Direct3D, and the debug windows will reflect the state of the Direct3D program. To switch between program types, use the Program combo box on the Debug Location toolbar. Note that when the application you are debugging is stopped in Native code, most of the state of the Direct3D program will be unavailable. The reverse is not true, however. If you stop at a breakpoint in a shader, for example, you can switch to the Native program and see the C++ call stack that caused your shader to be invoked.
For more information about debugging multiple program types simultaneously, see "Debugging Multiple Programs" in the Visual Studio .NET documentation.
If possible, build your shaders with debug information enabled (see Building with Debug Information). In cases where building with debug information is not feasible, such as for shaders that are built up "on the fly," debugging is possible but is much more difficult. Source code is not available, so source code breakpoints don't work. One approach to debugging without debug information is to set a BeginScene breakpoint, and when that breakpoint is reached, step into the shader code of the first shader run by pressing F10. Once you're stepping through disassembly code, you can set breakpoints in the Disassembly window. You can also use the Modules window to view the names of all shaders currently loaded and selectively set breakpoints on them as needed.
Another approach is to set a breakpoint in your C++ code immediately before calling IDirect3DDevice9::DrawPrimitive or IDirect3DDevice9::DrawIndexedPrimitive. When this breakpoint is reached, you can then set an address breakpoint using the current shader handle and then run until that breakpoint is reached.