Commit Graph
24969 Commits
Author SHA1 Message Date
J1coding d24e9d76ed iOS: clamp a shader's prescale so upscaling cannot blacken the frame
crt-aperture and sharp-bilinear each derive a whole-number prescale from
output height over source height and then divide by it. RetroArch only ever
feeds them a small console framebuffer being scaled up, so that ratio never
falls below one. PCSX2 renders internally at up to 8x: past roughly 1.5x on
a phone the source is taller than the screen, the ratio drops under one,
floor() returns zero and the divide yields NaN. The whole frame goes black.

Reported on an iPhone SE 2 with a 1334x750 window, where 1.5x rendered and
2x did not. Two hypotheses were wrong first -- push-constant placement, then
parameter placement -- and both were refuted by tester data before the
reporter supplied the actual trigger, which was the internal resolution and
not the preset. Reproduced in the simulator at 3x and fixed there.

The clamp is what the sibling sharp-bilinear-simple already carries as
max(floor(...), vec2(1.0)) and what crt-geom carries as clamp(floor(...),
1.0, 2.0). Nine of the eleven bundled presets never divide by a derived
scale and were unaffected.

These files are otherwise byte-verbatim copies of a pinned upstream commit,
so the divergence is a reversible patch beside the librashader one and a
note in ATTRIBUTION.md. The test fails if either guard is dropped, which is
what a re-sync from upstream would otherwise do silently.
2026-08-18 23:41:53 +02:00
J1coding 16b571cf72 iOS: put the shader chain and its parameters in settings
A section in Graphics after Shade Boost, matching pipeline order, a folder-
at-a-time preset browser, and every parameter a preset declares on screen.

The section is absent rather than disabled in a build without librashader,
gated on a bridge capability, so a cargo-less build does not advertise a
feature it cannot run.

Every number in a preset's parameter block is the shader author's, so every
number is treated as hostile. Absent, non-finite, inverted ranges and a zero
step all occur in the published collection. A parameter whose range cannot
be made sense of is dropped rather than rendered as a control that does
nothing.

Pushing a value sends the effective value of every parameter, not only the
changed one. librashader has no unset call, so a name dropped from the
override map would leave the chain on whatever was pushed last and a reset
would never take.

A tweaked preset can be saved as its own file: a #reference to the base plus
the changed values, written into My Presets inside the scanned root so it
becomes selectable with no extra plumbing. The reference is relative while
the base is in Documents, so the pair survives the container moving; a
bundled base gets a path instead, which a reinstall breaks, and the sheet
says so.

The naming sheet is .sheet(item:) rather than .sheet(isPresented:), because
the parent's body invalidating tears the content down and takes keyboard
focus with it, which is the failure this codebase has a rule about.

Also here: the once-cached name lists are owned rather than read after free,
and librashader builds for the simulator as well as the device, which is
what makes any of this testable without hardware.
2026-08-18 23:41:53 +02:00
J1coding f23ddabf1e iOS: run RetroArch shader chains on the Metal renderer
librashader built for arm64 and pinned, wired into GSDeviceMTL, and a preset
library behind it that can name a file the same way twice across a
reinstall.

The chain runs from DoApplyShaderChain, after ShadeBoost and before present,
on the same ping-pong the FXAA path uses. Two things about it are load-
bearing rather than incidental. EndRenderPass comes first, because the chain
opens its own passes and Metal aborts if ours is still encoding. And
FlushEncoders comes last, because librashader recycles its per-frame objects
over a ring shallower than our deferred-submit window, so a chain frame has
to end the batch. A failure latches on the preset that caused it, or a
preset that will not compile recompiles every frame forever.

Static archive rather than dylib, decided by building both against a working
tracer and measuring, and the loser was deleted rather than left as an option.

The library underneath is where the reinstall problem lives. Both preset
roots sit under a container UUID that changes on every sideload, so a
selection stored as an absolute path is stale within days. A preset is
stored as a marker plus a root-relative path -- bundle: or data: -- and re-
rooted at launch. The separator is a colon because Files refuses one in a
name and it is not a path separator, so the relative half never needs
escaping.

Packs come in as a picked zip or folder through an extractor that keeps the
directory tree, because a .slangp names its stages by relative path and the
tree is part of the pack rather than an arrangement of it. That is the
opposite of the skin extractor's flattening policy, so a test fences the two
apart. Sixteen presets ship in the app, each cleared against its own header
rather than a blanket grant.

librashader's own cache goes to Library/Caches through XDG_CACHE_HOME, set
before anything loads it. Latent today because the Metal runtime never
reaches that cache, but a pin bump that adds caching would otherwise put a
disposable file somewhere iOS can neither purge nor keep out of a backup.
2026-08-18 23:41:53 +02:00
pstef 5b616729f1 EE rec: stop raising TLB misses on unknown MMIO too
The _ext_mem* fallbacks raise a TLB exception when a registered region
gets an access its device has no case for. Under a recompiler that is
the defect just removed from vtlb_Miss by another route: nothing diverts
the block, so the raise only latches Status.EXL.

Raise on the interpreter alone. Recompilers report instead, which is new
- MEM_LOG is devbuild-only, so the raise was all a release build left.
2026-08-18 08:00:56 -07:00
pstef 040104142a Tests: pin the I immediate against a rewrite of micro memory
Both polarities of the gamefix. With it on, a rewritten immediate has to
reach the block already compiled; with it off, the rewrite has to force
a recompile. Each row asserts the compile count as well as the result,
so neither can pass by recompiling behind the value it checks.

Three things the harness needs handling for. The first re-entry through
RunJitPreserveBlockCache compiles a second block variant, because it
enters on the pipeline state the previous run left rather than the
post-Reset one, so the block only goes warm on the re-entry after that.
LoadProgram writes VU.Micro directly and so bypasses the vtlb path that
calls mVUclear, which leaves the stale quick slot serving the old
program unless the test runs the invalidation itself. And the pair that
carries the immediate writes VI[REG_I] again on the way out, as does the
delay slot LoadProgram appends, so the register reads back 0 and the
assertion belongs on the VF result.
2026-08-18 08:00:56 -07:00
pstef 19cb54586f microVU: read the I immediate at run time under IbitHack
x86's doIbit picks between folding the immediate into the block and
loading it from micro memory; arm64 only ever folded.

Scarface (SLUS-21111), the game the gamefix is listed for, writes a
per-object transform into VU1 micro memory as I immediates. Under the
recompiler its trees and much of the geometry around them took whichever
object's transform compiled first. The interpreter re-reads the word on
every dispatch and was unaffected.
2026-08-18 08:00:56 -07:00
pstef c5ad9ddd4d EE rec: stop raising TLB misses, matching x86
vtlb_Miss raised the exception under the arm64 recompiler and returned,
on the assumption that the rec would pick cpuRegs.pc up at the next
dispatch. Nothing picks it up: the block runs on and its tail writes its
own branch target over the vector PC, while cpuException has already
latched Status.EXL. cpuException leaves EPC alone whenever EXL is
already set, so from the first swallowed miss onward every exception
keeps its predecessor's EPC, and the guest kernel's syscall epilogue
erets to an address belonging to the fault.

Report the miss and continue instead, as the x86 rec already does. The
guest's handler still does not run, so a title that needs demand paging
still cannot work under the rec; a stray miss now stays local to the
instruction. ee_rec_tlb_divert_tests.cpp describes the divert and stays
disabled.

With nothing left to set s_recTlbMissOccurred, the poll after every
interpreter call goes too. It could not have covered the raise anyway:
only the interpreter-call sites had it, so a flag set at an inline
access sat there until an unrelated later recCall consumed it and
diverted on a pc belonging to neither.

This gives up the one case the rec did handle, a miss in a branch delay
slot reaching the vector through the cpuRegs.branch bracket epilogue.
Its recompiler half moves to the disabled file; the interpreter half
stays live, beside a new test pinning the rec's behaviour. The bracket
itself stays: the tlb_fallback_* handlers raise from a delay slot on
their own.
2026-08-18 08:00:56 -07:00
pstef 249aefdb9b Tests: pin the rec's TLB-miss divert, disabled
A TLB miss on an inline load or store leaves the arm64 EE rec at
exception level in user code. Eight cases, all disabled, because the rec
does not do this yet and the route to making it is staged: x86's
behaviour first as a floor, then the divert built back up with these
dropping their prefix one at a time.

Found from the other end. `3D Pinball Space Cadet (PS2) (3.0) (RA)`
hangs after "Parsing complete. Finalizing...", and the visible fault is
a thread id of 0xff966c22 arriving at a caller whose syscall returned 1.
That is strlen's `subu v0,v0,a0` on the correct v0, reached because the
kernel's syscall epilogue eret'ed into the middle of strlen: 131072 of
the run's next 162766 exceptions were taken with EXL already set, so
none of them updated EPC. Upstream of all of it is one swallowed miss on
a strlen(NULL). Two working hypotheses died on the way — that the
pinned-GPR cache lost v0, and that the call-ret shadow stack mispopped —
both refuted by reading the state at the moment of damage rather than by
reasoning about the emitters.

The tests are the chain in four instructions, plus the load and store
halves of both inline emitter pairs, plus the flush the divert needs.

Three separate defects fall out of the one missing poll. The block runs
past the faulting load. EPC names the instruction after it, because
cpuTlbMiss skips its `pc -= 4` for the rec while the rec's own cursor is
already one instruction ahead outside a delay slot — the delay-slot case
comes out right only because two errors cancel. And Cause is whatever
exception came last, describing a different instruction than EPC does.

Two findings shape the fix rather than the tests, so they are recorded
here.

Reaching the vector with guest state intact costs one writeback, not a
general flush: iFlushCall(FLUSH_VTLB) already precedes every inline
access and frees the caller-saved hosts, leaving only x28, the
allocator's single callee-saved host. A boot with fastmem off puts a
live dirty guest GPR there at 489 sites and nothing else anywhere.

The fastmem backpatch thunk is the part with no clean answer. It is
generated at fault time and cannot name the live guest values of the
block around it, so it cannot divert. A census of the 12411 fastmem
sites emitted during that boot says how much per-site state a precise
one would need: 9355 have nothing live and dirty, 2480 have between one
and seven GPRs, and 576 involve the NEON file.

Lesson, from a guard test that was written wrong first. Three dirty
registers before the faulting load is not enough to make the allocator
reach x28, so that test passed with the writeback deleted — it guarded
nothing. It takes sixteen live guest values at once. A test that guards
a writeback has to create the pressure that puts something in the
register the writeback exists for, and the way to find out is to delete
the code and watch.
2026-08-18 08:00:56 -07:00
pstef 83f2510134 Tests: pin the VU FMAC's range against the console
The VU's largest value is 0x7FFFFFFF, one binade above FLT_MAX, the same
range the EE FPU has. So an exponent-255 word is an ordinary number on
the way in and on the way out, and "overflow" starts above it rather
than above FLT_MAX.

Both engines put the boundary a binade lower. vuDouble() rewrites an
exponent-255 operand as 0x7F7FFFFF and VU_MAC_UPDATE() calls every
exponent-255 result an overflow; the arm64 COP2 macro emitters clamp the
result to +/-FLT_MAX and raise neither O nor U. microVU's per-op operand
clamps approximate the same thing from a list of games rather than a
rule.

68 rows off an SCPH-90000 through VU0 macro mode, scored per engine and
per column, with what each engine cannot yet reproduce recorded per case
so a fix trips the test as loudly as a regression. Nothing is fixed
here.

Two of the rows are structural rather than about range. An overflowed
product does not become 0x7FFFFFFF before the accumulate: an addend of
-0x7FFFFFFF cannot cancel it. An underflowed product does become zero
before it. And the multiplier is the EE's, with the same one-ULP deficit
decided by ft's mantissa alone.

The harness grows two things the rows need: a VADDA encoder, and an
opt-out from Run()'s VU0 JIT-vs-interp auto-diff for tests that score
each engine against a hardware capture instead of against the other
engine.
2026-08-18 08:00:56 -07:00
jpolo1224 2a98726692 Merge remote-tracking branch 'origin/master' into jit-android-catchup-gv7 2.6.6.7 nightly-20260817 2026-08-16 14:22:55 -04:00
jpolo1224 5b790427dd LSFG/FSR: remove the debug instrumentation
The per-second LSFG branch counters and the FSR gate line were added to find two
specific bugs and both did their job — the counters proved generated frames were
reaching the screen uncounted (VK_SUBOPTIMAL_KHR treated as failure), and the
gate proved all three FSR conditions passed while a misplaced log made the pass
look dead. Neither belongs in a release: one printed every second, the other on
every state change.

What stays is event-driven and diagnostic in the ordinary sense: LSFG's
initialise line, shader-cache hits and misses, load and ABI failures, and one
FSR line per output-size change.
2026-08-16 14:22:55 -04:00
Brian Degenhardt da25cb84cc Android: call eeClampMode 4 Exact, the name the other frontends use
The desktop and Big Picture pickers landed the same tier as Exact, and
GameIndex.md documents it under that name. Android was the only frontend
calling it something else, which made the same setting look like two
different ones depending on which screen the user was on.

The translation key moves with the label rather than keeping the old name
for a value it no longer matches, so the map stays alphabetical and there
is nothing left to mislead the next reader.
2026-08-16 11:18:41 -07:00
Brian Degenhardt 541b1abfaf Android: offer eeClampMode 4 as Ludicrous in the clamping pickers
The tier existed in the core but nowhere in the UI, on any frontend, so
reaching it meant hand-editing the settings file. Both Android pickers —
the Performance tab and the in-game pause menu — now carry a fifth
option, and the settings layer packs it.

The packing is the part that matters. emucore validates the four clamp
booleans as a cascade and silently resets an inconsistent set to the
defaults rather than rejecting it, so writing fpuExactMode without its
three predecessors would not fail loudly, it would quietly land the user
back on Normal. applyTo therefore writes all four cumulatively, and
readFromIni reads them back highest-first.

readFromIni treats a missing fpuExactMode as an older core rather than as
mode 3: a build without the key never wrote it, and inferring 3 there
would demote a Ludicrous setting every time the settings were reloaded
under a mixed pair of builds.

The chip row already scrolls horizontally, so a fifth option needs no
layout change.

⚠️ Not addressed here, and worth a decision: the GameDB overwrites the
whole tier for any title carrying an eeClampMode entry, and an entry
below 4 clears the exact bit outright. On those ~115 titles the new
option is inert unless game fixes are off — which is most of the titles
whose users would want it. The setting description says so; whether the
core should let a user's choice raise the database's is a separate call.
2026-08-16 11:09:47 -07:00
Brian Degenhardt 99299e556c Merge pull request #590 from pstef/mode-3-4
Widen FPRreg to host double
2026-08-16 11:06:22 -07:00
jpolo1224 c246b9a03c LSFG: let the generated frame wait for a display slot; unhide FSR on renderer=auto
Two bugs of mine, both found on-device with everything else working.

★ The zero-timeout acquire disabled frame generation entirely.

Forcing FIFO fixed the MAILBOX discard, and the display rate still equalled the
real rate. Under FIFO the presentation engine returns an image at a vblank, so
at steady state nothing is ever free INSTANTLY — vkAcquireNextImageKHR with a
zero timeout returns VK_NOT_READY every frame, the loop breaks, and every
interpolated frame is dropped. Silently, because a dropped generated frame is a
legitimate outcome and nothing logs it.

The reasoning behind the zero was that an interpolated frame is a bonus not
worth stalling for. That is backwards: presenting two frames per rendered frame
MEANS waiting for the second display slot. Waiting is the mechanism, not the
cost. Now a 50ms bound — six vblanks at 120Hz, so it expires only when something
is genuinely wrong, while still keeping a lost surface from wedging the GS
thread the way an unbounded wait would.

★ The in-game FSR row was gated on renderer == "vulkan", and the default is
"auto".

"auto" resolves to Vulkan on Android, so the row was hidden from anyone who had
not explicitly pinned the renderer — which is nearly everyone, and was the
reporter. Gated on the two backends that genuinely cannot run it instead.
2026-08-16 13:08:05 -04:00
jpolo1224 2f1a74c88a LSFG: force FIFO presentation, and surface FSR in the in-game menu
★ Frame generation produced nothing on a MAILBOX swapchain, silently.

Reported on an Adreno 740: LSFG logged 'active: 1920x1080 x2 frames, 3.1p',
cached its 52 shaders, never logged a single failure — and both the FPS and the
LSFG display counters read 59. The interpolator was working perfectly and its
output was being thrown away.

MAILBOX keeps only the most recent image queued for a given refresh. Presenting
an interpolated frame and then the real frame immediately after replaces the
interpolated one, so it is generated, costs its full GPU time, and is never
displayed. IMMEDIATE discards the same way. Nothing errors anywhere along that
path, which is why the only symptom is a display rate identical to the real one.

The device landed on MAILBOX because vsync was off — 'Immediate not supported
for vsync-disabled, using mailbox'. SelectPresentMode now forces FIFO while
frame generation is enabled. Eden reached the same conclusion; their setting
text reads 'Forces FIFO presentation while enabled'.

Gated on the setting rather than GSLsfg::IsAvailable(), which cannot answer at
swapchain-creation time: the DLL path only reaches GSLsfg from EndPresent.

Also adds the FSR rows to the in-game GraphicsPane. In full settings FSR sits
under Display Effects beside CAS, which is the right shelf for a post-effect and
the wrong one for finding it — it is an upscaler, so in the quick menu it goes
with the internal-resolution controls, which is where you reach while watching
the framerate. Vulkan-only, so it is never a dead toggle on OpenGL.
2026-08-16 12:49:03 -04:00
refractionpcsx2 bc268a7e83 GameDB: Add Prioritize Lower depth fix to Steambot Chronicles
(cherry picked from commit cde2448a6cd0d27f6651c59b43102d27dcb45e48)
2026-08-16 12:46:34 -04:00
refractionpcsx2 34333fe6bd GS/HW: Avoid detecting shuffles as source of truth for target format
(cherry picked from commit ded263f98aa2862b56439ea3c0b552748e4e64f0)
2026-08-16 12:46:34 -04:00
refractionpcsx2 0d558ce69f GS/TC: Improve single pixel overlap detection in RT in RT
(cherry picked from commit ffa569065638312d9c01a47f760130a2109ce7cc)
2026-08-16 12:46:34 -04:00
Ziemas ae4da6a633 EE/IOP: Fix loading elf's through relative path
(cherry picked from commit ca03806a4c86e8fbfc948fcb110f3961be10e5b2)
2026-08-16 12:46:34 -04:00
jpolo1224 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.
2026-08-16 12:11:33 -04:00
pstef 0882a8f109 Revert "GameIndex: record game serials that can benefit from mode 3"
The only purpose of the commit being reverted was to record the list of
games that potentially benefit from mode 3. Revert the revert when
ready.
2026-08-16 17:42:09 +02:00
pstef 46e75bdc94 GameIndex: record game serials that can benefit from mode 3
DJbox                                      2
Ratchet & Clank 2                         13
Tourist Trophy                             6
Monster Hunter                            12
The Taxi 2                                 1
Pride FC                                   4
Jak and Daxter - The Precursor Legacy     14
Jak II                                    10
Jak 3                                      6
Final Fantasy X                           15
Driv3r                                     6
Shadow of the Colossus                     5
Gran Turismo 4                            19
Tales of the Abyss                         4
Disney/Pixar Up                            5
Need for Speed - Undercover                9
Mortal Kombat - Shaolin Monks              3
Tokyo Xtreme Racer Zero                    5
Yu-Gi-Oh! - The Duelists of the Roses      3
Devil May Cry 3 Special Edition            5
Constantine                                3
Sega Ages 2500 Virtual On                  1
Tales of Destiny - Director's Cut          4
Colin McRae Rally 3                        2
Stuntman                                   5
Deadly Strike                              1
Virtua Fighter 4                           4
R:Racing Evolution                         5
Xenosaga Episode III                       7
Steambot Chronicles                       11
Fatal Frame                               10
Playmobil - Hype - The Time Quest          3
2026-08-16 17:41:25 +02:00
jpolo1224 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.
2026-08-16 11:13:54 -04:00
pstef 5598420cdb Tests: pin the COP2 macro MADD/MSUB clamp sets against the interpreter
Exponent-0xFF operands through VMADDw and VMSUB at every dest mask, one
case per half of MADDw's set. MADDx/y/z runs the same shape with its
broadcast lanes in range as a scope control.
2026-08-16 17:02:58 +02:00