The GL contexts' CreateDrawContext made the thin3d presets and ContextReset made them again,
leaking the first set on every context reset. Only ContextReset makes them now.
The software renderer draws the same frame dumps on arm64 as on x86-64, so the frametests run
on the arm64 runner too. b.sh --aarch64 and setup-cross.sh aarch64 give a headless build to run
them under qemu locally.
Readback buffers are allocated when the readback is recorded rather than on the render thread,
so only the thread that records frames touches VMA. The allocator is created externally
synchronized, and VMA is built with no-op mutexes and plain counters instead of std::mutex and
std::atomic.
Headless runs Vulkan offscreen in builds without SDL too, and CI runs the headless tests on
lavapipe, skipping the pixel tests a hardware backend doesn't match the software references on.
sleep_ms and sleep_us go, with the thread manager test waiting on an event instead, and so does
the unused portable-file-dialogs header.
The core no longer sleeps to pace itself: the frontend runs retro_run at
its own rate, so the frame throttle, the idle-vblank delay and the real
clock sync event (and its core option) are gone, and a stepping CPU hands
straight back instead of waiting 2 ms. Waits that polled now park on a
lock-free eventcount through Common/Thread/ParkingLot and are woken by
whoever changes the state: a paused GL emu thread, which also queues a
marker behind its last frame so pausing runs the render queue up to it
instead of polling; async I/O results; the software renderer when its
queue is full; Vulkan pipeline compiles; and debugger requests once
stepping ends. Boot joins the loader thread, cache shutdown joins its
read-ahead thread, and the slow-I/O simulation is removed.
The path browser, the HTTP and WebSocket servers, the Windows console and
the game info cache are left out of the core build. libretro/test gains
lrgltest, which drives the GL backend on a surfaceless EGL context through
every path that pauses or stops the emu thread; run.sh and the image
checks CI job run it on llvmpipe.
Deflate, zlib and gzip streams go through rinflate and rdeflate, zstd
through rzstd, CHD images through rchd, zip archives through
rzip_archive and PNG through rpng. The bundled libraries, their build
glue and the system-library options are gone; adler32 and uncompress
keep zlib's ABI for the prebuilt FFmpeg archives that still call them.
libretro-common is one CMake library that the core, the headless tool
and the unit tests all link.
The reads take shorter paths on the way. CHD decodes straight from the
loader's reads and a range of blocks at a time, and a file read at any
alignment decodes into place; CSO keeps one inflate stream instead of
one per block; a plain ISO serves a file read with one read of its exact
bytes; a stored zip member is read in place and a deflated one through
restart points rather than a copy of the whole image in memory; zip
members decode into the caller's buffer; sequential reads through the
frontend's VFS skip the seek, which also keeps its buffering.
libretro/test reads one image as ISO, CSO, CHD and zip through the block
devices, boots each, and checks the codecs against Python-made vectors;
a CI job runs it.
The tree is built as a libretro core and nothing else, so the
standalone frontends lose their lanes: the Visual Studio and UWP
solutions, the Android app, iOS, the macOS app bundle, the AppImage
and the manual packaging workflows. PPSSPPHeadless and
PPSSPPUnitTest stay, built as named targets so a configure no longer
pulls the SDL frontend in, and keep feeding the headless, frametest,
qemu and Alpine jobs; the Docker image builds the same way.
Two lanes cover builds nothing ran before: the core on an arm64 host,
and the CMake build of the core on Linux.
The MinGW platform linked FFmpeg from the toolchain while compiling
against the bundled 3.x headers, which fails against any current FFmpeg.
The prebuilt ffmpeg/Windows libraries cannot stand in: they are MSVC
builds, and GNU ld leaves calls between their function-level COMDATs
unrelocated. The bundled FFmpeg source is now configured and built with
the MinGW toolchain out of tree, with the components from
windows_x64-build.sh, and its static libraries are linked by path, so
the core no longer depends on any FFmpeg installed on the system.
FFMPEG_CROSS_PREFIX names the binutils prefix when cross-compiling.
The build-libretro-mingw CI job no longer installs MSYS2's FFmpeg.
The MinGW platform compiled against the bundled Windows FFmpeg headers
(3.x) but linked the toolchain's FFmpeg, since the bundled libraries are
MSVC builds MinGW cannot link; against a current FFmpeg the old API
calls the 3.x headers select are unresolved. It now compiles against the
same FFmpeg it links, which the existing version guards already handle.
zstd's x86-64 asm decoder loop is enabled for any GNU C compiler, so the
asm source is now built for MinGW too rather than only skipped on MSVC,
and version and iphlpapi join the link for GetFileVersionInfo* and
GetAdaptersAddresses/GetBestInterfaceEx.
A build-libretro-mingw job builds the core with platform=win from
MSYS2 so these breaks show up in CI.
Only the IR JIT: it's the sole native backend these two have and the one
thing here that isn't shared code, and the x86-64 and arm64 runners
already cover all four backends. Every run costs emulated wall clock, so
the timeout goes up to match.
test.py grows a per-architecture known-failure list, selected with
--known-failures=<arch>, so this can guard against new breakage while the
four outstanding ones stay outstanding. Each entry carries its reason.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Those two have no runner of their own, so the jobs only ever compiled -
which is thin coverage for a code generator. Both build PPSSPPUnitTest
now and run it under qemu-user, which is where the vertex decoder and
CrossSIMD bugs on those backends turned up in the first place.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
g_Config.iCpuCore does double duty: it picks the MIPS core, and it gates the
vertex decoder JIT in the DrawEngineCommon constructor. Headless forces it to
INTERPRETER to keep that decoder off, but it did so before ApplyToConfig(), so
--cpu=jit and --cpu=jit-ir overwrote it and enabled a GPU path the tests aren't
recorded against - 16 of them failed on x86-64, gpu/vertices/morph among them.
Force it after ApplyToConfig() instead. The core the tests actually run on
comes from CoreParameter, straight off the command line, so the backends are
still tested; only the GPU side is pinned.
Also limit the frametests to x86-64. The reference images don't match the arm64
software renderer - 23 of 30 dumps differ, reproducible on any arm64 host. That
predates the new runner, which just gave it somewhere to show up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The headless tests only ever exercised the JIT, since that's what headless
defaults to. Run all four on the Linux runners, and add an arm64 Linux lane
so the arm64 JIT is covered too - nothing else in the matrix tested it.
test.py scales the wall clock to the backend instead of raising it for
everyone: the interpreter needs 20s for gpu/rendertarget/copy, which does
over a million guest-side vsprintf calls, while a hang under the JIT is
still caught in five seconds.
The frametest report artifact needs a per-OS name now that two Linux legs
upload one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pieces were nearly all in place already - CMakeLists has detected
riscv64 and set RISCV64 since the backend landed - so this is mostly a
matter of not hardcoding loongarch64 in the cross-build plumbing:
- setup-loongarch64-cross.sh becomes setup-cross.sh <target>, taking the
triple and dynamic linker name from the argument.
- b.sh gains --riscv64, and the GL stub block is keyed off a compiler
variable rather than a loongarch64-only flag.
- New cmake/Toolchains/riscv64-linux-gnu.cmake, mirroring the existing one.
The SDL carve-out moves from LOONGARCH64_DEVICE to a new HEADLESS_CROSS
option set by b.sh. That flag was keyed off the target architecture, which
is also true when building natively on such a machine - harmless so far,
but riscv64 hardware that people actually build PPSSPP on exists, and it
should still get the normal SDL frontend.
Both toolchain files now find qemu rather than assuming the static build:
Debian ships the static binaries in qemu-user-static and the dynamic ones
in qemu-user, and only the latter is available on some releases. Neither
being present is fine too - it just means no compile-check programs run.
Verified by a clean loongarch64 build; riscv64 is untested so far, the
toolchain isn't installed here yet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
naett has had a complete libcurl backend all along; we just never built it,
so Linux ran with HTTPS_NOT_AVAILABLE. That means no homebrew store over
HTTPS, and RetroAchievements talking to plain http://retroachievements.org.
libcurl is loaded with dlopen rather than linked, the same way we handle the
Vulkan loader, so it stays a soft dependency: we need the curl headers at
build time, but a build made here still starts on a machine without libcurl
installed - it just reports HTTPS as unavailable, exactly like today. Distro
packagers get the behavior they'd expect either way, and certificate
validation comes free from the system CA store.
New net::HTTPSAvailable() answers "did that work", and SDLMain folds it into
SYSPROP_SUPPORTS_HTTPS, which everything downstream already degrades on.
Four fixes to the backend itself, all noted in ext/naett/README-ppsspp.md:
- panic() called exit(1) on a pipe or curl_multi_perform failure. Taking the
emulator down because a download failed isn't acceptable - the backend now
disables itself and requests complete with naettGenericError.
- CURLINFO_RESPONSE_CODE writes a long into res->code, which is an int. Eight
bytes into four, getting away with it only because the next field absorbs
the zeroes.
- curl_easy_setopt is varargs and wants a long for these options; int literals
and int variables are UB on LP64.
- naettPlatformCloseResponse called through a null function pointer when
libcurl was missing. Found by testing that path, which segfaulted.
CI needs libcurl4-openssl-dev (curl-dev on Alpine) or it would quietly keep
building without HTTPS.
Builds ppsspp_libretro.dll with make + cl.exe from MSYS2, for x64 and x86,
mirroring the two Windows jobs in .gitlab-ci.yml. That toolchain differs from
both the VS solution and our gcc/clang libretro builds, so it catches errors
neither of them does - multiply-defined symbols in particular, since MSVC emits
inline functions that gcc/clang inline away.
Visual Studio is located here and passed in as VsInstallRoot rather than left to
the Makefile's "cmd //c bash VSWhere.sh", which fails silently if COMSPEC or
ProgramFiles(x86) don't survive into the MSYS2 shell.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FzzCUp8y1ahgVueb1Cq92Y
The test set lives in the 'frametests' git submodule (hrydgard/ppsspp-frametest-ci).
The test job previously only inited pspautotests, so the submodule was never
synced on CI and the (now removed) hashFiles gates skipped the frametests steps.
Use an HTTPS submodule URL so CI can fetch it without SSH keys.
Extend the existing 'test' job (Linux leg) with the frametests run after
the pspautotests, plus an artifact upload of the output so failures can
be examined. Both steps are gated on the test set being present in the
checkout, since dumps, references and config are maintained separately
from the runner.
Our Qt backend has long been left behind and doesn't even support Vulkan
currently. There would be a lot of work to make it viable, and I don't
think anyone is really interested.
ImGui on SDL will soon fulfill the need for a more classic user interface
with a menu bar on Linux, and on Mac we already have a native UI.
The legacy android/jni build (ab.sh / ab.cmd / CI) used an ancient NDK
(r21e) and hardcoded job counts. Update it to the NDK version used by the
gradle build (29.0.14206865) and derive the job count from the available
cores (nproc / NUMBER_OF_PROCESSORS) instead of hardcoding.