From 6b0251e6d6e599247cf094a1a54a0d53aa1dc7eb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 12:27:23 +0000 Subject: [PATCH] Keep the position descriptor that GXClearVtxDesc leaves set Nintendo's GXClearVtxDesc, and Aurora's, keep GX_VA_POS at GX_DIRECT. The HLE mirror cleared it to GX_NONE, so the next draw could publish no position attribute and abort shader generation while a scene loads. Keep the mirror in step, and give the shader a zero position if a stream still arrives without one. Ported from heurazy/Wiicompiled_VR-PLUS (c2b2500). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Sabhzu6VgoWcHtpSk4C9KZ --- aurora-main/lib/gx/shader.cpp | 6 ++++++ runtime/src/hle/gx/gx_vertex.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/aurora-main/lib/gx/shader.cpp b/aurora-main/lib/gx/shader.cpp index 3c526c1..4b07fb2 100644 --- a/aurora-main/lib/gx/shader.cpp +++ b/aurora-main/lib/gx/shader.cpp @@ -559,6 +559,12 @@ static AlphaCompareExpr alpha_compare(GXCompare comp, u8 ref) { static inline std::string vtx_attr(const ShaderConfig& config, GXAttr attr) { const auto type = config.attrs[attr].attrType; if (type == GX_NONE) { + if (attr == GX_VA_POS) { + // Some scene transitions submit a vertex stream with no position attribute. + // The resulting primitive has no useful geometry, but shader generation + // must not abort the entire game while the next scene is loading. + return "vec3f(0.0)"s; + } if (attr == GX_VA_PNMTXIDX) { return "ubuf.current_pnmtx"; } diff --git a/runtime/src/hle/gx/gx_vertex.cpp b/runtime/src/hle/gx/gx_vertex.cpp index 2a4060d..0a78337 100644 --- a/runtime/src/hle/gx/gx_vertex.cpp +++ b/runtime/src/hle/gx/gx_vertex.cpp @@ -41,6 +41,11 @@ static void GX__ClearVtxDesc_8016dc34_gx() { changed |= g_hleGxState.vtxDesc[i] != GX_NONE; g_hleGxState.vtxDesc[i]=GX_NONE; } + // Nintendo's GXClearVtxDesc retains a direct position descriptor. Aurora's + // GXClearVtxDesc does the same; keep the HLE mirror in sync so the next + // draw does not publish GX_VA_POS=GX_NONE and abort shader generation. + changed |= g_hleGxState.vtxDesc[GX_VA_POS] != GX_DIRECT; + g_hleGxState.vtxDesc[GX_VA_POS] = GX_DIRECT; if (changed) g_hleGxState.InvalidateVtxLayoutHash(); // GXClearVtxDesc resets descriptors only; array base/stride state persists. GXClearVtxDesc(); @@ -49,6 +54,7 @@ extern "C" void GX__ClearVtxDesc_8016dc34() { for (int i = 0; i < 26; ++i) { g_gxGameVertexState.vtxDesc[i] = GX_NONE; } + g_gxGameVertexState.vtxDesc[GX_VA_POS] = GX_DIRECT; GxThread::Post(&GX__ClearVtxDesc_8016dc34_gx); } PPC_NATIVE_OVERRIDE_VOID(8016dc34, GX__ClearVtxDesc_8016dc34, (), ());