Fast semantics is a mode of operation applied to a graphics context that changes the management responsibility contract between runtime and application. When fast semantics is selected, an application assumes additional duties traditionally handled by driver and runtime in resource tracking, binding, and usage. In exchange, the application can realize significant performance gains by virtue of its superior insights into ultimate intent when interacting with contexts. The application will always have a more thorough understanding of its rendering domain than the runtime. Fast semantics provides additional opportunities to exploit this awareness, and it provides developers access to the best possible Direct3D 11.x graphics performance on the Xbox One platform.
This paper examines the motivations behind fast semantics, the benefits provided by its adoption, the scope of its application in Direct3D 11.x, the process required to enable it, and the functions that must be performed to meet with the obligations of the new contract.
Under Direct3D 11.x on Xbox One, the driver performs a number of tasks at relatively high frequency on behalf of the application. These tasks may be only indirectly related to driving the GPU, while still collectively imparting significant overhead to application execution. Some tasks serve to abstract the operation of the GPU and facilitate cross-platform development, but are overly elaborate for a console platform. Others are done to preserve the standards of the Direct3D 11 runtime architecture and to simplify the development process.
Taking the perspective that performance is paramount, it is advantageous given the fixed platform to eliminate superfluous processing wherever possible. With significant exposure of the low-level hardware available to applications through extensions in Direct3D 11.x, it becomes worthwhile to identify those tasks in the driver and runtime that may be more efficiently handled directly by the application with its greater domain knowledge. Fast semantics is an accounting of these tasks, and a directive the title can issue to the driver and runtime to avoid performing them. When fast semantics has been declared, an application agrees to perform the tasks instead in its own manner using available extensions (and in some cases it will even be able to avoid tasks entirely).
The main benefit to an application in assuming new responsibilities from the runtime is improved performance: the majority of the improvement will come on the CPU side, but even the GPU can benefit in some circumstances. Beyond performance, the added control afforded by fast semantics provides additional flexibility to an application. The application may choose how to go about satisfying the intent behind the tasks handed to it, and many solutions are often available. Adoption of the feature also serves as a useful path of migration to Direct3D 12.x, which inherently adopts a fast semantics approach to graphics development in many respects.
While fast semantics encompasses a number of diverse changes to the behavior of a context, it is not a piecemeal selection where an application may choose only a subset of the tasks for ownership transfer. Once fast semantics has been declared for a context, the application must ensure that it fulfills all obligations in the defined fast semantics space regarding the context. Failure to handle all tasks properly can lead to such problems as memory corruption, rendering errors, and GPU hangs. Some of these problems can be challenging to debug. Worse, some problems are timing dependent and may only manifest after seemingly unrelated changes to application code or the graphics driver and runtime (for example, the sudden appearance of graphical corruption after updating to a new graphics user-mode driver in the latest XDK or QFE, where the user-mode driver (UMD) contains no new changes beyond an optimized Draw function). Enabling fast semantics in an application is simple; correctly adapting to its presence is complex. This topic explains the steps that are necessary to successfully incorporate fast semantics into an application.
Fast semantics may be enabled at the granularity of a graphics context (ID3D11DeviceContextX) in Direct3D 11.x. Contexts created using fast semantics may be mixed with contexts created using legacy semantics in the same application. An immediate context is created with a fast semantics directive by including the D3D11_CREATE_DEVICE_IMMEDIATE_CONTEXT_FAST_SEMANTICS flag in one of the device creation functions (D3D11CreateDevice, D3D11CreateDeviceAndSwapChain, D3D11XCreateDeviceX, or D3D11XCreateDeviceXAndSwapChain1). Deferred contexts can be created with fast semantics using the D3D11_CREATE_DEFERRED_CONTEXT_FAST_SEMANTICS context creation flag (a device created with a fast semantics immediate context will still create legacy semantics deferred contexts by default).
A command list recorded by a deferred context that has been created with fast semantics will itself employ fast semantics upon execution. Fast semantics command lists may be executed on an immediate context created with legacy semantics, and both fast semantics and legacy semantics command lists may be executed on an immediate context created with fast semantics (though fast and legacy semantics command lists cannot be mixed within the scope of BeginCommandListExecution and EndCommandListExecution).
If an application chooses to create its immediate context and all deferred contexts using fast semantics, it is strongly recommended that it also specifies the D3DCOMPILE_NO_DEBUG_AND_ALL_FAST_SEMANTICS compile-time flag on retail builds. Adding #define D3DCOMPILE_NO_DEBUG_AND_ALL_FAST_SEMANTICS 1 before including d3d11_x.h will allow for additional compiler optimizations in IASetInputLayout, VSSetShader, and PSSetShader, where the APIs will transform into direct inline functions of very few instructions.
A natural follow-on inquiry might relate to flags available for the creation of non-graphics contexts with fast semantics policies. No such flags have been defined, because such contexts are always treated as being created under a fast semantics directive by the Direct3D 11.x driver and runtime. Even if the immediate context and all deferred contexts have been created with default legacy semantics, all compute (ID3D11ComputeContextX) and DMA engine (ID3D11DmaEngineContextX) contexts will still operate under fast semantics and require the application to handle the obligations required by the contract.
Beyond contexts, there are also a number of individual APIs defined as methods of a graphics context (with legacy semantics potential) that always operate as though fast semantics has been defined. The most prominent of these from ID3D11DeviceContextX are the SetFastResources and SetFastResourcesIntoBatch APIs. Others APIs include ReadGDS, WriteGDS, WriteQuery, ResetQuery, ConfigureQuery, SetPredicationFromQuery, LoadConstantRam, and StoreConstantRam. The DrawIndirect APIs also apply fast semantics synchronization requirements to the indirect arguments buffer supplied (for more information, see Multi-Draw calls).
One of the most significant areas of the fast semantics contract involves hazard tracking. Hazard tracking for legacy semantics takes several forms in the driver but generally involves the detection of, and response to, resource access transitions between hardware processors, processor engines, or engine blocks. Usage of a resource is tracked for every graphics context according to the binding and unbinding of the resource, or views that have been created on the resource. When a resource accessed through one view is re-bound and accessed through a different view, certain actions may be necessary to ensure that all pending work on the resource in the first view is completed before work using the second view can commence in the pipeline. Moreover, caching is often involved in the processing of a resource, requiring explicit flushing and invalidation of involved memory.
Even the resource contents themselves may require transformation based on which blocks of the GPU are acting upon them. Render targets and depth/stencil buffers typically employ compression as a bandwidth-saving mechanism and generally require decompression when transitioning from output merger destinations to read sources (such as shader resource views).
Fast semantics places the responsibility of hazard tracking exclusively on the application. All relevant resource usage transitions must be identified and appropriately handled. With fast semantics, the driver not only will fail to take actions to prepare a resource for shifting usage, it is no longer even aware of the usages themselves. Because of this, it no longer needs to store and update binding information for every type of resource or view slot at every shader stage. It does not have to spend often-wasted cycles detecting changes that might potentially merit a hazard response. The responses themselves can become more efficient as well, as the driver only maintains a very general knowledge of hazard conditions and, consequently, performs relatively heavy-handed responses to ensure proper synchronization (such as full wave evictions which can introduce unnecessary stalls).
Every non-dynamic resource maintains dedicated 64-bit fence values on a per-engine basis (graphics, compute, DMA, and so forth) to track read and write access. These fences are used to mark the last segment in which the resource was accessed by an engine, and they are used by legacy semantics to manage cross-engine synchronization (most commonly between GPU and CPU). A fence is updated when a resource is unbound from a legacy semantics immediate context, or when a command list referencing the resource has been executed.
While resources preserve fence storage under fast semantics (in the event that a legacy semantics deferred context accesses the resource in a mixed-semantics application), fences will never be updated by fast semantics contexts or command lists. In effect, a fence will reflect the last legacy semantics access to have occurred, if any, with fast semantics accesses going unrecorded.
APIs that rely on fences for synchronization between GPU and CPU will observe this fast semantics access behavior. The ID3D11DeviceContext::Map method, which blocks the CPU for certain usage and map types under legacy semantics until any outstanding GPU accesses have concluded, will not wait with fast semantics. It will instead return a resource pointer immediately to the CPU, allowing it to potentially read or write the resource data at the same time the GPU is still referencing it. The ID3D11DeviceX::IsResourcePending method will check only whether the given resource has pending work on legacy semantics contexts—a resource is never considered pending with fast semantics. The asynchronous ID3D11DeviceContext::GetData method will aggregate, process, and return query results under fast semantics even if the GPU has not yet finished all commands preceding the query ID3D11DeviceContext::End call.
In order to properly synchronize engines under fast semantics, it is left to the application to actively practice fence management. The InsertFence method will place a monotonically increasing fence into a command stream and advance the fence, while optionally kicking off the current segment into which the fence has been placed. The fence serves as an execution marker for a processor (typically GPU graphics CP ME, DMA, or compute engine) and can be tested or waited upon with a condition satisfied when the GPU has finished processing all commands prior to the fence. The IsFencePending method is used by the CPU to test for a crossed fence and returns a Boolean with the result. The InsertWaitOnFence method will halt execution of one GPU engine until another engine has arrived at a fence inserted into the latter’s command stream. Not only can fences be shared between different context types, it is a standard method for synchronizing their underlying engines when sharing resources between them (another option involves partial flush events). Applications may also choose to implement a completely custom fence system using the WriteValueBottomOfPipe and InsertWaitOnMemory methods.
One significant exception to the driver changes adopted by fast semantics worth noting is that they are employed fully only on the RETAIL and INSTRUMENTED flavors of the UMD. VALIDATED will retain a portion of legacy semantics logic in order to aid in catching violations of fast semantics compliance. In the case of fences, a resource will have fence updates performed on VALIDATED fast semantics and those fences will still be checked in many cases for access contention. For example, fast semantics VALIDATED will produce an API error when attempting to call Map on a resource that is still in use by the GPU, outputting a message of the form: Context or command list created with fast semantics attempting CPU read of resource still in use by Graphics engine (validated by tracked fence 0x123456789A). Explicit synchronization is required. It is strongly recommended that titles converting their engines to fast semantics run against the VALIDATED driver, at least initially, as a means of catching errors.
Although they can act as barriers to ensure that one set of draws or dispatches has completed with respect to another, fence-based waits alone are inadequate to completely synchronize engines with one another. Fences can only serve to guarantee that the back end of a context (the bottom of the engine’s pipeline) has processed all preceding commands. They do not ensure that memory writes from the completed execution have concluded in all cases, nor do they manage invalidation of relevant caches placed between memory and processor for subsequent reads.
It is therefore also necessary under fast semantics to give careful consideration to resource access transitions in the context of the underlying hardware. This requires a thorough understanding of the Xbox One GPU and the approaches required to accurately and optimally manage memory coherency. It is an involved enough subject that a separate white paper has been exclusively dedicated to the topic of synchronization and coherency on the platform. That white paper, Caches and Coherency on the Xbox One GPU, is an excellent resource for preparing to adopt fast semantics.
Proper hazard tracking, coherency in particular, is probably the most challenging obstacle to the adoption of fast semantics. It is certainly the one most prone to errors that are both difficult to detect and to diagnose, with potentially catastrophic consequences if not handled properly (such as data corruption, GPU hangs, and the like). Timing dependencies make the detection challenging, since a failure to correctly track and respond to a hazard may not immediately manifest in a tangible error. A sufficient initial delay between a producer and a consumer could naturally provide enough margin for the producer to complete before the consumer begins processing. Aggregate memory traffic in the system may also be enough to bring about necessary eviction of cache lines requiring invalidation. Errors may thus go unnoticed until later changes, unrelated to the hazards themselves (for example, optimized or reduced processing between producer and consumer; changes in memory transactions through the L2), cause them to appear. Such changes need not take place in the application itself—moving to a new XDK or QFE release can be enough to uncover failures.
Even when errors do manifest, debugging them can be a time-consuming process. Timing sensitivity can make failures unreliable to reproduce. Once an error is isolated to the point of observed failure, it can be challenging to track the failure to its source and identify the hazard triggering it.
Performance Investigator for Xbox (PIX) attempts to provide some assistance in catching and isolating failures in hazard tracking through a process based on the detection of non-determinism in GPU outputs. The fundamental concept is that a comparison of outputs in a capture between a normal playback run and a run in which full explicit synchronization has been applied (flushing all work, idling the pipeline, and invalidating all caches between every draw/dispatch) will produce differences when one or more hazard tracking failures are present.
When a capture file is first opened, a basic non-determinism test is run whereby the capture is played back twice: once without modification and once with synchronization applied. If the final swap buffer contains differences between the runs, a warning dialog is displayed. If true non-determinism from hazard tracking failures is suspected, a more extensive series of tests may be invoked through the Determinism pane in the UI.
Tests in the Determinism pane begin with a binary search for the range over which full synchronization must be applied to produce identical swap buffer results (as a relatively fast preliminary report on where to focus an investigation for hazard tracking failures). The range test is followed by a meticulous evaluation of the capture for individual failures, the identifiers of the draws causing them, the outputs affected, and the forms of synchronization found to correct the failures. Since this process evaluates the data from all output resources at every draw (not just the final swap buffer), and because it requires several passes at every step to reduce the chances of missing timing-dependent failures, it can take several hours to complete.
Though a useful aid in the development of hazard tracking in an application, the non-determinism features in PIX are not a complete solution. The detection of non-determinism suffers from the same timing sensitivities as the application, where a completion or coherency problem may be masked and not produce non-determinism in any output. Non-determinism may also appear in a capture for reasons other than hazard tracking failures. Other sources for non-determinism include EQAA, the use of append buffers for operations (where ordering is not guaranteed), and the use of the __XB_MemTime shader instruction. PIX will return E_PIX_DETERMINISM_ALTERNATE_SOURCE errors whenever it detects non-determinism, but the tool is unable to achieve equivalency with full synchronization applied between runs.
The render targets and depth buffers on Xbox One are commonly maintained in compressed forms as a means of reducing system bandwidth. Some blocks (color and depth) are capable of accessing a resource transparently in its compressed state, while access using all other blocks requires either a preliminary decompression of the resource or manually working with accessed data in their compressed form.
Under legacy semantics, the driver must examine all shader resource views and unordered access views when they are bound to a context. If the view’s base resource is a render target or depth/stencil buffer and the resource is found to be in a compressed state, the driver will perform an in-place decompression of it before binding it for shader access. It will perform the decompression even if the view is never actually accessed by a shader, and it decompresses the entire resource, even if only a sub-region is subsequently read or modified. Depth/stencil buffers are then left in a decompressed state and will not benefit from any further bandwidth savings on access through the output merger (until the next full Clear is performed). Render targets may recover multisampled compression savings over time but, given that they may resume such compression at OM binding, full decompression is performed at every OM-to-SRV transition (unless D3D11X_RESOURCE_MISC_NO_COLOR_EXPAND has been defined for the target).
Decompression also imposes overhead on legacy semantics in the form of command list split points. When an SRV or UAV of a render target or depth/stencil buffer is set on a deferred context, the compression state that the resource will assume at the time of recorded command list execution cannot be known. It is therefore necessary to insert a split point into the command list every time a potentially compressed view is bound to the context. During list execution, the split point will then evaluate resource state and perform the decompression if necessary. Beyond command list growth and increases in IB executions, the proliferation of decompression split points is particularly bad when placed within the scope of predication. In such cases each split point introduces a context roll, and a large enough number can impose a significant tax on GPU performance.
Fast semantics eliminates the overhead of testing all views for resource compression state, performing comprehensive decompression operations, and burdening command lists with unnecessary split points. It makes decompression an explicit task performed by the application and provides extensions that give a much greater degree of control over the decompression performed. Decompression requirements must be met whenever a compressible resource is processed by the GPU outside the CB and DB, not just when binding a view for shader access. For example, a resource may require decompression when used as source or destination in a CopySubresourceRegion call, or on a back buffer before a call to IDXGISwapChain::Present to propagate its initial clear color.
The current compression state of a resource can be queried on an immediate context using the GetResourceCompression or GetResourceCompressionX method. The DecompressResource or DecompressResourceX method can be used to limit the types of decompression performed (for instance, only gather the FMask in a compressed render target), restrict decompression to a sub-region of the resource, and even write the decompressed results to a second resource (preserving the original in its compressed state). The API needs to be called only before accessing a resource in its decompressed form. If an application prefers to access a compressed resource (color-compressed MSAA being the most common case), the decompression may be avoided entirely.
The VALIDATED driver will generate errors whenever an attempt is made to bind render targets or depth/stencil buffers as SRVs or UAVs when those resources have not yet been decompressed. Given a desire to access compressed resources, as well as cases where the relevant sub-regions of a resource have been decompressed, the SetResourceCompression or SetResourceCompressionX method can be used on an immediate context to update the current compression state assigned to a resource, overriding what has been maintained by the driver.
The swap chain presents another vector of access contention in the GPU. Back buffers on Xbox One are sequentially rotated into a front buffer position for direct display by the hardware. Further, the throttling of CPU processing by the driver when it gets far enough ahead of the presentation queue only occurs when an application calls IDXGISwapChain::Present on a back buffer that is already in the queue. It is therefore possible to issue GPU commands targeting a frame that is being actively scanned by the display scaler. Such behavior is desirable to allow GPU rendering of a frame to proceed until such time as the back buffer itself is accessed (which can often occur very late in the frame), and not introduce a stall until absolutely necessary.
The ability to access an active front buffer (as a back buffer wrapping around the swap chain) necessitates a guard to prevent writing to the buffer until the scaler has finished reading it and flipped to the next buffer in the chain. Such a back buffer may be read, but before any writes the GPU must perform a wait on a fence maintained by the presentation queue. Legacy semantics inserts such waits on every bind involving a back buffer render target view, not just the first binding in the frame. It also requires command list affinity with individual back buffers, such that a command list referencing the back buffer as a render target will not be patched by the driver. The command list must instead be rerecorded on subsequent frames for each buffer in the swap chain.
Fast semantics, following the general theme of hazard tracking, will perform no implicit waits on back buffers presented for display. The ID3D11DeviceContextX::InsertWaitOnPresent method must instead be called on the active back buffer before the first write to it in a frame (as a render target, UAV, CopyResource destination, and so forth). It needs to be called only once, but multiple calls will result only in potential loss in performance. Command list affinity with individual back buffers remains a requirement under fast semantics.
Legacy semantics contexts and command lists respect the traditional imperative to take references on objects bound to them. A context will increment the internal reference count of an object when it is bound (set as SRV, UAV, RTV, vertex buffer, index buffer, streamout buffer, input layout, shader, and so on) and decrement the count when the object is unbound. A command list will increment the reference count of each object referenced by its command stream one or more times, and it will release those references when the command list is itself destroyed. These modifications of count extend beyond bindings to include all references, including such things as indirect arguments buffers, predication queries, state objects, and CopyResource operands.
The motivation for taking references on objects is to prevent their premature destruction. An application may set an object on a context or record it into a command list, release its own reference to the object (having itself finished with the object), and trust that the runtime will keep the object alive until it is no longer in use. The fire-and-forget mentality is a convenient way to mark objects for destruction at time of last explicit usage.
The obvious overhead for contexts in modifying reference counts is the requirement to track resources at every bind site: a bound resource must be remembered in order to release its object reference when unbound. Deferred contexts and command lists similarly must maintain internal records of the resources that have been used with recorded commands, as well as a system to track how many references on each resource have been taken.
Less obvious overhead, made all the more significant due to its performance impact relative to its obscurity, is the cost of the reference count updates themselves when allocating command buffers in write-combined memory. In general, object reference counts are stored as single 32-bit values reflecting all outstanding references on an object. Objects may be used across any number of graphics contexts, command lists, and application-specific references over any number of threads; it is therefore necessary to perform atomic updates to the counts when modifying them. However, such atomic updates, implemented through InterlockedIncrement/InterlockedDecrement operations, have a highly detrimental impact on write-combining under the Xbox One platform’s memory coherency model.
Such updates involve lock operations on the memory in which a reference count resides. While reference counts are themselves stored in cached memory and abide by relevant storage alignment rules for efficient update, the lock operations are inherently prohibitive toward in-flight write-combined memory transactions. When a cached write involving a lock is encountered, all preceding writes to any write-combined allocation in the system must be committed to memory before the lock write is permitted to issue.
In this manner, reference count updates will significantly disrupt the performance of the write-combined command buffer construction happening concurrently on the context that is performing the count modifications, and which will frequently be occurring in parallel on other threads and deferred contexts. All write-combining efficiency suffers from premature flushing in the face of reference counting, and it is reasonable to expect a penalty of several hundred additional nanoseconds in CPU performance for each instance involving the replacement of one set object on a context with another when atomic reference count updates are employed.
Legacy semantics does attempt to provide partial relief from lock disruptions on high-frequency bind operations by maintaining per-context internal reference counting for a subset of resource types in order to avoid atomic updates. But this introduces another type of overhead in the form of deferred object destruction for resources bound to the pipeline at final release. When such an object is released to a public reference count of zero, it must be tracked in a deferred release list until the references to every created context (that is, every active binding) have gone away. The maintained release list must further be processed at each IDXGISwapChain::Present, where every object in the list has its context references checked to determine whether the object can finally be destroyed. Such testing is not insubstantial, especially if the list grows to a significant size due to a large number of fully released, but bound, resources.
Deferred context creation is extended under legacy semantics to transfer management of object lifetimes for the context and its created command lists from runtime to application through specification of the D3D11_CREATE_DEFERRED_CONTEXT_TITLE_MANAGED_COMMAND_LIST_OBJECT_LIFETIMES flag. Fast semantics takes the concept further to assume such behavior for both deferred and immediate contexts. With fast semantics, the driver and runtime will never modify the reference count of objects except through creation functions. Created objects will be assigned reference counts of one, created views will increment the reference count of the resources on which they have been created, and device children will increment the reference count of the device. Objects referenced through bindings or command list recordings will not experience any changes to their reference counts.
It is therefore incumbent upon an application using an object with a fast semantics context to ensure that the object is not released to a public reference count of zero when any references to that object remain. It should be completely unbound from all contexts before being fully released, no command lists referencing the object may be under active execution at time of release, and after release no command lists that reference the object may be executed. It is required only that no CPU usage of an object takes place after final release, not that the GPU has completed executing all commands that reference the object’s memory. This is a departure from Direct3D 12.x, which mandates that both CPU and GPU have no outstanding claims on the memory when an object is destroyed.
The VALIDATED driver will attempt to catch references to prematurely released objects by stamping the base memory of an object with a known signature before deallocation. If one of these signatures is encountered at time of object reference (such as when unbinding a resource from the pipeline), an error will be generated. However, such testing will be unsuccessful at catching failures when the memory is reclaimed by a new allocation and the signature is overwritten with new data.
With the transfer of object lifetime management to an application under fast semantics, one of the primary purposes for tracking pipeline bindings at the level of the runtime goes away. Fast semantics seizes the opportunity provided, and it eliminates the shadowing of bindings (resources, views, shaders, and so on) as well as state (blend, rasterizer, and depth/stencil) in order to save on tracking overhead. Consequently, fast semantics does not support Get APIs to retrieve bindings or state previously set on a context. Such APIs will output warnings and return null results to the caller. ID3DDeviceContextState objects are also unsupported, with no collected state information on which to base them. ID3D11Device1::CreateDeviceContextState and ID3D11DeviceContext1::SwapDeviceContextState will fail when called on a fast semantics context. Applications must shadow their own state in all cases where necessary.
The suspension and resumption of an application can cause issues for fast semantics, since it can no longer leverage the state shadow to restore the GPU back to a state established at time of Suspend. Instead, fast semantics will reset all context state to default when Resume is called. The reset has an effect equivalent to that which would result from a call to ClearState, and the application must take steps to explicitly restore previously established state, or discard the current frame and start from scratch at the next one. PIX is likewise unable to rely upon a runtime collection of state at the beginning of frame capture to use in playback initialization, so all state is also cleared after every Present.
While Present and Resume effect a ClearState under fast semantics, command list recording and execution remove the ClearState that would be performed by legacy semantics. A fast semantics deferred context will avoid the implicit recording of a full state reset at the start of every command list. This allows the application to choose whether it wishes to pay the CPU cost of recording, memory cost of storage, and GPU cost of execution by inserting its own explicit ClearState. If state is not explicitly reset, a command list will effectively inherit the GPU state established by the immediate context when the command list is executed.
After a command list is executed by an immediate context created with fast semantics, the GPU will not be cleared to the default Direct3D state, nor will it be restored to the state present on the immediate context before execution began. A fast semantics immediate context will leave the GPU in the state established by the command list (either fast or legacy semantics) at end of execution, and the immediate context will inherit said state. Beyond performance gains from state reset avoidance, the inheritance of state into and out of command lists under fast semantics provides additional design flexibility to applications.
Various operations on a resource can bring about modifications to the data stored for the object. With legacy semantics, such changes may propagate into other objects associated with the resource in the form of views. GPU updates may also be required if any views on the resource have active pipeline bindings. What may be a seemingly straightforward and immediate change to one view or resource can involve significant secondary work in identifying other affected views, replicating changes to them, determining the locations at which each are bound to the graphics pipeline, performing GPU updates where necessary, and generally handling thread contention on modifications to the resource base.
Fast semantics simplifies such updates in the driver by making direct alterations isolated exclusively to the resource or resource view involved in a given operation. Changes to a resource that may affect other views on that resource will only propagate to the views at such time as they are bound to the pipeline. If an associated view is already bound, the changes will not take effect until the next time it is re-bound. GPU updates reflecting the modifications will likewise only be performed at the time of binding, even when involving the view used in the operation originating the change. Notable operations that alter resources and require attention under fast semantics include the mapping and unmapping of dynamic usage resources, modifications to resources’ compression state, changes to resources’ minimum levels of detail, and the rotation of swap chains.
Resources with dynamic usage present a number of complications to legacy semantics, mostly arising from the ability to share them arbitrarily between any number of deferred contexts. The underlying memory of dynamic resources employs renaming, such that every write-discard mapped instance of the resource is assigned a new (and unique among all references queued in the pipeline) pointer to the resource data. These variable pointers alter the resources and resource views and require bind modifications to the GPU. Dynamic resources must maintain the set of multiple pointers, at least one for the immediate context and every deferred context using the resource. Any views on the resources similarly become context-specific.
Binding a dynamic resource to a legacy semantics deferred context is further problematic when the resource has not yet been mapped during the recording of a command list. Such cases require the command list to inherit the as-yet-unknown contents of the resource from the immediate context. To accomplish this, all pre-map bindings of all dynamic resources in command lists must be effectively patched or overridden with the pointers from the last Map or Unmap operation performed on the resources by the immediate context prior to command list execution. Split points are leveraged by the driver to perform these bind updates.
Fast semantics establishes a number of limitations on the usage of dynamic resources to alleviate the burdens they entail (whereas Direct3D 12.x eliminates dynamic usages entirely from the runtime). The application of pointer updates from resource renaming is managed more expressly by the requirement that any dynamic resources that are write-discard mapped while they (or views on them) are bound to the pipeline must be re-bound to the context on which the Map is invoked. The explicit re-bind operations will serve to refresh any views and direct the GPU to the new resource data.
When used with a deferred context, no dynamic resource or view on it may be bound during the recording of a command list until the resource has been mapped and populated. Executed command lists are therefore unable to inherit resource contents from an immediate context. After command list execution, a resource mapped and filled during the course of the execution and then used subsequently on the immediate context will have its resource contents revert to the data established before command list execution began (rebinding the view will have no effect).
Render target and depth/stencil resources maintain compression information pertaining to such things as whether they have been “fast” cleared, the values used to clear them, and whether they have been further compressed. The compression state of a resource and its related information may be updated by clear operations on an associated view, and decompress operations on the resource itself. ClearRenderTargetView, ClearDepthStencilView, and ClearView can apply compression to a given view’s resource when the resource and the clear operation meet certain criteria (compressible creation, full resource coverage, and so on). DecompressResource can leave a given resource in a decompressed state.
Because a view other than the one cleared may be bound to the output merger, and to avoid target shadowing in order to determine bind status, fast semantics updates views and compression state in the GPU only at the time of view binding. Whenever a render target or depth/stencil view is actively bound at the time its base resource is in-place decompressed or any view on the base resource is cleared, that view must be re-bound to the output merger.
A minimum level of detail (LOD) clamp can be applied to any texture resource created with the D3D11_RESOURCE_MISC_RESOURCE_CLAMP flag when the resource is used with legacy semantics contexts. Because the clamp is updated on the resource object itself using the ID3D11DeviceContext::SetResourceMinLOD method, either all views created on the object must in turn be updated with the new minimum LOD value at time of update, or every XXSetShaderResources call must test the given views for MISC_RESOURCE_CLAMP and look up and apply the current LODs from the resources where necessary. The former option necessitates the storage of a list of created views on every MISC_RESOURCE_CLAMP resource, guarded for thread safety whenever an associated view is created or destroyed. Moreover, the minimum LOD update to the resource and all views must also be atomic. The latter option imparts additional overhead to the high-frequency Set calls, since they can’t make direct copies of view descriptors.
Fast semantics transfers the minimum LOD clamp such that it becomes an attribute of an individual view and not of a base resource. Different clamps can simultaneously be set on different views of a resource using the ID3D11DeviceContextX::SetShaderResourceViewMinLOD method. The minimum LOD of the underlying resource will be used to establish the initial minimum LOD for the shader resource view at the time of its creation. Thereafter, any updates made to the resource using SetResourceMinLOD will be ignored by the created views used on fast semantics contexts in favor of SetShaderResourceViewMinLOD changes. Orthogonally, any SetShaderResourceViewMinLOD changes made to a shader resource view will be ignored in favor of SetResourceMinLOD base resource updates when that view is used with a legacy semantics context. Legacy semantics contexts derive the minimum LOD from the resource; fast semantics contexts derive the minimum LOD from the view.
When dealing with D3D11X_DESCRIPTOR_SHADER_RESOURCE_VIEW descriptors created through the ID3D11DeviceX::ComposeShaderResourceView method instead of by ID3D11ShaderResourceView objects, the minimum level of detail is established by the MinLevelOfDetail member of the D3D11X_TEX_ACCESS_CONTROL structure specified in the composition view description. Descriptors are considered to have an inherent affinity with fast semantics operation and so resource view descriptors ignore the base resource’s minimum LOD except when a default MinLevelOfDetail is requested during composition.
APIs that are also considered to always operate under the fast semantics contract for the minimum level of detail, even when used with legacy contexts, include ID3D11DeviceContextX::SetFastResources, SetFastResourcesIntoBatch, XXSetFastShaderResource, and XXSetPlacementShaderResource. Shader resource view objects set using these APIs will respect the minimum LOD set on the view through SetShaderResourceViewMinLOD.
In all cases for both legacy and fast semantics contexts, changes to a resource or view minimum LOD will not be applied in the sampling of the texture until such time as the view is re-bound to the sampled slot.
The sequential rotation of the back buffers in a swap chain involves a shifting of the identities of views created on those buffers with legacy semantics. All back-buffer views must essentially be reconstructed after the call to Present or DXGIPresentArray to reference the newly established back buffer for the frame (requiring lists of views to be maintained for every back buffer in a chain). After being updated, the views must then be re-bound to any set slots in the pipeline by the driver.
When the immediate context has been created with fast semantics, the rotation of views on a swap chain is left to the application. Render target views, shader resource views, and unordered access views on back buffers must all be updated after every Present. The recommended course of view rotation is to maintain a set of different views (object and/or descriptor) associated with every back buffer in the chain, and cycle through each set as the chain is traversed. Alternatively, the PlaceSwapChainView method is available to migrate a given view to a new back buffer.
As previously mentioned, care must also be taken when using swap chain views with deferred contexts, both legacy and fast semantics in nature. When a back buffer is referenced by a recorded command list, the reference in the list will not be patched on rotation of the swap chain. Executing the recorded command list after a Present will then cause the incorrect back buffer to be targeted. An application must either avoid the use of swap chain views with deferred contexts or dedicate a command list to each back buffer in a chain.
As with all other updates affecting a view under fast semantics, the application is expected to bind rotated swap chain views every frame. The views must be bound after presentation anyway, since the implicit fast semantics ClearState operation that takes place at the end of the Present removes all bindings from the pipeline.
A small number of other APIs have restricted usage with fast semantics contexts, with a couple being outright forbidden. The set of functions consists of ID3D11DeviceContext::IASetIndexBuffer, ID3D11DeviceContextX::IASetFastIndexBuffer, ID3D11DeviceContextX::IASetPlacementIndexBuffer, ID3D11DeviceContext::CSSetUnorderedAccessViews, ID3D11DeviceContext::CopyStructureCount, ID3D11DeviceContext::UpdateSubresource, and ID3D11DeviceContext1::UpdateSubresource1.
The binding of an index buffer to a fast semantics context through any of the Set APIs has a relatively small exception to it concerning the reset index value, which may yield conspicuous problems in input assembly. Rather than referencing a vertex, the primitive reset index (or “cut” index) is a special index value used to initiate the start of a new topology series in the input assembler. For example, a triangle strip may be terminated and a new strip begun by insertion of a reset value into the index stream.
With legacy semantics, the reset value is defined to be 0xFFFF for 16-bit index buffers (formatted as DXGI_FORMAT_R16_UINT), and 0xFFFFFFFF for 32-bit index buffers (formatted as DXGI_FORMAT_R32_UINT). Whenever an index buffer of one format type is replaced with an index buffer of the other format type, the reset index value (which can be natively set to any arbitrary 32-bit value on the Xbox One GPU) must be replaced. Such replacement is detrimental to GPU performance in that it involves a context roll. This less-than-obvious introduction of such context rolls should not even be necessary if the draws that reference the index buffer do not make use of the reset index.
To inhibit the generation of context rolls, and to expose full control over the value that may be used to reset the primitive topology series, fast semantics contexts will not modify the primitive reset index value when an index buffer is bound. The reset index is instead completely decoupled from the active index buffer. A reset value of 0xFFFFFFFF is used with a fast semantics context for all index buffers by default, regardless of their format type. The value may be explicitly changed using the ID3D11DeviceContextX::IASetPrimitiveResetIndex method, and it will persist until the next IASetPrimitiveResetIndex call.
Binding an unordered access view to a fast semantics context is limited if it has been created on a buffer with the D3D11_BUFFER_UAV_FLAG_APPEND or D3D11_BUFFER_UAV_FLAG_COUNTER flag. Append and counter buffer UAVs are restricted because they involve the binding of hidden counters associated with the views (tracking the count of records in the case of append) on legacy semantics. Without the required management of these associated counters, the CSSetUnorderedAccessViews method is able to perform direct and immediate descriptor binding for all views, with no additional branches and special case work necessary. Since UAV counter usage is ignored by fast semantics, transferring counter results is unnecessary. The CopyStructureCount method is disallowed as a result.
The use of append and counter operations is still possible under fast semantics through explicit management of the global data store, where the UAV counters are stored by default on the GPU. The SetGDSRange API extension may be used to partition GDS counter memory for the pixel shader and compute shader stages of graphics contexts, as well as for compute contexts. The WriteGDS extension is used to initialize counter memory with user-defined values. The ReadGDS extension will transfer counter results from GDS to an application-managed allocation in main memory.
The ability to perform updates to regions of a resource using data sourced from arbitrary CPU memory, as exposed by the ID3D11DeviceContext::UpdateSubresource and ID3D11DeviceContext1::UpdateSubresource1 methods, involves the use of staging allocations internal to the driver for legacy semantics. The source memory must be copied to a staging allocation for subsequent resource transfer in order to conclude all driver access to the memory before return from UpdateSubresource (without blocking the caller when the resource has one or more pending references by queued GPU commands).
Use of staging allocations and synchronization of updates with other GPU claims on resources requires hazard tracking. The driver must ensure that the GPU has finished previously submitted commands that access a resource before performing an update, and it must ensure that the update is completed before it can be permitted to reclaim the staging allocation for use by another operation. Since hazard tracking becomes the purview of the application under fast semantics, support for UpdateSubresource and UpdateSubresource1 cannot continue to be efficiently provided and its use is prohibited. As with hazard tracking, it is left to the title to prepare and manage its own staging resources as a means of performing general resource updates.
Fast semantics collectively comprises a diverse set of responsibilities that must be assumed by an application. When converting to fast semantics operation, it is useful to think first in general terms of ownership: anything involving hazard tracking, object lifetime management, state shadowing, dynamic usage, and re-binding after update is now the responsibility of the application. Once these areas have been classified, dedicated responses may be applied to each. As a reference for preparing the responses, a summary of the fast semantics contract can be found in the Fast Semantics topic. PIX and the VALIDATED driver should also be leveraged to ease the overall transition. Fast semantics on Direct3D 11.x is an involved undertaking, but the exercise is ultimately worthwhile in the performance gained, the increased freedom harvested, and the advancements secured in preparing for Direct3D 12.x.