Managing Xbox Graphics Allocations

By: David Cook, Advanced Technology Group

Published: August 6, 2018

Abstract

The Xbox graphics driver makes various memory allocations on the title’s behalf. Developers can often reduce or eliminate some of these allocations. When every megabyte counts, it’s worthwhile to understand these opportunities and take advantage of them.

Preliminary notes

This whitepaper covers the D3D12 Xbox graphics driver. The D3D11 Xbox driver is no longer under active development, but some of the same advice given here still applies to it.

This whitepaper was written around the time of the July 2018 XDK. Exact allocation characteristics have changed in the past, and are likely to change in the future. They also differ among Validated, Instrumented and Release versions of the D3D12 driver.

PIX Memory captures are useful tools for tracking driver allocations. In the Events view, by filtering to the name “XMemAlloc (D3D)”, a developer can see all D3D allocations. In the Allocation Tree view, by filtering to the name of a D3D API, the user can enumerate all allocations made by that API.

Memory numbers cited below are for base Xbox One unless otherwise indicated. Some allocations are larger on Xbox One X, and these are called out explicitly.

This paper references the older shader compiler fxc.exe (shader model 5.x and below). It does not yet cover the newer shader compiler [x]dxc.exe (shader model 6.0+). Much of the information discussed here applies to both compilers though.

Startup allocations

A number of fixed-size allocations occur at title startup or at device creation time. The following sections describe these allocations.

GPU page tables

Unlike CPU page tables, which grow as needed, GPU page tables are allocated at title startup and never resized. The title can control page table size using D3DConfigureVirtualMemory. This function is declared only in d3d11_x.h, but is implemented by both D3D11 and D3D12. The parameter PageTableMemory4MBPageCount determines page table size in increments of 4 MB. The default value is 5, meaning 20 MB.

A single page table entry for a 64 KB page occupies 8 bytes. So 20 MB of page table memory allows for a virtual address space of 160 GB. A title which uses less virtual address space can reduce the page table allocation accordingly. Titles can infer their own page table consumption via the method described in the forum post “How do I measure how much Graphics Virtual Address space I'm using?” Developers should extensively test any change to page table size, as a page table overflow will likely cause a hard crash of the console.

Shader scratch memory

The driver reserves two 16 MB “scratch” buffers, for use by graphics shaders and compute shaders respectively. A shader uses scratch when the compiler finds it necessary to spill register data to external memory. Scratch usage generally results in poor performance, and titles should avoid it if at all possible.

Developers can test whether a shader uses scratch by searching for ScratchSize in the assembly output of the shader compiler (/Fc in fxc.exe). If a title never uses scratch in any shader, then it should set GraphicsScratchMemorySizeBytes and ComputeScratchMemorySizeBytes to 0 in the D3D12XBOX_CREATE_DEVICE_PARAMETERS input to D3D12XboxCreateDevice. The validated graphics driver will emit an error like the following one if it encounters a Pipeline State which requires scratch but no scratch buffer has been allocated.

D3D12 ERROR: [0x419758C8] ID3D12Device::CreateComputePipelineState: Shader scratch space requirement exceeds allocated space. The compute shader requires 3145728 bytes of compute scratch space, which exceeds the allocated amount of 0 bytes.

Note that async compute queues do not support scratch memory, regardless of the value of ComputeScratchMemorySizeBytes. Concurrent calls cannot share scratch memory, and allocating scratch for each queue would simply be too expensive.

Inter-stage memory

Geometry shader

The driver reserves two buffers of size 16 MB and 32 MB for use by off-chip geometry shaders (32 MB and 64 MB for Xbox One X). These buffers serve to transfer data between the hardware ES and GS shader stages and between the hardware GS and VS shader stages, respectively.

Note that geometry shader can be configured as “on-chip”, in which case it doesn’t use the off-chip buffers. In addition to conserving memory, on-chip geometry shader often performs better than off-chip. On-chip geometry shader requires both a shader compiler flag and a run-time flag:

If a title never uses geometry shaders, or if all geometry shaders are of the “on-chip” type, then it should set DisableGeometryShaderAllocations to TRUE in D3D12XboxCreateDevice. The validated driver emits the following error if the title attempts to use an off-chip geometry shader but the off-chip allocations have been disabled:

D3D12 ERROR: [0x39988B14] ID3D12Device::CreateGraphicsPipelineState: when D3D12XBOX_CREATE_DEVICE_PARAMETERS::DisableGeometryShaderAllocations is set to TRUE, offchip GeometryShaders are disabled and pipeline states using them can't be created.

