Commit Graph

8757 Commits

Author SHA1 Message Date
fcdc2659fc [Audit] bring the audit_61 branch probe onto sylpheed-re
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m38s
Orchestrator / Commit Message Validation (pull_request) Successful in 45s
Orchestrator / Lint (pull_request) Failing after 1m1s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Orchestrator / Windows (x86-64) (pull_request) Has been skipped
Orchestrator / Linux (x86-64) (pull_request) Has been skipped
Orchestrator / Create Release (pull_request) Has been skipped
Fork issue #1: `--audit_61_branch_probe_pcs` existed only on
`phase-a-tracing` and the 2026-07-28 snapshot, so `/sylph-canary` -- which
drives probe runs and parses hit counts -- could not work. Measured, not
assumed: `strings` on both built binaries gives zero hits for the cvar.

MERGE, NOT REBUILD. `phase-a-tracing` is ONE commit past the common ancestor
while `sylpheed-re` is 232 commits ahead of it, so rebuilding from that branch
would have discarded 232 commits including the RE-INPUT/RE-DRAW instrumentation.
Bringing the one commit forward is the cheap direction.

Cherry-picked a15430902 (30d05ee97 does NOT introduce the probe -- it is the
cross-build snapshot). Three conflicts, each resolved on evidence:

  * `kernel/event_log.cc` looked like two rival implementations -- 726 lines
    vs 709 -- but they share 709 lines and differ by 19: ours is a strict
    SUPERSET, phase-a's file plus a later `phase_a_fileio_only` mode that
    suppresses per-export log spam. Took ours whole.
  * `cpu/cpu_flags.{cc,h}`: taken as a verified UNION rather than hand-edited.
    Theirs contributes 11 cvars (audit_61 plus audit_67/68/69/70 and the demo
    marker); ours contributes `phase_a_fileio_only` and
    `kernel_emit_contention`. Asserted afterwards that no flag present on
    either side is missing from the result -- which caught
    `kernel_emit_contention`, that a take-theirs would have dropped silently.

All ten probe cvars are referenced by the cleanly-merged `x64_emitter.cc` and
`ppc_hir_builder.cc`, so they are load-bearing, not decoration. The three
`audit_jit_prolog_*` cvars that look undeclared are DEFINE_uint32 in the
translation unit that uses them -- pre-existing on sylpheed-re, not this merge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 20:43:27 +02:00
Sylpheed RE agent
99cc726626 docs: adopt the full cross-build guide (the tracked one was a stub)
The project root carried a 59 KB version of this guide, untracked, on one
disk. This repository carried 6.8 KB of it. Consolidation Phase 6 in the
Sylpheed repo.

Its audience line predates the decision to retire xenia-rs, so a note at the
top says to read 'audit oracle for the Rust port' as 'instrumented build for
RE'. The instructions themselves are unaffected -- clang-cl, xwin, lld-link
and Wine do not care why you want the binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 21:18:30 +02:00
Sylpheed RE agent
8e63a95425 [RE] file-pad: opt-in Keystroke REPEAT, at the SDL driver's own constants
Some checks failed
Orchestrator / Lint (push) Failing after 1m49s
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
The instrument behind the F1 menu-repeat measurement. It was uncommitted for
a day while the numbers it produced were already shipping in the port.

The file driver deliberately emits exactly one event per press -- repeat is
what makes scripted menu steps overshoot -- so it structurally could not show
whether the GAME repeats a held direction. Measured through it, a held (down)
moved the cursor once and never again, at any hold length. That is a property
of the driver, not of the game, and the page that recorded it said so.

`--pad_file_repeat` opts in, off by default, so no existing script changes.
It reuses the SDL driver's Waiting/Repeating state machine and its constants
VERBATIM (HID_SDL_REPEAT_DELAY=400, HID_SDL_REPEAT_RATE=100, guest-time ms)
rather than re-deriving them -- the point is to be a fair stand-in for a real
controller, and a re-derived constant would only measure our own arithmetic.

With it, the cursor cycled the whole 5-item menu for as long as the button
was held: 19 distinct positions, 12 frames to the first repeat, 4 frames
between the rest, at 29.87 fps guest.

⚠️ The 4-frame interval is NOT the 100ms constant that drives it (100ms is
~3 frames). The game consumes drained keystrokes at its own per-frame pace.
The 400ms delay, by contrast, comes back as 402ms -- that confirms the
instrument, not the game.

docs/re/f1-repeat-measured-via-driver-patch.md in the Sylpheed repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 19:17:17 +02:00
Sylpheed RE agent
ab3203f792 RE: log a CONTENT hash beside every sampled texture
A base address cannot distinguish 'the guest decoded a new frame' from 'the
guest rotated to the next buffer of a triple-buffered set' -- a rotating
buffer visits the same three addresses either way. Reading a clean
one-base-change-per-present as one decode per present is how
docs/re/guest-frame-rate-measured.md reached a conclusion it had to
withdraw.

h=<FNV-1a over 4096 sampled bytes>, omitted rather than faked when the
address does not translate, so a missing hash cannot read as a matching
one. Sampled rather than full: a 1280x720 plane is 900 KB and hashing all
of it per draw would change the thing being measured.
2026-09-01 19:56:12 +00:00
Sylpheed RE agent
d90d14e021 [RE] Log render-target state, resolves and PS constants on every UI draw
The UI draw log recorded blend state, textures and geometry, which is
enough to say WHICH sprite a draw is and how it composites, and not
enough to answer whether a screen has more than one PASS. A second draw
into an off-screen target, or a resolve of the EDRAM into a texture that
a later full-screen quad samples, both look like just another quad in
the old format.

Three additions, all read straight out of the register file:

  mode=       RB_MODECONTROL.edram_mode. kCopy (6) is how this GPU issues
              a RESOLVE, and it arrives through the same DRAW_INDX packet
              as a sprite -- so without this field a resolve was
              indistinguishable from a draw.
  rt0=/pitch  RB_COLOR_INFO's EDRAM tile and format, RB_SURFACE_INFO's
              pitch and MSAA. A second target shows up as a different
              tile; a half-resolution post-process shows up as a pitch
              that is not the screen's.
  RESOLVE     on a kCopy draw, RB_COPY_CONTROL and RB_COPY_DEST_BASE.
              That destination reappearing as a later tex[base=...] is
              what 'resolve-and-resample' means stated in addresses,
              rather than inferred from the picture.
  ps_c[...]   the pixel shader's float constants, taken off its OWN
              float_bitmap -- the same one the backend uploads from -- so
              this is the shader's declared dependency set rather than a
              fixed window that could miss the one that matters. Pixel
              constants live at SHADER_CONSTANT_256_X and the c# printed
              is the index the shader's disassembly uses.

Without the constants, an alpha ramp driven by a shader constant and one
driven by per-vertex colour are the same picture.
2026-09-01 16:13:43 +00:00
Sylpheed RE agent
fa1e4c2214 [RE] Raise the UI draw capture's vertex dump from 8 to 64
8 vertices is TWO QUADS. A batched UI draw carries more: on Project Sylpheed's
EXTRAS screen one 24-index draw holds six sprites, and truncating at 8 reported
the first two while ptframe4, pteff21, pteff22 and pteff23 looked like elements
the game never draws at all. A cap that hides geometry is worse than a long
line, because the missing rows do not announce themselves.
2026-08-31 07:00:16 +00:00
Sylpheed RE agent
0f920e6454 [RE] Log the blend state of every UI draw
CaptureUiDrawForRE now records RB_BLENDCONTROL0, RB_COLORCONTROL and
RB_COLOR_MASK per draw, raw alongside the decoded src/op/dst fields so a decode
bug here cannot quietly become the answer.

