Sets up an ordered append counter, enabling shader threads to append data in order of their launch. See Remarks for an example.
public:
void SetupOrderedAppendCounterX(
UINT CounterIndex,
UINT RingBufferAvailableSpace,
UINT GdsAddressOfTheCounterDwords,
D3D12XBOX_ORDERED_APPEND_SHADER_STAGE ShaderStage,
UINT Enable,
UINT ReservedSetTo0
)
CounterIndex
Type: UINT
[in] One of the eight hardware OA counters to associate with a Global Data Share (GDS) location.
RingBufferAvailableSpace
Type: UINT
[in] The size for the WRAP operation of the counter.
GdsAddressOfTheCounterDwords
Type: UINT
[in] The offset from the base 0 of GDS memory with which to associate the hardware OA counter.
ShaderStage
Type: D3D12XBOX_ORDERED_APPEND_SHADER_STAGE
[in] The shader stage, as a D3D12XBOX_ORDERED_APPEND_SHADER_STAGE enumeration constant. Only the Compute Shader is currently supported; don’t use the other enumeration constants.
Enable
Type: UINT
[in] Whether to enable this counter or to disable it.
ReservedSetTo0
Type: UINT
[in] This parameter is reserved; set it to zero.
Ordered Append is a hardware feature of Xbox One akin to D3D AppendBuffer. Ordered Append allows shader threads to append data in order of their launch. It works by associating a DWORD in GDS with one of the eight hardware Ordered Append counters. In the shader, this functionality is triggered by using the __XB_GdsOrderedCount intrinsic; __XB_GdsOrderedCount implements the ds_ordered_count instruction in the shader to support Ordered Append.
The following is an example of a shader that compacts write indices and performs a write into an append buffer using Ordered Append.
After running this sample, the values in “osb” buffer will be scrambled, and the values in “asb” buffer will be ordered.
AppendStructuredBuffer<uint>
asb : register(u1);
RWByteAddressBuffer osb : register(u0);
#define OA_WVGLAG_NONE 0
#define OA_WVFLAG_RELEASE 1
#define OA_WVFLAG_DONE 2
#define OA_COUNTER_OP_ADD 0
#define OA_COUNTER_OP_EXCHANGE 1
#define OA_COUNTER_OP_CONDXCHG32 2
#define OA_COUNTER_OP_WRAP 3
uint __ConstructOAFlags(
uint gdsOffsetInDwords,
uint oaWaveFlag,
uint oaCounterOp)
{
// Hidden parameter -- top 16 bits can specify byte offset into GDS
return (((gdsOffsetInDwords & 0x3f) << 0) |
((oaWaveFlag & 0x03) << 8) |
((oaCounterOp & 0x03) << 10)) << 16;
}
[RootSignature("DescriptorTable(UAV(u0, numDescriptors=2), visibility=SHADER_VISIBILITY_ALL)")]
[numthreads(8, 8, 1)]
void main(uint3 dtid : SV_DispatchThreadID)
{
uint v = (dtid.x & 0xffff) + ((dtid.y & 0xffff) << 16);
// Ordered append
// Find how many live threads there are
uint collapsedThreadIds = __XB_MBCNT64(uint2(0xffffffff, 0xffffffff));
uint numLiveThreads = __XB_S_BCNT1_U64(__XB_Ballot64(true));
// Always needs to be issued, to prevent GPU hang
// The CPU needs to configure the counter and call DispatchX
uint appendBase = __XB_GdsOrderedCount(
numLiveThreads,
__ConstructOAFlags(0x00, OA_WVFLAG_DONE, OA_COUNTER_OP_ADD));
asb.Append(v);
// It's ok to skip the write
if (numLiveThreads)
{
uint addr = 4 * (appendBase + collapsedThreadIds);
osb.Store(addr, v);
}
}
Below is the code-side setup required. To store the counter, this example uses the GDS slot with offset 0.
UINT zero = 0;
pCmdList->WriteGDSX(0, 1, &zero, D3D12XBOX_GDS_OPERATION_DEFAULT);
pCmdList->SetupOrderedAppendCounterX(0, 1024, 0, D3D12XBOX_ORDERED_APPEND_SHADER_STAGE_CS, 1, 0);
pCmdList->SetComputeRootSignature(m_pRS);
pCmdList->SetPipelineState(m_pPSO);
pCmdList->SetComputeRootDescriptorTable(0, m_uavDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
pCmdList->DispatchX(2, 1, 1, D3D12XBOX_DISPATCH_FLAG_FORCE_START_AT_000 |
D3D12XBOX_DISPATCH_FLAG_ENABLE_ORDERED_APPEND);
pCmdList->Close();
Header: Declared in d3d12_x.h.
Library: Use d3d12_x.lib.