Tessellation

The driver reserves a 4 MB buffer for use by off-chip tessellation shaders. This buffer is used to transfer data between the hardware HS and VS stages, when the data overflows the allocated space in on-chip LDS.

Note that by default, tessellation shaders are on-chip, and do not use the off-chip buffer. Off-chip tessellation shaders are generated only if the title requests them from the shader compiler (using steps analogous to those for on-chip geometry shader above).

If a title never uses off-chip tessellation, then it should set DisableTessellationShaderAllocations to TRUE in D3D12XboxCreateDevice. The validated driver will emit an error like the following one if it encounters a Pipeline State which uses off-chip tessellation and the tessellation buffer has not been allocated.

D3D12 ERROR: [0x75F1393A] ID3D12Device::CreateGraphicsPipelineState: when D3D12XBOX_CREATE_DEVICE_PARAMETERS::DisableTessellationShaderAllocations is set to TRUE, tesselation related shaders are disabled and pipeline states using them can't be created.

Command queue

The driver allocates 4 MB to accommodate the graphics command queue. This value is configurable using GraphicsCommandQueueRingSizeBytes. An appropriate command queue size depends on title usage and should be determined by experiment. An excessively small command queue may cause more frequent kickoffs or, in extreme cases, crashes or deadlocks.

Dynamic allocations

The driver makes various allocations when a title creates graphics objects, such as shaders or textures. The following sections describe these dynamic allocations.

Shader allocations

Shader binaries emitted by the shader compiler contain a number of different types of data. Some of this data must be retained at runtime, while other pieces can be stripped away to save memory. The minimum functional set of shader data is comprised of the shader instructions themselves plus metadata for initializing hardware state.

By default, the shader compiler compresses binaries on disk. Often, however, titles already have an asset compression scheme in place. If automatic shader compression interferes with the title’s own asset management system, then developers can disable it using the preprocessor directive __XBOX_DISABLE_SHADER_OBJECT_COMPRESSION. Compression only reduces disk footprint and I/O bandwidth. It does not affect the size of the shader in memory once a pipeline state has been created.

Shader stages

Shader binaries sometimes contain multiple independent compilations targeting different hardware shader stages. For instance, a vertex shader compiled for D3D stage vs_5_1 will contain three different compilations by default, for use with the hardware shader stages in bold below:

If the title knows it will never use the vertex shader in conjunction with geometry shader or tessellation, it should set the preprocessor directives __XBOX_DISABLE_PRECOMPILE_LS and __XBOX_DISABLE_PRECOMPILE_ES to 1 to omit the unused compilations.

Note that, typically, D3D12 titles release shader binaries once they have created all PSOs. In this case, any extra compilations do not occupy any extra memory during gameplay. However, omitting them still saves compile time and disk space.

Shader symbols

Titles can generate shader symbols using the /Zi argument to fxc.exe, or the D3DCOMPILE_DEBUG argument to D3DCompile*. Shader symbols are used by the Edit-and-Continue and Shader Debugger features of PIX, and are therefore indispensable for profiling and debugging. However, they also bloat the size of shader binaries unless titles strip them to disk. With fxc.exe, this can be accomplished using these steps:

  1. /Qstrip_debug removes the symbol information from the binary

  2. /Fd <filename.pdb> saves the symbol information to a file, and leaves a reference to that file in the binary

With d3dcompiler_xdk.lib, the steps are:

  1. Retrieve symbols using D3DGetBlobPart with D3D_BLOB_PDB.

  2. Save symbols to a file using D3DWriteBlobToFile.

  3. Call D3DStripShader with D3DCOMPILER_STRIP_DEBUG_INFO to remove symbols from the binary.

  4. [Optionally] Add a reference to the symbol location using D3DSetBlobPart with D3D_BLOB_XBOX_PDB_PATH.

The HLSLSymbols XDK sample demonstrates various different methods for stripping shader symbols via a custom compile tool.

If the D3D_BLOB_XBOX_PDB_PATH strings in step 4 above impose too high a memory cost, the title can omit these strings as well. In that case, in order to use PIX with shader symbols, developers should set a symbol path in PIX and name symbol files as <semantic-hash>.updb, where the semantic hash is as described in the next section. PIX will then be able to locate symbols without reading an explicit path from the binary.

Shader miscellaneous additional data

By default, shader binaries contain an intermediate representation of the compiled shader. This representation is DirectX Byte Code (DXBC) for shader model 5.x and earlier, and DirectX Intermediate Language (DXIL) for shader model 6.0 and later. On PC, the driver compiles the intermediate representation to hardware instructions at runtime. On Xbox, the shader compiler precompiles to hardware instructions at offline compile time. The intermediate representation is therefore no longer needed except in the case of runtime recompilation. Runtime recompilation can happen under several conditions, such as:

Most titles can arrange to never need runtime recompile. In this case, the title can disable retention of bytecode using the preprocessor directive __XBOX_DISABLE_DXBC. This directive works for both DXBC and DXIL.1

By default, the shader compiler inserts two short strings into the shader binary. These contain the shader filename and entrypoint, and they are used by tools, such as Xhit hang dump analysis. The title can strip these strings from the shader using the preprocessor directive __XBOX_DISABLE_SHADER_NAME_EMPLACEMENT.2

The shader compiler includes a hash value in each shader binary, called the “unique hash”. For most purposes, the “semantic hash” described in the next section supersedes the unique hash. The title can strip unique hashes using the preprocessor directive __XBOX_DISABLE_UNIQUE_HASH_EMPLACEMENT. Disabling shader name emplacement automatically also disables unique hash emplacement.

Each D3D12 shader binary contains a copy of the root signature. Because each root signature is generally shared by many shaders, it’s not necessary to keep a separate copy in each shader binary. Moreover, titles which create root signatures at runtime in C++ code do not need to store these root signatures on disk at all. Fxc.exe can extract the root signature as follows:

  1. Compile once per root signature with /extractrootsignature, which generates a root signature binary instead of a shader binary.

  2. Compile shaders with /Qstrip_rootsignature, which removes the root signature from the shader binary.

  3. At runtime, load the root signature binary and the shader binary separately.

With d3dcompiler_xdk.lib, the steps are:

  1. Retrieve symbols using D3DGetBlobPart with D3D_BLOB_ROOT_SIGNATURE.

  2. Save symbols to a file using D3DWriteBlobToFile.

  3. Call D3DStripShader with D3DCOMPILER_STRIP_ROOT_SIGNATURE to remove symbols from the binary.

  4. At runtime, load the root signature binary and the shader binary separately. Recreate the ID3D12RootSignature using ID3D12RootSignatureDeserializer.

The chart below shows file sizes emitted by fxc.exe when used on VertexShader.hlsl from the SimpleTriangle12 XDK sample, relative to the default compile options in the sample, which are:

/Zi /E"main" /Od /Fo"[sample-path]\VertexShader.cso" /T vs_5_1 /nologo

Except for the “No changes” row, all the experiments list the uncompressed output size, since that size represents runtime memory consumption. The “delta” column is relative to the uncompressed baseline case in bold. Note that this shader is not a typical one, because it is extremely short.

Compile options bytes delta
/D__XBOX_DISABLE_SHADER_OBJECT_COMPRESSION=1 79,148 0
No changes [compression left on] 30,720 -48,428
/D__XBOX_DISABLE_PRECOMPILE_LS=1 /D__XBOX_DISABLE_PRECOMPILE_ES=1 36,792 -42,356
[Remove /Zi] 18,124 -61,024
/Zi /Qstrip_debug3 18,208 -60,940
/Zi /Fd"$(OutDir)%(Filename).cso.pdb" 81,642 2,494
/Zi /Qstrip_debug /Fd"$(OutDir)%(Filename).cso.pdb" 18,654 -60,494
/D__XBOX_FULL_PRECOMPILE_PROMISE=1 79,148 0
/D__XBOX_DISABLE_DXBC=1 79,028 -120
/D__XBOX_DISABLE_SHADER_NAME_EMPLACEMENT=1 78,572 -576
/D__XBOX_DISABLE_UNIQUE_HASH_EMPLACEMENT=1 79,064 -84
/Qstrip_rootsignature 79,112 -36

PSO de-duplication

On D3D12.X, titles can build Pipeline State Objects (PSOs) offline, save them to disk with SerializeGraphicsPipelineStateX, and load them at runtime with DeserializeGraphicsPipelineStateX. The output of SerializeGraphicsPipelineStateX is a D3D12XBOX_SERIALIZE_GRAPHICS_PIPELINE_STATE, which consists of various ID3DBlob interfaces. The largest of these are generally shader blobs, and all the previous memory optimizations apply to them.

When building PSOs, either offline or at runtime, titles should “de-duplicate” the serializable ID3DBlobs, retaining only unique instances, some of which may be referenced by multiple PSOs. Duplicate data can arise in several ways, such as:

  1. The exact same HLSL is compiled multiple times with the same arguments.

  2. Two different shaders happen to compile to the same output.

  3. The same shader or root signature is used by several PSOs.

  4. Two different PSOs use different shaders but match in all other state.

The shader compiler generates a 64-bit “semantic hash” which the title can use for de-duplication of the shader portions of PSOs. The semantic hash is based on the shader compiler output, so two different shaders which compile to the same bytes will have the same hash. Developer tools can retrieve the semantic hash by calling D3DGetBlobPart with the Part parameter set to D3D_BLOB_XBOX_SHADER_HASH.

Derived PSOs and PSO re-use

Even when two PSOs are not identical, they are often similar. In that case, one of the two can often be “derived” from the other using CreateDerivedGraphicsPipelineStateX or CreateDerivedComputePipelineStateX. Derived PSOs explicitly share shader data4, and differ only in the pPacket and pMetaData blobs, which are comparatively small. The unique memory owned by a typical derived graphics pipeline state is currently 80 bytes in the Release driver.

In certain cases, even derived PSOs impose a significant burden. For instance, consider the following situations:

  1. A title uses many values of depth bias for many PSOs (implies changing the RasterizerState field of D3D12_GRAPHICS_PIPELINE_STATE_DESC).

  2. A title wants to support multiple MSAA modes for all PSOs (implies changing the SampleDesc field of D3D12_GRAPHICS_PIPELINE_STATE_DESC).

Cases like these require multiple pipeline states in standard D3D12, in order to support all compliant hardware. However, the Xbox GPU can sometimes sidestep these requirements.

In the case of depth bias, D3D12.X adds the Xbox extension RSSetDepthBiasX. This extension allows the calling code to override the bound PSO’s DepthBias (and some related fields) without creating or deriving a new PSO. Microsoft is open to adding other extensions of this nature when appropriate candidates can be identified.

In the case of MSAA, the Xbox GPU supports using literally the same PSO with multiple MSAA modes, even though this usage violates the D3D12 spec, and will trigger validation errors like this one:

D3D12 ERROR: [0xD8A68114] ID3D12GraphicsCommandList(Graphics)::DrawIndexedInstanced: Render target view 0 specifies a sample count of 2, which does not match the graphics pipeline state's sample count of 1. (RTV resource ptr=0x0000000300006D00 resource name="m_spRenderTargets[1][0]", pipeline state=0x000004001CA54260)

Multisampling140Debug12.exe has triggered a breakpoint.

In practice, the Xbox D3D12 driver bases hardware MSAA settings only on the bound render targets and depth buffer, rather than on the PSO, so the above mismatch is benign. On Xbox, therefore, it’s often worth trying mismatched PSOs, if the alternative would be a combinatorial explosion. Developers should work with their Microsoft representatives to determine when mismatched PSOs can function properly.

Command list memory

When the title creates a command list, the driver allocates some memory to handle resource barriers. The size of this allocation is controlled by the parameter ResourceBarrierBatchSize. The current default value of this parameter is 512 for a compute command list, and 1024 for a graphics command list. These defaults result in allocation sizes of 18,432 bytes for a compute command list, and 94,720 bytes for a graphics command list.

The title should set ResourceBarrierBatchSize to the largest value which will ever be used as NumBarriers in a ResourceBarrier call on the command list. It is common for this maximum value to be quite small in practice (perhaps less than 10), so the memory savings can be substantial. The validation layer does not currently check whether calls to ResourceBarrier exceed the ResourceBarrierBatchSize. Violations of this limit may cause crashes.

Resource memory

When the title calls CreateCommittedResource (or related APIs), the driver allocates memory for tracking structures. Take, as a typical example, the immutable texture from the SimpleTexture12 XDK sample. This resource provokes two allocations in the Release driver, one of 96 bytes, and one of 32 bytes. Of course, the driver also allocates memory for the texture data itself. However, the title can take responsibility for that allocation by calling CreatePlacedResource[X].

For simple resource types, which will only ever be referenced by shaders via descriptors, the tracking structures are unnecessary. D3D12.X offers the extension APIs CreatePlacedRawShaderResourceViewX and CreatePlacedRawUnorderedAccessViewX to create standalone descriptors without underlying ID3D12Resource objects. Note that standard D3D12 (without Xbox extensions) already allows creation of index buffers, vertex buffers, and constant buffers based purely on a GPU address, without either an underlying resource or a descriptor. Standard D3D12 also allows titles to bind buffers (but not textures) as root SRVs or root UAVs based on a standalone GPU address.

Resource alignment and padding

In many cases, the driver must pad D3D textures due to hardware requirements. Through awareness of padding patterns, titles can sometimes avoid or reduce memory usage.

It is difficult to give a complete reckoning of all resource padding. Resource allocations are determined by the XG library, which calls into a hardware emulation layer. This section attempts to list some of the most common pitfalls which lead to large or unexpected padding.

Alignment

The typical alignment requirement for immutable textures is only 256 bytes. This requirement arises from the fact that descriptors do not store the least-significant 8 bits of the resource address. Textures which support use as render targets, depth targets, or swap chain buffers require higher alignment (and other padding). The largest required texture alignment is 32 KB on Durango, and 128 KB on Scorpio.

2D tile modes

Static textures use “1D” tile modes by default. 1D tiling occurs at a “micro” level, consisting of rectangles which occupy a 64-byte cache line. This level does not incur much padding cost.

Render targets, depth stencil buffers, and swap chain buffers are generally assigned “2D” tile modes, which mean there is an additional “macro” level of rectangular texture tile. The macro tile size depends on bit depth of the resource format and on the usage of the resource. The following table lists some empirically determined tile sizes and alignments.

Type Format Tile dimensions Tile dimensions Tile dimensions Tile dimensions Alignment Alignment
    Xbox One Xbox One Xbox One X Xbox One X Xbox One Xbox One X
    X Y X Y    
Depth R16_TYPELESS 128 64 256 128 8192 32768
Depth R32_TYPELESS 128 64 256 128 8192 32768
Color R8_TYPELESS 128 64 128 128 8192 32768
Color R16_TYPELESS 64 64 128 256 8192 65536
Color R32_TYPELESS 64 32 128 128 8192 65536
Color R32G32_TYPELESS 64 32 128 128 16384 131072
Color R32G32B32A32_TYPELESS 64 32 128 64 32768 131072
Swap chain R8G8B8A8_TYPELESS 256 32 512 128 8192 65536

When choosing resource sizes, and particularly when choosing overall rendering resolution, titles should avoid choices which slightly exceed a multiple of tile size in either direction. A particularly unfortunate example is the common resolution of 1600x900 on base Xbox One:

The table below lists some relatively efficient resolutions with nearly the same pixel count as 1600x900. Note that the most efficient resolutions are exactly aligned with tile size, and therefore have an aspect ratio slightly different to 16:9.

Xbox One Resolution Pixel count 16:9? Swap chain padding 32-bit RT padding 32-bit DS padding
1536x864 1327104 Yes 0 (0.00%) 0 (0.00%) 245760 (3.70%)
1536x896 1376256 No 0 (0.00%) 0 (0.00%) 0 (0.00%)
1584x891 1411344 Yes 777152 (13.77%) 89024 (1.58%) 398000 (5.64%)
1600x900 1440000 Yes 891904 (15.48%) 179200 (3.11%) 787200 (10.93%)
1536x944 1449984 No 98304 (1.69%) 0 (0.00%) 0 (0.00%)
1664x896 1490944 No 458752 (5.62%) 0 (0.00%) 0 (0.00%)
1648x927 1527696 Yes 541120 (8.86%) 65984 (1.08%) 348720 (4.57%)
1792x864 1548288 No 0 (0.00%) 0 (0.00%) 0 (0.00%)
1920x1080 2073600 Yes 618496 (7.46%) 61440 (0.74%) 76800 (0.74%)

A title will incur padding both on the swap chain itself and on many intermediate buffers. On base Xbox One, the padding may waste both DRAM and ESRAM. The table below shows empirical counts of unique 2D-tiled surfaces which matched the frame resolution across several titles. The swap chains themselves are not counted here. (Note that some of these surfaces may alias the same memory, so the total padding may be less than anticipated.)

Title Resolution Render Targets Depth buffers Stencil buffers
A Swap chain size 3 2 1
  Internal frame size 26 4 1
B Swap chain size 2 2 2
  Internal frame size 19 2 2
