Improved fragment program decompiler

Implemented shader finalization
Added shader compiler
This commit is contained in:
DHrpcs3
2015-12-14 03:44:37 +02:00
parent 1814b11654
commit f99047fc1f
3 changed files with 279 additions and 173 deletions

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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, &param);
if (param == 0)
{
std::cout << "compilation failed." << std::endl;
glGetShaderiv(fragmentShader, GL_SHADER_SOURCE_LENGTH, &param);
std::vector<char> buffer(param + 1);
glGetShaderInfoLog(fragmentShader, param, &param, 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)