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>
Splatted, the matrix took sixteen vector registers, and on ARM64 the compiler reloaded them from the
stack every iteration. Multiplied by lane, it fits in four, with the same operations in the same order.
About 14% fewer samples in the function in Colin McRae on a Cortex-A53, where it's the top C++
function on the emu thread. Most of what's left is waiting for the vertex loads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A draw with a write mask the hardware can't do reads the target from a copy, and takes a new copy
for every flush. But it only reads the bits its mask keeps, and doesn't change them, so while the
same mask draws on, without blending or a logic op, the first copy serves every draw. DBZ Tenkaichi
Tag Team draws its shadow map one bit of red at a time, about 26 copies a frame, now about 4.
The copy is dropped when the target or the mask changes, on any other draw, clear, block transfer or
stencil upload, and at the start of a frame. TEXFLUSH no longer drops it, only the self-texturing copy.
DBZ, 20 emulated seconds on an M1 Mac: 1.9 s to 1.1 s. On a Galaxy A21: 10.0 s to 9.7 s per 10.
The 1141 dumps in ppsspp-framedumps render the same, on Vulkan and OpenGL on macOS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The state is read and decompressed on a worker while the game keeps booting. On a slow host that
got far enough to reach different code, and with MX vs. ATV Untamed without a firmware, its boot
crash, before the state landed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceMpeg runs the real mpeg.prx by default since #22309, and boot only puts the HLE back when neither
the firmware nor the disc has one. MX vs. ATV Untamed carries an mpeg.prx it never loads, so without a
firmware it got the real sceMpeg, then asked sceUtility for the firmware's module, which wasn't there.
Every sceMpeg import stayed a missing stub, and the game crashed at boot writing through what the
calls returned.
Now that load turns the library over to our HLE and resolves the waiting imports against it. The
latched HLE flags change with it, so a savestate made afterwards restores the HLE too. Atrac goes on
to set up its HLE memory the same way. For a library the user disabled themselves the warning still
shows; for one that's real by default, like the boot-time check, it's only logged. MX now plays its
intro video without a firmware, and still loads the real module with one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 1.5x music browser reads PSP/MUSIC through msoem0:, the memory stick
with 8.3 names in the OEM code page, which wasn't mounted. It lists folders
with ioctl 0x02425051 on an ordinary directory fd (sceIoDread plus the name
in UTF-16), and sets a value on fatmsOem0: with devctl 0x02405852 at boot.
Formats from fatmsmod.prx and music_plugin.prx in 1.50.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#22890
Before 3.80, the XMB's music browser doesn't list a folder with
sceIoDopen/sceIoDread. It asks the FAT driver directly, with the path as a
list of UTF-16 components: devctl 0x02415864 starts a directory search and
returns a dir fd, read entry by entry with ioctl 0x02425069. 0x02435865 and
0x02435860 are getstat and open, with the path given the same way. None of
these were implemented, so firmware 3.71 and older found no tracks.
The argument layouts are from fatmsmod.prx and music_browser.prx in 3.71.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
theme_type and CONFIG/SYSTEM/XMB/THEME's color_mode, wallpaper_mode,
system_color and custom_theme_mode now persist in the config. Their
defaults (0) are the firmware's schema defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shell writes the default of every registry key added after the stored
category_version, and some of those (umd_autoboot) are now mapped to our
config. Each firmware's version is the default in its registry schema
table, in vshmain.prx (common_util.prx before 2.00). Installing a firmware
now stores it, and headless sets it from the NAND's firmware.
Also builds the config setting lookup in headless and libretro: without
g_Config.Init(), sceReg served its static dump instead of the mapped
config values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
owner_name and time_zone_area now read from and write to sNickName and
sTimeZoneArea. Also maps language, the date/time keys, summer_time and
parental_level. The config values are applied in the merged category view,
so reads by handle and the key info sizes see them too, not just
sceRegGetKeyValueByName. language reads through GetPSPLanguage() so that
the -1 'auto' setting reads as an actual language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WaitIfWriting compared addresses as VRAM offsets, which mapped RAM addresses onto VRAM too. Midnight
Club L.A. Remix loads CLUTs from RAM that land on its depth buffer's range that way, and the emu thread
waited for the depth worker at each, about a quarter of its time on a Galaxy A21. The depth buffers are
all in VRAM, so other addresses now return at once.
Midnight Club, 10 emulated seconds: 39.6 s to 24.0 s on the A21 (32.9 s in 1.20.4), 2.67 s to 2.32 s on
an M1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The smart 2D filtering check and the exact one for depal walked the rectangles separately. Now one loop
does both, stopping once neither can hold, and a rect of no size ends both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shader depal filters by hand, with four texel and four palette lookups per pixel. DBZ Tenkaichi Tag Team
does two full-screen through-mode depal passes per frame, and on a PowerVR GE8320 (Galaxy A21) they took
DBZ from 8.3 to 14.4 seconds per 10 emulated seconds once #22204 had made framebuffer depal work again.
They're 1:1, where bilinear gives the nearest texel anyway, so rectangles that put every pixel center on
a texel center (equal UV and position deltas, at a whole-texel offset) now count as pixel mapped for
depal, which turns the filter off. Unlike the smart 2D filtering check, this changes nothing on screen:
the frame dumps render identically on Vulkan and OpenGL, 69 of them through this path. DBZ is down to 9.2s.
The depal uniform is also repacked when pixelMapped changes between draws, rather than only when the
texture is reapplied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every instance after the first got PPSSPP_ID 1 on macOS, so they shared
the same ad hoc MAC and IP, and secondary instances saved the config.
- flock() on a shm object fails with ENOTSUP on Darwin, so the counter was
never updated. Lock with a named semaphore there; keep flock elsewhere.
- ftruncate() on a shm object that already has a size fails with EINVAL on
Darwin. Only size the object when it's new.
- Shutdown unlinked the shm object even while other instances were still
running, so the next one to start got a fresh counter and ID 1 again.
Only unlink when the last instance exits.
The loader streams it (HTTPFileLoader), but headless rejected anything File::Exists doesn't
find. This lets a phone run a game served from another machine, over adb reverse or the LAN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP never writes the info struct's length, and on a call without new data writes updated = 0 and
the last data's timestamp again, 0 before any came (pspautotests net/adhoc/pair/gamemodeinfo).
PPSSPP wrote the length and left the timestamp alone without new data. Shaun White Snowboarding
reads the timestamp either way, from a struct on its stack, and keeps the newest it has seen as the
time of the last update; stack garbage got in there, and the host gave up on the game a moment
after the match started ("The game is no longer available").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, with 8192 bytes in flight from an 8 KB send buffer, poll leaves out SEND and a
non-blocking send of 16 more returns WOULD_BLOCK until about half the buffer has drained, and poll
leaves out FLUSH until the data has arrived, which takes as long as a flush (pspautotests
net/adhoc/pair/ptpsendready). PPSSPP reported both at once and took any send. The in-flight bytes
from the flush model now drain linearly until their delivery time; sends that don't fit wait, and
GetPtpStat counts what's left. Relay PTP sockets now record their buffer size, which they didn't.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait polled the emulated clock with a new connection every few milliseconds, thousands of them
in a long wait, each with its own thread and subscribers on the PPSSPP side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Feed() held pendingLock_ across Core_RunOnCPUThread(). Once sendFeed_ was set, the CPU thread's
next flip waited for that lock in FlipListener(), while Feed() waited for the CPU thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DisplayFireFlip() copied the listener list and called the copy without the lock, so a debugger
connection closing on its own thread could free its GPU stats subscriber while the CPU thread was
about to call it. Headless crashed (or hung) in WebSocketGPUStatsState::FlipListener() with
scripts that open a connection per command. Hold the lock across the calls, as LogManager does for
its callbacks, so __DisplayForgetFlip() returns only once the listener can't be running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP returns size 0 without touching a buffer too small for one entry, and GetPeerList refuses
a negative size with INVALID_ARG (pspautotests net/adhoc/pair/listsizes). PPSSPP cleared the
whole buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a PSP does, where PPSSPP differed:
- A PtpOpen or PtpListen on a port in use uses up a socket ID, as PdpCreate's does.
- A connect to a MAC that isn't in the group would block or times out, and is refused once the
retransmits would have run out, (count + 1) * interval. It returned INVALID_ADDR at once.
- PtpAccept's flag must be 0 or 1.
- A blocking broadcast returns 0, so one the host can't take in time is dropped, not a TIMEOUT.
The FIXMEs and TODOs other tests already answer (socketids, adhocctl, gamemode, ptp) now say what
the PSP does instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pspautotests net/inet/calls measures what socket, bind, listen, connect,
accept, the socket options, getsockname and getpeername, shutdown, close
and the address conversions cost on a PSP when they don't wait: from 3
us for an address conversion to 76 us to close a TCP connection (95 us
with RST), and 4 us on a socket that isn't open. They cost about 1 us
each here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, select and poll with nothing ready return about 120 us after
their timeout (pspautotests net/inet/timing), wait at least about 330 us
for any timeout from 10 us up, and don't wait at all for a timeout of
1 us. We waited exactly the timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP a non-blocking flush right after a send returns WOULD_BLOCK, and 0 once the data is in
the peer's buffer, about 4.5 ms plus 2.4 us per byte, and GetPtpStat counts the bytes as unsent
until then (pspautotests net/adhoc/pair/ptpflush). PPSSPP's flush returned 0 at once. NBA 2K13's
host sends the match info, flushes until it gets 0 and closes; with the instant flush it closed in
the same frame, and the joiner, which checks for a disconnect before reading, never read the last
chunk ("There was a problem transferring the match information").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetScanInfo returned nothing while in a group. On a PSP the list from the last scan stays after
Create, Join and Disconnect, and only a scan attempted while connected empties it (pspautotests
net/adhoc/pair/scaninfo, added here along with ptpflush). NBA 2K13's joiner looks for the group it
just joined in the list and leaves when it isn't there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP refuses it immediately (pspautotests net/adhoc/ptp, "Connecting to nobody"), while the
relay held it for about 4 seconds before giving up. NBA 2K13's joiner connects to each entry of
its peer table in turn, itself first, and gave up on the group before it got to the host.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuilding the stencil after a reinterpret (89fabe21fb) copied the whole
destination to a temp buffer and redrew the stencil from it in a pass of its
own. Tantalus games reinterpret about 70 times a frame, and on an M1 with
Vulkan that made MX vs ATV Untamed 44% slower (GPU bound).
Now a stencil variant of the reinterpret shader reads the source the same way
the color reinterpret does and draws the stencil from its alpha, into the same
rectangle right after it, so it lands in the same render pass and needs no
copy. One draw with stencil export, otherwise a clear and one per alpha bit.
The 1141 dumps in ppsspp-framedumps render identically before and after, on
Vulkan (export) and OpenGL (per bit) on macOS. Split Second, OutRun and
Ultimate Ghosts 'n Goblins dumps go through this path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ninja only knew git-version.cpp from its timestamp at the start of the
build, so when the GitVersion step rewrote it, the object wasn't rebuilt
until some later build happened to notice. An Android APK built right
after a commit could carry the previous commit's version string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>