Switch: build the libretro core from this tree - #22153
Conversation
[Sqash me] Fixes after rebase [Squash me] Revert reporting disable for standalone [Squash me] Change GLInterface macro define for libretro purpose Rework JIT Integration/Masking -Needs more changes/reverts later on, WIP WIP fixes after rebase, confirmed partially working at least More safe buffer sizes Make it link after rebase gl3stub not required for switch Note: glBindFragDataLocationEXT, glGetProgramResourceLocationIndexEXT, glGetFragDataIndexEXT don't get currently populated Dirty fix libnx libretro core Compile and link after rebase for libretro Compile and link after rebase for standalone Fixes after rebase [CI/CD] Libretro CI only for Switch [CI/CD] Makefile path fix Fixes after rebase Split off libretro-common for Switch [Libnx] Fixes after rebase [Libnx] Remove leftover Rebase fixes Critical mitigation, cant call detach Fixes after first batch of PR's No longer required
Cherry-picked from the platform half of hrydgard#20195 (xerpi), which the out-of-tree branch predates: libnx dropped virtmemReserve for virtmemFindAslr, and it now defines BIT and BreakReason, which collide with Arm64Emitter and with the rename block CommonTypes.h keeps around switch.h. Plus the header gaps that only newlib has - no <sys/mman.h>, no <sys/syscall.h>, ENETDOWN sharing a value with ESHUTDOWN, <climits> not arriving transitively. Two hunks resolved against this branch rather than taken as they came: sys/syscall.h stays excluded for the Switch only, not for every libretro build (the Linux core's GetCurrentThreadIdForDebug goes through syscall(__NR_gettid)), and SocketCompat.h keeps the libnx netdb/TCP_MAXSEG block this port is known to build with.
The out-of-tree port replaced GLEW with a generated glsym loader by commenting out the glew include and pasting ~6900 lines of extern declarations and gl* redirections into GLCommon.h. Neither half was scoped: `//#include "GL/glew.h"` applies to every GL target, and the block itself sat behind `#if !defined(__LIBRETRO__) && !PPSSPP_PLATFORM(SWITCH) ... #else`, so the desktop libretro core stopped using GLEW too. Move the generated declarations into Common/GPU/OpenGL/GLSwitch.h and include it only under PPSSPP_PLATFORM(SWITCH). GLCommon.h goes back to what upstream has, plus that one branch - the file is 99 lines again instead of 6961, and GLEW is restored everywhere it was in use. Nothing else needed the Switch guards it had grown: USING_GLES2 is never defined for libnx (the Makefile's GLES flag is not set there, so the build takes -DHAVE_OPENGL), which is why the plain `#elif defined(USING_GLES2)` and the gl3stub include are correct as they stand. The stale "../gfx_es2/gl3stub.h" path goes with them.
The buildbot has been shipping a Switch core all along, from a separate fork whose base is PPSSPP from March 2022. With the tree building for libnx again it can come from here instead. Additive: the CONSOLES section of the include list and the job list were both empty, and the job goes through libretro/Makefile (.make-defs) rather than cmake, because RetroArch links the core in statically on that platform and libretro/CMakeLists.txt only knows how to build a shared library.
|
Looking good, but please follow our AI policy - in short, just mention what LLM you used. Also, might as well go ahead and ask it to add github CI support as well as gitlab, so we won't break this. |
So a change that breaks the Switch build gets caught here rather than on the buildbot after the fact. Same container and same make invocation as the GitLab job the previous commit adds - devkitA64 comes from the image, so there is nothing to install.
|
Both done. Disclosure is in the description now — Claude Opus 5 through Claude Code, with the split between what I decided and what it wrote, and what was and wasn't verified. Sorry for missing the policy on the way in. GitHub CI added in a7d5c87: One thing worth flagging while you have it open: the workflows on this PR are sitting at |
|
Approved PR to run on our github CI. Let's see how it goes. |
|
OK, lots of CI failures. I'll leave them to you to solve. |
CI found three unguarded hunks from the out-of-tree branch that had nothing to do with the Switch: - SDL/SDLGLGraphicsContext.cpp included <glsym/rglgen.h> unconditionally, which fails everywhere else - it is what killed gcc-normal, clang-normal, macos and the alpine headless job. The same file also forced SDL_GL_CONTEXT_PROFILE_MASK to 0 and SetGLCoreContext(false) for every non-GLES build, so desktop would have lost core profile. Reverted whole: the standalone Switch build is out of scope here, it needs an SDL3 that devkitPro does not ship. - GPU/Debugger/GECommandTable.cpp had <cstring> replaced by <strings.h>, and GPU/Common/GPUDebugInterface.cpp had <strings.h> added outright. MSVC has neither, which is what the four Windows and UWP jobs stopped on. Keep the portable include and reach for <strings.h> only on the Switch, where newlib does not bring strcasecmp along - behind ppsspp_config.h, so the platform test resolves. Also dropped the UI, portable-file-dialogs and imgui-console changes and the new icon: all standalone, none of them compiled into the core.
|
Fixed in 786235f — could you approve another run when you get a chance? Both causes were hunks the out-of-tree branch had left unguarded, and neither had anything to do with the Switch:
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
- SetGLCoreContext(true);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
+ SetGLCoreContext(false);— every non-GLES desktop build losing core profile. Reverted the file whole. The standalone Switch build is out of scope here anyway:
I also dropped the
|
|
A new run is going. |
|
Just so you know, i abandoned the libretro port for switch due to various compat and performance issues that simply didnt occur with my standalone builds and they are virtually identically and i didnt feel like chasing the needle... |
|
@m4xw Good to know - if this continued vibe port works, are you ok with merging it? the changes look fine to me. @WizzardSK Can you manually test and confirm that the current build from this branch works correctly on Switch hardware? That's the last other prerequisite for merging this. |
Well the libretro side should mostly work so this is a question of unfucking all the stuff i was too lazy to do clean to begin with. I already am in the progress of updating standalone for SDL3 but this wont matter for the LR port; other than tons of conflicts on my end. Overall no hard feelings and if the quality is up to @hrydgard 's standard then i'll just take it as a new base I should note tho, libretro was ALWAYS BUILT use the MAKEFILE for libnx Only standalone was using cmake in my deployments. |
Here a critical thing I forgot, at libretro we use my forked libnx which has the old JIT and virtmem implementation so this wont actually work on our CI/CD but only with upstream libnx |
Unfortunatelly, I don't have any Switch hardware. I just noticed that ppsspp is divided into 2 projects, so I wanted to try if there is any possibility to merge those 2 repos. |
|
Without someone to test it, this can't be merged. So, go find someone to do it, and also take m4xw's comments into account. |
|
Fwiw the virtmem stuff is a historic libretro infra issue, its done correct for the normal upstream libnx distributed builds, so maybe i can just add a define to the libnx fork that we can check here and either use the old or new way for virtmem, technically new way is more stable but it caused jit crash logs to be less deterministic or something like that. On another note, link what artifact you want tested, i put someone on it |
|
Ok, I made a testing release on my repo https://github.com/WizzardSK/ppsspp-ci/releases/download/0.1/ppsspp-switch-core.zip |
Thats only a static library, you need to link this with retroarch, I cant do that locally since our toolchain missmatches |
cb96567 swapped virtmemReserve() for virtmemFindAslr() as part of moving the port onto a current libnx, but the two do not do the same thing. The old call found free address space and reserved it. The new one only finds it - libnx's own header says the manager mutex has to be held across the find-and-map, and that virtmemAddReservation() is what stands in for the mapping "when the memory won't be mapped straight away". Neither applied here. Find4GBBase() took two 256 MB slices back to back with no lock and nothing claiming either, and then mapped only the second one. memoryBase is not mapped until CreateView() runs, so between the lookup and that mapping there was nothing to stop the next caller - or the second virtmemFindAslr() a line below - from being handed the same range. So take the lock around both lookups and reserve each slice. The reservations are deliberately never removed: the two addresses are cached in statics and reused across ReleaseSpace()/Find4GBBase() cycles, and ReleaseSpace() unmaps the code memory while keeping the address, so the claim has to outlive the mapping. memoryCodeBase now comes from virtmemFindCodeMemory() rather than virtmemFindAslr(), since it is handed to svcMapProcessCodeMemory(), which wants an address out of the code region and not out of general purpose address space. This is the one behaviour change here that wants confirming on hardware; the rest only stops address space from being handed out twice. A failed lookup now returns nullptr instead of carrying on with a null base. MemoryMap_Setup() already treats that as failure.
|
@m4xw Linked into RetroArch now: https://github.com/WizzardSK/ppsspp-ci/releases/download/0.2/retroarch_switch.nro Single-core build, so drop it next to an existing RetroArch install for the assets. Core Your libnx point may already be moot. This was built in One heads-up before your tester starts: @hrydgard all 25 CI jobs are green including the new |
Builds the libretro core for the Nintendo Switch from this tree, so the buildbot's Switch core can stop coming from a fork.
Today it is built from libretro/ppsspp-libnx, branch
switch, whose upstream base is6f04f52f5c— March 2022. That is what Switch users have been running. @m4xw has kept rebasing the port privately (hisrebase_2026branch sits on December 2025), and this is that work carried onto current master, with the parts that were never scoped to the Switch put back.The first three commits are his, rebased. The rest is mine.
What was in the way
Most of it was the port assuming a toolchain and a tree from four years ago:
virtmemReserveforvirtmemFindAslr, and now definesBITandBreakReason, which collide withArm64Emitterand with the rename blockCommonTypes.halready keeps aroundswitch.h.<sys/mman.h>, no<sys/syscall.h>,ENETDOWNsharing a value withESHUTDOWN,<climits>not arriving transitively.T3DCreateGLContextgrew a parameter;Common/GL/GLInterfaceBase.hstopped existing (the port carried an#includeof it next to a member its own comment marked// unknown leftover??);proAdhoc.cpphad 38 lines of resurrected DNS code callingg_Config.proAdhocServer, renamed tosProAdhocServerlong ago.Things that were not Switch-only, and are now
These are the reason the port could not be merged as it stood. Each one changed behaviour for platforms that never asked:
GLCommon.hhad//#include "GL/glew.h"and ~6900 lines of generatedextern/#definepasted in behind#if !defined(__LIBRETRO__) && !PPSSPP_PLATFORM(SWITCH) ... #else, so the desktop libretro core lost GLEW too. The generated half now lives inCommon/GPU/OpenGL/GLSwitch.h, included only underPPSSPP_PLATFORM(SWITCH);GLCommon.his back to 99 lines.LibretroGLContext::CreateDrawContextgot the same treatment — its glew init had been replaced outright rather than branched.MemArenaHorizonfor every POSIX target. The Horizon memory sources were added in the genericelsearm of the platform chain inMakefile.common, andMemArenaPosix.cppwas dropped from it, so a Linux libretro build from that branch would not have linked.MemoryUtil.cpphad likewise been moved somewhere Windows, macOS and Android could not see it.-DWITH_UPNP -DGLEW_STATIC -DGLEW_NO_GLU -DMINIUPNP_STATICLIBcommented out inlibretro/Makefilefor all platforms, with-DUSE_FFMPEGhardcoded in their place (twice, as it turns out).Two upstream oddities the Switch happened to expose
Worth a look independently of this PR:
libadrenotoolsis built for every arm64 libretro target byMakefile.common, thoughCMakeLists.txtlinks it onlyif(ANDROID)and its only consumer is behind#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64). Desktop aarch64 has been compiling it as dead weight; the Switch cannot compile it at all (no<sys/mman.h>).cpu_features'hwcaps.cis likewise built unconditionally, while cpu_features' own CMakeLists builds itif(UNIX)— it needsgetauxvalthrough<sys/auxv.h>ordlopen, has neither here, and#errors.What this does not do
PPSSPPSDLneeds SDL3 since 34c2218, and devkitPro's Switch portlibs carry SDL2 only — configure stops atSDL3 not found. That is the wall [WIP] GitHub actions: Switch build #20195 has been up against since May, on top of the glslang/EGL issues discussed there; the libretro core sidesteps it becauseCMakeLists.txtgates the SDL lookup onNOT LIBRETRO.-D__unixin the libnx flags, becauseext/aemu_postoffice'ssock_impl.hreaches for<netinet/in.h>only under__unix/__APPLE__/__PSP__, leavingsockaddr_inincomplete otherwise. The real fix is a one-line change in that submodule; nothing else in the compiled set tests__unix(the two libretro-common users spell it__unix__).libretro/glsym/andGLSwitch.hare a locally regenerated glsym set — the stock libretro-common one covers a different list of entry points. The other 27 files of the fork's vendoredlibretro-commoncopy are gone; they were shadowing the real submodule, which is howRETRO_DEVICE_ID_JOYPAD_MASKwent missing.CI
Both jobs are in:
.gitlab-ci.ymlfor the buildbot, and abuild_libretro_switchjob in.github/workflows/build.ymlso a change that breaks this gets caught here first. Same container, samemake platform=libnx.Testing
make platform=libnxin adevkitpro/devkita64container, buildingppsspp_libretro_libnx.aclean. I have no Switch to run it on, and no other platform was rebuilt here — the changes outside the libnx guards are restorations of what upstream already had, but they deserve a CI pass before this goes anywhere near a merge.AI disclosure
Per the AI policy: the three commits authored by me were written by Claude Opus 5, through Claude Code, working from m4xw's branch under my direction. The conflict resolutions in the rebase of his three commits are from the same source. I made the calls about scope — port the core rather than the standalone, keep his authorship in the history, take the loader out of
GLCommon.hrather than leave it — and the model did the work and wrote the commit messages and this description.Verification was CI-driven and is exactly what the Testing section says: twenty builds in the devkitA64 container, each failure read out of the build log and fixed, until the core linked. No Switch hardware, and no local build of any other platform — which is the reason I flagged the non-libnx hunks for a CI pass above rather than claiming they are safe.