diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp index 77ac29b4c6..61c51b5d34 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp @@ -133,7 +133,7 @@ static VkPipelineDepthStencilStateCreateInfo GetVulkanDepthStencilState(const De } static VkPipelineColorBlendAttachmentState -GetVulkanAttachmentBlendState(const BlendingState& state) +GetVulkanAttachmentBlendState(const BlendingState& state, AbstractPipelineUsage usage) { VkPipelineColorBlendAttachmentState vk_state = {}; @@ -143,7 +143,8 @@ GetVulkanAttachmentBlendState(const BlendingState& state) bool use_shader_blend = !use_dual_source && state.usedualsrc && state.dstalpha && g_ActiveConfig.backend_info.bSupportsFramebufferFetch; - if (use_shader_blend) + if (use_shader_blend || (usage == AbstractPipelineUsage::GX && + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z))) { vk_state.blendEnable = VK_FALSE; } @@ -349,7 +350,7 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con VkPipelineDepthStencilStateCreateInfo depth_stencil_state = GetVulkanDepthStencilState(config.depth_state); VkPipelineColorBlendAttachmentState blend_attachment_state = - GetVulkanAttachmentBlendState(config.blending_state); + GetVulkanAttachmentBlendState(config.blending_state, config.usage); VkPipelineColorBlendStateCreateInfo blend_state = GetVulkanColorBlendState(config.blending_state, &blend_attachment_state, 1); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 50cae2b6a8..d074b09c1f 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -371,6 +371,13 @@ void VulkanContext::PopulateBackendInfoFeatures(VideoConfig* config, VkPhysicalD // with depth clamping. Fall back to inverted depth range for these. if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_REVERSED_DEPTH_RANGE)) config->backend_info.bSupportsReversedDepthRange = false; + + // Calling discard when early depth test is enabled can break on some Apple Silicon GPU drivers. + if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z)) + { + // We will use shader blending, so disable hardware dual source blending. + config->backend_info.bSupportsDualSourceBlend = false; + } } void VulkanContext::PopulateBackendInfoMultisampleModes( diff --git a/Source/Core/VideoCommon/DriverDetails.cpp b/Source/Core/VideoCommon/DriverDetails.cpp index 829d050549..6d7749c949 100644 --- a/Source/Core/VideoCommon/DriverDetails.cpp +++ b/Source/Core/VideoCommon/DriverDetails.cpp @@ -140,6 +140,8 @@ constexpr BugInfo m_known_bugs[] = { -1.0, -1.0, true}, {API_VULKAN, OS_ALL, VENDOR_QUALCOMM, DRIVER_QUALCOMM, Family::UNKNOWN, BUG_PRIMITIVE_RESTART, -1.0, -1.0, true}, + {API_VULKAN, OS_OSX, VENDOR_APPLE, DRIVER_PORTABILITY, Family::UNKNOWN, + BUG_BROKEN_DISCARD_WITH_EARLY_Z, -1.0, -1.0, true}, }; static std::map m_bugs; diff --git a/Source/Core/VideoCommon/DriverDetails.h b/Source/Core/VideoCommon/DriverDetails.h index f902f31ed3..70c39450c9 100644 --- a/Source/Core/VideoCommon/DriverDetails.h +++ b/Source/Core/VideoCommon/DriverDetails.h @@ -319,7 +319,15 @@ enum Bug // BUG: Multi-threaded shader pre-compilation sometimes crashes // Used primarily in Videoconfig.cpp's GetNumAutoShaderPreCompilerThreads() // refer to https://github.com/dolphin-emu/dolphin/pull/9414 for initial validation coverage - BUG_BROKEN_MULTITHREADED_SHADER_PRECOMPILATION + BUG_BROKEN_MULTITHREADED_SHADER_PRECOMPILATION, + + // BUG: Some driver and Apple Silicon GPU combinations have problems with fragment discard when + // early depth test is enabled. Discarded fragments may appear corrupted (Super Mario Sunshine, + // Sonic Adventure 2: Battle, Phantasy Star Online Epsiodes 1 & 2, etc). + // Affected devices: Apple Silicon GPUs of Apple family 4 and newer. + // Started version: -1 + // Ended version: -1 + BUG_BROKEN_DISCARD_WITH_EARLY_Z, }; // Initializes our internal vendor, device family, and driver version diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 4d0996e814..3aeb77d747 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -321,7 +321,9 @@ PixelShaderUid GetPixelShaderUid() BlendingState state = {}; state.Generate(bpmem); - if (state.usedualsrc && state.dstalpha && g_ActiveConfig.backend_info.bSupportsFramebufferFetch && + if (((state.usedualsrc && state.dstalpha) || + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z)) && + g_ActiveConfig.backend_info.bSupportsFramebufferFetch && !g_ActiveConfig.backend_info.bSupportsDualSourceBlend) { uid_data->blend_enable = state.blendenable; @@ -944,10 +946,15 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || uid_data->useDstAlpha); const bool use_shader_blend = - !use_dual_source && uid_data->useDstAlpha && host_config.backend_shader_framebuffer_fetch; + !use_dual_source && + (uid_data->useDstAlpha || + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z)) && + host_config.backend_shader_framebuffer_fetch; const bool use_shader_logic_op = !host_config.backend_logic_op && uid_data->logic_op_enable && host_config.backend_shader_framebuffer_fetch; - const bool use_framebuffer_fetch = use_shader_blend || use_shader_logic_op; + const bool use_framebuffer_fetch = + use_shader_blend || use_shader_logic_op || + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z); if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) { @@ -1869,9 +1876,23 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat // ZCOMPLOC HACK: if (!uid_data->alpha_test_use_zcomploc_hack) { - out.Write("\t\tdiscard;\n"); - if (api_type == APIType::D3D) +#ifdef __APPLE__ + if (uid_data->forced_early_z && + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z)) + { + // Instead of using discard, fetch the framebuffer's color value and use it as the output + // for this fragment. + out.Write("\t\t{} = float4(initial_ocol0.xyz, 1.0);\n", + use_dual_source ? "real_ocol0" : "ocol0"); out.Write("\t\treturn;\n"); + } + else +#endif + { + out.Write("\t\tdiscard;\n"); + if (api_type == APIType::D3D) + out.Write("\t\treturn;\n"); + } } out.Write("\t}}\n"); diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp index e3917e90e6..161f40146a 100644 --- a/Source/Core/VideoCommon/UberShaderPixel.cpp +++ b/Source/Core/VideoCommon/UberShaderPixel.cpp @@ -57,7 +57,9 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, const bool use_shader_blend = !use_dual_source && host_config.backend_shader_framebuffer_fetch; const bool use_shader_logic_op = !host_config.backend_logic_op && host_config.backend_shader_framebuffer_fetch; - const bool use_framebuffer_fetch = use_shader_blend || use_shader_logic_op; + const bool use_framebuffer_fetch = + use_shader_blend || use_shader_logic_op || + DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z); const bool early_depth = uid_data->early_depth != 0; const bool per_pixel_depth = uid_data->per_pixel_depth != 0; const bool bounding_box = host_config.bounding_box; @@ -1007,8 +1009,21 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, out.Write(" depth = float(zbuffer_zCoord) / 16777216.0;\n"); } - out.Write(" // Alpha Test\n" - " if (bpmem_alphaTest != 0u) {{\n" + out.Write(" // Alpha Test\n"); + + if (early_depth && DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z)) + { + // Instead of using discard, fetch the framebuffer's color value and use it as the output + // for this fragment. + out.Write(" #define discard_fragment {{ {} = float4(initial_ocol0.xyz, 1.0); return; }}\n", + use_shader_blend ? "real_ocol0" : "ocol0"); + } + else + { + out.Write(" #define discard_fragment discard\n"); + } + + out.Write(" if (bpmem_alphaTest != 0u) {{\n" " bool comp0 = alphaCompare(TevResult.a, " I_ALPHA ".r, {});\n", BitfieldExtract<&AlphaTest::comp0>("bpmem_alphaTest")); out.Write(" bool comp1 = alphaCompare(TevResult.a, " I_ALPHA ".g, {});\n", @@ -1019,13 +1034,13 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, " switch ({}) {{\n", BitfieldExtract<&AlphaTest::logic>("bpmem_alphaTest")); out.Write(" case 0u: // AND\n" - " if (comp0 && comp1) break; else discard; break;\n" + " if (comp0 && comp1) break; else discard_fragment; break;\n" " case 1u: // OR\n" - " if (comp0 || comp1) break; else discard; break;\n" + " if (comp0 || comp1) break; else discard_fragment; break;\n" " case 2u: // XOR\n" - " if (comp0 != comp1) break; else discard; break;\n" + " if (comp0 != comp1) break; else discard_fragment; break;\n" " case 3u: // XNOR\n" - " if (comp0 == comp1) break; else discard; break;\n" + " if (comp0 == comp1) break; else discard_fragment; break;\n" " }}\n" " }}\n" "\n");