D973 - A wave twice its subgroup runs two lanes an invocation

Status: decided Date: 2026-10-25 known_by: measured

A guest compute dispatch whose program treats its wave as a whole runs one invocation per lane wherever the host's subgroup carries the wave. A wave the subgroup's width runs one lane an invocation, as D884's ballot does. A wave of sixty-four on a subgroup of thirty-two runs two lanes an invocation. The invocation's place in its subgroup, p, holds lanes p and p + 32. Every vector instruction is emitted once for each of the two, from registers of its own. Lane reads, permutes, DPP forms, ds_bpermute, whole masks, execution-mask writes, the local data share and s_barrier all run this way. Only a scalar store or atomic still keeps the whole wave in one invocation (D880), because every invocation computing the scalar state would make it. lane_exact::lane_masks names the choice Halves.

The wave and the subgroup. The subgroup is the wave. A group of several waves is as many subgroups (SubgroupId), and each wave's first thread is its subgroup's number times the wave's width. The workgroup is the group's waves times thirty-two invocations. Every invocation computes the scalar registers as its wave would (D880). Its wave's scalar control flow is then uniform within its subgroup, and a group's waves keep theirs apart.

Masks. A comparison, a carry or the execution mask's seed makes each half of the mask by the subgroup's ballot of that half's lane's condition: the low half from each invocation's first lane, the high half from its second. A branch on execz or vccz, a saved mask and a lane count then read what the wave holds, the same in every invocation.

Another lane's value. Lane n is invocation n mod 32's first lane below thirty-two, and its second from there. A read of lane n shuffles both halves by n mod 32 and takes the one n names. Each shuffle runs in every invocation, so a read is exact wherever each lane asks for a different lane, as DPP and ds_bpermute do. v_readlane, v_readfirstlane, v_writelane, v_mbcnt, the permutes and the DPP forms find each of an invocation's lanes at run time, as D760's one lane does.

The local data share and barriers. The share is workgroup memory, which the group's waves share as D892 has them. A wave's own program order between its lanes' accesses is a subgroup barrier after each access. It is not the workgroup's, which the group's other waves would have to meet in control flow they do not share. Byte writes and ors merge by atomics, as D760's do. s_barrier is the workgroup's barrier between segments, where every invocation stands at the top level (D892).

Scratch. An invocation's array holds both its lanes' words, alternating: a lane's word w is at 2 * (w / 64), plus one for the second lane.

Kept as before. An inactive lane stores nothing (D880). Escapes (D950), the loop budget and its cut (D949), partial groups (D855) and device addressing (D964) are each computed per lane or per invocation as in the one-lane model. A wave the subgroup does not carry, where the subgroup is not known, still makes only its whole masks through workgroup memory in a group of one wave (D884).

Why: Grand Theft Auto V's full-screen ray traversal at 0x74005b7e8600 (2,171 instructions, groups of 8 x 8, one wave of sixty-four) read its wave's lanes and masks, so it ran its whole wave in one invocation. At [240, 135, 1] groups it lost the device at the 2 s watchdog in run 341. Replayed alone (a_captured_dispatch_replays_both_ways), at a twentieth of the grid ([12, 135, 1]), with both ways leaving the same output:

whole wave two lanes an invocation
SPIR-V words 643,542 586,702
pipeline build (first run's host time) 273 s 4.2 s
device time, first run 82.4 ms 3.6 ms
device time, warm 10.1-10.2 ms 3.6-4.0 ms

Over the whole grid, run two lanes an invocation, the dispatch takes 4.4-4.8 ms on the device. The whole wave was not replayed at that size, because it loses the device. Both ways note an escape: the capture holds only the guest pages the dispatch named, so some of what the run reached reads zero here, and the times are the replay's rather than the title's.

Rejected: