Reverse the bits so we actually get the desired shader sorting order in the debug shader viewer

This commit is contained in:
Henrik Rydgård
2026-07-07 19:30:49 +02:00
parent e78a46407b
commit 93b570f8c7
8 changed files with 116 additions and 94 deletions
+17
View File
@@ -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 <intrin.h>
template <typename T>
+5
View File
@@ -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);
}
+41 -15
View File
@@ -1,10 +1,11 @@
#include <string>
#include <sstream>
#include <array>
#include <vector>
#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<std::string> ToSortedDebugShaderIdVec(std::vector<uint64_t> 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<std::string> strIds;
for (auto &id : ids) {
ShaderID shaderId;
shaderId.FromUint64(id);
std::string idStr;
shaderId.ToString(&idStr);
strIds.push_back(idStr);
}
return strIds;
}
+31 -42
View File
@@ -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<std::string> ToSortedDebugShaderIdVec(std::vector<uint64_t> ids);
+6 -8
View File
@@ -262,28 +262,26 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertexType, D3D11VertexShader
std::vector<std::string> ShaderManagerD3D11::DebugGetShaderIDs(DebugShaderType type) {
std::string id;
std::vector<std::string> ids;
std::vector<uint64_t> 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) {
+7 -11
View File
@@ -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<std::string> ShaderManagerGLES::DebugGetShaderIDs(DebugShaderType type) {
std::string id;
std::vector<std::string> ids;
std::vector<uint64_t> 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) {
+5 -10
View File
@@ -318,28 +318,23 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertexType, VulkanVertexShade
}
std::vector<std::string> ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType type) {
std::vector<std::string> ids;
std::vector<uint64_t> 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) {
+4 -8
View File
@@ -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);