From 93b570f8c7ad4444982e50b24db054944dfb5018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 19:30:49 +0200 Subject: [PATCH] Reverse the bits so we actually get the desired shader sorting order in the debug shader viewer --- Common/BitSet.h | 17 +++++++ Common/Data/Random/Rng.h | 5 ++ GPU/Common/ShaderId.cpp | 56 +++++++++++++++++------ GPU/Common/ShaderId.h | 73 +++++++++++++----------------- GPU/D3D11/ShaderManagerD3D11.cpp | 14 +++--- GPU/GLES/ShaderManagerGLES.cpp | 18 +++----- GPU/Vulkan/ShaderManagerVulkan.cpp | 15 ++---- unittest/TestShaderGenerators.cpp | 12 ++--- 8 files changed, 116 insertions(+), 94 deletions(-) diff --git a/Common/BitSet.h b/Common/BitSet.h index e76ddcce63..1f39b45352 100644 --- a/Common/BitSet.h +++ b/Common/BitSet.h @@ -24,6 +24,23 @@ inline u32 ReverseBits32(u32 v) { return v; } +inline u64 ReverseBits64(u64 v) { + // http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + // swap odd and even bits + v = ((v >> 1) & 0x5555555555555555ULL) | ((v & 0x5555555555555555ULL) << 1); + // swap consecutive pairs + v = ((v >> 2) & 0x3333333333333333ULL) | ((v & 0x3333333333333333ULL) << 2); + // swap nibbles ... + v = ((v >> 4) & 0x0F0F0F0F0F0F0F0FULL) | ((v & 0x0F0F0F0F0F0F0F0FULL) << 4); + // swap bytes + v = ((v >> 8) & 0x00FF00FF00FF00FFULL) | ((v & 0x00FF00FF00FF00FFULL) << 8); + // swap 2-byte long pairs + v = ((v >> 16) & 0x0000FFFF0000FFFFULL) | ((v & 0x0000FFFF0000FFFFULL) << 16); + // swap 4-byte long pairs + v = (v >> 32) | (v << 32); + return v; +} + #ifdef _WIN32 #include template diff --git a/Common/Data/Random/Rng.h b/Common/Data/Random/Rng.h index 942da65cd6..082053735f 100644 --- a/Common/Data/Random/Rng.h +++ b/Common/Data/Random/Rng.h @@ -21,6 +21,11 @@ public: m_w = 18000 * (m_w & 65535) + (m_w >> 16); return (m_z << 16) + m_w; } + uint64_t R64() { + const uint32_t bottom = R32(); + const uint32_t top = R32(); + return ((uint64_t)top << 32) | bottom; + } float F() { return (float)R32() / (float)(0xFFFFFFFF); } diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index db3c5d8474..a66aecfaf3 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -1,10 +1,11 @@ #include #include -#include +#include #include "Common/GPU/thin3d.h" #include "Common/StringUtils.h" #include "Common/Data/Text/StringWriter.h" +#include "Common/BitSet.h" #include "Core/Config.h" #include "GPU/ge_constants.h" @@ -15,11 +16,15 @@ #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/DrawEngineCommon.h" // Just for ClipInfoFlags +std::string ShaderID::ToDebugString() const { + return StringFromFormat("%08x:%08x", d >> 32, d & 0xFFFFFFFF); +} + std::string VertexShaderDesc(const VShaderID &id) { char buffer[512]; StringWriter desc(buffer, sizeof(buffer)); - desc.F("%08x:%08x ", id.d[1], id.d[0]); + desc.W(id.ToDebugString()).C(" "); if (id.Bit(VS_BIT_IS_THROUGH)) desc.C("THR "); if (id.Bit(VS_BIT_USE_HW_TRANSFORM)) desc.C("HWX "); else desc.C("SWX "); if (id.Bit(VS_BIT_HAS_NORMAL)) desc.C("N "); @@ -174,10 +179,19 @@ static bool MatrixNeedsProjection(const float m[12], GETexProjMapMode mode) { std::string FragmentShaderDesc(const FShaderID &id) { std::stringstream desc; - desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); + desc << id.ToDebugString() << " "; if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear "; - if (id.Bit(FS_BIT_DO_TEXTURE)) desc << (id.Bit(FS_BIT_3D_TEXTURE) ? "Tex3D " : "Tex "); - if (id.Bit(FS_BIT_DO_TEXTURE_PROJ)) desc << "TexProj "; + if (id.Bit(FS_BIT_DO_TEXTURE)) { + desc << (id.Bit(FS_BIT_3D_TEXTURE) ? "Tex3D" : "Tex"); + switch (id.Bits(FS_BIT_TEXFUNC, 3)) { + case GE_TEXFUNC_ADD: desc << "(TFuncAdd) "; break; + case GE_TEXFUNC_BLEND: desc << "(TFuncBlend) "; break; + case GE_TEXFUNC_DECAL: desc << "(TFuncDecal) "; break; + case GE_TEXFUNC_MODULATE: desc << "(TFuncMod) "; break; + case GE_TEXFUNC_REPLACE: desc << "(TFuncRepl) "; break; + default: desc << "(TFuncUnk) "; break; + } + } if (id.Bit(FS_BIT_LMODE)) desc << "LM "; if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(FS_BIT_DEPTH_TEST_NEVER)) desc << "DepthNever "; @@ -224,16 +238,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { } else if (id.Bit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE)) { desc << "StenOff "; } - if (id.Bit(FS_BIT_DO_TEXTURE)) { - switch (id.Bits(FS_BIT_TEXFUNC, 3)) { - case GE_TEXFUNC_ADD: desc << "TFuncAdd "; break; - case GE_TEXFUNC_BLEND: desc << "TFuncBlend "; break; - case GE_TEXFUNC_DECAL: desc << "TFuncDecal "; break; - case GE_TEXFUNC_MODULATE: desc << "TFuncMod "; break; - case GE_TEXFUNC_REPLACE: desc << "TFuncRepl "; break; - default: desc << "TFuncUnk "; break; - } - } + if (id.Bit(FS_BIT_ALPHA_AGAINST_ZERO)) desc << "AlphaTest0 " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " "; else if (id.Bit(FS_BIT_ALPHA_TEST)) desc << "AlphaTest " << alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)] << " "; @@ -429,3 +434,24 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip *id_out = id; } + +std::vector ToSortedDebugShaderIdVec(std::vector ids) { + // Reverse the bits so that the sort order matches the importance order. + for (auto &id : ids) { + id = ReverseBits64(id); + } + std::sort(ids.begin(), ids.end()); + // Reverse the bits back to get the original IDs. + for (auto &id : ids) { + id = ReverseBits64(id); + } + std::vector strIds; + for (auto &id : ids) { + ShaderID shaderId; + shaderId.FromUint64(id); + std::string idStr; + shaderId.ToString(&idStr); + strIds.push_back(idStr); + } + return strIds; +} diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index b853a4dc6b..58fd709494 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -121,87 +121,73 @@ struct ShaderID { clear(); } void clear() { - for (size_t i = 0; i < ARRAY_SIZE(d); i++) { - d[i] = 0; - } + d = 0; } void set_invalid() { - for (size_t i = 0; i < ARRAY_SIZE(d); i++) { - d[i] = 0xFFFFFFFF; - } + d = 0xFFFFFFFFFFFFFFFF; } bool is_invalid() const { - for (size_t i = 0; i < ARRAY_SIZE(d); i++) { - if (d[i] != 0xFFFFFFFF) - return false; - } - return true; + return d == 0xFFFFFFFFFFFFFFFF; } - uint32_t d[2]; bool operator < (const ShaderID &other) const { - for (size_t i = 0; i < sizeof(d) / sizeof(uint32_t); i++) { - if (d[i] < other.d[i]) - return true; - if (d[i] > other.d[i]) - return false; - } - return false; + return d < other.d; } bool operator == (const ShaderID &other) const { - for (size_t i = 0; i < sizeof(d) / sizeof(uint32_t); i++) { - if (d[i] != other.d[i]) - return false; - } - return true; + return d == other.d; } bool operator != (const ShaderID &other) const { return !(*this == other); } - uint32_t Word(int word) const { - return d[word]; - } - // Note: This is a binary copy to string-as-bytes, not a human-readable representation. void ToString(std::string *dest) const { dest->resize(sizeof(d)); - memcpy(&(*dest)[0], d, sizeof(d)); + memcpy(&(*dest)[0], &d, sizeof(d)); } // Note: This is a binary copy from string-as-bytes, not a human-readable representation. void FromString(std::string src) { - memcpy(d, &(src)[0], sizeof(d)); + memcpy(&d, &(src)[0], sizeof(d)); } + uint64_t ToUint64() const { + return d; + } + void FromUint64(uint64_t src) { + d = src; + } + + std::string ToDebugString() const; + uint64_t d; protected: bool Bit(int bit) const { - return (d[bit >> 5] >> (bit & 31)) & 1; + return (d >> bit) & 1; } // Does not handle crossing 32-bit boundaries. count must be 30 or smaller. int Bits(int bit, int count) const { const int mask = (1 << count) - 1; - return (d[bit >> 5] >> (bit & 31)) & mask; + return (d >> bit) & mask; } void SetBit(int bit, bool value = true) { if (value) { - d[bit >> 5] |= 1 << (bit & 31); + d |= 1ULL << bit; } else { - d[bit >> 5] &= ~(1 << (bit & 31)); + d &= ~(1ULL << bit); } } void SetBits(int bit, int count, int value) { const int mask = (1 << count) - 1; - const int shifted_mask = mask << (bit & 31); - d[bit >> 5] = (d[bit >> 5] & ~shifted_mask) | ((value & mask) << (bit & 31)); + const uint64_t shifted_mask = uint64_t(mask) << bit; + d = (d & ~shifted_mask) | (uint64_t(value & mask) << bit); } }; -struct VShaderID : ShaderID { +struct VShaderID : public ShaderID { VShaderID() : ShaderID() { } - explicit VShaderID(ShaderID &src) { - memcpy(d, src.d, sizeof(d)); + explicit VShaderID(const ShaderID &src) { + d = src.d; } bool Bit(VShaderBit bit) const { @@ -221,12 +207,12 @@ struct VShaderID : ShaderID { } }; -struct FShaderID : ShaderID { +struct FShaderID : public ShaderID { FShaderID() : ShaderID() { } - explicit FShaderID(ShaderID &src) { - memcpy(d, src.d, sizeof(d)); + explicit FShaderID(const ShaderID &src) { + d = src.d; } bool Bit(FShaderBit bit) const { @@ -261,3 +247,6 @@ std::string FragmentShaderDesc(const FShaderID &id); // For sanity checking. bool FragmentIdNeedsFramebufferRead(const FShaderID &id); + +// For the shader viewer. +std::vector ToSortedDebugShaderIdVec(std::vector ids); diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index 337c8a49d7..2a80039c1a 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -262,28 +262,26 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertexType, D3D11VertexShader std::vector ShaderManagerD3D11::DebugGetShaderIDs(DebugShaderType type) { std::string id; - std::vector ids; + std::vector ids; switch (type) { case SHADER_TYPE_VERTEX: { - for (auto iter : vsCache_) { - iter.first.ToString(&id); - ids.push_back(id); + for (auto &iter : vsCache_) { + ids.push_back(iter.first.ToUint64()); } break; } case SHADER_TYPE_FRAGMENT: { - for (auto iter : fsCache_) { - iter.first.ToString(&id); - ids.push_back(id); + for (auto &iter : fsCache_) { + ids.push_back(iter.first.ToUint64()); } break; } default: break; } - return ids; + return ToSortedDebugShaderIdVec(ids); } std::string ShaderManagerD3D11::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index a086a0f3e8..e239909e9b 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -673,7 +673,7 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { std::string errorString; FragmentShaderFlags flags; if (!GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &flags, &errorString)) { - ERROR_LOG_REPORT(Log::G3D, "FS shader gen error: %s (%s: %08x:%08x)", errorString.c_str(), "GLES", FSID.d[0], FSID.d[1]); + ERROR_LOG_REPORT(Log::G3D, "FS shader gen error: %s (%s: %s)", errorString.c_str(), "GLES", FSID.ToDebugString().c_str()); return nullptr; } _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "FS length error: %d", (int)strlen(codeBuffer_)); @@ -691,7 +691,7 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { std::string errorString; VertexShaderFlags flags; if (!GenerateVertexShader(VSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &attrMask, &uniformMask, &flags, &errorString)) { - ERROR_LOG_REPORT(Log::G3D, "VS shader gen error: %s (%s: %08x:%08x)", errorString.c_str(), "GLES", VSID.d[0], VSID.d[1]); + ERROR_LOG_REPORT(Log::G3D, "VS shader gen error: %s (%s: %s)", errorString.c_str(), "GLES", VSID.ToDebugString().c_str()); return nullptr; } _assert_msg_(strlen(codeBuffer_) < CODE_BUFFER_SIZE, "VS length error: %d", (int)strlen(codeBuffer_)); @@ -776,7 +776,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, // Could fail to generate, in which case we're kinda screwed. fs = CompileFragmentShader(FSID); if (!fs) { - ERROR_LOG(Log::G3D, "Failed to generate fragment shader with ID %08x:%08x", FSID.d[0], FSID.d[1]); + ERROR_LOG(Log::G3D, "Failed to generate fragment shader with ID %s", FSID.ToDebugString().c_str()); // Still insert it so we don't end up spamming generation. } fsCache_.Insert(FSID, fs); @@ -834,26 +834,22 @@ std::string Shader::GetShaderString(DebugShaderStringType type, ShaderID id) con std::vector ShaderManagerGLES::DebugGetShaderIDs(DebugShaderType type) { std::string id; - std::vector ids; + std::vector ids; switch (type) { case SHADER_TYPE_VERTEX: vsCache_.Iterate([&](const VShaderID &id, Shader *shader) { - std::string idstr; - id.ToString(&idstr); - ids.push_back(idstr); + ids.push_back(id.ToUint64()); }); break; case SHADER_TYPE_FRAGMENT: fsCache_.Iterate([&](const FShaderID &id, Shader *shader) { - std::string idstr; - id.ToString(&idstr); - ids.push_back(idstr); + ids.push_back(id.ToUint64()); }); break; default: break; } - return ids; + return ToSortedDebugShaderIdVec(ids); } std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index c2b8c20ca5..26ad4c8557 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -318,28 +318,23 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertexType, VulkanVertexShade } std::vector ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType type) { - std::vector ids; + std::vector ids; switch (type) { case SHADER_TYPE_VERTEX: vsCache_.Iterate([&](const VShaderID &id, VulkanVertexShader *shader) { - std::string idstr; - id.ToString(&idstr); - ids.push_back(idstr); + ids.push_back(id.ToUint64()); }); break; case SHADER_TYPE_FRAGMENT: fsCache_.Iterate([&](const FShaderID &id, VulkanFragmentShader *shader) { - std::string idstr; - id.ToString(&idstr); - ids.push_back(idstr); + ids.push_back(id.ToUint64()); }); break; - case SHADER_TYPE_GEOMETRY: - return ids; default: break; } - return ids; + + return ToSortedDebugShaderIdVec(ids); } std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index a0f6f80167..bd3e9372dc 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -368,11 +368,9 @@ bool TestVertexShaders() { // Generate a bunch of random vertex shader IDs, try to generate shader source. // Then compile it and check that it's ok. for (int i = 0; i < count; i++) { - uint32_t bottom = rng.R32(); - uint32_t top = rng.R32(); + uint64_t id64 = rng.R64(); VShaderID id; - id.d[0] = bottom; - id.d[1] = top; + id.FromUint64(id64); // The generated bits need some adjustment: @@ -453,11 +451,9 @@ bool TestFragmentShaders() { // Generate a bunch of random fragment shader IDs, try to generate shader source. // Then compile it and check that it's ok. for (int i = 0; i < count; i++) { - uint32_t bottom = rng.R32(); - uint32_t top = rng.R32(); + uint64_t id64 = rng.R64(); FShaderID id; - id.d[0] = bottom; - id.d[1] = top; + id.FromUint64(id64); // bits we don't need to test because they are irrelevant on d3d11 id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, false);