Sources whose streams are VP9 or Opus were dropped by IsStreamSupported, so
a WebM movie added with sceAvPlayerAddSource had no streams and the player
reported an error. The bundled FFmpeg already decodes both; their frames go
through the existing NV12 and PCM output paths.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BywPXoZACVi8ALV4JKvuPZ
Each image-table snapshot and resolution built node-based indexes and
dropped them at the end of the call: an unordered_map of distinct
descriptor words in TableSnapshotter, and in ResolveImageTables a map of
poison records, a set of expected poison codes, a map of bound elements
and an unordered_map of records per table. A table of N distinct entries
cost several allocations per entry on every dispatch.
The words, element and poison indexes are now open-addressing tables over
the vectors they index, and the per-table records a dense array by code.
Their slots live in per-thread scratch stamped per use, so a call reuses
the previous call's storage without clearing it; scratch above 2 MiB is
released after the call. Expected poison codes are a bit vector.
AI-assisted: yes
Adds MovedForwardedWordReachesStageDataHit to the draw cache relocation
test (agc_driver_draw_cache_relocation).
The fragment shader loads a pointer from the table its user words
s[0:1] point to, loads a float through that pointer and exports it as
red and alpha. It also exports bit 4 of user word s0 as green and blue,
so the compiled stage forwards s0 into its shader data while the table
read keeps s0 relocatable. The float is read through the loaded pointer,
not through a user word, so it is a stage data position and not part of
the relocation alias.
Six draws through tables at 32-byte aligned addresses (bit 4 clear) that
hold the same pointer teach the relocation and must reuse the cached
draw at least once; their pixels must be 255,0,0,255. The float is then
changed in place and one draw goes through a table at a 16-byte offset
(bit 4 set). It must be one lookup, one hit and one stage data hit: a
relocated entry whose fragment stage validates as EqualData. Every
written pixel must be 64,255,255,64: the refreshed data word and the
moved user word. Before 800ee777b the stage data copy kept the donor's
shader data, and the test fails with:
AGC graphics: with graphics pipeline libraries: a relocated stage data
hit did not draw with the moved user word: pixel 0 is 64,0,0,64,
expected 64,255,255,64
DrawFrom takes the fragment shader as a parameter. The test binary
runs in 1.5 s. Tested on Windows 11 (MinGW GCC 15.2) on an NVIDIA
GeForce RTX 3050 Laptop GPU through Vulkan; agc_driver_draw_cache_reuse
and its _off, _verify and _inline variants still pass.
A draw served through a relocated entry can also be a stage data hit on
the same stage. lookupDraw builds the stage data result first:
patchDataWords copies the variant's compiled result and refreshes the
flattened SRT data words. Only afterwards does draw apply the relocation's
shaderDataPatches, and only to matched[i]->compiled. programResults[i]
prefers stageData[i].compiled, so the stage was drawn with the push
constants and ShaderData user-word slots of the donor draw. Pointers the
shader reads at run time, which relocation had moved, kept the donor's
old values.
After applying a variant's shaderDataPatches, the stage data result of
the same stage is now rebuilt from the patched variant with the words the
lookup refreshed, so it carries both the new user words and the refreshed
data words. Variants without pending patches are skipped as before.
Found by reading the hit path: patchDataWords copies *variant.compiled
(VariantValidation.cpp) before applyShaderDataPatches replaces it
(DrawRelocate.cpp), and Draw.cpp selects the stage data copy when one
exists. Builds on Windows (MinGW GCC 15.2). A regression test that drives
a relocated stage data hit is still to be added.
sceVideodec2Decode copied decoder.parameterSets into the packet before
the access unit, also when no parameter sets were pending. An empty
std::vector's data() may be null, and memcpy with a null source is
undefined even for zero bytes. A UBSan build reports it in
guest_videodec2 and guest_vdecsw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A new press refreshed the latch timestamp for every press still latched, so an
older press could survive a later one. Drop the latch first when it has expired,
and add guest tests for the cleared, expired and newer-press cases.
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
Narrowing a table column read its key buffer one key at a time, with a
page lookup and a single-word read per key; PPSA05684 scans about 20,000
keys per table snapshot. The scan now checks each page once and reads the
keys with the bulk reader, and maps a key to its record without dividing
when the multiplication cannot wrap.
Gameplay, image-table time per snapshot: about 6.5 ms before, 5 ms after.
AI-assisted: yes
After #7147 the CPU-side tests pass under AddressSanitizer, but 18 of
the 518 AGC driver and shader tests still failed there with
ASAN_OPTIONS=detect_leaks=0:
- Guest memory is raw memory, not a C++ object. ReadRawShader reads a
shader's first 256 bytes before it knows the program's length, and
ShaderMemory copies the 64-byte block around each word it is asked
for. The tests place shaders and descriptors in small globals and
locals, so those reads ran past them into ASan's redzones (13 tests,
global- and stack-buffer-overflow). GuestMemory::CopyGuestBytes copies
guest bytes with memcpy, and in an ASan build without
instrumentation; Read and ShaderMemory's block load use it.
- The depth surface reuse checks in agc_driver_graphics use the made-up
guest addresses 0x80000000 and 0x81000000, which lie in ASan's low
shadow, so the HTILE read there looked mapped and read shadow memory.
They now use 0x30000000 and 0x31000000.
- agc_driver_flat_store maps its watched blocks at 8 TiB and 32 TiB to
cover page numbers on both sides of 16 TiB. 8 TiB is in ASan's high
shadow; the ASan build uses 0x60000000 for the low one.
- agc_driver_gpu_map_on_fiber requires the fiber to use at most 8 KiB
of its stack while its mapping is imported on a host thread. ASan's
redzones make the same frames 9200 bytes; the ASan build allows
16 KiB. It also requires a mapping on an ordinary thread to import on
that thread's stack. Like MeasureStackReserve before #7115, the
driver's inline-import check (Lifecycle.cpp) and onOwnLiveStack
(GuestMemory.cpp) took the address of a local, which ASan's fake
stack moves off the thread's stack, so the ASan build always imported
on a host thread. Both now measure from __builtin_frame_address(0).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The entry sat right after "connect to AF_UNSPEC", where the open #6884 adds
its own entry, so whichever merges second would conflict. Move it after the
mlockall entry, which no open pull request changes. The text is unchanged.
Written with AI assistance (Claude).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Linux path mapped ENOTDIR from getdents64 to EINVAL, but a pipe or FIFO
still failed with ESPIPE from the lseek that reads the base offset first.
FreeBSD 11.4's kern_getdirentries returns EINVAL for any descriptor that is
not a directory vnode, pipes included, and EBADF for one that is not open.
Check the descriptor with fstat before the lseek: EBADF and other host
errors are passed through, and anything that is not a directory returns
EINVAL. The getdents64 error line is unchanged again, and the technical debt
entry is removed since no difference is left.
Written with AI assistance (Claude).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Broken Sword - Shadow of the Templars: Reforged (PPSA21480) decodes its
audio itself and uses AJM codec 23 only to resample interleaved s16 PCM
from 44.1 to 48 kHz: every block initializes the instance with
0, 2, 44100, sets the ratio 0.91875 and decodes 235 frames into a
256-frame output. The run threw "AJM: decoding codec 23 is not
implemented".
A codec 23 run now consumes its whole input and emits every output
frame whose position lies inside it, with the Catmull-Rom of the
existing resampler. Cases the title does not show throw, among them a
run after a resampled run without a new initialize.
guest_directory_close expected ENOTDIR from getdents on a descriptor number
reused by a regular file, and guest_directory_buffer expected ENOTDIR for
descriptor -1. kern_getdirentries returns EINVAL for a vnode that is not a
directory and getvnode returns EBADF for a descriptor that is not open, which
is what sceKernelGetdirentries now reports on Windows.
Written with AI assistance (Claude).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The fragment request of agc_driver_vertex_clip_cull_distances took its
user data from a temporary vector, so GuestContext::userData (a span)
pointed at freed memory by the time Recompile read it. The ASan build
reports a heap-use-after-free in materializeSnapshot; other builds
read whatever the freed block still holds. The vector is now a local
that outlives the request, like the vertex stage's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A compute kernel that stores one colour to every texel of a storage image over a depth plane (a shadow atlas clear) copied
the whole plane depth image -> staging buffer -> mirror before the dispatch and mirror -> staging buffer -> depth image
after it, although the kernel reads nothing and writes every texel.
A dispatch of the 15-word 8x8x1 image clear kernel (direct, no partial last group) whose T# lies on a depth surface now
reads its colour from guest memory under the GPU mutex, the place the kernel reads it. When the dispatch covers the whole
plane, the plane is one layer of level 0, the colour is a value the plane's format holds exactly (0 or 1 for a 16-bit
unorm view, 0 or 0xffff for a 16-bit uint view, 0 or a normal float in (0, 1] for a 32-bit view) and nothing is waiting to
write the colour, the dispatch still writes the mirror but the copy-in is left out, and the copy-out becomes a clear of the
depth image to that value. The colour is read again when the copy-out is replaced; a changed value keeps the copy-out.
Every other dispatch, stencil planes and declined values keep both copies.
agc_driver_depth_clear_hle_tests runs the clear sequences with the clear taken and with the copies, and compares the depth
image texel by texel through the mirror, the guest memory and the depth tests.
The scan tests' includes now sit before <array>, where #6706 and #5859 add theirs after it, and the pass counter's address flag is set after the read-only check instead of next to the pass reads #6566 rewrites. No behaviour change.
Every snapshot texture in the sampled texture cache kept a host copy of
its guest bytes, used only when the write tracker reports the surface
written, to tell a rewrite of the same bytes from a change. The cache
budget counts the GPU allocation alone, so the host copies grew with it:
PPSA05684 held 2.7 GB of them in its first level.
A snapshot larger than 256 KiB is released after the upload when the
write tracker (a write generation or an untracked key) can prove the
surface unchanged. A tracked write to such a surface re-uploads it
instead of comparing bytes; smaller snapshots, and surfaces the tracker
cannot vouch for, keep their copy as before.
agc_driver_large_tracked_snapshot samples a 1 MiB surface, committed
and partially resident: an unchanged surface is reused without reading
guest memory, a CPU write is uploaded, and a driver store of the same
bytes is uploaded again because no snapshot is left to compare.
AI-assisted: yes