Commit Graph

8758 Commits

Author SHA1 Message Date
goldislead
8486e97a06 [GPU] Sample locked mip unnormalized fetches in the mip's grid
Fixes visibility popping in 555308B6 and 5553080B.

Unnormalized texture coordinates address texels of the mip level being sampled, not always the base level. The titles in question lock the fetch constant to one mip and address that mip's grid. The denominator was still the base level size, so each reduction after the first read garbage.

So the locked size is now used for 2D fetches (point filter, clamp-to-edge) with MipMinLevel == MipMaxLevel. Everything else keeps the base level denominator until a shared effective LOD model exists.
2026-08-02 20:53:25 +02:00
The-Little-Wolf
364dc1d770 [XBOXKRNL/IO] - add structs for NtDeviceIoControlFile
- Add structs for NtDeviceIoControlFile
2026-08-02 19:12:54 +02:00
goldislead
da47dfaacf [GPU/DXBC] Fix signed round bias breaking memexport
Write the bias into round_bias_temp as intended in both packed 8/16/32bpp path and 16_16_16_16.

The copysign rounding bias was targeted the eM register, and destroying the scaled value, then added round_bias_temp, which nothing had written.
2026-08-02 18:05:24 +02:00
Gliniak
7010c86fb1 Revert "[Kernel] Initial XMP Notifications"
This reverts commit 0c6dc9b628.
2026-07-30 20:02:22 +02:00
Adrian
e4f28f6984 [Kernel] Fixed object type 2026-07-30 16:55:27 +02:00
The-Little-Wolf
2585d3cd69 [XAM/NET] - Stub XampXAuth functions
- Stub XampXAuthStartup, XampXAuthShutdown, XampXAuthGetTitleBuffer
2026-07-30 09:52:59 +02:00
Adrian
1807da06db [Kernel] Added usage of X_DISPATCHER_FLAGS 2026-07-30 08:54:23 +02:00
bomabomabomaboma
2ddc5ef737 [GPU] 8_8_8_8_GAMMA resolve update; fix unsigned-biased fetch scaling
8_8_8_8_GAMMA resolve update:
This hopefully settles how safe resolves of gamma RTs are handled, because it's almost certainly not settled by destination.

It's now believed that gamma sources seen by resolve are always being decoded, and that's a decode that's keyed only on the source format, where every destination is fed linear values, and any encode is now just removed rather than conditional.

These sources are re-alised as plain before resolving, which is the same exact idea that preserves float 2_10_10_10 bits through a resolve too.

Integer texture fetch scaling update:
Unsigned-biased components were using the regular unsigned scale even though the conversion had remapped the sample [0, 1] to [-1, 1].

Scale bits data is now increased from 5 to 6 bits and use the new bit to mark unsigned-biased components. DXBC/SPIR-V use half of the unsigned scale and add a -0.5 offset.

