SotC: unblock asset streaming, honour GNM fast clears, and play LLE audio - #15
Open
Force67 wants to merge 2 commits into
Open
SotC: unblock asset streaming, honour GNM fast clears, and play LLE audio#15Force67 wants to merge 2 commits into
Force67 wants to merge 2 commits into
Conversation
Instrumentation from the black-frame investigation, all env-gated and default
off, kept because each one answered a question that had been guessed at:
DELTA_GPU_VTXTRACE_RT=<hex>[+_AFTER] decode and print the first vertex's
attributes for draws into one render target. This is what proved the
fullscreen quad SotC paints over its scene really does carry colour
0xff000000 in GUEST memory -- a black curtain the emulator is reproducing
faithfully, not a decode error. Its position attribute decodes to an exact
(-1,-1,0) NDC quad from the same fetch, so the V# resolution is sound too.
DELTA_GPU_SKIP_PS=<hex> drop every draw using one pixel shader. Dropping the
curtain's PS (0x804340f700) takes the flip buffer from 1 distinct colour to
908 -- an end-to-end causal proof of which single draw blacks the frame.
DELTA_GPU_RINGHWM per-frame high-water marks for the vertex, index,
uniform and storage upload rings.
DELTA_GPU_VBRING_MB=<n> size the vertex ring at runtime.
On the ring: exhaustion is real and now measured. Up to 735 declines in ~30
frames, every one of them the vertex ring (never IB/UBO/SBO), pegged at
8,272,244 of 8,388,608 bytes -- kVbRing is 16 MB but is halved per frame slot --
with single declined uploads up to 2.42 MB for an 86,790-index mesh. Raising it
drives declines to exactly zero and reveals 20,723 KB of genuine per-frame
vertex demand, 2.5x the per-slot budget.
It changes nothing on screen, and that is worth recording: the scene RT is
bit-for-bit identical with and without it (distinct=3606 min=0.069 max=0.836
mean=0.412 either way). So this is a real capacity bug that was starving real
draws, and it is NOT the black frame. The default is left at 16 MB rather than
raised on a hunch, since the only measured effect so far is memory.
Default path unchanged: 20 fps, no crash, the same 18 pre-existing module-load
errors.
VSRC (bits [7:0]) selects which parameter v_interp_mov_f32 reads: 0 = P10, 1 = P20, 2 = P0. Only P0 is handled. A P10 or P20 site falls through the gate, emits zero SPIR-V body words and leaves the destination VGPR at its zero initialiser -- and because it never calls WarnUnsupported, the shader still reports verdict=ok with zero declines and zero unsupported ops while quietly computing from a zero. That combination is why it went unnoticed: every audit this session reported the pixel shaders as translating cleanly. DELTA_GPU_SHAUDIT measures the real blast radius on SotC -- 356 sites across 33 of its 56 unique pixel shaders -- and it is exactly the failure shape observed downstream, where shaders that "translate fine" write constants or zero into their targets. Only documented, not fixed. A correct fix needs the per-vertex parameters, since P10 = P1 - P0 and P20 = P2 - P0 at the provoking triangle: declare the Location PerVertexKHR (VK_KHR_fragment_shader_barycentric) and emit the subtraction. The cheap interim step is to route these through WarnUnsupported so the shader is declined rather than drawn wrong, but that flips ~33 shaders from silently-wrong to declined and the consequences could not be measured in this session -- so it is written down rather than guessed at.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourteen commits from a Shadow of the Colossus bring-up session. Three land real
functionality; the rest are the diagnostics that found them, plus findings written
down where the next person will look.
The one that unblocked everything
kern/umtx: publishUMUTEX_CONTESTED.MUTEX_WAITslept without ever writingthe contested bit. FreeBSD's
_do_lock_normaldoes exactly that, for exactly thisreason: "Set the contested bit so that a release in user space knows to use the
system call for unlock." Without it libthr's inline
atomic_cmpset_rel_32(id -> UNOWNED)always succeeds, the owner never traps, noMUTEX_WAKEis ever issued,and our waiter escapes only via the 2 ms safety poll — 15,708 failed re-acquires on
one JobSystem mutex. The three release-side ops were wrong in the matching way, so
op 6, op 18/22 and CV_WAIT's mutex release are fixed together against the real
kernel source.
Frame rate went ~6 -> ~20 fps and SotC reached in-world for the first time: 33,023
distinct asset opens, whole world streamed, no synchronisation stall left.
Rendering
Honour GNM's fast-clear rect. SotC clears with a
RECT_LISTdraw carrying nopixel shader and no vertex attributes, colour in
CB_COLORn_CLEAR_WORD0/1. Thelazy-clear heuristic requires float positions and attributes so it could never
match, and the draw was rasterised as a no-op — every target ran
loadOp=LOADforever. The fingerprint was unambiguous: the world RT held exactly the frame
index (100.0 at f100) in 2 distinct texels across 2,073,600. Compute was a victim
faithfully processing a saturated input, not the culprit.
v_cvt_pk_u8_f32+ the cbuffer planning cap. Two independent reasons a pixelshader was refused translation, which made the command processor skip the draw —
so the world meshes' depth pre-pass was submitted every frame while the shaded
colour pass of the same meshes never reached the backend. Declined shaders 23 -> 2,
dropped draws 32/400 -> 6/400, six-MRT G-buffer populated for the first time.
Scene RT went from 2 distinct values to 5,869, and fps ~21 -> ~41.
Audio
Host the system audio daemon. The real
libSceAudioOutneeds no ioctl device.It creates a control shm plus one region per port and speaks a one-block-deep
handshake:
submit()returns BUSY unless a token is zero, and the mixer threadthen parks on
sceAudioOutMix<pid>forever. We hosted no consumer, so exactly oneblock was ever written.
kern/ps4/audio_daemon.cppnow takes the block, clears thetoken, grants the event-flag bit and pushes to the existing SDL sink, paced to real
time.
Verified against Isaac, the only title on hand with a non-zero signal: the LLE path
delivers PCM bit-identical to the HLE shim — ~3.1M aligned frames over 65 s,
zero mismatching frames, reproduced across two independent runs at 1.00x real time.
Two unrelated code paths converging on identical bytes rules out wrong channel
count, format, stride, endianness, duplication, loss and tearing at once.
Also here
ps5-minecraft-ipmi, plus aSceUserServicehandler and thefix for manager op 594 (a client event-flag wait answered as "success" made
sceUserServiceGetEventleak libkernel's 16 MiB arena untilbad_alloc).libSceUserServiceruns LLE.DELTA_LLE/DELTA_HLEto pick HLE or the real firmware module per library,with the measured caveats recorded next to it.
sys_mnamestack tags, so a wait probe names thesubsystem instead of a numeric tid.
Not fixed, and written down rather than guessed at
SotC still presents black. It is not a compositing bug: the guest paints a
fullscreen curtain whose vertex colour really is
0xff000000in guest memory, andout = ui.rgb + (1-ui.a)*scene.rgbis then pinned to zero. Dropping that one drawtakes the flip buffer from 1 distinct colour to 908 — a grey field, not a picture.
The largest silent correctness hole behind that is now named and documented in
gcn_spirv.cc:v_interp_mov_f32is silently dropped for the P10 and P20selectors — 356 sites across 33 of SotC's 56 pixel shaders emit zero SPIR-V and
leave the destination register zero, with no warning, so the shader still reports
verdict=ok. Fixing it needs the per-vertex parameters(
VK_KHR_fragment_shader_barycentric). Left documented because the cheap interimstep flips ~33 shaders from silently-wrong to declined and that could not be
measured here.
Vertex-ring exhaustion is also real and measured (735 declines, all the VB ring,
2.5x the per-slot budget) —
DELTA_GPU_VBRING_MBsizes it at runtime. It changesnothing on screen, which is recorded so nobody re-runs it.
Default path throughout: no crash, same 18 pre-existing module-load errors. Every
new knob is env-gated and off by default.