Describes the advantages and disadvantages of Onchip and Offchip tessellation.
Tessellation on Xbox One performs the following actions, regardless of whether tessellation is performed on-chip or off-chip:
VS threads = (input control points) x (patches per threadgroup)
Threads are always packed at 64 threads per wave per shader stage.
HS threads = (output control points) x (patches per threadgroup)
Threads are always packed at 64 threads per wave per shader stage.
By default, the D3D11 driver always configures the GPU for onchip tessellation mode. In onchip tessellation mode, all of the data for input and output control points and per-patch constant is stored in LDS, including the constant factors. Because LDS is memory internal to the GPU, this means that no additional memory bandwidth is generated, and access to the data is guaranteed to be low-latency.
However, because this data is needed by the DS threads generated by tessellating the patches in a threadgroup, all such DS threads will need to run in the same CU that ran the VS and HS threads for the same threadgroup. This poses a severe limitation to the GPU’s ability to load-balance work among the 12 available CUs, especially when the tessellation factors are high.
With high tessellation factors, a single threadgroup might generate many waves of DS threads, and the LDS memory used will be blocked for any other use, including other threadgroups which might otherwise be able to run in the CU. (LDS is also used for threadgroup-local data in compute shaders, for PS interpolant values, and for onchip GS mode.)
Offchip tessellation is an option that enables the use of non-LDS memory with hull and domain shaders.
The GPU’s offchip tessellation mode is enabled by specifying the flag D3D11X_TESSELLATION_OFFCHIP. This mode uses the same amount of LDS as the onchip mode, but the HS also writes all output control points, tessellation factors, and per-patch constants to a memory buffer.
A heuristic is then used to run some DS waves in the same CU in onchip mode, to read the data from LDS, and to run other DS waves in other CUs, using offchip mode and reading the data from memory. This allows the GPU to release the threadgroup’s LDS memory before all DS waves are finished, or even launched, and it also allows the GPU to load-balance DS waves better across all CUs.
The advantage of doing tessellation off-chip is that LDS memory is freed-up for other graphics purposes. Whether a performance improvement is actually achieved by doing tessellation off-chip is very much dependent on the title code; a performance improvement might not be the result.
The heuristic used to run some DS waves in onchip mode and some in offchip mode is run per threadgroup as follows:
Hull and domain shaders to be used with offchip tessellation should be compiled with the following #define statement:
__XBOX_ENABLE_HSOFFCHIP
Setting this value causes the hull or domain shaders to be precompiled for offchip tessellation, and avoids runtime recompilation. The shaders must match, both offchip or both not.
To use offchip tessellation, call ID3D11DeviceContextX::HSSetTessellationParameters:
D3DINLINE void D3DAPI ID3D11DeviceContextX::HSSetTessellationParameters(
_In_opt_ const D3D11X_TESSELLATION_PARAMETERS* pTessellationParameters);
Calling ID3D11DeviceContextX::HSSetTessellationParameters will change the behavior of any future Draws that use tessellation. If pTessellationParameters is NULL, the driver will revert to its default behavior. If pTessellationParameters is non-NULL, it must point to a D3D11X_TESSELLATION_PARAMETERS structure that is instantiated and populated by the title. This optional structure has the following layout:
typedef struct D3D11X_TESSELLATION_PARAMETERS
{
UINT Size;
UINT Flags;
UINT PatchesPerThreadgroup;
float TfThreshold;
UINT DsWaveThreshold;
} D3D11X_TESSELLATION_PARAMETERS;
The fields of this structure are as follows:
| Field | Description |
|---|---|
| Size | The size of this structure, such as sizeof(D3D11X_TESSELLATION_PARAMETERS). |
| Flags | Flags to specify offchip tessellation and to enable each of the below values found in this structure by specifying “override”. A bitwise-OR’d combination of D3D11X_TESSELLATION_FLAGS enumeration constants. |
| PatchesPerThreadgroup | How many patches will be processed simultaneously in a compute unit in the GPU. Currently, the driver calculates a value for this that maximizes the number of patches that can be processed simultaneously. |
| TfThreshold | Tessellation Factor Threshold: This is the minimum average tessellation factor in a threadgroup that will cause the hull shader to dynamically switch modes from onchip to offchip. This field is only valid when Flags contains the flag D3D11X_TESSELLATION_OFFCHIP. Currently, the driver calculates a tessellation factor that will generate approximately 256 DS threads (4 waves). |
| DsWaveThreshold | Domain Shader Wave Threshold: The number of DS waves that will be launched by the tessellation unit using the onchip method, before switching DS processing to offchip mode, freeing LDS memory and starting to launch DS waves to other CUs. Currently, the driver hardcodes this to 4 waves. This field is only valid when Flags contains the flag D3D11X_TESSELLATION_OFFCHIP. |
The main fields in the optional D3D11X_TESSELLATION_PARAMETERS structure are PatchesPerThreadgroup, TfThreshold, and DsWaveThreshold. These parameter values are normally calculated by the driver, rather than using this structure. The driver calculates these values, taking into consideration factors including control point counts and sizes, with the overall goal of maximizing patch throughput.
Instead of using the driver’s calculated values for these parameters, the application can use the fields of the optional D3D11X_TESSELLATION_PARAMETERS structure in a call to ID3D11DeviceContextX::HSSetTessellationParameters, to explicitly specify the desired values, overriding the driver’s calculations. These fields can also be read after a call to ID3D11DeviceContextX::HSGetLastUsedTessellationParameters, to find out which values the driver calculated.
Even when you override the driver’s calculated parameter values, the driver might need to modify the values you specify in the D3D11X_TESSELLATION_PARAMETERS structure, to internally account for internal limitations of the GPU. To discover such modifications, call ID3D11DeviceContextX::HSGetLastUsedTessellationParameters.
The PatchesPerThreadgroup field specifies how many patches to put in each threadgroup. That’s the value calculated in the first step above.
The method ID3D11DeviceContextX::HSGetLastUsedTessellationParameters can be used to retrieve from the driver the values that the driver calculated, which can be the values it adjusted for the immediately preceding Draw call. Before calling HSGetLastUsedTessellationParameters, the caller must initialize the Size field to the size of the structure.
D3DINLINE void D3DAPI ID3D11DeviceContextX::HSGetLastUsedTessellationParameters(
_Inout_ D3D11X_TESSELLATION_PARAMETERS* pTessellationParameters);