D967 - A format load through a descriptor the program computes decodes it in place

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

A buffer access whose descriptor the program computes, in a device-addressed dispatch (D964), reads that descriptor as the module runs and carries it out whole: format loads included. Nothing of the descriptor is resolved on the host, so no table it was selected from is bound and no record count bounds it.

Placing. The access is placed and bounded as the instruction set's buffer address equation says, from the descriptor's own fields in the registers: its base, stride and record count, the out-of-bounds mode (OOB_SELECT, word 3 bits 29:28), swizzling (word 1 bit 31) with the index stride (word 3 bits 22:21) and the four-byte element GFX9 on fix, and adding the lane's id to the index (word 3 bit 23). A scratch access is placed by the same equation (D954), and the two share it. Swizzled and lane-indexed addressing were refused before as unmodelled (D147); here they are carried out. Out-of-bounds mode 3 checks a swizzled access's byte rather than its offset (Mesa src/amd/common/ac_descriptors.c:824-833). An untyped access places each word on its own, since a swizzled buffer's words lie apart.

Converting. A format load reads the descriptor's fourth word: the format code (bits 18:12) and the channel selects (bits 11:0). The module chooses, by the code, a layout among the formats a format load the module is translated for converts (D738): each component's width, x first, and how its bits become a number. Each component is extracted from the element at its bit and converted by the same arithmetic a translated format load uses, the field's width read rather than compiled in: unsigned and signed normalised, scaled, integers, IEEE halves, the packed 11- and 10-bit floats, and whole words. A normalised quotient over a maximum read at run time is refined once by its fused remainder, since the driver divides by a run-time value through a reciprocal a unit in the last place off (255 over 255 came back just under 1.0). A constant maximum it divides by exactly. Each channel is then what its select names: zero, one (1 for an integer format, 1.0 otherwise) or a component, zero outside the buffer as a translated load answers it. The element is read from the guest's bytes by device address, at any byte, its words looked up in at most two chunks.

What cannot be answered. A format code no translated load converts (including the invalid code), a reserved select, a select of a component the format lacks, and an element wider than four bytes in a swizzled buffer are escapes: the lane reads zero and the dispatch's result is refused (D950), as a refusal would have refused it before it ran. A null descriptor, all of whose selects are SQ_SEL_0, reads zeros and is none.

D966 afterwards. A table bound whole stays where it binds: its records are read once on the host, its accesses read bound pages rather than looking each address up, and a format load converts by the one format the module was translated for. Where it cannot - a table of more than 256 records, or one whose records disagree in format while the program converts by format - the dispatch is refused with the reason, and the command processor prepares it device-addressed (D964), where this decision carries the access out. No access is refused for a table's size.

Why: Grand Theft Auto V's ray-traversal programs at 0x74005b857200 and 0x74005b858800 select a buffer descriptor per lane from a table of 131072 records through waterfalls, and fetch through it with buffer_load_format_x: the first at 0xd7c, 0xe0c and 0xe8c through s[12:15], the second at 33 places. The table is too large to bind whole (D966), and device addressing carried no format, so the accesses were refused (D753, D964).

Rejected: