From fbed4485b74c9b49cf9d926b8120f143c229a4b7 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 4 Aug 2026 19:47:39 -0400 Subject: [PATCH] [Fix] (DirectVulkan): key the program cache on the transform feedback capture layout The program cache is content-hash-shared across GL program names, so its key has to cover everything that changes the modules it stores. The capture layout did not: XfbCaptureDecoratePass bakes XfbBuffer/XfbStride/Offset into the SPIR-V from the frontend's layout, none of which is in the SPIR-V being hashed. Two programs with identical shaders and different glTransformFeedbackVaryings therefore shared one entry, and the first one linked decided how both captured. That is precisely what changing the buffer mode does -- the same varyings recorded with GL_SEPARATE_ATTRIBS instead of GL_INTERLEAVED_ATTRIBS -- so the separate-attribs pass of transform_feedback.draw_xfb_test replayed a capture that was still interleaved into buffer 0. Hash the captured varyings' names, buffer indices and offsets plus the per-buffer strides, and only for a capturing compile, so no other program changes key. --- .../DirectVulkan/Renderer/ProgramFactory.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index 69e733e9..39c55858 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -1761,6 +1761,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { XXHASH_VERIFY(XXH64_update(m_hashState, &binding, sizeof(binding))); } + // The transform feedback capture layout is baked into the modules by + // XfbCaptureDecoratePass rather than coming from the SPIR-V, so it has to be part of + // the key: two programs can share every shader and still capture differently, which + // is exactly what changing the buffer mode does (glTransformFeedbackVaryings with the + // same varyings but GL_SEPARATE_ATTRIBS instead of GL_INTERLEAVED_ATTRIBS). Only + // hashed for a capturing compile, so nothing else changes key. + if (flags & CompileOptionBit::XfbCapture) { + for (const auto& varying : program.GetTransformFeedbackVaryings()) { + XXHASH_VERIFY(XXH64_update(m_hashState, varying.name.data(), varying.name.size())); + XXHASH_VERIFY(XXH64_update(m_hashState, &varying.bufferIndex, sizeof(varying.bufferIndex))); + XXHASH_VERIFY(XXH64_update(m_hashState, &varying.offsetBytes, sizeof(varying.offsetBytes))); + } + const SizeT bufferCount = program.GetTransformFeedbackBufferCount(); + for (SizeT i = 0; i < bufferCount; ++i) { + const Uint32 stride = program.GetTransformFeedbackStride(static_cast(i)); + XXHASH_VERIFY(XXH64_update(m_hashState, &stride, sizeof(stride))); + } + } + HashType hash = XXH64_digest(m_hashState); return hash; }