D954 - A dispatch's scratch is each wave's own private memory

Status: decided Date: 2026-10-24 known_by: published

A compute dispatch whose program asks for scratch (COMPUTE_PGM_RSRC2.SCRATCH_EN) is carried out, not refused. Each wave holds COMPUTE_TMPRING_SIZE.WAVESIZE bytes, counted in 1 KiB units before GFX11 (Mesa src/amd/common/ac_gpu_info.c:1617, applied at src/amd/common/ac_shader_util.c:1078-1079). A dispatch that asks for scratch without the stream having written that register, or with a size of zero, is refused, and so is one asking for more than 256 KiB a wave. The size is compiled into the module, so it is part of the dispatch module's key.

The wave's offset into the scratch ring arrives in the scalar register after the user data and the workgroup ids (src/amd/vulkan/radv_shader_args.c:648-663: the workgroup ids, the group size, then scratch_offset). The group size (TG_SIZE_EN) is still refused, so the offset register is the one after the last enabled workgroup id.

No other wave and no later dispatch reads a wave's scratch, so where the ring lies in guest memory is never observed. The module holds scratch as a private array instead. The array is indexed by the access's byte address from the wave's scratch base, over four. The offset register reads zero, as every register the module seeds nothing into does, and the descriptor's base is not read. Where one invocation is the whole wave, the array is the wave's scratch word for word, laid out as the hardware lays it out. Where each invocation is one lane (D760, D880), the array holds that lane's own words: a word whose low bits name another lane is not reachable from this one.

A program reaches scratch two ways, and both are translated:

Dword accesses one to four wide are translated both ways, and the byte and half loads of the scratch segment and buffer_load_ushort. A dword must be aligned, and a byte or half must lie within its dword. An access the array does not hold reads zero, stores nothing, and is an escape, so the dispatch's result is refused after it as D746 refuses any escape.

A scratch access reaches no buffer and no window. The buffer tracer, the window placement and the dispatch's write spans leave it out, by the same test the translation makes. A program that writes the offset register is refused, since its scratch accesses would add something the module reads as zero.

Why: Grand Theft Auto V's in-world frame runs two full-screen compute passes with scratch, the programs at 0x74005b854a00 (240 by 135 groups) and 0x74005b857200 (32400 groups), and both were refused outright, so what they draw never landed.

Rejected: