Skip to content

Port upstream renderer fixes and stop spinning on staging buffer maps - #8

Merged
mitch030504 merged 11 commits into
openxr-workfrom
claude/project-thread-by97b7
Oct 5, 2026
Merged

mitch030504 merged 11 commits into
openxr-workfrom
claude/project-thread-by97b7

Conversation

@mitch030504

@mitch030504 mitch030504 commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Requested by Mitch · project thread

Before: a few renderer bugs that upstream (patchzyy/Wiicompiled) fixed in its KartPad batch (patchzyy#244, commit 6f14bde) were still in the fork. Draws with an incomplete last primitive could index vertices that belonged to the next merged draw or past the buffer, merges could wrap the 16-bit index offset, native EFB readback sampled only row 0 and used a pipeline built for the wrong format on BGRA surfaces, and a failed graphics start aborted with little detail. While the GPU was two frames behind, begin_frame also spun a whole core on ProcessEvents until the next staging buffer mapped.

After: those fixes are in, adapted to the VR merge path, and the staging-buffer wait sleeps on a condition variable (woken by the map callback, at most 1 ms per wait) instead of spinning. Release builds skip Dawn validation and robustness, so the out-of-range indices and the pipeline/attachment format mismatch were not being caught at all on the Frame.

How: each fix is its own commit. Upstream's staging-capacity split from the same batch is deliberately left out, because it rewires the frame worker, FIFO drain and merge locking that the GX thread and stereo replay changed.

Commit What it fixes
Wake every pipeline waiter Compiler workers and renderer waiters share one condvar; notify_one could wake the wrong one, and promotion to priority woke nobody
Packed three-byte attributes WGSL load_u24 no longer reads the next word when all three bytes fit in the first
Lock the video render mode vi render mode reads and writes are now under a mutex; the resize request happens after the lock is released
Pipeline cache rows Rows with inconsistent size or compression metadata become cache misses instead of driving a bad allocation or copy
Draw boundaries Incomplete primitives emit no indices, empty draws are skipped, merges stop before 65,536 vertices, never fold into a single line/point draw, and break after GXInvalidateVtxCache or a vertex-format switch. The fork's VR merge conditions (vertex motion, ortho in stereo, native wheel) are kept
GEN_MODE The first all-zero GEN_MODE write is decoded instead of deduplicated against the zeroed cache
TEV liveness test Fixes the test that was already failing on openxr-work (it assumed no alignment rounding)
EFB readback Readback uniform no longer clamps Y to row 0. Blit pipelines are built for RGBA8, BGRA8 and the surface format and picked by destination. Upstream only builds RGBA8/BGRA8; the surface format is added so an unusual surface keeps working. The Quest 1 path is unchanged
Copy source snapshot The snapshot texture gets CopySrc, which the plain-copy resolve path needs
Startup errors aurora_initialize returns the first adapter, device or window error instead of hitting an ASSERT. The runtime throws it, so the existing fatal popup and fatal log record the reason. ImGui shutdown now skips backends that never started
Staging wait Uses a generation-checked map state (a retired callback can't mark the current buffer mapped), checks for device loss inside the wait, then sleeps instead of spinning

Validation

  • PR checks are green. "Recompilation test / Windows runtime (synthetic DOL)" compiles Aurora and the full runtime from scratch and links WiiCompiled.exe, so every change here builds and links on Windows.
  • gx_fifo_tests built from the fork sources against Dawn headers in a local Linux harness: 358/358 pass. On openxr-work the suite was 346/347; the failure was the TEV liveness test fixed here.
  • The 10 new or changed draw and GEN_MODE tests all fail when run against the unfixed command processor, so they test the fork's own merge path.
  • The new WGSL helper passes naga validation, and the other changed files pass clang++ -fsyntax-only on Linux.
  • Not run: a Frame (Linux/SteamOS) build and the headset. Before merging, this needs a Frame build (steam-frame-install.sh) and a race or two, checking the log for Ignoring cache entry, Unsupported blit destination format and begin_frame aborted.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B

claude added 11 commits October 5, 2026 13:17
Compiler workers and renderer waiters share g_pipelineCv. notify_one could
wake a renderer thread instead of a compiler, leaving a queued pipeline
until some later wake-up; with skip_unready_pipelines its draws stay
missing that much longer. Promoting a background build to priority now
wakes the workers too.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad 956d811).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
load_u24 and raw_fetch_u8_3 read a whole 32-bit word pair even when the
three bytes sit in the first word, so an attribute at the end of its
binding read past it. Release builds disable Dawn's robustness, so that
read isn't clamped.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad 0f6b274).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
VIConfigure writes the mode on the game side while the GX thread, the
frame worker and the window code read its size. The resize request is
made after the lock is released.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
Dawn asks for a blob's size before allocating and copying it. A row whose
stored length, declared size or zstd frame size disagree, or whose
compression flag is unknown, made it allocate the declared size and copy
or decompress past the stored blob. Such rows are now logged and skipped.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad 7095102).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
Incomplete primitives no longer emit indices: quads drop a one- or
two-vertex tail and draw a three-vertex tail as a triangle, triangle
lists ignore leftover vertices, and fans or strips under three vertices
emit nothing. Draws with no complete primitive are skipped. Previously an
incomplete quad indexed vertices that belonged to the next merged draw,
or past the end of the buffer.

Merging now stops before the 16-bit index offset would wrap, never folds
triangles into a single-instance line or point draw, and breaks after
GXInvalidateVtxCache or a vertex-format switch, so the next draw uploads
fresh arrays and uses its own format's shader.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
The BP register cache skips writes equal to the cached value, and the
cache starts at zero. A cold GEN_MODE write of zero (one TEV stage, no
culling, no channels or texgens) was therefore dropped, leaving the
decoded state at zero TEV stages until some other GEN_MODE value arrived.
Seed the cache's register ID byte so the first write always decodes.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
The test expected one extra TEV register to grow the uniform by exactly
16 bytes, but the allocation is alignment-rounded, so it failed on the
fork's baseline. Build the uniform and check that the register and the
K color after it are packed next to each other instead.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
The shared blit shader clamps the sampled Y to flags.z..w, but the
native readback uniform set flags.w to 0, so every row sampled row 0.
The blit pipeline was also built only for the surface format, while the
readback draws into an RGBA8 texture; on a BGRA surface (the usual
Linux/Vulkan choice) the pipeline and attachment formats disagree, which
release builds no longer catch because Dawn validation is skipped.

Build blit pipelines for RGBA8, BGRA8 and the surface format, and pick
one by destination format. The Quest 1 simple blit path is unchanged.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
When a copy with clear snapshots its source, the plain-copy path reads
the snapshot with CopyTextureToTexture, but the snapshot texture was
created without CopySrc usage.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
When no backend could create a window and a WebGPU device, aurora hit
an ASSERT and the process aborted with only SDL's last error in the log.
Keep the first adapter, device or window error, return it from
aurora_initialize with a new initializationStatus, and have the runtime
throw it so the existing fatal popup and fatal log record the reason.

ImGui shutdown now skips backends that never initialized, so the
shutdown that follows a failed start does not crash.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
When the GPU is two frames behind, begin_frame waits for the next
staging buffer's MapAsync. It used to spin on ProcessEvents, holding a
whole core busy for as long as the GPU took. It now calls ProcessEvents,
checks for device loss, then sleeps on a condition variable until the
map callback lands or 1 ms passes.

The map state also carries a generation, so a callback from a request
that shutdown or a rotation already retired can no longer mark the
current buffer mapped or unmapped.

From upstream patchzyy/Wiicompiled 6f14bde (patchzyy#244, KartPad batch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
@mitch030504 mitch030504 self-assigned this Oct 5, 2026
@mitch030504
mitch030504 marked this pull request as ready for review October 5, 2026 13:47
@mitch030504
mitch030504 merged commit f2f4f08 into openxr-work Oct 5, 2026
2 checks passed
@mitch030504
mitch030504 deleted the claude/project-thread-by97b7 branch October 5, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants