committed by
FernandoS27
6 changed files with 510 additions and 5 deletions
-
3src/video_core/CMakeLists.txt
-
180src/video_core/shader/ast.cpp
-
184src/video_core/shader/ast.h
-
58src/video_core/shader/control_flow.cpp
-
4src/video_core/shader/control_flow.h
-
86src/video_core/shader/expr.h
@ -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
|
|||
@ -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 |
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue