From fdd6da77c0685bda6aae7a982186825936bf5342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 3 Jul 2026 19:34:34 +0200 Subject: [PATCH 1/7] Only show Z-raster stats in debug stats if z raster is on --- GPU/GPUCommonHW.cpp | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 1d54435528..8cf35e046d 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1780,9 +1780,7 @@ void GPUCommonHW::FormatGPUStatsCommon(StringWriter &w) { "block transfers: %d\n" "replacer: tracks %d references, %d unique textures\n" "Cpy: depth %d, color %d, reint %d, blend %d, self %d\n" - "GPU cycles: %d (%0.1f per vertex)\n" - "Z-rast: %0.2f+%0.2f+%0.2f (total %0.2f/%0.2f) ms\n" - "Z-rast: %d prim, %d nopix, %d small, %d earlysize, %d zcull, %d box\n%s", + "GPU cycles: %d (%0.1f per vertex)\n", gpuStats.perFrame.msProcessingDisplayLists * 1000.0f, gpuStats.perFrame.numEnqueue, gpuStats.perFrame.numUpdateStall, @@ -1823,18 +1821,23 @@ void GPUCommonHW::FormatGPUStatsCommon(StringWriter &w) { gpuStats.perFrame.numCopiesForShaderBlend, gpuStats.perFrame.numCopiesForSelfTex, gpuStats.perFrame.vertexGPUCycles + gpuStats.perFrame.otherGPUCycles, - vertexAverageCycles, - gpuStats.perFrame.msPrepareDepth * 1000.0, - gpuStats.perFrame.msCullDepth * 1000.0, - gpuStats.perFrame.msRasterizeDepth * 1000.0, - (gpuStats.perFrame.msPrepareDepth + gpuStats.perFrame.msCullDepth + gpuStats.perFrame.msRasterizeDepth) * 1000.0, - gpuStats.perFrame.msRasterTimeAvailable * 1000.0, - gpuStats.perFrame.numDepthRasterPrims, - gpuStats.perFrame.numDepthRasterNoPixels, - gpuStats.perFrame.numDepthRasterTooSmall, - gpuStats.perFrame.numDepthRasterEarlySize, - gpuStats.perFrame.numDepthRasterZCulled, - gpuStats.perFrame.numDepthEarlyBoxCulled, - debugRecording_ ? "(debug-recording)" : "" - ); + vertexAverageCycles); + + if (PSP_CoreParameter().compat.flags().SoftwareRasterDepth) { + w.F("Z-rast: %0.2f+%0.2f+%0.2f (total %0.2f/%0.2f) ms\n" + "Z-rast: %d prim, %d nopix, %d small, %d earlysize, %d zcull, %d box\n%s", + gpuStats.perFrame.msPrepareDepth * 1000.0, + gpuStats.perFrame.msCullDepth * 1000.0, + gpuStats.perFrame.msRasterizeDepth * 1000.0, + (gpuStats.perFrame.msPrepareDepth + gpuStats.perFrame.msCullDepth + gpuStats.perFrame.msRasterizeDepth) * 1000.0, + gpuStats.perFrame.msRasterTimeAvailable * 1000.0, + gpuStats.perFrame.numDepthRasterPrims, + gpuStats.perFrame.numDepthRasterNoPixels, + gpuStats.perFrame.numDepthRasterTooSmall, + gpuStats.perFrame.numDepthRasterEarlySize, + gpuStats.perFrame.numDepthRasterZCulled, + gpuStats.perFrame.numDepthEarlyBoxCulled, + debugRecording_ ? "(debug-recording)" : "" + ); + } } From 6508ae0aae1d10cb610d3719b098394af6d92556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 18:37:32 +0200 Subject: [PATCH 2/7] Reorganize the vertex ShaderId bits, better sorting order --- Common/Data/Text/StringWriter.h | 3 ++ GPU/Common/ShaderId.cpp | 59 ++++++++++++---------- GPU/Common/ShaderId.h | 79 ++++++++++++++---------------- GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 2 +- 5 files changed, 74 insertions(+), 71 deletions(-) diff --git a/Common/Data/Text/StringWriter.h b/Common/Data/Text/StringWriter.h index 42d29998ce..0486323a50 100644 --- a/Common/Data/Text/StringWriter.h +++ b/Common/Data/Text/StringWriter.h @@ -22,6 +22,9 @@ public: std::string_view as_view() const { return std::string_view(start_, p_ - start_); } + std::string as_string() const { + return std::string(start_, p_ - start_); + } const char *begin() const { return start_; } diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 56178ae393..db3c5d8474 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -4,6 +4,7 @@ #include "Common/GPU/thin3d.h" #include "Common/StringUtils.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/Config.h" #include "GPU/ge_constants.h" @@ -15,53 +16,57 @@ #include "GPU/Common/DrawEngineCommon.h" // Just for ClipInfoFlags std::string VertexShaderDesc(const VShaderID &id) { - std::stringstream desc; - desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); - if (id.Bit(VS_BIT_IS_THROUGH)) desc << "THR "; - if (id.Bit(VS_BIT_USE_HW_TRANSFORM)) desc << "HWX "; - if (id.Bit(VS_BIT_HAS_COLOR)) desc << "C "; - if (id.Bit(VS_BIT_HAS_TEXCOORD)) desc << "T "; - if (id.Bit(VS_BIT_HAS_NORMAL)) desc << "N "; - if (id.Bit(VS_BIT_LMODE)) desc << "LM "; - if (id.Bit(VS_BIT_NORM_REVERSE)) desc << "RevN "; + char buffer[512]; + StringWriter desc(buffer, sizeof(buffer)); + + desc.F("%08x:%08x ", id.d[1], id.d[0]); + 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 "); + if (id.Bit(VS_BIT_HAS_TEXCOORD)) desc.C("T "); + if (id.Bit(VS_BIT_HAS_COLOR)) desc.C("C "); + if (id.Bit(VS_BIT_LMODE)) desc.C("LM "); + if (id.Bit(VS_BIT_NORM_REVERSE)) desc.C("RevN "); + if (id.Bit(VS_BIT_FLATSHADE)) desc.C("Flat "); + if (id.Bits(VS_BIT_MATERIAL_UPDATE, 3)) desc.C("MatUp:").F("%d", id.Bits(VS_BIT_MATERIAL_UPDATE, 3)).C(" "); + int uvgMode = id.Bits(VS_BIT_UVGEN_MODE, 2); + static constexpr std::string_view uvgModes[4] = {"UV ", "UVMtx ", "UVEnv ", "UVUnk "}; + if (uvgMode) desc.W(uvgModes[uvgMode]); if (uvgMode == GE_TEXMAP_TEXTURE_MATRIX) { int uvprojMode = id.Bits(VS_BIT_UVPROJ_MODE, 2); - const char *uvprojModes[4] = { "TexProjPos ", "TexProjUV ", "TexProjNNrm ", "TexProjNrm " }; - desc << uvprojModes[uvprojMode]; + static constexpr std::string_view uvprojModes[4] = { "TexProjPos ", "TexProjUV ", "TexProjNNrm ", "TexProjNrm " }; + desc.W(uvprojModes[uvprojMode]); } - static constexpr std::array uvgModes = { "UV ", "UVMtx ", "UVEnv ", "UVUnk " }; + + if (id.Bit(VS_BIT_ENABLE_BONES)) desc.F("Bones:%d ", id.Bits(VS_BIT_BONES, 3) + 1); + if (id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)) desc.F("WScale:%d ", id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)); + int ls0 = id.Bits(VS_BIT_LS0, 2); int ls1 = id.Bits(VS_BIT_LS1, 2); - if (uvgMode) desc << uvgModes[uvgMode]; - if (id.Bit(VS_BIT_ENABLE_BONES)) desc << "Bones:" << (id.Bits(VS_BIT_BONES, 3) + 1) << " "; + if (id.Bit(VS_BIT_FS_MINMAX_DISCARD)) desc.C("FSMinMax "); + if (id.Bit(VS_BIT_FS_DEPTH_CLAMP)) desc.C("FSDepthClamp "); // Lights if (id.Bit(VS_BIT_LIGHTING_ENABLE)) { - desc << "Light: "; + desc.C("Light: "); } if (id.Bit(VS_BIT_LIGHT_UBERSHADER)) { - desc << "LightUberShader "; + desc.C("LightUberShader "); } for (int i = 0; i < 4; i++) { bool enabled = id.Bit(VS_BIT_LIGHT0_ENABLE + i) && id.Bit(VS_BIT_LIGHTING_ENABLE); if (enabled || (uvgMode == GE_TEXMAP_ENVIRONMENT_MAP && (ls0 == i || ls1 == i))) { - desc << i << ": "; - desc << "c:" << id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2) << " t:" << id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2) << " "; + desc.F("%d: ", i); + desc.F("c:%d t:%d ", id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2), id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); } } - if (id.Bits(VS_BIT_MATERIAL_UPDATE, 3)) desc << "MatUp:" << id.Bits(VS_BIT_MATERIAL_UPDATE, 3) << " "; - if (id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)) desc << "WScale " << id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2) << " "; - if (id.Bit(VS_BIT_FLATSHADE)) desc << "Flat "; - if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) desc << "RangeCull "; + if (id.Bit(VS_BIT_SIMPLE_STEREO)) desc.C("SimpleStereo "); + if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) desc.C("RangeCull "); - if (id.Bit(VS_BIT_SIMPLE_STEREO)) desc << "SimpleStereo "; - if (id.Bit(VS_BIT_FS_MINMAX_DISCARD)) desc << "FSMinMax "; - if (id.Bit(VS_BIT_FS_DEPTH_CLAMP)) desc << "FSDepthClamp "; - - return desc.str(); + return desc.as_string(); } void ComputeVertexShaderID(VShaderID *id_out, u32 vertType, bool useHWTransform, bool weightsAsFloat, bool useSkinInDecode, ClipInfoFlags clipInfoFlags) { diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 26807c3837..b853a4dc6b 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -24,52 +24,47 @@ inline bool needFragmentDepthClamp() { // VS_BIT_LIGHT_UBERSHADER indicates that some groups of these will be // sent to the shader and processed there. This cuts down the number of shaders ("ubershader approach"). enum VShaderBit : uint8_t { - VS_BIT_LMODE = 0, - VS_BIT_IS_THROUGH = 1, - // bit 2 is free. - VS_BIT_HAS_COLOR = 3, - // bit 4 is free. - VS_BIT_VERTEX_RANGE_CULLING = 5, - VS_BIT_SIMPLE_STEREO = 6, - // bit 7 is free, - VS_BIT_USE_HW_TRANSFORM = 8, - VS_BIT_HAS_NORMAL = 9, // conditioned on hw transform - VS_BIT_NORM_REVERSE = 10, - VS_BIT_HAS_TEXCOORD = 11, - // 4 bits free: 12-15 - VS_BIT_UVGEN_MODE = 16, - VS_BIT_UVPROJ_MODE = 18, // 2, can overlap with LS0 - VS_BIT_LS0 = 18, // 2 - VS_BIT_LS1 = 20, // 2 - VS_BIT_BONES = 22, // 3 should be enough, not 8 - // 25 - 29 are free. - VS_BIT_ENABLE_BONES = 30, + VS_BIT_IS_THROUGH = 0, + VS_BIT_USE_HW_TRANSFORM = 1, + VS_BIT_HAS_NORMAL = 2, // conditioned on hw transform + VS_BIT_HAS_TEXCOORD = 3, + VS_BIT_HAS_COLOR = 4, + VS_BIT_LMODE = 5, + VS_BIT_NORM_REVERSE = 6, + VS_BIT_FLATSHADE = 7, + VS_BIT_MATERIAL_UPDATE = 8, // 3 bits + // Free bit: 11 + VS_BIT_UVGEN_MODE = 12, // 2 bits + VS_BIT_UVPROJ_MODE = 14, // 2 bits + VS_BIT_ENABLE_BONES = 16, + VS_BIT_WEIGHT_FMTSCALE = 17, // only two bits + VS_BIT_BONES = 19, // 3 should be enough to represent 1-8 bones. + VS_BIT_FS_MINMAX_DISCARD = 22, // Do min/max and/or depth clamp in the fragment shader. It just means we need to forward Z and W to the fragment shader. + VS_BIT_FS_DEPTH_CLAMP = 23, // Do depth clamp in the fragment shader. + VS_BIT_LIGHTING_ENABLE = 24, + VS_BIT_LS0 = 25, // 2 bits + VS_BIT_LS1 = 27, // 2 bits // If this is set along with LIGHTING_ENABLE, all other lighting bits below // are passed to the shader directly instead. - VS_BIT_LIGHT_UBERSHADER = 31, + VS_BIT_LIGHT_UBERSHADER = 29, - VS_BIT_LIGHT0_COMP = 32, // 2 bits - VS_BIT_LIGHT0_TYPE = 34, // 2 bits - VS_BIT_LIGHT1_COMP = 36, // 2 bits - VS_BIT_LIGHT1_TYPE = 38, // 2 bits - VS_BIT_LIGHT2_COMP = 40, // 2 bits - VS_BIT_LIGHT2_TYPE = 42, // 2 bits - VS_BIT_LIGHT3_COMP = 44, // 2 bits - VS_BIT_LIGHT3_TYPE = 46, // 2 bits - VS_BIT_MATERIAL_UPDATE = 48, // 3 bits - // Bit 51 is free. - VS_BIT_LIGHT0_ENABLE = 52, - VS_BIT_LIGHT1_ENABLE = 53, - VS_BIT_LIGHT2_ENABLE = 54, - VS_BIT_LIGHT3_ENABLE = 55, - VS_BIT_LIGHTING_ENABLE = 56, - VS_BIT_WEIGHT_FMTSCALE = 57, // only two bits - // 59 - 61 are free. - VS_BIT_FS_MINMAX_DISCARD = 59, // Do min/max and/or depth clamp in the fragment shader. It just means we need to forward Z and W to the fragment shader. - VS_BIT_FS_DEPTH_CLAMP = 60, // Do depth clamp in the fragment shader. - VS_BIT_FLATSHADE = 62, // 1 bit - // Bit 63 is free. + VS_BIT_LIGHT0_COMP = 30, // 2 bits + VS_BIT_LIGHT0_TYPE = 32, // 2 bits + VS_BIT_LIGHT1_COMP = 34, // 2 bits + VS_BIT_LIGHT1_TYPE = 36, // 2 bits + VS_BIT_LIGHT2_COMP = 38, // 2 bits + VS_BIT_LIGHT2_TYPE = 40, // 2 bits + VS_BIT_LIGHT3_COMP = 42, // 2 bits + VS_BIT_LIGHT3_TYPE = 44, // 2 bits + VS_BIT_LIGHT0_ENABLE = 46, + VS_BIT_LIGHT1_ENABLE = 47, + VS_BIT_LIGHT2_ENABLE = 48, + VS_BIT_LIGHT3_ENABLE = 49, + + VS_BIT_VERTEX_RANGE_CULLING = 50, + VS_BIT_SIMPLE_STEREO = 51, + // bits 52-63 are free. }; static inline VShaderBit operator +(VShaderBit bit, int i) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 34cf666006..a086a0f3e8 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -900,7 +900,7 @@ enum class CacheDetectFlags { }; #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 40 +#define CACHE_VERSION 41 struct CacheHeader { uint32_t magic; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index acf919f0b9..c2b8c20ca5 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -408,7 +408,7 @@ enum class VulkanCacheDetectFlags { }; #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 56 +#define CACHE_VERSION 57 struct VulkanCacheHeader { uint32_t magic; From e78a46407b56ef1be07afd3ff71cae921f805ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 18:41:10 +0200 Subject: [PATCH 3/7] Add new relay "Jeddah", minor update --- GPU/Common/VertexShaderGenerator.cpp | 2 +- assets/adhoc-servers.json | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index ce8581f64a..57cfc0d8ba 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -296,7 +296,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // And the "varyings". if (useHWTransform) { - WRITE(p, "struct VS_IN { \n"); + WRITE(p, "struct VS_IN {\n"); if (enableBones) { WRITE(p, " %s", boneWeightAttrDeclHLSL[numBoneWeights]); } diff --git a/assets/adhoc-servers.json b/assets/adhoc-servers.json index ef3b0fed49..48e37ae100 100644 --- a/assets/adhoc-servers.json +++ b/assets/adhoc-servers.json @@ -187,7 +187,16 @@ "location-emoji": "🇧🇷", "description": "Rede br para partidas online de jogos PSP.", "data_mode": "AemuPostoffice", - "status_data_json": "http://pspbrasil.duckdns.org/data.json" + "status_data_json": "https://pspbrasil.duckdns.org/data.json" + }, + { + "name": "Jeddah relay", + "host": "jeddah-relay.ddns.net", + "discord": "", + "location": "Saudi Arabia", + "location-emoji": "🇸🇦", + "description": "For players looking to play any games", + "data_mode": "AemuPostoffice" } ] } 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 4/7] 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); From d07ade200e3899b5988c02abffd16f89e9550a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 19:39:42 +0200 Subject: [PATCH 5/7] Convert FragmentShaderDesc to use ShaderWriter, add missing bit Fog --- GPU/Common/ShaderId.cpp | 113 +++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index a66aecfaf3..d2782d1b53 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -1,5 +1,4 @@ #include -#include #include #include "Common/GPU/thin3d.h" @@ -178,96 +177,100 @@ static bool MatrixNeedsProjection(const float m[12], GETexProjMapMode mode) { } std::string FragmentShaderDesc(const FShaderID &id) { - std::stringstream desc; - desc << id.ToDebugString() << " "; - if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear "; + char buffer[512]; + StringWriter desc(buffer, sizeof(buffer)); + + desc.W(id.ToDebugString()).C(" "); + if (id.Bit(FS_BIT_CLEARMODE)) desc.C("Clear "); if (id.Bit(FS_BIT_DO_TEXTURE)) { - desc << (id.Bit(FS_BIT_3D_TEXTURE) ? "Tex3D" : "Tex"); + desc.W(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; + case GE_TEXFUNC_ADD: desc.C("(TFuncAdd) "); break; + case GE_TEXFUNC_BLEND: desc.C("(TFuncBlend) "); break; + case GE_TEXFUNC_DECAL: desc.C("(TFuncDecal) "); break; + case GE_TEXFUNC_MODULATE: desc.C("(TFuncMod) "); break; + case GE_TEXFUNC_REPLACE: desc.C("(TFuncRepl) "); break; + default: desc.C("(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 "; - if (id.Bit(FS_BIT_COLOR_WRITEMASK)) desc << "WriteMask "; + if (id.Bit(FS_BIT_ENABLE_FOG)) desc.C("Fog "); + if (id.Bit(FS_BIT_LMODE)) desc.C("LM "); + if (id.Bit(FS_BIT_FLATSHADE)) desc.C("Flat "); + if (id.Bit(FS_BIT_DEPTH_TEST_NEVER)) desc.C("DepthNever "); + if (id.Bit(FS_BIT_COLOR_WRITEMASK)) desc.C("WriteMask "); if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) { - desc << "TClamp"; - if (id.Bit(FS_BIT_CLAMP_S)) desc << "S"; - if (id.Bit(FS_BIT_CLAMP_T)) desc << "T"; - desc << " "; + desc.C("TClamp"); + if (id.Bit(FS_BIT_CLAMP_S)) desc.C("S"); + if (id.Bit(FS_BIT_CLAMP_T)) desc.C("T"); + desc.C(" "); } int blendBits = id.Bits(FS_BIT_REPLACE_BLEND, 3); if (blendBits) { switch (blendBits) { case ReplaceBlendType::REPLACE_BLEND_BLUE_TO_ALPHA: - desc << "BlueToAlpha_" << "A:" << id.Bits(FS_BIT_BLENDFUNC_A, 4); + desc.C("BlueToAlpha_" "A:").F("%d ", id.Bits(FS_BIT_BLENDFUNC_A, 4)); break; default: - desc << "ReplaceBlend_" << id.Bits(FS_BIT_REPLACE_BLEND, 3) - << "A:" << id.Bits(FS_BIT_BLENDFUNC_A, 4) - << "_B:" << id.Bits(FS_BIT_BLENDFUNC_B, 4) - << "_Eq:" << id.Bits(FS_BIT_BLENDEQ, 3) << " "; + desc.C("ReplaceBlend_").F("%d ", id.Bits(FS_BIT_REPLACE_BLEND, 3)) + .C("A:").F("%d ", id.Bits(FS_BIT_BLENDFUNC_A, 4)) + .C("_B:").F("%d ", id.Bits(FS_BIT_BLENDFUNC_B, 4)) + .C("_Eq:").F("%d ", id.Bits(FS_BIT_BLENDEQ, 3)); break; } } switch (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)) { case REPLACE_ALPHA_NO: break; - case REPLACE_ALPHA_YES: desc << "StenToAlpha "; break; - case REPLACE_ALPHA_DUALSOURCE: desc << "StenToAlphaDual "; break; + case REPLACE_ALPHA_YES: desc.C("StenToAlpha "); break; + case REPLACE_ALPHA_DUALSOURCE: desc.C("StenToAlphaDual "); break; + default: desc.C("StenToAlphaUnknown "); break; // bad } + if (id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2) != REPLACE_ALPHA_NO) { switch (id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4)) { - case STENCIL_VALUE_UNIFORM: desc << "StenUniform "; break; - case STENCIL_VALUE_ZERO: desc << "Sten0 "; break; - case STENCIL_VALUE_ONE: desc << "Sten1 "; break; - case STENCIL_VALUE_KEEP: desc << "StenKeep "; break; - case STENCIL_VALUE_INVERT: desc << "StenInv "; break; - case STENCIL_VALUE_INCR_4BIT: desc << "StenIncr4 "; break; - case STENCIL_VALUE_INCR_8BIT: desc << "StenIncr8 "; break; - case STENCIL_VALUE_DECR_4BIT: desc << "StenDecr4 "; break; - case STENCIL_VALUE_DECR_8BIT: desc << "StenDecr8 "; break; - default: desc << "StenUnknown "; break; + case STENCIL_VALUE_UNIFORM: desc.C("StenUniform "); break; + case STENCIL_VALUE_ZERO: desc.C("Sten0 "); break; + case STENCIL_VALUE_ONE: desc.C("Sten1 "); break; + case STENCIL_VALUE_KEEP: desc.C("StenKeep "); break; + case STENCIL_VALUE_INVERT: desc.C("StenInv "); break; + case STENCIL_VALUE_INCR_4BIT: desc.C("StenIncr4 "); break; + case STENCIL_VALUE_INCR_8BIT: desc.C("StenIncr8 "); break; + case STENCIL_VALUE_DECR_4BIT: desc.C("StenDecr4 "); break; + case STENCIL_VALUE_DECR_8BIT: desc.C("StenDecr8 "); break; + default: desc.C("StenUnknown "); break; } } else if (id.Bit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE)) { - desc << "StenOff "; + desc.C("StenOff "); } - - 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)] << " "; - if (id.Bit(FS_BIT_COLOR_AGAINST_ZERO)) desc << "ColorTest0 " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match; - else if (id.Bit(FS_BIT_COLOR_TEST)) desc << "ColorTest " << alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)] << " "; // first 4 match - if (id.Bit(FS_BIT_TEST_DISCARD_TO_ZERO)) desc << "TestDiscardToZero "; - if (id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL)) desc << "StencilDiscardWorkaround "; + if (id.Bit(FS_BIT_ALPHA_AGAINST_ZERO)) desc.C("AlphaTest0 ").W(alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)]).C(" "); + else if (id.Bit(FS_BIT_ALPHA_TEST)) desc.C("AlphaTest ").W(alphaTestFuncs[id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3)]).C(" "); + if (id.Bit(FS_BIT_COLOR_AGAINST_ZERO)) desc.C("ColorTest0 ").W(alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)]).C(" "); // first 4 match; + else if (id.Bit(FS_BIT_COLOR_TEST)) desc.C("ColorTest ").W(alphaTestFuncs[id.Bits(FS_BIT_COLOR_TEST_FUNC, 2)]).C(" "); // first 4 match + if (id.Bit(FS_BIT_TEST_DISCARD_TO_ZERO)) desc.C("TestDiscardToZero "); + if (id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL)) desc.C("StencilDiscardWorkaround "); int logicMode = id.Bits(FS_BIT_REPLACE_LOGIC_OP, 4); - if ((logicMode != GE_LOGIC_COPY) && !id.Bit(FS_BIT_CLEARMODE)) desc << "RLogic(" << logicFuncs[logicMode] << ")"; - if (id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE)) desc << "TexArray "; - if (id.Bit(FS_BIT_STEREO)) desc << "Stereo "; - if (id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH)) desc << "(fetch)"; - if (id.Bit(FS_BIT_MINMAX_DISCARD)) desc << "FragMinMaxDiscard "; - if (id.Bit(FS_BIT_DEPTH_CLAMP)) desc << "FragDepthClamp "; + if ((logicMode != GE_LOGIC_COPY) && !id.Bit(FS_BIT_CLEARMODE)) desc.C("RLogic(").W(logicFuncs[logicMode]).C(")"); + if (id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE)) desc.C("TexArray "); + if (id.Bit(FS_BIT_STEREO)) desc.C("Stereo "); + if (id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH)) desc.C("(fetch)"); + if (id.Bit(FS_BIT_MINMAX_DISCARD)) desc.C("FragMinMaxDiscard "); + if (id.Bit(FS_BIT_DEPTH_CLAMP)) desc.C("FragDepthClamp "); const ShaderDepalMode depalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2); switch (depalMode) { case ShaderDepalMode::OFF: break; - case ShaderDepalMode::NORMAL: desc << "Depal("; + case ShaderDepalMode::NORMAL: desc.C("Depal("); { const GEBufferFormat shaderDepalFormat = (GEBufferFormat)id.Bits(FS_BIT_SHADER_DEPAL_FORMAT, 3); - desc << GeBufferFormatToString(shaderDepalFormat) << ") "; + desc.W(GeBufferFormatToString(shaderDepalFormat)).C(") "); break; } - case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break; - case ShaderDepalMode::CLUT8_8888: desc << "CLUT8From8888Depal"; break; + case ShaderDepalMode::SMOOTHED: desc.C("SmoothDepal "); break; + case ShaderDepalMode::CLUT8_8888: desc.C("CLUT8From8888Depal"); break; } - return desc.str(); + return desc.as_string(); } bool FragmentIdNeedsFramebufferRead(const FShaderID &id) { From 2e9ee496793eec8405ba63a2e5d3f08ea192c9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 19:47:55 +0200 Subject: [PATCH 6/7] Reorganize the FS shader bits too, for a better sort order in the shader viewer --- GPU/Common/ShaderId.cpp | 2 +- GPU/Common/ShaderId.h | 40 +++++++++++++++--------------- GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index d2782d1b53..2fcfd03c66 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -193,8 +193,8 @@ std::string FragmentShaderDesc(const FShaderID &id) { default: desc.C("(TFuncUnk) "); break; } } - if (id.Bit(FS_BIT_ENABLE_FOG)) desc.C("Fog "); if (id.Bit(FS_BIT_LMODE)) desc.C("LM "); + if (id.Bit(FS_BIT_ENABLE_FOG)) desc.C("Fog "); if (id.Bit(FS_BIT_FLATSHADE)) desc.C("Flat "); if (id.Bit(FS_BIT_DEPTH_TEST_NEVER)) desc.C("DepthNever "); if (id.Bit(FS_BIT_COLOR_WRITEMASK)) desc.C("WriteMask "); diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 58fd709494..3aab8023a1 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -75,20 +75,23 @@ static inline VShaderBit operator +(VShaderBit bit, int i) { enum FShaderBit : uint8_t { FS_BIT_CLEARMODE = 0, FS_BIT_DO_TEXTURE = 1, - FS_BIT_TEXFUNC = 2, // 3 bits - FS_BIT_SHADER_TEX_CLAMP = 5, - FS_BIT_CLAMP_S = 6, - FS_BIT_CLAMP_T = 7, - FS_BIT_SHADER_DEPAL_FORMAT = 8, // 3 bits (GEBufferFormat), connected to FS_BIT_SHADER_DEPAL_MODE + FS_BIT_3D_TEXTURE = 2, + FS_BIT_DO_TEXTURE_PROJ = 3, + FS_BIT_TEXFUNC = 4, // 3 bits + FS_BIT_LMODE = 7, + FS_BIT_ENABLE_FOG = 8, + FS_BIT_FLATSHADE = 9, + FS_BIT_DEPTH_CLAMP = 10, // These both are connected to VS_BIT_MINMAX_DISCARD_OR_DEPTH_CLAMP in the vertex shader. + FS_BIT_MINMAX_DISCARD = 11, FS_BIT_ALPHA_TEST = 12, FS_BIT_ALPHA_TEST_FUNC = 13, // 3 bits FS_BIT_ALPHA_AGAINST_ZERO = 16, FS_BIT_COLOR_TEST = 17, FS_BIT_COLOR_TEST_FUNC = 18, // 2 bits FS_BIT_COLOR_AGAINST_ZERO = 20, - FS_BIT_ENABLE_FOG = 21, - FS_BIT_DO_TEXTURE_PROJ = 22, - FS_BIT_MINMAX_DISCARD = 23, + FS_BIT_SHADER_TEX_CLAMP = 21, + FS_BIT_CLAMP_S = 22, + FS_BIT_CLAMP_T = 23, FS_BIT_STENCIL_TO_ALPHA = 24, // 2 bits FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE = 26, // 4 bits (ReplaceAlphaType) FS_BIT_SIMULATE_LOGIC_OP_TYPE = 30, // 2 bits @@ -96,20 +99,17 @@ enum FShaderBit : uint8_t { FS_BIT_BLENDEQ = 35, // 3 bits FS_BIT_BLENDFUNC_A = 38, // 4 bits FS_BIT_BLENDFUNC_B = 42, // 4 bits - FS_BIT_FLATSHADE = 46, - // Free bit 47 - FS_BIT_TEST_DISCARD_TO_ZERO = 48, - FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL = 49, - FS_BIT_COLOR_WRITEMASK = 50, - FS_BIT_REPLACE_LOGIC_OP = 51, // 4 bits. GE_LOGIC_COPY means no-op/off. - FS_BIT_SHADER_DEPAL_MODE = 55, // 2 bits (ShaderDepalMode) - FS_BIT_SAMPLE_ARRAY_TEXTURE = 57, // For multiview, framebuffers are array textures and we need to sample the two layers correctly. + FS_BIT_TEST_DISCARD_TO_ZERO = 46, + FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL = 47, + FS_BIT_COLOR_WRITEMASK = 48, + FS_BIT_REPLACE_LOGIC_OP = 49, // 4 bits. GE_LOGIC_COPY means no-op/off. + FS_BIT_SHADER_DEPAL_MODE = 53, // 2 bits (ShaderDepalMode) + FS_BIT_SHADER_DEPAL_FORMAT = 55, // 3 bits (GEBufferFormat), connected to FS_BIT_SHADER_DEPAL_MODE FS_BIT_STEREO = 58, FS_BIT_USE_FRAMEBUFFER_FETCH = 59, - FS_BIT_LMODE = 60, - FS_BIT_DEPTH_TEST_NEVER = 61, // Only used on Mali. Set when depth == NEVER. We forcibly avoid writing to depth in this case, since it crashes the driver. - FS_BIT_DEPTH_CLAMP = 62, // These both are connected to VS_BIT_MINMAX_DISCARD_OR_DEPTH_CLAMP in the vertex shader. - FS_BIT_3D_TEXTURE = 63, + FS_BIT_DEPTH_TEST_NEVER = 60, // Only used on Mali. Set when depth == NEVER. We forcibly avoid writing to depth in this case, since it crashes the driver. + FS_BIT_SAMPLE_ARRAY_TEXTURE = 61, // For multiview, framebuffers are array textures and we need to sample the two layers correctly. + // Free bits: 62-63 }; static inline FShaderBit operator +(FShaderBit bit, int i) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index e239909e9b..d57965ca78 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -896,7 +896,7 @@ enum class CacheDetectFlags { }; #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 41 +#define CACHE_VERSION 42 struct CacheHeader { uint32_t magic; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 26ad4c8557..6c798f398c 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -403,7 +403,7 @@ enum class VulkanCacheDetectFlags { }; #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 57 +#define CACHE_VERSION 58 struct VulkanCacheHeader { uint32_t magic; From cc6c46790c384088eb28560e51ff34c4b95f7c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jul 2026 19:49:45 +0200 Subject: [PATCH 7/7] Fix translation typo in de_DE.ini --- assets/lang/de_DE.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 8bf111a119..b2ef362d3d 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -1033,7 +1033,7 @@ No games in progress on this server = Keine Spiele auf diesem Server aktiv Other versions of this game that should work: = Andere Versionen dieses Spiels, die funktionieren sollten: P2P mode = P2P-Modus PacketRelayHint = Verfügbar auf Servern, die 'aemu_postoffice' Paketweiterleitung wie socom.cc anbieten. Deaktiviere dies für LAN- oder VPN-Spiel. Kann zuverlässiger sein, aber manchmal langsamer. -Players waiting: %1 = Spieler warten: +Players waiting: %1 = Spieler warten: %1 players: %1 = Spieler: %1 Please change your Port Offset = Bitte ändere deinen Port-Offset Open PPSSPP Multiplayer Wiki Page = PPSSPP-Ad-Hoc-Wiki-Seite