mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
master
453
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ff2b0c2155 | Merge branch 'pr-593' into jit-android-catchup-gv7 | ||
|
|
f6ddff79bc |
LSFG: expose adaptive frame pacing, and remove the diagnostic tracing
The pacer shipped in the previous commit but was inert: GSConfig.LsfgTargetRate defaulted to 0, which means "hold the multiplier fixed", and nothing in the Android settings could change it. So the port ran, but the specific problem it was brought over to solve — games that oscillate between 60 and 30fps on a 60Hz panel, where a fixed multiplier presents 120 then 60 and judders at every transition — was still there. Working but inert is the failure mode worth naming: nothing errors, the feature simply does not do the thing it was for. Plumbed through the usual twelve places (the field, INI read and write, the differs chain, toJson/fromJson, both per-game override paths, the reset list, the search index, the strings, and the two call sites), plus the two C++ ones in Pcsx2Config. Presented as a switch rather than a number. The pacer needs a concrete Hz, but picking one by hand is not a decision anyone can make usefully and the only sensible answer is the panel's own refresh rate — so the UI writes that when the toggle goes on, and 0 when it goes off. Off remains the default, so behaviour is unchanged until it is asked for. Also removes the step tracing added while chasing the Turnip crash. It did its job: five rounds of reading the code produced three wrong theories, and the trace produced the answer in two. The reasoning it uncovered is in the comments, which is where it belongs — the instrumentation is not. The new strings live in the github-only table, so the Play split still holds: playDebug has zero class files containing 'Lossless' or 'perf.lsfg', githubDebug has 2 and 4. |
||
|
|
a6d1e35748 |
Settings: copying global settings into a game writes only the real decisions
"Copy Global Settings" does not copy the settings you can see. It runs the whole configuration through a wrapper that writes every key unconditionally, so the file it leaves behind holds roughly seven hundred of them — network adapters, the debugger, trace logging, memory cards, sections no settings page ever shows. That was untidy and no worse, until a key present in a per-game file started meaning the player claimed it. Now one press of a button whose dialog promises only that "the configuration for this game will be replaced by the current global settings" turns off every automatic fix that game had, permanently and silently. A value is worth writing down only if it decides something, and there are two ways it can fail to. It can be the stock default, in which case the file carries it as noise. Or it can be what the game database is going to set anyway, in which case writing it can only become a claim that suppresses the fix it agrees with. So the copy now excludes both, and what lands is what the player actually chose. The comparison goes through the string form rather than the typed value, so a float or an enum name compares the way it will be stored rather than the way it happens to sit in memory. That is why the references are built with the same interface class: same formatting on both sides, exact comparison, one path for every type. The database reference is a default configuration with the entry applied, not this one with the entry applied. The question is what the database wants, not where it would leave the source. It matters for the handful of fixes that clamp rather than assign, and it errs towards writing the player's value — never towards dropping a fix, since a value is only skipped when it already equals what the fix would set. Working the reference out means running the apply functions for an outcome nobody is going to run with, so they take an apply mode. A hypothetical apply says nothing to the log, raises none of the recommendation messages, and does not allocate the four megabyte lookup table that the Goemon TLB fix asks for. The tests cover the precedence rule and the filter, but the ones that matter are the drift guards: they assert every gamefix, speedhack and clamp mode has a settings key, and that the only graphics fixes without one are the six that genuinely have no setting behind them — three renderer routine selectors and three that only raise a recommendation. A knob nobody maps is a setting that goes quietly back to being overridden, with no warning and no failure, and that is what these are here to catch. |
||
|
|
2a98726692 | Merge remote-tracking branch 'origin/master' into jit-android-catchup-gv7 | ||
|
|
eeb3affb13 |
GS: FidelityFX Super Resolution 1 as an output-scaling mode
Adds FSR1 (EASU upscale + RCAS sharpen, two compute passes) to the Vulkan backend, so a game rendered below display size can be upscaled properly instead of bilinear-stretched at present. Slots in beside the existing MetalFX branch in GSRenderer rather than introducing a parallel abstraction: GSUpscaler and the non-pure DoXxx virtuals already occupy that design space, and OpenGL and Metal inherit a false return and need no change. FSR1 is MIT (AMD, 2021) and the tree already ships ffx_a.h and ffx_cas.h under the identical grant, so the headers are vendored verbatim with their licence blocks intact. ★ ffx_a.h is NOT replaced. FSR1 wants the 2021 header, ours is 2019, and the 2019 one has been locally patched for Metal Shading Language (A16, A_MSL, A_MAYBE_UNUSED) with ffx_cas.h depending on those. Swapping it would break the Metal backend. The 2021 copy ships alongside as ffx_a_fsr1.h, used only for GPU-side string substitution. The CPU-side FsrEasuConOffset/FsrRcasCon compile against the existing 2019 header — verified by compiling a probe, not by grepping, because AU1_AF1 and AU1_AH2_AF2 are functions and a grep for a #define reports a false negative. Two shader modules, not two specializations. FSR_EASU_F and FSR_RCAS_F are preprocessor gates deciding which function bodies ffx_fsr1.h emits at all, and specialization constants resolve after preprocessing, so CAS's constant_id trick would produce a shader calling undefined functions. Confirmed distinct: disassembly shows EASU with three OpImageGather and RCAS with none. Both passes push the full 80-byte constant block. With all five uvec4 declared so one layout serves both, Sample decorates to byte offset 64 — pushing the 32 bytes RCAS nominally needs would leave it undefined, and Sample gates a gamma-squaring branch, so garbage there squares the image. Binding 0 is a combined image sampler, unlike CAS's plain sampled image, because EASU uses textureGather. The EASU intermediate stays in GENERAL with explicit compute-to-compute barriers. Layout::ShaderReadOnly targets the FRAGMENT stage and TransitionToLayout early-outs when the layout already matches, so neither of the usual tools makes a compute write visible to a compute read. The barrier also covers frame N+1's EASU write against frame N's RCAS read, since the image is parked across frames. FSR and CAS are alternatives, not a chain: RCAS is itself a sharpener. Selecting FSR hides the CAS rows. Pipeline compilation failure is non-fatal and leaves Features().fsr1 false, matching the CAS path that exists because of an Adreno 650 crash. GSUpscaler::FSR1 is appended, not inserted, since the enum is persisted as an integer. Android clamps to the enum's own maximum rather than the count of options its picker shows — clamping to the picker would have rewritten FSR1 back to Off on every save, because MetalFX occupies value 1 and is never displayed. Verified: build clean, no C++ or Kotlin errors; all three resource files packaged into the APK; FSR code present in the core. NOT verified: anything on a GPU. No visual check, no perf numbers, and in particular no confirmation that textureGather in a compute shader works on the Adreno drivers this targets. |
||
|
|
dfd92a4f31 |
LSFG: persist settings and shaders, report status, add flow scale and 3.1p
Five changes, one of which is a plain bug in what shipped. ★ LSFG settings were never persisted. lsfgEnabled, lsfgMultiplier and lsfgDllPath were absent from toJson/fromJson, and that pair IS the persistence format — ConfigStore stores toJson().toString(). So every choice, including the Lossless.dll the user went and found, was discarded on restart. Added to the round-trip, the per-game override diff/merge, and gsDiffersFrom. Translated SPIR-V is now cached to disk. Extraction used to keep raw DXBC and translate inside the shader callback, so all 26 translations re-ran on the GS thread inside EndPresent after every enable, resize or multiplier change. Now ExtractShaders translates eagerly, drops the DXBC, and writes <cache>/lsfg_shaders.bin. The DLL's size and mtime go in the header and a mismatch re-extracts — ARMSX3's equivalent has no invalidation at all. Frame generation can no longer fail invisibly. GetStatusText() feeds one line to the performance overlay, empty ONLY when the user has not enabled it: unavailable / failed / no shaders / starting / a display rate. That rate counts frames actually PRESENTED, real plus generated, because the acquire loop can break early and assuming the multiplier would overstate it. FPS alone cannot show this — frame generation deliberately does not change the emulator's frame rate, so without a separate line 'working', 'broken' and 'unsupported' are all the same absent line. Flow scale and the 3.1p pipeline are exposed. flowScale is a DIVISOR — framegen computes flowExtent = inputExtent / flowScale — so the UI percentage is passed as clamp(100/percent, 1, 4). ARMSX3 passes percent/100, where only the default is right because 1.0 is its own reciprocal and every lower position makes it slower; that inversion is not copied here. 3.1p is a separate shader family with separate device state, so the shim fixes the choice at initialise and dispatches every entry point on it, and the name table gains the p_* resource IDs. Frames the game did not draw are no longer interpolated. PresentWithGeneration captured unconditionally, so pause menus and boot screens got interpolated at full GPU cost. It now takes frame_has_new_content, sourced from the condition GSRenderer already computes (current && !blank_frame) rather than a new heuristic, and consumed with std::exchange because RenderBlankFrame presents without going through BeginPresent. A false also resets the frame history, so the pair either side of a gap is never stitched into one bogus in-between frame. Verified in the built APK: four status states and lsfg_shaders.bin in the core, 26 p_* names in its table, 3.1p linked into the shim. Build clean, no C++ or Kotlin errors. NOT verified: any of it at runtime — no Adreno 7xx here, so the flow-scale direction, the cache round-trip and the capture gate are reasoned, not observed. |
||
|
|
94c4fa72ea |
EE/FPU: split iFPUd's multiplier deficit into a fourth clamp mode
Both iFPUd modes emitted the multiplier deficit in full: the Booth term, the boundary predicate over it, and an out-of-line call to the multiply array for what neither decides. eeClampMode 3 now emits the Booth term alone, three instructions off ft's mantissa; a new eeClampMode 4 keeps the other two. Nothing else differs between them. Mode 4 reaches the config through the GameDB and the INI and has no picker entry, but the front ends still write the bit: ApplySanityCheck rejects a config whose bits are not a whole mode, and a rejected config falls back to the default rather than to the mode that was picked. The harness's clamp-mode helpers set whole modes for the same reason. |
||
|
|
318588f7a6 |
Android: LSFG frame generation (github flavour only)
Drives Lossless Scaling's interpolation from our own Vulkan present path. Upstream's consumer app captures the screen with MediaProjection and composites over the target process, because Android 12+ forbids injecting code into a non-debuggable app. That constraint is not ours: ARMSX2 owns its swapchain, so it hands the library its own images through the AHardwareBuffer entry points. No screen capture, no overlay, no accessibility service. NOTHING PROPRIETARY SHIPS. The interpolation shaders are read at runtime out of the user's own Lossless.dll, supplied through SAF exactly as a PS2 BIOS is. The requirements dialog says so before the toggle commits, not after it silently fails. Only the MIT-licensed lsfg-vk-android framegen library is fetched; its sibling app carries a no-commercial-use licence and is not. framegen is ISOLATED IN ITS OWN .so BEHIND A C ABI. It links volk, which defines 759 globals named vkCreateImage, vkQueueSubmit and so on -- precisely the names VKLoader.cpp defines. In one library that is a duplicate-symbol error at best; at worst the linker merges them and framegen's volkLoadDevice() call, made against its OWN VkDevice, silently repoints every entry point the GS renderer uses, which would present as a driver crash with nothing pointing back at frame generation. libarmsx2_lsfg.so gives volk its own copies, and nm confirms only the eight armsx2_lsfg_* entry points are exported. The interface is C because the CMake project builds ANDROID_STL=c++_static, so an std::vector crossing that boundary would be two unrelated types sharing a name; errors come back as codes, never exceptions. The shader chain (pe-parse over the PE resources, then upstream's DXBC to SPIR-V translator) stays in the core -- neither half touches Vulkan symbols. GSLsfg.cpp is the one PCSX2 translation unit built with exceptions, because that translator throws and the alternative is std::terminate on exactly the paths a wrong DLL takes. Present path mirrors upstream's Android sequence: copy the rendered frame into shared storage, idle, interpolate, idle, present each generated frame, then the real one. The idles are not laziness -- Turnip rejects OPAQUE_FD on AHB-imported memory, so there is no cross-device semaphore and a device idle is the only barrier that exists. Every failure degrades to an ordinary present rather than taking the GS thread down. Gated on Vulkan + Adreno 7xx and newer, asked of the resolved driver profile rather than a GL_RENDERER substring. The UI reports WHY it is unavailable, since 'needs an Adreno 7xx' and 'you have not picked a DLL yet' are the same greyed row otherwise and only one is actionable. Rows live in All Settings > Performance and the in-game performance tab, from one shared section, wired to each host's own settings tier the same way ShaderChainSection is. Play builds compile the whole thing out -- gradle sets ARMSX2_ENABLE_LSFG=OFF, BuildConfig.LSFG is false, and build-play-aab.sh now fails closed if libarmsx2_lsfg.so ever appears in a bundle. Verified: github APK carries libarmsx2_lsfg.so and 13 live @@ANDROID_LSFG@@ strings in the core; the play variant configures with zero references to either. NOT verified: the present path itself, which needs an Adreno 7xx device, a real Lossless.dll and a running game. |
||
|
|
fc4d1eb44d |
GS: let players claim the remaining hardware fixes
Eleven more hacks get a claim bit: palette conversion, depth support, framebuffer conversion, read targets on close, the 24 bit depth limit, texture region estimation, draw buffering, both CPU sprite render values, CPU CLUT render and GPU target CLUT. They move behind the same keep guard the sprite hacks use, and the ten with a database fix id map across to it. Every one of them was cleared on each settings load whenever manual hacks were off, which is the default, so a frontend row for any of them did nothing at all unless the player also turned the database fixes off for that game. Disable safe features and disable render fixes stay unconditional, since no frontend exposes them. |
||
|
|
8ee55f7039 |
GS: say so when native resolution turns an upscaling fix back off
The GameDB apply announces every fix it sets with "Enabled GS Hardware Fix", and at native resolution MaskUpscalingHacks then turns a subset of them straight back off again — silently. Nothing in the log ever contradicted the Enabled line, so a log read at face value overstated what was actually in force. On Rogue Galaxy at 1x it claimed halfPixelOffset, roundSprite and nativeScaling were on when all three were off, which is exactly what it looked like during the Rogue Galaxy work. The apply line is not wrong when it is printed; the second event was just never reported. So report it where it happens, which is the only place it is true by construction. Only fixes that were genuinely on get named. That keeps the line honest, and it also keeps repeat calls quiet: after the first pass there is nothing left to clear, so a settings re-apply adds no noise — measured as one line across a 40-iteration replay, and none at all above 1x, where the fixes legitimately stay on. Names match the GameDB ones so the two lines read together. |
||
|
|
7fee32e49f |
GS: let a player claim preload frame data and partial invalidation
Both are GameDB hardware fixes, so the database sets them per game and the player's own value is discarded. The only way out was manual hack mode, which is all or nothing: switching one fix off throws away every automatic fix that game had. The pinning mechanism exists precisely for this, and already covers twelve other fixes; these two were simply never added to it. Append them to GSUserHackOverride after TextureOffsetY, so masks already written to an INI keep meaning what they meant, and map the two GameDB fix ids onto them. MaskUserHacks reset both unconditionally in the block below the keep() guards, so move them up: a pinned value has to survive the mask as well as the database, and only both together make the claim stick. MaskUserHacks(false) — the BIOS-boot call that strips hacks for safety rather than preference — still resets them, since the new guards take the same respect_claims parameter as the rest. Verified on a Rogue Galaxy replay, which carries seven fixes including disablePartialInvalidation. Pinning that one alone reports it skipped and still applies the other six; pinning preload frame data behaves the same against a temporary database row; pinning both skips both. The mask half shows up as silence — with a fix pinned and its value on, the database finds the config already agreeing and logs nothing, where the same run without the pin logs the fix being applied over the wiped value. No frontend exposes these yet. Pins are set by writing the override mask, and the only frontend doing that today lists upscaling fixes only, which neither of these is. AutoFlush and TextureInsideRt already sit in the enum with no frontend entry, so the mapping is useful on its own and surfacing them is a separate decision per frontend. |
||
|
|
37d56169db |
GS/Vulkan: take the RT copy on every draw where the self-read is broken
The workaround was gated on texture replacements being loaded. That read the
evidence backwards. Tales of the Abyss lost its text layer with a pack while NFS
Underground pushed 608 barrier draws per frame with no pack and looked fine, so
the failure was attributed to sampling a replacement. It is not: the in-pass
self-read is unreliable for ordinary blending too, it just fails subtly enough
there to pass inspection.
OutRun 2006 has no pack and renders its sea as high-contrast two-tone speckle.
Measured against the software renderer on Turnip/Adreno 650, in-tile differs from
the oracle over 4.0% of the frame by more than 16 levels; through the RT copy,
0.13%. Both in-pass shapes - the subpassLoad input attachment and the
feedback-loop-layout texelFetch sampler - are byte-identical wrong, which is what
identifies this as the driver rather than the draw.
Drop the LoadTextureReplacements term so every draw on an affected driver reads a
copy. Cost measured on device, RT copy vs in-tile, median frame time over two
runs each:
1x 3x 4x
FlatOut 2 +7.5% +10.5% +24.6% (copies/frame 23 -> 477, RPs 100 -> 538)
OutRun +20.5% +9.9% +0.5%
GoW II -3.2% +2.9%
RG lamps -1.5% +9.3%
The old note recorded +38%/+40% at 3x/4x on NFSU. Nothing here reproduces that
on the titles available now - FlatOut 2 makes a bigger structural change for a
third of the cost at 3x - so it is recorded as an upper bound on a build we can
no longer run rather than as a contradiction. OverrideTextureBarriers = 1 still
restores the in-tile path for anyone who would rather have the frames.
This depends on the preceding DATE change: turning texture barriers off also
turns framebuffer fetch off, and Adreno has no stencil buffer, which used to
leave DATE with no mechanism at all and washed the road blue.
LoadTextureReplacements leaves RestartOptionsAreEqual with it: the shader variant
now follows only OverrideTextureBarriers and the driver profile, so replacements
can be toggled in place again.
Verified on device: with default settings the water dump is now byte-identical to
the forced RT-copy run and within 0.13% of the software oracle. Non-Adreno is
untouched - 16 colour frames identical across OutRun, Katamari, MGS3, Dirge of
Cerberus and Shadow of the Colossus on Honeykrisp.
|
||
|
|
3cb1e88029 |
Merge remote-tracking branch 'origin/master' into jit-android-catchup-gv7
# Conflicts: # tests/ctest/core/gs/CMakeLists.txt |
||
|
|
de7ec8509c |
GS: 20:9/19.5:9/custom aspect, interlace+presentation policies, VK feedback flags
Aspect ratios: added 20:9, 19.5:9 and a user-entered Custom ratio
(GSOptions::CustomAspectRatio, clamped 0.5..5.0). All APPENDED, never inserted —
these values are persisted as raw ints in the ini and in the Android prefs, so
slotting one in mid-enum would silently repoint every saved config at a different
ratio. Also filled in the two ultrawide cases RequestDisplaySize was missing.
Interlace/presentation: ported sashkinbro's EmuCoreX 30799e4. SelectGSInterlaceMode
centralises the mode choice and keeps shader_mode -1 for automatic full-frame output
(a deinterlace pass must not run over progressive output during a video-mode
transition); our formula already agreed, so this is centralisation plus
static_asserts rather than a behaviour change. ShouldSkipAndroidBlankFrame is new
behaviour: Vulkan now suppresses only the startup blank, so a mid-game fade reaches
the normal present path and its recorded command buffer is submitted.
Vulkan: declare the attachment feedback loops on the PIPELINE, not just on the image
layout and render pass. We put attachments into FEEDBACK_LOOP_OPTIMAL without ever
setting VK_PIPELINE_CREATE_{COLOR,DEPTH_STENCIL}_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT,
which the spec requires — undefined behaviour rather than a missed optimisation, and
strict mobile drivers are where undefined shows up as stale attachment reads.
|
||
|
|
915e914ba6 |
GS: let a hack the player picked outrank the game database
Right now changing one sprite hack is all or nothing. With manual hacks off, MaskUserHacks wipes whatever the player set and the game database writes its own value on top, so for the 227 games that carry alignSprite the toggle does nothing in either direction while the settings screen still shows the player's value. Turning manual hacks on to get around that throws away every other automatic fix the game had, which tends to trade one glitch for a different one. So let a single hack be claimed instead. GSOptions carries a bitmask of the ones the player set on purpose. MaskUserHacks leaves those alone and clears the rest as before, and applyGSHardwareFixes skips a claimed fix down the same path it already uses for manual mode, so it still gets named in the warning and every other database fix for the game is applied normally. The mask sits outside the bitfield union deliberately. That packing is what OptionsAreEqual compares wholesale, and this is not a hack value, it is who owns one. It gets its own comparison so that claiming or releasing a hack counts as a settings change even when no value moved with it. MaskUpscalingHacks does not honour the mask. Below 2x the renderer skips these regardless, so keeping one set there would only leave GSConfig and the settings overlay claiming something that never runs. The warning text now depends on which of the two cases fired, since blaming manual mode when someone only claimed one hack would be wrong. Nothing sets a bit yet, so behaviour is unchanged until a frontend does. |
||
|
|
c553aa8acb |
GS: add a 21:9 aspect ratio
Requested by David (SSR), who noted no PS2 emulator offers one: without it the only way to use an ultrawide patch was Stretch, which distorts. Useful on folds, tablets, DeX and anything driving a 21:9 panel. Added to the generic aspect AND the FMV override, since a game that wants ultrawide gameplay usually wants it during cutscenes too. Adding it to the FMV enum also shifted MaxCount, which the name array is sized from -- that array had to grow with it or the last entry would have been a hole. The Kotlin side needed SIX edits for one new enum value, and getting five of them right still left the feature completely dead: RendererTab options list + clamp RendererTab FMV options list + clamp EmulationMenu setAspectRatio clamp EmulationMenuScreen the pause menu's own options list Settings NativeApp.setAspectRatio(coerceIn(0, 4)) <-- the killer Settings INI name<->index, both directions That fifth one clamped on the way to the core, so the picker highlighted 21:9 while the emulator was told 10:7 -- UI correct, nothing happens, no error. The sixth meant the choice would not have survived a reload even once it applied. The NATIVE clamp needed no change at all, because it derives its bound from AspectRatioType::MaxCount instead of hard-coding it. That is the pattern the Kotlin side should follow; four literal 4s in four files is why this was a six-site change instead of a one-site one. |
||
|
|
55c22d3ca2 |
GS: fix missing draws on Adreno when texture replacements are loaded
Tales of the Abyss with an HD texture pack loses its entire 2D text layer on Vulkan/Adreno (#442). The replacement shifts the source alpha range, which flips those draws to require_one_barrier; the draw then reads the render target back while it is still bound as the colour attachment, and the driver silently drops it. Device A/B on Turnip/Mesa 26.1.2 + Adreno 650: both in-pass forms fail -- the subpassLoad input attachment and the feedback-loop-layout texelFetch sampler -- while reading a separate copy of the target renders correctly. Not tile-size related; the text is missing at 1x as well as 4x. Route this through the driver-bug database instead of another inline vendor test. That database was built for exactly this and had never been consulted: its sources were compiled only under if(ANDROID) and were missing from pcsx2.vcxproj, and every call site was #if defined(__ANDROID__) -- so every rule was dead on the ARM Linux handhelds we test on, including the device that reproduces this bug. Resolve the driver profile on all platforms and give it its first HasBug/UsesWorkaround consumer. The mobile-only consequences (runtime GPU profile, GS pool tuning) stay Android-gated on purpose: off Android the detector classifies every non-Mali GPU as Adreno, and desktop pool sizing is not this code's business. Non-Adreno targets resolve to zero rules and zero workarounds, verified on Apple/Honeykrisp. Narrow the workaround to when replacements are loaded. NFS Underground pushes 608 barrier draws per frame through the same in-tile self-read with no pack and renders correctly, so the read is fine for ordinary blending. Applying the copy unconditionally cost +38%/+40% frame time at 3x/4x on an NFSU dump replay (copies 5 -> 348 per frame, render passes 62 -> 391) for no correctness gain. This replaces an is_adreno block that forced the subpassLoad path on regardless of INI. Its own comment already recorded that the feedback-loop sampler drops content; what it missed is that subpassLoad drops it too, so it was choosing between two broken reads. Adreno joins vendor_allows_fbfetch so removing the force does not demote the proprietary blob to the per-primitive barrier path, and DisableFramebufferFetch now actually takes effect there instead of being eaten. LoadTextureReplacements joins RestartOptionsAreEqual: it now selects the tfx.glsl RT-read variant at shader-compile time, so toggling it in place would leave the feature flag and every compiled pipeline disagreeing with the setting. OverrideTextureBarriers still wins when set explicitly -- 1 restores the in-tile path, 0 forces the copy for anyone who hits this without a pack. |
||
|
|
1620453785 |
GS: add the render-pass scheduler, one open run
Introduces GSPassScheduler, which holds hardware draws in a queue instead of handing them straight to the backend, and emits them when something needs to observe the target. This is the plumbing only: with a single open run the queue can only ever hold draws that are already consecutive and already share a render pass, so GPU order is identical to before by construction. Coalescing across a target alternation - the point of the exercise, and where the win on a tiler is - needs a second open run and lands separately. Deferral copies the draw config and, importantly, its geometry: config.verts and config.indices point into GSState's per-draw buffers, which the very next draw overwrites. Records index into two vectors rather than holding pointers, since those vectors reallocate as a run grows; they are never shrunk, so a scene reaches its high-water mark and then stops allocating. Only "plain" draws are deferred - no barrier, no feedback loop, no destination alpha, no colclip, no second pass, no drawlist. Everything else renders immediately after flushing, so a game that never ping-pongs targets keeps exactly today's behaviour. Gated on EmuCore/GS/CoalesceRenderPasses, default off, deliberately not in the restart set: toggling it just stops deferring. Verified over the .gs dump corpus (Dirge of Cerberus, God of War II, FlatOut 2, Katamari Damacy, MGS3, Ratchet & Clank UYA) with gsrunner: every frame hash and every render-pass count identical between off and on. On Dirge, 96.5% of draws take the deferred path and the longest run reaches 204 draws, so the copy, the run-key match and the flush hooks are all genuinely exercised. |
||
|
|
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. |
||
|
|
5117a3d955 |
GS: per-draw debug labels, decoupled from UseDebugDevice
Draws in a graphics-debugger capture were anonymous: the only per-draw label was
GL_PUSH("HW: Draw %lld (Context %u)"), carrying a serial and a context index and
nothing about the PS2 state that made the draw expensive. Worse, that label is
double-gated -- compiled out unless ENABLE_OGL_DEBUG, and further requiring
UseDebugDevice, which also installs the Vulkan validation layer. So a labelled
capture was necessarily a validation-layer capture, and useless for timing.
Adds EmuCore/GS/DebugLabels and a GSDevice::PushDrawLabel/PopDrawLabel pair that
is compiled into every build, so a capture taken on a handheld perf build still
names its draws. GSRendererHW::DescribeDraw builds the string: primitive type
and count, FRAME/ZBUF/TEX0 addresses, formats and buffer widths, FBMSK, blend
equation, and alpha-test / DATE state, each included only when it applies.
The decoupling has two halves. Emission is gated on DebugLabels rather than
UseDebugDevice, and the VK_EXT_debug_utils *instance extension* now follows
DebugLabels too -- without it vkCmdBeginDebugUtilsLabelEXT never resolves and the
labels would silently do nothing. The debug messenger stays on UseDebugDevice,
since that is a debug-device concern rather than a labelling one.
DebugLabels joins the restart set: the instance extension is fixed at instance
creation, so toggling it in place could not take effect.
Deliberately not un-gating ENABLE_OGL_DEBUG wholesale, which would have been the
smaller diff. The GL_* macros evaluate their format arguments at every call site
before the emitter can bail, and there are thousands of them across the texture
cache and HW renderer, so that would cost far more than it is worth in a release
build. A dedicated path pays only when labelling is on.
Verified on a 20-loop dump replay: counters identical to a label-free run and
frame time within noise (-0.02%), with VK_EXT_debug_utils enabled and no
validation layer. Label *content* in a capture is not verified here; that needs
RenderDoc.
|
||
|
|
4fc1fa7c5f |
PINE: add statistics and settings opcodes, plus a gsctl client
Debugging a GS performance problem meant restarting the emulator and reloading a
savestate for every "did you try setting X?", and the only way to read the
statistics that drive that decision was to look at the OSD. PINE already
provides a unix socket, a thread, framing, a config key and the RunOnCPUThread
marshalling pattern, but its opcodes stop at guest-RAM peek/poke plus savestates
and game identity -- no host statistics, no settings.
Adds four ARMSX2-local opcodes at 0x10+ (upstream PINE ends at 0xF, so a generic
client will never send them):
MsgGetStats PerformanceMetrics, all GSPerfMon counters and the texture
cache memory figures, as JSON.
MsgGetSetting read a setting by section/key.
MsgSetSetting write a setting, apply it, and report whether the key forces a
GS device reopen.
MsgFrameAdvance step a paused VM.
MsgSetSetting writes the persisted key rather than poking EmuConfig directly,
because a direct poke is silently reverted by the next ApplySettings, which
re-derives EmuConfig from the INI layer stack. The restart_required answer comes
from a new GSOptions::IsRestartOption, sitting next to RestartOptionsAreEqual so
the two lists stay in sync.
Statistics are gathered on the PINE thread. PerformanceMetrics and g_perfmon are
benign scalar reads, but GSgetMemoryStats dereferences g_texture_cache and
g_gs_device, which are GS-thread owned, so that one is marshalled through
RunOnGSThread.
tools/gsctl.py is a stdlib-only client emitting JSON on stdout.
Verified against a headless gsrunner replay: toggling accurate_blending_unit
between 0 and 5 over the socket moves barriers 1.0 <-> 91.5 and draw calls
55.5 <-> 101.5, repeatably, with no restart.
|
||
|
|
2262c7bf55 |
Merge remote-tracking branch 'yaps2/main' into jit-transplant
# Conflicts: # pcsx2/arm64/BaseblockEx-arm64.h |
||
|
|
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. |
||
|
|
679c230841 |
ee/fpu: make add/sub guard-bit emulation a toggleable option (default on)
Reintroduces the fpuGuardedAddSub Recompiler option removed in
|