Completes the switch to the Eden port. GSLsfg keeps its entire public surface — availability, status text, display FPS, the settings and OSD plumbing all untouched — and only its internals change, so nothing above the renderer had to move. What actually changed on screen: the old implementation ran the interpolator on a SECOND VkDevice and shared images as AHardwareBuffers, and because Android offers no cross-device semaphore (Turnip rejects OPAQUE_FD export on AHB memory) the only barrier available was a full device idle — twice per frame, every frame. That is gone. Generation is now ordinary compute recorded into a command buffer on the device we already have, and interpolated frames are written STRAIGHT into an acquired swap chain image through a storage view, so the intermediate copy is gone too. The pacer comes with it, which is the fix for games that oscillate between 60 and 30fps on a 60Hz panel: the generation count now varies to hold the presented rate near a target instead of blindly multiplying whatever the game produced. ★ ONE submit, N+1 semaphores. All the generation work goes into a single command buffer, submitted once, waiting on the caller's render-finished semaphore plus every acquire, and signalling one semaphore per present that follows. The obvious alternative — a submit per generated frame — walks straight back into the binary-semaphore bug this file was bitten by before, where the real present and the first generated present both want to wait on the semaphore that says the source has been read. A binary semaphore may be waited exactly once. ★ The hook fires AFTER vkQueueSubmit, so FrameGen had to take its command buffer as a parameter. It was written against GSDeviceVK::GetCurrentCommandBuffer(), which at that point is in flight or already belongs to the next frame; recording into it is undefined and the symptom would have been interpolation running a frame late rather than anything resembling an error. Layout bracketing is ours: the ported passes speak Eden's convention where a presentable image lives in GENERAL, and PCSX2 hands them over in PRESENT_SRC_KHR and needs them back in it. The swap chain now requests VK_IMAGE_USAGE_STORAGE_BIT — but only when frame generation is on AND both the surface and the chosen format allow it. Asking unconditionally fails swap chain creation outright on drivers that do not, which would take the whole renderer down for a feature that is switched off. The format half is the easy one to miss: a surface can report STORAGE support while the sRGB format picked for it has no STORAGE_IMAGE feature bit, and that only shows up later as a validation error at image-view creation. Because usage is fixed at creation, switching the feature on mid-session needs a renderer restart; Initialize says so rather than failing silently. DELETED: platforms/android/app/src/main/cpp/3rdparty/lsfg in full — the lsfg-vk-android framegen library, the DXVK dxbc compiler, pe-parse, volk and its 759-symbol collision with VKLoader, the C ABI shim, the version script, the separate .so and the dlopen that found it, and the -fexceptions carve-out they needed. GSLsfg.cpp went from 1259 lines to 654. The ~130 MB configure-time fetch goes with it. build-play-aab.sh's guard was rewritten rather than dropped: it checked for a file that can no longer exist either way, so it would have passed forever without proving anything. It now looks inside the core for a symbol only the ported implementation defines. Verified: all 18 affected translation units compile without errors, with ARMSX2_HAS_LSFG on AND off (the play flavour still compiles the feature out entirely). Not yet run on hardware.
ARMSX2 — Native ARM64 JIT Fork of PCSX2
ARMSX2 is a free and open-source PlayStation 2 (PS2) emulator based on PCSX2. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your phone, PC, or gaming handheld, with many additional features and benefits.
Thank You
The ARMSX2 team is eternally indebted to the PCSX2 project it is based on. We are so fortunate to build on their 20 years of hardcore development.
About This Fork
The upstream PCSX2 project ships an ARM64 interpreter build for ARM, but its high-performance JIT recompilers (EE, IOP, VU0, VU1, and vtlb fast memory) are x86-64 only.
This fork exists to close that gap. The goal is to preserve the correctness features of 20 years of PCSX2 development, while generating the fastest native ARM performance possible.
Current status:
- ✅ EE (Emotion Engine) recompiler — integer, float, MMI, COP0/COP1/COP2, branches, load/store
- ✅ IOP (I/O Processor / R3000A) recompiler — full integer, load/store, branches, coprocessors
- ✅ VU (Vector Unit) recompiler — microVU skeleton + Upper FMAC vector ISA complete; Lower ISA and runtime complete
- ✅ vtlb fast memory
- ✅ Native ARM64 binary builds and boots the PS2 BIOS
- ✅ 2D games are already playable
- ✅ 3D games run
Why LLMs / AI Were Used
A word on methodology:
The x86-64 JIT code in upstream ARMSX2 is already proven correct — it has run thousands of PS2 titles for years. The challenge in this port is not emulator design or JIT theory; it is mechanical translation of a large, well-understood x86-64 assembly codebase into equivalent ARM64 assembly (via VIXL) while preserving the exact same register-allocation contracts, block lifecycle, and recompiler semantics.
Large language models (LLMs) were used as an accelerant for this translation work — pattern-matching x86 JIT boilerplate to ARM64 equivalents, scaffolding emit routines, and keeping the porting velocity high. The JIT logic (block compiler, dispatcher, analysis passes, flag pipelines, clamping rules, Tri-Ace hacks, etc.) is taken directly from the upstream x86 implementation and validated against it. Nothing was hallucinated from scratch.
In other words: the hard engineering was done by the PCSX2 team over two decades. The hard typing — translating ~50k lines of x86 emitter code into ARM64 — is what AI helped compress.
System Requirements
ARMSX2 targets ARM64 across desktop (macOS, Windows, Linux) and mobile (Android, iOS/iPadOS), all from the single shared core. Our setup documentation page contains additional details on software and hardware requirements.
Please note that a BIOS dump from a legitimately-owned PS2 console is required to use the emulator. For more information, visit this page.
Building
Check out our github actions for the latest build recipe
