Commit Graph
48082 Commits
Author SHA1 Message Date
Henrik Rydgård 68755d7f63 Merge pull request #22392 from hrydgard/headless-vulkan-screenshot-assert
Headless: Take the timeout screenshot before ending the draw frame
2026-09-29 15:41:12 -06:00
Henrik RydgårdandClaude Opus 5.5 f6e70b88ad Headless: Take the timeout screenshot before ending the draw frame
On Vulkan, reading back the display framebuffer after EndDrawFrame hit
the insideFrame_ assert in CopyFramebufferToMemory, so any run that
timed out with --screenshot-save crashed in debug builds.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 14:05:00 -06:00
Henrik Rydgård c9c26ce0fd Merge pull request #22389 from hrydgard/savedata-icon-black-background
Savedata dialog: Draw icons over black, with alpha blending
2026-09-29 13:52:34 -06:00
Henrik Rydgård 949423d8e9 Merge pull request #22388 from hrydgard/update-xxhash
Update xxhash to v0.8.4, use XXH3 for texture hashing
2026-09-29 13:52:00 -06:00
Henrik Rydgård b9c5b28b8f Merge pull request #22386 from hrydgard/adhoc-shutdown-race
Adhoc: Fix shutdown hanging when it comes right after adhoc init
2026-09-29 13:29:20 -06:00
Henrik RydgårdandClaude Opus 5.5 1288c294fd Savedata dialog: Draw icons over black, with alpha blending
The PSP blends save icons over a black background, so their transparent
parts come out black. 0a5fa27957 turned blending off instead, which is
wrong for icons that rely on it. Revert that, and draw a black rectangle
under each icon. Fixes #22280.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 13:21:24 -06:00
Henrik Rydgård 0c6a28fe11 Merge pull request #22387 from hrydgard/homebrew-slim-extra-ram
Expose extra ram to homebrew apps if Slim model is emulated
2026-09-29 13:13:10 -06:00
Henrik RydgårdandClaude Opus 5.5 7de7b2d76b TextureCache: Use XXH3 instead of the quick hash for texture data
With xxhash 0.8.4, XXH3 is faster than StableQuickTexHash on ARM64
(about 37 vs 24 GB/s on a Snapdragon X), and its scalar path, which
RISC-V builds get, is about as fast as the quick hash's. It doesn't
collide the way the quick hash does (#8249).

Texture replacement still uses its own hash setting, so texture packs
are unaffected.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 13:10:28 -06:00
Henrik RydgårdandClaude Opus 5.5 21ee60a8ab Build: Update xxhash to v0.8.4.
Keeps our two local changes (the ppsspp_config.h include and the ARM32
prefetch in the XXH32 loop). Hash values are unchanged.

XXH3 is much faster on ARM64 now: about 37 GB/s against 13.5 with v0.8.1
on a Snapdragon X, where our quick texture hash does 24.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 13:04:08 -06:00
Henrik Rydgård 36cbb32e77 Merge pull request #22385 from hrydgard/goedit-camera-hang
Fix Go!Edit camera hang, and some other related issues
2026-09-29 13:00:49 -06:00
Henrik RydgårdandClaude Opus 5.5 3062ffd695 Map the PSP-2000 extra RAM for homebrew PBPs, outside the user partition
Homebrew written for a PSP-2000+ under custom firmware can use the top
32MB of RAM directly without setting MEMSIZE, which leaves the user
partition at its normal size. NJEMU's slim builds do this (#8925).
Previously we didn't map that memory at all for PBPs; MEMSIZE=1 isn't a
workaround either, since it grows the partition and the heap and stacks
land where the program writes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:40:59 -06:00
Henrik RydgårdandClaude Opus 5.5 c9a631c336 sceCtrl: Add the 3.71 sceCtrl_driver NID for sceCtrlPeekBufferPositive
NJEMU's SystemButtons.prx kernel plugin imports it when the firmware
reports 3.71 or later, and polls it every frame to read HOME/volume.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:40:59 -06:00
Henrik RydgårdandClaude Opus 5.5 619d331a86 Headless: Use localhost as the adhoc server
The net tests need WLAN on, but that also made every game run log into a
real adhoc server on the internet, so results depended on the network.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:37:48 -06:00
Henrik RydgårdandClaude Opus 5.5 7ec860c24f Adhoc: Stop waiting for a login that has already failed
sceNetAdhocctlInit waits until the friend finder has logged into the adhoc
server, polling in emulated time but giving up only after 5s of wall time.
The friend finder makes one attempt per login request, and it too waited
the full 5s on a connection that had already been refused, because it
only looked for success. Now it checks the socket error and gives up at
once, and records the failure so the wait ends with it.

In headless, which runs far ahead of real time, Gods Eater Burst sat in
this wait for its whole run.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:37:48 -06:00
Henrik Rydgård 1b8553956f Merge pull request #22383 from hrydgard/preemptible-syscalls
Fixes after hardware scheduling tests by Claude
2026-09-29 12:35:27 -06:00
Henrik RydgårdandClaude Opus 5.5 9e54cebcc2 Camera: Remember the JPEG quality that fit the last frame
Start from the quality that fit the previous frame instead of the top, so
most frames encode once. Step down while a frame is too big, and step back
up when one comes out under half the limit. Windows and the recompression
fallback share the logic.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:29:04 -06:00
Henrik RydgårdandClaude Opus 5.5 07556d064e Camera: Recompress oversized frames on every platform
Android, iOS, macOS and Linux encode camera frames at a fixed quality, so a
detailed frame can exceed the game's framesize just as on Windows before.
pushCameraImage now decodes such a frame and re-encodes it at lower quality
until it fits.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 12:24:40 -06:00
Henrik RydgårdandClaude Opus 5.5 2cd84012d0 Adhoc: Fix shutdown hanging when it comes right after adhoc init
The friend finder thread set friendFinderRunning itself, after a DNS
lookup of the adhoc server. A shutdown in that window cleared the flag
first; the thread then set it again, looped forever, and the join in
NetAdhocctl_Term() never returned. Gods Eater Burst hit this in about one
headless run in six. The flag is now set before the thread is created,
and a finished thread is joined before a new one replaces it, which
would otherwise call std::terminate.

The built-in adhoc server thread had the same race, behind its check for
an existing server, and gets the same fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:58:42 -06:00
Henrik RydgårdandClaude Opus 5.5 9d33e828ab Camera: Compress webcam frames to fit the game's framesize
The PSP camera keeps JPEG frames within the framesize from the video setup.
Go!Edit stores frames in 15KB slots and only takes frames that fit, so our
uncompressed-quality webcam frames were dropped and one frame got repeated
for the whole clip. Lower the JPEG quality until a frame fits.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:44:10 -06:00
Henrik RydgårdandClaude Opus 5.5 948062907b sceUsbMic: Complete blocking reads when the samples are due
With a host microphone present, a blocking read waited until the host had
delivered all the data. If it never did, the thread waited forever: Go!Edit's
sound thread stalled that way and its video recording never advanced. The
PSP mic streams in real time, so wake at the scheduled time and fill what
the host didn't deliver with silence.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:44:10 -06:00
Henrik RydgårdandClaude Opus 5.5 2a5febd634 wsdbg: Add :screenshot, and document nested key=value params
:screenshot saves gpu.buffer.screenshot as a PNG without dumping the data
URI into the output. The docs claimed nested parameters need a raw JSON
line, which gets no ticket; a single-quoted JSON value in key=value form
works and keeps it.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:19:18 -06:00
Henrik RydgårdandClaude Opus 5.5 4504092d3d sceUsbMic: Don't crash when there's no Windows capture device object
Only the app creates winMic, so a blocking mic read crashed headless.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:19:18 -06:00
Henrik RydgårdandClaude Opus 5.5 280ae09856 sceUsbCam: Make sceUsbCamReadVideoFrameBlocking wait for the next frame
It returned at once. Go!Edit reads frames in a loop on a high-priority
thread (bhCameraGetJpeg) that only yields to its own priority level, so
after the "Loading complete" dialog it spun and starved the rest of the
game. Return at the camera's next frame tick, at the rate from the setup
params.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:19:18 -06:00
Henrik RydgårdandClaude Opus 5.5 5865a560eb ARM JITs: Mask what ctc1 writes to fcr31
Only the rounding mode, flags, enables, cause, FCC and FS bits can be
written (0x0181FFFF, pspautotests cpu/fpu/fcr), as the interpreter, IR
and x86 already have it. Both ARM JITs stored the whole value.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 178186ef4e sceVaudio: Reserving the channel waits about 250us
On hardware sceVaudioChReserve takes ~260us once it gets past the busy
check, succeed or fail, and worse threads can run meanwhile. Releasing
takes ~25us and doesn't wait. We returned at once, which is what
audio/sceaudio/reserve's [r] markers showed; it now passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 69e10728c9 test.py: Add gpu/primitives/indices32 to the to-do list
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 af8088b790 Threads: A delay can end before it starts waiting
A delay's deadline is now + usec, and the clock is read again when the
alarm is set. If the deadline has passed by then, the call returns 0 at
once without giving up the CPU. On hardware that makes
sceKernelDelayThread(0) return at once about 60% of the time. On a thread's
first wait after it starts, a delay of 1 does so about two times in three
as well (pspautotests threads/scheduling/delayzero). We always waited at
least 210us.

The choice is pseudo-random off the tick count, not the tick phase, since
our cycle counts are regular enough for a polling loop to lock into never
yielding. Threads remember whether they've waited since starting (Thread
savestate section version 6).

Also moves threads/vpl/create into the passing tests: re-recorded on 6.61,
it agrees with what we do for partitions 8 and 9.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 13dee85375 sceIo: host0: behaves like usbhostfs with dispatch suspended
With dispatch suspended, IO fails in the driver when it tries to wait. The
memory stick driver returns SCE_KERNEL_ERROR_CAN_NOT_WAIT, while usbhostfs,
which serves host0: under PSPLink, returns -1. host0: is mostly what
homebrew developers run from, so it now does the same.

Also from threads/scheduling/dispatch, which now passes:
- sceIoRead reports an async operation still in progress before failing
  on suspended dispatch.
- A write to stdout or stderr doesn't give up the CPU.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 f2eba8f711 sceKernelLoadModule: Charge for reading the file and loading it
On hardware a load costs an open and a read of the file, plus about 1ms
and 30us per KB of loader work (pspautotests threads/scheduling/callcosts),
with the caller waiting throughout. It now charges sceIoOpen's and
sceIoRead's estimates for the file plus that, instead of a flat 500us.

Also notes why sceKernelLoadModuleByID fails from a game's own fd on ms0:
or host0: on hardware, which we don't emulate.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 11:09:47 -06:00
Henrik Rydgård 806618f9a3 Merge pull request #22382 from hrydgard/gpu-lifecycle-fixes-2
Claude code review: GPU fixes 2
2026-09-29 11:04:38 -06:00
Henrik RydgårdandClaude Opus 5.5 93f57a4f38 GPU: Delete copy operations on classes that own resources
These own GPU objects, memory or refcounts in their destructors (or assert
there that they were torn down), so a copy would double-free. Nothing copies
them today; this keeps it that way. The manager base classes cover every
backend's subclass.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 39049a67fd GLES: Free textures on device lost, and unsubmitted step data at exit
- The texture and fragment test caches dropped their GLRTexture objects on
  DeviceLost without queueing them for deletion, leaking them on every
  Android background/resume. The deleter already skips the GL calls when
  the context is gone.
- GLRenderManager::ThreadEnd cleared unsubmitted init and render steps
  without freeing the data they own. Run them through the dry run instead,
  which now also frees stereo matrices and shader code.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 b72927bbeb Texture replacement: Plug leaks on error paths
- Release the file reference when loading a level fails or finds nothing,
  since only a loaded level takes ownership of it.
- Free the PNG image when the size changed since the header was read.
- Delete the VFS when a pack without an ini has no hash-named textures.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 e108e41675 GPU: Fix assorted leaks, null derefs and small rendering bugs
- Put the anisotropy level in the sampler key, so changing it applies on
  Vulkan and D3D11.
- Release CLUT textures at shutdown on GLES and D3D11.
- Fix the depth readback viewport, which squeezed the image whenever the
  read rectangle was smaller than the fbo.
- Test the computed depth, not the unset result, in the equal-depth clear
  check.
- Don't read back a CLUT from a framebuffer without an fbo.
- Tolerate null entries when releasing post-shader objects and CLUT
  textures after a failed creation.
- ImGe: Don't crash on a framebuffer without an fbo, or on GetVFB under the
  software renderer.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 71bc3187db Vulkan: Reload the shader cache after a device restore
DeviceLost saves the cache and clears everything, and the next save wrote
back only what was drawn since, so each Android background/resume cycle
shrank the cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 71210f3fa2 GE recorder: Don't let a second request hijack a recording
RecordNextFrame now refuses while a recording is active and during frame
dump playback (which asserted on the next replay). The callback handoff to
the CPU thread is locked, and a failed file open no longer crashes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 d6f8615d2a SoftGPU: Fix block transfer and self-render overlap tracking
- The block transfer overlap check passed the stride in pixels where bytes
  are expected, so it only covered part of the rectangle.
- A selfrender/selfdepth flush in UpdateState dropped the current draw's
  pending writes and reads, so later transfers didn't wait for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 507bb0801b GE: Complete lists dropped on error, flush before immediate draws
- A list dropped for a bad pc or a GE error stayed RUNNING: its ID was never
  freed and sceGeListSync on it never returned. Complete it like a finished
  one.
- FlushImm switches to through mode and another vertex decoder, so flush the
  queued draws first even when the immediate flags match.
- Clear leftover temporary GE breakpoints when setting or clearing the next
  break, so a step that never got there doesn't trip later.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 16ea2cbf88 GPU: Fix draw engine buffer overruns and stale vertex data
- Flush before the queued draws would decode more than VERTEX_BUFFER_MAX
  vertices. The batch was limited by index count, which doesn't bound a
  sparse index range, and DecodeVerts silently stopped while DecodeInds
  still emitted indices for the undecoded draws.
- Give TestBoundingBox its own scratch buffer. It used offsets in decoded_,
  which can hold decoded vertices that aren't flushed yet.
- Read 32-bit indices the way the PSP does, ignoring the upper 16 bits.
  IndexConverter and the fast bounding box test used all 32, so a game
  setting them indexed far past the decoded vertices.
- D3D11: Flush in FinishDeferred like the other backends, since indices
  are still read from PSP memory at flush time (#10095).
- Don't JIT new vertex decoders once the code space is full.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:33:32 -06:00
Henrik Rydgård c40cce5173 Merge pull request #22381 from hrydgard/gpu-lifecycle-fixes
Claude code review: GPU lifecycle fixes
2026-09-29 10:24:27 -06:00
Henrik RydgårdandClaude Opus 5.5 4ba981b5b3 sceAtrac: Charge setting data and decoding what the ME takes
Setting data decodes and throws away the frames before the first sample,
so on hardware it costs a decoder setup plus that decode, with the caller
waiting: ~900us for mono Atrac3 and ~3.5ms for stereo Atrac3+, whatever the
buffer size (pspautotests threads/scheduling/callcosts). It was charged
100us.

Decoding a frame now costs what the same frame costs through
sceAudiocodecDecode, through the shared ME queue, instead of a flat
2300us. That's about the same for stereo Atrac3+ and less for Atrac3
(685us mono, ~1100us stereo). The first Atrac3+ frames after setup still
come out ~500us short.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 10:17:46 -06:00
Henrik Rydgård 52a16ea365 Make TempImage act a bit more robustly. 2026-09-29 10:16:08 -06:00
Henrik RydgårdandClaude Opus 5.5 c1e70c9686 Unloading a module is busy work, sceKernelVolatileMemTryLock is quick
From pspautotests threads/scheduling/syscallkinds:

- sceKernelUnloadModule takes ~400us that better threads can preempt
  and worse ones don't get in on, so it uses the busy delay rather than
  a wait.
- A successful sceKernelVolatileMemTryLock takes under 100us. It ate
  500000 cycles as a hack for Crash Tag Team Racing, which has since
  moved to (and no longer needs) the DrawSyncEatCycles compat flag.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:56:28 -06:00
Henrik RydgårdandClaude Opus 5.5 8301c35d7d Threads: Let better threads preempt a long sceKernelCreateThread
On hardware the kernel fills a new thread's stack with interrupts on, so
a better thread that wakes during it runs before the call returns, the
time it takes doesn't count towards the call, and worse threads get
nothing (pspautotests threads/scheduling/preemptsyscall). PPSSPP ate the
whole cost at once and only rescheduled at the end.

__KernelBusyDelayResult() models such a syscall: the caller waits, an
idle thread stands in for it while nothing better wants the CPU, and its
remaining cycles only count down while that's the case. When done it
goes back ahead of threads of its own priority, having never given up
the CPU. sceKernelCreateThread uses it for the stack fill, unless a
thread event handler is about to run.

Booting 75 games against master shows no difference.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:56:28 -06:00
Henrik Rydgård 16135e4e2f Merge pull request #22378 from hrydgard/intr-waits
Claude hardware testing: Interrupt waits
2026-09-29 09:56:08 -06:00
Henrik RydgårdandClaude Opus 5.5 24017a1ba4 GE debugger: Fix the stepping request handshake
- Wait on actionComplete instead of a bare condition variable wait, which
  could miss the wakeup and hang until resume.
- Serialize requesters, so two debuggers can't overwrite each other's
  action, and make SetCmdValue/FlushDrawing wait too.
- Give up and withdraw the request when stepping ends, instead of waiting
  forever (this deadlocked game shutdown against the Win32 GE debugger).
- Run requests during CPU stepping, which already accepted them.
- Clear the stepping state on Core_Resume from GE stepping and on
  GPU_Shutdown.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:49:10 -06:00
Henrik RydgårdandClaude Opus 5.5 24b71386ff Document that shader cache key changes need a CACHE_VERSION bump
The OpenGL and Vulkan shader caches store raw shader IDs (and, for Vulkan,
pipeline keys) on disk. Add the rule to AGENTS.md and point to it from the
persisted types.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:35:17 -06:00
Henrik RydgårdandClaude Opus 5.5 a9eacff1e1 SoftGPU: Keep the compile flushes on builds without the JIT
Skipping them is only a speed-up, but it changes how states get batched and
optimized, which changes the rendered output (NBA 2K13 and Virtua Tennis
frame dumps). Keep the old behaviour until that's understood.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:35:17 -06:00
Henrik RydgårdandClaude Opus 5.5 056fd05231 UnitTest: Make BlockAllocator run in about a second
Validating after every churn step was quadratic in the block count. Check
every 64 steps and at the end, and do fewer iterations.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:35:17 -06:00
Henrik RydgårdandClaude Opus 5.5 a9c782bfcf SoftGPU: Recompute the raster state after a JIT cache clear
A code space clear frees the functions the current state points to, but
the state was kept as long as the GE registers didn't change. Track the
clear generations and recompute, also when a compile during the state
computation clears the caches.

Also skip the binner flush for compiles on builds without the software
JIT, where Compile() does nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:35:17 -06:00