The SOPK branch of the GCN decoder never set the literal for
s_setreg_imm32_b32, so the eight-byte instruction was re-encoded as one
word and its decode threw "literal operand word is out of the code span
bounds". The decoder now copies the literal for that opcode; the new
agc_gcn_decode test decodes the reported stream.
Co-authored-by: javierUDP <[email protected]>
Co-authored-by: Savvanis Spyros <[email protected]>
Each GCN instruction is re-encoded to its RDNA form and decoded by the
existing RDNA decoder, so the PS4 path shares the recompiler. Covers the
legacy VS stage (program at the VS slot, user SGPRs from s0, vertex and
instance index in v0-v3), fetch shaders linked as user-data calls, MIMG
(image_sample and friends), PS4 buffer descriptors (GCN DATA_FORMAT /
NUM_FORMAT, 44-bit base) and T# image descriptors (GCN tiling in word 7).
The GCN switch is keyed into the shader disk cache. PS5 behaviour is
unchanged: every PS4-only rule is gated on GcnGuestShaders().
#9433 merged red: main's push build 39725502001 failed
agc_driver_pipelined_indirect_draw with "the queue worker and the draw pipeline
deadlocked" (the failure predates #8807, whose diff only touches the frame dump
writer; the test's timing was never robust).
Cause: the Blocker queued on the draw pipeline captured `this`, a stack
object. The first test's Finish() can return before the blocker lambda has
run, so it ran during the second test through a dangling pointer, read a
`release` that was never set, and held the committer forever. Reproduced
under CPU starvation (12 processes on 2 cores, lavapipe, no udmabuf): ~17% of
runs failed, 0 of 72 hang after the fix.
The blocker state is now shared and the tests wait for the blocker to finish;
the fixed 500 ms sleeps wait for the pipeline items and the drain instead.
Co-Authored-By: Claude <[email protected]>
A T# loaded as two s_load_dwordx4 halves from a pointer table, where the
second half's offset is formed per lane (v_lshl_add_u32 key, 5, 16) before
v_readfirstlane and the first half's after it (readfirstlane(key) << 5),
was refused: the second half's +16 sat inside ReadFirstLane(Select(exec,
..., old), exec), so TryMakeTableColumn saw a different base and a zero
addend. Compare such offsets in a first-lane normal form (lane value, mask,
shift, addend), peeling selects the mask implies as the SRT walker already
does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An RX 6700 XT hands a geometry shader odd strip triangle i as
(i+1, i, i+2) with the last provoking vertex, which is the order the
translator already uses, and since #7374 last mode takes the flat
outputs of the program's primitives from their last index. Keep the
last-mode refusal for fans only, which reach the program as
(i+1, i+2, 0) while the hardware gives (0, i+1, i+2). TEKKEN DARK
RESURRECTION (PPSA31371) stops on such a strip draw.
Co-Authored-By: Claude <[email protected]>
The guest block's tail trim unmapped begin + bytes + UnitBytes - aligned bytes from the end of the block, which runs past the mapping it made. Trim begin + UnitBytes - aligned, as the sibling tests do.
On a device that cannot sample two-sample depth, as lavapipe, the DB
depth copy case now also creates a two-sample surface and expects the
copy's sampling refusal, so that refusal is covered where it applies.
Devices that sample two-sample depth skip it.
Co-authored-by: simonbbby <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ImportWatched kept a copy of every non-zero page of the range being imported, so that a page the
write watch reported during the import could be told from one that still held its bytes. The copy
had the range's footprint: a first import of a 0.5-3 GiB guest heap held that much host memory
while it ran.
The window now keeps a 64-bit digest per 4 KiB page (four xxHash64 lanes) and compares digests:
8 bytes per page, 1/512 of the range. Same decision for every page, same reads of guest memory.
CheckImportWindowFootprint imports a 64 MiB watched range and requires the heap held during the
import to stay under 1 MiB. It failed with "held 64 MiB" before this change.
The idle flush (VulkanDevice::StoreIdleResults) publishes the unit shadows over guest memory into
the host import and only submits the copies. publishUnits picks the units to store on the CPU,
and the slab -> import copy lands when the GPU runs it, so a guest store made between the two is
overwritten with the older shadow bytes. The memory-access hook waits through SyncThrough right
after its flush; the idle flush has no reader that waits.
FlushPending and PublishShadowsOnly now sync the recorder under the GPU mutex after an idle
publish that stored units. Other flush reasons are unchanged.
Test: agc_idle_publish_wait (a CPU store made after the idle flush returns survives; a label
flush's publish stays recorded).
DepthPlanePartialStore draws depth 0.5 into a 384x200 D32 Z_X plane (3x2
64 KiB tiles, the last row 72 texels high) over memory filled with 1.0 and
checks the plane's memory unit by unit:
- a read ending 3 KiB into the plane stores unit 0 alone (one store, one
unit, 64 KiB counted) and leaves the plane stale; the same read again
stores nothing;
- a read of the whole plane stores the other five units, the edge tiles
holding 128x72 depth values with their padding left as it was;
- a guest write into unit 4 after unit 0 was stored wins in unit 4 only:
a later whole read stores the four other stale units over 1.0, and a
LESS draw at 0.6 passes exactly over unit 4 (the plane-wide generation
synced every unit from memory instead);
- the retire of a surface the guest wrote into, through a view with two
levels, stores its stale units the guest did not write;
- a color target over unit 5 retires the surface after storing units 1-4;
- over unwatched memory a partial store leaves the other units on the GPU,
so a LESS draw at 0.6 passes nowhere.
TextureTiling checks that the 64KB Z_X equation of 4-byte elements reads
only x and y bits below 7, so each 64 KiB unit of a 3840x2160 D32 plane is
exactly one 128x128 tile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>