Dispatch IDs and you
Do you frequently find yourself struggling to remember how the various built-in compute shader semantic values work by glancing at the image below?
You can find this image on the various
SV_* Win32 documentation pages such as this one.
It’s easy to get confused as to when to use which semantic value if you’re not used to it, so here’s how I remember things.
The main thing to remember is that both
SV_GroupIndex are poorly named, or at the very least, are inconsistently named.
After we examine the different compute shader semantics available, you’ll see what I mean (or you might not, this is just how I tend to think of things).
You’re also welcome to skip to the summary table below.
Compute shader semantics
There are three IDs we need to consider:
Recall that compute dispatches are specified in a two-level hierarchy (ignoring the notion of
WaveSize for now).
First, threads are grouped into, well, groups. The size of these groups is given by the three arguments to the
numthreads attribute attached to your compute shader entry point.
The groups themselves are launched on the host using a call to
Dispatch, which form the next layer of the hierarchy.
The number of threads in a group is independent of the number of groups dispatched.
So, if you had an entry function declaration like so:
[numthreads(5, 5, 1)] void main();
the group size would be 25 threads in total, spanning a 5x5x1 conceptual volume, where each cell in that volume is a separate thread or invocation.
The address of a thread within a group is given by an
SV_GroupThreadID value, which is a
uint3 as you might expect.
SV_GroupThreadID has nothing to do with how many groups are present in a given dispatch.
SV_DispatchThreadID is also a “thread ID” so we expect the value to identify a thread somehow.
Dispatch here gives us a hint. Whereas the
SV_GroupThreadID identifies a thread within a group scope,
SV_DispatchThreadID instead identifies a thread across the entire dispatch.
That is, if we invoked
Dispatch(3, 2, 4) with the entry point above, we would be dispatching a total of $3 \times 2\times 4 = 24$ groups total,
for a total of $24 * 25 = 600$ total threads, and the
SV_DispatchThreadID will thus take on 600 unique values while the shader executes.
As with threads within a group, groups within a dispatch are addressed using 3 coordinates, so combining the group address and the address of a thread
within a group, it should be clear why an
SV_DispatchThreadID has a
uint3 type, just like the
This brings us to the
SV_GroupID value. As you might expect, unlike the
SV_*ThreadID values which identify threads within some scope,
SV_GroupID identifies a group instead. Here, the scope is omitted because only one scope is really possible.
SV_GroupID value identifies a group within the dispatch. Given the example again of invoking
Dispatch(3, 2, 4), the
uint3 values spanning the 24-cell volume of groups (each containing a 5x5x1 set of threads). The reason I dislike the name
personally is because while the scope of the ID is unambiguous (it has to be a dispatch after all), I would have preferred
mirror the naming convention used for the other two values we’ve seen so far. If you have trouble remembering which is which, it might be helpful
to think of
SV_DispatchGroupID instead to recover the logical consistency with the other terms.
With those terms out of the way, we arrive at the last value to remember:
SV_GroupID, whose values are mapped to groups,
SV_GroupIndex values are mapped to threads. Confusing, right? Specifically,
SV_GroupIndex values map to “threads within a group”.
Given the main function above with attribute
numthreads(5, 5, 1),
SV_GroupID would take on values from 0 to 24 inclusive.
The naming of this value is particularly egregious, because while the index describes “threads”, the term “thread” doesn’t appear in the name itself.
Note that there is no
SV_DispatchIndex value which would hypothetically be a flattened thread index unique across the entire dispatch.
This value is sometimes useful in compute shaders, and you generally compute it yourself by flattening an
To summarize, here’s a table with the terms in the leftmost column, a description in the center column, and what I think they should have been named on the rightmost column. The suggested renamings are mainly here as a mnemonic device, and in some cases as discussed, the original names are fine.