From 2635fe84b67f98a0011aceccc779ae34029e48ff Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 27 Aug 2026 03:18:13 -0400 Subject: [PATCH] [Fix] (Tessellation): compare the default patch levels by bit pattern, so a NaN level stops re-linking the program on every draw --- .../MG_State/GLState/RenderState/RenderState.cpp | 9 +++++++-- MobileGL/MG_Util/Math/VectorTypes.h | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index 135aa1e1..cd96d084 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -220,8 +220,13 @@ namespace MobileGL { // BumpVersions(), not just ++m_version, for the same reason SetPatchVertices does it: // these levels are compiled INTO the synthesized pass-through tessellation control // stage on both backends, so changing one makes an already-built program stale. + // + // The redundant-write guard compares BIT PATTERNS, not floats: glPatchParameterfv + // accepts NaN, and a float compare would let a re-set of the identical NaN tuple fall + // through and bump the pipeline-state version - invalidating DirectVulkan's pipeline + // memo and DirectGLES's render-state span - on every single call. void RenderState::SetPatchDefaultOuterLevel(const FloatVec4& levels) { - if (m_parameters.PatchDefaultOuterLevel == levels) return; + if (BitwiseEqual(m_parameters.PatchDefaultOuterLevel, levels)) return; m_parameters.PatchDefaultOuterLevel = levels; BumpVersions(); @@ -232,7 +237,7 @@ namespace MobileGL { } void RenderState::SetPatchDefaultInnerLevel(const FloatVec2& levels) { - if (m_parameters.PatchDefaultInnerLevel == levels) return; + if (BitwiseEqual(m_parameters.PatchDefaultInnerLevel, levels)) return; m_parameters.PatchDefaultInnerLevel = levels; BumpVersions(); diff --git a/MobileGL/MG_Util/Math/VectorTypes.h b/MobileGL/MG_Util/Math/VectorTypes.h index 5ea2f3c3..e51b8e4d 100644 --- a/MobileGL/MG_Util/Math/VectorTypes.h +++ b/MobileGL/MG_Util/Math/VectorTypes.h @@ -10,6 +10,8 @@ #include +#include + namespace MobileGL { template struct VecBase { @@ -84,6 +86,16 @@ namespace MobileGL { } }; + // Bit-pattern equality, for a vector used as a cache or staleness KEY rather than as a + // number. IEEE `==` - which operator== above is - says a NaN never equals itself, so a single + // NaN component makes every comparison answer "changed" and whatever the key guards is + // rebuilt on every use, forever. Two zeros of opposite sign compare unequal here, which only + // ever costs one extra rebuild. + template + Bool BitwiseEqual(const VecBase& a, const VecBase& b) { + return std::memcmp(a.data.data(), b.data.data(), sizeof(T) * N) == 0; + } + template struct Vec2 : public VecBase, T, 2> { using Base = VecBase, T, 2>;