This fixes post-processing in 415608B2 and will likely improve other titles w/ EDRAM transports through affected textures, but perhaps not so dramatically.
2026-07-30 07:34:28 +02:00
Adrian
888b9ad258 [Kernel] Added cvar for loading XDBM
XDBM triggers anti-cheat in BO2.
2026-07-27 21:44:26 +02:00
Adrian
0c6dc9b628 [Kernel] Initial XMP Notifications 2026-07-27 21:03:16 +02:00
MechaCat02
a08526dff0 [GPU] ship-capture: numbered per-press snapshots + capture every instance
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m45s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
F10 now writes xenia_ship_capture_NN.log (NN = press number) so several angles
can be captured in one run without overwriting, and de-dups by (vertex-buffer
address, c0..c2 WVP hash) instead of address alone -- the same part buffer
drawn at different transforms (two engine nacelles, each ship of a fleet) now
yields one record per distinct placement. Seen-set cap raised to 8192.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 20:35:20 +02:00
MechaCat02
93fe5d69df [GPU] ship-capture: also dump VS float constants (the placement matrix)
The captured vertex BUFFER holds LOCAL positions (byte-identical to the .xpr) —
capital-ship parts are placed entirely in the vertex shader, not in the buffer.
So the per-part world/WVP matrix lives in the VS float constants. Extend the F10
capture to dump the first 48 vec4 from the SQ_VS_CONST base per draw; diffing two
parts isolates the world matrix (the camera VP block is shared across draws).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:35:20 +02:00
MechaCat02
067a373734 [GPU] F10 ship-placement capture: dump world-space draw vertices for RE
One-shot snapshot for reversing capital-ship part placement. F10 arms
RequestShipCaptureFrame(); the next batch of draws is dumped to
xenia_ship_capture.log with each draw's guest vertex-buffer address, vertex/
index counts, and up to 64 WORLD-space vertex positions. Capital-ship parts are
transformed into world space before the draw (unlike stage geometry, which
matches the .xpr byte-for-byte), so these are ground truth for the assembled
placement; a reborn-side correlator affine-fits each decoded .xpr part to a
captured draw to recover its exact transform. De-duped by buffer address, gated
independently of the log_draws cvar, budget-capped at 8000 draws / 4096 buffers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:35:20 +02:00
The-Little-Wolf
7d8be7f17e [XEX] - Add missing xex2_system_flags_32 flags
-  Add missing xex2_system_flags_32 flags
2026-07-27 10:45:20 +02:00
MechaCat02
877163792b [GPU] ship-capture: numbered per-press snapshots + capture every instance
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m10s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
F10 now writes xenia_ship_capture_NN.log (NN = press number) so several angles
can be captured in one run without overwriting, and de-dups by (vertex-buffer
address, c0..c2 WVP hash) instead of address alone -- the same part buffer
drawn at different transforms (two engine nacelles, each ship of a fleet) now
yields one record per distinct placement. Seen-set cap raised to 8192.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:15:43 +02:00
goldislead
8f55b4abf7 [GPU] Store Vulkan gamma RTs as linear UNORM16
Roughly matching D3D12, R16G16B16A16_UNORM is used for gamma RTs when sampled and blendable color attachments are supported. The host target is kept linear for blending, and converted during ownership transfers, clears, and readbacks.

Shares the PWL conversion helpers between the translator and RT cache. Raw gamma bytes are decoded to safe linear midpoints so round trips preserve the original bytes.
2026-07-24 21:46:46 +02:00
MechaCat02
3ba56a5005 [GPU] ship-capture: also dump VS float constants (the placement matrix)
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m51s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
The captured vertex BUFFER holds LOCAL positions (byte-identical to the .xpr) —
capital-ship parts are placed entirely in the vertex shader, not in the buffer.
So the per-part world/WVP matrix lives in the VS float constants. Extend the F10
capture to dump the first 48 vec4 from the SQ_VS_CONST base per draw; diffing two
parts isolates the world matrix (the camera VP block is shared across draws).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:18:40 +02:00
MechaCat02
87d02d59f1 [GPU] F10 ship-placement capture: dump world-space draw vertices for RE
One-shot snapshot for reversing capital-ship part placement. F10 arms
RequestShipCaptureFrame(); the next batch of draws is dumped to
xenia_ship_capture.log with each draw's guest vertex-buffer address, vertex/
index counts, and up to 64 WORLD-space vertex positions. Capital-ship parts are
transformed into world space before the draw (unlike stage geometry, which
matches the .xpr byte-for-byte), so these are ground truth for the assembled
placement; a reborn-side correlator affine-fits each decoded .xpr part to a
captured draw to recover its exact transform. De-duped by buffer address, gated
independently of the log_draws cvar, budget-capped at 8000 draws / 4096 buffers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 09:49:45 +02:00
The-Little-Wolf
6e9bac0dff [XAM/INFO] - Stub XamXStudioRequest
- Stub XamXStudioRequest to stop log spam
2026-07-24 08:53:26 +02:00
Herman S
ed4540a641 [XAM] Remove XMP polling throttle sleeps
Fixes audio in Tekken Tag 2 and Def Jam Icon. Improves framerates in
Radiant Silvergun and Split/Second.
2026-07-23 16:58:42 +02:00
Gliniak
913ec6b9b2 [XAM] Limit guest UI to only one instance
This fixes potential crashing in games that spam UI show calls
2026-07-23 16:49:33 +02:00
goldislead
74db632ab3 [GPU] draw_resolution_scale_threshold for host render targets
Implements resolution scale selection, per surface, for both backends.

Surfaces with pitches at or below the defined threshold render at 1x1 while everything else scales. This catches downscaled post-process chains that gain nothing from upscaling and often break from it. 0 leaves everything scaled.

Class is a function of pitch and MSAA samples, baked into RenderTargetKey, and the caches create, clear, transfer, and dump per that class. Transfer shaders carry separate dest and source scales. Pixel shaders get a modification bit so 1x1 draws translate with 1x addressing for param_gen and memexport, and per-draw viewports, point sizes, and divisors pull from RT cache instead of draw_resolution_scale_x/y.

1x1 resolves copy unscaled data straight into shared memory and unmark the range from scaled resolve page tracking, so textures pick up the correct copy. Readback resolves skip downscaling when appropriate. ZPD normalizes per segment so mixed reports don't undercount.

Host RT only. ROV/FSI EDRAM layout assumes a single global scale and, while implementing support for scale selection is probably doable, it's likely an enormous task.
2026-07-23 14:18:03 +02:00
Adrian
536900e76b [XAM] Cleanup XamGetCachedTitleName 2026-07-23 13:30:51 +02:00
MechaCat02
201553aea3 Instrument: file.read offset tracer + XMA-PARAM probe (voice RE)
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m52s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Additive, cvar-gated instrumentation used to reverse-engineer Project
Sylpheed's cutscene-voice storage (movie -> continuous sound-stream cue
region). Default-off; no behaviour change.

- event_log.cc: MaybeEmitFileRead emits `file.read` events with the real
  NtReadFile ByteOffset (from r10), length, handle->path, and buffer VA, so a
  sound.pNN read offset can be mapped to a sound.pak TOC entry.
  Gate: --phase_a_fileio_only=true --phase_a_event_log_path=<file>.
- cpu_flags.cc: phase_a_fileio_only cvar (+ xma_param_probe).
- xma_context_new.cc: XMA-PARAM probe in XmaContextNew::Decode (the decoder the
  title actually uses) logging ctx/buffer/read-offset/channels/packets/byte_size
  /signature — to confirm which bytes get decoded for a given cutscene.

Used to produce /tmp/rt_fileio.jsonl (RT01A playthrough), which cracked the
movie->voice mapping now implemented in sylpheed-reborn. Next: run the same
file.read trace on an unbound hokyu (e.g. hokyu_LS_s03A) to resolve which
VOICE_D the game streams for the 13 manifest-unbound resupply cutscenes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 06:56:45 +02:00
MechaCat02
ea9f037b82 [GPU,Kernel] RE instrumentation: per-buffer draw log + file-I/O guest map
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m35s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Draw logger (command_processor.cc): also key each record on the index-buffer
guest base so distinct static index buffers (ship body vs hangar vs weapons) no
longer collapse to one record; dump the first decoded index VALUES + raw index
bytes (8-in-16/32 endian-corrected) and the stream-base hex, to pin an index
buffer's exact .xpr file offset and validate a mesh decode against ground truth.

File-I/O log (xboxkrnl_io.cc): --log_file_io cvar writes each distinct NtReadFile
as 'READ <name> off=.. len=.. -> guest=..' to xenia_re_files.log, giving the
file->guest-memory mapping. Cross-referenced with the draw log's buffer guest
addresses this resolves which .xpr (and where inside it) a mesh loaded from.
De-duped by (offset, guest), capped at 65536 entries.

Also ignore build-cross/ (Wine cross-build output).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 20:19:12 +02:00
goldislead
a635ac64f5 [GPU] Scaled resolve readback through downscale CS
Implements readback for resolution-scaled resolves in both D3D12 & Vulkan backends, mostly mirroring Xenia Edge's approach, with a few small adjustments.

The scaled resolve buffer stores each texel as a group aligned scale_x by scale_y block. A small CS now reverses that packing per 32x32 tile and writes 1x. So far, this is Edge's approach verbatim. This slightly diverges from Edge since now the source window is bound at the written extent, not the texture base & the tail after the last whole tile is no longer copied.

Dest format and texel size are the same ResolveInfo the written extent was calculated with rather than rereading RB_COPY_DEST_INFO, which probably matters for depth resolves where the register holds k_8. 128bpp dests, extents that aren't group-aligned, and extents outside the scaled range skip readback.
2026-07-19 10:13:00 +02:00
Adrian
23b79f2a97 [XAM] Removed returning error in XamUserCreateAchievementEnumerator 2026-07-18 19:24:08 +02:00
MechaCat02
29efc0381a [GPU] log_draws: also dump vertex positions for file correlation
Extend the RE draw-logger to dump the first few vertex POSITIONS (read
from guest memory) under each draw. The f32 position bytes are identical
between the guest buffer and the on-disc .xpr (only f16 pairs are
rearranged on load), so these values can be grep'd for in a resource file
to locate a mesh whose in-file offset is otherwise unknown.

Validated: world-space stage geometry byte-matches Stage_*.xpr at exact
offsets. (Load-time-transformed meshes like the player ship don't match,
which is itself a useful finding.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 14:28:47 +02:00
MechaCat02
c6caa9e9c5 [GPU] Add log_draws: dump per-draw vertex declaration for RE
Reverse-engineering aid for decoding game mesh formats against GPU ground
truth. When the `log_draws` cvar is set, each distinct draw's primitive
type, index buffer (guest base / count / format / endianness), and full
per-stream vertex declaration (fetch-constant base + stride, and every
element's format + offset) is written to xenia_re_draws.log.

- command_processor.{h,cc}: CommandProcessor::LogDrawForRE(), no-op unless
  the cvar is set. De-dups by the vertex-declaration fingerprint (shader +
  primitive + element formats/offsets), so animated UI that redraws the
  same format into fresh buffers every frame collapses to one record --
  keeping it near-free (an earlier address-keyed de-dup flooded the log and
  stalled the GPU thread). Capped at 4096 distinct formats.
- pm4_command_processor_implement.h: call it from ExecutePacketType3Draw
  after IssueDraw (so the vertex shader has been analyzed). Backend-agnostic
  base path -- works for the Vulkan build.

Used to confirm Project Sylpheed's XBG7 mesh layout (triangle list, pos
f32x3 / normal f16x4 / uv f16x2, variable stride).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 14:28:47 +02:00
MechaCat02
3c8faf03f7 [Fix] threading_posix: reap thread handle once (mission-end freeze)
post_execution() runs on every successful Wait() and a Thread handle stays
signaled forever after exit, so multiple guest waits on the same exited handle
each call pthread_join() on an already-reaped pthread_t. On glibc that hangs
(recycled tid) -> the black-screen freeze at mission teardown. Guard reap with
an atomic exchange so it happens exactly once. Isolated from the audio/crash
WIP on phase-a-args-fileread; only this hunk, on stock 16e1eb8e2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 22:05:49 +02:00
goldislead
16e1eb8e28 [GPU] Clarify 64bpp clear register order
This resolves the TODO in GetColorClearShaderConstants about which 32 bit portion is in which register.

For 64bpp formats, RB_COLOR_CLEAR_LO holds the lower 32 bits of the packed clear value, and RB_COLOR_CLEAR holds the upper 32 bits.
2026-07-17 08:18:56 +02:00
MechaCat02
7b6902e08f [WIP] Audio/threading fixes + crash investigation; NEW ORACLE: crash is ours not the game
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m34s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Snapshot for handoff. Contains the mission-audio + threading fixes and the
crash-investigation instrumentation (all diagnostic cvars default-OFF).

Fixes (behavioral):
- threading_posix.cc: reap-once guard on PosixCondition<Thread>::post_execution
  (double pthread_join at mission teardown -> fault loop -> audio death + freeze).
- xma_decoder.cc: work_event_->Set() in Pause() so the idle XMA worker observes
  paused_ and signals pause_fence_ (Pause() deadlock -> permanent audio death).
- audio_system / xma_context_master / xboxkrnl_audio / apu_flags / alsa: mission
  audio keepalive + guest_audio_flags + watchdogs.

Instrumentation (additive, default-off): xboxkrnl_debug cache-throw diag +
guest-catch dispatcher, xex_module PE/PDATA/EH scans, kernel_state mem_watch
(NOTE: mem_watch DEFAULTS TRUE -- an always-on host poll thread; prime crash suspect).

NEW ORACLE (see HANDOFF-crash-oracle-2026-07-16.md): stock 6e5b8324f built with
our toolchain + zero custom code = NO crash, NO sound-stop, plays the Ready Room.
=> the Ready-Room out_of_range crash is introduced by THESE changes, not the game
and not the (LTO-broken) build chain. Bisection plan + suspect ranking in the note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:43:50 +02:00
NicknineTheEagle
c6298dd0e3 [XAM] Implemented game exit behavior in XamLoaderTerminateTitle 2026-07-16 11:48:30 +02:00
Gliniak
44e1a64f35 [XAM] Extend UI delay on close. Added delay to headless after notification sent.
Created thread to do async notification broadcast on close for headless.

This fixes game freeze in 534407FC
2026-07-16 11:17:16 +02:00
Gliniak
c2650c6e58 [Testing] Define full test paths, unify test results of vectors to unsigned type 2026-07-16 10:47:15 +02:00
Gliniak
7ff152a5a7 [X64] MAX_V128/MIN_V128 Proper NaN handling.
According to console. FFC00000->FFC00000 instead of FFFFFC00 as it was previously.

Test case: 415607E8 - "Evil Queen's Castle" level
2026-07-15 23:31:38 +02:00
The-Little-Wolf
51322122a5 [Xam/Info] - Implement XamFeatureEnabled
- Implement XamFeatureEnabled
2026-07-15 19:55:04 +02:00
Gliniak
deaf21e9e1 [CI] Fixed CI failure caused by incorrect variable check 2026-07-15 19:53:27 +02:00
The-Little-Wolf
2799234fbb [Xam/XMP] - Stub XMPGetNumSongsInTitlePlaylist
- Stub XMPGetNumSongsInTitlePlaylist
2026-07-15 09:18:46 +02:00
SaveEditors
e20f26963f [GPU] Initialize GPU registers to hardware reset defaults
RegisterFile zeroes the whole register file at construction, but a real
console comes up with non-zero values in several of the context
registers. If a game reads one of those before writing it, it gets 0
under emulation and the real default on hardware.

I read the reset values off a retail console (read-only) and checked
them against the AMD R6xx register reference and the r600g defaults in
Mesa, which set the same registers to the same values at context init.
Where the hardware read matches the driver default it's a genuine
power-on default, not leftover runtime state.

Left the tessellation levels out on purpose: they reset to 1.0f too, but
the backends do register + 1.0f, so seeding them here would change the
effective reset factor. That needs its own change.
2026-07-14 21:07:49 +02:00
Margen67
bad346717a [xb] Skip aarch64 submodule for x64 target 2026-07-14 03:24:00 -07:00
MechaCat02
0aa3eadca7 [Kernel] Add null-safe XThread::TryGetCurrentThread() accessor
The additive event_log extraction tracer needs a current-thread lookup that
returns nullptr instead of asserting when called from boot code before any
XThread exists (GetCurrentThread asserts on non-guest threads). New read-only
static accessor over the thread-local; no behaviour change. Fixes the native
Linux build of the phase-a args/file.read tracer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:42:54 +02:00
MechaCat02
a40bc50159 [Phase A] Port args / file.read extraction tracing onto instrument-current
Bring the cvar-gated JSONL extraction tracer (event_log) forward from the
phase-a-tracing line so args/args_resolved + file.read events are
available alongside the GPU draw logging. Purely additive:

- src/xenia/kernel/event_log.{cc,h}: the tracer (auto-globbed into the
  kernel target). Tier 1 fills kernel.call args (raw r3..r10) +
  args_resolved.path under --phase_a_trace_args; Tier 2 resolves
  NtReadFile handle->path via the object table and emits file.read.
- cpu_flags: phase_a_event_log_path / _mem_writes / _trace_args /
  _hash_probe / kernel_emit_contention (all default-off).
- shim_utils.h: phase_a_bridge decl + EmitImportAndCallWithCtx/EmitReturn
  hooks in the active X::Trampoline, skipped when Enabled() is false.

Deliberately EXCLUDES the Tier-3 hash-probe HIR/emitter trap (the IPFB
name-hash was recovered statically, so the probe is moot). The
phase_a_hash_probe cvar is defined but inert.

Default-off => instrument-current behaviour byte-identical when unused.
Not yet compile-verified against instrument-current's toolchain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 19:13:36 +02:00
MechaCat02
f970a5173f [GPU] log_draws: also dump vertex positions for file correlation
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m57s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Extend the RE draw-logger to dump the first few vertex POSITIONS (read
from guest memory) under each draw. The f32 position bytes are identical
between the guest buffer and the on-disc .xpr (only f16 pairs are
rearranged on load), so these values can be grep'd for in a resource file
to locate a mesh whose in-file offset is otherwise unknown.

Validated: world-space stage geometry byte-matches Stage_*.xpr at exact
offsets. (Load-time-transformed meshes like the player ship don't match,
which is itself a useful finding.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:09:08 +02:00
MechaCat02
25eb17b91d [GPU] Add log_draws: dump per-draw vertex declaration for RE
Reverse-engineering aid for decoding game mesh formats against GPU ground
truth. When the `log_draws` cvar is set, each distinct draw's primitive
type, index buffer (guest base / count / format / endianness), and full
per-stream vertex declaration (fetch-constant base + stride, and every
element's format + offset) is written to xenia_re_draws.log.

- command_processor.{h,cc}: CommandProcessor::LogDrawForRE(), no-op unless
  the cvar is set. De-dups by the vertex-declaration fingerprint (shader +
  primitive + element formats/offsets), so animated UI that redraws the
  same format into fresh buffers every frame collapses to one record --
  keeping it near-free (an earlier address-keyed de-dup flooded the log and
  stalled the GPU thread). Capped at 4096 distinct formats.
- pm4_command_processor_implement.h: call it from ExecutePacketType3Draw
  after IssueDraw (so the vertex shader has been analyzed). Backend-agnostic
  base path -- works for the Vulkan build.

Used to confirm Project Sylpheed's XBG7 mesh layout (triangle list, pos
f32x3 / normal f16x4 / uv f16x2, variable stride).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:42:33 +02:00
MechaCat02
f10484834c [APU] Fix mission-audio silence: ALSA underrun keepalive + guest_audio_flags
Two Linux mission-audio fixes for Project Sylpheed (audio played in
intro/menu but died when a mission finished loading, never returning):

- alsa_audio_driver: when the guest stalls (mission "Preparing for Sortie"
  load) and the ring buffer empties, feed silence to keep the PCM alive
  instead of sleeping. Previously the small buffer drained, XRUN'd, and
  playback never recovered (matches the known "audio muted permanently").
- xconfig: make the guest speaker config a cvar (guest_audio_flags,
  default Digital Stereo) instead of hardcoded Dolby Digital surround.

(The practical fix on this box also needed log_mask=13 to stop kernel log
spam starving the audio pipeline during missions — config, not code.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:49:25 +02:00
MechaCat02
919e526fd8 [Fix] Screenshot: null-check presenter before use (fixes F12 crash)
TakeScreenshot() called GetGraphicsSystemPresenter()->CaptureGuestOutput()
and only tested the pointer for null AFTER dereferencing it, so taking a
screenshot with no live presenter dereferenced null and crashed. Check for
null first, and re-enable notifications on the capture-failure path too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:44:11 +02:00
MechaCat02
2233fae235 [Build] Guard ThinLTO behind XENIA_ENABLE_LTO for memory-constrained hosts
Release enables -flto=thin unconditionally; the ThinLTO link spikes memory
past what a 15GB box can handle. Gate it behind an option (default ON, so
normal/CI builds are unchanged); pass -DXENIA_ENABLE_LTO=OFF to build
Release without the LTO memory spike.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:43:23 +02:00
MechaCat02
f81e90f03a audit: add poll-based arbitrary guest-VA memory-watch cvars
Adds audit_mem_watch_addr (comma-separated hex guest VAs) and
audit_mem_watch_size (1/2/4/8 bytes) to watch arbitrary guest memory
locations at runtime. Once per vblank, GraphicsSystem::MarkVblank()
reads each VA big-endian via Memory::TranslateVirtual and emits an
XELOGKERNEL "AUDIT-MEM-WATCH" line on every value change vs the prior
frame (same greppable log stream as AUDIT-HLC / AUDIT-MEM-READ).

Mechanism is poll / value-change: it captures WHEN a value changes
(vblank index) but NOT the writer guest-PC; pair with audit_jit_prolog_pc
on a suspected writer to recover the PC. Heap VAs vary per run/engine,
so they are supplied at runtime via cvar/CLI. Default empty => disabled
=> zero overhead and no emulation-behavior change.

Validated against the controller vsync "clock A" field (+0x58): the
watch fires once per change, advancing monotonically in lockstep with
vblanks. Game still boots and runs muted with the cvar unset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:43:23 +02:00
MechaCat02
07f09f8fe0 [Audit] round 24: signal-path probes (XEvent::Set + IO + NtDuplicateObject)
Adds AUDIT-HLC probes for every path that signals an XEvent or duplicates
its handle, so the round-23 puzzle (waits completing without visible
NtSetEvent/KeSetEvent) can be cross-referenced by underlying X_KEVENT VA
and by primary handle.

  * NtCreateEvent_inner: always log kevent_va for handle/VA cross-ref
  * XEvent::Set: universal hook prints primary handle + kevent_va + LR;
    catches all paths into the event regardless of shim used
  * NtSetEvent: extended with PPC back-chain walk for caller's guest_lr
    (same idiom as the wait probe), so the signaler function is
    immediately identifiable
  * NtReadFile / NtReadFileScatter / NtWriteFile: log when signal_event
    path fires ev->Set() inline at IO completion (bypasses both
    NtSetEvent and KeSetEvent shims)
  * CompleteOverlappedEx (kernel_state.cc): log when overlapped event
    is signaled at completion
  * NtDuplicateObject: log src/dst handle pair (round-24 confirmed silph
    event is dup'd before signaling, explaining the handle-mismatch
    puzzle between NtSetEvent's `handle=` argument and XEvent::Set's
    primary handle())

Result: silph wait at sub_821CB030+0x1B0 on handle F80000A0 is signaled
via NtSetEvent on handle F80000A8 (the duplicate created by sub_8245D9D8
at lr=0x82450DF4 inside the worker chain sub_82450A28 ← 0xA68 ← 0xB68).
Verdict A confirmed: signaler exists and is reachable, but round-17.β
missed it because the search keyed on the wrong handle. No bypass path;
the worker chain is identical between canary and ours-impl, but ours
signals different handles (work-item queue divergence at sub_82452DC0).

Audit-only diagnostic. No semantic changes. Net delta +86 LOC across 7
files. All probes gated on the existing audit_handle_lifecycle cvar
(default off). Tested via Sylpheed boot (35 s, 4 MB log). Audit-handle-
lifecycle-probes branch, local-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-12 15:43:23 +02:00