readGpuTiming runs after the batch's fence, so every timestamp the batch
wrote is already available. It still passed VK_QUERY_RESULT_WAIT_BIT, so a
timed range whose end stamp was never written (a BeginGpuTiming whose
EndGpuTiming did not run) made vkGetQueryPoolResults wait forever on the
reaping thread under APS5_PROFILE_GPU or a performance trace. Without WAIT_BIT the call returns
VK_NOT_READY and the batch's timings are dropped, as other failures are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu
Restore world-to-listener matrix construction instead of rejecting
non-default orientation, translation or flags.
- Build an orthonormal row-major matrix from position, orient_front
and orient_up; apply the right-handed axis flip for flag 1.
- Store the negated dotted translation in each matrix row.
- Validate unknown flags, invalid or non-finite input, short
orientation axes, and unrepresentable translation without changing
the output.
- Preserve finite velocity and positive finite sound speed.
- Update NGS2 geometry tests and the libSceNgs2 technical debt note.
AI assistance: yes
A DISPATCH_INDIRECT read on the GPU took its group counts unchecked. A count
buffer that a skipped producer never wrote (PPSA08427: 0xffffffff left by
skipped depth-plane passes) launched up to 2^32 groups and kept the GPU busy
until the title hung. The CPU path already refuses counts above
maxComputeWorkGroupCount.
- Recorder::RecordDispatchArguments copies the three counts from the import
into a 12-byte indirect buffer with a one-thread kernel
(DispatchArguments.comp), all three 0 when one exceeds the device limit, as
RecordMeshArguments does for mesh indirect records.
- VulkanDevice records the dispatch from that buffer; without the kernel the
import is read directly, as before.
- agc_driver_indirect_dispatch_limit: 0xffffffff groups run nothing, one group
still runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu
The two fixtures were converted for Windows only. They now run for
Linux, Windows and macOS. The syscall scan of a module with data after
its code fails on all three without the fix, and the loader path
without PT_INTERP fails on Windows and macOS, so the macOS code
analysis stays under test.
PS4 homebrew built with the OpenOrbis toolchain draws text through the
PS4 libSceFreeType: the font, keyboard, threading, system and SDL2
samples stop at start-up with "unresolved ELF import GNsiLzm4SH8"
(FT_Init_FreeType) and the five other functions they import.
FT_Init_FreeType, FT_New_Face, FT_Set_Pixel_Sizes, FT_Get_Char_Index,
FT_Load_Glyph and FT_Render_Glyph now call the bundled FreeType. The
guest reads FreeType's structures directly with the LP64 layout, where
FT_Long and FT_Pos are 64 bits, so the face, size, charmap and glyph
slot it receives are copies in that layout, updated after each call;
outline points are converted and written back before rendering. Guest
font paths are resolved and the file is read into memory. The face
index is masked as FreeType does before it is narrowed, and character
codes are truncated to 32 bits as FreeType does. The bundled FreeType's
own functions are kept out of the library's exports.
Vulkan cannot view a stencil aspect as UNORM, so an 8-bit UNORM view of
a stencil plane threw. Copy the stencil aspect through a buffer into an
R8_UNORM image of the surface's extent and sample that. The copy is kept
on the surface and refreshed when the surface was written since, or while
it is the bound depth target.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The dispatch look-ahead stopped before every DISPATCH_INDIRECT whose
arguments an earlier dispatch of the run may write, or whose earlier
dispatches have unknown writes. That stop protects a CPU read of the
arguments at the resolve, which only the thread-dimensions, misaligned and
APS5_NO_GPU_INDIRECT paths make. A GPU-read indirect dispatch never reads
its arguments on the CPU, so the stop left it and every dispatch behind it
to be captured when the worker reached them, after the GPU mutex waits
between dispatches. That is the late read the group capture exists to
avoid: by then the title had re-armed the gate and reused its table slots,
and the capture read descriptors the title had not written yet. The
look-ahead now stops only before an indirect dispatch whose arguments it
would read on the CPU; the path choice is shared with dispatchIndirect.
AgcDriverDispatchesAdoptedAhead_nid_postfix counts the dispatches adopted
from the look-ahead so a test can tell a gate-time capture from a late one.
Two tests check through it that both dispatches of a gated group are
captured at the gate: one whose writer stores the indirect arguments
through a registered binding, and one whose writer stores them through a
binding misaligned in its host import.
The runtime layout printed ../app0/sce_module/... under app0/ when the
output path contained . or .. components (./out.elf), because the module
paths are normal and the output directory was not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Four prepared-shader modes, run with the recompiler's failure memo off
so a second preparation would show as a second front-end build:
- compute: a shader that calls user-data pointers is resolved twice and
prepared once, then its dispatch links the callees and every lane
holds 3 * (5 + 64); a shader whose registration preparation failed
still throws its stored failure from the ABI call.
- pixel: a waterfall whose T# pointer is a phi of user data and a
ballot is logged once with its reason, resolved again without a
second preparation, and resolved for another state inside a
ShaderPreparationScope that commits. A draw of it fails at use with
the same reason, and the ABI call then throws the driver's failure.
- stages: the same chain in a vertex shader that registration leaves
alone, through ResolveShaderAbi inside a scope and through
ResolveGraphicsStagesAbi.
- rectangle: sceAgcDriverResolveGraphicsAbi with a pixel shader
deferred at registration and with one deferred at its ABI call.
sceAgcDriverResolveShaderAbi and sceAgcDriverResolveGraphicsStagesAbi
prepare every stage when the title creates its pipeline, and threw when
the recompiler could not: Resident Evil Village stops there on a pixel
shader whose T# pointer is a phi of user data and a ballot, and a
compute shader that calls user-data pointers stopped the same way
although its dispatch links the callees.
ResolveShaderAbi and PrepareGraphicsStages now catch the failure inside
their own preparation transaction, so the ShaderPreparationScope that
libSceAgc opens around the call still commits. The shader is logged
once, marked deferred like a shader deferred at registration, and the
ABI key is recorded, so a repeated call prepares nothing. The draw or
dispatch that uses the stage prepares it as before and fails there if
it cannot. ResolvePreparedGraphics skips the rectangles of a deferred
stage that has no artifact yet, which also lets a stage deferred at
registration through sceAgcDriverResolveGraphicsAbi.
Driver failures, ProcessShutdown and a failure stored by the shader's
registration preparation still propagate.
__std_find_trivial(_unsized), __std_count_trivial, __std_min/max/minmax_element,
__std_reverse_trivially_swappable, __std_reverse_copy_trivially_copyable and
__std_swap_ranges_trivially_swappable_noalias for 1-, 2-, 4- and 8-byte
elements, matching the libc.prx bundled with PPSA08427 including the rescan of
the first aligned block in the unsized find.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu
Since #5364, Draw/DrawRegisterKey.hpp includes Shaders/ShaderRegistry.hpp,
which reaches ShaderMemory.hpp and its "Recompiler.hpp". tests/Pm4.cpp
includes DrawRegisterKey.hpp, but agc_driver_pm4_tests only links
libSceAgcDriver, libc and libkernel, so the recompiler's include
directories are not on its path and Pm4.cpp no longer compiles. The test
targets that already reach ShaderMemory.hpp link shader_recompiler_agc_driver;
this one now does too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ConstantFolder::Fold visits each instruction once, in block order. A loop
header phi whose back-edge value is a later phi that folds to it in the
same pass, such as the header phi of an inner loop in which an S# is
copied out and back, is visited before that phi folds and stays a phi of
its one definition and itself. Later folds run only after read-lane or
masked-select elimination changed the program, and since #4150 the one
after masked-select elimination only runs in programs where a
PackedAncillary builtin still has live uses.
These phis reach resource tracking, which compares descriptor dwords as IR
values, so the phi of an S# and the S# itself become two samplers. Two
PPSA11386 (007 First Light) pixel shaders each had 21 direct samplers for
6 distinct S# values; at two heap slots each they exceed the 32 sampler
slots, and the shaders stopped at their first draw with "shader sampler
pairs exceed runtime heap capacity".
After its pass over the instructions, Fold now folds the phis of its
blocks again until a pass folds none. Each fold turns a phi into an
Identity, so the loop ends. Replayed on an older local branch with #4150's
restriction in place, the captured requests plan 6 samplers each, one per
distinct S#, and recompile. agc_shader_memory compiles an S# copied out
and back in three nested loops and sampled before and inside them, with
and without a masked write: one sampler and one sampled pair each, two of
each before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows the write watch is dropped in whole 64 KiB blocks
(WriteWatchCoverage::Exclude), but the unwatch hook was given the page
range of the import. An image with results pending in the same block as
an imported page, outside that page, lost the watch without its guest
bytes saved, and storing its results threw.
The hook now gets the span of blocks Exclude drops (ExcludedSpan, the same
block math), so every image with results pending in those blocks saves its
bytes first.