rsx: Improve fragment and vertex program usage

- Introduces a gpu program analyser step to examine shader contents before attempting compilation or cache search
  - Avoids detecting shader as being different because of unused textures having state changes
  - Adds better program size detection for vertex programs
- Improved vertex program decompiler
  - Properly support CAL type instructions
  - Support jumping over instructions marked with a termination marker with BRA/CAL class opcodes
  - Fix SRC checks and abort
  - Fix CC register initialization
  - NOTE: Even unused SRC registers have to be valid (usually referencing in.POS)
This commit is contained in:
kd-11 2018-03-20 14:14:45 +03:00 committed by kd-11
parent 75b40931fc
commit a52ea7f870
9 changed files with 287 additions and 206 deletions

View file

@ -22,6 +22,46 @@ size_t vertex_program_utils::get_vertex_program_ucode_hash(const RSXVertexProgra
return hash; return hash;
} }
vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vertex_program(const std::vector<u32>& data)
{
u32 ucode_size = 0;
u32 current_instrution = 0;
u32 last_instruction_address = 0;
D3 d3;
D2 d2;
D1 d1;
for (; ucode_size < data.size(); ucode_size += 4)
{
d1.HEX = data[ucode_size + 1];
d3.HEX = data[ucode_size + 3];
switch (d1.sca_opcode)
{
case RSX_SCA_OPCODE_BRI:
case RSX_SCA_OPCODE_BRB:
case RSX_SCA_OPCODE_CAL:
case RSX_SCA_OPCODE_CLI:
case RSX_SCA_OPCODE_CLB:
{
d2.HEX = data[ucode_size + 2];
u32 jump_address = ((d2.iaddrh << 3) | d3.iaddrl) * 4;
last_instruction_address = std::max(last_instruction_address, jump_address);
break;
}
}
if (d3.end && (ucode_size >= last_instruction_address))
{
//Jumping over an end label is legal (verified)
break;
}
}
return{ ucode_size + 4 };
}
size_t vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const size_t vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const
{ {
size_t hash = vertex_program_utils::get_vertex_program_ucode_hash(program); size_t hash = vertex_program_utils::get_vertex_program_ucode_hash(program);
@ -84,21 +124,56 @@ size_t fragment_program_utils::get_fragment_program_ucode_size(void *ptr)
} }
} }
u32 fragment_program_utils::get_fragment_program_start(void *ptr) fragment_program_utils::fragment_program_metadata fragment_program_utils::analyse_fragment_program(void *ptr)
{ {
const qword *instBuffer = (const qword*)ptr; const qword *instBuffer = (const qword*)ptr;
size_t instIndex = 0; size_t instIndex = 0;
s32 program_offset = -1;
u16 textures_mask = 0;
while (true) while (true)
{ {
const qword& inst = instBuffer[instIndex]; const qword& inst = instBuffer[instIndex];
u32 opcode = inst.word[0] >> 16 & 0x3F; const u32 opcode = (inst.word[0] >> 16) & 0x3F;
if (opcode) if (opcode)
{
if (program_offset < 0)
program_offset = instIndex * 16;
if (opcode == RSX_FP_OPCODE_TEX ||
opcode == RSX_FP_OPCODE_TEXBEM ||
opcode == RSX_FP_OPCODE_TXP ||
opcode == RSX_FP_OPCODE_TXPBEM ||
opcode == RSX_FP_OPCODE_TXD ||
opcode == RSX_FP_OPCODE_TXB ||
opcode == RSX_FP_OPCODE_TXL)
{
//Bits 17-20 of word 1, swapped within u16 sections
//Bits 16-23 are swapped into the upper 8 bits (24-31)
const u32 tex_num = (inst.word[0] >> 25) & 15;
textures_mask |= (1 << tex_num);
}
if (is_constant(inst.word[1]) || is_constant(inst.word[2]) || is_constant(inst.word[3]))
{
//Instruction references constant, skip one slot occupied by data
instIndex++;
}
}
if ((inst.word[0] >> 8) & 0x1)
{
if (program_offset < 0)
program_offset = instIndex * 16;
break; break;
}
instIndex++; instIndex++;
} }
return instIndex * 16; return{ (u32)program_offset, textures_mask };
} }
size_t fragment_program_utils::get_fragment_program_ucode_hash(const RSXFragmentProgram& program) size_t fragment_program_utils::get_fragment_program_ucode_hash(const RSXFragmentProgram& program)

View file

@ -26,7 +26,14 @@ namespace program_hash_util
struct vertex_program_utils struct vertex_program_utils
{ {
struct vertex_program_metadata
{
u32 ucode_size;
};
static size_t get_vertex_program_ucode_hash(const RSXVertexProgram &program); static size_t get_vertex_program_ucode_hash(const RSXVertexProgram &program);
static vertex_program_metadata analyse_vertex_program(const std::vector<u32>& data);
}; };
struct vertex_program_storage_hash struct vertex_program_storage_hash
@ -41,6 +48,12 @@ namespace program_hash_util
struct fragment_program_utils struct fragment_program_utils
{ {
struct fragment_program_metadata
{
u32 program_start_offset;
u16 referenced_textures_mask;
};
/** /**
* returns true if the given source Operand is a constant * returns true if the given source Operand is a constant
*/ */
@ -48,7 +61,7 @@ namespace program_hash_util
static size_t get_fragment_program_ucode_size(void *ptr); static size_t get_fragment_program_ucode_size(void *ptr);
static u32 get_fragment_program_start(void *ptr); static fragment_program_metadata analyse_fragment_program(void *ptr);
static size_t get_fragment_program_ucode_hash(const RSXFragmentProgram &program); static size_t get_fragment_program_ucode_hash(const RSXFragmentProgram &program);
}; };

View file

@ -158,7 +158,7 @@ void VertexProgramDecompiler::SetDST(bool is_sca, std::string value)
if (d0.cond_update_enable_0 || d0.cond_update_enable_1) if (d0.cond_update_enable_0 || d0.cond_update_enable_1)
{ {
dest = m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(d0.cond_reg_sel_1), getFloatTypeName(4) + "(0., 0., 0., 0.)") + mask; dest = AddCondReg() + mask;
} }
else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f)) else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f))
{ {
@ -174,24 +174,6 @@ void VertexProgramDecompiler::SetDST(bool is_sca, std::string value)
AddCodeCond(Format(dest), value); AddCodeCond(Format(dest), value);
} }
std::string VertexProgramDecompiler::GetFunc()
{
std::string name = "func$a";
for (const auto& func : m_funcs) {
if (func.name.compare(name) == 0) {
return name + "()";
}
}
m_funcs.emplace_back();
FuncInfo &idx = m_funcs.back();
idx.offset = GetAddr();
idx.name = name;
return name + "()";
}
std::string VertexProgramDecompiler::GetTex() std::string VertexProgramDecompiler::GetTex()
{ {
return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(d2.tex_num)); return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(d2.tex_num));
@ -210,17 +192,13 @@ std::string VertexProgramDecompiler::Format(const std::string& code)
{ "$am", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrMask), this) }, { "$am", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrMask), this) },
{ "$a", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrReg), this) }, { "$a", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrReg), this) },
{ "$vm", std::bind(std::mem_fn(&VertexProgramDecompiler::GetVecMask), this) }, { "$vm", std::bind(std::mem_fn(&VertexProgramDecompiler::GetVecMask), this) },
{ "$t", std::bind(std::mem_fn(&VertexProgramDecompiler::GetTex), this) }, { "$t", std::bind(std::mem_fn(&VertexProgramDecompiler::GetTex), this) },
{ "$fa", [this]()->std::string { return std::to_string(GetAddr()); } },
{ "$f()", std::bind(std::mem_fn(&VertexProgramDecompiler::GetFunc), this) },
{ "$ifcond ", [this]() -> std::string { "$ifcond ", [this]() -> std::string
{ {
const std::string& cond = GetCond(); const std::string& cond = GetCond();
if (cond == "true") return ""; if (cond == "true") return "";
return "if(" + cond + ") "; return "if(" + cond + ") ";
} }
}, },
{ "$cond", std::bind(std::mem_fn(&VertexProgramDecompiler::GetCond), this) }, { "$cond", std::bind(std::mem_fn(&VertexProgramDecompiler::GetCond), this) },
{ "$ifbcond", std::bind(std::mem_fn(&VertexProgramDecompiler::GetOptionalBranchCond), this) } { "$ifbcond", std::bind(std::mem_fn(&VertexProgramDecompiler::GetOptionalBranchCond), this) }
@ -261,7 +239,7 @@ std::string VertexProgramDecompiler::GetCond()
swizzle += f[d0.mask_w]; swizzle += f[d0.mask_w];
swizzle = swizzle == "xyzw" ? "" : "." + swizzle; swizzle = swizzle == "xyzw" ? "" : "." + swizzle;
return "any(" + compareFunction(cond_string_table[d0.cond], "cc" + std::to_string(d0.cond_reg_sel_1) + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)" + swizzle) + ")"; return "any(" + compareFunction(cond_string_table[d0.cond], AddCondReg() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)" + swizzle) + ")";
} }
std::string VertexProgramDecompiler::GetOptionalBranchCond() std::string VertexProgramDecompiler::GetOptionalBranchCond()
@ -315,7 +293,7 @@ void VertexProgramDecompiler::AddCodeCond(const std::string& dst, const std::str
swizzle = swizzle == "xyzw" ? "" : "." + swizzle; swizzle = swizzle == "xyzw" ? "" : "." + swizzle;
std::string cond = compareFunction(cond_string_table[d0.cond], "cc" + std::to_string(d0.cond_reg_sel_1) + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); std::string cond = compareFunction(cond_string_table[d0.cond], AddCondReg() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)");
ShaderVariable dst_var(dst); ShaderVariable dst_var(dst);
dst_var.symplify(); dst_var.symplify();
@ -353,6 +331,11 @@ std::string VertexProgramDecompiler::AddAddrRegWithoutMask()
return m_parr.AddParam(PF_PARAM_NONE, getIntTypeName(4), "a" + std::to_string(d0.addr_reg_sel_1), getIntTypeName(4) + "(0, 0, 0, 0)"); return m_parr.AddParam(PF_PARAM_NONE, getIntTypeName(4), "a" + std::to_string(d0.addr_reg_sel_1), getIntTypeName(4) + "(0, 0, 0, 0)");
} }
std::string VertexProgramDecompiler::AddCondReg()
{
return m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(d0.cond_reg_sel_1), getFloatTypeName(4) + "(0., 0., 0., 0.)");
}
u32 VertexProgramDecompiler::GetAddr() u32 VertexProgramDecompiler::GetAddr()
{ {
return (d2.iaddrh << 3) | d3.iaddrl; return (d2.iaddrh << 3) | d3.iaddrl;
@ -379,37 +362,6 @@ std::string VertexProgramDecompiler::NotZeroPositive(const std::string& code)
return "max(" + code + ", 0.0000000001)"; return "max(" + code + ", 0.0000000001)";
} }
std::string VertexProgramDecompiler::BuildFuncBody(const FuncInfo& func)
{
std::string result;
for (uint i = func.offset; i<m_body.size(); ++i)
{
if (i != func.offset)
{
uint call_func = -1;
for (uint j = 0; j<m_funcs.size(); ++j)
{
if (m_funcs[j].offset == i)
{
call_func = j;
break;
}
}
if (call_func != -1)
{
result += '\t' + m_funcs[call_func].name + "();\n";
break;
}
}
result += '\t' + m_body[i] + '\n';
}
return result;
}
std::string VertexProgramDecompiler::BuildCode() std::string VertexProgramDecompiler::BuildCode()
{ {
std::string main_body; std::string main_body;
@ -459,20 +411,14 @@ std::string VertexProgramDecompiler::BuildCode()
VertexProgramDecompiler::VertexProgramDecompiler(const RSXVertexProgram& prog) : VertexProgramDecompiler::VertexProgramDecompiler(const RSXVertexProgram& prog) :
m_data(prog.data) m_data(prog.data)
{ {
m_funcs.emplace_back();
m_funcs[0].offset = 0;
m_funcs[0].name = "main";
m_funcs.emplace_back();
m_funcs[1].offset = 0;
m_funcs[1].name = "func0";
//m_cur_func->body = "\tgl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n";
} }
std::string VertexProgramDecompiler::Decompile() std::string VertexProgramDecompiler::Decompile()
{ {
for (unsigned i = 0; i < PF_PARAM_COUNT; i++) for (unsigned i = 0; i < PF_PARAM_COUNT; i++)
m_parr.params[i].clear(); m_parr.params[i].clear();
m_instr_count = 0;
m_instr_count = m_data.size() / 4;
for (int i = 0; i < m_max_instr_count; ++i) for (int i = 0; i < m_max_instr_count; ++i)
{ {
@ -480,11 +426,11 @@ std::string VertexProgramDecompiler::Decompile()
} }
bool is_has_BRA = false; bool is_has_BRA = false;
bool program_end = false;
u32 i = 1;
for (u32 i = 1; m_instr_count < m_max_instr_count; m_instr_count++) while (i < m_data.size())
{ {
m_cur_instr = &m_instructions[m_instr_count];
if (is_has_BRA) if (is_has_BRA)
{ {
d3.HEX = m_data[i]; d3.HEX = m_data[i];
@ -497,6 +443,7 @@ std::string VertexProgramDecompiler::Decompile()
switch (d1.sca_opcode) switch (d1.sca_opcode)
{ {
case RSX_SCA_OPCODE_BRA: case RSX_SCA_OPCODE_BRA:
LOG_ERROR(RSX, "Unimplemented VP opcode BRA");
is_has_BRA = true; is_has_BRA = true;
m_jump_lvls.clear(); m_jump_lvls.clear();
d3.HEX = m_data[++i]; d3.HEX = m_data[++i];
@ -505,6 +452,9 @@ std::string VertexProgramDecompiler::Decompile()
case RSX_SCA_OPCODE_BRB: case RSX_SCA_OPCODE_BRB:
case RSX_SCA_OPCODE_BRI: case RSX_SCA_OPCODE_BRI:
case RSX_SCA_OPCODE_CAL:
case RSX_SCA_OPCODE_CLI:
case RSX_SCA_OPCODE_CLB:
d2.HEX = m_data[i++]; d2.HEX = m_data[i++];
d3.HEX = m_data[i]; d3.HEX = m_data[i];
i += 2; i += 2;
@ -517,22 +467,9 @@ std::string VertexProgramDecompiler::Decompile()
break; break;
} }
} }
if (d3.end)
{
m_instr_count++;
if (i < m_data.size())
{
LOG_ERROR(RSX, "Program end before buffer end.");
}
break;
}
} }
uint jump_position = 0; uint jump_position = 0;
if (is_has_BRA || !m_jump_lvls.empty()) if (is_has_BRA || !m_jump_lvls.empty())
{ {
m_cur_instr = &m_instructions[0]; m_cur_instr = &m_instructions[0];
@ -561,9 +498,58 @@ std::string VertexProgramDecompiler::Decompile()
return jump; return jump;
}; };
for (u32 i = 0; i < m_instr_count; ++i) auto do_function_call = [this, &i](const std::string& condition)
{ {
m_cur_instr = &m_instructions[i]; //call function
m_call_stack.push(i+1);
AddCode(condition);
AddCode("{");
m_cur_instr->open_scopes++;
i = GetAddr();
};
auto do_function_return = [this, &i]()
{
if (!m_call_stack.empty())
{
//TODO: Conditional returns
i = m_call_stack.top();
m_call_stack.pop();
m_cur_instr->close_scopes++;
AddCode("}");
}
else
{
AddCode("$ifcond return");
}
};
auto do_program_exit = [this, do_function_return, &i](bool abort)
{
if (abort)
{
AddCode("//ABORT");
}
while (!m_call_stack.empty())
{
LOG_ERROR(RSX, "vertex program end in subroutine call!");
do_function_return();
}
if ((i + 1) < m_instr_count)
{
//Forcefully exit
AddCode("return;");
}
};
for (i = 0; i < m_instr_count; ++i)
{
if (m_call_stack.empty())
{
m_cur_instr = &m_instructions[i];
}
d0.HEX = m_data[i * 4 + 0]; d0.HEX = m_data[i * 4 + 0];
d1.HEX = m_data[i * 4 + 1]; d1.HEX = m_data[i * 4 + 1];
@ -576,22 +562,30 @@ std::string VertexProgramDecompiler::Decompile()
src[2].src2l = d3.src2l; src[2].src2l = d3.src2l;
src[2].src2h = d2.src2h; src[2].src2h = d2.src2h;
if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end())) if (!src[0].reg_type || !src[1].reg_type || !src[2].reg_type)
{ {
m_cur_instr->close_scopes++; AddCode("//Src check failed. Aborting");
AddCode("}"); do_program_exit(true);
AddCode(""); break;
AddCode(fmt::format("if (jump_position <= %u)", jump_position++));
AddCode("{");
m_cur_instr->open_scopes++;
} }
if (!d1.sca_opcode && !d1.vec_opcode) if (m_call_stack.empty())
{ {
AddCode("//nop"); //TODO: Subroutines can also have arbitrary jumps!
if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end()))
{
m_cur_instr->close_scopes++;
AddCode("}");
AddCode("");
AddCode(fmt::format("if (jump_position <= %u)", jump_position++));
AddCode("{");
m_cur_instr->open_scopes++;
}
} }
program_end = !!d3.end;
switch (d1.vec_opcode) switch (d1.vec_opcode)
{ {
case RSX_VEC_OPCODE_NOP: break; case RSX_VEC_OPCODE_NOP: break;
@ -624,7 +618,7 @@ std::string VertexProgramDecompiler::Decompile()
default: default:
AddCode(fmt::format("//Unknown vp opcode 0x%x", u32{ d1.vec_opcode })); AddCode(fmt::format("//Unknown vp opcode 0x%x", u32{ d1.vec_opcode }));
LOG_ERROR(RSX, "Unknown vp opcode 0x%x", u32{ d1.vec_opcode }); LOG_ERROR(RSX, "Unknown vp opcode 0x%x", u32{ d1.vec_opcode });
Emu.Pause(); program_end = true;
break; break;
} }
@ -644,39 +638,58 @@ std::string VertexProgramDecompiler::Decompile()
break; break;
case RSX_SCA_OPCODE_BRA: case RSX_SCA_OPCODE_BRA:
{ {
AddCode("$if ($cond) //BRA"); if (m_call_stack.empty())
AddCode("{"); {
m_cur_instr->open_scopes++; AddCode("$if ($cond) //BRA");
AddCode("jump_position = $a$am;"); AddCode("{");
AddCode("continue;"); m_cur_instr->open_scopes++;
m_cur_instr->close_scopes++; AddCode("jump_position = $a$am;");
AddCode("}"); AddCode("continue;");
m_cur_instr->close_scopes++;
AddCode("}");
}
else
{
//TODO
LOG_ERROR(RSX, "BRA opcode found in subroutine!");
}
} }
break; break;
case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;) case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;)
{ {
u32 jump_position = find_jump_lvl(GetAddr()); if (m_call_stack.empty())
{
u32 jump_position = find_jump_lvl(GetAddr());
AddCode("$ifcond //BRI"); AddCode("$ifcond //BRI");
AddCode("{"); AddCode("{");
m_cur_instr->open_scopes++; m_cur_instr->open_scopes++;
AddCode(fmt::format("jump_position = %u;", jump_position)); AddCode(fmt::format("jump_position = %u;", jump_position));
AddCode("continue;"); AddCode("continue;");
m_cur_instr->close_scopes++; m_cur_instr->close_scopes++;
AddCode("}"); AddCode("}");
}
else
{
//TODO
LOG_ERROR(RSX, "BRI opcode found in subroutine!");
}
} }
break; break;
case RSX_SCA_OPCODE_CAL: case RSX_SCA_OPCODE_CAL:
// works same as BRI // works same as BRI
AddCode("$ifcond $f(); //CAL"); AddCode("//CAL");
do_function_call("$ifcond");
break; break;
case RSX_SCA_OPCODE_CLI: case RSX_SCA_OPCODE_CLI:
// works same as BRI // works same as BRI
AddCode("$ifcond $f(); //CLI"); LOG_ERROR(RSX, "Unimplemented VP opcode CLI");
AddCode("//CLI");
do_function_call("$ifcond");
break; break;
case RSX_SCA_OPCODE_RET: case RSX_SCA_OPCODE_RET:
// works like BRI but shorter (RET o[1].x(TR);) // works like BRI but shorter (RET o[1].x(TR);)
AddCode("$ifcond return;"); do_function_return();
break; break;
case RSX_SCA_OPCODE_LG2: SetDSTSca("log2(" + NotZeroPositive("$s") + ")"); break; case RSX_SCA_OPCODE_LG2: SetDSTSca("log2(" + NotZeroPositive("$s") + ")"); break;
case RSX_SCA_OPCODE_EX2: SetDSTSca("exp2($s)"); break; case RSX_SCA_OPCODE_EX2: SetDSTSca("exp2($s)"); break;
@ -686,29 +699,32 @@ std::string VertexProgramDecompiler::Decompile()
// works differently (BRB o[1].x !b0, L0;) // works differently (BRB o[1].x !b0, L0;)
{ {
LOG_WARNING(RSX, "sca_opcode BRB, d0=0x%X, d1=0x%X, d2=0x%X, d3=0x%X", d0.HEX, d1.HEX, d2.HEX, d3.HEX); LOG_WARNING(RSX, "sca_opcode BRB, d0=0x%X, d1=0x%X, d2=0x%X, d3=0x%X", d0.HEX, d1.HEX, d2.HEX, d3.HEX);
AddCode(fmt::format("//BRB opcode, d0=0x%X, d1=0x%X, d2=0x%X, d3=0x%X", d0.HEX, d1.HEX, d2.HEX, d3.HEX));
u32 jump_position = find_jump_lvl(GetAddr()); if (m_call_stack.empty())
{
u32 jump_position = find_jump_lvl(GetAddr());
AddCode("$ifbcond //BRB"); AddCode("$ifbcond //BRB");
AddCode("{"); AddCode("{");
m_cur_instr->open_scopes++; m_cur_instr->open_scopes++;
AddCode(fmt::format("jump_position = %u;", jump_position)); AddCode(fmt::format("jump_position = %u;", jump_position));
AddCode("continue;"); AddCode("continue;");
m_cur_instr->close_scopes++; m_cur_instr->close_scopes++;
AddCode("}"); AddCode("}");
AddCode(""); AddCode("");
}
else
{
//TODO
LOG_ERROR(RSX, "BRA opcode found in subroutine!");
}
break; break;
} }
case RSX_SCA_OPCODE_CLB: break; case RSX_SCA_OPCODE_CLB: break;
// works same as BRB // works same as BRB
LOG_WARNING(RSX, "sca_opcode CLB, d0=0x%X, d1=0x%X, d2=0x%X, d3=0x%X", d0.HEX, d1.HEX, d2.HEX, d3.HEX);
AddCode("//CLB"); AddCode("//CLB");
do_function_call("$ifbcond");
AddCode("$ifbcond $f(); //CLB");
AddCode("");
break; break;
case RSX_SCA_OPCODE_PSH: break; case RSX_SCA_OPCODE_PSH: break;
// works differently (PSH o[1].x A0;) // works differently (PSH o[1].x A0;)
@ -722,7 +738,13 @@ std::string VertexProgramDecompiler::Decompile()
default: default:
AddCode(fmt::format("//Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode })); AddCode(fmt::format("//Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode }));
LOG_ERROR(RSX, "Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode }); LOG_ERROR(RSX, "Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode });
Emu.Pause(); program_end = true;
break;
}
if (program_end)
{
do_program_exit(!d3.end);
break; break;
} }
} }
@ -741,10 +763,6 @@ std::string VertexProgramDecompiler::Decompile()
m_jump_lvls.clear(); m_jump_lvls.clear();
m_body.clear(); m_body.clear();
if (m_funcs.size() > 2)
{
m_funcs.erase(m_funcs.begin() + 2, m_funcs.end());
}
return result; return result;
} }

