diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 66b800dae1..db01e817c0 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -823,6 +823,11 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) // corrupt the depth buffer. This is easily worked around by simply scaling Z down a tiny bit when this case // is detected. See: https://github.com/hrydgard/ppsspp/issues/11937 bugs_.Infest(Bugs::EQUAL_WZ_CORRUPTS_DEPTH); + + if (IsHashMaliDriverVersion(deviceProps) || VK_VERSION_MAJOR(deviceProps.driverVersion) <= 16) { + // At least one driver at the upper end of the range is known to be likely to suffer from the bug causing issue #13833 (Midnight Club map broken). + bugs_.Infest(Bugs::MALI_STENCIL_DISCARD_BUG); + } } caps_.deviceID = deviceProps.deviceID; diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index c611cca0f1..8ae1ded37d 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -592,5 +592,18 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_ } } +const char *Bugs::GetBugName(uint32_t bug) { + switch (bug) { + case NO_DEPTH_CANNOT_DISCARD_STENCIL: return "NO_DEPTH_CANNOT_DISCARD_STENCIL"; + case DUAL_SOURCE_BLENDING_BROKEN: return "DUAL_SOURCE_BLENDING_BROKEN"; + case ANY_MAP_BUFFER_RANGE_SLOW: return "ANY_MAP_BUFFER_RANGE_SLOW"; + case PVR_GENMIPMAP_HEIGHT_GREATER: return "PVR_GENMIPMAP_HEIGHT_GREATER"; + case BROKEN_NAN_IN_CONDITIONAL: return "BROKEN_NAN_IN_CONDITIONAL"; + case COLORWRITEMASK_BROKEN_WITH_DEPTHTEST: return "COLORWRITEMASK_BROKEN_WITH_DEPTHTEST"; + case BROKEN_FLAT_IN_SHADER: return "BROKEN_FLAT_IN_SHADER"; + case EQUAL_WZ_CORRUPTS_DEPTH: return "EQUAL_WZ_CORRUPTS_DEPTH"; + case MALI_STENCIL_DISCARD_BUG: return "MALI_STENCIL_DISCARD_BUG"; + } +} } // namespace Draw diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 60e70b4c28..2a3ea9d09d 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -308,6 +308,10 @@ public: void Infest(uint32_t bug) { flags_ |= (1 << bug); } + uint32_t MaxBugIndex() const { + return (uint32_t)MAX_BUG; + } + const char *GetBugName(uint32_t bug); enum : uint32_t { NO_DEPTH_CANNOT_DISCARD_STENCIL = 0, @@ -318,6 +322,8 @@ public: COLORWRITEMASK_BROKEN_WITH_DEPTHTEST = 5, BROKEN_FLAT_IN_SHADER = 6, EQUAL_WZ_CORRUPTS_DEPTH = 7, + MALI_STENCIL_DISCARD_BUG = 8, + MAX_BUG, }; protected: diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index f1be3b94fe..db48c4f4c9 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -81,6 +81,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "MpegAvcWarmUp", &flags_.MpegAvcWarmUp); CheckSetting(iniFile, gameID, "BlueToAlpha", &flags_.BlueToAlpha); CheckSetting(iniFile, gameID, "CenteredLines", &flags_.CenteredLines); + CheckSetting(iniFile, gameID, "MaliDepthStencilBugWorkaround", &flags_.MaliDepthStencilBugWorkaround); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 37173adc6d..9340c36411 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -80,6 +80,7 @@ struct CompatFlags { bool MpegAvcWarmUp; bool BlueToAlpha; bool CenteredLines; + bool MaliDepthStencilBugWorkaround; }; class IniFile; diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 291c1afa8b..cbfbeeffa3 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -3,6 +3,7 @@ #include "Common/GPU/thin3d.h" #include "Common/StringUtils.h" +#include "Core/System.h" #include "Core/Config.h" #include "GPU/ge_constants.h" @@ -341,6 +342,10 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { 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()); + } else if (bugs.Has(Draw::Bugs::MALI_STENCIL_DISCARD_BUG) && PSP_CoreParameter().compat.flags().MaliDepthStencilBugWorkaround) { + // Very similar driver bug to the Adreno one, with the same workaround (though might look into if there are cheaper ones!) + // Keeping the conditions separate since it can probably be made tighter. + id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, !IsStencilTestOutputDisabled() && !gstate.isDepthWriteEnabled()); } } } diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 71961b8d21..11bbb70c00 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -682,6 +682,26 @@ void SystemInfoScreen::CreateViews() { cpuExtensions->Add(new TextView(exts[i], new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); } + ViewGroup *driverBugsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); + driverBugsScroll->SetTag("DevSystemInfoDriverBugs"); + LinearLayout *driverBugs = new LinearLayoutList(ORIENT_VERTICAL); + driverBugs->SetSpacing(0); + driverBugsScroll->Add(driverBugs); + + tabHolder->AddTab(si->T("Driver bugs"), driverBugsScroll); + + bool anyDriverBugs = false; + for (int i = 0; i < (int)draw->GetBugs().MaxBugIndex(); i++) { + if (draw->GetBugs().Has(i)) { + anyDriverBugs = true; + driverBugs->Add(new TextView(draw->GetBugs().GetBugName(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); + } + } + + if (!anyDriverBugs) { + driverBugs->Add(new TextView(si->T("No GPU driver bugs detected"), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); + } + ViewGroup *gpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); gpuExtensionsScroll->SetTag("DevSystemInfoOGLExt"); LinearLayout *gpuExtensions = new LinearLayoutList(ORIENT_VERTICAL); diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 93ed51976d..10a6640520 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -321,6 +321,7 @@ void GPUDriverTestScreen::DiscardTest() { InputLayout *inputLayout = ui_draw2d.CreateInputLayout(draw); BlendState *blendOff = draw->CreateBlendState({ false, 0xF }); + BlendState *blendOffNoColor = draw->CreateBlendState({ false, 0x8 }); // Write depth, write stencil. DepthStencilStateDesc dsDesc{}; @@ -331,8 +332,8 @@ void GPUDriverTestScreen::DiscardTest() { dsDesc.front.compareMask = 0xFF; dsDesc.front.compareOp = Comparison::ALWAYS; dsDesc.front.passOp = StencilOp::REPLACE; - dsDesc.front.failOp = StencilOp::ZERO; - dsDesc.front.depthFailOp = StencilOp::ZERO; + dsDesc.front.failOp = StencilOp::REPLACE; // These two shouldn't matter, because the test that fails is discard, not stencil. + dsDesc.front.depthFailOp = StencilOp::REPLACE; dsDesc.front.writeMask = 0xFF; dsDesc.back = dsDesc.front; DepthStencilState *depthStencilWrite = draw->CreateDepthStencilState(dsDesc); @@ -353,6 +354,9 @@ void GPUDriverTestScreen::DiscardTest() { dsDesc.stencilEnabled = true; dsDesc.depthCompare = Comparison::ALWAYS; dsDesc.front.compareOp = Comparison::EQUAL; + dsDesc.front.failOp = StencilOp::KEEP; + dsDesc.front.depthFailOp = StencilOp::KEEP; + dsDesc.front.writeMask = 0x0; dsDesc.back = dsDesc.front; DepthStencilState *stencilEqualDepthAlways = draw->CreateDepthStencilState(dsDesc); @@ -393,7 +397,7 @@ void GPUDriverTestScreen::DiscardTest() { PipelineDesc discardDesc{ Primitive::TRIANGLE_LIST, { draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), discardFragShader_ }, - inputLayout, depthStencilWrite, blendOff, rasterNoCull, &vsColBufDesc, + inputLayout, depthStencilWrite, blendOffNoColor, rasterNoCull, &vsColBufDesc, }; discardWriteDepthStencil_ = draw->CreateGraphicsPipeline(discardDesc); discardDesc.depthStencil = depthWrite; @@ -501,7 +505,7 @@ void GPUDriverTestScreen::DiscardTest() { dc.Flush(); dc.BeginPipeline(writePipelines[j], samplerNearest_); - // Draw the rectangle with stencil value 0, depth 0.1f and the text with stencil 0xFF, depth 0.9. Then leave 0xFF as the stencil value and draw the rectangles at depth 0.5. + // Draw the rectangle with stencil value 0, depth 0.1f and the text with stencil 0xFF, depth 0.9. Then set 0xFF as the stencil value and draw the rectangles at depth 0.5. draw->SetStencilRef(0x0); dc.SetCurZ(0.1f); dc.FillRect(UI::Drawable(bgColorBAD), bounds); diff --git a/assets/compat.ini b/assets/compat.ini index 1f0e2b2178..5b6cfadeed 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -586,6 +586,23 @@ NPJH50304 = true ULES00703 = true ULAS42095 = true +[MaliDepthStencilBugWorkaround] +# See issue #13833 where the map is supposed to be round but is not. + +# Midnight Club: LA Remix +ULUS10383 = true +ULES01144 = true +ULJS00180 = true +ULJS00267 = true +ULJM05904 = true +NPJH50440 = true +# Midnight Club 3 : DUB edition +ULUS10021 = true +ULES00108 = true + +# Tales of Phantasia - Narikiri Dungeon X. See #15526 +ULJS00293 = true + [RequireDefaultCPUClock] # GOW : Ghost of Sparta UCUS98737 = true