mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-23 07:13:00 +12:00
Initial shader decompilation work
This commit is contained in:
parent
d7bca1460c
commit
2d72b66042
2 changed files with 136 additions and 3 deletions
|
@ -1,9 +1,90 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "PICA/shader.hpp"
|
#include "PICA/shader.hpp"
|
||||||
#include "PICA/shader_gen_types.hpp"
|
#include "PICA/shader_gen_types.hpp"
|
||||||
|
|
||||||
|
struct EmulatorConfig;
|
||||||
|
|
||||||
namespace PICA::ShaderGen {
|
namespace PICA::ShaderGen {
|
||||||
std::string decompileShader(PICAShader& shaderUnit);
|
// Control flow analysis is partially based on
|
||||||
}
|
// https://github.com/PabloMK7/citra/blob/d0179559466ff09731d74474322ee880fbb44b00/src/video_core/shader/generator/glsl_shader_decompiler.cpp#L33
|
||||||
|
struct ControlFlow {
|
||||||
|
struct Function {
|
||||||
|
using Labels = std::set<u32>;
|
||||||
|
|
||||||
|
enum class ExitMode {
|
||||||
|
Unknown, // Can't guarantee whether we'll exit properly, fall back to CPU shaders (can happen with jmp shenanigans)
|
||||||
|
AlwaysReturn, // All paths reach the return point.
|
||||||
|
Conditional, // One or more code paths reach the return point or an END instruction conditionally.
|
||||||
|
AlwaysEnd, // All paths reach an END instruction.
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 start; // Starting PC of the function
|
||||||
|
u32 end; // End PC of the function
|
||||||
|
Labels outLabels{}; // Labels this function can "goto" (jump) to
|
||||||
|
ExitMode exitMode = ExitMode::Unknown;
|
||||||
|
|
||||||
|
explicit Function(u32 start, u32 end) : start(start), end(end) {}
|
||||||
|
// Use lexicographic comparison for functions in order to sort them in a set
|
||||||
|
bool operator<(const Function& other) const { return std::tie(start, end) < std::tie(other.start, other.end); }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<Function> functions{};
|
||||||
|
|
||||||
|
// Tells us whether analysis of the shader we're trying to compile failed, in which case we'll need to fail back to shader emulation
|
||||||
|
// On the CPU
|
||||||
|
bool analysisFailed = false;
|
||||||
|
|
||||||
|
void analyze(const PICAShader& shader, u32 entrypoint);
|
||||||
|
|
||||||
|
// This will recursively add all functions called by the function too, as analyzeFunction will call addFunction on control flow instructions
|
||||||
|
const Function* addFunction(u32 start, u32 end) {
|
||||||
|
auto searchIterator = functions.find(Function(start, end));
|
||||||
|
if (searchIterator != functions.end()) {
|
||||||
|
return &(*searchIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this function and analyze it if it doesn't already exist
|
||||||
|
Function function(start, end);
|
||||||
|
function.exitMode = analyzeFunction(start, end, function.outLabels);
|
||||||
|
|
||||||
|
// This function
|
||||||
|
if (function.exitMode == Function::ExitMode::Unknown) {
|
||||||
|
analysisFailed = true;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add function to our function list
|
||||||
|
auto [it, added] = functions.insert(std::move(function));
|
||||||
|
return &(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function::ExitMode analyzeFunction(u32 start, u32 end, Function::Labels& labels);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShaderDecompiler {
|
||||||
|
ControlFlow controlFlow{};
|
||||||
|
|
||||||
|
PICAShader& shader;
|
||||||
|
EmulatorConfig& config;
|
||||||
|
std::string decompiledShader;
|
||||||
|
|
||||||
|
u32 entrypoint;
|
||||||
|
u32 currentPC;
|
||||||
|
|
||||||
|
API api;
|
||||||
|
Language language;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShaderDecompiler(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language)
|
||||||
|
: shader(shader), entrypoint(entrypoint), currentPC(entrypoint), config(config), api(api), language(language), decompiledShader("") {}
|
||||||
|
|
||||||
|
std::string decompile();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string decompileShader(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language);
|
||||||
|
} // namespace PICA::ShaderGen
|
|
@ -1 +1,53 @@
|
||||||
#include "PICA/shader_decompiler.hpp"
|
#include "PICA/shader_decompiler.hpp"
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
|
using namespace PICA;
|
||||||
|
using namespace PICA::ShaderGen;
|
||||||
|
using Function = ControlFlow::Function;
|
||||||
|
using ExitMode = Function::ExitMode;
|
||||||
|
|
||||||
|
void ControlFlow::analyze(const PICAShader& shader, u32 entrypoint) {
|
||||||
|
analysisFailed = false;
|
||||||
|
|
||||||
|
const Function* function = addFunction(entrypoint, PICAShader::maxInstructionCount);
|
||||||
|
if (function == nullptr) {
|
||||||
|
analysisFailed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitMode analyzeFunction(u32 start, u32 end, Function::Labels& labels) { return ExitMode::Unknown; }
|
||||||
|
|
||||||
|
std::string ShaderDecompiler::decompile() {
|
||||||
|
controlFlow.analyze(shader, entrypoint);
|
||||||
|
|
||||||
|
if (controlFlow.analysisFailed) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
decompiledShader = "";
|
||||||
|
|
||||||
|
switch (api) {
|
||||||
|
case API::GL: decompiledShader += "#version 410 core"; break;
|
||||||
|
case API::GLES: decompiledShader += "#version 300 es"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.accurateShaderMul) {
|
||||||
|
// Safe multiplication handler from Citra: Handles the PICA's 0 * inf = 0 edge case
|
||||||
|
decompiledShader += R"(
|
||||||
|
vec4 safe_mul(vec4 a, vec4 b) {
|
||||||
|
vec4 res = a * b;
|
||||||
|
return mix(res, mix(mix(vec4(0.0), res, isnan(rhs)), product, isnan(lhs)), isnan(res));
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return decompiledShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PICA::ShaderGen::decompileShader(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language) {
|
||||||
|
ShaderDecompiler decompiler(shader, config, entrypoint, api, language);
|
||||||
|
|
||||||
|
return decompiler.decompile();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue