2020-12-05 13:08:24 +01:00
|
|
|
#pragma once
|
2015-05-19 18:17:08 +02:00
|
|
|
#include "ShaderParam.h"
|
2021-05-12 23:56:01 +02:00
|
|
|
#include "RSXFragmentProgram.h"
|
2015-05-19 18:17:08 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
// Helper for GPR occupancy tracking
|
|
|
|
|
struct temp_register
|
2015-05-19 18:17:08 +02:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
bool aliased_r0 = false;
|
|
|
|
|
bool aliased_h0 = false;
|
|
|
|
|
bool aliased_h1 = false;
|
|
|
|
|
bool last_write_half[4] = { false, false, false, false };
|
2017-11-30 19:47:25 +01:00
|
|
|
|
2021-05-22 20:46:10 +02:00
|
|
|
u32 real_index = -1;
|
2017-11-30 19:47:25 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
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
|
2019-01-18 10:23:30 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
void tag(u32 index, bool half_register, bool x, bool y, bool z, bool w)
|
|
|
|
|
{
|
|
|
|
|
if (half_register)
|
2017-11-30 19:47:25 +01:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
if (index & 1)
|
2017-11-30 19:47:25 +01:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
if (x) last_write_half[2] = true;
|
|
|
|
|
if (y) last_write_half[2] = true;
|
|
|
|
|
if (z) last_write_half[3] = true;
|
|
|
|
|
if (w) last_write_half[3] = true;
|
|
|
|
|
|
|
|
|
|
aliased_h1 = true;
|
|
|
|
|
h1_writes++;
|
2017-11-30 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
if (x) last_write_half[0] = true;
|
|
|
|
|
if (y) last_write_half[0] = true;
|
|
|
|
|
if (z) last_write_half[1] = true;
|
|
|
|
|
if (w) last_write_half[1] = true;
|
2019-01-18 10:23:30 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
aliased_h0 = true;
|
2019-01-18 10:23:30 +01:00
|
|
|
h0_writes++;
|
2017-11-30 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-12 23:25:44 +02:00
|
|
|
else
|
2017-11-30 19:47:25 +01:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
if (x) last_write_half[0] = false;
|
|
|
|
|
if (y) last_write_half[1] = false;
|
|
|
|
|
if (z) last_write_half[2] = false;
|
|
|
|
|
if (w) last_write_half[3] = false;
|
2018-02-23 20:48:51 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
aliased_r0 = true;
|
2017-11-30 19:47:25 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
h0_writes++;
|
|
|
|
|
h1_writes++;
|
2017-11-30 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-22 20:46:10 +02:00
|
|
|
if (real_index == umax)
|
2017-11-30 19:47:25 +01:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
if (half_register)
|
|
|
|
|
real_index = index >> 1;
|
|
|
|
|
else
|
|
|
|
|
real_index = index;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-30 19:47:25 +01:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
bool requires_gather(u8 channel) const
|
|
|
|
|
{
|
|
|
|
|
//Data fetched from the single precision register requires merging of the two half registers
|
2020-12-09 08:47:45 +01:00
|
|
|
ensure(channel < 4);
|
2019-04-12 23:25:44 +02:00
|
|
|
if (aliased_h0 && channel < 2)
|
|
|
|
|
{
|
|
|
|
|
return last_write_half[channel];
|
2017-11-30 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
if (aliased_h1 && channel > 1)
|
2017-11-30 19:47:25 +01:00
|
|
|
{
|
2019-04-12 23:25:44 +02:00
|
|
|
return last_write_half[channel];
|
2017-11-30 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool requires_split(u32 /*index*/) const
|
|
|
|
|
{
|
|
|
|
|
//Data fetched from any of the two half registers requires sync with the full register
|
|
|
|
|
if (!(last_write_half[0] || last_write_half[1]) && aliased_r0)
|
|
|
|
|
{
|
|
|
|
|
//r0 has been written to
|
|
|
|
|
//TODO: Check for specific elements in real32 register
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-09 21:12:47 +02:00
|
|
|
std::string gather_r() const
|
2019-04-12 23:25:44 +02:00
|
|
|
{
|
|
|
|
|
std::string h0 = "h" + std::to_string(real_index << 1);
|
|
|
|
|
std::string h1 = "h" + std::to_string(real_index << 1 | 1);
|
|
|
|
|
std::string reg = "r" + std::to_string(real_index);
|
|
|
|
|
std::string ret = "//Invalid gather";
|
|
|
|
|
|
|
|
|
|
if (aliased_h0 && aliased_h1)
|
|
|
|
|
ret = "(gather(" + h0 + ", " + h1 + "))";
|
|
|
|
|
else if (aliased_h0)
|
|
|
|
|
ret = "(gather(" + h0 + "), " + reg + ".zw)";
|
|
|
|
|
else if (aliased_h1)
|
|
|
|
|
ret = "(" + reg + ".xy, gather(" + h1 + "))";
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This class is used to translate RSX Fragment program to GLSL/HLSL code
|
|
|
|
|
* Backend with text based shader can subclass this class and implement :
|
2020-12-18 08:39:54 +01:00
|
|
|
* - virtual std::string getFloatTypeName(usz elementCount) = 0;
|
|
|
|
|
* - virtual std::string getHalfTypeName(usz elementCount) = 0;
|
2019-04-12 23:25:44 +02:00
|
|
|
* - virtual std::string getFunction(enum class FUNCTION) = 0;
|
|
|
|
|
* - virtual std::string saturate(const std::string &code) = 0;
|
|
|
|
|
* - virtual std::string compareFunction(enum class COMPARE, const std::string &, const std::string &) = 0;
|
|
|
|
|
* - virtual void insertHeader(std::stringstream &OS) = 0;
|
|
|
|
|
* - virtual void insertInputs(std::stringstream &OS) = 0;
|
|
|
|
|
* - virtual void insertOutputs(std::stringstream &OS) = 0;
|
|
|
|
|
* - virtual void insertConstants(std::stringstream &OS) = 0;
|
|
|
|
|
* - virtual void insertMainStart(std::stringstream &OS) = 0;
|
|
|
|
|
* - virtual void insertMainEnd(std::stringstream &OS) = 0;
|
|
|
|
|
*/
|
|
|
|
|
class FragmentProgramDecompiler
|
|
|
|
|
{
|
2019-04-13 12:20:50 +02:00
|
|
|
enum OPFLAGS
|
|
|
|
|
{
|
|
|
|
|
no_src_mask = 1,
|
|
|
|
|
src_cast_f32 = 2,
|
|
|
|
|
skip_type_cast = 4,
|
2019-04-23 16:53:39 +02:00
|
|
|
texture_ref = 8,
|
2019-04-13 12:20:50 +02:00
|
|
|
|
|
|
|
|
op_extern = src_cast_f32 | skip_type_cast,
|
|
|
|
|
};
|
|
|
|
|
|
2015-12-21 04:35:56 +01:00
|
|
|
OPDEST dst;
|
|
|
|
|
SRC0 src0;
|
|
|
|
|
SRC1 src1;
|
|
|
|
|
SRC2 src2;
|
2019-04-13 12:20:50 +02:00
|
|
|
u32 opflags;
|
2015-12-21 04:35:56 +01:00
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
std::string main;
|
|
|
|
|
u32& m_size;
|
2020-02-21 13:20:10 +01:00
|
|
|
u32 m_const_index = 0;
|
2015-05-19 18:17:08 +02:00
|
|
|
u32 m_offset;
|
2020-02-21 13:20:10 +01:00
|
|
|
u32 m_location = 0;
|
2015-05-19 18:17:08 +02:00
|
|
|
|
|
|
|
|
u32 m_loop_count;
|
|
|
|
|
int m_code_level;
|
|
|
|
|
std::vector<u32> m_end_offsets;
|
|
|
|
|
std::vector<u32> m_else_offsets;
|
|
|
|
|
|
2018-09-03 18:04:47 +02:00
|
|
|
std::array<temp_register, 64> temp_registers;
|
2017-11-30 19:47:25 +01:00
|
|
|
|
2021-04-09 21:12:47 +02:00
|
|
|
std::string GetMask() const;
|
2015-05-19 18:17:08 +02:00
|
|
|
|
2019-04-13 12:20:50 +02:00
|
|
|
void SetDst(std::string code, u32 flags = 0);
|
2015-05-19 18:17:08 +02:00
|
|
|
void AddCode(const std::string& code);
|
2019-04-12 23:25:44 +02:00
|
|
|
std::string AddReg(u32 index, bool fp16);
|
|
|
|
|
bool HasReg(u32 index, bool fp16);
|
2015-05-19 18:17:08 +02:00
|
|
|
std::string AddCond();
|
|
|
|
|
std::string AddConst();
|
|
|
|
|
std::string AddTex();
|
2019-06-08 07:58:04 +02:00
|
|
|
void AddFlowOp(const std::string& code);
|
2017-12-10 23:34:44 +01:00
|
|
|
std::string Format(const std::string& code, bool ignore_redirects = false);
|
2015-05-19 18:17:08 +02:00
|
|
|
|
2017-03-21 12:53:52 +01:00
|
|
|
//Technically a temporary workaround until we know what type3 is
|
|
|
|
|
std::string AddType3();
|
|
|
|
|
|
2017-12-10 23:34:44 +01:00
|
|
|
//Support the transform-2d temp result for use with TEXBEM
|
|
|
|
|
std::string AddX2d();
|
|
|
|
|
|
2017-11-13 20:07:23 +01:00
|
|
|
//Prevents operations from overflowing the desired range (tested with fp_dynamic3 autotest sample, DS2 for src1.input_prec_mod)
|
2019-04-23 16:53:39 +02:00
|
|
|
std::string ClampValue(const std::string& code, u32 precision);
|
2017-03-07 11:40:38 +01:00
|
|
|
|
2017-06-22 19:09:05 +02:00
|
|
|
/**
|
|
|
|
|
* Returns true if the dst set is not a vector (i.e only a single component)
|
|
|
|
|
*/
|
2021-04-09 21:12:47 +02:00
|
|
|
bool DstExpectsSca() const;
|
2017-06-22 19:09:05 +02:00
|
|
|
|
2019-08-31 13:31:01 +02:00
|
|
|
void AddCodeCond(const std::string& lhs, const std::string& rhs);
|
2017-05-21 21:14:59 +02:00
|
|
|
std::string GetRawCond();
|
2015-05-19 18:17:08 +02:00
|
|
|
std::string GetCond();
|
|
|
|
|
template<typename T> std::string GetSRC(T src);
|
|
|
|
|
std::string BuildCode();
|
|
|
|
|
|
2021-04-09 21:12:47 +02:00
|
|
|
static u32 GetData(const u32 d) { return d << 16 | d >> 16; }
|
2015-11-16 00:37:44 +01:00
|
|
|
|
|
|
|
|
/**
|
2019-04-12 23:25:44 +02:00
|
|
|
* Emits code if opcode is an SCT/SCB one and returns true,
|
2015-11-16 00:37:44 +01:00
|
|
|
* otherwise do nothing and return false.
|
|
|
|
|
* NOTE: What does SCT means ???
|
|
|
|
|
*/
|
2019-04-12 23:25:44 +02:00
|
|
|
bool handle_sct_scb(u32 opcode);
|
2015-11-16 00:37:44 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Emits code if opcode is an TEX SRB one and returns true,
|
|
|
|
|
* otherwise do nothing and return false.
|
|
|
|
|
* NOTE: What does TEX SRB means ???
|
|
|
|
|
*/
|
|
|
|
|
bool handle_tex_srb(u32 opcode);
|
2019-04-12 23:25:44 +02:00
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
protected:
|
2016-01-26 20:42:54 +01:00
|
|
|
const RSXFragmentProgram &m_prog;
|
2017-06-14 00:33:53 +02:00
|
|
|
u32 m_ctrl = 0;
|
2020-02-21 13:20:10 +01:00
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
/** returns the type name of float vectors.
|
|
|
|
|
*/
|
2020-12-18 08:39:54 +01:00
|
|
|
virtual std::string getFloatTypeName(usz elementCount) = 0;
|
2015-05-19 18:17:08 +02:00
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
/** returns the type name of half vectors.
|
|
|
|
|
*/
|
2020-12-18 08:39:54 +01:00
|
|
|
virtual std::string getHalfTypeName(usz elementCount) = 0;
|
2019-04-12 23:25:44 +02:00
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
/** returns string calling function where arguments are passed via
|
|
|
|
|
* $0 $1 $2 substring.
|
|
|
|
|
*/
|
|
|
|
|
virtual std::string getFunction(FUNCTION) = 0;
|
|
|
|
|
|
2016-07-19 13:06:01 +02:00
|
|
|
/** returns string calling comparison function on 2 args passed as strings.
|
2015-05-19 18:17:08 +02:00
|
|
|
*/
|
|
|
|
|
virtual std::string compareFunction(COMPARE, const std::string &, const std::string &) = 0;
|
|
|
|
|
|
|
|
|
|
/** Insert header of shader file (eg #version, "system constants"...)
|
|
|
|
|
*/
|
|
|
|
|
virtual void insertHeader(std::stringstream &OS) = 0;
|
|
|
|
|
/** Insert global declaration of fragments inputs.
|
|
|
|
|
*/
|
2018-04-29 08:41:51 +02:00
|
|
|
virtual void insertInputs(std::stringstream &OS) = 0;
|
2015-05-19 18:17:08 +02:00
|
|
|
/** insert global declaration of fragments outputs.
|
|
|
|
|
*/
|
|
|
|
|
virtual void insertOutputs(std::stringstream &OS) = 0;
|
|
|
|
|
/** insert declaration of shader constants.
|
|
|
|
|
*/
|
|
|
|
|
virtual void insertConstants(std::stringstream &OS) = 0;
|
2019-06-12 20:02:51 +02:00
|
|
|
/** insert helper function definitions.
|
2017-12-02 13:13:13 +01:00
|
|
|
*/
|
|
|
|
|
virtual void insertGlobalFunctions(std::stringstream &OS) = 0;
|
2015-05-19 18:17:08 +02:00
|
|
|
/** insert beginning of main (signature, temporary declaration...)
|
|
|
|
|
*/
|
|
|
|
|
virtual void insertMainStart(std::stringstream &OS) = 0;
|
|
|
|
|
/** insert end of main function (return value, output copy...)
|
|
|
|
|
*/
|
|
|
|
|
virtual void insertMainEnd(std::stringstream &OS) = 0;
|
2018-01-24 22:09:27 +01:00
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
public:
|
2019-08-23 18:36:01 +02:00
|
|
|
enum : u16
|
|
|
|
|
{
|
|
|
|
|
in_wpos = (1 << 0),
|
|
|
|
|
in_diff_color = (1 << 1),
|
|
|
|
|
in_spec_color = (1 << 2),
|
|
|
|
|
in_fogc = (1 << 3),
|
|
|
|
|
in_tc0 = (1 << 4),
|
|
|
|
|
in_tc1 = (1 << 5),
|
|
|
|
|
in_tc2 = (1 << 6),
|
|
|
|
|
in_tc3 = (1 << 7),
|
|
|
|
|
in_tc4 = (1 << 8),
|
|
|
|
|
in_tc5 = (1 << 9),
|
|
|
|
|
in_tc6 = (1 << 10),
|
|
|
|
|
in_tc7 = (1 << 11),
|
|
|
|
|
in_tc8 = (1 << 12),
|
|
|
|
|
in_tc9 = (1 << 13),
|
|
|
|
|
in_ssa = (1 << 14)
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-24 22:09:27 +01:00
|
|
|
struct
|
|
|
|
|
{
|
2020-12-24 19:44:15 +01:00
|
|
|
u16 in_register_mask = 0;
|
|
|
|
|
|
2021-01-20 21:23:00 +01:00
|
|
|
u16 common_access_sampler_mask = 0;
|
2020-12-24 19:44:15 +01:00
|
|
|
u16 shadow_sampler_mask = 0;
|
|
|
|
|
u16 redirected_sampler_mask = 0;
|
|
|
|
|
|
2018-01-24 22:09:27 +01:00
|
|
|
bool has_lit_op = false;
|
|
|
|
|
bool has_gather_op = false;
|
2018-02-08 15:49:26 +01:00
|
|
|
bool has_no_output = false;
|
2019-01-18 10:23:30 +01:00
|
|
|
bool has_discard_op = false;
|
2019-04-15 19:39:42 +02:00
|
|
|
bool has_tex_op = false;
|
2019-04-23 16:53:39 +02:00
|
|
|
bool has_divsq = false;
|
|
|
|
|
bool has_clamp = false;
|
2019-08-25 16:23:46 +02:00
|
|
|
bool has_w_access = false;
|
2020-06-11 21:46:53 +02:00
|
|
|
bool has_exp_tex_op = false;
|
2018-01-24 22:09:27 +01:00
|
|
|
}
|
|
|
|
|
properties;
|
|
|
|
|
|
2019-04-12 23:25:44 +02:00
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
bool has_native_half_support = false;
|
2019-04-15 19:39:42 +02:00
|
|
|
bool emulate_depth_compare = false;
|
2019-04-12 23:25:44 +02:00
|
|
|
}
|
|
|
|
|
device_props;
|
|
|
|
|
|
2015-05-19 18:17:08 +02:00
|
|
|
ParamArray m_parr;
|
2016-01-10 20:09:56 +01:00
|
|
|
FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size);
|
|
|
|
|
FragmentProgramDecompiler(const FragmentProgramDecompiler&) = delete;
|
|
|
|
|
FragmentProgramDecompiler(FragmentProgramDecompiler&&) = delete;
|
2015-05-19 18:17:08 +02:00
|
|
|
std::string Decompile();
|
|
|
|
|
};
|