VMManager::Internal::CPUThreadInitialize() can run more than once in
a process lifetime on mobile hosts that tear down and rebuild the CPU
thread across game launches. Achievements::Initialize() asserts
`!s_client && !s_http_downloader` at the top, so a second call with
RetroAchievements enabled fired pxFailRel("No client and downloader")
and aborted the process. The crash only manifested on hosts that
re-enter CPUThreadInitialize without an intervening Achievements
shutdown, which is why desktop builds never hit it.
Gate the call with !Achievements::IsActive(), which checks the same
s_client pointer the assertion reasons about. The normal cold-start
path (s_client == nullptr) still calls Initialize(); a second entry
with the client already constructed is now a no-op, matching the
expectation the rest of the file already has.
Verified by repeated launch/exit/relaunch cycles on iOS with
RetroAchievements enabled: no abort, and the client initialises
exactly once per process.
Port the complete JIT and write-xor-execute infrastructure to DarwinMisc with four JitModes (Simulator, Legacy, LuckTXM, LuckNoTXM), dual-mapping via vm_remap for writable code aliases, the csops CS_DEBUGGED probe for JIT availability detection, brk assembly helpers for the TXM protocol, and the W^X toggle functions. Connect the JIT foundation to the code emitters through AsmHelpers dual-map bridge, Memory.cpp MmapCodeDualMap allocation, and the aR5900 LegacyEnsureExecutable path. Refresh the iOS SwiftUI frontend from the iOS-refresh branch, bringing in 11 missing and 20 drifted Swift files plus ios_main.mm integration. Switch the CI to a real device build using the iphoneos SDK. Fix the Achievements crash by gracefully degrading when no HTTPDownloader is available (no CURL on iOS). Fix the Metal surface to reuse the UIView's existing CAMetalLayer instead of an orphaned allocation that caused half-screen crops. Fix GS memory allocation by using mmap and vm_remap instead of shm_open which is blocked by the iOS sandbox. Suppress the false positive Graphics not Automatic OSD warning. Merge upstream master and resolve all resulting compile errors.
Gate all macOS-only system APIs behind TARGET_OS_IPHONE checks across the shared core: ApplicationServices, IOKit, mouse APIs, AppKit, MetalFX, CDVD Darwin sources, USB, discord-rpc, cubeb CoreAudio HAL, and BSD networking headers in DEV9. Port TARGET_OS_IPHONE guards for DEV9 AdapterUtils with iOS fallbacks for sockaddr_dl, rt_msghdr, and sysctl. Add ARMSX2_ROOT to the include path for the frontend's common include style. Drop the global _M_ARM64 define that triggered fast_float MSVC intrin.h inclusion since __aarch64__ covers the core paths. Gate MTLFeatureSet_macOS_GPUFamily1_v1 behind !TARGET_OS_IPHONE. Fix missing unistd.h and gate pthread_jit_write_protect_np which is unavailable on iOS. Add stubs for DarwinMisc JIT diagnostics, Achievements stats and info APIs, Discord_Register, Host capture callbacks, and the host hotkeys map so the frontend links cleanly. Merge upstream master.
Strip the legacy Android, React Native, Java, Gradle, and res/ directories from platforms/ios that were inherited from the original port. Configure the CMake build for the iOS target with local module discovery, PCAP and CURL guards, rapidyaml source path fixes, Vulkan disabled, lz4 build flags scoped, libjpeg-turbo skipped on iOS, and the Qt UI and test runners turned off. Set CMAKE_SYSTEM_PROCESSOR to arm64 for cross-compilation. Add a self-contained GitHub Actions workflow that builds an unsigned IPA for real devices using the iphoneos SDK, named with the commit SHA. Merge upstream master to stay current.
Re-apply the Android-specific GS deltas on top of the canonical GS as guarded hunks so
PC/mac/Linux/Windows stay byte-for-canonical:
- VK: restore the push-descriptor fallback (Adreno/Mali stall inside
vkCmdPushDescriptorSetKHR -> textures never bind -> black screen) as a capability-gated
path; desktop keeps push descriptors unchanged. Mobile vendor gates
(Mali/Adreno/PowerVR/Xclipse), dyn_shaderc, FIFO_RELAXED present mode, PowerVR swapchain
width-align. depth_feedback forced off only under #if __ANDROID__ (desktop keeps
feedback_loops()).
- OGL: re-apply GLES support (EGL context, is_gles shader branches, GLES query objects),
runtime-gated by is_gles; guard 3 desktop-reachable riders (present-path
glInvalidateFramebuffer -> is_gles, EGL SetDisplay body -> #if __ANDROID__, restore the
negative-swap-interval probe).
- RA toast: guard the AddRect call on IMGUI_VERSION_NUM (desktop imgui 1.92.8 swapped the
thickness/flags args; Android vendors 1.92.6) so the toast border renders on both.
- Suppress the 'Graphics API is not set to Automatic' OSD warning on Android
(#if !__ANDROID__); desktop keeps the canonical warning.
- REFACTOR_STATUS: mark the GS re-apply resolved; note the imgui two-copies version skew.
Three shared-core behavioral changes rode into PC unguarded via ecfebfd6b2 and are
reverted to canonical; the genuinely Android-only pieces around them (force-float +
nice-priority in VMManager, guarded #if __ANDROID__) are kept:
* MTVU: WaitForWork() -> WaitForWorkWithSpin() spin-wait swap (all platforms)
* VMManager: removal of the 'Graphics API is not set to Automatic' OSD warning
* VMManager: s_thread_affinities_set tracked new_pin_enable instead of the
canonical EmuConfig.EnableThreadPinning (desktop pinning-flag behavior)
Kept as legitimate additive/guarded shared changes: the new ThreadHandle API
(SetNicePriority/GetAffinity/GetCurrentCpu) + semaphore cache-line alignment, the
GetThreadPlacementDebug helper, Oboe/SoC-name/OSD-label Android features, and the
armsx2_overrides.yaml GameDB loader (PC-safe; no override file shipped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vulkan render fixes (GS device backends), Oboe audio backend, GameDB armsx2_overrides.yaml override loader, EE+VU mac-port recompiler graft, PGO=optimize, and the VU-slam fix: an Android-only force-float in VMManager::SetEmuThreadAffinities (the slam was thread pinning locking the VU1 worker off the prime core, not the VU codegen). Also: CPU-name SoC-property fallback, Mali VK attachment-feedback-loop crash gate, MediaTek fbfetch disable, and on-device thread-placement diagnostic helpers.
The recompiler grafts and GS deltas are unguarded and land in the shared core (they reach the mac/Linux arm64 builds) - see REFACTOR_STATUS.md items 2 and 3 for the reconciliation this still needs; the VU graft is reducible to just the force-float fix.
- Host::GetHTTPUserAgent: report ARMSX2/2.7.407.0 (the RA-registered client)
instead of 'PCSX2 <gitrev>', which RA rejects as an outdated emulator and so
disables hardcore unlocks.
- ImGuiFullscreen: fix ImGui 1.92 AddRect arg order (flags/thickness swapped)
that drew a ~240px border on the RetroAchievements toast.
- VMManager: suppress the false 'Controller 1 has no input bindings' warning
(Android injects pad state directly, bypassing InputManager bindings).
Debugging tool for the microVU1 correctness regression (2D games black-screen,
FFX renders with artifacts; bug attributed to microVU1 via the VU1-rec-off test).
With env MVU_DIFF=1, recMicroVU1::Execute runs the *interpreter* as the real
(known-good) VU1 so the game keeps rendering, and runs microVU1 as a
side-effect-suppressed shadow on the same restored input, comparing VF/VI/ACC/Q
and the full 16 KB VU data memory. The first diverging program's PC is logged
and the low program memory disassembled (disVU1MicroUF/LF). s_mvuShadowRun gates
the microVU XGKICK C entry points so the shadow can't double-transfer to GS;
LoadCoreSettings forces MTVU off under MVU_DIFF so VU1 runs synchronously through
this hook (otherwise it executes on the MTVU thread).
Zero impact on normal builds (all paths gated on the env var).
Findings so far (Rayman 3): microVU1 produces huge-negative results that clamp to
-FLT_MAX (0xff7fffff) where the interpreter has small/zero values, and the Q
register lags the interpreter by exactly one DIV — consistent with a stale Q
feeding the perspective-divide MULq (collapsed geometry → black 2D / 3D artifacts).
Root op not yet isolated (math lives in dynamically-uploaded subroutines).
Flip the ARM64 branches in VMManager to mirror the x86 path now that microVU
(Upper + Lower + flags + branches) is fully ported:
- InitializeCPUProviders: CpuMicroVU0/1.Reserve() (recMicroVU1::Reserve opens
vu1Thread for us, so the explicit vu1Thread.Open() workaround is removed).
- ShutdownCPUProviders: CpuMicroVU1/0.Shutdown() (recMicroVU1::Shutdown waits
on / closes vu1Thread).
- UpdateCPUImplementations: CpuVU0/1 = EnableVU0/1 ? CpuMicroVU0/1 : CpuIntVU0/1.
- ClearCPUExecutionCaches: reset CpuMicroVU0 for macro mode when EE rec is on
but VU0 micro is off (mirrors x86).
Builds arm64, unittests 2/2. This selection alone still crashes during BIOS
boot (XGKICK packet-size misread) — fixed in the following commit.
New pcsx2/arm64/aR3000A.{h,cpp} implementing the psxRec provider on ARM64.
Execution model mirrors the EE rec's Phase 4.3 baseline: a C++ dispatcher loop
(recExecuteBlock) drives the IOP timeslice — recLUT block lookup (compile on
miss), per-block prologue/epilogue (save x19+LR, pin RESTATEPTR=&psxRegs, RET),
cycle debit via iopAddEECycles (PS2 x8 / PS1 gcd-carry) + gated iopEventTest,
matching R3000AInterpreter.cpp intExecuteBlock (incl HLE-BIOS a0/b0/c0 entry).
All opcodes currently single-step through the interpreter via the new
iopExecuteOneInst() (recTranslateOp returns false for everything): correct but
not yet faster than the interpreter. Native generators land next.
Wired into VMManager (reserve/shutdown/reset + psxCpu = CHECK_IOPREC ? &psxRec
: &psxInt on ARM64) and registered in CMakeLists.
Builds arm64; unittests green.
The EE recompiler now actually runs guest code: Cpu = &recCpu boots the BIOS
on ARM64 (Mode Changed to DVD PAL, Pad DS2 Config Finished).
- aR5900.cpp: multi-instruction recCompileBlock (per-block x19+LR prologue/
epilogue, straight-line ops, handled-branch + delay-slot terminator, length
cap, exit-before-uncompilable-op); recEmitBranch decodes J/JAL/JR/JALR +
BEQ/BNE/BLEZ/BGTZ + REGIMM B*Z[AL] onto the Phase 4.1/4.2 generators; C++
recExecute dispatcher loop (pc -> recGetBlock -> run -> cycle add ->
_cpuEventTest_Shared, fastjmp exit); recScaleBlockCycles; reset/exit plumbing.
- Interpreter.cpp/R5900.h: intExecuteOneInst() — per-opcode interpreter fallback
for ops the rec can't compile (COP/FPU/MMI, syscalls, traps, likely/COP
branches), mirroring execI with the interpreter's own branch/delay-slot handling.
- VMManager.cpp: select recCpu for EE on ARM64 when CHECK_EEREC (IOP+VU still int).
Key fix: blocks clobber LR via vtlb/interp calls, so each block saves/restores
LR itself (a shared enter-trampoline + bare block crashed on the block's RET).
unittests green; arm64 binary; BIOS-boot verified with the rec active.
Add #else (ARM64) branches to the _M_X86 guards in VMManager so the EE
recompiler's Reserve/Reset/Shutdown run on the real VM lifecycle:
- InitializeCPUProviders: recCpu.Reserve()
- ShutdownCPUProviders: recCpu.Shutdown()
- ClearCPUExecutionCaches: recCpu.Reset()
UpdateCPUImplementations is left untouched (Cpu = &intCpu): the rec is
reserved but NOT selected, so the interpreter stays ground truth until
real EE codegen exists. Verified on a real PS2 BIOS boot: Reserve+Reset
run, interpreter executes BIOS to pad-config, no crash.