Compare commits

...

3 Commits

Author SHA1 Message Date
FernandoS27
b7f8058200 Dump uncompiled shaders 2018-11-07 21:45:41 -04:00
FernandoS27
acaa4247c2 Improve Shader Dumper: now outputs decompiled shaders 2018-11-07 19:26:56 -04:00
FernandoS27
7f0645fc10 Implement Shader Dumper 2018-11-07 19:26:55 -04:00
8 changed files with 188 additions and 9 deletions

View File

@@ -260,6 +260,14 @@ public:
return WriteArray(str.c_str(), str.length());
}
std::size_t WriteCString(const char* str) {
if (!IsOpen()) {
return std::numeric_limits<std::size_t>::max();
}
std::fputs(str, m_file);
return std::strlen(str);
}
bool IsOpen() const {
return nullptr != m_file;
}

View File

@@ -39,6 +39,8 @@ add_library(video_core STATIC
renderer_opengl/gl_shader_cache.h
renderer_opengl/gl_shader_decompiler.cpp
renderer_opengl/gl_shader_decompiler.h
renderer_opengl/gl_shader_dumper.cpp
renderer_opengl/gl_shader_dumper.h
renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_manager.cpp

View File

@@ -17,6 +17,7 @@
#include "video_core/engines/shader_header.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_dumper.h"
namespace OpenGL::GLShader::Decompiler {
@@ -278,7 +279,7 @@ public:
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
const Tegra::Shader::Header& header)
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header},
fixed_pipeline_output_attributes_used{}, local_memory_size{0} {
fixed_pipeline_output_attributes_used{}, local_memory_size{0}, faulty{false} {
BuildRegisterList();
BuildInputList();
}
@@ -603,6 +604,10 @@ public:
local_memory_size = lmem;
}
bool IsFaulty() {
return faulty;
}
private:
/// Generates declarations for registers.
void GenerateRegisters(const std::string& suffix) {
@@ -942,6 +947,7 @@ private:
const Tegra::Shader::Header& header;
std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
u64 local_memory_size;
bool faulty;
};
class GLSLGenerator {
@@ -953,9 +959,14 @@ public:
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
local_memory_size = header.GetLocalMemorySize();
regs.SetLocalMemory(local_memory_size);
faulty = false;
Generate(suffix);
}
bool IsFaulty() {
return faulty;
}
std::string GetShaderCode() {
return declarations.GetResult() + shader.GetResult();
}
@@ -1460,6 +1471,7 @@ private:
// Decoding failure
if (!opcode) {
faulty = true;
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value);
UNREACHABLE();
return offset + 1;
@@ -3726,6 +3738,12 @@ private:
}
void Generate(const std::string& suffix) {
u64 hash = ShaderDumper::GenerateHash(program_code);
shader.AddLine(fmt::format("// Program Hash Id: 0x{:x}UL", hash));
faulty |= ShaderDumper::IsProgramMarked(hash);
// Add declarations for all subroutines
for (const auto& subroutine : subroutines) {
shader.AddLine("bool " + subroutine.GetName() + "();");
@@ -3799,6 +3817,7 @@ private:
}
GenerateDeclarations();
faulty = faulty || regs.IsFaulty();
}
/// Add declarations for registers
@@ -3819,6 +3838,7 @@ private:
Maxwell3D::Regs::ShaderStage stage;
const std::string& suffix;
u64 local_memory_size;
bool faulty;
ShaderWriter shader;
ShaderWriter declarations;
@@ -3835,11 +3855,12 @@ std::string GetCommonDeclarations() {
std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix) {
const std::string& suffix, bool& faulty_shader) {
try {
const auto subroutines =
ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix);
faulty_shader = generator.IsFaulty();
return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
} catch (const DecompileFail& exception) {
LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());

View File

@@ -19,7 +19,7 @@ using Tegra::Engines::Maxwell3D;
std::string GetCommonDeclarations();
std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix);
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix, bool& faulty_shader);
} // namespace OpenGL::GLShader::Decompiler

View File

@@ -0,0 +1,80 @@
#include <unordered_set>
#include "common/file_util.h"
#include "common/hash.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/renderer_opengl/gl_shader_dumper.h"
struct DumpSet {
DumpSet() : values{} {
// Insert Marked Shaders here.
values.insert(0);
}
const bool IsMarked(u64 index) const {
return values.count(index) != 0;
}
std::unordered_set<u64> values;
};
auto dump_set = DumpSet{};
bool ShaderDumper::IsProgramMarked(u64 hash) {
return dump_set.IsMarked(hash);
}
template <typename I>
std::string n2hexstr(I w, size_t hex_len = sizeof(I) << 1) {
static const char* digits = "0123456789ABCDEF";
std::string rc(hex_len, '0');
for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4)
rc[i] = digits[(w >> j) & 0x0f];
return rc;
}
std::string ShaderDumper::hashName() {
return n2hexstr(hash);
}
bool IsSchedInstruction(u32 offset, u32 main_offset) {
// sched instructions appear once every 4 instructions.
static constexpr size_t SchedPeriod = 4;
u32 absolute_offset = offset - main_offset;
return (absolute_offset % SchedPeriod) == 0;
}
void ShaderDumper::dump() {
FileUtil::IOFile sFile;
std::string name = prefix + hashName() + ".bin";
sFile.Open(name, "wb");
u32 start_offset = 10;
u32 offset = start_offset;
u64 size = 0;
while (true) { // dump until hitting not finding a valid instruction
u64 inst = program[offset];
if (!IsSchedInstruction(offset, start_offset)) {
if (inst == 0) {
break;
}
}
sFile.WriteArray<u64>(&inst, 1);
size += 8;
offset += 1;
}
u64 fill = 0;
// Align to 32 bytes for nvdisasm
while ((size % 0x20) != 0) {
sFile.WriteArray<u64>(&fill, 1);
size += 8;
}
sFile.Close();
}
void ShaderDumper::dumpText(const std::string out) {
FileUtil::IOFile sFile;
std::string name = prefix + hashName() + ".txt";
sFile.Open(name, "w");
sFile.WriteString(out);
sFile.Close();
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/hash.h"
class ShaderDumper {
public:
ShaderDumper(const std::vector<u64>& prog, std::string prefix) : program(prog) {
this->hash = GenerateHash(program);
this->prefix = prefix;
}
void dump();
void dumpText(const std::string out);
static bool IsProgramMarked(u64 hash);
static u64 GenerateHash(const std::vector<u64>& program) {
return Common::ComputeHash64(program.data(), sizeof(u64) * program.size());
}
private:
std::string hashName();
u64 hash;
std::string prefix;
const std::vector<u64>& program;
};

View File

@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_dumper.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
namespace OpenGL::GLShader {
@@ -14,6 +15,8 @@ using Tegra::Engines::Maxwell3D;
static constexpr u32 PROGRAM_OFFSET{10};
ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
bool faultyA = false;
bool faultyB = false;
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
@@ -36,7 +39,7 @@ layout(std140) uniform vs_config {
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
Maxwell3D::Regs::ShaderStage::Vertex, "vertex", faultyA)
.value_or(ProgramResult());
out += program.first;
@@ -44,7 +47,7 @@ layout(std140) uniform vs_config {
if (setup.IsDualProgram()) {
ProgramResult program_b =
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b", faultyB)
.value_or(ProgramResult());
out += program_b.first;
}
@@ -78,10 +81,21 @@ void main() {
)";
if (faultyA) {
ShaderDumper s(setup.program.code, "VS");
s.dump();
s.dumpText(out);
}
if (faultyB) {
ShaderDumper s(setup.program.code_b, "VS");
s.dump();
s.dumpText(out);
}
return {out, program.second};
}
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
bool faulty = false;
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
@@ -89,7 +103,7 @@ ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
Maxwell3D::Regs::ShaderStage::Geometry, "geometry", faulty)
.value_or(ProgramResult());
out += R"(
out gl_PerVertex {
@@ -112,10 +126,16 @@ void main() {
)";
out += program.first;
if (faulty) {
ShaderDumper s(setup.program.code, "GS");
s.dump();
s.dumpText(out);
}
return {out, program.second};
}
ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
bool faulty = false;
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
@@ -123,7 +143,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
Maxwell3D::Regs::ShaderStage::Fragment, "fragment", faulty)
.value_or(ProgramResult());
out += R"(
layout(location = 0) out vec4 FragColor0;
@@ -174,6 +194,11 @@ void main() {
)";
out += program.first;
if (faulty) {
ShaderDumper s(setup.program.code, "FM");
s.dump();
s.dumpText(out);
}
return {out, program.second};
}
} // namespace OpenGL::GLShader

View File

@@ -5,6 +5,7 @@
#include <vector>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -41,7 +42,16 @@ GLuint LoadShader(const char* source, GLenum type) {
if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "{}", shader_error);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error);
static u32 error_shader_counter = 0;
FileUtil::IOFile sFile;
std::string name = "RejectedShader" + std::to_string(error_shader_counter) + ".txt";
sFile.Open(name, "w");
sFile.WriteCString(source);
sFile.WriteString("\n //");
sFile.WriteString(shader_error);
sFile.Close();
error_shader_counter++;
LOG_CRITICAL(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error);
}
}
return shader_id;