Updated January 4, 2016
Over the past year, we have enabled a number of new graphics performance options on Xbox One. To help you take advantage of these options, our graphics experts have put together a list of quick architectural and performance-tuning tips. Each tip is marked with some combination of the following descriptors:
TUNING
These tips, which are related to enhancing your title’s performance, typically involve minimal code changes that you can usually iterate on quickly.
ARCHITECTURAL
The changes proposed in these tips relate to engine-design considerations. Some of these suggestions can involve a more substantial engineering effort.
CPU/GPU/MEMORY
These tips can help you either improve CPU or GPU performance or decrease memory footprint.
In this topic:
ARCHITECTURAL; TUNING; GPU
Manage your ESRAM carefully. Making the most efficient use of ESRAM is the #1 priority for efficient rendering on Xbox One. Use the ESRAM layout viewer in Performance Investigator for Xbox (PIX) to identify gaps where you might be able to take advantage of ESRAM during your frame.
It’s preferable to put read-modify-write targets, such as depth buffers, in ESRAM.
Surfaces don’t have to fit entirely in ESRAM. You can have surfaces spill off the end of ESRAM, or you can keep in main memory any portions of your ESRAM frame buffer that get little overdraw. See the Split Render Target code sample for an example of how to do this.
ARCHITECTURAL; CPU
In standard Direct3D on Xbox One, the use of dynamic resources (by specifying the D3D11_USAGE_DYNAMIC enumeration constant) is supported—see D3D11_USAGE Enumeration. However, because of the necessary hazard tracking and checks that the driver has to do, it is preferable to avoid using dynamic resources. Instead, perform your own manual hazard tracking using the InsertFence or WriteValueBottomOfPipe method in Direct3D.
One of the most common patterns is the use of MAP_DISCARD constant buffers. As an alternative, use a linear allocator or ring buffer to allocate memory for your constants each time and bind to the device using the SetPlacement* APIs.
ARCHITECTURAL; CPU
Use Fast Semantics for the best graphics CPU run-time performance on Xbox One. Switching to Fast Semantics nets even greater benefits over dynamic resource removal by avoiding many more run-time checks when binding resources to the driver. Additionally, the SetFastResources and SetFastResourcesFromBatch methods simply copy in the resource descriptors to the command stream, so the CPU sees gains from cache-prefetching those descriptors. You can also bind up to eight resources in a single SetFastResources call, or 4KB worth of resources in a single SetFastResourcesFromBatch call.
ARCHITECTURAL; CPU
Deferred Contexts allow you to build Command Lists asynchronously, meaning that you can use them to offload some of the CPU cost of your rendering onto other cores. After you have constructed your Command Lists, you can submit them for kick-off by calling the ID3D11DeviceContext::ExecuteCommandList Method on the Immediate Context.
TUNING; CPU
Note Not applicable when using Fast Semantics.
If you aren’t using Fast Semantics but you are using Command Lists, try the D3D11_CREATE_DEFERRED_CONTEXT_TITLE_MANAGED_COMMAND_LIST_OBJECT_LIFETIMES flag—see D3D11_CREATE_DEFERRED_CONTEXT_FLAG Enumeration. This flag reduces Command List record times and Command List release overhead. The Xbox One Software Development Kit (XDK) has compliant Direct3D reference-counting semantics on objects by default. This flag allows you to avoid that overhead if you’re managing Direct3D object lifetimes yourself.
TUNING; CPU
If you use Command Lists and call the ExecuteCommandList method multiple times in a row, then you will significantly enhance CPU performance by bracketing the sequence of executions with a single BeginCommandListExecution/EndCommandListExecution pair. This saves significant CPU performance because Direct3D doesn’t have to reset all of the API states on every ExecuteCommandList call. Instead, it does so only once at EndCommandListExecution time. Note that other Draw methods cannot be called while in a BeginCommandListExecution/EndCommandListExecution bracket.
ARCHITECTURAL; CPU
Draw bundles allow you to save CPU overhead in a way that is similar to how Command Buffers were used on Xbox 360. These are similar to Direct3D 11 Command Lists, but where Command Lists are intended for frequent recording, draw bundles are optimized for fast and frequent playback.
It is worth noting that some titles have chosen not to adopt draw bundles because of memory constraints.
For more information, see Draw Bundles, in this XDK documentation.
TUNING; CPU
We strongly recommend that everyone use the PresentImmediateThreshold parameter with calls to the Present method. This allows you to have VSync-locked swaps when you’re hitting your target frame rate and to have immediate swaps when you’re running behind your target frame rate. You can control what portion of the display is permitted to have tearing. This is essentially the same functionality as the D3DRS_PRESENTIMMEDIATETHRESHOLD parameter provided on Xbox 360. You will need to switch from calling the Present method to the new DXGIXPresentArray method to enable this functionality.
TUNING; CPU
Instead of stalling when you call Present, you can use this time for your title code. Use the DXGI_PRESENT_DO_NOT_WAIT flag and check to see whether the return value of Present is DXGI_ERROR_WAS_STILL_DRAWING.
See the Xfest 2014 presentation Frame Buffer Afterlife for more information.
TUNING; GPU
TriJuicing is the hardware sampler functionality equivalent to what was called D3DSAMP_TRILINEARTHRESHOLD on Xbox 360. Use the CreateSamplerStateX method and set the PerfMip field appropriately to make trilinear sampling cost the same as bilinear would when the sampling occurs close to a MIP map. The higher the value specified by PerfMip, the more it does bilinear fetches instead of trilinear ones. The easiest way to determine how to fill in all of the many fields of D3D11X_SAMPLER_DESC is to use the CreateSamplerState method to create the closest Direct3D equivalent, call the GetDescX method to get the corresponding D3D11X_SAMPLER_DESC structure, override the appropriate fields, and then call CreateSamplerStateX.
TUNING; GPU; MEMORY
We recommend avoiding HDR BCC, 64-bit and 128-bit render-target formats where possible. These read at half or quarter rate—and can cut bandwidth by half or a quarter—with only a small increase in quality as the payoff. There are cases where you might truly need full-range float, but in most cases using one of the following formats is much more preferable:
For shadow maps, we have seen good wins from using 16-bit depth-buffer formats instead of 32-bit.
The DXGI_FORMAT_D24_UNORM_S8_UINT format should never be used. The Xbox One hardware will actually allocate a separate 8-bit buffer for stencil and a 32-bit buffer for depth, truncating the bottom eight bits. If you are not using the stencil bits, use DXGI_FORMAT_D32_FLOAT. If you are using stencil bits, we recommend that you use the DXGI_FORMAT_D32_FLOAT_S8X24_UINT format.
TUNING; GPU
What’s your render color space? You can choose whether to render in sRGB or REC709 space, and if rendering in sRGB space, whether full or limited rendering is required. Rather than burning ALU on gamma and range conversion, get the hardware to do it for free. For more information, see the Frame Buffer Afterlife Xfest 2014 presentation or the SetGammaControl API topic in this XDK documentation.
ARCHITECTURAL; GPU
You can dispatch compute work on the GPU in parallel with other graphics work. You can create up to six compute contexts on the low-priority pipe (pipe 0) and three on the high-priority pipe (pipe 1). For more information, see “Asynchronous Compute Context” in the XDK topic Optimizing Monolithic Driver Performance.
TUNING; ARCHITECTURAL; GPU
If you’re having trouble hitting your target frame rate at your target resolution on the GPU, consider dynamically resizing your resolution based on GPU load. The DXGIXPresentArray method allows the scaler resolution settings to be synchronously changed frame-to-frame. Don’t forget that you can have your user interface on one overlay plane and your 3D content on another overlay plane with a different resolution and blended using per-pixel hardware. If your situation allows, you can also render one plane at a slower update rate by taking advantage of the UsePreviousBuffer field.
The quality of the scaler on Xbox One is even better than on Xbox 360—and Xbox 360 had a great scaler. The quality of the scale is biased in favor of horizontal scales, so consider rendering at lower horizontal resolutions such as 1600×1080.
ARCHITECTURAL; GPU
Reduce the size of your vertices as much as possible. Avoid uploading redundant vertex parameters, splitting your vertices into multiple streams if necessary. Reduce precision where it is not necessary and use parameter packing where possible.
Break your meshes into 64k vertex chunks and use a 16-bit index buffer. You can batch-draw multiple chunks in one draw call using the MultiDraw[Indexed][Instanced]Indirect method.
As part of your content pipeline, reorder your vertices for better cache locality. For more information about doing this in this post-Direct3D X era, see Chuck Walbourn’s blog post on DirectXMesh.
ARCHITECTURAL; GPU
Many post-process pixel shaders involve sampling in a radius around the current pixel. Such shaders include blurs, FXAA, SSAO, and so forth. We propose that you use a compute shader, skipping the draw pipeline altogether and leveraging local data-store (LDS) memory.
For any kind of sparse and jittered post-processing filter, consider using the Deinterleaved Texturing method proposed by NVIDIA.
TUNING; GPU
In PIX, you can see exactly what state change caused a context roll by looking at the D3D State and GPU State tabs. State highlighted in blue shows what state changed since the last draw, and state highlighted in red shows whether state was set redundantly.
If PIX indicates a lot of context rolls due to shader changes, try using the __XBOX_PRESERVE_ALL_INPUTS flag. This will cause the compiler to attempt to preserve all inputs to the pixel shader. It will also force pixel shaders to have the same input signature, thereby avoiding driver-context rolls.
Note that because the compiler has no understanding of the usage for unused inputs, it will fall back to the defaults for those inputs. There is no guarantee that an input is used the same way in other shaders, so a context roll could still be incurred, even with __XBOX_PRESERVE_ALL_INPUTS.
For more information, refer to Shaders and the FXC Shader Compiler, in this XDK documentation.
TUNING; GPU
Make sure that you are doing the depth test before the pixel shader wherever possible (“early–z”). Using alpha testing or using a pixel shader that writes SV_Depth will cause the depth test to happen after the pixel shader (“post–z”).
When early–z is not possible, there are specific circumstances in which the GPU will attempt to do a “re–z”, which is to say that it will attempt to use early–z to cull samples where possible and do post–z to test any samples that could not be culled by early–z. Because this is effectively doing twice the work, we recommend that it be done only for longer shaders.
To implicitly ensure that the GPU can perform re-z, do not write any of the following:
However, you can force a shader to use a specific mode using one of the following defines in your shader-compilation step:
TUNING; MEMORY
Compiling a shader with the __XBOX_FULL_PRECOMPILE_PROMISE flag ensures that the driver releases all the memory held to support future run-time compilation of the shader.
TUNING; CPU
If you use Command Lists and profile with the instrumented version of Direct3D, be sure to double-check performance occasionally with the retail version of the libraries. PIX currently adds measurable record-time overhead to Command Lists with instrumented builds.
TUNING; GPU
Try enhancing performance with the D3D11_CREATE_DEVICE_WRITE_COMBINED_COMMAND_BUFFERS and D3D11_CREATE_DEVICE_WRITE_COMBINED_DYNAMIC_BUFFERS flags. The improved cache effects could be a win for your title.
Use non-coherent GPU memory as much possible. Bandwidth for non-coherent combined reads and writes can reach 68GB/s, while the maximum bandwidth when using GPU cache-coherent memory is 30GB/s combined read/write. GPU coherent reads are capped at 24GB/s and writes are capped at 15GB/s.
Make sure that you obey all rules regarding usage of write-combined memory. Write memory in a linear ascending sequence with no gaps. Do not write to any address twice. Do not read from any address you have written to.
ARCHITECTURAL; CPU
You can eliminate massive CPU overhead by tuning your XMemAlloc allocator (or the default system XMemAllocDefault allocator) to never call the kernel to either allocate or free pages when your game is in its steady state. This is particularly important for any titles that use Command Lists, because those perform graphics memory allocations every time a Command List is recorded. If you’re using the default system XMemAllocDefault allocator, you can query statistics by calling the XMemGetAllocationStatistics function. Make sure that the dwAllocationCount and dwFreeCount fields of the XMEM_HEAP_STATISTICS Structure structure never increment during game play. If they do increment, you can call the XMemSetAllocationHysteresis function to tune the size hysteresis so that this doesn’t happen. The XMemPreallocateFreeSpace function can optionally be called to ensure that the heap has fully committed pages up to its hysteresis limit. So call XMemSetAllocationHysteresis to the value appropriate to your content, and XMemPreallocateFreeSpace at level load time, and you can thereby ensure that the heap will never go back to the kernel to ask for memory.
ARCHITECTURAL; GPU
Note Not applicable when using Fast Semantics.
If you have multiple Dispatch calls in a row, you might be able save significant GPU time by manually controlling hazard tracking. By default, the driver inserts GPU flushes in between every Dispatch call to avoid hazards with unordered access views (UAVs) where one Dispatch call might write to a UAV and the next Dispatch call immediately reads from the same UAV. Such a case requires a partial flush to be issued to the hardware to ensure proper coherency with the read-after-write. It’s too much overhead for the driver to properly track the true dependencies, so by default, it simply and conservatively always issues such a flush after every Dispatch. But if you know that you don’t have any such hazard problem with your Dispatch (that is, you know that the results of one Dispatch aren’t fed directly into the next one), you can stop the driver from doing this by calling CSEnableAutomaticGpuFlush(FALSE). This will inhibit all of the driver’s automatic partial flushes and allow you to have complete control. Doing so can save significant GPU time that is lost to partial pipeline idling, particularly when there are a large number of small dispatches. You can manually call the GpuSendPipelinedEvent method with the D3D11X_GPU_PIPELINED_EVENT_CS_PARTIAL_FLUSH flag if you need to insert synchronization, or you can call CSEnableAutomaticGpuFlush(TRUE) to resume the driver’s default behavior.
TUNING; CPU
If you run a CPU profile on the graphics driver (either UMD*.DLL or CIUMD_HYDRA.DLL) and see CGraphicsContext::SlowRebindBecauseResourceSetTwiceInSameStage anywhere, then that’s good news, because it is typically easy CPU time that you can reclaim. When the Map method is called on a dynamic resource that is currently bound into the context (for example, by a call to the VSSetConstantBuffers method), the driver must tell the hardware the new location of the resource. The driver uses a fast path when the resource is bound only once into any given stage. However, if a resource is bound more than once into any single stage, then this slow path is called. It’s extremely slow because it has to do an exhaustive search through all stages and all slots to find all occurrences of that resource. So if you see this function in your list, put a breakpoint on it—for example, umd_i.dll!CGraphicsContext::SlowRebindBecauseResourceSetTwiceInSameStage—and remove your duplicate binds.
Avoid setting blocks of resources to NULL. This was a common pattern on Direct3D 9, but it is very harmful to CPU performance on Xbox One. Only explicitly unbind resources when absolutely necessary.
ARCHITECTURAL; GPU
Consider using enhanced quality antialiasing (EQAA) for antialiasing with a small performance and memory overhead. See the Multisamplingcode sample for an example implementation.
ARCHITECTURAL; GPU
The render-target plane in the HTile buffer stores the minimum and maximum depths of the z–buffer for each 8×8 tile. Many tile-based lighting techniques require this information, so use the existing HTile buffer instead of performing the operation yourself. To do this, call the ID3D11DeviceContextX::DecompressResourceX Method with the D3D11X_DECOMPRESS_RESUMMARIZE_HTILE flag. Use the XG and Placement APIs to allocate HTile memory and bind it as a shader resource view.
For an example of how to decode the HTile, see the HiZDecodecode sample, available for download on XGD.
TUNING; GPU
For non-MSAA (multisample antialiasing) color targets, consider how the target will be cleared. If you find that you are seeing Fast Clear Eliminates for targets you don’t clear often, you can avoid these by using the D3D11X_RESOURCE_MISC_NO_COLOR_COMPRESSION flag. This also reduces memory footprint by removing the need for a CMASK plane. Clearing targets with this flag set will be much more expensive.
Similarly, for depth buffers you might see Depth Decompress events in PIX. The D3D11X_RESOURCE_MISC_NO_DEPTH_COMPRESSION flag disables depth-buffer compression, eliminating Depth Decompresses but increasing the cost of depth-buffer clears. However, using this flag means that you can clear the target yourself using Asynchronous Compute or DMA decompression.
GPU; ARCHITECTURAL (Extended System Reserve); TUNING (NUI Reserve)
It’s possible to reclaim some of a title’s GPU reservation by eliminating certain system features related to NUI and Extended System Reserve. By default, NUI takes 4.5%, and Extended System Reserve uses up to another 4%. In order to bring the title’s GPU allocation from 91.5% closer to 100%, the following conditions must be true:
It is worth noting that these conditions will not affect the availability of speech recognition and the Kinect color stream.
To reclaim the 4.5% NUI reserve, use the CoreApplication.DisableKinectGpuReservation Property. This should only be used during gameplay scenarios and not during menus or lobbies.
Reclaiming the Extended System Reserve is more involved. The system will still need to take GPU time to render UI elements from time to time, meaning that the title will need to relinquish that time or drop frames. Titles can react to this scenario by hooking into the CoreApplication.ResourceAvailabilityChanged Event.
For more information, see GPU Allocation, in this XDK documentation.
“Deinterleaved Texturing for Cache-Efficient Interleaved Sampling,” NVIDIA Corporation, March 2014. Accessed on December 31, 2014, at https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf.