Each hint quotes the old label as that language showed it. Khmer and Lao
get the English text for now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Force real clock sync (slower, less lag)" no longer described it: it costs
little CPU, and falls back to normal timing by itself when the host is busy.
The config key stays until it's on by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slow display list can hold up emulation for several milliseconds on a slow
device. Lag sync catches up afterwards, but the output keeps pulling in the
meantime, and the fixed 4.4 ms over the output's period wasn't enough. Each
sync now records how far behind the wall clock emulation is, and the most
over the last five seconds is added to the target.
The granular mixer's queue cap no longer makes room for a frame's burst of
audio while pacing; it adds the room for stalls to that for two output reads
instead, up to a frame. The classic resampler drops a lasting excess above
5 ms rather than 10, to settle sooner after the room shrinks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--audio-granular picks the granular mixer (the Audio sync mode setting), and
the sink then reports the queue's level and cap, and its underruns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While lag sync paces, audio arrives every millisecond or two instead of a
frame's worth at a time, so the resampler's target drops from 40 ms to the
output's period plus 4.4 ms (or 20 ms where the platform doesn't report a
period), and goes back on a fallback to bursting.
The rate adjustment took around ten seconds to drain a startup fill down to
a small target, so a lasting excess (the lowest level over a quarter second
more than 10 ms over the target) is now dropped in one go.
The audio debug overlay shows the target in ms, the lag sync mode and the
dropped excess.
Headless gets --audio-sink=FRAMES, which pulls audio like an output device
with that period and prints how full the buffer ran and how often it ran
dry. docs/lag-sync.md has how to use it, and the Windows measurements: with
a 10 ms period, the buffer averages about 16 ms paced against 35-38 ms
bursting, with a 3 ms period about 9-10 ms, without underruns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sleep_precise and sleep_approx shared one waitable timer. It has one due
time and releases one waiter, so when two threads slept at once, the second
one's due time replaced the first's and one of them waited forever (and the
other woke at the wrong time). Lag sync sleeping on the emulator thread while
another thread slept was enough.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lag sync waited with sleep_precise, which on Windows spins the last
millisecond, so its 1 ms waits were nearly all spinning. It now sleeps
with a new sleep_approx (no spin), only once 2 ms or more ahead, and asks
for as much less as the OS has been waking it late. Being late doesn't
add up, since the goal is measured from a fixed base.
Pacing every millisecond only pays with time to spare. Each second, the
share of wall time spent waiting (in lag sync or in the frame throttle)
is measured. Under 10%, it falls back to bursting with the frame
throttle, as without lag sync, and goes back after two seconds over 25%.
GTA VCS on an M1, at --speed values around the host's limit, now runs
as fast as without lag sync or slightly faster. At 1x it costs about 1%
of a core in cycles over bursting, the same as before.
Headless --frame-stats prints lag sync's sleeps, lateness and fallbacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The IR frontend dropped a branch or jump in a delay slot (and reported it),
or approximated it with a link to +12 and an exit into the delay slot. Such
a pair now ends the block with the new BranchPair pseudo-op, which the
interpreter runs until it's out of the delay slot, so every core does what
tests/cpu/delayslot/branch recorded. It's an ordinary Interpret, so the
native backends need nothing new, and other branches compile as before.
This removes the delay-slot special cases from the branch compilers and
ResolveNotTakenTarget(), with the eight reports about them. The CPU fuzzer
now runs its d class (branches in delay slots) on every core, and they
agree, cycles included. cpu/delayslot/branch moves to tests_good.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As tests/cpu/delayslot/branch shows: in a jump's delay slot, a jump or a
taken branch replaces the jump's target, and a link there saves that target.
In a conditional branch's delay slot, a jump is ignored but still links. A
jump skipped by a not-taken likely branch still jumps, with the next
instruction as its delay slot, but doesn't link (Brooktown High relies on
the jump).
The CPU fuzzer gets a d class, which puts branches and jumps in delay slots.
Only the interpreter runs those cases until the IR frontend handles them;
PPSSPP_CPUFUZZ_ALLCORES runs them on every core.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Branches and jumps in the delay slot of another branch, recorded on a PSP.
Neither the interpreter nor the IR frontend matches it yet: a jump in a
jump's delay slot should take its own target, links in a jump's delay slot
go to the outer jump's target, a conditional branch in a jump's slot should
override it in IR, and a jump annulled by a not-taken likely branch still
jumps but doesn't link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Queue fill and cap (in granules and ms), under/overruns, output rate, read
size and frame time, replacing the "no stats yet" placeholder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The queue cap was four output callbacks plus a frame of audio. Bluetooth and
other outputs without a fast path call back with large chunks, so that came
to about 100 ms there. Now it's the classic target (which also honors Extra
audio buffering), raised for slow games' per-frame bursts and to keep half
the queue above one callback's read, plus a granule for the overlap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since we started booting PSP/GAME/<DISC_ID>/PBOOT.PBP, a modified disc that
has the latest update built in (with its APP_VER raised to match) gets its
executable replaced by the stock update's. With fan translations, that runs
the untranslated executable, which can reject the translated data files.
Only boot the update when its APP_VER is higher than the disc's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DriverManagerScreen.cpp included VulkanLoader.h, which pulls in X11 on
Linux, whose Screen typedef clashes with our Screen class.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Google reports PPSSPP crashing at startup on Android 17 internal builds,
in linkernsbypass (part of libadrenotools), whose constructor walks
libdl's dlopen to find the linker's internal __loader_dlopen. It ran on
every launch, custom driver or not.
Switch libadrenotools to the hrydgard fork, where linkernsbypass resolves
those symbols on first use, so only users who picked a custom driver get
there. Also disable custom drivers on Android 17 and its previews until
they've been tested there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#21802
The estimate from #21678 made the first-frame readback read back a part of
any buffer drawn in through mode. In the Japanese GTA games, that writes a
64x320 strip of the 32-bit loading screen into VRAM, where the two 16-bit
display buffers later go. Nothing else writes that memory, and when pausing,
the game shows one of them for a few frames before drawing it, which we then
display from memory, pink strip and all. Before, it was all black.
The estimate was for small render targets (Evangelion Jo's menu text), so
limit it to buffers smaller than the screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Releasing the button over the tabs also selected the top visible register,
so a double click there opened the change value dialog for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 16 pixel left margin of the register rows was excluded from every click,
including on the tab row, which has no margin, so most of "GPR" didn't
respond.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The memory window's single-letter shortcuts (F, B, H, U...) were taken even
when it wasn't focused, opening the search form and focusing its text field
while the game had the keyboard. Only take them when the window is focused,
like the disassembly window does.
Key events don't reach imgui while the debugger is hidden, so a key held while
closing it stayed down. Clear imgui's key state while it's hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offset 0 only helps with a real PSP running the old Pro Online plugin. Also
say that all players must use the same offset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing calls it any more. Its SSE path also had the left and right
volumes swapped (_mm_set_epi16 takes its arguments high lane first).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On by default. Off gives the hardware's unfiltered rate conversions
around the reverb: the send decimated by keeping the second frame of
each pair, and the output zero-stuffed at double level. With it off,
pspautotests' audio/reverb/upsample matches its .expected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ME decimates the send by keeping one frame of each pair, so
everything above 11khz aliases into the reverb (pspautotests'
audio/reverb/upsample: a 22.05khz tone reaches it as DC). Use the
output side's half-band filter at half gain instead. Splitting the send
into its even and odd frames first makes each tap one multiply-add for
two outputs and both channels.
This deliberately departs from the hardware, like the output side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "off" path decimated the send to 22khz and then doubled every
sample, which folded everything above 11khz down. The ME runs a
separate full-rate loop for it instead: pspautotests'
audio/reverb/upsample shows a 22.05khz tone surviving with no reverb
selected. The comments on the reverb's resampling now cite that test
too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Filter the whole grain at once, from a float history. Each input frame
gives one vector holding both output frames for both channels, already
in output order, so each tap is one load and one multiply-add.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A signed saturating narrow to 16 bits. Vec4U16::FromVec4S32 can't stand
in for it: it saturates unsigned on SSE and LSX and truncates on NEON.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ME upsamples the 22khz wet signal by putting it on every other
44khz frame at double gain. That leaves a full-amplitude mirror image
of the reverb just below 22khz, which eats the mix's headroom: in
Outrun 2006 it put about a fifth of the output energy above 15khz and
made the final mix clip hard, mostly in the left channel.
Replace it with a 24-tap half-band interpolation filter (flat to 9khz,
-69dB from 13khz up), at the same average level. This deliberately
departs from the hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- StereoResampler: the interpolation multiplied a 17-bit difference by a
16-bit fraction in 32 bits, which wraps when adjacent samples are more
than half the range apart. Same with the volume multiply on a loud mix.
- StereoResampler: the padding after an underrun took left from the right
sample and right from the left.
- SAS reverb: the four comb products could sum past 32 bits, and the
return multiply overflowed once the output passed 16 bits.
- SAS: a fast decay can leave the envelope height negative for a sample,
which mixed that sample inverted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pspautotests modules/selfunload and modules/idlist show:
- sceKernelGetModuleId and the self stop/unload calls act on the module the caller's code is in,
not the module that created the thread.
- Address lookups cover text_addr up to the text, data and bss sizes, and refuse kernel addresses
from user mode (ILLEGAL_ADDR).
- sceKernelStopUnloadSelfModule takes (argSize, argp, status, option) and exits with 0. It was
unimplemented for user mode and given the wrong arguments for kernel mode.
- When module_stop returns 0, the module is unloaded and the calling thread exits with its exit
code. A return of 1 gives CAN_NOT_STOP; any other nonzero value gives NOT_REMOVABLE. In both
cases the module stays started and the status gets the value.
- GetModuleIdList refuses a NULL or kernel count pointer.
- text_size leaves out .MIPS.abiflags.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>