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, (), ());