Commit Graph
50294 Commits
Author SHA1 Message Date
Henrik Rydgård 9366aa7dc2 Merge pull request #22933 from hrydgard/more-ui-work
Fix a bunch of UI bugs
2026-11-04 19:12:03 +01:00
Henrik Rydgård 9e972e0dd3 GameInfoCache: Parse the game ID out of save data. nice to display. 2026-11-04 18:29:51 +01:00
Henrik Rydgård 6baa0a9805 Fix issue in game tab settings with increase/decrease size. 2026-11-04 18:19:15 +01:00
Henrik Rydgård d0b271820a Make SasAudio addresses clickable 2026-11-04 18:14:33 +01:00
Henrik Rydgård 029d9582a0 Visual fixes on pause screen 2026-11-04 18:07:50 +01:00
Henrik Rydgård b5f91caae4 Windows app (not headless): Inform the user of bad command line parameters, but proceed. 2026-11-04 18:07:29 +01:00
Henrik Rydgård 3ca6ab6a79 Fix ImDebugger font screwup 2026-11-04 18:07:17 +01:00
Henrik Rydgård 9e78ef5677 Misc UI and cmdline parse work 2026-11-04 17:58:18 +01:00
Henrik Rydgård 3bc60270ea Merge pull request #22931 from hrydgard/parseuri-hardware-tests
sceParseUri: Rewrite the parser against hardware, and implement sceUriUnescape
2026-11-04 14:20:10 +01:00
Henrik Rydgård 148e9c82d2 Merge pull request #22930 from hrydgard/vblank-timing
sceDisplay/sceCtrl: Charge the vblank interrupt's and ctrl peeks' CPU time, as measured on hardware
2026-11-04 14:19:29 +01:00
Henrik Rydgård 7b2860ec42 Add icon for VR settings 2026-11-04 11:41:53 +01:00
Henrik Rydgård ac31417b71 Merge pull request #22928 from hrydgard/softgpu-zero-normals
SoftGPU: Light zero normals by the sign of the last Bezier patch
2026-11-04 11:31:21 +01:00
Henrik RydgårdandClaude Opus 5 96cda92669 sceParseUri: rewrite the parser against hardware, and implement sceUriUnescape
Nothing tested sceParseUri, and it showed. Measured on a real PSP by the new
net/parseuri tests:

- A URI without "//" was rejected outright, so mailto:, urn:, about: and even
  file:///tmp/x all failed. They parse fine; that is what noSlash is for.
- A fragment ended up appended to the path, because the code looked for "#"
  inside the query rather than the whole remainder.
- An empty path came back as "/", because Url insists on one.
- Ports take at most five digits and at most the value 65536, which wraps to
  zero; anything longer or larger is refused. A host runs while the characters
  are alphanumeric, dot or dash, so an underscore or a bracketed IPv6 address
  falls through to the path.
- The three error codes are real and specific - 80433060 bad URI, 804331FE no
  output pointer, 80433022 work area too small - where everything returned -1.
  Leaving out either area is a size query that succeeds.

So sceUriParse no longer goes through the Url class, which is built for HTTP and
has nowhere to put a fragment.

sceUriEscape kept the wrong two characters: hardware leaves "*" alone and escapes
"~", which is the other way round from RFC 3986. It also fills what it can before
reporting a buffer too small, and leaves the length output alone when it does.

sceUriUnescape was a null entry, so calling it got "library not linked". It is
implemented, including the odd positive return value - one per plain character
and five per escape - that the hardware hands back.

Also lists the two exports libparse_uri.prx has that we did not know about, so a
game calling one is named in the log rather than appearing as a bare NID.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-04 11:11:20 +01:00
Henrik RydgårdandClaude Opus 5.5 201e1be9ef SoftGPU: Light zero normals by the sign of the last Bezier patch
The GE keeps the sign of the z of the last Bezier patch's normal at its last
corner (flipped by patch facing, 0 when sideways or degenerate), and lights a
normal that is exactly zero as that sign times (1,1,1) in model space
(ppsspp-re geprobe exp226-253). Normals the tessellation generates don't take
it. Nayuta no Kiseki and Zettai Zetsumei Toshi 3 rely on it.

Frame dump playback draws a flat +z patch after INIT, as the PSP replayer does
before each replay, so both start from +1.

A run's chunks read the sign and the generated-normals flag, so changing either
waits for the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 11:07:23 +01:00
Henrik Rydgård c0479ac6f2 Merge pull request #22926 from hrydgard/libretro-depth-raster
libretro: Add the Lens flare occlusion (depth raster) option
2026-11-04 11:02:45 +01:00
Henrik RydgårdandClaude Opus 5.5 7af0b9d432 test.py: threads/scheduling/alarmcosts passes now
The test checks ranges instead of rounding on hardware's noise.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 10:41:00 +01:00
Henrik RydgårdandClaude Opus 5.5 58c009c07e Add the display/vblankphase and ctrl/peekcost tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 10:40:53 +01:00
Henrik RydgårdandClaude Opus 5.5 c7efd845b8 sceCtrl: Peeking costs more the more samples it copies
~2us plus ~0.2us per sample on hardware, 15us for 64 (pspautotests
ctrl/peekcost). We charged a flat 1.5us.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 10:40:42 +01:00
Henrik RydgårdandClaude Opus 5.5 5702b0f511 sceDisplay: Charge the vblank interrupt's CPU time, and count lines from 0
On hardware the vblank interrupt comes as the last line (285) ends, and
hcount wraps to 0 inside it. It takes ~66us of CPU from whatever runs, or
~78us with a handler, which runs ~29us in. It releases the vblank waiters
after the handlers, and they get the CPU once it's done: ~81us after it,
~91us with a handler, ~3us later for each extra waiter. The vblank ends
~818us after it (pspautotests display/vblankphase).

We charged nothing and released waiters from a separate event 48us after
the vblank, and hcount started at 1 because user code rarely sees line 0:
the interrupt takes most of it. Now the interrupt carries those costs, the
waiters are released inside it, hcount runs 0-285 over the real frame
length, and a thread that waits for the vblank pays the ~6us that makes
waiters take turns ~12us apart, as on hardware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 10:40:41 +01:00
Henrik RydgårdandClaude Opus 5.5 e199241906 libretro: Add the Lens flare occlusion (depth raster) option
The app's iDepthRasterMode: Auto, Low, Off or Always on. Its default depends on the device (Low on
Android and iOS, Off without SIMD), which a core option can't express, so "Default" picks that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:30:47 +01:00
Henrik Rydgård 8f83f1ea16 Merge pull request #22925 from hrydgard/dualsrc-fallback
GPU: Write the stencil as alpha without dual source blending where the blend allows
2026-11-04 08:40:09 +01:00
Henrik Rydgård a2001917b2 Merge pull request #22919 from hrydgard/pvr-clip-cull-distance
Vulkan: Don't use cull distances on PowerVR
2026-11-04 01:02:06 +01:00
Henrik Rydgård 62d8696d5f Merge pull request #22914 from hrydgard/ignored-fault-storm
MemFault: Stop ignoring bad accesses after 1000 in one frame
2026-11-04 01:01:41 +01:00
Henrik RydgårdandClaude Opus 5.5 2027130763 GPU: Write the stencil as alpha without dual source blending where the blend allows
When the color blend uses the src alpha and the stencil has to be written as alpha, both need the alpha
output, so it took dual source blending, and without it the alpha came out wrong. Where the src alpha is
only in the src factor (SRCALPHA or INVSRCALPHA, with DSTALPHA, INVDSTALPHA or a fixed color for the
dst), the shader now applies the src factor (REPLACE_BLEND_PRE_SRC), which leaves the alpha output for
the stencil on every GPU. That's WipEout Pure's additive glows, among others. On a Mac (MoltenVK), none
of the 1,141 dumps in the frame dump collection change with dual source blending on. With it off,
God of War's black squares and one of WipEout Pure's shield dumps now match its render exactly, and
two more WipEout Pure dumps come closer (3,631 -> 2,793 and 4,642 -> 4,163 pixels off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 00:56:16 +01:00
Henrik RydgårdandClaude Opus 5.5 a41186c3b0 Vulkan: Don't use cull distances on PowerVR
On a BXM-8-256 (Moto G54), a vertex shader that writes both clip and cull distances doesn't get culled,
while either one alone works. Geometry the PSP culls got drawn: the ground in DBZ Tenkaichi Tag Team,
scenery in Monster Hunter 3rd. With cull distances off, those draws get clipped and culled on the CPU
instead, and the frame dumps match a desktop GPU's render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 00:28:32 +01:00
Henrik Rydgård aad016741c Merge pull request #22918 from hrydgard/claude-heredoc-hook
Agents: Refuse Bash heredocs with backslashes on Windows
2026-11-04 00:27:37 +01:00
Henrik Rydgård 4ea2f6506f Merge pull request #22916 from hrydgard/depth-raster-skip-list-end-wait
Depth raster: Wait for it before GE callbacks instead of at every list end
2026-11-04 00:07:02 +01:00
Henrik RydgårdandClaude Opus 5.5 e1deb5665c Depth raster: Wait for it before GE callbacks instead of at every list end
Since a37e1f3660, the CPU waits for the depth rasterizer every time a display list ends, because Syphon
Filter reads its depth buffer for the light flares right after. It reads it in the list's GE interrupt
callback though, and so do Wipeout Pure and Resistance: Retribution. On hardware the drawing is done
when that runs, so that's where to wait now, letting the worker overlap with the CPU until then.
sceGeDrawSync and sceGeListSync wait as before.

With interpreter loads from the depth buffer counted and the worker slowed down by 10-50 ms a batch,
no read in Syphon Filter: Logan's Shadow, Wipeout, Resistance or Midnight Club: L.A. Remix landed while
it was busy. Without any wait, nearly all of Syphon Filter's, Wipeout's and Resistance's did.

L.A. Rush reads its depth buffer only after sceGeDrawSync, and the new compat flag
DepthRasterNoCallbackWait skips the callback wait for it.

10 emulated seconds on a Moto G54 (two Cortex-A78, six A55):

  Wipeout Pure     7.24 s -> 6.50 s
  Resistance      17.98 s -> 13.26 s
  Midnight Club    8.75 s -> 8.01 s
  L.A. Rush        5.95 s -> 4.76 s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 23:32:11 +01:00
Henrik Rydgård 7164fce7c4 Merge pull request #22897 from hrydgard/adhoc-ptp-segments
Ad hoc relay: Deliver back-to-back PTP sends about 1 ms apart
2026-11-03 23:09:50 +01:00
Henrik RydgårdandClaude Opus 5.5 92fbf32e19 Ad hoc relay: Iterate the PTP block queue instead of indexing it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 22:03:18 +01:00
Henrik Rydgård 723732ca67 Merge pull request #22913 from hrydgard/firmware-model-check
Warn when the installed firmware is for a different PSP model
2026-11-03 21:45:39 +01:00
Henrik Rydgård b86023c60c Merge pull request #22915 from hrydgard/texture-pack-saf-case
Replacement: Don't index packs on Android's shared storage to ignore case
2026-11-03 21:45:11 +01:00
Henrik RydgårdandClaude Opus 5.5 2742a6374a MemFault: Stop ignoring bad accesses after 1000 in one frame
An ignored bad access takes microseconds of host time and no emulated
cycles, so a guest looping over a bad pointer stretched a single emulated
frame out for seconds. On Android the emulator only checks for exit between
frames, so destroying the surface waited on it until the app hit an ANR.
Play reports show the render loop inside the fault handler when that happens.

Past the limit, the access is treated like one that can't be ignored: the
core stops with the memory exception, which notes why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:03:13 +01:00
Henrik RydgårdandClaude Opus 5.5 06f19c6473 Replacement: Don't index packs on Android's shared storage to ignore case
Looking for a file whose name differs in case lists the whole pack on
the first miss. For a pack behind a content URI, that's one content
resolver query per directory, which in a big pack takes long enough
that a Play report hit an ANR: the surface was destroyed while the
render loop sat in the texture cache's destructor, waiting for a load
blocked on the index. Shared storage already ignores case, so the index
couldn't find anything there anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:03:13 +01:00
Henrik Rydgård caa91b54b5 Merge pull request #22912 from hrydgard/pvr-dynamic-ubo-crash
Vulkan: Work around a PowerVR crash when a vertex shader doesn't read the lights UBO
2026-11-03 19:56:02 +01:00
Henrik RydgårdandClaude Opus 5.5 6e292608d4 Translate the firmware model warning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:50:30 +01:00
Henrik RydgårdandClaude Opus 5.5 c1c815da80 Ad hoc relay: Deliver back-to-back PTP sends about 1 ms apart
On a PSP, two PTP sends made back to back arrive about 1 ms apart (0.95 ms plus 0.65 us a byte), so
a receiver polling fast reads them one at a time; once both have arrived they come back as one read
(pspautotests net/adhoc/pair/ptpsegments, ptpmsg). Through the relay both arrived together, and
Capcom Classics Collection Reloaded's Street Fighter II, which expects them one at a time, dropped
the connection as the match started.

Relay PTP sessions now run in aemu_postoffice's block boundary mode, which hands over one send at a
time. Each block goes into a queue on the socket, up to its buffer size, and becomes readable no
earlier than the previous one plus the PSP's gap, counted from when it arrived; the relay's 1 ms poll
takes blocks as they come, so a receiver that looks late still gets them all at once. P2P mode
can't do this: its TCP stream has no send boundaries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:39:44 +01:00
Henrik Rydgård bca75c7381 Some popup stylin' 2026-11-03 19:22:58 +01:00
Henrik RydgårdandClaude Opus 5.5 509d9a9f87 Use one string for both firmware model warnings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:42:35 +01:00
Henrik RydgårdandClaude Opus 5.5 3a9ecbd04b Warn when the installed firmware is for a different PSP model
A firmware only has the files of the model it was installed for, and its XMB looks for the files of
the model we report, so a mismatch brings back #22909's blank theme colors and 6.61's red error
screen. Which models an install is for shows in the names of its ipl/ files (nandipl_01g.bin,
nandipl_psp2000.ipl); an install for every model has all of them, and firmwares older than the
PSP-2000 have one ipl for every model.

Changing the PSP model to one the installed firmware doesn't fit now asks first, with a way to
cancel, and booting the XMB with a mismatch shows a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:37:26 +01:00
Henrik RydgårdandClaude Opus 5.5 73559d15aa Vulkan: Work around a PowerVR crash when a vertex shader doesn't read the lights UBO
On a Moto G54 (PowerVR BXM-8-256), F1 2009 and MX vs. ATV Untamed crash every few seconds in
vkCmdDrawIndexed, with a write fault at the start of a page inside vulkan.mtk.so.

At draw time, the driver writes the address and range of every dynamic UBO in the bound descriptor
set into a table, but sizes the table for the ones the shader stage reads. Our layout has two
dynamic UBOs, base and lights (vertex only), and a vertex shader without lighting reads only base,
so each such draw writes 16 bytes past the table. That's usually harmless, and faults when the
table ends a page.

With the new PVR_UNUSED_DYNAMIC_UBO_OVERRUN set on PowerVR, Vulkan vertex shaders always declare and
read the lights UBO. Declaring it isn't enough. On the G54, F1 went from crashing in 5 of 5
10-second runs to 0 of 5, and MX ran 3 of 3. The SPIR-V cache is keyed by source, so no cache
version bump is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:45:44 +01:00
Henrik Rydgård b00f806d3e Merge pull request #22904 from hrydgard/lazy-stencil-rebuild
Rebuild the stencil after a reinterpret only when a draw needs it
2026-11-03 17:16:52 +01:00
Henrik Rydgård 466e51a402 Merge pull request #22910 from hrydgard/sceReg-category-migration
Report the real PSP model to the VSH; let firmware upgrades migrate the registry, reset it on downgrades
2026-11-03 16:58:17 +01:00
Henrik RydgårdandClaude Opus 5.5 b5cf7b3f0c sceKernelGetModel: Report the emulated model instead of always a Slim
The VSH goes by this to decide what the PSP has. With the PSP model set to PSP-1000, the firmware
installer only writes the PSP-1000's files, so a shell told it's on a Slim looked for the Slim's:
3.71's theme color list showed its extra colors (flash0:/vsh/resource/13-27.bmp) as blank entries,
and 6.61 stopped at its red error screen. Both reproduced with the reporter's NAND and a PSP-1000
install of 6.61, and both are gone with this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:28:48 +01:00
Henrik RydgårdandClaude Opus 5.5 ff1a7840a3 Translations: DowngradeFirmware and UpgradeFirmware
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:10:10 +01:00
Henrik RydgårdandClaude Opus 5.5 928100c370 Firmware install: Say that an upgrade keeps the XMB's settings
An upgrade gets its own notice, informational rather than a warning, since the new shell migrates
the registry and nothing is lost. ReplaceFirmware stays for when a version can't be told.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:07:39 +01:00
Henrik RydgårdandClaude Opus 5.5 0f08ad103c Firmware install: Let the new shell upgrade the registry, reset it on a downgrade
Installing a firmware set category_version to the new firmware's schema, so no shell ever migrated
the settings PPSSPP keeps in its config. A shell upgrades the registry itself at its first boot: it
writes the default of every key added since the stored version and stores its own (seen booting
2.82, 3.71, 4.05, 5.50 and 6.61 from 1, and 3.71 and 6.61 from the previous firmware's version).
So an upgrade now leaves the version alone. A PSP can't be downgraded, and an older shell takes a
newer version for a corrupt registry, so a downgrade sets it back to 1; that shell's first boot then
rewrites everything added since 1.50, the theme, colour and wallpaper settings among them. Values a
newer shell left there across a downgrade and back broke the theme settings and then 6.61's boot.

The install screen says a downgrade resets the XMB's settings. Headless no longer sets the version
per boot either, so each boot migrates like a fresh config does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:58:29 +01:00
Henrik Rydgård 5708b4e44c Merge pull request #22908 from hrydgard/ir-vsrt-vf2h
IR: Compile vsrt1-4 and vf2h instead of interpreting them
2026-11-03 15:43:38 +01:00
Henrik RydgårdandClaude Opus 5.5 b18c3ed4d3 IR: Compile vsrt1-4 and vf2h instead of interpreting them
Colin McRae Rally 2005 interprets about 2 million of these each 10 emulated seconds, mostly vf2h.q.
vsrt1-4 become pairs of FMin/FMax, which order and break ties like the interpreter's vfpu_min and
vfpu_max, in the same operand order. The D prefix applies as usual (Colin masks half the lanes);
S and T prefixes, which vsrt rewrites, still go to the interpreter. vf2h gets an IR op of its own,
FFloatToHalf2, calling vfpu_f2h; the native backends call the IR interpreter's version of it.
vf2h with any prefix, and sizes other than pair and quad, still go to the interpreter.

The CPU equivalence fuzzer finds no differences on the IR interpreter or the ARM64 JIT, for these
ops alone, with random and unknown prefixes, and with the D prefixes Colin uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:45:06 +01:00
Henrik Rydgård 6215d4689e Merge pull request #22907 from hrydgard/cull-matrix-lanes
GPU: Multiply by lane in TestBoundingBoxFast instead of splatting the cull matrix
2026-11-03 14:41:40 +01:00