mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
master
303
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3e56da7f86 |
Merge upstream PCSX2 (2026-07-15 .. 2026-08-10)
71 commits from |
||
|
|
1a13086768 |
iOS: Optimize FPS Caps for Performance, Battery Life, and Thermal Efficiency
# iOS: Optimize FPS Caps for Performance, Battery Life, and Thermal Efficiency ## Summary This pull request is organized as two incremental commits: 1. **FPS Cap Fixes and Optimizations** 2. **JIT Double Alert and In-Game No-JIT Indicator** The work is rebased onto `origin/master` at `f4e6452141be556f46bb0f3249d07865df4f64d5`. This base includes the upstream GS back-queue ownership fix from `173d10bf0c6cf9e46b2850ac40f9e865a50635d5`. The FPS-cap implementation does not restore the removed EE-thread back-queue drain or alter that synchronization. The two source archives mirror that order. The second archive is intended to be applied after the first. Each archive contains complete source files at that commit's state, with repository-relative paths. No patch file is required. ## Features - Presentation-only FPS targets which preserve 100% game, CPU, and audio timing. - One shared FPS-target slider design globally and per game, with exact detents at `15`, `23.976`, `29.94`, `30`, `45`, `59.97`, `60`, `90`, and `120` FPS. - Dedicated per-game FPS overrides with global inheritance. - Compact FPS labels: integer targets display without `.000`, while broadcast/film cadences retain their meaningful decimals. - Exact active-cap reporting in the performance OSD. - Turbo-aware cap suspension and automatic cadence restoration. - Progressive and interlaced cap-skipped rendering paths. - Bounded Metal command-buffer batching across eligible skipped frames. - A two-stage No-JIT boot warning with an explicit destructive confirmation. - A red in-game **No JIT** indicator beside the Pause/Quick Menu control. ## Fixes - Selecting 30 FPS no longer halves emulation and audio speed. - Fractional FPS targets no longer get rounded into a different cadence, while whole targets no longer display unnecessary `.000` suffixes. - Per-game FPS targets correctly override the global target. - Legacy per-game and global `NominalScalar` FPS profiles migrate without losing their selected cadence. - RetroAchievements Hardcore Mode no longer rejects a presentation cap as though it were slow motion. - Quick Menu normal-speed reporting no longer derives game speed from display FPS. - Fast Forward no longer races settings reloads or inherits a stale presentation deadline. - The OSD hides the cap label on the unchanged 60 FPS-or-higher master path. - Interlaced skipped frames retain required deinterlacing history. - Screenshots, recording, GS dumps, framebuffer feedback, and software rendering retain complete frames. - Texture age continues advancing during capped frames without scanning the complete pool every skipped frame. - Metal skipped-frame batches have bounded frame, encoder, and wall-time limits. - Normal Metal submissions clear skipped-batch bookkeeping, preventing a stale counter from forcing a later unnecessary flush. - Cancelling or dismissing either No-JIT warning safely clears the pending game boot. ## Performance, battery, and thermal improvements - Eligible capped frames omit final display merge/composition and optional output processing. - FXAA, CAS, MetalFX, ShadeBoost, shader-chain, final-resize, drawable, presentation, and OSD passes can be avoided on those frames. - Metal work is submitted in bounded batches to reduce driver and tile load/store overhead. - Texture-pool deletion scans are amortized while preserving texture lifetime and memory bounds. - Fewer output-sized render passes can reduce GPU occupancy and memory bandwidth. - Reduced GPU, memory-controller, and driver activity can lower package power and battery drain. - Lower sustained package power can reduce heating, preserve thermal headroom, and delay throttling. - Screen recording and other system workloads can receive more GPU and thermal headroom, reducing the chance that recording pushes an already demanding game into thermal slowdown. - Higher internal/output resolutions can benefit disproportionately because the skipped final effects operate over substantially more pixels at 2× and 3× resolution. - The native 60 FPS and above-60 FPS paths retain master's original rendering and performance behavior. - Android retains master's presentation-only cap path and does not opt into the iOS final-render bypass. ## Commit 1 — FPS Cap Fixes and Optimizations Suggested commit message: > iOS: make FPS caps presentation-only and optimize skipped frames ### Problem addressed The old iOS FPS-target implementation encoded the selected target in `Framerate/NominalScalar`. A 30 FPS selection on a roughly 60 FPS game therefore requested approximately 50% emulation speed. CPU/game timing and audio slowed down together with the displayed frame rate. An FPS cap should instead control how often the finished image is presented while the emulated PlayStation 2 continues running at normal speed. ### Presentation cadence is independent from game speed The selected target is now stored in the dedicated `ARMSX2iOS/FramePacing/TargetFPS` setting. - `Framerate/NominalScalar` remains `1.0` whenever the normal limiter is enabled. - EE, IOP, VU, game logic, input processing, patches, and SPU2 audio remain at 100% timing. - Turning the limiter off still uses the existing unlimited-speed scalar. - Fast Forward remains a separate emulation-speed control. - The Quick Menu reports Normal Speed from the actual nominal-speed scalar, not from the presentation target. - RetroAchievements Hardcore Mode no longer treats a presentation-only target below 60 as slow-motion. Legacy global and per-game profiles that encoded an FPS target in `NominalScalar` are migrated to the dedicated cadence setting. Their selected display target is retained, while normal game speed is restored. ### Exact and per-game targets Frame Pacing now uses one slider with precise detents for: - `15 FPS` - `23.976 FPS` - `29.94 FPS` - `30 FPS` - `45 FPS` - `59.97 FPS` - `60 FPS` - `90 FPS` - `120 FPS` The per-game Frame Pacing screen uses the same slider, detents, formatting, and presentation-only behavior. A per-game profile can inherit the global target or enable its own override. Whole values render as `15 FPS`, `30 FPS`, and `120 FPS`; exact fractional cadences remain `23.976 FPS`, `29.94 FPS`, and `59.97 FPS`. This is integrated with master's numeric override-row design rather than restoring the deleted picker lists. ### Native behavior at 60 FPS and above The custom presentation scheduler is enabled only for positive targets below `60.000 FPS`. - Exactly 60 FPS follows master's normal render, texture-pool, Metal submission, and presentation behavior. - Targets above 60 FPS also follow master's normal path. - `59.97 FPS` remains a real fractional cap because it is below 60. - The OSD does not show a cap label on the native 60-or-higher path. This keeps the default configuration and high-refresh targets out of the new cap-specific optimization paths. ### Cadence scheduler The GS thread uses a deadline accumulator rather than a simple “was the last frame too recent?” check. - It preserves the requested average rate when the target is not an integer divisor of the game's source cadence. - It avoids assuming that every game produces exactly 60.000 frames per second. - It re-synchronizes after a large hitch instead of banking skip credit and presenting a burst afterward. - Turbo suspends the custom cap and clears its deadline. - Returning from Turbo re-primes the scheduler from the next normal frame. - Turbo state and cap suspension are changed together on the CPU thread, avoiding a settings-reload race. ### OSD reporting When FPS is shown in the OSD, the active target is appended in a readable form, for example: > FPS: 30 (Cap 30 FPS) Fractional values are retained instead of rounded away. The label is hidden when no sub-60 cap is active and while Turbo temporarily owns presentation cadence. ### Work omitted on eligible cap-skipped frames By the time the GS VSync scheduler chooses to skip presentation, correctness-sensitive PS2 draw commands and framebuffer writes have already been processed. On an eligible hardware-renderer frame, ARMSX2 can omit display-only work that cannot affect emulated GS memory. For a progressive cap-skipped frame, the optimized path can omit: - final GS display merge/composition; - ShadeBoost; - FXAA; - RetroArch shader chains; - final output resizing and the bilinear-sharp pass; - CAS sharpening/upscaling; - MetalFX spatial upscaling; - TV/presentation shader work; - drawable acquisition and final presentation; - that frame's ImGui/OSD render pass. This is deliberately not blanket GS draw skipping. Geometry processing, texture uploads, render-target state, framebuffer writes, and other emulation-visible graphics work continue so later frames remain correct. ### Interlaced games Interlaced cap-skipped frames use a separate path: - display merge and required interlace/deinterlace history are updated; - optional visual filters, final output processing, and presentation are omitted. This preserves temporal history for 480i titles. Because required history work remains, interlaced games generally save less work than progressive games. ### Correctness guards The aggressive final-composition bypass is disabled when a complete image or emulation-visible output is required, including: - screenshots; - video capture; - GS dumps and configured frame/texture dumps; - active `EXTWRITE` framebuffer feedback; - invalid or unavailable current output; - software-renderer operation; - non-cap skips such as manual frame skip and the existing duplicate-frame path. Those situations retain the full rendering path. ### Texture-pool maintenance Texture lifetime still advances for every emulated frame. Cap-skipped frames preserve deferred-draw ordering but amortize the pool deletion scan: - cleanup runs no later than every fourth cap-skipped frame; - normal presented frames keep master's standard pool maintenance; - native 60+, manual skips, and duplicate-frame skips continue using master's standard `AgePool()` path; - purging resets the deferred-cleanup counter. This reduces repeated GS-thread pool scanning without allowing frame age or retained pool memory to stop advancing. ### Bounded Metal batching Compatible Metal work can remain in one command buffer across cap-skipped frames, reducing repeated submission and tile load/store overhead. Retention is bounded and flushed when any of these limits is reached: - 4 cap-skipped frames; - 256 encoders; - 75 milliseconds. The bookkeeping resets whenever encoders are flushed, including normal presentation, readback, and upload-only flushes. This prevents a submitted batch's old frame count or start time from causing an unnecessary flush in the next batch. ### Detailed 30 FPS example Consider a game producing approximately 60 GS VSyncs each second with an active 30 FPS target: - the PlayStation 2 simulation still processes approximately 60 game-time updates per second; - audio continues at normal pitch and timing; - approximately 30 frames are selected for display; - approximately 30 final presentation opportunities are cap-skipped; - each displayed frame is spaced at roughly 33.3 ms instead of 16.7 ms. The cadence is based on wall-clock deadlines, so this should be understood as an average target rather than a hard-coded “always skip every second frame” rule. #### Battery and power benefits On eligible skipped frames, the app avoids the final output-sized passes, drawable work, post-processing, and presentation listed above. This can reduce: - GPU execution time devoted to final composition; - final render-target reads and writes; - memory-controller and bandwidth activity; - Metal command encoding and submission pressure; - drawable acquisition and compositor-facing work; - repeated texture-pool cleanup work on the GS thread. Lower GPU, memory, and driver activity generally lowers package power consumption. Over a long session this can slow battery discharge, especially at 2× or higher internal/output resolutions or when FXAA, CAS, MetalFX, ShadeBoost, or shader chains make the final display pipeline expensive. #### Heat and sustained-performance benefits Electrical power consumed by the CPU/GPU package becomes heat. Removing avoidable final-frame work can therefore: - slow the device's temperature rise; - provide more thermal headroom; - delay or reduce iOS CPU/GPU thermal throttling; - improve consistency in long sessions; - leave more GPU time for the frames that are actually displayed; - reduce contention with screen recording and other concurrent system work. This can be more useful than a short benchmark suggests: preventing an early thermal limit may preserve steadier performance later in a session. #### A 30 FPS target does not guarantee, but nearlies a 50% reduction in total GPU use, battery drain, or heat. Only the final-display portion of the workload can approach a 50% reduction when roughly half of 60 presentation opportunities are removed. Correctness-sensitive work still runs, including: - EE/IOP/VU execution; - game logic and input; - SPU2 audio; - GS command processing; - geometry and vertex work; - texture uploads; - framebuffer and render-target writes that future frames can consume. The total benefit therefore depends on the bottleneck: - **Largest expected gain:** GPU/output-bound games, high resolutions, and expensive post-processing. - **Smaller gain:** EE/VU/geometry-bound games where final presentation is a small part of frame cost. - **Smaller gain:** interlaced games because history updates remain required. - **Minimal gain:** games already producing fewer frames than the selected cap. - **Temporarily disabled optimization:** capture/dump/feedback cases that require complete frames. No fixed battery percentage, wattage, temperature reduction, or total GPU percentage is claimed without physical-device profiling. #### Other observable metrics Potential improvements include lower average GPU occupancy, fewer presented drawables, fewer final post-process passes, reduced memory bandwidth, reduced command-buffer submissions, lower thermal pressure, and improved battery endurance. The tradeoff is visibly less fluid motion and a longer visual update interval, even though simulation and input processing remain at normal speed. ### Why 23.976 FPS is a cinematic option Theatrical cinema traditionally uses `24.000 FPS`. `23.976 FPS` is the NTSC-compatible fractional form of 24p and is commonly described as `23.98` or simply `24p` in film and video workflows. It preserves the familiar approximately 24-frame cinematic cadence while fitting fractional-rate video systems. Traditional 24 FPS capture is often associated with a 180-degree shutter, which gives an exposure close to 1/48 second per frame. Motion occurring during that exposure is integrated into each source frame as motion blur. That combination of cadence and source motion blur is part of the familiar film look and can make continuous movement feel less mechanically sharp than high-frame-rate capture. The ARMSX2 cap does **not** create synthetic motion blur. It controls presentation cadence only. A game's own motion-blur implementation may complement 23.976 FPS; without suitable source blur, fast camera pans can instead show more judder. The option is therefore a visual/performance tradeoff, not a claim that 23.976 FPS is objectively smoother than 60 FPS. Useful background references: - [Adobe video delivery specifications](https://www.adobe.com/content/dam/experience-fragments/www/us/en/experience_cloud/avstg/Adobe_Avstg_Cloud-Ad-Specs-12.13.2018.pdf) describe `23.976` as film-look content commonly referred to as 24 FPS. - [ARRI's shutter white paper](https://www.arri.com/resource/blob/186770/ad23969317dafff402f0902d47bb4ab7/alexa-studio-electronic-and-mirror-shutter-white-paper-data.pdf) explains the 24 FPS, 180-degree-shutter relationship and its motion-blur characteristics. ### Resolution, recording, and contributor-observed result This optimization is particularly relevant at higher resolutions. Final merge, scaling, sharpening, post-processing, and presentation passes operate over output-sized images. At 3× resolution those passes touch many more pixels than at 1×, so omitting them on eligible non-presented frames can return more GPU time and memory bandwidth than it would at native resolution. That recovered headroom can be used for a higher resolution, screen recording, or more stable sustained clocks. The contributor reports that on an iPhone 14 Pro Max this branch can run at 3× resolution with a cinematic `23.976 FPS` target while retaining enough headroom to record, whereas ARMSX2 iOS 2.4.1 was usable only at 1× in the same practical comparison. This is a device/workload observation, not a guaranteed result for every title or device; gains depend on whether the title is limited by output rendering, EE/VU work, GS geometry, or another subsystem. ### Why this can improve battery life and heating Apple's performance guidance connects GPU workload and memory bandwidth with energy consumption and thermal behavior: - [Measuring the GPU's use of memory bandwidth](https://developer.apple.com/documentation/xcode/measuring-the-gpus-use-of-memory-bandwidth) notes that reducing bandwidth saves energy and leaves memory-system capacity available to other processors. - [Analyzing your app's battery use](https://developer.apple.com/documentation/xcode/analyzing-your-app-s-battery-use) covers CPU/GPU activity, power consumption, and device temperature as related diagnostic signals. - Apple's [`ProcessInfo.ThermalState.serious`](https://developer.apple.com/documentation/foundation/processinfo/thermalstate-swift.enum/serious) guidance explicitly gives reducing a target frame rate from 60 FPS to 30 FPS as an example response to elevated thermal pressure. - Apple's [Energy Efficiency Guide for iOS Apps](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/index.html) explains why eliminating unnecessary work improves energy use and sustained behavior. For this implementation, the benefit comes from avoiding eligible final-display work—not from slowing the PlayStation 2 simulation. Less output-side GPU work can reduce instantaneous GPU occupancy, memory traffic, command processing, and compositor interaction. Lower sustained power produces less heat, which can delay thermal throttling and preserve performance during longer play or simultaneous screen recording. The amount saved is title-, resolution-, effect-, and device-dependent. ### Commit 1 files - `pcsx2/GS/GS.cpp` - `pcsx2/GS/GS.h` - `pcsx2/GS/Renderers/Common/GSDevice.cpp` - `pcsx2/GS/Renderers/Common/GSDevice.h` - `pcsx2/GS/Renderers/Common/GSRenderer.cpp` - `pcsx2/GS/Renderers/Common/GSRenderer.h` - `pcsx2/GS/Renderers/Metal/GSDeviceMTL.h` - `pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm` - `pcsx2/ImGui/ImGuiOverlays.cpp` - `platforms/ios/app/src/main/cpp/ARMSX2Bridge.h` - `platforms/ios/app/src/main/cpp/ARMSX2Bridge.mm` - `platforms/ios/app/src/main/cpp/IOS/HostImpls.mm` - `platforms/ios/app/src/main/cpp/IOS/IOSRuntime.h` - `platforms/ios/app/src/main/swift/Models/AppLanguage+UISupplementTranslations.swift` - `platforms/ios/app/src/main/swift/Models/SettingsStore.swift` - `platforms/ios/app/src/main/swift/Views/GameScreenView.swift` - `platforms/ios/app/src/main/swift/Views/HelpView.swift` - `platforms/ios/app/src/main/swift/Views/PerGameSettingsPanel.swift` - `platforms/ios/app/src/main/swift/Views/Settings/EmulatorSettingsView.swift` - `platforms/ios/app/src/main/swift/Views/Settings/FramePacingSettingsView.swift` - `platforms/ios/app/src/main/swift/Views/Settings/NumberRow.swift` - `platforms/ios/app/src/main/swift/Views/Settings/PerGame/FramePacingTab.swift` - `platforms/ios/app/src/main/swift/Views/Settings/PerGame/NumberOverrideRow.swift` ## Commit 2 — JIT Double Alert and In-Game No-JIT Indicator Suggested commit message: > iOS: add two-stage No-JIT warning and runtime indicator ### Two-stage warning before interpreter fallback When a game is started without detected JIT access, the existing warning remains the first step: > JIT access is not available. Match the StikDebug script to the JIT Script setting in Emulator settings. Cancel abandons the pending boot. Continue is now destructive and opens a second confirmation rather than immediately starting the game: > Are you sure you want to continue? > JIT access is required for 60 FPS. > > EXPECT TOO LOW PERFORMANCE. > PROCEED AT YOUR OWN RISK! The second alert uses the same warning title and icon. Its red Continue action is the only route that calls the existing interpreter-fallback boot continuation. Dismissing or cancelling either stage clears the pending boot safely. ### Red in-game No JIT status While the VM reports interpreter fallback as active, a compact red **No JIT** capsule appears immediately to the left of the Pause/Quick Menu button in portrait and landscape gameplay layouts. - It uses the existing native fallback-status bridge already present in master. - It updates through the existing runtime menu-state refresh points. - It adds no emulator-core or JIT implementation changes. - It adds no independent timer, polling service, or per-frame native work. - It disappears when fallback mode is no longer active or emulation ends. - It includes an accessibility label. ### Commit 2 files - `platforms/ios/app/src/main/swift/Views/GameScreenView.swift` - `platforms/ios/app/src/main/swift/Views/RootView.swift` ## Scope boundaries This PR intentionally does not modify JIT allocation, validation, keep-alive, worker lifecycle, recompilers, interpreter implementation, or No-JIT performance behavior from master. Commit 2 is UI and boot-confirmation behavior only. Unrelated existing workspace changes—such as simulator generation, Swift compilation-memory settings, third-party notices, and Dynamic Background shader files—are not included in either PR archive. ## Validation - Rebased onto `origin/master` at `f4e6452141be556f46bb0f3249d07865df4f64d5`. - Includes upstream GS back-queue fix `173d10bf0c6cf9e46b2850ac40f9e865a50635d5` through the new master base. - Both requested changes were reconstructed as dependency-ordered commits in a clean worktree. - Commit 1 contains 23 source files and no No-JIT UI hunks. - Commit 2 contains only `RootView.swift` and the No-JIT portions of `GameScreenView.swift`. - `git diff --check` passes. - Full unsigned IPA build completed successfully with `platforms/ios/scripts/build-ios-ipa.sh`. - Generated IPA: `platforms/ios/build-ios-xcode/ARMSX2-iOS-unsigned.ipa`. |
||
|
|
624717dfad | GS: Fix status bar GPU % when OSD GPU usage is off | ||
|
|
8b31dbce6c |
GS: let the pipelined split run with asynchronous HW downloads
The front-object split was refused whenever the EE thread services the readback
itself, which covers both Unsynchronized and Asynchronous. That groups the modes
by which thread reads, when the question is what it reads.
Unsynchronized takes GS local memory directly, no lock and no drain, so a queued
back thread leaves it arbitrarily far behind what the EE expects. Asynchronous
does not read local memory at all: it takes the CPU shadow under
m_async_readback_mutex, and the mutex is the synchronization point. The shadow
moves only when the GS thread publishes a completed GPU download, never when a
record is queued or executed, so queue depth cannot change what the EE sees.
Every shadow accessor already routes through m_mem_target, so a front object
reaches the back's authoritative copy - the plumbing was in place, only the gate
was wrong. The refusal was Unsynchronized-only when the split landed; the
asynchronous readback import widened it to the shared predicate.
Keep lockstep for the one case that does read live memory under Asynchronous: a
shadow that never came up sends ReadLocalMemoryUnsync down the fallback path.
The renderer is constructed before this decision, so ask it directly.
Skip the shadow allocation on the front object. The base constructor could not
tell it was building one - m_mem_target still points at itself there - so it
allocated and seeded a full GS-memory-sized copy that nothing can ever read once
the derived constructor repoints it. UpdateSettings runs on both halves and had
the same problem, re-seeding that dead copy on every settings change.
Measured on the SD865, which ships this exact configuration (HWDownloadMode 5,
GSBackThreadMode 3) and was therefore never pipelining at all. Both arms come
from one binary: the back object always constructs lockstep, so -backthread 2 is
precisely what -backthread 3 did before this change. Fan pinned, 3 runs per arm,
-loop 40, medians, ranges disjoint in both titles:
lockstep pipelined
OutRun 12.05 ms 7.75 ms -36% 83 -> 129 fps
Rogue Gal 18.97 ms 12.87 ms -32% 53 -> 78 fps
Rogue Galaxy is the title that just took Asynchronous by GameDB, and it crosses
60 fps on this device as a result.
Correctness: frames are byte-identical across back-thread modes Off,
InlineRecords, Lockstep and Pipelined under Asynchronous, on both the M2 and the
SD865, against a same-binary control run first to confirm the dumps reproduce.
40-loop runs of the previously deadlocking combination complete cleanly with
readbacks exercised. On the M2 frame time is flat at ~6.6 ms - that replay is not
GS-CPU-bound there - though the GS thread still drops 6.17/6.55 ms to 4.06/4.26.
Default configuration is untouched: the back thread is off by default, and only
Asynchronous plus Pipelined changes behaviour.
|
||
|
|
5793dbc1ef |
GS:HW: Only enable GPU timing/stats in backend if the option is enabled.
Helps prevent a crash in Mesa3D Windows drivers. |
||
|
|
17b2be058a |
GS: record a complete dump under the pipelined back-thread split
The dump's transfer and ReadFIFO hooks sit on the parse path, and its initial state came from Freeze() on the renderer. Under GSBackThreadMode=Pipelined the parse path belongs to the front object, so both were reading the wrong object: the front's transfers never reached the dump at all. A Rogue Galaxy capture that should be 39.4 MB of packets came out with 90 KB -- 0.2% of the stream, the ReadFIFO and VSync packets alone -- and replayed as nothing. GSQueueSnapshot warned about it rather than fixing it (GV7-2). The dump stays owned by the renderer, which opens and closes it on the present path; the parse side reaches it through GetDumpSink(), which routes via m_mem_target, and the initial freeze goes through a new m_parse_target, the inverse pointer. Both paths run on the MTGS thread -- the front's runahead is over the back thread, not over the thread handling vsync -- so the front writes straight into the back's dump with no synchronisation. m_parse_target->Freeze() is the same call GSfreeze makes for a savestate, which already drains and already takes registers from the front and local memory from the back. Verified on a Rogue Galaxy savestate, frame-stepped over PINE so both arms start from the identical state: the mode 3 dump is byte-identical to the mode 0 dump, 4.2 MB of initial state and 39.4 MB of packets, and it replays in gsrunner to ten frames identical under both modes. Two mode 0 runs are likewise identical, so the harness has no slack. Reverting just the transfer sink reproduces the 90 KB dump, so the comparison has teeth. Two bytes of bookkeeping ride along: GSQueueSnapshot loses the warning, and the MsgGSDump reply loses pipelined_incomplete, which now has nothing to report. Whether the split engaged is a genuine question, so it moves to the stats reply as gs_front_parser, next to gs_back_thread_pct where it belongs. |
||
|
|
975e408ed5 |
PINE: add a GS-dump opcode so a script can capture without a hotkey
MsgGSDump (0x14, ARMSX2-local) queues a GS dump of the next N frames: [u32 frames][u32 path_len][path bytes], where frames == 0 stops a recording dump and UINT32_MAX records until stopped -- the same press/release pair the GSDumpMultiFrame hotkey binds. The reply is JSON carrying the resolved dump path, so a client knows the file to wait for instead of guessing at the snapshots folder's auto-naming. Three things the naive version of this gets wrong, all found by testing it against a live Dragon Quest VIII: QueueSnapshot honours a caller-supplied path only when it ends in .png, and silently substitutes an auto-named file otherwise -- a scripted client would write somewhere it never looks. Normalise the path up front instead, dropping a .gs/.gs.xz/.gs.zst/.png suffix if the caller spelled one out so that naming the file you want does not earn a doubled extension. A request that arrives while a dump is already recording creates no second dump: the VSync handler only opens one when none exists. It writes a stray screenshot, and worse, overwrites the running dump's remaining frame count and cuts it short. The first version of this replied with a path for a file that was never created and truncated the recording that was. Refuse instead, with reason "already recording"; the caller can stop the running dump first. The same defect reachable via the Screenshot hotkey is left alone here -- it is a renderer behaviour change and belongs in its own commit. The PINE thread cannot push MTGS packets: the ring is single-producer and that producer is the EE thread. Take the same two-hop route BuildStatsJson already documents -- Host::RunOnCPUThread, then RunOnGSThread -- and read GSConfig's compression method on the GS thread, since it decides the extension. QueueSnapshot and GSQueueSnapshot now return whether they took the request; existing callers ignore it. GSIsDumpRecording and GSHasFrontParser expose the two pieces of GS-thread state the reply needs. pipelined_incomplete surfaces the known GV7-2 gap rather than letting a script collect corrupt dumps. Verified live: every promised path was written, refusals produced no files, and all three dump shapes replay in gsrunner -- single-frame as 4 (2) frames, a stopped multi-frame recording as 186 (91). |
||
|
|
3b38bd2605 |
GS: purge the texture cache when a geometry hack changes
Turning Align Sprite off and watching nothing happen is the report that keeps coming back. The setting does reach the renderer now, but the pixels it already moved are sitting in a cached target, and nothing invalidates them. These hacks rewrite vertex positions or texture coordinates on the way into a draw, so what they change ends up baked into whatever target that draw landed on. Games that redraw a target every frame hide this, because the next frame paints over it with the new setting. Games that build a background or a HUD layer once and keep sampling it do not: that target keeps the shifted pixels until something else happens to invalidate it, which can be a whole scene later. From the player's side that reads as a setting that will not switch off, and then as ghosting, which is exactly how it was reported. Native scaling was already in the purge list for the same reason and its comment claimed it was the only geometry hack that outlived the draw. It is not, it is just the most obvious one, because it swaps the texture outright rather than nudging what gets drawn into it. Align sprite, merge sprite, round sprite, half pixel offset, wild arms, native palette draw, bilinear and the texture offsets all have the same problem. Costs a frame when you toggle one, same as every other entry in that list. |
||
|
|
b53ba9a2a3 |
GS: drain the back queue before presenting
Pausing with the GS back thread enabled crashed the app, reported for a long time as "crashes when I pause to change settings". The settings were never involved: the emulog shows the apply completing, the VM resuming, running for a second, and dying on the NEXT pause. Looking at the settings path found nothing wrong with it because nothing is. GSPresentCurrentFrame was the one device-mutating entry point that did not drain. GSreopen, GSResizeDisplayWindow, GSUpdateDisplayWindow and SetVSyncMode all do. It reads like a query rather than a mutation, but presenting records into the command buffer and begins a render pass. That only bites while paused, which is the one time anything calls it in a loop: MTGS's idle branch re-presents the frame whenever the VM is not Running so the window is not left black. The back thread can still be working through queued draws at that moment, so both threads call vkCmdBeginRenderPass on the same VkCommandBuffer -- which Vulkan requires the caller to externally synchronize. Adreno's driver faults inside vkCmdBeginRenderPass rather than reporting an error, and both threads abort; the log prints "Aborting application." twice, once per thread. Reproduced and verified fixed on an Adreno 740 (driver 512.676.53) over repeated pause/resume cycles with the back thread on. Diagnosed with jpolo1224 from an on-device tombstone. |
||
|
|
2c02dc8b96 |
GS: drain the back queue in GSreopen before shredding its textures
GSreopen opens with GSParseTarget()->Flush(GSREOPEN), which flushes FRONT parse state and *queues* the resulting draw -- GSState::Flush does not drain. Both arms below it then hand the back thread's textures to the shredder: the device-loss arm (recreate_device && !recreate_renderer) calls PurgeTextureCache, ClearCurrent and PurgePool, and the other arm reads the texture cache back. Same class as the three window/vsync seams fixed in 578cb3a83a, which is where this was found and deliberately left alone pending the safety question. That question resolves in favour of draining. The worry was that on device loss the back thread could be wedged in the driver and waiting on it would hang recovery instead of recovering. It cannot: BeginPresent only reports DeviceLost off m_last_submit_failed, so the driver has already declared the loss by the time we get here, and post-loss calls return VK_ERROR_DEVICE_LOST rather than blocking. There is no backlog to chew through either -- SubmitVsync drains before ExecVsyncRecord and present never queues, so the queue is empty on entry and the Flush above is the only producer. Note this is NOT the Android suspend/resume path. Backgrounding kills the surface, not the device: BeginPresent returns FrameSkipped and resume comes back through onNativeSurfaceChanged -> MTGS::UpdateDisplayWindow, which 578cb3a83a already drains. The trigger here is genuine device loss, which the tree documents twice -- the Mali r44p1 blob that returns VK_ERROR_DEVICE_LOST on every game, and Rogue Galaxy hitting it at vkWaitForFences. DrainBackQueueBeforeDeviceMutation moves above GSreopen unchanged so it can be called from there. No test: this seam class has no runtime test surface, same as 578cb3a83a. |
||
|
|
2e40d9e799 |
GS: drain the back queue before the three window/vsync device seams
GSResizeDisplayWindow, GSUpdateDisplayWindow and GSSetVSyncMode all reach
g_gs_device from the MTGS thread -- swapchain resize, window recreate, vsync
change -- while the back thread is executing draws against that same device.
Every sibling seam of this class drains first; GSUpdateConfig does, and its
comment names this exact hazard.
|
||
|
|
538421f65b |
iOS: fix graphics settings that never reach the running game
Align Sprite and Merge Sprite look stuck: turn one on and it stays on after you turn it off. The INI write is fine in both directions -- nothing ever tells the VM about it. A graphics setting only live-applies if its Setting<T> declares an onSet closure calling requestGraphicsApplyGuarded(), and most declare nothing. The C++ fallback in setINIBool/setINIInt covers a hardcoded allow-list with none of the user hacks on it. 33 keys are in neither and only take effect once some unrelated setting forces a VMManager::ApplySettings, or on reboot. The allow-listed ones were not really working either. The poke writes GSConfig from the UI thread, so when GSUpdateConfig runs it takes old_config from that already-updated GSConfig and every new != old guard is false: hw_mipmap and texture_preloading skip the texture-cache purge they are listed for, LoadTextureReplacements skips UpdateConfig and the hash purge, OsdShowGPU never reaches SetGPUTimingEnabled. So make it structural rather than extend the list, since the list is what rotted. Setting<T> now gives every EmuCore/GS key the hook by default and the 23 hand-written copies are gone; both poke helpers go with them, leaving one apply per key and nothing writing GS state off the CPU thread. Fourteen keys are not Setting<T> instances -- five plain didSets, and the nine homogeneous hardware fixes that live in a [String: Bool] dictionary -- so those call it from their write funnel, and resetGraphicsDefaults goes through that funnel instead of writing the INI beside it. Renderer keeps no hook: it is a restart option, so applying it live tears the Metal device down under the running game. Native scaling joins the texture-cache purge, being the one geometry hack that outlives the draw -- it swaps a target's texture for a downscaled one and pins m_scale to 1, so those targets stay downscaled after it is off. Also: MaskUpscalingHacks zeroes the sprite hacks below 2x while the UI only gated them on Manual Advanced Hacks, so at 1x they read on and did nothing. Say so. The Skipdraw footer claimed its changes need a reset; they do not. |
||
|
|
069f8a44f3 |
Android 2.6.5.1: Local Link LAN play, async GS readback, pause/rotation/settings fixes
Crash and correctness - Fix a crash when backgrounding the app mid-game: onPause flushed the Vulkan pipeline cache from the UI thread while the GS thread was creating pipelines into the same VkPipelineCache. Vulkan requires that handle to be externally synchronised, so this was a driver-level data race and crashed on Adreno and Xclipse alike. The flush now runs on the GS thread, posted via the CPU thread so it does not race the EE-owned MTGS ring. - Fix an unbounded out-of-bounds vertex read in the GSRendererHW sprite-merge paving path: the inner loop advanced i instead of j, so j stayed loop-invariant and the scan walked past m_vertex->tail. - Fix per-game settings being silently ignored: gamesettings/<serial>_<CRC>.ini loads into a higher-priority layer than anything the app writes, and saves made from the library never regenerated it, so any key already in that file overrode the user permanently. Only the category-Reset path rewrote it, which is why Reset appeared to be the only thing that worked. - Fix screen rotation: the BIOS followed the launcher rotation instead of the renderer's (it has no GameInfo, and the tier was keyed on that), and the launcher stayed locked in a game's orientation after exit because the cleanup lived only inside stop()'s vmRunLoopActive-guarded branch, which loses a race against the VM thread's own finally. Rotation tier is now an explicit flag and the cleanup runs on every terminal path. - Discard the Vulkan pipeline blob whenever the SPIR-V cache is discarded. It was validated only against the device header (vendor/device/pipelineCacheUUID), which is identical across an app update, so a SHADER_CACHE_VERSION bump kept every pipeline built from the old shaders and nothing pruned it. - Make eeRecExitRequested atomic: it was a plain bool written from the JNI thread and read on the CPU thread. - OpenGL: restore GL_PACK_ALIGNMENT after readback, add the missing memory barrier after the CAS dispatch, and initialise GLState::depth_mask to GL's actual default. - DEV9: log the GetNetAdapter default: bail and the InitNet skip. Both returned silently, so a settings mistake surfaced as missing hardware three layers away. Local Link (new) - New DEV9 backend bridging emulated PS2 Ethernet between devices over authenticated local UDP, so games with a built-in LAN / System Link mode can play together. Ported from EmuCoreX (sashkinbro) with the wire format unchanged, so peers remain compatible across both forks. - Network mode picker (Online / Host / Join), host address readout, auto-derived peer ids, generated room codes, hostname support alongside numeric IPv4, and a link to the supported-games list. Fully controller-navigable. Performance - Asynchronous hardware download mode (experimental, opt-in): non-blocking GPU->CPU readback so the EE thread no longer waits on the GS thread. Ported from EmuCoreX. Appending Asynchronous to GSHardwareDownloadMode makes the enum non-ordered, so the relational comparisons on it are replaced with IsHardwareDownloadReadbackEnabled / IsHardwareDownloadEEThreadRead. - Affinity Control Mode (experimental, opt-in): EE/VU/GS priority orders plus a Performance Cores mode. Android otherwise leaves these threads unpinned. - Raise the texture-replacement cache ceiling from 6 to 16 GB; RAM/2 remains the real limiter, so this only binds at 12 GB RAM and up. - Low Latency frame pacing is no longer the default, with a one-time migration for installs that took the earlier flip. Features - Auto renderer resolves to Vulkan HW on Adreno. - Auto Progressive Scan (per-game): holds Triangle+Cross through boot. - OLED black as a modifier over any accent colour, including Custom and RGB. - Optional system keyboard instead of the built-in on-screen one. Game compatibility - Everybody's Golf 4 / Hot Shots Golf Fore! hwDownloadMode across all regions (PR #421, XDarkFallenX). - Delta Force: Black Hawk Down (PR #401, XDarkFallenX). - Reduced input latency and input handling improvements (PR #403, Splaser). RetroAchievements - Inject the client version from a build-time secret kept out of public source, with a stock-PCSX2 fallback for secret-less builds, so third parties cannot copy the client identity. Covers the iOS token too. |
||
|
|
fca9018dce |
GS: per-draw ledger (GSDrawLog)
We could see that a frame issued 900 draws and 40 barriers, but not which draws were expensive or what PS2 state made them so. The existing per-draw facility, GSHWDrawConfig::DumpConfig driven by SaveHWConfig, writes one text file per draw: right for inspecting a single suspicious draw, wrong for profiling a scene, since a heavy frame produces hundreds of files. Records one append-only table instead, so a whole scene can be sorted by cost, grouped by pixel format, or scanned for draws that forced a barrier, in one pass with shell tools. A row is assembled from two points in the draw, because the field sets do not overlap. The PS2 view is live at the top of GSRendererHW::Draw -- primitive type and count, FRAME/ZBUF/TEX0 addresses, formats and buffer widths, FBMSK, blend equation, alpha test and DATE. The backend view only exists at submit, and reuses the fields DumpConfig already knows: topology, barrier requirement, tex_hazard, destination_alpha, colormask and drawarea. Draws that return before submit still get a row, marked unsubmitted, since "which draws were skipped" is itself a signal. I/O discipline is the design. At ~900 draws/frame and 60 fps this is ~54k rows/sec; formatting a row costs microseconds and writing it costs bandwidth, and both would land on the GS thread -- the thread under investigation. So capture stores a packed POD into a preallocated arena, with no formatting and no I/O, and serialisation happens once afterwards. The arena is bounded to ~64 heavy frames, so a long session yields a contiguous prefix rather than an unusable file; truncation is reported rather than silent. Activity tracks GSConfig.DumpDrawLog directly rather than a config edge, so recording works when the setting is already true at GS open. GSUpdateConfig writes the CSV on the true->false edge, making a live capture "turn it on, play the slow bit, turn it off" -- both edges drivable over PINE. gsrunner gets -drawlog <path.csv>. The stringifiers GetTopologyName/GetTexHazardName/GetDestinationAlphaModeName were TU-local statics in GSDevice.cpp; exposed as GSGet* so the ledger names enum fields instead of duplicating the tables. Verified on a dump replay: row count matches @HWSTAT@ Draws exactly (277/277), and with the frame limiter off, median-of-5 p50 frame time is unchanged (+0.00%, distributions overlapping). Note the test scene is ~70 draws/frame rather than the ~900 the arena is sized for. This is an attribution tool, never a comparison tool: an A/B with the ledger enabled on one arm is invalid. |
||
|
|
90daa091db |
Android: audio backend options, setting descriptions, RA/haptics polish, and ported GS fixes
Audio - Optional OpenSL ES output backend for devices where the default AAudio path crackles, glitches or won't initialise (Settings -> Audio), plus a lightweight SPU2 mode that skips the reverb pipeline to free CPU on low-end devices. - Keep the audio device alive across the in-game menu pause so Android no longer reclaims the idle stream and drops sound after the menu sits open (#333). Settings - Restored the per-setting descriptions under every GameDB Fix and Advanced Speedhack toggle (lost in the settings redesign). - Per-game Reset now clears the native per-game INI, so it truly reverts to the global values instead of the game keeping stale overrides. - On-screen display now defaults off; Custom stats appear on boot without a reset (#385). Controls / RetroAchievements - Vibration Strength slider scaling all rumble and touch haptics 0-200%. - Achievement Sound Volume slider; points now show in the menu before a game loads; unlock sounds play with Do Not Disturb enabled. Misc - Drop the compiled GS shader/pipeline cache automatically on app update to avoid post-update graphical corruption. - Animated XMB library-background fallback for GPUs without float-texture filtering. GS correctness (ported from sashkinbro/EmuCoreX) - Reset per-game hardware-hack HLE state on game change (Burnout bloom, IRem/GT channel-shuffle) so it no longer leaks across in-app game switches. - Fix a non-strict-weak-ordering comparator in SortMultiStretchRects. - Free the leaked m_expand_vao on the OpenGL device teardown path. |
||
|
|
616e5900f3 |
Merge yaps2/main: GV7 GS front/back thread split (17 commits)
Brings the complete GV7 campaign: GSBackQueue SPSC record ring, draw/ transfer/PCRTC/vsync records with inline executors, the GSFrontState two-object front split, lockstep + pipelined back-thread modes (default Off), mid-frame MTGS-thread drain seams, back-thread affinity fix, and the Qt/Big Picture GSBackThreadMode settings UI. Conflict resolutions: - pcsx2/GS/GS.cpp: adjacent additions unioned (Android Tekken 5 Mali override + GV7 g_gs_front/GSParseTarget). GSAllocateWrappedMemory unioned semantically: keeps the iOS-safe HostSys::CreateSharedMemory routing AND drops the static fd so two wrapped allocations coexist (the GV7 two-object split runs two GSStates, each with a wrapped vm); CreateSharedMemory already unlinks/memfds the name and the iOS file-backed fallback O_EXCL-retries per-attempt paths, so coexisting allocations cannot collide. - GraphicsAdvancedSettingsTab.ui: take gsBackThreadMode tabstop; drop yaps2's stale "rov" tabstop (no such widget in either tree). - FullscreenUI_Settings.cpp: GS Back Thread setting inserted outside the ARMSX2 !__APPLE__ guard around exclusive fullscreen. Audit: ARMSX2 one-liners in GV7-rewritten files survived (GSClut CreateFeedbackTarget, GSState SaveTransferImages); ARMSX2's Merge()-path additions (RetroArch shader chain, FastMAD fallback) run post-drain on the vsync path, so no unaudited device seams. Gates on merged tree: recompiler_tests 1359/1359, gs_vertex_tests 21/21 (incl. new gs_backqueue suite), full build incl. armsx2-qt. |
||
|
|
5aeb3dd8bc |
GV7-2: drain the back queue at mid-frame MTGS-thread device seams
Sync-point audit fixes for pipelined mode: GSUpdateConfig's non-reopen branches, SaveSnapshotToMemory, and capture begin/end all touch renderer or GSDevice state from the MTGS thread while the back thread may be mid-draw on the same device. Each now drains queued records first (the front only parses on the MTGS thread, so nothing new queues during the operation). DrainBackQueue becomes public for the GS.cpp seam. Audit conclusions (no code needed): WaitGS callers never touch back-owned state EE-side (state access travels through MTGS ring packets into already drained seams); SIGNAL/FINISH are GIFRegHandlerNull in GSState — CSR semantics are entirely EE-side; a back-thread assert failure aborts the process on Linux, so it cannot deadlock the front. Gates: gs_vertex_tests 21/21; gsrunner PNG hashes identical to GV-0 baselines, modes 0 and 3, vk and sw. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
5e3819a087 |
GV7-1d-ii-c: flip mode 3 to true pipelining
The front parser object no longer drains after each push — the record ring and pool backpressure bound the runahead, and the back thread executes draws while the front parses ahead. Every cross-boundary read is either record-carried, behind an explicit drain, or per-object: - Serial counters s_n / s_transfer_n / s_last_transfer_draw_n become per-object members (were process statics): the front assigns order and carries serials in records, the draw executor installs s_n from the record, and the transfer executor counts its own slice stream — so TC timestamps and age heuristics on the back thread see the executing draw's serial, not the front's runahead position. Qualified static refs in the TC/MultiISA/OGL-debug/SW-dump paths now go through the renderer object. The front re-syncs s_n and the scanmask after each (drained) vsync. - Kick-time IsCoverageAlphaSupported drains before reading last-flushed- draw state (exact AND deterministic: post-drain state is a function of the record stream, not thread timing; the alpha clause can read CLUT bytes so it is not front-computable). Memoized per (draw serial, live ALPHA) => at most one drain per AA1 draw. - The HOST->LOCAL exec cursor stays back-side: the front skips the inline m_tr.x/y mirror, Freeze adopts the drained back cursor before serializing, and Defrost seeds it back. - GSreset resets the front first (flushing pending draws as records) so the back's drain executes them before memory/TC reset, like serial pre-reset draws. - Pipelined mode is refused (falls back to single-object lockstep) when HWDownloadMode is Unsynchronized: that path reads local memory from the EE thread with no drain. Gates: gs_vertex_tests 21/21; gsrunner PNG hashes bit-identical to GV-0 baselines for modes 0/1/2/3 x vk+sw over all 10 dumps, and mode 3 repeated 3x with identical hashes (pipelining is deterministic by construction). Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
84a1de62b5 |
GV7-1d-ii-b: two-object front split (GSFrontState + entry-point routing)
Instantiate a GSFrontState parser object under GSBackThreadMode::Pipelined (SEAM-AUDIT.md $7): it owns all parse state and emits records into the back renderer's channel; the back object executes them, installing record state into its own members so the HW look-ahead heuristics read the same names they always did. Mode 3 still drains per record (lockstep) — the pipelined flip is the next commit. - GS.cpp routes GIF transfers, SoftReset, CSR, readbacks, savestates, and the vsync PCRTC digestion to the front; present/TC/settings stay on the renderer. The front is created only when the back thread engaged, and is destroyed first (it drains the shared channel the back owns). - Drained seams reach authoritative memory through m_mem_target: readback ReadImageX/SaveBMP, InvalidateLocalMem, savestate vm8 serialize/restore, TC readback/purge (now draining), plus back-side Reset/CLUT-reset and a PCRTC re-sync on Defrost. - The draw executor on a split back aims m_draw_env/PRIM/m_context around the tail exactly as FlushDraw does on the front, and restores after. - m_channel_shuffle_finish is written on both sides; the front's ApplyTEX0 set becomes a one-shot edge OR-ed into the back-owned flag (a level install clobbered the draw path's own sets/clears — FlatOut 2 lost its channel-shuffle skip, caught by the vk hash gate). - Kick-time IsCoverageAlphaSupported reproduces single-object mixed semantics: live PRIM/ALPHA from the front, last-executed-draw primclass/ cached-ctx/alpha-minmax from the back (IsRTWrittenLive split). - GSAllocateWrappedMemory drops its process-global singleton (close the fd/handle once the views are mapped) so two GSStates can each own a wrapped vm; also fixes a handle leak in the Windows free path. - s_transfer_n moves to the submit side: transfer serials are front-assigned, and the vsync idle-frame check reads them on the MTGS thread. Gates: gs_vertex_tests 21/21; gsrunner PNG hashes bit-identical to GV-0 baselines for modes 0/1/2/3 x vk+sw over all 10 dumps (mode 3 exercises Defrost + readback seams through the front object). Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
7e612f4750 |
GV7-0e: PCRTC_SYNC + VSYNC records with inline executors
PCRTCDisplays is not vsync-only state — the HW Draw() heuristics read it per draw — so under the split it is duplicated front/back and refreshed by a PCRTC_SYNC record carrying the whole digested GSPCRTCRegs plus the pre-decrement scanmask counter. GSvsync now digests (unchanged), submits the PCRTC record, flushes, then submits a VSYNC record carrying field/registers_written/idle_frame; the executor runs the whole VSync() body (Merge, present, capture). Record order reproduces today's semantics: vsync-flushed draws see the fresh display state, mid-frame draws the previous frame's. Merge's scanmask decrement stays back-side; the front mirrors it at enqueue once the copies are distinct (GV7-1). GSPCRTCRegs hoists to GSBackQueue.h as the record payload type; GSState keeps an alias. Gate: gs_vertex_tests 17/17; gsrunner PNG hashes bit-identical to the GV-0 baselines on all dumps, Vulkan and SW. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
3e077eff9b |
Merge yaps2: arm64 JIT transplant + test/perf/libretro infrastructure
Merges yaps2/main (github.com/yaps2/yaps2, |
||
|
|
445d7aee1b |
iOS: Use sandbox-safe shared memory for GS
GSAllocateWrappedMemory's POSIX branch called shm_open("/GS.mem",
O_RDWR | O_CREAT | O_EXCL, 0600) directly. The iOS application
sandbox rejects named POSIX shared memory in the system-wide
namespace, so shm_open returned -1, the function returned nullptr,
and GSLocalMemory::GSLocalMemory() aborted via
pxFailRel("Failed to allocate GS memory storage."). On hosts that
re-enter the CPU thread across launches, this surfaced as a game-
launch abort on the GS thread.
The rest of the codebase already routes shared-memory creation
through HostSys::CreateSharedMemory (pcsx2/Memory.cpp uses it for
EE/IOP RAM), whose Linux/Apple branch selects memfd_create on
Android, shm_open on desktop POSIX, and a file-backed TMPDIR
fallback on iOS so the same call works under the sandbox.
GSAllocateWrappedMemory was the only production caller bypassing
the helper.
Delegate fd creation to HostSys::CreateSharedMemory, drop the
Android-only memfd_create special-case and the redundant ftruncate
(both are handled inside the helper), and use
HostSys::GetFileMappingName so the name is PID-qualified instead of
the fixed "/GS.mem". The MAP_SHARED repeat-mirroring mmap loop is
preserved unchanged so the 4 MB GS VRAM still appears `repeat` times
at contiguous virtual addresses for the PS2 GS address-wrap
behaviour. GSFreeWrappedMemory is updated symmetrically to call
HostSys::DestroySharedMemory.
The Windows branch is untouched, and the caller contract (return
nullptr on failure; the caller's pxFailRel handles the abort) is
preserved.
Verified with the iOS build and repeated game launch/exit/relaunch
cycles: GSLocalMemory construction no longer aborts and the wrapped
memory layout is unchanged.
|
||
|
|
9e0dc436f7 |
Android 2.6.0: ANGLE toggle, gyroscope, cheats/disc-swap, GameDB + fixes
- OpenGL-via-ANGLE renderer toggle for broken native GLES drivers, with a driver-keyed GL shader cache so switching drivers recompiles instead of feeding foreign program binaries to glProgramBinary (fixes Mali-G77 crash) - Gyroscope input (aim/steering modes, sensitivity, smoothing, invert) shared between the Pad settings tab and the in-game Controls tab - Re-add disc swap without closing the game, and per-cheat PNACH enable - Xclipse GPU profile + Mali-G615 freeze gate; MediaTek Tekken 5 override - GameDB: KH2, Tekken 5, Rumble Racing, MK Shaolin, Avatar - Folder-reuse settings recovery (reverse-map INI + config mirror) - In-game pause menu rail icons; make new settings searchable - Resume/auto-load: wait for the renderer to present before restoring state and force a present after load (reduces black screen on resume) - Boot crash guards (pad state before VM); FXAA + CAS sharpening |
||
|
|
5682ed9951 |
GS: add GSDeviceNone — deviceless host device for the Null renderer
GSRendererType::Null previously still required a real graphics device: GetAPIForRenderer() had no Null case and fell through to the host's preferred API, so headless runs needed a working Vulkan/GL context. On boxes where no API works from a scripted session (mq65: turnip reports Vulkan 1.0 over ssh — cause unfound; EGL can't create a surfaceless context) the VM failed to initialize at GS open, blocking eerunner A/Bs. GSDeviceNone touches no graphics API: BeginPresent() always reports FrameSkipped so the present/ImGui path never draws, textures are RAM-backed stubs so map/update callers stay memory-safe, and the Null renderer pairs with it via a new RenderAPI::None case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
431ca0c063 |
iOS: port JIT and W^X foundation, refresh SwiftUI frontend, fix critical boot and display bugs
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. |