mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
shader_ir: Initial Decompile Setup
This commit is contained in:
parent
d633397883
commit
c17953978b
6 changed files with 510 additions and 5 deletions
|
@ -105,9 +105,12 @@ add_library(video_core STATIC
|
||||||
shader/decode/warp.cpp
|
shader/decode/warp.cpp
|
||||||
shader/decode/xmad.cpp
|
shader/decode/xmad.cpp
|
||||||
shader/decode/other.cpp
|
shader/decode/other.cpp
|
||||||
|
shader/ast.cpp
|
||||||
|
shader/ast.h
|
||||||
shader/control_flow.cpp
|
shader/control_flow.cpp
|
||||||
shader/control_flow.h
|
shader/control_flow.h
|
||||||
shader/decode.cpp
|
shader/decode.cpp
|
||||||
|
shader/expr.h
|
||||||
shader/node_helper.cpp
|
shader/node_helper.cpp
|
||||||
shader/node_helper.h
|
shader/node_helper.h
|
||||||
shader/node.h
|
shader/node.h
|
||||||
|
|
180
src/video_core/shader/ast.cpp
Normal file
180
src/video_core/shader/ast.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/shader/ast.h"
|
||||||
|
#include "video_core/shader/expr.h"
|
||||||
|
|
||||||
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
class ExprPrinter final {
|
||||||
|
public:
|
||||||
|
ExprPrinter() = default;
|
||||||
|
|
||||||
|
void operator()(ExprAnd const& expr) {
|
||||||
|
inner += "( ";
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
inner += " && ";
|
||||||
|
std::visit(*this, *expr.operand2);
|
||||||
|
inner += ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprOr const& expr) {
|
||||||
|
inner += "( ";
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
inner += " || ";
|
||||||
|
std::visit(*this, *expr.operand2);
|
||||||
|
inner += ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprNot const& expr) {
|
||||||
|
inner += "!";
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprPredicate const& expr) {
|
||||||
|
u32 pred = static_cast<u32>(expr.predicate);
|
||||||
|
if (pred > 7) {
|
||||||
|
inner += "!";
|
||||||
|
pred -= 8;
|
||||||
|
}
|
||||||
|
inner += "P" + std::to_string(pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprCondCode const& expr) {
|
||||||
|
u32 cc = static_cast<u32>(expr.cc);
|
||||||
|
inner += "CC" + std::to_string(cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprVar const& expr) {
|
||||||
|
inner += "V" + std::to_string(expr.var_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ExprBoolean const& expr) {
|
||||||
|
inner += expr.value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& GetResult() {
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string inner{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTPrinter {
|
||||||
|
public:
|
||||||
|
ASTPrinter() = default;
|
||||||
|
|
||||||
|
void operator()(ASTProgram& ast) {
|
||||||
|
scope++;
|
||||||
|
inner += "program {\n";
|
||||||
|
for (ASTNode& node : ast.nodes) {
|
||||||
|
Visit(node);
|
||||||
|
}
|
||||||
|
inner += "}\n";
|
||||||
|
scope--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTIf& ast) {
|
||||||
|
ExprPrinter expr_parser{};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n";
|
||||||
|
scope++;
|
||||||
|
for (auto& node : ast.then_nodes) {
|
||||||
|
Visit(node);
|
||||||
|
}
|
||||||
|
scope--;
|
||||||
|
if (ast.else_nodes.size() > 0) {
|
||||||
|
inner += Ident() + "} else {\n";
|
||||||
|
scope++;
|
||||||
|
for (auto& node : ast.else_nodes) {
|
||||||
|
Visit(node);
|
||||||
|
}
|
||||||
|
scope--;
|
||||||
|
} else {
|
||||||
|
inner += Ident() + "}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTBlockEncoded& ast) {
|
||||||
|
inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) +
|
||||||
|
");\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTVarSet& ast) {
|
||||||
|
ExprPrinter expr_parser{};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
inner +=
|
||||||
|
Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTLabel& ast) {
|
||||||
|
inner += "Label_" + std::to_string(ast.index) + ":\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTGoto& ast) {
|
||||||
|
ExprPrinter expr_parser{};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" +
|
||||||
|
std::to_string(ast.label) + ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTDoWhile& ast) {
|
||||||
|
ExprPrinter expr_parser{};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
inner += Ident() + "do {\n";
|
||||||
|
scope++;
|
||||||
|
for (auto& node : ast.loop_nodes) {
|
||||||
|
Visit(node);
|
||||||
|
}
|
||||||
|
scope--;
|
||||||
|
inner += Ident() + "} while (" + expr_parser.GetResult() + ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTReturn& ast) {
|
||||||
|
ExprPrinter expr_parser{};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
inner += Ident() + "(" + expr_parser.GetResult() + ") -> " +
|
||||||
|
(ast.kills ? "discard" : "exit") + ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& Ident() {
|
||||||
|
if (memo_scope == scope) {
|
||||||
|
return tabs_memo;
|
||||||
|
}
|
||||||
|
tabs_memo = tabs.substr(0, scope * 2);
|
||||||
|
memo_scope = scope;
|
||||||
|
return tabs_memo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(ASTNode& node) {
|
||||||
|
std::visit(*this, *node->GetInnerData());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& GetResult() {
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string inner{};
|
||||||
|
u32 scope{};
|
||||||
|
|
||||||
|
std::string tabs_memo{};
|
||||||
|
u32 memo_scope{};
|
||||||
|
|
||||||
|
static std::string tabs;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string ASTPrinter::tabs = " ";
|
||||||
|
|
||||||
|
std::string ASTManager::Print() {
|
||||||
|
ASTPrinter printer{};
|
||||||
|
printer.Visit(main_node);
|
||||||
|
return printer.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon::Shader
|
184
src/video_core/shader/ast.h
Normal file
184
src/video_core/shader/ast.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "video_core/shader/expr.h"
|
||||||
|
#include "video_core/shader/node.h"
|
||||||
|
|
||||||
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
class ASTBase;
|
||||||
|
class ASTProgram;
|
||||||
|
class ASTIf;
|
||||||
|
class ASTBlockEncoded;
|
||||||
|
class ASTVarSet;
|
||||||
|
class ASTGoto;
|
||||||
|
class ASTLabel;
|
||||||
|
class ASTDoWhile;
|
||||||
|
class ASTReturn;
|
||||||
|
|
||||||
|
using ASTData = std::variant<ASTProgram, ASTIf, ASTBlockEncoded, ASTVarSet, ASTGoto, ASTLabel,
|
||||||
|
ASTDoWhile, ASTReturn>;
|
||||||
|
|
||||||
|
using ASTNode = std::shared_ptr<ASTBase>;
|
||||||
|
|
||||||
|
class ASTProgram {
|
||||||
|
public:
|
||||||
|
ASTProgram() = default;
|
||||||
|
std::list<ASTNode> nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTIf {
|
||||||
|
public:
|
||||||
|
ASTIf(Expr condition, std::list<ASTNode> then_nodes, std::list<ASTNode> else_nodes)
|
||||||
|
: condition(condition), then_nodes{then_nodes}, else_nodes{then_nodes} {}
|
||||||
|
Expr condition;
|
||||||
|
std::list<ASTNode> then_nodes;
|
||||||
|
std::list<ASTNode> else_nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTBlockEncoded {
|
||||||
|
public:
|
||||||
|
ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {}
|
||||||
|
u32 start;
|
||||||
|
u32 end;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTVarSet {
|
||||||
|
public:
|
||||||
|
ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {}
|
||||||
|
u32 index;
|
||||||
|
Expr condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTLabel {
|
||||||
|
public:
|
||||||
|
ASTLabel(u32 index) : index{index} {}
|
||||||
|
u32 index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTGoto {
|
||||||
|
public:
|
||||||
|
ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {}
|
||||||
|
Expr condition;
|
||||||
|
u32 label;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTDoWhile {
|
||||||
|
public:
|
||||||
|
ASTDoWhile(Expr condition, std::list<ASTNode> loop_nodes)
|
||||||
|
: condition(condition), loop_nodes{loop_nodes} {}
|
||||||
|
Expr condition;
|
||||||
|
std::list<ASTNode> loop_nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTReturn {
|
||||||
|
public:
|
||||||
|
ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {}
|
||||||
|
Expr condition;
|
||||||
|
bool kills;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTBase {
|
||||||
|
public:
|
||||||
|
explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {}
|
||||||
|
|
||||||
|
template <class U, class... Args>
|
||||||
|
static ASTNode Make(ASTNode parent, Args&&... args) {
|
||||||
|
return std::make_shared<ASTBase>(parent, ASTData(U(std::forward<Args>(args)...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetParent(ASTNode new_parent) {
|
||||||
|
parent = new_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode& GetParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ASTNode& GetParent() const {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetLevel() const {
|
||||||
|
u32 level = 0;
|
||||||
|
auto next = parent;
|
||||||
|
while (next) {
|
||||||
|
next = next->GetParent();
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTData* GetInnerData() {
|
||||||
|
return &data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ASTData data;
|
||||||
|
ASTNode parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTManager final {
|
||||||
|
public:
|
||||||
|
explicit ASTManager() {
|
||||||
|
main_node = ASTBase::Make<ASTProgram>(nullptr);
|
||||||
|
program = std::get_if<ASTProgram>(main_node->GetInnerData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeclareLabel(u32 address) {
|
||||||
|
const auto pair = labels_map.emplace(address, labels_count);
|
||||||
|
if (pair.second) {
|
||||||
|
labels_count++;
|
||||||
|
labels.resize(labels_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertLabel(u32 address) {
|
||||||
|
u32 index = labels_map[address];
|
||||||
|
ASTNode label = ASTBase::Make<ASTLabel>(main_node, index);
|
||||||
|
labels[index] = label;
|
||||||
|
program->nodes.push_back(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertGoto(Expr condition, u32 address) {
|
||||||
|
u32 index = labels_map[address];
|
||||||
|
ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, condition, index);
|
||||||
|
gotos.push_back(goto_node);
|
||||||
|
program->nodes.push_back(goto_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertBlock(u32 start_address, u32 end_address) {
|
||||||
|
ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address);
|
||||||
|
program->nodes.push_back(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertReturn(Expr condition, bool kills) {
|
||||||
|
ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills);
|
||||||
|
program->nodes.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Print();
|
||||||
|
|
||||||
|
void Decompile() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<u32, u32> labels_map{};
|
||||||
|
u32 labels_count{};
|
||||||
|
std::vector<ASTNode> labels{};
|
||||||
|
std::list<ASTNode> gotos{};
|
||||||
|
u32 variables{};
|
||||||
|
ASTProgram* program;
|
||||||
|
ASTNode main_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon::Shader
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/shader/ast.h"
|
||||||
#include "video_core/shader/control_flow.h"
|
#include "video_core/shader/control_flow.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ struct CFGRebuildState {
|
||||||
std::list<u32> inspect_queries{};
|
std::list<u32> inspect_queries{};
|
||||||
std::list<Query> queries{};
|
std::list<Query> queries{};
|
||||||
std::unordered_map<u32, u32> registered{};
|
std::unordered_map<u32, u32> registered{};
|
||||||
std::unordered_set<u32> labels{};
|
std::set<u32> labels{};
|
||||||
std::map<u32, u32> ssy_labels{};
|
std::map<u32, u32> ssy_labels{};
|
||||||
std::map<u32, u32> pbk_labels{};
|
std::map<u32, u32> pbk_labels{};
|
||||||
std::unordered_map<u32, BlockStack> stacks{};
|
std::unordered_map<u32, BlockStack> stacks{};
|
||||||
|
@ -415,6 +416,54 @@ bool TryQuery(CFGRebuildState& state) {
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) {
|
||||||
|
const auto get_expr = ([&](const Condition& cond) -> Expr {
|
||||||
|
Expr result{};
|
||||||
|
if (cond.cc != ConditionCode::T) {
|
||||||
|
result = MakeExpr<ExprCondCode>(cond.cc);
|
||||||
|
}
|
||||||
|
if (cond.predicate != Pred::UnusedIndex) {
|
||||||
|
Expr extra = MakeExpr<ExprPredicate>(cond.predicate);
|
||||||
|
if (result) {
|
||||||
|
return MakeExpr<ExprAnd>(extra, result);
|
||||||
|
}
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return MakeExpr<ExprBoolean>(true);
|
||||||
|
});
|
||||||
|
if (branch.address < 0) {
|
||||||
|
if (branch.kill) {
|
||||||
|
mm.InsertReturn(get_expr(branch.condition), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mm.InsertReturn(get_expr(branch.condition), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mm.InsertGoto(get_expr(branch.condition), branch.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecompileShader(CFGRebuildState& state) {
|
||||||
|
ASTManager manager{};
|
||||||
|
for (auto label : state.labels) {
|
||||||
|
manager.DeclareLabel(label);
|
||||||
|
}
|
||||||
|
for (auto& block : state.block_info) {
|
||||||
|
if (state.labels.count(block.start) != 0) {
|
||||||
|
manager.InsertLabel(block.start);
|
||||||
|
}
|
||||||
|
u32 end = block.branch.ignore ? block.end + 1 : block.end;
|
||||||
|
manager.InsertBlock(block.start, end);
|
||||||
|
if (!block.branch.ignore) {
|
||||||
|
InsertBranch(manager, block.branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manager.Decompile();
|
||||||
|
LOG_CRITICAL(HW_GPU, "Decompiled Shader:\n{} \n", manager.Print());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||||
std::size_t program_size, u32 start_address) {
|
std::size_t program_size, u32 start_address) {
|
||||||
CFGRebuildState state{program_code, program_size, start_address};
|
CFGRebuildState state{program_code, program_size, start_address};
|
||||||
|
@ -441,7 +490,10 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||||
|
|
||||||
// Sort and organize results
|
// Sort and organize results
|
||||||
std::sort(state.block_info.begin(), state.block_info.end(),
|
std::sort(state.block_info.begin(), state.block_info.end(),
|
||||||
[](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; });
|
[](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; });
|
||||||
|
if (decompiled) {
|
||||||
|
DecompileShader(state);
|
||||||
|
}
|
||||||
ShaderCharacteristics result_out{};
|
ShaderCharacteristics result_out{};
|
||||||
result_out.decompilable = decompiled;
|
result_out.decompilable = decompiled;
|
||||||
result_out.start = start_address;
|
result_out.start = start_address;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_set>
|
#include <set>
|
||||||
|
|
||||||
#include "video_core/engines/shader_bytecode.h"
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
@ -70,7 +70,7 @@ struct ShaderCharacteristics {
|
||||||
bool decompilable{};
|
bool decompilable{};
|
||||||
u32 start{};
|
u32 start{};
|
||||||
u32 end{};
|
u32 end{};
|
||||||
std::unordered_set<u32> labels{};
|
std::set<u32> labels{};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||||
|
|
86
src/video_core/shader/expr.h
Normal file
86
src/video_core/shader/expr.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
|
|
||||||
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
using Tegra::Shader::ConditionCode;
|
||||||
|
using Tegra::Shader::Pred;
|
||||||
|
|
||||||
|
class ExprAnd;
|
||||||
|
class ExprOr;
|
||||||
|
class ExprNot;
|
||||||
|
class ExprPredicate;
|
||||||
|
class ExprCondCode;
|
||||||
|
class ExprVar;
|
||||||
|
class ExprBoolean;
|
||||||
|
|
||||||
|
using ExprData =
|
||||||
|
std::variant<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>;
|
||||||
|
using Expr = std::shared_ptr<ExprData>;
|
||||||
|
|
||||||
|
class ExprAnd final {
|
||||||
|
public:
|
||||||
|
ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {}
|
||||||
|
|
||||||
|
Expr operand1;
|
||||||
|
Expr operand2;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprOr final {
|
||||||
|
public:
|
||||||
|
ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {}
|
||||||
|
|
||||||
|
Expr operand1;
|
||||||
|
Expr operand2;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprNot final {
|
||||||
|
public:
|
||||||
|
ExprNot(Expr a) : operand1{a} {}
|
||||||
|
|
||||||
|
Expr operand1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprVar final {
|
||||||
|
public:
|
||||||
|
ExprVar(u32 index) : var_index{index} {}
|
||||||
|
|
||||||
|
u32 var_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprPredicate final {
|
||||||
|
public:
|
||||||
|
ExprPredicate(Pred predicate) : predicate{predicate} {}
|
||||||
|
|
||||||
|
Pred predicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprCondCode final {
|
||||||
|
public:
|
||||||
|
ExprCondCode(ConditionCode cc) : cc{cc} {}
|
||||||
|
|
||||||
|
ConditionCode cc;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExprBoolean final {
|
||||||
|
public:
|
||||||
|
ExprBoolean(bool val) : value{val} {}
|
||||||
|
|
||||||
|
bool value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
Expr MakeExpr(Args&&... args) {
|
||||||
|
static_assert(std::is_convertible_v<T, ExprData>);
|
||||||
|
return std::make_shared<ExprData>(T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon::Shader
|
Loading…
Reference in a new issue