rsx/fp: Detect broken/NOP shaders automatically

- Do not compile body if the shader is of no consequence, leave as a passthrough shader
This commit is contained in:
kd-11 2019-01-18 12:23:30 +03:00 committed by kd-11
parent 6fdc0fd7f0
commit 736415fcd9
3 changed files with 73 additions and 25 deletions

View file

@ -482,6 +482,59 @@ template<typename T> std::string FragmentProgramDecompiler::GetSRC(T src)
std::string FragmentProgramDecompiler::BuildCode()
{
// Shader validation
// Shader must at least write to one output for the body to be considered valid
const std::string vec4_type = getFloatTypeName(4);
const std::string init_value = vec4_type + "(0., 0., 0., 0.)";
std::array<std::string, 4> output_register_names;
std::array<u32, 4> ouput_register_indices = { 0, 2, 3, 4 };
bool shader_is_valid = false;
// Check depth export
if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT)
{
if (shader_is_valid = !!temp_registers[1].h0_writes; !shader_is_valid)
{
LOG_WARNING(RSX, "Fragment shader fails to write the depth value!");
}
}
// Add the color 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)
{
output_register_names = { "r0", "r2", "r3", "r4" };
}
else
{
output_register_names = { "h0", "h4", "h6", "h8" };
}
for (int n = 0; n < 4; ++n)
{
if (!m_parr.HasParam(PF_PARAM_NONE, vec4_type, output_register_names[n]))
{
m_parr.AddParam(PF_PARAM_NONE, vec4_type, output_register_names[n], init_value);
continue;
}
const auto block_index = ouput_register_indices[n];
shader_is_valid |= (!!temp_registers[block_index].h0_writes);
}
if (!shader_is_valid)
{
properties.has_no_output = true;
if (!properties.has_discard_op)
{
// NOTE: Discard operation overrides output
LOG_WARNING(RSX, "Shader does not write to any output register and will be NOPed");
main = "/*" + main + "*/";
}
}
std::stringstream OS;
insertHeader(OS);
OS << "\n";
@ -765,23 +818,6 @@ 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);
@ -888,7 +924,10 @@ std::string FragmentProgramDecompiler::Decompile()
switch (opcode)
{
case RSX_FP_OPCODE_NOP: break;
case RSX_FP_OPCODE_KIL: AddFlowOp("discard"); break;
case RSX_FP_OPCODE_KIL:
properties.has_discard_op = true;
AddFlowOp("discard");
break;
default:
int prev_force_unit = forced_unit;

View file

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "ShaderParam.h"
#include "Emu/RSX/RSXFragmentProgram.h"
#include <sstream>
@ -28,6 +28,9 @@ class FragmentProgramDecompiler
u32 real_index = UINT32_MAX;
u32 h0_writes = 0u; // Number of writes to the first 64-bits of the register
u32 h1_writes = 0u; // Number of writes to the last 64-bits of the register
void tag(u32 index, bool half_register, bool x, bool y, bool z, bool w)
{
if (half_register)
@ -40,6 +43,7 @@ class FragmentProgramDecompiler
if (w) last_write_half[3] = true;
aliased_h1 = true;
h1_writes++;
}
else
{
@ -49,6 +53,7 @@ class FragmentProgramDecompiler
if (w) last_write_half[1] = true;
aliased_h0 = true;
h0_writes++;
}
}
else
@ -59,6 +64,9 @@ class FragmentProgramDecompiler
if (w) last_write_half[3] = false;
aliased_r0 = true;
h0_writes++;
h1_writes++;
}
if (real_index == UINT32_MAX)
@ -247,6 +255,7 @@ public:
bool has_gather_op = false;
bool has_wpos_input = false;
bool has_no_output = false;
bool has_discard_op = false;
}
properties;

View file

@ -67,8 +67,8 @@ enum ParamFlag
struct ParamItem
{
std::string name;
std::string value;
const std::string name;
const std::string value;
int location;
ParamItem(const std::string& _name, int _location, const std::string& _value = "")
@ -81,7 +81,7 @@ struct ParamItem
struct ParamType
{
const ParamFlag flag;
std::string type;
const std::string type;
std::vector<ParamItem> items;
ParamType(const ParamFlag _flag, const std::string& _type)
@ -127,13 +127,13 @@ struct ParamArray
return false;
}
bool HasParam(const ParamFlag flag, std::string type, const std::string& name)
bool HasParam(const ParamFlag flag, const std::string& type, const std::string& name)
{
ParamType* t = SearchParam(flag, type);
return t && t->SearchName(name);
}
std::string AddParam(const ParamFlag flag, std::string type, const std::string& name, const std::string& value)
std::string AddParam(const ParamFlag flag, const std::string& type, const std::string& name, const std::string& value)
{
ParamType* t = SearchParam(flag, type);
@ -150,7 +150,7 @@ struct ParamArray
return name;
}
std::string AddParam(const ParamFlag flag, std::string type, const std::string& name, int location = -1)
std::string AddParam(const ParamFlag flag, const std::string& type, const std::string& name, int location = -1)
{
ParamType* t = SearchParam(flag, type);