diff --git a/rpcs3/Emu/RSX/Common/GLSLCommon.h b/rpcs3/Emu/RSX/Common/GLSLCommon.h index d3c9d8395c..29cf509963 100644 --- a/rpcs3/Emu/RSX/Common/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Common/GLSLCommon.h @@ -383,12 +383,17 @@ namespace glsl OS << " if ((rop_control & 0xFF) != 0)\n" " {\n" - " bool alpha_test = (rop_control & 0x11) > 0;\n" + " bool alpha_test = (rop_control & 0x1) > 0;\n" " uint alpha_func = ((rop_control >> 16) & 0x7);\n" " bool srgb_convert = (rop_control & 0x2) > 0;\n\n" + " bool a2c_enabled = (rop_control & 0x10) > 0;\n" " if (alpha_test && !comparison_passes(" << reg0 << ".a, alpha_ref, alpha_func))\n" " {\n" " discard;\n" + " }\n" + " else if (a2c_enabled && !coverage_test_passes(" << reg0 << ", rop_control >> 5))\n" + " {\n" + " discard;\n" " }\n"; if (!_32_bit_exports) @@ -415,7 +420,9 @@ namespace glsl static void insert_glsl_legacy_function(std::ostream& OS, glsl::program_domain domain, bool require_lit_emulation, bool require_depth_conversion = false, bool require_wpos = false, bool require_texture_ops = true) { - OS << "#define _select mix\n\n"; + OS << "#define _select mix\n"; + OS << "#define _saturate(x) clamp(x, 0., 1.)\n"; + OS << "#define _rand(seed) fract(sin(dot(seed.xy, vec2(12.9898f, 78.233f))) * 43758.5453f)\n\n"; if (require_lit_emulation) { @@ -456,6 +463,23 @@ namespace glsl program_common::insert_compare_op(OS); + // NOTES: + // Lowers alpha accuracy down to 2 bits, to mimic A2C banding + // Alpha lower than the real threshold (e.g 0.25 for 4 samples) gets a randomized chance to make it to the lowest transparency state + // Helps to avoid A2C tested foliage disappearing in the distance + OS << + "bool coverage_test_passes(inout vec4 _sample, uint control)\n" + "{\n" + " if ((control & 0x1) == 0) return false;\n" + "\n" + " float samples = ((control & 0x2) != 0)? 4.f : 2.f;\n" + " float hash = _saturate(_rand(gl_FragCoord) + 0.5f) * 0.9f;\n" + " float epsilon = hash / samples;\n" + " float alpha = trunc((_sample.a + epsilon) * samples) / samples;\n" + " //_sample.a = min(_sample.a, alpha);\n" // Cannot blend A2C samples naively as they are order independent! Causes background bleeding + " return (alpha > 0.f);\n" + "}\n\n"; + if (require_depth_conversion) { //NOTE: Memory layout is fetched as byteswapped BGRA [GBAR] (GOW collection, DS2, DeS) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 1048b549db..abc7ef20d4 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1499,38 +1499,18 @@ void GLGSRender::update_draw_state() rsx::method_registers.blend_enabled_surface_3() }; - bool blend_equation_override = false; - if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && - !rsx::method_registers.alpha_test_enabled()) - { - if (rsx::method_registers.msaa_enabled() && - rsx::method_registers.msaa_sample_mask() && - rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample) - { - //fake alpha-to-coverage - //blend used in conjunction with alpha test to fake order-independent edge transparency - mrt_blend_enabled[0] = mrt_blend_enabled[1] = mrt_blend_enabled[2] = mrt_blend_enabled[3] = true; - blend_equation_override = true; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); - } - } - if (mrt_blend_enabled[0] || mrt_blend_enabled[1] || mrt_blend_enabled[2] || mrt_blend_enabled[3]) { - if (!blend_equation_override) - { - glBlendFuncSeparate(blend_factor(rsx::method_registers.blend_func_sfactor_rgb()), - blend_factor(rsx::method_registers.blend_func_dfactor_rgb()), - blend_factor(rsx::method_registers.blend_func_sfactor_a()), - blend_factor(rsx::method_registers.blend_func_dfactor_a())); + glBlendFuncSeparate(blend_factor(rsx::method_registers.blend_func_sfactor_rgb()), + blend_factor(rsx::method_registers.blend_func_dfactor_rgb()), + blend_factor(rsx::method_registers.blend_func_sfactor_a()), + blend_factor(rsx::method_registers.blend_func_dfactor_a())); - auto blend_colors = rsx::get_constant_blend_colors(); - glBlendColor(blend_colors[0], blend_colors[1], blend_colors[2], blend_colors[3]); + auto blend_colors = rsx::get_constant_blend_colors(); + glBlendColor(blend_colors[0], blend_colors[1], blend_colors[2], blend_colors[3]); - glBlendEquationSeparate(blend_equation(rsx::method_registers.blend_equation_rgb()), - blend_equation(rsx::method_registers.blend_equation_a())); - } + glBlendEquationSeparate(blend_equation(rsx::method_registers.blend_equation_rgb()), + blend_equation(rsx::method_registers.blend_equation_a())); } gl_state.enablei(mrt_blend_enabled[0], GL_BLEND, 0); diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index f61f612b07..82f09f3fbd 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -648,20 +648,21 @@ namespace rsx //TODO: Properly support alpha-to-coverage and alpha-to-one behavior in shaders auto fragment_alpha_func = rsx::method_registers.alpha_func(); auto alpha_ref = rsx::method_registers.alpha_ref() / 255.f; - auto rop_control = (u32)rsx::method_registers.alpha_test_enabled(); + auto rop_control = rsx::method_registers.alpha_test_enabled()? 1u : 0u; - if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && !rop_control) + if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && + rsx::method_registers.msaa_enabled() && + rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample) { - if (rsx::method_registers.msaa_enabled() && - rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample) - { - //alpha values generate a coverage mask for order independent blending - //requires hardware AA to work properly (or just fragment sample stage in fragment shaders) - //simulated using combined alpha blend and alpha test - fragment_alpha_func = rsx::comparison_function::greater; - alpha_ref = rsx::method_registers.msaa_sample_mask()? 0.25f : 0.f; - rop_control |= (1 << 4); - } + // Alpha values generate a coverage mask for order independent blending + // Requires hardware AA to work properly (or just fragment sample stage in fragment shaders) + // Simulated using combined alpha blend and alpha test + const u32 mask_bit = rsx::method_registers.msaa_sample_mask() ? 1u : 0u; + const u32 samples_bit = rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::diagonal_centered_2_samples ? 1u : 0u; + + rop_control |= (1u << 4); // CSAA enable bit + rop_control |= (mask_bit << 5); // MSAA mask enable bit + rop_control |= (samples_bit << 6); // Sample configuration bit } const f32 fog0 = rsx::method_registers.fog_params_0(); @@ -681,7 +682,7 @@ namespace rsx case rsx::surface_color_format::x32: break; default: - rop_control |= 0x2; + rop_control |= (1u << 1); break; } } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index d5ca439ffc..2455e4ad3f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2518,38 +2518,17 @@ bool VKGSRender::load_program() rsx::method_registers.blend_enabled_surface_3() }; - bool blend_equation_override = false; VkBlendFactor sfactor_rgb, sfactor_a, dfactor_rgb, dfactor_a; VkBlendOp equation_rgb, equation_a; - if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && - !rsx::method_registers.alpha_test_enabled()) - { - if (rsx::method_registers.msaa_enabled() && - rsx::method_registers.msaa_sample_mask() && - rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample) - { - //fake alpha-to-coverage - //blend used in conjunction with alpha test to fake order-independent edge transparency - mrt_blend_enabled[0] = mrt_blend_enabled[1] = mrt_blend_enabled[2] = mrt_blend_enabled[3] = true; - blend_equation_override = true; - sfactor_rgb = sfactor_a = VK_BLEND_FACTOR_SRC_ALPHA; - dfactor_rgb = dfactor_a = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - equation_rgb = equation_a = VK_BLEND_OP_ADD; - } - } - if (mrt_blend_enabled[0] || mrt_blend_enabled[1] || mrt_blend_enabled[2] || mrt_blend_enabled[3]) { - if (!blend_equation_override) - { - sfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_rgb()); - sfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_a()); - dfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_rgb()); - dfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_a()); - equation_rgb = vk::get_blend_op(rsx::method_registers.blend_equation_rgb()); - equation_a = vk::get_blend_op(rsx::method_registers.blend_equation_a()); - } + sfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_rgb()); + sfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_a()); + dfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_rgb()); + dfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_a()); + equation_rgb = vk::get_blend_op(rsx::method_registers.blend_equation_rgb()); + equation_a = vk::get_blend_op(rsx::method_registers.blend_equation_a()); for (u8 idx = 0; idx < m_draw_buffers.size(); ++idx) {