Renames played_seconds to seconds_played. On SDL, the OS build is now
uname's sysname and release on Linux and Mac.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ticks don't measure time played, since games change the CPU clock.
played_seconds is emulated time. ticks is still sent for now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load the PARAM.SFO before pushing the installer — the same g_gameInfoCache pattern the game list already uses for update PBPs — and pass info->GetTitle(). --By MIMO v2.6 Pro
On an Adreno 506 (Vulkan 1.0.20 driver, Redmi Note 4), the ubershader's
loop over the lights gives wrong lighting: LittleBigPlanet's Sackboy and
lit scenery came out pale. The per-light shaders render it right. Uses
the existing UNIFORM_INDEXING_BROKEN flag, set so far for old Mali.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an Adreno 506 (driver V@145, Redmi Note 4), fetch can return black for
pixels an earlier draw in the same render pass wrote, which showed up as
dark noise on LittleBigPlanet's foliage. Framebuffer copies render it
correctly, and that frame needs only a few of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With EXT_shader_framebuffer_fetch, fragColor0 is inout. On Adreno 506
(driver V@145, Redmi Note 4), reading it back after writing it gives the
wrong value: the color write mask path packs it and LittleBigPlanet's
objects drew solid black. Build the color in a local and write the output
once at the end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Present multiview game framebuffer layers as frontend stereo views, sharing the tracked cylinder camera with OpenGL. Resize presentation images before drawing when VR changes.
Recreate cached Vulkan backbuffers after GPU device-loss handling so frontend resets and resolution changes cannot start duplicate rendering threads.
The crash screen and crash reports read it from the exception info,
which only memory, FPU and HLE errors filled in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When reporting is enabled, a crash is sent as a report message when the
crash screen first shows it. The message is a template per exception
type, so the server groups crashes of the same kind, and the value has
the details and the MIPS stack trace.
When reporting is off, the crash screen gets a "Report crash" button
that sends the same report once, to report.ppsspp.org. Like the
compatibility reports, it isn't offered when cheats, plugins or other
settings that make reports unreliable are in use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A broken filters file still parses and MSBuild ignores it, so the build
stays green while Visual Studio's folder tree falls apart. The check
catches nested items, undefined filters, duplicates and items that the
filters file and the project disagree on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ui_images used a forward slash in an undefined filter, KL4E.h, upload and
packages.config were missing, and two atlas entries were for files the
project no longer lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a slow device, pausing again right after saving a state could show
the slot's Save and Load buttons enabled while the save was still being
written. The slot now has its buttons and screenshot disabled, with a
spinner, until the save ends, and the menu refreshes then.
The refresh message is now posted when a save ends any way (written,
failed or refused), after it has left the pending list, so the refresh
can't still see it as pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A zipped PS3 save folder has a PARAM.SFO and ICON0.PNG, the same layout
as a PSP save, so it was offered for install as one. PS3 saves have
CATEGORY SD in the SFO (PSP saves have MS) and a PARAM.PFD; either now
makes the zip unrecognized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It's now in release builds too, Android's included, and on iOS, UWP and
libretro, so no build runs a rarely tested configuration. NO_PROFILER still
leaves it out.
The scopes per GE CALL (gpu_call), per primitive (execprim) and per draw inside
a flush (renderman_q, applydrawstate, soft) are gone: their time shows in
gpuloop and flush (now named the same on every backend, D3D11 included). A
software-transformed flush is labelled "soft" instead. PROFILE_THIS_SCOPE_DYNAMIC
is gone too; its one user, the curve code, labels a "curve" scope with the kind.
The profiler windows and Tools/profile_report.py show how many scopes run per
frame, and headless ends a profiler frame per emulated one, so --profile-log
works there as it says.
What a scope costs while off, timed in the unit test on a Galaxy S8 (20 million
of them against the same loop without): nothing measurable. It's two
instructions there (a load of the switch and a branch), up to four elsewhere.
GTA: Liberty City Stories runs about 1,640 scopes a frame, so a few thousand
instructions in about 31 million. (The 0.39% more instructions measured over
whole runs is about 64 per scope, so most of it was something else, likely
threads that spin while waiting.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sampling profiler shows its costs better. Some were per pixel, and two in
one scope kept USE_PROFILER builds from compiling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The category's static has a thread-safe init guard, which every scope checked
before the profiler's on switch. Now the lookup is in a lambda that only runs
while the profiler is on, so a scope costs a load and a branch while it's off.
A non-literal name no longer compiles there (the lambda can't capture it); that
takes PROFILE_THIS_SCOPE_DYNAMIC.
Measured on a Galaxy S8 with ppsspp_headless, 20 emulated seconds of GTA:
LCS driving from a savestate, 12 runs each: compiled in and off, it runs 0.39%
more instructions than a build with NO_PROFILER (the runs spread 0.35-0.47%),
and no measurable CPU time difference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
attach drives one PPSSPP that's already running with --debugger, like the
app. With --adb it's the app on an Android device: the port is forwarded and
screenshots are taken with screencap, cut to the game's image (--rect) and
scaled to 480x272, so references and positions mean the same as headless.
screencap also doesn't pause emulation, unlike the debugger's screenshot, so
it can watch a run that's being profiled.
until takes --or REF@X,Y for screens that differ between runs (a menu that
only shows with a save), and a script line "if REF --box X,Y : COMMAND ;
COMMAND" runs the commands only when the screen shows the reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--profile-log=FILE writes each frame's per-thread totals as JSON lines, and
for each slow frame every scope of at least --profile-span-ms that ended in it,
with its own time. A frame is slow if it takes at least --profile-hitch-ms and
1.5 times the median of the frames before it (a 30 fps game takes 33 ms every
frame). Slow frames also get a line in the log naming their longest scopes.
Tools/profile_report.py tells stalls (frames that fell behind the emulated
time they covered, counted with a new vblank scope) from frames where the
game just didn't flip, and sums up what the stalls' time went to.
PROFILE_LABEL names a scope: syscalls get the function's name, CoreTiming
events (now a scope) the event's, module loads the file and pipeline creation
the pipeline's tag. New scopes cover sleeps (labelled with their reason, and
the frame-idle wait now goes through sleep_us), Vulkan's waits for the GPU, the
render thread, pipelines and shaders, and GPU memory copies, uploads and
framebuffer readbacks.
The profiler is now compiled in everywhere except iOS, UWP, libretro and
Android release builds, and off until --profile-log, the frame profile overlay
or an ImGui profiler window turns it on. Off, a scope costs a load and a
branch. While on, syscalls compiled by the JIT go through CallSyscall, which
has the scope (decided in GetQuickSyscallFunc, instead of at build time in
each backend). The frame profile overlay's slot is no longer build-dependent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frame profiler kept its state in 4 thread slots, and every thread after
the fourth shared the last one's nesting depth and stack without locking.
Several threads entering and leaving scopes at once corrupted the depth,
which then indexed outside the arrays and crashed.
Now each thread that enters a scope gets a lane of its own: a ring of begin
and end events that only it writes. A thread that exits hands its lane to the
next new one; lanes are never freed, so readers can walk them without locks,
and a reader checks that an event wasn't overwritten while it copied it. A
literal scope name is looked up once per call site.
The per-frame totals (time in each category, not counting nested scopes) are
worked out from the events at each frame end, so the on-screen overlay keeps
working. The ImGui debugger gets a CPU profiler window, with a zoomable
timeline of each thread's scopes and frame ends plus a table of per-frame
totals, and a Frame profile window with the overlay's graph, per thread.
The Vulkan render thread has scopes for rendering, submitting and presenting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Android, Save picks a place with the system's save picker, through
a new BROWSE_FOR_FILE_SAVE implementation (ACTION_CREATE_DOCUMENT).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exporting a single save stays in the savedata manager's popup. Exporting
a whole game's saves is rarer, so it moves to the game's menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An outline with a zero radius isn't drawn at all, so use square corners
instead. Medium width, so it doesn't look like the gaps between rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page-wide button depended on the game the screen was opened for, which
the screen doesn't show. The popup knows the game from the save itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where the file can't be shown in its folder (Android, iOS), "Create frame
dump" in the dev menu now zips the dump and opens the share sheet. The
frame dump hotkey does the same when its message is clicked, so the game
isn't interrupted. Both go through OnFrameDumpCreated(), which moves the
share to the main thread, since the recorder calls back on the emulation
thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#23031
The savedata popup gets an Export button, which zips the save folder (or
a savestate and its screenshot) and hands it to ShareFilesAsZip: the
share sheet on Android and iOS, a Save As dialog on Windows. The zips
use the layouts the zip import already recognizes, so they import back.
Opened for a game, the manager outlines that game's saves and states,
scrolls to them, and offers to export all of the game's saves in one
zip. The game screen gets a button that opens it that way.
Installed data is never exported. It looks like a save (same PARAM.SFO
fields), but games write the installed files with sceIo rather than the
savedata utility, so they're missing from SAVEDATA_FILE_LIST.
IsInstalledGameData() counts a folder as installed data when the files
outside that list add up to 10 MB or more. The game screen's split
between save and install sizes now uses it too, instead of treating any
folder over 10 MB as installed data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>