This section describes some APIs released in the March 2014 XDK that are geared towards maximizing performance of a title using the Monolithic D3D driver. Setting fast resources is the fastest way to set resources into the D3D11 pipeline, and an asynchronous compute-only immediate context can be used to submit compute shader workloads to the GPU that run in parallel with graphics workloads. These techniques do require considerably more programming work for the title developers.
Fences are used to synchronize processing between the GPU, CPU and DMA engines. They can be used to force one processor to wait on another.
A fence is a 64 bit integer ID defined by the driver, and IDs will be unique across all contexts (the top bits of the fence value define the source context). A fence can be inserted into the GPU command buffer using ID3D11DeviceContextX::InsertFence, on an asynchronous compute context using ID3D11ComputeContextX::InsertFenceID3D11ComputeContextX::InsertFence, or a DMA engine using ID3D11DmaEngineContextX::InsertFence. A fence represents a particular position in a command buffer, and at any time the fence can be checked to determine if execution has passed that position or not.
When a fence is processed by the GPU or DMA engine, a simple writeback is performed to note progress, and processing does not stop. Inserting a wait on a fence indicates that command buffer processing should halt on that particular device until the fence ID that is being waited on is activated. The free-threaded ID3D11DeviceX::IsFencePending API can check the status of any fence using the CPU.
Normally when inserting a fence, all pending commands are flushed. However there is a flag available (D3D11_INSERT_FENCE_NO_KICKOFF) that will prevent the flushing. If this flag is set on an InsertFence API call, the fence will not be activated until all pending commands are flushed by a subsequent kickoff, either manual or automatic.
Setting D3D11_INSERT_FENCE_NO_KICKOFF should be done with caution, as improper ordering of fence insertions and fence waits can cause a fence deadlock. Careful use of this flag can reduce kickoffs and consequently increase speed.
This section describes the fastest way of setting resources into the D3D11 pipeline.
ID3D11DeviceContextX::SetFastResources is the fastest way to set resources into the D3D11 pipeline when the permutation of state is known at compile time. Similarly, ID3D11DeviceContextX::SetFastResourcesFromBatch is the fastest way to set resources into the D3D11 pipeline when the permutation of state is known only at run time. Titles should use SetFastResources and SetFastResourcesFromBatch for setting resource state whenever possible for best performance. This is because SetFastResources writes directly to the GPU’s Constant Engine (CE) command buffer and produces optimal code for any combination of resources being set. SetFastResources is implemented entirely inline as a pseudo variadic template method and can be inspected in the D3D11_X.H header.
The following example shows issuing a single call to check to see if there is room in the Constant Engine command buffer, and if so, does the following:
SetFastResources(
D3D11X_SET_FAST_IA_VB_WITH_OFFSET, 5, pBufferA, D3D11X_MAKE_STRIDE(strideA), pBufferB, D3D11X_MAKE_STRIDE(strideB),
D3D11X_SET_FAST_VS_WITH_OFFSET, 3, pBufferC, delta,
D3D11X_SET_FAST_PS, 3, pSampler, 0,
D3D11X_SET_FAST_PS, 0, pSrvA, 0, pSrvB, 0, pSrvC, 0);
D3D11X_SET_FAST_IA_VB cannot be used in conjunction with an offset and stride for vertex buffer objects or descriptors, so D3D11X_SET_FAST_IA_VB cannot be used to set a vertex buffer object for SetFastResources - the D3D11X_SET_FAST_IA_VB_WITH_OFFSET set type must be used instead, as shown in the first parameter above.
A maximum of eight resources in any permutation can be specified. SetFastResources is more CPU-efficient when as many resources as possible can be specified in one call, due to the amortization of the check to see if there is room in the command buffer. SetFastResources is more CPU- and GPU-efficient when resources set to consecutive slots are specified using the consecutive-slot notation, due to the two dwords of the command buffer packet header being amortized across those consecutive resources.
SetFastResources can be used as a replacement for all D3D11, D3D11_1, and D3D11_x methods that set the following resources, including “placement” and non-placement permutations:
The following resources cannot be set using SetFastResources:
There are some restrictions when using SetFastResources with shader resource views (SRVs):
Additionally, SetFastResources must abide by the rules for “D3D11X Fast Semantics”. This means that the following rules must be adhered to when using this API, otherwise GPU crashes or rendering corruption may result:
SetFastResources requires that the count and combination of resources be known at compile-time. When the count and combination of resources is only known at run-time (usually when the code is setting the resources needed on behalf of an arbitrary pre-created object), consider using SetFastResourcesIntoBatch and SetFastResourcesFromBatch for optimal performance.
For ultimate performance and reduced code size with your retail builds, #define D3DCOMPILE_NO_DEBUG to 1 before including D3D11_X.H. This will remove an if statement and halve the code size for every SetFastResources invocation. However, this does have some implications:
ID3D11DeviceContextX::SetFastResourcesFromBatch is the fastest way to set resources into the D3D11 pipeline when the permutation of state is known at run time but not compile time, which is usually when setting the resources on behalf of an object.
A batch is essentially a Constant Engine (CE) command buffer snippet containing a pre-compiled collection of commands for setting resources into the pipeline. SetFastResourcesFromBatch simply does an inline copy of this pre-compiled command buffer snippet directly into the Constant Engine command buffer.
Using this API requires that the rules for “D3D11X Fast Semantics” are followed (see the section “Asynchronous Compute Context” in the present topic). Note in particular that it is the caller’s responsibility to ensure that all resources referenced by the batch still exist at the time that the GPU executes SetFastResourcesFromBatch.
Refer to the following methods:
An asynchronous compute-only immediate context can be used to submit compute shader workloads to the GPU that run in parallel with graphics workloads. Similar to the DMA engine contexts, a set of fence APIs can be used to synchronize execution between the contexts and the CPU.
The use of this new API context requires a new set of usage semantics that give much more power and flexibility to the title developer. Specifically, all resource fence tracking, refcounting, and data hazard tracking has been removed from the compute context. The use of DYNAMIC usage resources with compute contexts is not supported (as those resources depend on fence tracking) There are new APIs to handle cache and pipeline flushes where they are needed to ensure correct output.
To create a compute context, call ID3D11DeviceX::CreateComputeContextX. A D3D11_COMPUTE_CONTEXT_DESC structure is used to specify parameters for the compute context, such as pipe and queue index, ring buffer size, and flags.
Up to nine compute contexts can be created at a time; six on the low priority pipe (pipe 0) and three on the high priority pipe (pipe 1). Queues on the low priority pipe run at a lower priority than graphics workloads, and queues on the high priority pipe run at a higher priority than graphics workloads. By default, queues on the low priority pipe have only 4 CUs enabled at context creation time; this can be adjusted after context creation using ID3D11DeviceContextX::SetComputeShaderLimits.
The methods of the asynchronous compute context are available through the ID3D11ComputeContextX interface.
To bind resources to the compute context, two options are available: “Set” APIs, and “SetPlacement” APIs, as follows.
The first option for binding resources to the compute context is the traditional set of Compute Stage “Set” APIs (CSSet…):
The second option for binding resources to the compute context is the D3D 11.X-style “Placement” APIs (CSSetPlacement…):
The “Placement” variants only allow one slot to be set at once, and allow you to provide a new resource base address that overrides the resource base address stored in the view or buffer.
For all of the “Set” APIs (traditional or placement style), the following rules apply. Note that these rules are similar to the SetFast family of APIs on the D3D 11.X device context:
Similar to the D3D graphics context, compute shaders can be set via the CSSetShader API. Work is queued via Dispatch and DispatchIndirect, with the same rules as the graphics context. Make sure to insert appropriate synchronization (see the next section) for the indirect dispatch parameter buffer. The Flush API is used to kick off command buffers for processing.
We recommend limiting the scope of compute workloads to execute in less than one 30Hz graphics frame. Long-duration workloads can negatively impact system performance as well as your title’s graphics workloads.
To properly synchronize access to resource data, you may need to insert cache and pipeline flushes in order to ensure correct operation. Similar to the graphics context with CSEnableAutomaticGpuFlush set to FALSE, the compute context does not insert any pipeline flushes between dispatches.
By default, just like the graphics context, the compute context uses the GPU L1 caches while processing dispatches. This behavior can be changed using the SetDispatchFlags method. In some cases, disabling the use of L1 caches via this API may provide a benefit over using and flushing L1 appropriately.
There are three APIs used to resolve resource access problems:
Note that cache flushes affect the entire cache; range-based cache flushes are not supported by the hardware. This may affect any GPU work executing on the graphics context at the same time.
For debugging purposes, debug and validated builds of the D3D user mode driver feature 3 debug-only dispatch flags that reintroduce automatic flushes following each dispatch. The 3 flags allow you to enable the following:
If your compute shader algorithm is not working correctly and you suspect a pipeline or cache flush may be missing somewhere, these debug flags are an easy way to test the algorithm with and without the flushes.
Similar to the D3D11.X graphics and DMA engine contexts, a pair of fence APIs are available for implementing cross-engine synchronization as well as CPU-engine synchronization.
As noted in the section “State Setting and Fast Resource Semantics” above, resource fences are not updated when resources are bound to the compute context. As a result, ID3D11DeviceX::IsResourcePending will not reflect resource pending status for work done on the compute context.
In addition to the familiar ID3D11ComputeContextX::CopyResource and ID3D11ComputeContextX::CopySubresourceRegion APIs, the compute context introduces raw graphics memory Copy and Fill APIs: ID3D11ComputeContextX::CopyMemoryToMemory and ID3D11ComputeContextX::FillMemoryWithValue. These APIs only take graphics memory that was allocated by calling VirtualAlloc with MEM_GRAPHICS or by calling XMemAlloc with XALLOC_MEMTYPE_GRAPHICS_*.
Raw memory copies and fills operate on byte alignment for source and destination, but the copy/fill size must be 4-byte aligned.
For performance analysis of compute workloads, the compute context has the following GPU timestamp writeback APIs: WriteTimestampToMemory and WriteTimestampToBuffer. These APIs write a 64-bit timestamp value to memory at the bottom of the compute pipeline. The destination address must be 8-byte aligned.
Timestamp values are the “GPU global” 100MHz clock frequency, which is synchronized across all GPU engines. To correlate GPU global timestamps with CPU clock ticks (rdtsc), call ID3D11DeviceX::GetTimestamps to retrieve an immediate, synchronized pair of timestamps: a GPU global timestamp (pGpuTimestamp), and a CPU rdtsc timestamp (pCpuRdtscTimestamp). Title code can synchronize rdtsc with QueryPerformanceCounter ticks if necessary.
When handling a suspend notification, work on the compute context must be suspended before suspending the graphics immediate context. Therefore, when ID3D11DeviceContextX::Suspend is called on the graphics immediate context, it will first lock the compute context (and the DMA engine contexts) as well, so that no APIs can be called until ID3D11DeviceContextX::Resume is called on the graphics immediate context.
The Xbox title operating system will ensure that all kicked-off compute workloads are complete before the system powers down the GPU. However, this check has a timeout, so if in-flight compute workloads take more than a few seconds to complete, then this will be detected as a GPU hang, and the system will reboot. Take care to limit the execution time of each individual compute workload, and for any workloads that are anticipated to take a relatively long time, consider implementing an early-out path that can triggered by a CPU write, for purposes of suspend handling.