D879 - A vertex fetch indexed by the vertex id binds only the records the draw can index

Status: decided Date: 2026-10-14

A draw buffer read through a descriptor with a record index (D733) binds from the descriptor's base to the end of the last record an access can reach. Until now that was the descriptor's last record, records - 1. It is now the last record the draw can index, when the translator shows where the index comes from. The range still starts at the descriptor's base, so neither the module nor the draw's places (D747) change.

The translator follows the vector registers of a primitive shader alongside the scalar ones it already traces (D733). At entry v5 holds the vertex id and v8 the instance id (D863). A v_mov_b32 copies what a register holds. A v_add_nc_u32 of an id and a scalar register that holds a traced word records the id plus that word: a user-data word, a constant, or a word loaded from a table. Any other write to a vector register forgets it, as do branches that disagree. An id written to the local data share and read back is not followed: the trace cannot show which address of the share holds it. An indexed access whose index register holds one of these marks its reads with the source. Every indexed access through the same descriptor must agree, or none of them bounds the range.

The draw finds the bound. A non-indexed draw's vertex ids run from its first vertex through its vertex count; a split draw's (D741, D787) run through its last chunk. Its instance ids run from zero through its instance count. The added word is resolved for the draw as the descriptor's words are. The last record is then the last id plus the word, capped at records - 1. An index the hardware would wrap past 32 bits keeps today's range. An indexed draw also keeps today's range: its vertex ids are the index buffer's values, which nothing here reads.

This narrows what D733 and D758 bind and nothing else: an access with no traced index source, a dispatch, a fragment shader and a raw or unchecked descriptor all bind what they did.

Why: MCUB00001 (Mesa Cube, oops-mesa) spent 76% of its run reading draw buffers, about 17,000 reads of 842 us each. Mesa points every vertex buffer descriptor from the draw's base to the end of a 4 MB upload ring. Each draw's range came out 4.19 MB, then 3.46, 2.74, 1.99 and so on, a new key every draw. The buffer cache never hit, and each draw copied and hashed megabytes to read a few kilobytes. Its fetch is v_add_nc_u32 v5, s13, v5 and then buffer_load_format_xyzw v[0:3], v5, s[20:23], 0 idxen: the vertex id plus the base vertex in user data. That is all the draw can index. Its culling shader's second fetch reads the id back from the local data share after compaction, so that one buffer keeps its whole range.

Rejected: