mirror of
https://github.com/RPCS3/rsx_program_decompiler.git
synced 2026-01-31 01:25:19 +01:00
Improved fragment program decompiler
Implemented shader finalization Added shader compiler
This commit is contained in:
@@ -86,49 +86,48 @@ namespace rsx
|
||||
{
|
||||
class decompiler : public decompiler_base<shader_code::glsl_language>
|
||||
{
|
||||
enum class variable_modifier
|
||||
{
|
||||
none,
|
||||
in,
|
||||
out,
|
||||
constant
|
||||
};
|
||||
|
||||
struct context_t
|
||||
{
|
||||
struct variable_info
|
||||
{
|
||||
std::string type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
u32 offset;
|
||||
decompiled_program program;
|
||||
bool is_next_is_constant;
|
||||
|
||||
std::vector<u32> constants_offsets;
|
||||
std::unordered_map<variable_modifier, variable_info> variables;
|
||||
|
||||
template<typename Type = float_point_t<4>>
|
||||
expression_from<Type> variable(const std::string& name, variable_modifier modifier = variable_modifier::none)
|
||||
{
|
||||
variables[modifier] = { Type::name(), name };
|
||||
|
||||
return{ name };
|
||||
}
|
||||
u32 offset;
|
||||
|
||||
expression_from<float_point_t<4>> constant()
|
||||
{
|
||||
return variable("fc" + std::to_string(offset + sizeof(instruction_t)));
|
||||
constant_info info;
|
||||
info.id = offset + sizeof(instruction_t);
|
||||
info.name = "fc" + std::to_string(info.id);
|
||||
program.constants.insert(info);
|
||||
is_next_is_constant = true;
|
||||
|
||||
return info.name;
|
||||
}
|
||||
|
||||
expression_from<float_point_t<4>> temporary(bool is_fp16, int index)
|
||||
{
|
||||
return variable((is_fp16 ? "h" : "r") + std::to_string(index));
|
||||
register_info info;
|
||||
info.id = index;
|
||||
info.type = is_fp16 ? register_type::half_float_point : register_type::single_float_point;
|
||||
info.name = (is_fp16 ? "h" : "r") + std::to_string(index);
|
||||
program.temporary_registers.insert(info);
|
||||
return info.name;
|
||||
}
|
||||
|
||||
expression_from<float_point_t<4>> condition(int index)
|
||||
{
|
||||
register_info info;
|
||||
info.id = index;
|
||||
info.type = register_type::single_float_point;
|
||||
info.name = "cc" + std::to_string(index);
|
||||
program.temporary_registers.insert(info);
|
||||
|
||||
return info.name;
|
||||
}
|
||||
|
||||
expression_from<float_point_t<4>> input(int index)
|
||||
{
|
||||
return variable(input_attrib_map[index]);
|
||||
program.input_attributes |= (1 << index);
|
||||
return input_attrib_map[index];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -222,9 +221,7 @@ namespace rsx
|
||||
{
|
||||
case src_reg_type_t::temporary: return context.temporary(src.fp16, src.tmp_index);
|
||||
case src_reg_type_t::input: return context.input(data.dst.src_attr_reg_num);
|
||||
case src_reg_type_t::constant:
|
||||
context.is_next_is_constant = true;
|
||||
return context.constant();
|
||||
case src_reg_type_t::constant: return context.constant();
|
||||
}
|
||||
|
||||
throw;
|
||||
@@ -291,12 +288,12 @@ namespace rsx
|
||||
return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest);
|
||||
}
|
||||
|
||||
expression_from<float_point_t<4>> modify_condition_register() const
|
||||
expression_from<float_point_t<4>> modify_condition_register()
|
||||
{
|
||||
return{ "cc" + std::to_string(instruction.data.src0.cond_mod_reg_index) };
|
||||
return context.condition(instruction.data.src0.cond_mod_reg_index);
|
||||
}
|
||||
|
||||
expression_from<float_point_t<4>> execution_condition_register() const
|
||||
expression_from<float_point_t<4>> execution_condition_register()
|
||||
{
|
||||
std::string swizzle;
|
||||
|
||||
@@ -305,7 +302,7 @@ namespace rsx
|
||||
swizzle += mask[instruction.data.src0.cond_swizzle_z];
|
||||
swizzle += mask[instruction.data.src0.cond_swizzle_w];
|
||||
|
||||
return{ "cc" + std::to_string(instruction.data.src0.cond_reg_index), swizzle };
|
||||
return{ context.condition(instruction.data.src0.cond_reg_index).text, swizzle };
|
||||
}
|
||||
|
||||
enum class condition_operation
|
||||
@@ -345,7 +342,7 @@ namespace rsx
|
||||
throw;
|
||||
}
|
||||
|
||||
expression_from<boolean_t<1>> execution_condition(condition_operation operation) const
|
||||
expression_from<boolean_t<1>> execution_condition(condition_operation operation)
|
||||
{
|
||||
auto cond = execution_condition_register();
|
||||
|
||||
@@ -516,7 +513,7 @@ namespace rsx
|
||||
|
||||
if (flags & disable_swizzle_as_dst)
|
||||
{
|
||||
src.assign(expression_from<float_point_t<4>>(arg.text, true, dest.mask.size()));
|
||||
src.assign(expression_from<float_point_t<4>>(arg.text, arg.mask, true, dest.mask.size()));
|
||||
}
|
||||
|
||||
for (auto &entry : condition_map)
|
||||
@@ -653,7 +650,7 @@ namespace rsx
|
||||
case opcode::SEQ: return set_dst(compare(compare_function::equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst);
|
||||
case opcode::FRC: return set_dst(fract(src_swizzled_as_dst(0)), disable_swizzle_as_dst);
|
||||
case opcode::FLR: return set_dst(floor(src_swizzled_as_dst(0)), disable_swizzle_as_dst);
|
||||
case opcode::KIL: return conditional(expression_from<void_t>("discard"));
|
||||
case opcode::KIL: return conditional(expression_from<void_t>("discard;"));
|
||||
case opcode::PK4: return unimplemented("PK4");
|
||||
case opcode::UP4: return unimplemented("UP4");
|
||||
case opcode::DDX: return set_dst(ddx(src_swizzled_as_dst(0)), disable_swizzle_as_dst);
|
||||
@@ -702,7 +699,7 @@ namespace rsx
|
||||
case opcode::LIF: return unimplemented("LIF");
|
||||
case opcode::FENCT: return comment("fenct");
|
||||
case opcode::FENCB: return comment("fencb");
|
||||
case opcode::BRK: return conditional(expression_from<void_t>("break"));
|
||||
case opcode::BRK: return conditional(expression_from<void_t>("break;"));
|
||||
case opcode::CAL: return unimplemented("CAL");
|
||||
case opcode::IFE:
|
||||
writer += writer_t{ "if (" + execution_condition(condition_operation::all).to_string() + ")\n{\n" };
|
||||
@@ -727,13 +724,13 @@ namespace rsx
|
||||
writer.after(instruction.data.src2.end_offset >> 2, "}\n");
|
||||
return "";
|
||||
case opcode::REP: return unimplemented("REP");
|
||||
case opcode::RET: return conditional(expression_from<void_t>("return"));
|
||||
case opcode::RET: return conditional(expression_from<void_t>("return;"));
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void decompile(std::size_t offset, instruction_t* instructions)
|
||||
decompiled_program decompile(std::size_t offset, instruction_t* instructions)
|
||||
{
|
||||
context.offset = 0;
|
||||
context.is_next_is_constant = false;
|
||||
@@ -753,26 +750,17 @@ namespace rsx
|
||||
if (instruction.data.dst.end)
|
||||
break;
|
||||
}
|
||||
|
||||
context.program.entry_function = "func0";
|
||||
context.program.code = "void func0()\n{\n" + writer.build() + "}\n";
|
||||
|
||||
return context.program;
|
||||
}
|
||||
};
|
||||
|
||||
decompiled_program decompile(std::size_t offset, ucode_instr *instructions)
|
||||
{
|
||||
decompiler dec;
|
||||
dec.decompile(offset, (decompiler::instruction_t*)instructions);
|
||||
|
||||
decompiled_program result{};
|
||||
|
||||
//result.constant_offsets = ...;
|
||||
//result.uniforms = ...;
|
||||
//result.textures = ...;
|
||||
//result.temporary_registers = ...;
|
||||
//result.input_attributes = ...;
|
||||
//result.output_attributes = ...;
|
||||
|
||||
result.code = dec.writer.build();
|
||||
|
||||
return result;
|
||||
return decompiler{}.decompile(offset, (decompiler::instruction_t*)instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,7 @@
|
||||
#pragma once
|
||||
#include "rsx_fp_ucode.h"
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
struct rsx_vertex_shader
|
||||
{
|
||||
std::vector<unsigned int> data;
|
||||
|
||||
struct hash_t
|
||||
{
|
||||
std::size_t operator ()(const rsx_vertex_shader& arg) const
|
||||
{
|
||||
return 0;
|
||||
//return arg.hash;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator ==(const rsx_vertex_shader& rhs) const
|
||||
{
|
||||
if (data.size() != rhs.data.size())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (data[i] != rhs.data[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct rsx_fragment_shader
|
||||
{
|
||||
std::vector<unsigned int> data;
|
||||
|
||||
struct hash_t
|
||||
{
|
||||
std::size_t operator ()(const rsx_fragment_shader& arg) const
|
||||
{
|
||||
return 0;
|
||||
//return arg.hash;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator ==(const rsx_fragment_shader& rhs) const
|
||||
{
|
||||
if (data.size() != rhs.data.size())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (data[i] != rhs.data[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct finalized_rsx_vertex_shader
|
||||
{
|
||||
int input_attributes;
|
||||
|
||||
std::string code;
|
||||
|
||||
struct hash_t
|
||||
{
|
||||
std::size_t operator ()(const finalized_rsx_vertex_shader& arg) const
|
||||
{
|
||||
return 0;
|
||||
//return arg.hash;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator ==(const finalized_rsx_vertex_shader& rhs) const
|
||||
{
|
||||
return
|
||||
input_attributes == rhs.input_attributes &&
|
||||
code == rhs.code;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct finalized_rsx_fragment_shader
|
||||
{
|
||||
int output_attributes;
|
||||
int control;
|
||||
|
||||
std::string code;
|
||||
|
||||
struct hash_t
|
||||
{
|
||||
std::size_t operator ()(const finalized_rsx_fragment_shader& arg) const
|
||||
{
|
||||
return 0;
|
||||
//return arg.hash;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator ==(const finalized_rsx_fragment_shader& rhs) const
|
||||
{
|
||||
return
|
||||
output_attributes == rhs.output_attributes &&
|
||||
control == rhs.control &&
|
||||
code == rhs.code;
|
||||
}
|
||||
};*/
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
@@ -131,23 +24,75 @@ namespace rsx
|
||||
{
|
||||
int id;
|
||||
sampler_type type;
|
||||
std::string name;
|
||||
|
||||
bool operator==(const texture_info& rhs) const
|
||||
{
|
||||
return rhs.name == name;
|
||||
}
|
||||
|
||||
std::size_t hash() const
|
||||
{
|
||||
return std::hash<std::string>{}(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct constant_info
|
||||
{
|
||||
int id;
|
||||
std::string name;
|
||||
|
||||
bool operator==(const constant_info& rhs) const
|
||||
{
|
||||
return rhs.name == name;
|
||||
}
|
||||
|
||||
std::size_t hash() const
|
||||
{
|
||||
return std::hash<std::string>{}(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct register_info
|
||||
{
|
||||
int id;
|
||||
register_type type;
|
||||
std::string name;
|
||||
|
||||
bool operator==(const register_info& rhs) const
|
||||
{
|
||||
return rhs.name == name;
|
||||
}
|
||||
|
||||
std::size_t hash() const
|
||||
{
|
||||
return std::hash<std::string>{}(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct hasher
|
||||
{
|
||||
template<typename Type>
|
||||
std::size_t operator()(const Type &obj) const
|
||||
{
|
||||
return obj.hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct decompiled_program
|
||||
{
|
||||
std::vector<std::size_t> constant_offsets;
|
||||
std::vector<std::string> uniforms;
|
||||
std::vector<texture_info> textures;
|
||||
std::vector<register_info> temporary_registers;
|
||||
unsigned int input_attributes;
|
||||
unsigned int output_attributes;
|
||||
std::unordered_set<constant_info, hasher> constants;
|
||||
std::unordered_set<texture_info, hasher> textures;
|
||||
std::unordered_set<register_info, hasher> temporary_registers;
|
||||
unsigned int input_attributes = 0;
|
||||
unsigned int output_attributes = 0;
|
||||
|
||||
std::string entry_function;
|
||||
std::string code;
|
||||
};
|
||||
|
||||
struct complete_program
|
||||
{
|
||||
std::string code;
|
||||
};
|
||||
|
||||
@@ -155,4 +100,48 @@ namespace rsx
|
||||
{
|
||||
decompiled_program decompile(std::size_t offset, ucode_instr* instructions);
|
||||
}
|
||||
|
||||
inline complete_program finalize_program(const decompiled_program& program)
|
||||
{
|
||||
complete_program result{ "#version 420\n\n" };
|
||||
|
||||
for (const constant_info& constant : program.constants)
|
||||
{
|
||||
result.code += "uniform vec4 " + constant.name + ";\n";
|
||||
}
|
||||
|
||||
result.code += "\n";
|
||||
|
||||
for (const register_info& temporary : program.temporary_registers)
|
||||
{
|
||||
result.code += "vec4 " + temporary.name + " = vec4(0.0);\n";
|
||||
}
|
||||
|
||||
result.code += "\n";
|
||||
|
||||
for (const texture_info& texture : program.textures)
|
||||
{
|
||||
result.code += "uniform sampler2D " + texture.name + ";\n";
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < std::size(rsx::fragment_program::input_attrib_map); ++index)
|
||||
{
|
||||
if (program.input_attributes & (1 << index))
|
||||
{
|
||||
result.code += "in vec4 " + rsx::fragment_program::input_attrib_map[index] + ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
result.code += "\n";
|
||||
result.code += program.code;
|
||||
|
||||
result.code +=
|
||||
R"(
|
||||
void main()
|
||||
{
|
||||
)" + program.entry_function + R"(();
|
||||
}
|
||||
)";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,10 +182,139 @@ std::vector<char> load_file(const std::string& path)
|
||||
throw;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <Windows.h>
|
||||
#include <gl/GL.h>
|
||||
|
||||
#pragma comment(lib, "OpenGL32.lib")
|
||||
|
||||
LRESULT __stdcall WindowProcedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
default:
|
||||
std::cout << '.';
|
||||
return DefWindowProc(hwnd, msg, wp, lp);
|
||||
}
|
||||
}
|
||||
|
||||
void test(const std::string &shader)
|
||||
{
|
||||
WNDCLASSEXA wndclass =
|
||||
{
|
||||
sizeof(WNDCLASSEXA),
|
||||
CS_DBLCLKS,
|
||||
WindowProcedure,
|
||||
0, 0,
|
||||
GetModuleHandle(nullptr),
|
||||
LoadIcon(nullptr, IDI_APPLICATION),
|
||||
LoadCursor(nullptr, IDC_ARROW),
|
||||
HBRUSH(COLOR_WINDOW + 1),
|
||||
0,
|
||||
"TestClass",
|
||||
LoadIcon(0,IDI_APPLICATION)
|
||||
};
|
||||
|
||||
RegisterClassExA(&wndclass);
|
||||
|
||||
HWND hwnd = CreateWindowA("TestClass", "", WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, GetModuleHandleA(nullptr), nullptr);
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd =
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR),
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags
|
||||
PFD_TYPE_RGBA, //The kind of framebuffer. RGBA or palette.
|
||||
32, //Colordepth of the framebuffer.
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0,
|
||||
24, //Number of bits for the depthbuffer
|
||||
8, //Number of bits for the stencilbuffer
|
||||
0, //Number of Aux buffers in the framebuffer.
|
||||
PFD_MAIN_PLANE,
|
||||
0,
|
||||
0, 0, 0
|
||||
};
|
||||
|
||||
HDC hdc = GetDC(hwnd);
|
||||
SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd);
|
||||
|
||||
HGLRC hglrc = wglCreateContext(hdc);
|
||||
wglMakeCurrent(hdc, hglrc);
|
||||
|
||||
using glShaderSource_t = void(*)(GLuint shader, GLsizei count, const char **string, const GLint *length);
|
||||
using glCreateShader_t = GLuint(*)(GLenum shaderType);
|
||||
using glCompileShader_t = void(*)(GLuint shader);
|
||||
using glDeleteShader_t = void(*)(GLuint shader);
|
||||
using glGetShaderiv_t = void(*)(GLuint shader, GLenum pname, GLint *params);
|
||||
using glGetShaderInfoLog_t = void(*)(GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog);
|
||||
|
||||
enum
|
||||
{
|
||||
GL_FRAGMENT_SHADER = 35632,
|
||||
GL_SHADER_SOURCE_LENGTH = 35720,
|
||||
GL_COMPILE_STATUS = 35713,
|
||||
};
|
||||
|
||||
auto glShaderSource = (glShaderSource_t)wglGetProcAddress("glShaderSource");
|
||||
auto glCreateShader = (glCreateShader_t)wglGetProcAddress("glCreateShader");
|
||||
auto glCompileShader = (glCompileShader_t)wglGetProcAddress("glCompileShader");
|
||||
auto glDeleteShader = (glDeleteShader_t)wglGetProcAddress("glDeleteShader");
|
||||
auto glGetShaderiv = (glGetShaderiv_t)wglGetProcAddress("glGetShaderiv");
|
||||
auto glGetShaderInfoLog = (glGetShaderInfoLog_t)wglGetProcAddress("glGetShaderInfoLog");
|
||||
|
||||
const char *shader_text = shader.data();
|
||||
const GLint length = shader.length();
|
||||
|
||||
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragmentShader, 1, &shader_text, &length);
|
||||
glCompileShader(fragmentShader);
|
||||
|
||||
GLint param;
|
||||
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, ¶m);
|
||||
if (param == 0)
|
||||
{
|
||||
std::cout << "compilation failed." << std::endl;
|
||||
glGetShaderiv(fragmentShader, GL_SHADER_SOURCE_LENGTH, ¶m);
|
||||
|
||||
std::vector<char> buffer(param + 1);
|
||||
|
||||
glGetShaderInfoLog(fragmentShader, param, ¶m, buffer.data());
|
||||
|
||||
std::cout << buffer.data();
|
||||
}
|
||||
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(hglrc);
|
||||
ReleaseDC(hwnd, hdc);
|
||||
DestroyWindow(hwnd);
|
||||
UnregisterClassA("TestClass", GetModuleHandleA(nullptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void print_info(const rsx::decompiled_program& program)
|
||||
{
|
||||
std::cout << "[CODE]" << std::endl;
|
||||
std::cout << program.code;
|
||||
//std::cout << "[RAW CODE]" << std::endl;
|
||||
//std::cout << program.code;
|
||||
|
||||
rsx::complete_program complete_program = rsx::finalize_program(program);
|
||||
std::cout << "[COMPLETE CODE]" << std::endl;
|
||||
std::cout << complete_program.code;
|
||||
|
||||
#ifdef _DEBUG
|
||||
test(complete_program.code);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
||||
Reference in New Issue
Block a user