mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-21 03:55:32 +00:00
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:
parent
6fdc0fd7f0
commit
736415fcd9
3 changed files with 73 additions and 25 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue