From d41b49d8b4722c453ff1a13d2fae36dafae6d11d Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 25 Feb 2018 16:07:55 +0300 Subject: [PATCH] rsx/fp: Color output registers are always present and zero initialized - According to NV_fragment_program spec, registers are zero initialized always - A program even without writing to these registers will have black (0, 0, 0, 0) output Confirmed behaviour with MotorStorm games. Their engine uses this quirk to clear color buffers when doing depth replace Might be an unfixed game bug --- .../RSX/Common/FragmentProgramDecompiler.cpp | 94 ++++--------------- 1 file changed, 19 insertions(+), 75 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp index 10c8a7d134..2c31f31a90 100644 --- a/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp @@ -496,79 +496,6 @@ template std::string FragmentProgramDecompiler::GetSRC(T src) std::string FragmentProgramDecompiler::BuildCode() { - //Scan if any outputs are available - const bool use_32_bit_exports = !!(m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS); - const bool exports_depth = !!(m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); - const std::set output_values = - { - (use_32_bit_exports) ? "r0" : "h0", - "r1", - (use_32_bit_exports) ? "r2" : "h4", - (use_32_bit_exports) ? "r3" : "h6", - (use_32_bit_exports) ? "r4" : "h8", - }; - - bool gather_output_registers = true; - const auto float4_name = getFloatTypeName(4); - for (auto &v : output_values) - { - if (m_parr.HasParam(PF_PARAM_NONE, float4_name, v)) - { - gather_output_registers = false; - break; - } - } - - //Explicitly discard on encountering null shaders - if (gather_output_registers) - { - bool has_any_output = false; - bool first_output_exists = false; - - if (use_32_bit_exports || exports_depth) - { - for (int reg = 0; reg < 5; ++reg) - { - if (reg == 1 && !exports_depth) - continue; - - const std::string half_register = "h" + std::to_string(reg + 1); - if (m_parr.HasParam(PF_PARAM_NONE, float4_name, half_register)) - { - has_any_output = true; - if (!reg) first_output_exists = true; - - const std::string this_register = "r" + std::to_string(reg); - AddReg(reg, 0); - AddCode("//Register gather because output was not specified"); - AddCode(this_register + ".zw = gather(" + half_register + ");"); - } - } - } - - if (!has_any_output) - { - properties.has_no_output = true; - - LOG_ERROR(RSX, "Invalid fragment shader: No output register was updated!"); - - //Comment out main block as it is now useless - main = "/*\n" + main + "*/\n"; - AddCode("//No output, manually abort writes (nvidia+vulkan writes garbage otherwise)"); - AddCode("discard;"); - } - else - { - //Requires gather operation for output... - properties.has_gather_op = true; - - if (!first_output_exists) - { - LOG_WARNING(RSX, "Fragment shader does not write to first RTT and has no explicit output registers"); - } - } - } - std::stringstream OS; insertHeader(OS); OS << "\n"; @@ -830,10 +757,27 @@ std::string FragmentProgramDecompiler::Decompile() int forced_unit = FORCE_NONE; + //Add the output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z) + //This can be used instead of an explicit clear pass in some games (Motorstorm) + if (m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) + { + AddReg(0, CELL_GCM_FALSE); + AddReg(2, CELL_GCM_FALSE); + AddReg(3, CELL_GCM_FALSE); + AddReg(4, CELL_GCM_FALSE); + } + else + { + AddReg(0, CELL_GCM_TRUE); + AddReg(4, CELL_GCM_TRUE); + AddReg(6, CELL_GCM_TRUE); + AddReg(8, CELL_GCM_TRUE); + } + while (true) { for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); - found != m_end_offsets.end(); + found != m_end_offsets.end(); found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) { m_end_offsets.erase(found); @@ -843,7 +787,7 @@ std::string FragmentProgramDecompiler::Decompile() } for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); - found != m_else_offsets.end(); + found != m_else_offsets.end(); found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) { m_else_offsets.erase(found);