The content key of a compute stage leaves the base address and size of its
read-only guest buffers out, as a draw's key does. A template hit whose such
buffers moved is proved the way a draw's is (ShaderResources::MovedDispatchBuffers:
read-only, read in place when built, in reach, nothing pending over the new range)
and binds snapshots of the new ranges in a descriptor set of its own, kept with the
batch that records the dispatch. A refused proof builds the dispatch fresh, as
before. APS5_NO_DISPATCH_MOVE=1 turns the path off, APS5_DISPMOVE_MAX_KIB caps the
bytes snapshotted per dispatch.
A template whose storage image lies over memory that a depth surface covers is no
longer proved from stamps (fastRevalidate): cachedStorageTexture answers such a
binding with the plane's colour mirror, copied in from the depth image for every use,
and nothing stamps the appearance of a depth surface after the build. The full walk
decides, and a stale image can no longer be copied over the depth image.
Tests: Graphics.cpp (verdicts of the proof), execution DispatchMove (real device:
moved, exact, shortened, stored-over, own-output and written-range refusals) and
DispatchMoveDepth (a depth surface appearing over a template's plain storage image,
planes inside and outside the write watch, imports watched and unwatched).
On RDNA2 (gfx1031) f16/f64 denormal modes 0 and 2 flush the denominator
and numerator of v_div_fixup_f64 to a zero of the same sign and keep a
denormal quotient and result, as v_div_fixup_f32 does with the f32
field. The translation used them unflushed, so 0 / 2^-1074 gave 0
instead of the default NaN.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v_fma_mix_f32 used its f16 sources unflushed in every mode. On RDNA2
(gfx1031) f16 denormal modes 0 and 2 flush each f16 source to a zero
of its sign, whatever the f32 denormal field, so
v_fma_mix_f32 0, 0, 0x0001 (all f16) is 0 there instead of 2^-24.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
agc_driver_address_passes draws a triangle twice onto a guest block, with pixel shaders that global_load_dword or global_store_dword through s[2:3], and reads DrawPassTotals for the address-based draws:
- --readers: two draws reading through the table share one render pass;
- --store-after-read: a draw storing through the table after one reading through it begins its own pass;
- --read-sees-store: a draw reading through the table what the previous draw stored sees the store, in a pass of its own.
Taken from c44c9a670 (perf/agc-bounded-address-passes) without its --store-beside-reads case, which needs that commit's store bounds.
DrawPassTotals returns, since the process started, how many recorded
draws began a render pass of their own or continued the previous draw's,
and how many flushed the queued DCC key stores or label stores before
themselves, for address-based draws and for the others. A draw is
address-based when its resources hold the allocation registry lease,
whether they were built for it or found in the draw cache.
The totals are kept with the [draws] line's (APS5_PROFILE_DRAW), so a
test or a harness can tell whether an address-based draw kept the open
pass without reading the 10-second report.
pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock and
pthread_mutex_timedlock threw for a null abstime. FreeBSD's libthr
passes a null abstime on to the untimed kernel lock, so these calls
then wait like pthread_rwlock_rdlock, pthread_rwlock_wrlock and
pthread_mutex_lock.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both cases printed that the image storage table exceeds its capacity. They need different fixes, so each now has its own message with the set, the element's index in the set and the capacity. The debt entry now says that the index counts every table element before it in the set.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The non-split descriptor set's pool now counts the storage table binding's
descriptors, as the split path already did. A table storage element whose
minimum LOD clamps above the level it addresses is rejected per element, as
the direct path refuses it, and the element is looked up as written so a depth
surface under it is retired. The registration test checks again that
image_store_mip, image_store_pck and image_atomic_add through a table stay
unsupported operations that fault on use.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The shader memory registration test pinned the old refusal. It now checks
that the store's table is supported, does not fault, and binds every
element as written on the storage table binding, and that a store-only
table binds no sampled elements. The driver no longer requires a written
flag per table element, so bindings that carry none read as unwritten.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An IMAGE_STORE whose T# comes from a runtime-indexed image table used to
poison the whole table as an unsupported operation. Written, non-atomic
table images now get storage modes, a STORAGE_IMAGE table binding (260,
the sampled image table moves to 261 and keeps the variable count), and
storage views of tracked storage textures at the same element indices.
Every bound entry is marked written, so each dispatch writes back all of
the table's storage images. Atomic, packed, BY2/BY4 and dynamic-mip table
stores still fault. Tables with written entries are not reused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v_fma_f16, v_fmac_f16, v_fmamk_f16 and v_fmaak_f16 ignored the
FLOAT_MODE f16 denormal field. On RDNA2 (gfx1031) at f16 round to
nearest even, bit 6 clear flushes every denormal input to a zero of its
sign, and bit 7 clear flushes the result when the exact product-sum
rounded to 11 significant bits with an unbounded exponent is below
2^-14, the rule v_mul_f16 already follows. The f16 FMA path now flushes
its inputs and its round-to-odd result in those modes; the magnitude
test is shared with float16Binary as flushTinyF16Value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both threw std::invalid_argument for a null or uninitialized attribute.
FreeBSD's libthr returns EINVAL for one, as the other scePthread
attribute calls here already do. The clock id is still stored as
given.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mprotect already returns 0 for a zero length. The sceKernel entry
points passed it to DoMprotect, which throws for an empty range.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both ignored the FLOAT_MODE f16 denormal field. On RDNA2 (gfx1031)
v_cvt_pkrtz_f16_f32 flushes each denormal half result to its signed zero
when bit 7 of FLOAT_MODE is clear, and v_pack_b32_f16 flushes each
denormal half input when bit 6 is clear. Modes that keep them and
shaders without a known float mode are translated as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scePthreadAttrGetstack, Getstackaddr, Getguardsize, Setguardsize,
Setstack and Setstackaddr threw for a null attribute, a null pointer
argument or a stack below the 16 KiB minimum. FreeBSD's libthr returns
EINVAL for those, as the pthread_attr_* wrappers here and the other
scePthreadAttr calls already do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On RDNA2 (gfx1031) f16 denormal modes 0 and 2 flush the denominator and
numerator of v_div_fixup_f16 to a zero of the same sign and keep a
denormal quotient and result, as v_div_fixup_f32 does with the f32
field. The translation used them unflushed in every mode, so 0x0001 / 0
gave +infinity instead of the default NaN.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD's fstat looks the descriptor up before it copies the result
out, so a bad descriptor still fails with EBADF and an open one fails
with EFAULT when sb is null. fstat and sceKernelFstat threw
std::invalid_argument instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
setitimer refuses to arm the CPU interval timers and already reads and
disarms them as zero, so they are never armed. getitimer threw for them
instead of reporting that state, as FreeBSD's kern_getitimer does for a
timer that is not set.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v_cmp_X_f16 and v_cmpx_X_f16 compared denormal halves as their exact
values in every mode. On RDNA2 (gfx1031) f16 denormal modes 0 and 2
flush both inputs to a zero of the same sign first, so
v_cmp_eq_f16 0x0000, 0x0001 is true there. Both inputs now go through
flushF16InputValue; modes 1 and 3 and shaders without a known float
mode are translated as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD's getrusage rejects an unknown `who` with EINVAL before it
copies anything out and returns EFAULT when the result can't be
written. The HLE threw std::invalid_argument for both, which ends the
title.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD's libthr rejects a null sched_param and, under SCHED_FIFO or
SCHED_RR, a priority outside sched_get_priority_min..max with ENOTSUP,
and a policy outside SCHED_FIFO..SCHED_RR with ENOTSUP. The wrappers
returned EINVAL for the null parameter and stored any priority or
policy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD's read, write, pread and pwrite reject a byte count above
IOSIZE_MAX with EINVAL before they look up the descriptor. write threw
std::invalid_argument for a negative count, and the others passed the
count on to the host, which reports EFAULT on Linux.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On AMD's Windows driver 32.0.21045.5002 (RX 6700 XT), a product of two
values the driver knows fit in 16 bits, reduced to 16 bits and stored
with nothing else merged in, keeps all 32 bits of the product.
v_mul_lo_u16 and v_pk_mul_lo_u16 extracted 16-bit factors, so into a
register whose high half was zero they wrote 0xfffe0001 for
0xffff * 0xffff. A factor taken from the low half of its source is now
read as the low 24 bits of the register; the low 16 bits of the product
depend only on the low 16 bits of the factors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
read and write already fail with EFAULT for a null buffer and a
non-zero length and treat a zero-length null buffer as empty. The
sceKernel entry points threw std::invalid_argument for both. They now
do the same as read and write.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
recordDraw passed every address-based draw to the pass hazard tracker as
both an unknown reader and an unknown writer of guest memory. An unknown
writer conflicts with any earlier access in the pass, so no address-based
draw ever continued the previous draw's pass: each one ended the pass and
began a new one, which loads and stores the attachments again. At Resident
Evil Village's menu about half of all draws are address-based, and about
590 render passes begin for about 850 draws per frame.
A build whose programs only read through the BDA table now reports unknown
reads only. The tracker already orders unknown reads behind any recorded
range write in the pass and keeps later range writes behind them, so such
a draw continues a pass whose earlier draws wrote no guest ranges. Builds
with stores through the table, copied writes, or an address lease without
a table keep both flags. APS5_NO_ADDRESS_READ_PASSES=1 restores the old
behaviour.
Render targets reach guest memory only by explicit write-backs, so a read
through the table sees the same bytes whether or not the pass breaks.
Raghav314's #6947 makes the same change inside its GPU descriptor heap,
with the read-only fact from #6946's recompiler field; this one keeps it in
the driver, without a shader cache format change. The idea also comes from
fable-void's 5d57298425 (wip/new-gpu-path), which orders address-based
builds by their known access ranges; this takes the read-only half without
his run-time pre-pass bounds.
A program writes guest memory through the BDA table only with a store or
an atomic through a PhysicalStorageBuffer pointer. SpirvStoresThroughAddresses
scans a module for such an instruction: it collects the PhysicalStorageBuffer
pointer types, every value of those types, and the stores, copies and
atomics whose pointer operand is one of them. A module it can't read to its
end counts as storing.
An address-based build now records whether its programs only read through
the table (ShaderResources::BdaReadOnly). Each module is scanned once; the
answer is kept by its content hash.
HostStackLimits cached the queried stack range in one thread_local
std::pair. GCC gives that 16-byte object 16-byte alignment and stores it
with movaps. With CI's wild 0.10.0 link, libkernel.prx's PT_TLS segment
starts at an address that is 8 mod 16 (p_align 16), so the pair sits at
TLS offset 0x38. When libkernel.prx is loaded by dlopen its TLS block is
allocated dynamically and the pair ends up 8 bytes off a 16-byte boundary.
The first store then faults: guest_memory_lifetime's child, which loads
libkernel through its module and calls DoMprotect after static
destruction, died with SIGSEGV in HostStackLimits.
Two 8-byte thread-locals carry no 16-byte alignment assumption, so the
cache works with either linker.
Keep both sides in GuestSysmodule.cpp (the unknown id check and the Windows
unwind region checks) and in libSceSysmodule.md (Silent stubs and Functional).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>