The question it answers: the Godot port composites every UI element with
straight alpha-over and four elements come out too dark against the capture,
with the shortfall correlating with the background. Nothing on the disc selects
a per-element mode, so this reads what the GPU was actually told. Result: the
title-side UI uses two states and one pixel shader -- 0x07010701 (src ONE, dst
1-SRC_ALPHA) for backgrounds, text and buttons, and 0x01010101 (src ONE, dst
ONE, ADDITIVE) for the frame sprites and the rotated sweep strips.
2026-08-31 06:09:25 +00:00
Sylpheed RE agent
590912722c [RE] Fix the keystroke-swallow log against upstream's XamState
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m24s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
A SILENT semantic conflict from merging auto/re-kernel-pages-probe: git merged
every file cleanly and the result did not compile. The RE aid added by
043002a87 prints the dialog counters

  xam_dialogs_shown_ / xam_nui_dialogs_shown_

which upstream has since collapsed into a single is_xam_dialog_present_ flag,
reachable only through IsUIActive(). The counts no longer exist, so report the
state that does -- which is what the message is actually about.

Worth noting for the next merge of this fork: a clean `git merge` is not
evidence that instrumentation still matches the API it reads. Build it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 15:48:38 +02:00
Sylpheed RE agent
ff46ec87a4 Merge branch 'auto/re-ui-draw-order' into sylpheed-re
# Conflicts:
#	src/xenia/gpu/command_processor.cc
2026-08-28 15:34:37 +02:00
Sylpheed RE agent
88ce68958e Merge branch 'auto/re-kernel-pages-probe' into sylpheed-re
# Conflicts:
#	src/xenia/apu/audio_system.cc
#	src/xenia/emulator.cc
#	src/xenia/gpu/command_processor.cc
#	src/xenia/kernel/kernel_flags.cc
#	src/xenia/kernel/kernel_flags.h
2026-08-28 15:33:58 +02:00
Sylpheed RE agent
5977400461 [RE] log_stuck_waits v2: count CALLS per second, not just timeout streaks
The v1 streak counter caught nothing during a real freeze: the healthy-run
baseline and not one new (thread, object) pair, while the process burned 1255
ticks per 10 s with two guest threads sitting in KeWaitForSingleObject. That
refuted "loops on timeouts against one object" and left two blind spots, and this
covers both.

A per-thread one-second window counts EVERY call, tracks how many DISTINCT
objects it saw, and logs the last object and the last RESULT when the rate passes
500/s. So a thread rotating over several handles (which resets a same-object
streak) and a thread whose waits SUCCEED rather than time out (which a timeout
counter cannot see) both show up now.

Self-selecting like v1: the healthy poller runs at ~33 calls/s on a 30 ms
timeout, so the 500/s floor stays silent on a good run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-24 07:45:41 +00:00
Sylpheed RE agent
820696c114 [RE] log_stuck_waits: name the object a stuck thread keeps timing out on
Project Sylpheed freezes mid-mission about half the time. Under gdb every one of
the emulator's 79 threads is in a WAIT, yet the process still burns 1253 ticks
per 10 s - 280 of them in guest threads the backtrace shows blocked inside
KeWaitForSingleObject. So they are cycling: a timed wait that expires and is
re-entered on an object nobody signals. Naming that object is the next step.

Logging it the ordinary way is not possible. KeWaitForSingleObject is
kHighFrequency, so it is silent unless --log_high_frequency_kernel_calls=true,
and measured: that flag writes 175 MB and leaves the emulator seventeen minutes
into a boot with the screen still black.

So count CONSECUTIVE timeouts on the SAME object, per thread, in
xeKeWaitForSingleObject, and log at 100 and then every 500. It is self-selecting:
a wait that is being satisfied never builds a streak.

Measured on a healthy 25-minute Stage 02 run: 27 lines, all one thread
(F800004C) polling one Event (guest VA BE56BB5C) with a ~30 ms timeout - a
legitimate poller, and the baseline a frozen run has to be compared against. Not
"silent", as first drafted, but quiet enough to leave on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-24 02:30:50 +00:00
Sylpheed RE agent
c1b57f93bb [RE] threading_posix: fix a lost resume that left CREATE_SUSPENDED threads dead
A thread created suspended publishes its state and its suspend count in TWO
separate lock scopes:

    { lock; state_ = kSuspended; notify_all(); }        // lock released here
    if (create_suspended) { lock; suspend_count_ = 1; wait(count == 0); }

and Resume() does WaitStarted() - which waits only for state_ != kUninitialized -
followed by `if (suspend_count_ == 0) return false;`. So a resumer can slip into
the gap: it sees the thread started, sees suspend_count_ still 0, drops the
resume and returns false. The new thread then sets the count to 1 and waits on it
forever. A textbook lost wakeup.

Measured in Project Sylpheed. Pressing (A) on the title makes the game do
XamUserGetXUID -> NtCreateEvent -> ExCreateThread(entry=821748F0,
CREATE_SUSPENDED) -> NtResumeThread, and the loader thread then never ran: zero
kernel calls of its own (it appeared in the log only as an argument) and 00:00:00
host CPU time, while the emulator sat at 546% CPU. Boots reached the main menu
1 time in 6.

Fixed by publishing state_ and suspend_count_ under one lock and waiting without
releasing it, so a resumer past WaitStarted() always observes 1.

On the first clean boot after the fix the same loader thread is the CALLER on 20
kernel-call lines and issues 4 ResolvePath asset reads. Every failed boot before
it had exactly zero of both.

Also logs when the host resume is refused. XThread::Resume's Linux path
discarded that bool - the Windows path turns it into X_STATUS_UNSUCCESSFUL - so a
dropped resume was invisible from both sides. Note the log is not by itself a
defect: resuming a thread that is not suspended legitimately returns false, and
it fires ~7 times in a normal boot.

(cherry picked from commit a60fe7d11c)
2026-08-19 18:24:44 +00:00
91200ca993 [HID] file-pad: implement GetKeystroke -- menus do not read GetState
The pad looked correct and did nothing. Its own log showed A arriving, the
emulator sat on "PRESS (A) BUTTON", and the title never advanced.

Cause: 360 front-ends poll XamInputGetKeystrokeEx, not XamInputGetState. This
title imports both, and its menus use the keystroke path; the driver returned
X_ERROR_EMPTY there, so every scripted press went into the void while GetState
faithfully reported a button nobody asked about.

Implement it edge-triggered, one event per call: KEYUPs for everything released
first, then KEYDOWNs, matching the SDL driver's ordering (so a thumb transition
clears before it sets). Deliberately NO auto-repeat -- scripted input wants
exactly one event per press, and repeat is precisely what makes menu steps
overshoot. Bits without a virtual key (guide, unused) are swallowed rather than
re-offered forever.

Verified on the real game: title -> main menu -> EXTRAS driven entirely from the
pad file, with `[file-pad] keystroke vk=5800 down/up` in the log for each press.

(cherry picked from commit 15fe11d5d9)
2026-08-19 18:07:57 +00:00
47209a1cbd [HID] file-pad: nanosecond change detection, and log every state change
Two things that only show up once you actually script this pad.

st_mtime is whole seconds. Combined with size it looked like enough and is not:
a script stepping a menu writes several same-length states per second
(`press=A` then `press=B`, both 8 bytes), and every one after the first was
silently dropped -- the emulator simply did not react, with nothing in any log
to say why. Compare st_mtim.tv_nsec as well, and track whether the file existed
at all so a delete is registered once rather than every frame.

Also log one line per state change (not per frame, so it stays quiet). Driving
the emulator headless means there is nothing to watch; this line is the only
proof that a scripted press was picked up, which turns "did my input land?" from
a guess into a grep.

