diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index bd5f36f1bb..f3a8e09228 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -1,6 +1,7 @@ #include #include +#include "thin3d/thin3d.h" #include "Common/StringUtils.h" #include "Core/Config.h" @@ -225,7 +226,7 @@ std::string FragmentShaderDesc(const ShaderID &id) { // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. -void ComputeFragmentShaderID(ShaderID *id_out) { +void ComputeFragmentShaderID(ShaderID *id_out, const Draw::Bugs &bugs) { ShaderID id; if (gstate.isModeClear()) { // We only need one clear shader, so let's ignore the rest of the bits. @@ -292,9 +293,6 @@ void ComputeFragmentShaderID(ShaderID *id_out) { if (stencilToAlpha != REPLACE_ALPHA_NO) { // 4 bits id.SetBits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4, ReplaceAlphaWithStencilType()); - } else { - // Use those bits instead for whether stencil output is disabled. - id.SetBit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, IsStencilTestOutputDisabled()); } // 2 bits. @@ -312,6 +310,12 @@ void ComputeFragmentShaderID(ShaderID *id_out) { id.SetBit(FS_BIT_FLATSHADE, doFlatShading); id.SetBit(FS_BIT_SHADER_DEPAL, useShaderDepal); + + if (g_Config.bVendorBugChecksEnabled) { + if (bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) { + id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, !IsStencilTestOutputDisabled() && !gstate.isDepthWriteEnabled()); + } + } } *id_out = id; diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index df0efb36d5..ec57b0273f 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -87,7 +87,8 @@ enum { FS_BIT_FLATSHADE = 46, FS_BIT_BGRA_TEXTURE = 47, FS_BIT_TEST_DISCARD_TO_ZERO = 48, - // 49+ are free. + FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL = 49, + // 50+ are free. }; struct ShaderID { @@ -175,11 +176,15 @@ struct FShaderID : ShaderID { } }; +namespace Draw { +class Bugs; +} + void ComputeVertexShaderID(ShaderID *id, uint32_t vertexType, bool useHWTransform); // Generates a compact string that describes the shader. Useful in a list to get an overview // of the current flora of shaders. std::string VertexShaderDesc(const ShaderID &id); -void ComputeFragmentShaderID(ShaderID *id); +void ComputeFragmentShaderID(ShaderID *id, const Draw::Bugs &bugs); std::string FragmentShaderDesc(const ShaderID &id); diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index 70471523da..935c101e68 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -191,7 +191,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader ** if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID); + ComputeFragmentShaderID(&FSID, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index a035c307d2..0fd16f2b3d 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -556,7 +556,7 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID); + ComputeFragmentShaderID(&FSID, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 4c5e3d8f44..585a6cf459 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -703,7 +703,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID); + ComputeFragmentShaderID(&FSID, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 92196f2a1e..361d38813c 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -84,15 +84,11 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer, uint32_ const char *shading = doFlatShading ? "flat" : ""; bool earlyFragmentTests = ((!enableAlphaTest && !enableColorTest) || testForceToZero) && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); - bool hasStencilOutput = stencilToAlpha != REPLACE_ALPHA_NO || id.Bit(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE) == 0; - - // TODO: This is a bug affecting shader cache generality - we CANNOT check anything but the shader ID and (indirectly) the game ID in here really. - // Need to move this check somehow to the shader ID generator. That's tricky though because it's generic... - bool isAdreno = vulkanVendorId == VULKAN_VENDOR_QUALCOMM && g_Config.bVendorBugChecksEnabled; + bool useAdrenoBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL); if (earlyFragmentTests) { WRITE(p, "layout (early_fragment_tests) in;\n"); - } else if (isAdreno && hasStencilOutput && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { + } else if (useAdrenoBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n"); } @@ -588,7 +584,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer, uint32_ WRITE(p, " z = (1.0/65535.0) * floor(z * 65535.0);\n"); } WRITE(p, " gl_FragDepth = z;\n"); - } else if (!earlyFragmentTests && isAdreno && hasStencilOutput) { + } else if (!earlyFragmentTests && useAdrenoBugWorkaround) { // Adreno (and possibly MESA/others) apply early frag tests even with discard in the shader. // Writing depth prevents the bug, even with depth_unchanged specified. WRITE(p, " gl_FragDepth = gl_FragCoord.z;\n"); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index aa812c7de2..10675fd5e1 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -240,7 +240,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID); + ComputeFragmentShaderID(&FSID, draw_->GetBugs()); } else { FSID = lastFSID_; } diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 8baa8cc5e2..07b19c08e8 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -315,6 +315,23 @@ struct Viewport { float MaxDepth; }; +class Bugs { +public: + bool Has(uint32_t bug) const { + return (flags_ & (1 << bug)) != 0; + } + void Infest(uint32_t bug) { + flags_ |= (1 << bug); + } + + enum : uint32_t { + NO_DEPTH_CANNOT_DISCARD_STENCIL = 0, + }; + +protected: + uint32_t flags_ = 0; +}; + class RefCountedObject { public: RefCountedObject() : refcount_(1) {} @@ -524,6 +541,8 @@ public: bool CreatePresets(); void DestroyPresets(); + Bugs GetBugs() const { return bugs_; } + virtual const DeviceCaps &GetDeviceCaps() const = 0; virtual uint32_t GetDataFormatSupport(DataFormat fmt) const = 0; virtual std::vector GetFeatureList() const { return std::vector(); } @@ -647,6 +666,8 @@ protected: int targetWidth_; int targetHeight_; + + Bugs bugs_; }; extern const UniformBufferDesc UBPresetDesc; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 7f8d36afac..1a6ba3e216 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -762,7 +762,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. - switch (vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID) { + auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()); + switch (deviceProps.vendorID) { case VULKAN_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break; case VULKAN_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break; case VULKAN_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break; @@ -773,6 +774,14 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.vendor = GPUVendor::VENDOR_UNKNOWN; } + if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) { + // Adreno 5xx devices, all known driver versions, fail to discard stencil when depth write is off. + // See: https://github.com/hrydgard/ppsspp/pull/11684 + if (deviceProps.deviceID >= 0x05000000 && deviceProps.deviceID < 0x06000000) { + bugs_.Infest(Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL); + } + } + device_ = vulkan->GetDevice(); queue_ = vulkan->GetGraphicsQueue();