View file

@ -2,6 +2,7 @@
#include "Emu/RSX/RSXVertexProgram.h" #include "Emu/RSX/RSXVertexProgram.h"
#include <vector> #include <vector>
#include <set> #include <set>
#include <stack>
#include <sstream> #include <sstream>
#include "ShaderParam.h" #include "ShaderParam.h"
@ -54,9 +55,7 @@ struct VertexProgramDecompiler
std::set<int> m_jump_lvls; std::set<int> m_jump_lvls;
std::vector<std::string> m_body; std::vector<std::string> m_body;
std::vector<FuncInfo> m_funcs; std::stack<u32> m_call_stack;
//wxString main;
const std::vector<u32>& m_data; const std::vector<u32>& m_data;
ParamArray m_parr; ParamArray m_parr;
@ -67,13 +66,13 @@ struct VertexProgramDecompiler
std::string GetScaMask(); std::string GetScaMask();
std::string GetDST(bool is_sca = false); std::string GetDST(bool is_sca = false);
std::string GetSRC(const u32 n); std::string GetSRC(const u32 n);
std::string GetFunc();
std::string GetTex(); std::string GetTex();
std::string GetCond(); std::string GetCond();
std::string GetOptionalBranchCond(); //Conditional branch expression modified externally at runtime std::string GetOptionalBranchCond(); //Conditional branch expression modified externally at runtime
std::string AddAddrMask(); std::string AddAddrMask();
std::string AddAddrReg(); std::string AddAddrReg();
std::string AddAddrRegWithoutMask(); std::string AddAddrRegWithoutMask();
std::string AddCondReg();
u32 GetAddr(); u32 GetAddr();
std::string Format(const std::string& code); std::string Format(const std::string& code);
@ -82,7 +81,6 @@ struct VertexProgramDecompiler
void SetDST(bool is_sca, std::string value); void SetDST(bool is_sca, std::string value);
void SetDSTVec(const std::string& code); void SetDSTVec(const std::string& code);
void SetDSTSca(const std::string& code); void SetDSTSca(const std::string& code);
std::string BuildFuncBody(const FuncInfo& func);
std::string BuildCode(); std::string BuildCode();
protected: protected:

View file

@ -24,7 +24,7 @@ namespace
GLGSRender::GLGSRender() : GSRender() GLGSRender::GLGSRender() : GSRender()
{ {
m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1.2")); m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1.3"));
if (g_cfg.video.disable_vertex_cache) if (g_cfg.video.disable_vertex_cache)
m_vertex_cache.reset(new gl::null_vertex_cache()); m_vertex_cache.reset(new gl::null_vertex_cache());
@ -610,9 +610,9 @@ void GLGSRender::on_init_thread()
if (g_cfg.video.debug_output) if (g_cfg.video.debug_output)
gl::enable_debugging(); gl::enable_debugging();
LOG_NOTICE(RSX, "%s", (const char*)glGetString(GL_VERSION)); LOG_NOTICE(RSX, "GL RENDERER: %s (%s)", (const char*)glGetString(GL_RENDERER), (const char*)glGetString(GL_VENDOR));
LOG_NOTICE(RSX, "%s", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); LOG_NOTICE(RSX, "GL VERSION: %s", (const char*)glGetString(GL_VERSION));
LOG_NOTICE(RSX, "%s", (const char*)glGetString(GL_VENDOR)); LOG_NOTICE(RSX, "GLSL VERSION: %s", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
auto& gl_caps = gl::get_driver_caps(); auto& gl_caps = gl::get_driver_caps();

View file

@ -241,15 +241,6 @@ struct RSXFragmentProgram
return (rsx::texture_dimension_extended)((texture_dimensions >> (id * 2)) & 0x3); return (rsx::texture_dimension_extended)((texture_dimensions >> (id * 2)) & 0x3);
} }
void set_texture_dimension(const std::array<rsx::texture_dimension_extended, 16> &dimensions)
{
texture_dimensions = 0;
for (u32 i = 0, offset = 0; i < 16; ++i, offset += 2)
{
texture_dimensions |= (u32)dimensions[i] << offset;
}
}
RSXFragmentProgram() RSXFragmentProgram()
{ {
memset(this, 0, sizeof(RSXFragmentProgram)); memset(this, 0, sizeof(RSXFragmentProgram));

View file

@ -1338,23 +1338,11 @@ namespace rsx
u32* ucode_src = rsx::method_registers.transform_program.data() + (transform_program_start * 4); u32* ucode_src = rsx::method_registers.transform_program.data() + (transform_program_start * 4);
u32* ucode_dst = current_vertex_program.data.data(); u32* ucode_dst = current_vertex_program.data.data();
u32 ucode_size = 0;
D3 d3;
for (int i = transform_program_start; i < 512; ++i) memcpy(ucode_dst, ucode_src, current_vertex_program.data.size() * sizeof(u32));
{
ucode_size += 4;
memcpy(ucode_dst, ucode_src, 4 * sizeof(u32));
d3.HEX = ucode_src[3]; auto program_info = program_hash_util::vertex_program_utils::analyse_vertex_program(current_vertex_program.data);
if (d3.end) current_vertex_program.data.resize(program_info.ucode_size);
break;
ucode_src += 4;
ucode_dst += 4;
}
current_vertex_program.data.resize(ucode_size);
const u32 input_mask = rsx::method_registers.vertex_attrib_input_mask(); const u32 input_mask = rsx::method_registers.vertex_attrib_input_mask();
const u32 modulo_mask = rsx::method_registers.frequency_divider_operation_mask(); const u32 modulo_mask = rsx::method_registers.frequency_divider_operation_mask();
@ -1562,10 +1550,10 @@ namespace rsx
const u32 program_offset = (shader_program & ~0x3); const u32 program_offset = (shader_program & ~0x3);
result.addr = vm::base(rsx::get_address(program_offset, program_location)); result.addr = vm::base(rsx::get_address(program_offset, program_location));
auto program_start = program_hash_util::fragment_program_utils::get_fragment_program_start(result.addr); const auto program_info = program_hash_util::fragment_program_utils::analyse_fragment_program(result.addr);
result.addr = ((u8*)result.addr + program_start); result.addr = ((u8*)result.addr + program_info.program_start_offset);
result.offset = program_offset + program_start; result.offset = program_offset + program_info.program_start_offset;
result.valid = true; result.valid = true;
result.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); result.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT);
result.unnormalized_coords = 0; result.unnormalized_coords = 0;
@ -1577,7 +1565,6 @@ namespace rsx
result.redirected_textures = 0; result.redirected_textures = 0;
result.shadow_textures = 0; result.shadow_textures = 0;
std::array<texture_dimension_extended, 16> texture_dimensions;
const auto resolution_scale = rsx::get_resolution_scale(); const auto resolution_scale = rsx::get_resolution_scale();
for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i) for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i)
@ -1587,14 +1574,10 @@ namespace rsx
result.texture_scale[i][1] = sampler_descriptors[i]->scale_y; result.texture_scale[i][1] = sampler_descriptors[i]->scale_y;
result.texture_scale[i][2] = (f32)tex.remap(); //Debug value result.texture_scale[i][2] = (f32)tex.remap(); //Debug value
if (!tex.enabled()) if (tex.enabled() && (program_info.referenced_textures_mask & (1 << i)))
{
texture_dimensions[i] = texture_dimension_extended::texture_dimension_2d;
}
else
{ {
u32 texture_control = 0; u32 texture_control = 0;
texture_dimensions[i] = sampler_descriptors[i]->image_type; result.texture_dimensions |= ((u32)sampler_descriptors[i]->image_type << (i << 1));
if (tex.alpha_kill_enabled()) if (tex.alpha_kill_enabled())
{ {
@ -1669,8 +1652,6 @@ namespace rsx
} }
} }
result.set_texture_dimension(texture_dimensions);
//Sanity checks //Sanity checks
if (result.ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) if (result.ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT)
{ {
@ -1694,10 +1675,10 @@ namespace rsx
const u32 program_offset = (shader_program & ~0x3); const u32 program_offset = (shader_program & ~0x3);
result.addr = vm::base(rsx::get_address(program_offset, program_location)); result.addr = vm::base(rsx::get_address(program_offset, program_location));
auto program_start = program_hash_util::fragment_program_utils::get_fragment_program_start(result.addr); auto program_info = program_hash_util::fragment_program_utils::analyse_fragment_program(result.addr);
result.addr = ((u8*)result.addr + program_start); result.addr = ((u8*)result.addr + program_info.program_start_offset);
result.offset = program_offset + program_start; result.offset = program_offset + program_info.program_start_offset;
result.valid = true; result.valid = true;
result.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); result.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT);
result.unnormalized_coords = 0; result.unnormalized_coords = 0;
@ -1709,7 +1690,6 @@ namespace rsx
result.redirected_textures = 0; result.redirected_textures = 0;
result.shadow_textures = 0; result.shadow_textures = 0;
std::array<texture_dimension_extended, 16> texture_dimensions;
const auto resolution_scale = rsx::get_resolution_scale(); const auto resolution_scale = rsx::get_resolution_scale();
for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i) for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i)
@ -1720,13 +1700,9 @@ namespace rsx
result.textures_alpha_kill[i] = 0; result.textures_alpha_kill[i] = 0;
result.textures_zfunc[i] = 0; result.textures_zfunc[i] = 0;
if (!tex.enabled()) if (tex.enabled() && (program_info.referenced_textures_mask & (1 << i)))
{ {
texture_dimensions[i] = texture_dimension_extended::texture_dimension_2d; result.texture_dimensions |= ((u32)tex.get_extended_texture_dimension() << (i << 1));
}
else
{
texture_dimensions[i] = tex.get_extended_texture_dimension();
if (tex.alpha_kill_enabled()) if (tex.alpha_kill_enabled())
{ {
@ -1801,8 +1777,6 @@ namespace rsx
} }
} }
} }
result.set_texture_dimension(texture_dimensions);
} }
void thread::reset() void thread::reset()

View file

@ -632,7 +632,7 @@ VKGSRender::VKGSRender() : GSRender()
else else
m_vertex_cache.reset(new vk::weak_vertex_cache()); m_vertex_cache.reset(new vk::weak_vertex_cache());
m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "vulkan", "v1.25")); m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "vulkan", "v1.3"));
open_command_buffer(); open_command_buffer();

View file

@ -306,7 +306,7 @@ namespace rsx
return; return;
} }
std::string directory_path = root_path + "/pipelines/" + pipeline_class_name; std::string directory_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix;
if (!fs::is_dir(directory_path)) if (!fs::is_dir(directory_path))
{ {
@ -331,6 +331,9 @@ namespace rsx
root.rewind(); root.rewind();
// Invalid pipeline entries to be removed
std::vector<std::string> invalid_entries;
// Progress dialog // Progress dialog
std::unique_ptr<progress_dialog_helper> fallback_dlg; std::unique_ptr<progress_dialog_helper> fallback_dlg;
if (!dlg) if (!dlg)
@ -348,11 +351,9 @@ namespace rsx
if (tmp.name == "." || tmp.name == "..") if (tmp.name == "." || tmp.name == "..")
continue; continue;
if (tmp.name.compare(0, prefix_length, version_prefix) != 0) const auto filename = directory_path + "/" + tmp.name;
continue;
std::vector<u8> bytes; std::vector<u8> bytes;
fs::file f(directory_path + "/" + tmp.name); fs::file f(filename);
processed++; processed++;
dlg->update_msg(processed, entry_count); dlg->update_msg(processed, entry_count);
@ -360,6 +361,7 @@ namespace rsx
if (f.size() != sizeof(pipeline_data)) if (f.size() != sizeof(pipeline_data))
{ {
LOG_ERROR(RSX, "Cached pipeline object %s is not binary compatible with the current shader cache", tmp.name.c_str()); LOG_ERROR(RSX, "Cached pipeline object %s is not binary compatible with the current shader cache", tmp.name.c_str());
invalid_entries.push_back(filename);
continue; continue;
} }
@ -377,6 +379,16 @@ namespace rsx
} }
} }
if (!invalid_entries.empty())
{
for (const auto &filename : invalid_entries)
{
fs::remove_file(filename);
}
LOG_NOTICE(RSX, "shader cache: %d entries were marked as invalid and removed", invalid_entries.size());
}
dlg->close(); dlg->close();
} }
@ -416,7 +428,7 @@ namespace rsx
state_hash ^= rpcs3::hash_base<u64>(data.fp_zfunc_mask); state_hash ^= rpcs3::hash_base<u64>(data.fp_zfunc_mask);
std::string pipeline_file_name = fmt::format("%llX+%llX+%llX+%llX.bin", data.vertex_program_hash, data.fragment_program_hash, data.pipeline_storage_hash, state_hash); std::string pipeline_file_name = fmt::format("%llX+%llX+%llX+%llX.bin", data.vertex_program_hash, data.fragment_program_hash, data.pipeline_storage_hash, state_hash);
std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "-" + pipeline_file_name; std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "/" + pipeline_file_name;
fs::file(pipeline_path, fs::rewrite).write(&data, sizeof(pipeline_data)); fs::file(pipeline_path, fs::rewrite).write(&data, sizeof(pipeline_data));
} }