(cherry picked from commit e3e17e4951)
2026-08-19 18:07:57 +00:00
cbfad1ab36 [HID] file-pad: a controller driven by a text file, for scripted RE
The scripted-input tool this project uses for reverse engineering created its
pad through /dev/uinput. Input devices are NOT namespaced by the kernel, so a
uinput device created inside a container registers with the HOST's input stack:
every trigger hold and button press is delivered to whatever on the host reads
gamepads, not just to the emulator. That was noticed the hard way, and it made
every runtime experiment -- booting, menu navigation, unit harvesting, flight
measurement -- unusable from inside the box.

This driver takes the kernel out of the loop. Pad state lives in an ordinary
text file only the container can see; GetState re-reads it when it changes.
Nothing is registered with the host and no X server is involved. A bonus for RE:
analogue values are exact rather than whatever a virtual stick quantises to.

    press=A,START     buttons by name, comma separated
    buttons=0x1010    or the raw XINPUT mask
    lt=0 rt=255       triggers, 0..255
    lx=0 ly=0         thumbs, -32768..32767

Absent keys are neutral, so `press=A` alone is a valid file, and a missing or
empty file means no input -- the safe default if it is deleted mid-run.

Selected with --hid=file, path from --pad_file (default /tmp/xenia_pad.txt).
Deliberately NOT part of "any": this pad has to be asked for. Header-only, so it
adds no build target and no cost to anyone not using it.

(cherry picked from commit d15c8cfab6)
2026-08-19 18:07:57 +00:00
Sylpheed RE agent
a60fe7d11c [RE] threading_posix: fix a lost resume that left CREATE_SUSPENDED threads dead
A thread created suspended publishes its state and its suspend count in TWO
separate lock scopes:

    { lock; state_ = kSuspended; notify_all(); }        // lock released here
    if (create_suspended) { lock; suspend_count_ = 1; wait(count == 0); }

and Resume() does WaitStarted() - which waits only for state_ != kUninitialized -
followed by `if (suspend_count_ == 0) return false;`. So a resumer can slip into
the gap: it sees the thread started, sees suspend_count_ still 0, drops the
resume and returns false. The new thread then sets the count to 1 and waits on it
forever. A textbook lost wakeup.

Measured in Project Sylpheed. Pressing (A) on the title makes the game do
XamUserGetXUID -> NtCreateEvent -> ExCreateThread(entry=821748F0,
CREATE_SUSPENDED) -> NtResumeThread, and the loader thread then never ran: zero
kernel calls of its own (it appeared in the log only as an argument) and 00:00:00
host CPU time, while the emulator sat at 546% CPU. Boots reached the main menu
1 time in 6.

Fixed by publishing state_ and suspend_count_ under one lock and waiting without
releasing it, so a resumer past WaitStarted() always observes 1.

On the first clean boot after the fix the same loader thread is the CALLER on 20
kernel-call lines and issues 4 ResolvePath asset reads. Every failed boot before
it had exactly zero of both.

Also logs when the host resume is refused. XThread::Resume's Linux path
discarded that bool - the Windows path turns it into X_STATUS_UNSUCCESSFUL - so a
dropped resume was invisible from both sides. Note the log is not by itself a
defect: resuming a thread that is not suspended legitimately returns false, and
it fires ~7 times in a normal boot.
2026-08-19 10:41:05 +00:00
Sylpheed RE agent
8d3299975e [RE] xam_input: test X_ERROR_SUCCESS exactly, not XSUCCEEDED
X_ERROR_EMPTY is 0x10D2 and XSUCCEEDED is ((s & 0xC0000000) == 0), so an empty
keystroke poll passes it. The first run of this instrumentation therefore logged
every empty poll: 6499 lines of vk=0000 burying the two that mattered.

The pre-existing assignment above shares the quirk - it writes user_index_ptr on
an empty poll too. Left alone: it is upstream's and harmless, since the struct
is zeroed first.