C Swap chain size 26 3 3
D Swap chain size 4 0 0
  Internal frame size 15 4 4

Texture page padding

The default implementation of XMemAlloc routes requests straight through to VirtualAlloc for most textures. (The forum post “How XMemAllocDefault works internally” has more details.) VirtualAlloc operates at page granularity, so if the texture size is not an exact multiple of the page size, the allocation will have a fractional page of padding.

The waste due to padding is often small, but it can sometimes become significant[]{#_Toc520377488 .anchor}. That’s particularly true of swap chains, which must reside in 4 MB pages. The driver uses 4 MB pages only for swap chains, and for resources with alignment requirements greater than 64 KB (which exist only on Xbox One X).

A swap chain buffer of resolution of 1920x1080 on base Xbox One is already padded to 2048x1088, due to tile size of 256x32. (Also, the buffer uses a cmask metadata plane.) In total, the required memory for each swap chain buffer is slightly over 8 MB. Therefore, the allocator will commit three 4MB pages, leaving 3,645,440 bytes of page padding (on top of the padding from tile size). A hypothetical title with two 1080p swap chains of three buffers each would therefore lose over 20 MB to page padding. The recommended mitigation is for titles to override XMemAlloc and manually share pages among swap chain allocations.

Non-power-of-2 textures

Textures whose dimensions are not powers of 2 have no additional padding, beyond the types described previously, as long as they have only one mip level. However, if such textures have mipmaps, then the dimensions of each mip level are padded to power-of-2 size. A particularly bad case occurs when a title adds mipmaps to a frame-sized texture. If the title resolution is 1920x1080, then the base mip level is padded to 2048x2048, effectively doubling the size of the texture.

Array textures with mip chains have their slice count padded to a power of 2 for all mip levels. In particular, cubemaps with mips have their slice count padded from 6 to 8, increasing resource size by 33%. Large cubemap arrays make the problem even more acute. When allocating cubemap arrays with mipmaps, it is therefore best to choose cubemap count to be slightly less than one sixth of a power of 2.

The table below illustrates the effect of power-of-2 padding for a selection of 32-bpp texture types and dimensions. Notice that slightly exceeding a power of 2 in one dimension nearly doubles texture size when mipmaps are present.

Type Width Height Depth/slices Bytes (no mips) Bytes (full mips)
2D 64 64 1 16384 22528
2D 64 65 1 18432 38912
2D 64 66 1 18432 43008
2D 64 68 1 18432 44032
2D 64 72 1 18432 44288
2D 64 80 1 20480 44288
2D 64 96 1 24576 44288
2D 64 128 1 32768 44544
2D array 64 64 16 262144 360448
2D array 64 64 17 278528 720896
2D array 64 64 18 294912 720896
2D array 64 64 20 327680 720896
2D array 64 64 24 393216 720896
2D array 64 64 32 524288 720896
3D 64 64 16 262144 300288
3D 64 64 17 278528 562432
3D 64 64 18 294912 595200
3D 64 64 20 327680 599296
3D 64 64 24 393216 599808
3D 64 64 32 524288 600064

Resource aliasing

Xbox One titles often conserve memory by aliasing different resources on top of the same physical pages. This technique is observed most frequently in conjunction with ESRAM, but it remains useful, even for DRAM and even on Xbox One X. The presentation Advanced GPU Memory Management In Frostbite by Yuriy O’Donnell at XFest 2017 discusses how to achieve maximal memory re-use, given complete information about resource lifetimes. As reported in the presentation, by employing aliasing, one title dropped its overall transient resource footprint from 147 MB to 76 MB on base Xbox One.

Conclusion

As each console generation reaches maturity, developers struggle more and more to fit their titles into available memory. Graphics allocations form a substantial part of overall memory consumption. By carefully monitoring these allocations, titles can reduce them to the bare minimum.

  1. There is a related directive __XBOX_FULL_PRECOMPILE_PROMISE, which is only relevant to D3D11. This directive tells the driver to release the shader bytecode after a call to Create*Shader. It does not change the size of the binary on disk. 

  2. Also, leaving the shader name in the binary may defeat de-duplication of identical shaders with different names. 

  3. This row also reflects the file size which would result from stripping symbols to a file, and omitting the D3D_BLOB_XBOX_PDB_PATH from the shader binary. There is no fxc argument for that combination. Only a custom tool can accomplish it. 

  4. Titles should be careful not to free a base PSO before its derived PSO(s) for this reason.