Commit Graph
49103 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 074b66b49b thin3d: Add Framebuffer::HasDepthStencil, check it before depth/stencil copies
ResizeFramebufFBO now asks the framebuffer whether it has a depth/stencil
buffer instead of guessing from the z address and stride.

Vulkan CopyFramebuffer gets the debug asserts and bail-out BlitFramebuffer
already had, and GL copies and blits get the same, so a copy out of a
framebuffer without depth/stencil asserts where it's queued instead of in
the queue runner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 17:44:09 +02:00
Henrik RydgårdandClaude Opus 5.5 8fb0f74255 Don't copy stencil out of a framebuffer that has none when resizing
A color framebuffer made by CreateRAMFramebuffer has no depth/stencil
buffer. When a game later draws to it with depth, it's recreated through
ResizeFramebufFBO, and the stencil copy added for Need for Speed Shift
copied from the missing buffer, asserting in VulkanQueueRunner::PerformCopy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 17:43:53 +02:00
Henrik Rydgård 49eea8645e Merge pull request #22605 from hrydgard/darkstalkers-libretro-native
libretro: Hand DarkStalkers' 384x224 source image to the frontend
2026-10-16 14:14:55 +02:00
Henrik Rydgård d844150507 Merge pull request #22604 from hrydgard/vfpu-debugger-dpi
Debugger: Fix the Win32 VFPU window at high DPI, implement it in ImDebugger
2026-10-16 14:10:44 +02:00
Henrik Rydgård a1d717b431 Merge pull request #22599 from hrydgard/misc
Move the "hardware transform" setting to developer tools, delete spline quality setting
2026-10-16 14:03:21 +02:00
Henrik RydgårdandClaude Opus 5.5 39205d99ad libretro: Hand DarkStalkers' 384x224 source image to the frontend
- Fixes #21739

With the DarkStalkers hack, SoftGPU skips the game's stretch blit and
stretches the source image to the screen itself. Libretro always presents
software rendering at 480x272, so that stretch happened at 1x and the
frontend then scaled up a blurry image.

Now libretro gets the 384x224 source as is, with the aspect the game would
have stretched it to, and switches back to the full frame when the game
stops using the stretch. GL and D3D11 read the frame from a corner of their
larger framebuffer. RetroArch's Vulkan driver samples the whole image the
core hands it, so the Vulkan presentation images are resized to the frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:57:16 +02:00
Henrik RydgårdandClaude Opus 5.5 f982a6a475 Debugger: Fix the Win32 VFPU window at high DPI, implement it in ImDebugger
- Fixes #21636

The Win32 window drew with a fixed 12px font and hardcoded pixel offsets, so at
high DPI the grid ran under the (dialog-unit sized) tab bar. It now uses the
DPI-scaled debugger font, derives the layout from its metrics and the tab bar's
position, and sizes the window to fit. The HalfFloat, Bytes, Shorts and Ints
tabs, which used to show floats, now work.

The ImDebugger VFPU window, which was a TODO, shows the same grid and modes,
with changed values highlighted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 13:42:06 +02:00
Henrik Rydgård b5df6aa301 Debug mode buildfix on Windows 2026-10-16 13:27:26 +02:00
Henrik Rydgård 54a6ac7dd1 Remove translation strings 2026-10-16 13:27:26 +02:00
Henrik Rydgård 03d66e78e1 Bump rust dependencies 2026-10-16 13:27:26 +02:00
Henrik Rydgård 0a9b57371f Delete the spline quality setting 2026-10-16 13:27:26 +02:00
Henrik Rydgård f5255eefaf Move the Hardware Transform setting to developer tools, reset by renaming the key.
This setting should always be on, not having it ruins performance.
2026-10-16 13:27:23 +02:00
Henrik Rydgård 9df356129b Bump gradle, fix warnings 2026-10-16 13:08:35 +02:00
Henrik Rydgård 2568de883d Merge pull request #22603 from hrydgard/bejeweled-fix
Bejeweled 2: Work around race condition during loading, due to bad frame accounting
2026-10-16 12:58:26 +02:00
Henrik RydgårdandClaude Opus 5.5 f53c27da1b Bejeweled 2: Fix the crash at the end of the loading bar
While loading, the game presents every frame but also pushes its
"next draw" time 35-50ms ahead per frame, so by the end of loading
it's about 7 seconds in the future. If loading finishes while the main
thread is in the middle of a present, that present resets the time and
nothing goes wrong. If it finishes anywhere else, the game stops
presenting for those 7 seconds but keeps drawing every frame into the
same 128KB display list, which overruns into the game's own globals
within a second and crashes.

Which way it goes depends on small timing differences.

A hook on the draw function does what the winning side of the race
does: once loading has completed, it pulls the next draw time back if
it's more than a second ahead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 12:42:53 +02:00
Henrik Rydgård 65554c2ede Merge pull request #22602 from hrydgard/depth-raster-thread
GPU: Rasterize software depth on a worker thread
2026-10-16 12:37:11 +02:00
Henrik Rydgård 8b1293d223 Merge pull request #22601 from hrydgard/shader-compile-stutter
Vulkan: Don't pass a pipeline cache to MoltenVK
2026-10-16 12:35:45 +02:00
Henrik Rydgård 52174f743c Merge pull request #22600 from hrydgard/force-default-clock-speed
Compat: Require default CPU clock for the PopCap games (Bejeweled 2, Peggle, Zuma)
2026-10-16 11:26:19 +02:00
Henrik RydgårdandClaude Opus 5.5 01ed2c3cd5 Docs: Write down where shader compile time goes on MoltenVK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 11:23:35 +02:00
Henrik RydgårdandClaude Opus 5.5 a37e1f3660 GPU: Finish the depth raster when a list ends, before the CPU runs again
Syphon Filter reads its depth buffer back on the CPU for the light flares as soon as a list has
ended, without a DrawSync, and saw it before the worker had drawn the walls, so flares behind them
flickered. Now returning to the CPU after an END waits for the batches already submitted. Returning
at a stall doesn't wait, as Wipeout does that about 300 times a frame.

Wipeout on a Galaxy S8: 11% less wall time than before the worker (16% without the wait).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 11:12:27 +02:00
Henrik RydgårdandClaude Opus 5.5 ee0f6a4164 Vulkan: Don't pass a pipeline cache to MoltenVK
MoltenVK serializes the MSL compiles of pipelines that share a
VkPipelineCache, and we create every pipeline with the same one, so our
compile threads mostly waited on each other. On MoltenVK the in-memory
cache only saves the SPIR-V to MSL conversion, under a millisecond.

Wipeout Pure from a savestate on an M1, with no shader caches (Metal's
own cleared too): rendering blocked on pipelines for 1.2s instead of
2.9s, longest single stall 0.3s instead of 1s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:59:54 +02:00
Henrik RydgårdandClaude Opus 5.5 cc5f2588be CmdLine: Add --shader-cache=false
Turns off loading and saving the per-game shader cache for one run, so a
run can time shader and pipeline compiles like a first boot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:59:54 +02:00
Henrik RydgårdandClaude Opus 5.5 8e9b19ab7b Compat: Require default CPU clock for the PopCap games (Bejeweled 2, Peggle, Zuma)
They are very sensitive to timing on startup.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 10:33:37 +02:00
Henrik Rydgård ea8b92ebb7 Merge pull request #22593 from hrydgard/null-gpu-minimal
GPU: Add a null GPU (headless --graphics=null), and skip discarded AA lines like culled draws
2026-10-16 09:29:49 +02:00
Henrik Rydgård a838053e4c Merge pull request #22595 from kreinholz/master
Add https support to FreeBSD builds (which also unbreaks builds where curl is installed and detected by find_package). Also, explicitly include <clocale> to unbreak with newer clang
2026-10-16 02:36:42 +02:00
Kevin Reinholz aa2a55c883 Add curl support to naett on FreeBSD. (Unbreaks build on dirty/live systems where curl is installed and passes find_package() check). 2026-10-15 17:18:36 -07:00
Kevin Reinholz 75e89175ee Explicitly include <clocale> in draw_text_sdl.cpp. Fixes build with clang21.1.8 on FreeBSD amd64 (and potentially other systems using newer clang) 2026-10-15 17:13:20 -07:00
Henrik RydgårdandClaude Opus 5.5 b3c92cbb3b GPU: Skip discarded AA lines like culled draws
The hardware backends discard antialiased lines drawn with REPLACE (the
heuristic from #6483 and #12588) by returning from Execute_Prim before the
vertex or index pointer moves past them, and without taking their time. A
game that draws on from where they end then reads the wrong vertices. Skip
them like a culled draw instead, which does both, as the software renderer
and the null GPU do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:21:04 +02:00
Henrik RydgårdandClaude Opus 5.5 1d7487e5a7 GPU: Add a null GPU, and headless --graphics=null to use it
GPU_Null derives straight from GPUCommon and has no thin3d context, framebuffer
manager, texture cache or shaders. It runs display lists with the flow control,
bounding boxes, matrix loads (the hardware backends'), immediate vertices and
block transfers of the others, moves the vertex and index pointers past each
draw and curve, and charges the per-vertex and fill cost the software renderer
does. A DrawEngineCommon that queues nothing provides the vertex decoders.

It's for measuring the CPU emulation. From savestates, God of War, Tekken 6,
Wipeout Pure and LocoRoco flip the same frames in 10 emulated seconds as with
Vulkan and software, in 44-77% fewer instructions than Vulkan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:12:38 +02:00
Henrik RydgårdandClaude Opus 5.5 c440d53160 GPU: Move the hardware backends' matrix commands up to GPUCommon
So a backend that isn't GPUCommonHW can use them. No change in behaviour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:12:26 +02:00
Henrik Rydgård d36af22430 Merge pull request #22589 from hrydgard/cpu-timing-determinism
CPU: Make the interpreter and IR cores agree on timing
2026-10-15 22:27:54 +02:00
Henrik Rydgård 9df6ba2031 Merge pull request #22587 from hrydgard/ir-interp-chain
No-JIT speedups: IR interpreter block chaining, SIMD exact multi-dot, branchless skinning
2026-10-15 22:26:03 +02:00
Henrik Rydgård 5fa79c6871 Merge pull request #22574 from LunaMoo/texture-replacement-indexed
Add's support for indexed textures, luma-only texture replacement
2026-10-15 17:46:17 +02:00
Henrik RydgårdandClaude Opus 5.5 5631c3c9b6 Interpreter: Continue past branches where the IR cores do
The IR frontend compiles on past a conditional branch that isn't taken,
so the IR cores don't check downcount there, while the interpreter
ended its block at every branch. Events then landed on different
instructions, and a game's trace left the interpreter's within a few
hundred syscalls.

The interpreter now applies the same rule at run time: not likely, the
operands not decided by their registers, no branch in the delay slot,
no prefix pending after the delay slot, and the same instruction and
branch limits, counted the same way. It also goes on past a beql/bnel
over a break, which the IR frontend drops. The limits move to IRInst.h
so both use them.

With it, the interpreter, IR interpreter and ARM64 JIT produce the same
trace of syscalls and thread switches over 5 emulated seconds of God of
War, Tekken 6 and Daxter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:43:31 +02:00
Henrik RydgårdandClaude Opus 5.5 6a2fa313e6 CPU: Make the interpreter and IR cores agree on timing
The cores charged cycles differently and checked downcount at different
places, so interrupts and thread switches landed on different
instructions, and a game could take a different path on each core.

- IR: j, jal, bc1t and bc1f now charge their delay slot.
- IR: a syscall sees every earlier instruction charged, including a
  branch it's the delay slot of, and is charged once it returns. It used
  to charge itself first, or, in a delay slot, never.
- Interpreter: a skipped likely delay slot costs what the instruction
  would, as in the IR cores, instead of 1.
- Interpreter: both run loops go on to the end of what the IR cores run
  as a block before checking downcount, and charge each instruction as
  it runs.

The CPUEquivalence fuzzer now compares the cycles up to its terminating
syscall. With continueBranches off, the three cores now produce the same
trace of syscalls and thread switches, ticks included, over several
emulated seconds of commercial games. With it on, the IR cores leave the
interpreter within a few hundred syscalls, as it moves block ends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:43:21 +02:00
Henrik Rydgård afdbe691ef Merge pull request #22588 from hrydgard/crash-screen-info
Show the crash screen whenever the core stops with a runtime error
2026-10-15 17:41:53 +02:00
Henrik RydgårdandClaude Opus 5.5 bf7e32cdae Vertex decoder: Build the skin matrix without a branch per weight on ARM64
The steps skipped bones with a zero weight. With weights that vary from vertex to vertex that
branch mispredicts a lot, and it saves only a few multiplies. Now every weight is applied, with
fused multiply-adds, as the vertex JIT does. Without the vertex JIT (as on iOS): Tekken 4.5% fewer
cycles, God Eater 2.6%, God of War 1.7%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:05:52 +02:00
Henrik RydgårdandClaude Opus 5.5 4dedfa2b8b VFPU: Work out the four exact dots of a multi-dot side by side on ARM64
vfpu_multi_dot ran the exact dot four times, each summing across the lanes. Now each lane works
out one output, a term at a time, the same steps as vfpu_dot_neon, so nothing crosses lanes until
the rounding at the end. Inf and NaN still take the old way. The IR interpreter (no JIT, as on
iOS) runs every multi-dot through it: GTA 3% fewer cycles, Tekken 2%.

The VFPUDot test checks it against the reference too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:05:52 +02:00
Henrik RydgårdandClaude Opus 5.5 ab21def9d6 IR interpreter: Go straight on to the next block at exits
Each block was a call into IRInterpret from RunBlocks: register save and restore, the first
dispatch through the shared switch, then the shadow lookup in RunBlocks. Now the exits look up the
next block themselves and keep dispatching while downcount lasts, the same check RunBlocks makes.
Anything else (a hook, an uncompiled block, an invalid address, downcount gone) still returns.

Without the vertex JIT, as on iOS: 4.5-10% fewer instructions and 1-4% fewer cycles in the games
measured.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:05:16 +02:00
Henrik RydgårdandClaude Opus 5.5 b13945d9e5 Show the crash screen whenever the core stops with a runtime error
EmuScreen only draws the crash info when an exception was recorded, and
showed just the Resume/Reset/Back buttons otherwise. Several ways of
stopping the core with CORE_RUNTIME_ERROR recorded nothing:

- A bad memory access in JIT code that MemFault can't skip, either
  because it can't decode the host instruction or because ignoring is
  only configured through the exception action, went to the crash
  handler, while Core_MemoryException returned early under the default
  Ignore action. A halting access is now always recorded.
- HLEReturnFromMipsCall on a corrupt stack, and a failed LoadExec, set
  the state directly. They now go through Core_HLEError, which records a
  new HLE exception type with a message that the crash screen shows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:03:12 +02:00
Henrik RydgårdandClaude Opus 5.5 cd59a16d85 GPU: Rasterize software depth on a worker thread
The depth draws were queued and rasterized on the emu thread at each render target change, in
Wipeout about 40 small batches a frame, and around 40% of that thread's time. Now a render target
change hands the batch to a worker thread and carries on. Waiting for it is left to whatever reads
the depth buffers: DrawSync and ListSync, block transfers, framebuffer copies, showing a frame,
savestates and shutdown, and the texture cache when the texture or CLUT it reads overlaps a depth
buffer still being written.

Batches grow as needed instead of being preallocated at full size. A full batch is now handed over
and a new one started, where the draw used to be dropped.

Wipeout headless, output unchanged: 16% less wall time on a Galaxy S8 (8.2 s to 6.9 s for 10
emulated seconds). On an M1, where headless waits on the GPU, the emu thread uses 38% less CPU time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 16:42:45 +02:00
Henrik Rydgård 8c379781aa Merge pull request #22585 from hrydgard/dnas-install
Install Killzone: Liberation's DLC download without a PSP
2026-10-15 16:33:34 +02:00
Henrik Rydgård d84c27f9af Merge pull request #22586 from hrydgard/cpu-equiv-riscv-loongarch
RISC-V and LoongArch64 JITs: Accurate dot products, recent IR features, and fuzzer fixes
2026-10-15 16:33:06 +02:00
Claude 1692aad51e Texture replacement: Optionally dump paletted textures as 8-bit palette PNGs
saveIndexed = true in [options] saves the CLUT4/CLUT8 textures listed in
[indexed] as palette PNGs, saveIndexed = all saves every one of them.
The file is named <hash>_indexed.png, with the CLUT hash zeroed so one
file serves every palette of the texture. The other textures are dumped
as before.

The indices are decoded from PSP memory and the palette comes from the
current CLUT, so the dump looks like the game's texture. Add the file to
[hashes] and [indexed] to use it as a replacement.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 16:15:11 +02:00
Henrik RydgårdandClaude Opus 5.5 09a9be3484 Only look for a DRM BB install package when booting a game that had one
Saves every other game a memory stick access at boot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:45:45 +02:00
Henrik RydgårdandClaude Opus 5.5 179595c8f7 Install Killzone: Liberation's DLC download without a PSP
The download (a DRM BB install package) is installed by the game's boot program only when it's
started with -i, which nothing does any more. PPSSPP now installs it itself when the game boots, the
way psheet does. Unpacking the package needs only known keys: a fixed one from psheet, and the
secure install ID, which comes out of the package's MAC.

The module inside is a type 3 PRX whose first step is encrypted for KIRK command 2, with a key
that isn't known. Command 2's result is the same on every PSP, though, so it is now stored for the
EU and US modules (taken on a PSP through sceMesgIns_driver_D062B635, the call the install makes,
and cross-checked against an install). The type 3 decrypt uses it in place of that step, so the
installed module needs no Fuse ID.

--re-decrypt also takes such a package now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:39:11 +02:00
Henrik RydgårdandClaude Opus 5.5 d4a2cd3e43 RISC-V and LoongArch64 IR JITs: Name the fixed code routines, and profile on Linux
Both now list their routines (GetFixedCodeLabels), so the disassembly
names jump targets and --jit-profile charges routine samples to the
calling block. The profiler samples them on Linux, and its report
disassembles their host code per instruction (RISC-V's in 2- and 4-byte
steps).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 08b398bbd1 LoongArch64 IR JIT: Vec2 packs with LSX, and join two mapped pairs into a quad
With LSX, the Vec2 pack and unpack ops went to the interpreter, as they
addressed the lanes of a pair one by one. They're now an interleave or a
pick of halfwords in the pair's register.

That makes pairs, so mapping a quad whose halves are mapped pairs (two
vs2i.s feeding a vi2f.q) joins them with VILVL_D rather than storing both
and loading the quad back, like the ARM64 JIT's ZIP1 (3d20d51c70).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 a80cea2b9e RISC-V and LoongArch64 IR JITs: Vec4 vmin, vmax, vslt, vsge and vi2f
The ops from 108e66f2d1, so both now get moreVec4Ops. With LSX they're
vector code: vfpu_minmax_key() of each lane picked by VBITSEL, a compare
ANDed with 1.0, and VFFINT with a scale. RISC-V and LoongArch without LSX
do them lane by lane, vslt and vsge as a compare into a GPR rather than
through fpcond.

Vec4VfpuMultiDot stays ARM64-only, so the frontend still doesn't emit it
for these.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 59b6b4de78 RISC-V and LoongArch64 IR JITs: Look up the block for a jump to a register at the jump
Like the ARM64 JIT (e33f38afa6), each jr and jalr does the dispatcher's
lookup itself, for an indirect branch of its own, and goes to the
dispatcher when out of downcount or with no block yet.

A bad target now faults in the block rather than at dispatchFetch, so the
fault handler takes any fault in the block shadow as a bad jump, with the
masked target from the address. That covers ARM64's too, which was handled
as a bad memory access.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:37 +02:00