The measurement itself survives, and is worth stating: at the title screen the
game polls constantly and receives EXACTLY the two events sent - vk=5800
flags=0001 (KEYDOWN) and flags=0002 (KEYUP). Input delivery is not the problem.
2026-08-19 08:43:43 +00:00
Sylpheed RE agent
f9170fd3fe [RE] xam_input: log the keystroke path that REACHES the driver
The existing [RE-INPUT] line only fires when IsUIActive() swallows a keystroke,
so its silence is ambiguous: it means either 'the game is polling and getting
nothing' or 'the game is not polling at all'. That is exactly the fork the
title-screen investigation is stuck on - with the sign-in dialog fixed the
swallow count is 0 and the title still does not respond to (A).

Adds two logs on the other side: a rate-limited count of calls that get past
IsUIActive to a driver, and one line per keystroke actually handed to the guest
(rare by construction - one per physical press - so every one is logged, with
user index, virtual key and flags).
2026-08-19 08:35:26 +00:00
Sylpheed RE agent
53b208bf4d [RE] log_ui_draws: log the UI quad's colour attribute
A UI quad carries a k_8_8_8_8 colour next to its position, and its alpha is the
fade the bundle's keyframes animate. Logging it costs one more read per vertex
and turns the capture from "where is this quad" into "where is it and how faded".

It was added to separate two elements that decode to the same 1133x280 and sit on
opposite sides of a third in the declaration table. It does NOT separate them —
both rest at 255 — but it does check the fade decode against the running game for
the first time: every static element draws at exactly the resting alpha the
bundle predicts, and the only two quads whose alpha changes between consecutive
frames are the rotating effect pair and the PRESS (A) glow, which are the two
things visibly animating.
2026-08-19 01:09:04 +00:00
Sylpheed RE agent
4cd019b769 [RE] Arm the UI draw capture on F10 unconditionally
The capture used to require --log_ui_draws at LAUNCH, which put an unexplained
variable into every navigation run: across 12 runs, launching with the flag
correlates with the title screen refusing (A) — 0 of 7 with it, 4 of 5 without.

No mechanism was found. The cvar is read in exactly one place, when F10 arms a
capture, and F10 was never pressed in those runs; the per-draw hook is a single
relaxed atomic load; the startup config dumps of the two arms are byte-identical.
An interleaved A/B also ruled out the obvious confound (boot duration): the
latest title of all, 268 s, ACCEPTED (A), while a 232 s title refused.

So rather than keep a variable nobody can explain in the path of every run, arm
on F10 the way the ship capture next to it already does. The cvar stays, marked
obsolete, so existing command lines still parse. Verified: F10 with no capture
flags at all writes xenia_re_ui_draws_01.log.
2026-08-19 00:36:34 +00:00
Sylpheed RE agent
043002a871 [RE] Say so when a guest keystroke is swallowed by IsUIActive
XamInputGetKeystrokeEx returns X_ERROR_SUCCESS with a zeroed keystroke whenever
any Xenia dialog is up, before consulting a driver. That is correct behaviour and
an invisible one: from outside, scripted input simply stops working, the pad
driver logs nothing because it is never asked, and the guest keeps polling. It is
easy to enter that state by accident — F10 is both the RE capture hotkey and the
toolkit's menu-bar key, and a dialog closed by its own [x] rather than by the
menu toggle leaves the counter incremented.

So log it, rate-limited to one line per 600 swallowed calls, with the dialog
counters. On the runs this was written to diagnose it stays silent, which is what
ruled the theory out — a diagnostic that is useful when it does not fire.
2026-08-18 22:18:38 +00:00
Sylpheed RE agent
7dbb24e64f [RE] --create_profile_if_none: bootstrap a profile for scripted runs
The title screen's (A) opens the Sign In dialog when no profile exists, and the
game goes no further. That dialog cannot be completed from a scripted container:
"Create Profile" asks for a gamertag in an ImGui text field, and synthetic X key
events do not reach it — tried with the window focused, via XTEST and via
--window, char by char, after clicking the field. Mouse clicks work; text entry
does not.

So: when no profile exists on disk and this cvar is set, create one at startup
and sign it in. It uses ProfileManager's fixed `default_xuid`
(B13EBABEBABEBABE), which is what makes it usable from a script — a later run
passes --logged_profile_slot_0_xuid=B13EBABEBABEBABE and is deterministic.
Ignored when a profile already exists, so it is safe to leave in a launcher.

Verified: "RE bootstrap profile 'SylphRE' -> created", then on the next boot
"Found 1 Profiles" / "Loaded SylphRE (GUID: B13EBABEBABEBABE) to slot 0".

This does NOT get the game past the title — see the Reborn repo's
docs/re/canary-scripted-input-traps.md for what does and does not, and for the
content-path crash that is the actual blocker.
2026-08-18 20:15:35 +00:00
Sylpheed RE agent
8cca105eb9 [RE] log_ui_draws: bound the capture by cvar, and log the quad's Z
Three changes, each paid for by a measurement that could not be made without it:

* `--ui_draw_capture_frames` / `--ui_draw_capture_max` replace the compiled-in
  3-frame, 20k-draw bounds. A screen that redraws every frame needs 3; finding
  the frames in which a screen is BUILT needs hundreds, and that answer (the
  title screen never rebuilds — it submits the same 11 draws every frame) is
  only reachable by turning the window up.

* the vertex dump prints all three floats. Attribute 0 of a UI quad is
  k_32_32_32_FLOAT, so the stream carries a Z — the game's own layer key, if it
  had one. It does not: every Z is 0.00000, which is what makes submission order
  the whole of the paint order.

* positions print as floats rather than raw words, now that the format is known.
2026-08-18 20:15:35 +00:00
Sylpheed RE agent
2263470723 [RE] log_ui_draws: a per-draw, submission-order capture for 2D screens
Neither existing RE hook can say in what order the game paints a UI screen:
`log_draws` de-dups by vertex-declaration fingerprint, so a screen's sprites —
which share a declaration — collapse into one record; and the F10 ship capture
returns early on any draw without an f32x3 position stream, which is every UI
quad.

So: `--log_ui_draws` arms (on F10, alongside the ship capture) a snapshot of the
next `--ui_draw_capture_frames` submitted frames, undeduplicated, writing every
draw with its primitive type, index count, VS/PS hashes, each bound texture's
base and dimensions, and the quad's vertex positions. The positions are the part
that matters: a screen's sprites all share one shader and sample big texture
pages, so the geometry is what names an element.

Frames are counted here rather than from `counter_`. `counter_` looks like a
frame number — the VdSwap packet increments it — but MarkVblank() increments it
too, from the vblank thread, so it advances between two draws of the SAME frame.
Bounded by it, the first capture ended after one draw having "covered 6 frames".

Measured on the title screen: 11 draws a frame, every frame.
2026-08-18 18:57:18 +00:00
Gliniak
a5a18f5c75 [Misc] Random optimizations.
- Seems like it improves performance in debug, not sure about release

- Stuff suggested by AI
2026-08-18 11:43:52 +02:00
Clippy95
4cc584f47d [D3D12] Fix constant-alpha blending in the RTV path 2026-08-18 09:16:57 +02:00
goldislead
7cd47947b0 [GPU] Select promoted tfetch1D layouts at runtime
This keeps 545407D4's 2D use working while fixing wide 1D bindings in 555308CE.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
95545f8e76 [D3D12] Fix over-invalidation of bulk fetch register writes
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
197929d967 [D3D12] Fall back to point sampling for non-filterable formats
Matches Vulkan.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
61a8aa3607 [Vulkan] Add spirv_disable_rounding_mode_rte
RenderDoc's shader debugger doesn't support RoundingModeRTE and crashes.

Co-Authored-By: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
31168682b0 [Vulkan] Fix draw_resolution_scaled_texture_offsets cancelling itself out
The offset was being multiplied by the scale and divided by the host scaled size, which collapsed back to guest step, so the cvar changed nothing on Vulkan while D3D12 stepped host texels. Since the size is already in host texels, dividing the offset by it gives the proper step.

Unnormalized coordinates convert to host texels before the offset add so the offset isn't multiplied along with the coordinate. Folds in the fix for 5841095A.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
3eab2b8b39 [GPU] Handle tessellated triangle strip and fan draws
Strips and fans normalize to triangle list host draws instead of being rejected. The conversion buffer is built at runtime when the backend did not bake one at init.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
Co-authored-by: Reality <reality@xenios.jp>
2026-08-18 08:32:23 +02:00
goldislead
77597d62ae [Vulkan] Select back-face stencil ref/mask when culling only front faces
They were picked whenever front faces were culled, even with both faces culled. Matches D3D12.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
53061c63fe [Vulkan] Use dedicated alpha blend factor map for alpha srcb/dest blend
Matches D3D12.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
2d5b41080c [Vulkan] Clamp stacked-texture layer index for Inf/NaN coords
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
9e9d3cdd3f [GPU] Clamp out-of-bounds vertex fetch words to 0
Words at or past the size in the fetch constant read as 0 now instead of whatever sits in shared memory, matching real hardware.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
e6bdb0fdf7 [Vulkan] Skip draws with surface_pitch==0 to match D3D12
Realistically only affects memexport draws.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
654a8cacf5 [Vulkan] Fix FSI 2x-as-4x sample mask reading host sample 2 instead of 3
Guest sample 1 lives in host sample 3 when 2x-as-4x. This might have originally just been a typo or oversight.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
0d38781760 [Vulkan] Apply float controls to geometry shaders to match VS/PS
Mainly for NaN preservation. GS discards primitives by checking positions for NaN, mostly for vertex kill, and without the controls drivers might fold those checks away.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
f25003c0ea [GPU] Load guest-resolved mips from scaled resolve memory
Mips were always regenerated by blitting down from base, even when guest resolved real data into scaled memory.

The upload footprint is the guest mip reduced then scaled while the subresource is the base scaled then reduced, so the deepest mips of scaled textures can disagree by a row or column per axis. Compressed formats round up to the block and absorb it, uncompressed copies now clamp per axis so they never overrun the host image.

Matches D3D12 fix for the same problem.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
7101021150 [Vulkan] Bind shared memory persistently for texture loads and resolves
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
cb240560df [Vulkan] Fix 2x MSAA alpha-to-coverage sample layout
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
4aeb518c9c [Vulkan] Clamp scalar maxas/maxasf address register to [0, 255]
Matches DXBC.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
3ff230d23b [GPU] Emulate extended-range float16 in render target pack/unpack
Exp 31 is a large finite value up to 131,008 on the guest, not Inf or NaN.
Packing used to clamp colors at 65504 and unpacking returned Inf for extended encodings.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
2b071d9b09 [Vulkan] Invalidate user clip plane constants on source registers change
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
66779fb873 [Vulkan] Fix adaptive tessellation issues
The domain shader reads the patch index from the hull shader output instead of gl_PrimitiveID, which bypassed the endian swap, offset, wrap and clamp already applied upstream.

The tessellator winds clockwise now. Clip space Y is not flipped on Vulkan, so counterclockwise winding inverted the facing and guest backface culling removed whole surfaces.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
goldislead
aed81ca93a [Memory] Rework guest access resolution and unwatched invalidation
This is a combination of three different Edge commits.

Guest page access is resolved with system page granularity so that anything deciding on protection now takes permissive access of every guest page a system page covers, which matters when the host is larger than the guest.

Access violations: Write faults on pages with no watch armed are only reported handled if guest mapping allows the write.

Invalidation of unwatched ranges when made writable or freed: Decommit, Release and Protect-to-writable (including write-combine) raise invalidation callbacks even with no watch armed.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
2026-08-18 08:32:23 +02:00
Adrian
59c08cd462 [Kernel] Replaced X_DISPATCHER_FLAGS with X_OBJECT_TYPES 2026-08-18 08:00:29 +02:00
The-Little-Wolf
cdd60494d7 [XEX] - Add support for dash 1746
- Add support for dash 1746
2026-08-18 07:26:06 +02:00