An Ad blend with alpha writes masked can be substituted (Ad -> As) and run in
hardware if the draw reads the render target. The draw did not otherwise need
that read, so the substitution is only worth taking where reading is free.
The gate for "free" was !texture_barrier, written to mean D3D11, where the
fallback is a plain copy on an API with no render passes. It is equally true of
every driver carrying UseRenderTargetCopyForFeedback, where the fallback is a
per-draw copy bracketed by a render-pass break - the most expensive feedback
read we have. Widening that workaround to all of Adreno therefore handed those
drivers the whole optimization in its worst form, on thousands of draws that
never needed to read anything. This is the same regression fixed for the
framebuffer-fetch path in ec57f7f1c6, arriving by the other term.
Replayed on the same dumps and binaries, draws whose shader reads the render
target, per frame:
barriers on barriers off with this change
NFS U'ground 14 610 1
FlatOut 2 n/a 448 17
Ask for the property being asserted instead. cheap_rt_feedback_read is set by
D3D11, and by Metal when programmable blending is available - a feedback draw
there binds the target and stays in the same render pass. Vulkan's
ordered-attachment-access spelling does not qualify: the loop is declared
through the pass configuration, so toggling it ends the pass.
Cost on the SD865 (Adreno 650, turnip, fan and governors pinned, median frame
time over 3 runs of 20 loops, 3x upscale):
shipped OverrideTextureBarriers=1 this change
NFS U'ground 17.15 ms 12.86 ms (1.33x) 12.00 ms (1.43x)
FlatOut 2 22.84 ms 21.93 ms (1.04x) 17.91 ms (1.28x)
Katamari 1.42 ms 1.29 ms (1.10x) 1.40 ms (1.02x)
Katamari is the control: it has no Ad-masked draws, its population is unchanged
(49 -> 50 copies per frame) and so is its frame time. Render passes per frame on
NFSU go 390 -> 47 and copies 347 -> 4.
Correctness is unchanged, and specifically the workaround still applies wherever
it did. Scored per-pixel against the software rasteriser over frames verified
stable across runs, this change renders Tales of the Abyss and God of War II
byte-for-byte identically to the texture-barrier path - same tiers, same
worst-case pixel - and leaves OutRun 2006 and Katamari untouched. The Abyss
title screen text, the defect the workaround exists for, is unaffected. Ad
blends that genuinely need software blending are still forced into it by
blend_requires_barrier.
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
