Nightly and MoonStore builds identify themselves to RetroAchievements as
stock PCSX2, so the server allows softcore only. Hardcore has worked on
locally built IPAs and nowhere else.
Nothing was broken. pcsx2/Host.cpp reads the client version from
ra_ua_secret.h behind __has_include and falls back to a stock agent when
the macro is absent. That header is gitignored, so it has never existed on
a runner.
CI now writes it from the IOS_RA_UA_VERSION repository secret, the way the
Android job already writes its keystores from NIGHTLY_RELEASE_KS_B64. A
missing secret warns and continues, because fork pull requests never
receive secrets and a nightly that fails to publish is worse than one
without hardcore. A malformed version fails the build instead:
RetroAchievements refuses a version it cannot order, and a refused agent is
indistinguishable from an unknown one on the client.
Only the nightly and pushes to master embed it. Pull request artifacts keep
the stock agent, so a test build is not one more public copy of an identity
that needs a release to revoke.
A second step reads the finished binary and fails unless the exact version
is in it, trailing space included, since a stale 1.2.3 is a prefix of a
current 1.2.345. Both iOS jobs are continue-on-error, so that failure does
not stop the workflow, but it does skip the upload that follows it, and the
publish step treats a missing IPA as absent rather than fatal. A softcore
build is not shipped.
None of this makes the version private. The compiler bakes the finished
agent into the binary as a plain literal, so anyone holding a build can
read it out. What the gitignored header prevents is a fork inheriting a
live identity straight from source.
71 commits from 474ad59818 to 2cf8dabe6b, triaged rather than taken wholesale.
Declined, resolved to ours:
- AGENTS.md: upstream's AI-agent instructions; we carry our own and do not
want a second, conflicting policy file.
- CI deps bump (setup-node, labeler): both target workflows are absent here,
and the labeler job is gated on the repository being PCSX2/pcsx2.
- KDDockWidgets 2.4.1: two of the six files do not exist here; we already
build 2.4.0 against a 2.3.0 floor, so there is nothing to gain.
- The FullscreenUI Achievements-layout realignment: our section already
carries the same settings, and ours is the branded copy.
- The GS draw/vertex-buffer cluster (7887919e74, b2fa00844e, 99cfbb49c1,
5c611f85e1, 9945046a49, af48193ebb, d88510e3a6, 8c1bb5742e). Our vertex
kick is an ARM64 rewrite of the same hot path -- register-resident cursor,
fused min/max with a rewind watermark, and a scalar cull mirror that
dual-issues against the NEON parse -- so upstream's generic pointer-logic
optimisation is a variant of work already banked here, and their growth
restructure replaces per-buffer capacity with a single global value, which
the pooled draw-node model cannot express. Two of the four August commits
in that cluster repair regressions the July rewrite introduced, and the
third's genuine fix (staging arrays sized from an unrelated buffer) we had
already made independently.
Taken with adjustment:
- EATAN coefficients (aae9438f98). Upstream relabelled mVU_Globals so the
names match the powers; we had fixed the same defect by ordering the arm64
call sites by power instead. Both fixes are correct alone and CANCEL when
combined, so the arm64 call sites move to plain ascending order in the same
commit. The values never moved, so this emits an identical instruction
sequence. Their fix also repairs the x86 mVU we still carry.
- Shader cache version: upstream numbered their tfx.glsl change 109, which is
below our 110. Taking their value would hand every user a stale blob, so
this lands as 111.
- FullscreenUI: took the two readback-spin toggles, placed outside our
non-Apple guard rather than inside upstream's unguarded run.
- Restored tools/generate_fullscreen_ui_translation_strings.py, dropped by
431ca0c063, and regenerated both string areas. That also registers the Big
Picture setup-wizard strings, which had never been extractable.
GameDB: the three serials upstream gave gsHWFixes (SLES-53869, PAPX-90020,
SCPS-15064) are absent from the mobile overlay, so no fix is silently erased
on handhelds.
The template was inherited from PCSX2, where the AI section is a yes/no
question a contributor must answer and a link to upstream's LLM usage
policy. That policy is not ours to enforce, and the framing treats
AI-assisted work as something to be declared rather than reviewed.
Replace it with an optional note. The requirements that actually matter
apply to every PR regardless of how it was written: the author
understands the change, can explain why it is correct, and has built and
tested it.
The nightly Android APK job has been failing since at least 2026-07-27
with "error[E0463]: can't find crate for `core`" at the librashader
cargo step: the runner's rustup ships only the host std, so the
cross-compile to aarch64-linux-android has no core to link against.
build-all.yml already carries this step (10fab243e7), but nightly.yml
has its own separate dual-core + PGO Android job that never got it.
Because that job is continue-on-error, the workflow kept reporting
success and the nightly release simply published without an APK.
Both jobs have been in build-all.yml since it was written, so they build
and get artifact-uploaded on every push, but neither was ever added to
nightly.yml. The result is that the RetroArch core and the bare-kmsdrm
handheld frontend are the two targets with no published download at all,
which is backwards: those users are the least likely to build from source.
Wire both into the nightly with the same inputs build-all.yml passes, and
collect their .tar.zst into the release. Non-blocking, like the mobile
jobs: publish's guard names only the PC jobs, so a failure here costs the
asset rather than the release. Unlike the mobile jobs they cannot be
marked continue-on-error, since that key is not permitted on a job that
uses a reusable workflow, so a failure will still redden the run. That
matches how they already behave in build-all.yml.
Both build at OVERRIDE_HOST_PAGE_SIZE=4096, so the release notes say so.
Neither is packaged as an AppImage on purpose: the AppImage runtime wants
FUSE, and a bare-display handheld is exactly where that cannot be assumed.
package-sdl.sh already bundles the libs it built and points the rpath at
$ORIGIN/lib, so the tarball is self-contained without it.
The nightly attached whatever filename each build job happened to produce,
and the job families use three unrelated conventions: the PC jobs share
name-artifacts.sh (armsx2-<target>-sha[<sha>]), Android bakes in a
versionCode derived from Unix seconds, and iOS ships a fixed
ARMSX2-iOS-unsigned.ipa. So a downloaded file carried no date at all (that
lives only in the release title), iOS carried no build identity whatsoever
(two nightlies collide as "(1)"), and GitHub rewrites the '[' and ']' of
sha[...] to '.' on asset upload, leaving names that read as though they
have a second file extension.
Rename in the publish job as assets are collected, to
ARMSX2-nightly-<YYYYMMDD>-<sha>-<platform>.<ext>
which keeps the per-workflow CI artifact names untouched for the Actions
tab and for build-all.yml, so the blast radius is the release page only.
A missing artifact (a failed non-blocking job) logs MISSING and the step
still exits clean, so it costs that asset rather than the release.
Also replace the one-line platform list in the release notes with a short
per-file legend, since which Linux AppImage to take is not something a
downloader can infer, and picking the wrong page size just fails to run.
The nightly iOS job ran on macos-15 without selecting full Xcode, so
build-ios-ipa.sh bailed at its xcrun --sdk iphoneos --find metal check
(Command Line Tools have no iOS SDK). build-all's iOS job builds on
macos-26 after sudo xcode-select -switch and is green, so match that:
same runner + select /Applications/Xcode.app before the build.
The forced dual-map validation step segfaulted after several passing tests
(so alias emission + RX execution fundamentally work); with no local macOS
hardware the failing step must produce its own root-cause data. On failure,
dump the macOS DiagnosticReports .ips crash log (faulting PC, fault address,
exception subtype — distinguishes a KERN_CODESIGN_ERROR exec fault, i.e. a
test-hook mapping problem, from a write fault at an RX address, i.e. an
unrouted code-write path) and rerun under lldb --batch for a live backtrace.
Port the W^X protocol from the previous ARMSX2 recompilers (aR*/aVU) onto
the transplanted JIT so it runs under all four DarwinMisc JIT modes:
Simulator (MAP_JIT + pthread_jit_write_protect_np toggle), iOS 26 LuckTXM
and LuckNoTXM (vm_remap dual-mapping, writes at rx + g_code_rw_offset),
and Legacy (mprotect RW/RX toggle, iOS <= 18).
- AsmHelpers: export armGetWritableCodePtr (RX -> RW alias, identity off
Apple); armStartBlock/armEndBlock switch to BeginCodeWriteRange with a
1 MiB Legacy write window and construct the MacroAssembler over the RW
alias while armAsmPtr stays the RX base, so armGetCurrentCodePointer()
and all displacement math remain in execute space; armEmitJmpPtr and the
constant-pool trampoline/literal writes go through the alias with their
own write scopes.
- Arm64BaseBlocks::PatchAtomic (block linking + exception-path unlink) and
recPatchIslandB store via the alias; displacements/icache flushes stay RX.
- RecStubs fastmem backpatch stores the redirect B via the alias.
- microVU: the persistent per-VU MacroAssembler is built over the alias of
prog.x86start; ProgCache hydration fixups patch through the alias while
Rel26/ADRP math keeps using the RX chunk address.
- recExecute re-arms Legacy-mode execute protection via
DarwinMisc::LegacyEnsureExecutable (mirrors the previous recompiler).
- BeginCodeWrite/EndCodeWrite skip the macOS MAP_JIT toggle when a
dual-mapping is active (offset != 0), matching the iOS branches.
- CI validation without an iOS device: ARMSX2_FORCE_DUAL_MAP=1 now also
works on macOS (Memory.cpp routes the code arena through
DarwinMisc::MmapCodeDualMap, which builds the vm_remap RW alias there),
and the macOS workflow reruns recompiler_tests under it, forcing every
emission/patch path through the alias. Production macOS keeps MAP_JIT
with offset 0, unchanged.
Linux/Android paths compile to identity no-ops. Gates: recompiler_tests
1359/1359, gs_vertex_tests 21/21, mvu_progcache_versioning_tests 13/13.
The script came over from yaps2 verbatim; the SDL job's build step
already produces bin/armsx2-sdl (rebranded frontend), so packaging
died at 'cannot stat .../yaps2-sdl' after an otherwise clean build.
MSVC has no __attribute__((preserve_most)), which the arm64 recompiler
requires on the vtlb dispatchers (hard #error in pcsx2/vtlb.h since
f7a039e870 -- the emitted JIT code depends on the x9-x15 preservation
contract, so a compiler without the attribute produces a miscompile,
not a slowdown). clang-cl targeting aarch64-pc-windows-msvc compiles
the attribute with correct codegen (verified: caller keeps a live
value in x9 across the annotated call, matching the linux target).
Only the emulator configure step changes: clang-cl uses the MSVC ABI,
so the deps stage stays on MSVC unchanged and keeps its cache, and
vcvars still provides the SDK, armasm64 (FastJmp), and link.exe. The
CMake side already supports clang-cl upstream (USE_CLANG_CL). The
runner image ships a native arm64 LLVM; the locate step prints the
triple so a wrong-arch install fails loudly at configure.
The Android APK job fails (also on master - the job is
continue-on-error so it never blocked) because librashader's cargo
build targets aarch64-linux-android and the runner's rustup only has
the host std: error[E0463] can't find crate for 'core'. One rustup
target add before the gradle build fixes it. Candidate to send
upstream to master independently of this branch.
The libretro + SDL handheld jobs wired into build-all.yml (fabccaa4fb)
call .github/workflows/scripts/linux/build-dependencies-runner.sh, but
the merge resolved .github/workflows/ wholesale to the ARMSX2 side and
dropped the script. Both jobs failed at Build Dependencies with exit
127 in run 29700912398. Its common/shaderc-changes.patch reference is
already present in this tree.
Rename the imported yaps2 libretro core (output .so, .info, core-option
prefixes, ini name, Vulkan app/engine identity) to ARMSX2. The core is
still gated behind ENABLE_LIBRETRO (default OFF) and needs X11_API=OFF
WAYLAND_API=OFF (headless Vulkan-context-negotiation build, same as the
CI job). Exports remain retro_* only via link.T.
The yaps2 nightly_release.yml is dropped — ARMSX2 has its own release
process — and the reusable libretro/SDL build workflows are instead
invoked from build-all.yml, so PR runs cover them. armsx2-sdl keeps the
kmsdrm handheld frontend buildable for Rocknix-style downstreams.
Merges yaps2/main (github.com/yaps2/yaps2, c16b88cb7) into ARMSX2,
replacing the arm64 recompiler family with the yaps2 JITs and importing
the yaps2 testing, perf, and libretro infrastructure. Common ancestor is
upstream PCSX2 342db5152 (2026-06-19); git auto-merged all but 38 files.
Replaced (deleted in this merge, recoverable from history):
- arm64/aR5900*, aR3000A*, aVU* -> arm64/iR5900*/iR3000A*/microVU*-arm64:
EE static-pin register file with lazy dirty tracking, dual-residence
allocator, IOP block linking, native COP2 macro ops, inline unaligned
fastmem, persisted VU program cache, call-ret shadow ring, VU0 spin
fast-forward.
- MVU_DIFF shadow-run hooks in shared VU interpreter TUs (superseded by
the offline vurunner JIT-vs-interp oracle).
Imported from yaps2:
- tests/ctest/core/recompilers: ~80 gtest suites (EE/IOP/VU differential
harnesses, fuzzers, ABI digest tripwire, capture format pins) plus the
gs_vertex_tests kernel oracle.
- pcsx2-vurunner / pcsx2-eerunner headless capture-replay runners.
- tools/perf counter-based A/B rigs, perf jitdump productionization,
PmuCounters, clang-perf/clang-handheld presets.
- pcsx2-libretro core (ENABLE_LIBRETRO, default OFF; rename pending).
- GS vertex-kick fast path (GV series): TBL-based packed parse,
register-resident kick, scalar-outcode cull, fused draw-rect/FindMinMax.
- Null renderer, VK_KHR_display direct WSI, swapchain PresentStats.
- SPU2 NEON mixer vectorization, EE timer read clamp (NFL 2K5 hang),
IOP ioman signed-compare fix, assorted UB fixes.
Kept from ARMSX2 in the both-touched files:
- iOS dual-map W^X and fastmem-unavailable resilience (Memory, HostSys,
vtlb). The split data/code area model is retained; both areas now take
fixed VA hints so cached VU JIT code stays deterministic on Linux.
- Android thread-affinity model, VMState shutdown early-outs, all
platform frontends, branding, CI, RetroAchievements identity/policy.
- GSDeviceVK: ARMSX2's push-descriptor decision logic (Mali crash gate,
proprietary-vs-turnip Adreno split) merged with yaps2's descriptor-pool
exhaustion recovery (flush + render-pass restart instead of dropped
binds). Vendor feature policy is the union: Mali fbfetch policy with
MediaTek/G57/Xclipse gates from ARMSX2; Adreno stencil/ROV/
test-and-sample-depth hang avoidance and no_ps2_z_quantization from
yaps2.
Build-system notes:
- The Qt debugger is now gated behind ENABLE_QT_DEBUGGER (default off on
arm64) so handheld builds drop the KDDockWidgets dependency.
- GSDeviceNone and remaining yaps2 GS code were ported to the newer
upstream GSTexture Usage-flags API.
The replaced backend's interpreter-fallback glue (intExecuteOneInst,
AndroidEEOpHist) and the EEDiffVerify runtime differ are retained for
now; dead pieces will be removed in a follow-up commit.
* Libretro: build the core on PRs too, and ship its info file
The libretro core build is only wired into nightly_release.yml, so a PR or a
push can break pcsx2-libretro while Linux CI stays green -- you find out the
next morning. Call the same reusable workflow from linux_build_matrix.yml, so
the core is built alongside the Qt and SDL flavours on every PR and push.
Also add yaps2_libretro.info. Frontends read it for the core name, extensions,
BIOS requirements and feature flags, and it has to sit next to the .so, but it
never landed in the repo -- so the nightly ships a core with no info file.
Every field comes from pcsx2-libretro/Main.cpp: hw_render/required_hw_api
reflect the mandatory Vulkan context sharing (no software or GL fallback
toward the frontend), disk_control the .m3u and tray-swap support, and cheats
/memory_descriptors/input_descriptors are false because those interfaces are
not registered. The same file is proposed for libretro-super in
libretro/libretro-super#2013, so the two stay in sync.
* Libretro: dithering, trilinear, mipmapping and FXAA core options
Fill the graphics-option gap vs the pcee2/lrps2 cores: yaps2_dithering
(dithering_ps2 0/1/2), yaps2_trilinear_filtering (TriFilter enum, Automatic=-1),
yaps2_mipmapping (hw_mipmap) and yaps2_fxaa (fxaa), defaults matching the
base config. Both the v2 table and the legacy variables list carry them.