On Windows, utimes, lutimes, sceKernelUtimes and futimes open a
FILE_WRITE_ATTRIBUTES handle and only then encode the times. A time the
host cannot represent returned EINVAL without calling SetFileTime, and
with NTFS last-access updates enabled closing that handle set the file's
access time to the current time, so a rejected call still changed the
file and guest_file_timestamps failed on "access time preserves
microseconds". A rejected time now marks the handle with 0xFFFFFFFF,
which SetFileTime documents as preventing operations on the handle from
updating the times, before the handle is closed. Error order is
unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The startup probe that lets shaders use the device's f32 denormal flush
instead of the emulated one only ran on devices that declare
shaderDenormFlushToZeroFloat32 with independent denormal behavior.
MoltenVK declares neither flushing nor preserving f32 denormals, so every
shader kept the emulated flush on every f32 add, multiply, fma, min, max
and comparison.
On MoltenVK the probe now runs too. Since the device declares no
DenormFlushToZero capability there, a module compiled for the hardware
flush declares no DenormFlushToZero execution mode and relies on the
native behavior the probe measured; when any probe result differs from
the emulated flush, shaders keep the emulation as before. Devices that
declare the capability are unchanged.
Raise the requested floating-point exceptions on the x87 and SSE units
and set errno as the console's libc.prx does.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Add _Flt, _Dbl and _Ldbl, the 80-byte tables behind Dinkumware's
float.h macros, with the values the console's libc.prx exports.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
FreeBSD 11.4 kern_mprotect masks the argument with VM_PROT_ALL instead of
refusing it (sys/vm/vm_mmap.c:613). sceKernelMprotect already does this
since #5770; the POSIX mprotect export still failed with EINVAL.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
HighestDrawIndex read each index through a branch on the index size and
kept the maximum in a std::optional, so neither clang nor GCC vectorized
it, and an indexed draw scans its whole index range when its range is not
kept (fewer than Recorder::KeptIndexRangeIndices indices, or a range
whose memory is not tracked). The scan now runs one loop per index type:
the maximum without restart, or with restart the maximum of the indices
masked by whether they are the restart index, plus an OR of those masks
for whether any index was not a restart index. Results are unchanged.
Add _Denorm, _Hugeval, _Nan, _Snan, _FDenorm, _FSnan, _LDenorm, _LInf,
_LNan and _LSnan with the 16 bytes the console's libc.prx exports.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The checks on a guest module's output name threw without an input
path, so the conversion's catch reported the main executable. They now
carry the module's path, as the guest image reader's errors do. The
loop's duplicate-export check gets the same change for consistency; it
cannot fire, because the reader rejects a duplicate first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
When the full read fails, readFile asked std::filesystem::exists with the
plain path, so on Windows a cache file past MAX_PATH was reported missing
instead of unreadable and could be saved over.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows the UCRT does not switch an r+, w+ or a+ stream from writing
to reading without fflush or fseek: fread after fwrite returned bytes of
the write buffer and the next flush wrote them over the file, and ungetc
failed on an update stream that was not already reading. On Linux glibc
does not switch either for ungetc, which pushes the byte back over the
pending write, drops the write and can corrupt the heap on the next read,
or for an fread of at least the buffer size, which drops the write and
reads from the old offset.
fread, fgetc, getc_unlocked, fgets, ungetc, ungetwc and fscanf now flush
pending output and mark the stream as reading first, as FreeBSD 11.4's
__srefill and __ungetc do, through _IO_switch_to_get_mode on glibc.
Writes directly after reads still go through the UCRT on Windows; the
gap is recorded in the libc technical debt.
The Windows gs_traps group grew to 21 relinker invocations and ran close to its 30 s CTest timeout on a loaded host. Split the implicit-memory cases into gs_traps_implicit so each group stays well under the limit, and share the per-case check between them.
PipelineCache::readHeader opened the cache file with a plain path while
readFile and the writer already use NativeCachePath_nid_no_patch. Past
MAX_PATH the header read failed, so every save saw the file as changed
and merged it again (#7452).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A queued store run records each group's plain stores and then its
occlusion counter dumps, and any store after a dump starts a new group,
so a label the title writes after its dumps lands after them. A title
that clears each query slot, dumps into it and then writes a flag after
it gets a group per slot that way: a barrier, then three fold dispatches
with a barrier before each.
The run's writes are now recorded in three parts where that keeps the
final bytes: the stores before the first dump and the stores that a
later dump overlaps (the clears of the slots), then every dump in one
fold, then the other stores in their order. A store after a dump that
no later dump overlaps still lands after every dump before it, and those
stores keep their order. A clear that only a later dump covers in part
must not overlap an earlier dump, and no clear may overlap a store it
moves ahead of; otherwise the run keeps its groups.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Six vkCreateShaderModule calls added to main since this branch's base
still passed their SPIR-V unchanged, so on AMD's Windows driver they
shared on-disk cache entries across the two pipeline library modes:
splitComputePipeline's recompiled compute programs, the multisample
readback modules, the dispatch-arguments module, the texture repack
module and the two performance overlay modules.
Graphics::CreateShaderModule now keys the generator word and creates the
module in one place. Every module the driver creates goes through it,
including the recompiled graphics and compute programs that copied the
key inline, so new call sites can't skip it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The replay rebuilds a captured compute pipeline to time it. With the
library feature off, production now flips the SPIR-V generator word
before vkCreateShaderModule. The replay creates the same module, so it
hits the same AMD driver cache entries as the run it reproduces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AMD's Windows driver compiles pipelines differently when
VK_EXT_graphics_pipeline_library is enabled on the device, but its
on-disk cache (%LOCALAPPDATA%\AMD\VkCache) does not key on it. A process
in one mode is then served pipelines compiled in the other, and their
writes never land (#4768, #5271).
With the library feature off, every module now flips bit 15 of the SPIR-V
generator word, which no driver interprets, so the two modes hash to
separate cache entries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A guest wave's lanes see each other's LDS writes without a barrier, so
the recompiler adds a subgroup-scope OpControlBarrier between an LDS
write and the next LDS access when the guest wave is one host subgroup
(WaveLdsScope). AMD's proprietary Windows driver 32.0.21042.62 (Vulkan
driver 2.0.353, "AMD proprietary shader compiler", Radeon 0x13c0)
compiles that barrier to s_barrier, as the pipeline's internal ISA
shows (26 s_barrier for the guest's 9 in a small LDS reduction). In a
workgroup of several waves, the waves that enter a branch holding LDS
accesses then execute extra s_barriers, which pair with the other
waves' barriers, and those waves read LDS before it is written. LLPC
compiles a subgroup-scope control barrier to a wave barrier instead.
PS5LM Plus (PPSA99581) answered with garbage on that driver: the first
compute dispatch of its first graph, an RMS norm with a 512-thread
wave32 LDS tree reduction, scaled every row except its first 32
elements by a partial sum, while NVIDIA matched a NumPy reference and
the inputs were identical.
On that driver the barrier is now a subgroup-scope OpMemoryBarrier,
which it compiles to no instruction. A 32-lane subgroup is one hardware
wave there, which runs its lanes in lockstep and issues its LDS
instructions in order; Vulkan does not guarantee that, so it is
recorded as debt. Other drivers keep the control barrier. The flag is
part of the shader cache key, and agc_shader_replay takes
--wave-lds-memory-barriers to replay a request the way that driver
compiles it.
agc_driver_lds_tree_reduction sums over eight wave32 waves the way the
guest does, with its last four steps inside one wave and no s_barrier
between them: before, thread 32 read 1936 instead of 32896 on the AMD
driver; it passes on AMD and NVIDIA now. agc_subgroup_barrier checks
the driver quirk and the emitted barriers on every runner. PS5LM Plus
now answers " Paris. It is the largest city in Europe ..." on the AMD
iGPU without flash attention, as on NVIDIA and the CPU.
Co-Authored-By: Claude Opus 5.5 <[email protected]>