DriverDetails: Add broken discard with early-Z bug on Apple Silicon GPUs

This commit is contained in:
OatmealDome 2021-12-25 00:27:43 -05:00
parent e7f5e5172c
commit 259a5fc7c0
6 changed files with 70 additions and 16 deletions

View File

@ -133,7 +133,7 @@ static VkPipelineDepthStencilStateCreateInfo GetVulkanDepthStencilState(const De
} }
static VkPipelineColorBlendAttachmentState static VkPipelineColorBlendAttachmentState
GetVulkanAttachmentBlendState(const BlendingState& state) GetVulkanAttachmentBlendState(const BlendingState& state, AbstractPipelineUsage usage)
{ {
VkPipelineColorBlendAttachmentState vk_state = {}; VkPipelineColorBlendAttachmentState vk_state = {};
@ -143,7 +143,8 @@ GetVulkanAttachmentBlendState(const BlendingState& state)
bool use_shader_blend = !use_dual_source && state.usedualsrc && state.dstalpha && bool use_shader_blend = !use_dual_source && state.usedualsrc && state.dstalpha &&
g_ActiveConfig.backend_info.bSupportsFramebufferFetch; 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; vk_state.blendEnable = VK_FALSE;
} }
@ -349,7 +350,7 @@ std::unique_ptr<VKPipeline> VKPipeline::Create(const AbstractPipelineConfig& con
VkPipelineDepthStencilStateCreateInfo depth_stencil_state = VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
GetVulkanDepthStencilState(config.depth_state); GetVulkanDepthStencilState(config.depth_state);
VkPipelineColorBlendAttachmentState blend_attachment_state = VkPipelineColorBlendAttachmentState blend_attachment_state =
GetVulkanAttachmentBlendState(config.blending_state); GetVulkanAttachmentBlendState(config.blending_state, config.usage);
VkPipelineColorBlendStateCreateInfo blend_state = VkPipelineColorBlendStateCreateInfo blend_state =
GetVulkanColorBlendState(config.blending_state, &blend_attachment_state, 1); GetVulkanColorBlendState(config.blending_state, &blend_attachment_state, 1);

View File

@ -371,6 +371,13 @@ void VulkanContext::PopulateBackendInfoFeatures(VideoConfig* config, VkPhysicalD
// with depth clamping. Fall back to inverted depth range for these. // with depth clamping. Fall back to inverted depth range for these.
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_REVERSED_DEPTH_RANGE)) if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_REVERSED_DEPTH_RANGE))
config->backend_info.bSupportsReversedDepthRange = false; 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( void VulkanContext::PopulateBackendInfoMultisampleModes(

View File

@ -140,6 +140,8 @@ constexpr BugInfo m_known_bugs[] = {
-1.0, -1.0, true}, -1.0, -1.0, true},
{API_VULKAN, OS_ALL, VENDOR_QUALCOMM, DRIVER_QUALCOMM, Family::UNKNOWN, BUG_PRIMITIVE_RESTART, {API_VULKAN, OS_ALL, VENDOR_QUALCOMM, DRIVER_QUALCOMM, Family::UNKNOWN, BUG_PRIMITIVE_RESTART,
-1.0, -1.0, true}, -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<Bug, BugInfo> m_bugs; static std::map<Bug, BugInfo> m_bugs;

View File

@ -319,7 +319,15 @@ enum Bug
// BUG: Multi-threaded shader pre-compilation sometimes crashes // BUG: Multi-threaded shader pre-compilation sometimes crashes
// Used primarily in Videoconfig.cpp's GetNumAutoShaderPreCompilerThreads() // Used primarily in Videoconfig.cpp's GetNumAutoShaderPreCompilerThreads()
// refer to https://github.com/dolphin-emu/dolphin/pull/9414 for initial validation coverage // 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 // Initializes our internal vendor, device family, and driver version

View File

@ -321,7 +321,9 @@ PixelShaderUid GetPixelShaderUid()
BlendingState state = {}; BlendingState state = {};
state.Generate(bpmem); 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) !g_ActiveConfig.backend_info.bSupportsDualSourceBlend)
{ {
uid_data->blend_enable = state.blendenable; 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) || (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) ||
uid_data->useDstAlpha); uid_data->useDstAlpha);
const bool use_shader_blend = 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 && const bool use_shader_logic_op = !host_config.backend_logic_op && uid_data->logic_op_enable &&
host_config.backend_shader_framebuffer_fetch; 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) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
@ -1868,11 +1875,25 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
// ZCOMPLOC HACK: // ZCOMPLOC HACK:
if (!uid_data->alpha_test_use_zcomploc_hack) if (!uid_data->alpha_test_use_zcomploc_hack)
{
#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"); out.Write("\t\tdiscard;\n");
if (api_type == APIType::D3D) if (api_type == APIType::D3D)
out.Write("\t\treturn;\n"); out.Write("\t\treturn;\n");
} }
}
out.Write("\t}}\n"); out.Write("\t}}\n");
} }

View File

@ -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_blend = !use_dual_source && host_config.backend_shader_framebuffer_fetch;
const bool use_shader_logic_op = const bool use_shader_logic_op =
!host_config.backend_logic_op && host_config.backend_shader_framebuffer_fetch; !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 early_depth = uid_data->early_depth != 0;
const bool per_pixel_depth = uid_data->per_pixel_depth != 0; const bool per_pixel_depth = uid_data->per_pixel_depth != 0;
const bool bounding_box = host_config.bounding_box; 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(" depth = float(zbuffer_zCoord) / 16777216.0;\n");
} }
out.Write(" // Alpha Test\n" out.Write(" // Alpha Test\n");
" if (bpmem_alphaTest != 0u) {{\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", " bool comp0 = alphaCompare(TevResult.a, " I_ALPHA ".r, {});\n",
BitfieldExtract<&AlphaTest::comp0>("bpmem_alphaTest")); BitfieldExtract<&AlphaTest::comp0>("bpmem_alphaTest"));
out.Write(" bool comp1 = alphaCompare(TevResult.a, " I_ALPHA ".g, {});\n", 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", " switch ({}) {{\n",
BitfieldExtract<&AlphaTest::logic>("bpmem_alphaTest")); BitfieldExtract<&AlphaTest::logic>("bpmem_alphaTest"));
out.Write(" case 0u: // AND\n" 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" " 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" " 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" " case 3u: // XNOR\n"
" if (comp0 == comp1) break; else discard; break;\n" " if (comp0 == comp1) break; else discard_fragment; break;\n"
" }}\n" " }}\n"
" }}\n" " }}\n"
"\n"); "\n");