This section contains a number of procedures and workarounds to aid with GPU performance and debugging.
GPU hangs might appear to originate in the use of ring buffer memory. The graphics driver uses two internal ring buffers, one for the Constant Engine, and one for the Draw Engine, and both are 4MB in size. This size is not configurable. The driver waits within RingBuffer::Allocate until the required space is available. Running out of ring buffer space may therefore cause a CPU stall, but not normally a GPU crash. However, if this wait is longer than four seconds, a GPU hang is reported. This hang is most likely caused by a faulty draw call coming from elsewhere, but is simply detected by the timeout in the RingBuffer::Allocate call.
To track down the source of GPU hangs, set the D3D__CpuSingleStepper flag. This flag is within the graphics driver and can be set by the debugger (after the graphics driver has loaded) using the command:
ciumd_hydra.dll!D3D__CpuSingleStepper
Set this flag to true as close as possible before the hang. This will force the CPU to wait for the GPU after every draw call. The title will run very slowly with this procedure, but when the GPU hangs the callstack will be for the draw call that caused the hang.
Split points occur where the driver has to “split” a command list recording, and insert a late-bound graphics operation that is evaluated at command list execution time. Some instances are unavoidable – a good example would be calling ClearRenderTargetView on a deferred context. An avoidable split point is the usage of an externally updated dynamic resource within a command list recording – the current state of the resource will not be known at record time, therefore the usage must be done at playback time, to capture the current state of that resource. The System Monitor in PIX has a number of GPU counters that you can use to monitor the rate of split points in your title. If you find a high rate of split points over time, use GPU capture analysis in PIX to investigate your usage of command lists. System Timing captures will also attribute timing events as split points occur.
By far the most common cause of split points is the use of dynamic resources that are updated outside of the command list. Often this takes the form of dynamic constant buffers or vertex buffers. This causes a split point because the driver does not know at record time where to look for the resource data. If a ID3D11DeviceContext::Map call is made with the MapType parameter set to D3D11_MAP_WRITE_DISCARD, then the split point will go away because the data is known at record time; however, the data written to the resource will not persist past the end of command list execution.
To eliminate split points caused by dynamic resources, change the resources to DEFAULT usage and manually double buffer them to allow for updates every frame. This will not only eliminate the split points, it will also allow your resources to take advantage of the faster Garlic memory bus.
The following operations can also cause split points when performed while recording a command list on a deferred context.
Direct3D causes a context roll whenever occlusion queries turn on and off. For example, a title might issue an occlusion query for every draw call in the gbuffer pass. The driver turns on PERFECT_ZPASS_COUNTS inside an occlusion query scope, and turns it off outside an occlusion query scope. This causes a context roll on every draw during the affected pass.
There are two possible workarounds to avoid the context roll: either call ID3D11DeviceContextX::SetPredicationFromQuery, or, encompass the draws in a dummy query, so that the nesting count remains at 1.