diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp index 36fc6addc1..62979e3fee 100644 --- a/Source/Core/VideoCommon/UberShaderPixel.cpp +++ b/Source/Core/VideoCommon/UberShaderPixel.cpp @@ -55,6 +55,13 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, const bool stereo = host_config.stereo; const bool use_dual_source = host_config.backend_dual_source_blend; const bool use_shader_blend = !use_dual_source && host_config.backend_shader_framebuffer_fetch; + const bool use_shader_logic_op = +#ifdef __APPLE__ + !host_config.backend_logic_op && host_config.backend_shader_framebuffer_fetch; +#else + false; +#endif + const bool use_framebuffer_fetch = use_shader_blend || use_shader_logic_op; 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; @@ -71,7 +78,37 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, // Shader inputs/outputs in GLSL (HLSL is in main). if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) { +#ifdef __APPLE__ + // Framebuffer fetch is only supported by Metal, so ensure that we're running Vulkan (MoltenVK) + // if we want to use it. + if (api_type == APIType::Vulkan) + { + if (use_dual_source) + { + out.Write("FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 0) out vec4 ocol0;\n" + "FRAGMENT_OUTPUT_LOCATION_INDEXED(0, 1) out vec4 ocol1;\n"); + } + else if (use_shader_blend) + { + // Metal doesn't support a single unified variable for both input and output, so we declare + // the output separately. The input will be defined later below. + out.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 real_ocol0;\n"); + } + else + { + out.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n"); + } + + if (use_framebuffer_fetch) + { + // Subpass inputs will be converted to framebuffer fetch by SPIRV-Cross. + out.Write("INPUT_ATTACHMENT_BINDING(0, 0, 0) uniform subpassInput in_ocol0;\n"); + } + } + else if (use_dual_source) +#else if (use_dual_source) +#endif { if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_FRAGMENT_SHADER_INDEX_DECORATION)) { @@ -679,11 +716,16 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, out.Write("void main()\n{{\n"); out.Write(" float4 rawpos = gl_FragCoord;\n"); - if (use_shader_blend) + + if (use_framebuffer_fetch) { // Store off a copy of the initial fb value for blending - out.Write(" float4 initial_ocol0 = FB_FETCH_VALUE;\n" - " float4 ocol0;\n" + out.Write(" float4 initial_ocol0 = FB_FETCH_VALUE;\n"); + } + + if (use_shader_blend) + { + out.Write(" float4 ocol0;\n" " float4 ocol1;\n"); } } @@ -1243,6 +1285,40 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, " }}\n" "\n"); + if (use_shader_logic_op) + { + static constexpr std::array logic_op_mode{ + "int4(0, 0, 0, 0)", // CLEAR + "TevResult & fb_value", // AND + "TevResult & ~fb_value", // AND_REVERSE + "TevResult", // COPY + "~TevResult & fb_value", // AND_INVERTED + "fb_value", // NOOP + "TevResult ^ fb_value", // XOR + "TevResult | fb_value", // OR + "~(TevResult | fb_value)", // NOR + "~(TevResult ^ fb_value)", // EQUIV + "~fb_value", // INVERT + "TevResult | ~fb_value", // OR_REVERSE + "~TevResult", // COPY_INVERTED + "~TevResult | fb_value", // OR_INVERTED + "~(TevResult & fb_value)", // NAND + "int4(255, 255, 255, 255)", // SET + }; + + out.Write(" // Logic Ops\n" + " if (logic_op_enable) {{\n" + " int4 fb_value = iround(initial_ocol0 * 255.0);" + " switch (logic_op_mode) {{\n"); + for (size_t i = 0; i < logic_op_mode.size(); i++) + { + out.Write(" case {}u: TevResult = {}; break;\n", i, logic_op_mode[i]); + } + + out.Write(" }}\n" + " }}\n"); + } + // D3D requires that the shader outputs be uint when writing to a uint render target for logic op. if (api_type == APIType::D3D && uid_data->uint_output) {