mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 05:28:56 +02:00
[shader_recompiler] use reusable stable_vector<> instead of object pools
Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
772e38cb8d
commit
4fe8b533d8
17 changed files with 313 additions and 491 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
|
|
@ -234,13 +234,12 @@ add_library(shader_recompiler STATIC
|
||||||
ir_opt/texture_pass.cpp
|
ir_opt/texture_pass.cpp
|
||||||
ir_opt/vendor_workaround_pass.cpp
|
ir_opt/vendor_workaround_pass.cpp
|
||||||
ir_opt/verification_pass.cpp
|
ir_opt/verification_pass.cpp
|
||||||
object_pool.h
|
|
||||||
profile.h
|
profile.h
|
||||||
program_header.h
|
program_header.h
|
||||||
runtime_info.h
|
runtime_info.h
|
||||||
shader_info.h
|
shader_info.h
|
||||||
varying_state.h
|
varying_state.h
|
||||||
|
shader_pool.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit::sirit SPIRV-Tools::SPIRV-Tools)
|
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit::sirit SPIRV-Tools::SPIRV-Tools)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
Block::Block(boost::container::stable_vector<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||||
|
|
||||||
Block::~Block() = default;
|
Block::~Block() = default;
|
||||||
|
|
||||||
|
|
@ -23,13 +23,12 @@ void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
||||||
Inst* const inst{inst_pool->Create(base_inst)};
|
Inst* const inst{&inst_pool->emplace_back(base_inst)};
|
||||||
return instructions.insert(insertion_point, *inst);
|
return instructions.insert(insertion_point, *inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args, u32 flags) {
|
||||||
std::initializer_list<Value> args, u32 flags) {
|
Inst* const inst{&inst_pool->emplace_back(op, flags)};
|
||||||
Inst* const inst{inst_pool->Create(op, flags)};
|
|
||||||
const auto result_it{instructions.insert(insertion_point, *inst)};
|
const auto result_it{instructions.insert(insertion_point, *inst)};
|
||||||
|
|
||||||
if (inst->NumArgs() != args.size()) {
|
if (inst->NumArgs() != args.size()) {
|
||||||
|
|
@ -53,12 +52,10 @@ void Block::AddBranch(Block* block) {
|
||||||
block->imm_predecessors.push_back(this);
|
block->imm_predecessors.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, Block* block) {
|
||||||
Block* block) {
|
if (const auto it{block_to_index.find(block)}; it != block_to_index.end())
|
||||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
|
|
||||||
return fmt::format("{{Block ${}}}", it->second);
|
return fmt::format("{{Block ${}}}", it->second);
|
||||||
}
|
return fmt::format("$<unknown block {:016x}>", u64(block));
|
||||||
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
|
@ -13,11 +13,11 @@
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "shader_recompiler/frontend/ir/condition.h"
|
#include "shader_recompiler/frontend/ir/condition.h"
|
||||||
#include "shader_recompiler/frontend/ir/value.h"
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ public:
|
||||||
using reverse_iterator = InstructionList::reverse_iterator;
|
using reverse_iterator = InstructionList::reverse_iterator;
|
||||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||||
|
|
||||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
explicit Block(boost::container::stable_vector<Inst>& inst_pool_);
|
||||||
~Block();
|
~Block();
|
||||||
|
|
||||||
Block(const Block&) = delete;
|
Block(const Block&) = delete;
|
||||||
|
|
@ -170,7 +170,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Memory pool for instruction list
|
/// Memory pool for instruction list
|
||||||
ObjectPool<Inst>* inst_pool;
|
boost::container::stable_vector<Inst>* inst_pool;
|
||||||
|
|
||||||
/// List of instructions in this block
|
/// List of instructions in this block
|
||||||
InstructionList instructions;
|
InstructionList instructions;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
|
@ -174,11 +174,10 @@ bool Block::Contains(Location pc) const noexcept {
|
||||||
return pc >= begin && pc < end;
|
return pc >= begin && pc < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
Function::Function(ObjectPool<Block>& block_pool, Location start_address)
|
Function::Function(boost::container::stable_vector<Block>& block_pool, Location start_address) : entrypoint{start_address} {
|
||||||
: entrypoint{start_address} {
|
|
||||||
Label& label{labels.emplace_back()};
|
Label& label{labels.emplace_back()};
|
||||||
label.address = start_address;
|
label.address = start_address;
|
||||||
label.block = block_pool.Create(Block{});
|
label.block = &block_pool.emplace_back(Block{});
|
||||||
label.block->begin = start_address;
|
label.block->begin = start_address;
|
||||||
label.block->end = start_address;
|
label.block->end = start_address;
|
||||||
label.block->end_class = EndClass::Branch;
|
label.block->end_class = EndClass::Branch;
|
||||||
|
|
@ -187,12 +186,13 @@ Function::Function(ObjectPool<Block>& block_pool, Location start_address)
|
||||||
label.block->branch_false = nullptr;
|
label.block->branch_false = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address,
|
CFG::CFG(Environment& env_, boost::container::stable_vector<Block>& block_pool_, Location start_address, bool exits_to_dispatcher_)
|
||||||
bool exits_to_dispatcher_)
|
: env{env_}
|
||||||
: env{env_}, block_pool{block_pool_}, program_start{start_address}, exits_to_dispatcher{
|
, block_pool{block_pool_}
|
||||||
exits_to_dispatcher_} {
|
, program_start{start_address}
|
||||||
|
, exits_to_dispatcher{exits_to_dispatcher_} {
|
||||||
if (exits_to_dispatcher) {
|
if (exits_to_dispatcher) {
|
||||||
dispatch_block = block_pool.Create(Block{});
|
dispatch_block = &block_pool.emplace_back(Block{});
|
||||||
dispatch_block->begin = {};
|
dispatch_block->begin = {};
|
||||||
dispatch_block->end = {};
|
dispatch_block->end = {};
|
||||||
dispatch_block->end_class = EndClass::Exit;
|
dispatch_block->end_class = EndClass::Exit;
|
||||||
|
|
@ -371,7 +371,7 @@ void CFG::AnalyzeCondInst(Block* block, FunctionId function_id, Location pc,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Create a virtual block and a conditional block
|
// Create a virtual block and a conditional block
|
||||||
Block* const conditional_block{block_pool.Create()};
|
Block* const conditional_block{&block_pool.emplace_back()};
|
||||||
Block virtual_block{};
|
Block virtual_block{};
|
||||||
virtual_block.begin = block->begin.Virtual();
|
virtual_block.begin = block->begin.Virtual();
|
||||||
virtual_block.end = block->begin.Virtual();
|
virtual_block.end = block->begin.Virtual();
|
||||||
|
|
@ -546,7 +546,7 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function
|
||||||
if (label_it != function.labels.end()) {
|
if (label_it != function.labels.end()) {
|
||||||
return label_it->block;
|
return label_it->block;
|
||||||
}
|
}
|
||||||
Block* const new_block{block_pool.Create()};
|
Block* const new_block{&block_pool.emplace_back()};
|
||||||
new_block->begin = pc;
|
new_block->begin = pc;
|
||||||
new_block->end = pc;
|
new_block->end = pc;
|
||||||
new_block->end_class = EndClass::Branch;
|
new_block->end_class = EndClass::Branch;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -9,6 +12,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
#include <boost/intrusive/set.hpp>
|
#include <boost/intrusive/set.hpp>
|
||||||
|
|
||||||
#include "shader_recompiler/environment.h"
|
#include "shader_recompiler/environment.h"
|
||||||
|
|
@ -17,7 +21,6 @@
|
||||||
#include "shader_recompiler/frontend/maxwell/instruction.h"
|
#include "shader_recompiler/frontend/maxwell/instruction.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/location.h"
|
#include "shader_recompiler/frontend/maxwell/location.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
|
|
||||||
namespace Shader::Maxwell::Flow {
|
namespace Shader::Maxwell::Flow {
|
||||||
|
|
||||||
|
|
@ -96,7 +99,7 @@ struct Label {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Function {
|
struct Function {
|
||||||
explicit Function(ObjectPool<Block>& block_pool, Location start_address);
|
explicit Function(boost::container::stable_vector<Block>& block_pool, Location start_address);
|
||||||
|
|
||||||
Location entrypoint;
|
Location entrypoint;
|
||||||
boost::container::small_vector<Label, 16> labels;
|
boost::container::small_vector<Label, 16> labels;
|
||||||
|
|
@ -110,8 +113,7 @@ class CFG {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CFG(Environment& env, ObjectPool<Block>& block_pool, Location start_address,
|
explicit CFG(Environment& env, boost::container::stable_vector<Block>& block_pool, Location start_address, bool exits_to_dispatcher = false);
|
||||||
bool exits_to_dispatcher = false);
|
|
||||||
|
|
||||||
CFG& operator=(const CFG&) = delete;
|
CFG& operator=(const CFG&) = delete;
|
||||||
CFG(const CFG&) = delete;
|
CFG(const CFG&) = delete;
|
||||||
|
|
@ -138,27 +140,20 @@ private:
|
||||||
/// Inspect already visited blocks.
|
/// Inspect already visited blocks.
|
||||||
/// Return true when the block has already been visited
|
/// Return true when the block has already been visited
|
||||||
bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
|
bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
|
||||||
|
|
||||||
AnalysisState AnalyzeInst(Block* block, FunctionId function_id, Location pc);
|
AnalysisState AnalyzeInst(Block* block, FunctionId function_id, Location pc);
|
||||||
|
void AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, EndClass insn_end_class, IR::Condition cond);
|
||||||
void AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, EndClass insn_end_class,
|
|
||||||
IR::Condition cond);
|
|
||||||
|
|
||||||
/// Return true when the branch instruction is confirmed to be a branch
|
/// Return true when the branch instruction is confirmed to be a branch
|
||||||
bool AnalyzeBranch(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
bool AnalyzeBranch(Block* block, FunctionId function_id, Location pc, Instruction inst, Opcode opcode);
|
||||||
Opcode opcode);
|
void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, bool is_absolute);
|
||||||
|
AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, FunctionId function_id);
|
||||||
void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst,
|
|
||||||
bool is_absolute);
|
|
||||||
AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute,
|
|
||||||
FunctionId function_id);
|
|
||||||
AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst);
|
AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst);
|
||||||
|
|
||||||
/// Return the branch target block id
|
/// Return the branch target block id
|
||||||
Block* AddLabel(Block* block, Stack stack, Location pc, FunctionId function_id);
|
Block* AddLabel(Block* block, Stack stack, Location pc, FunctionId function_id);
|
||||||
|
|
||||||
Environment& env;
|
Environment& env;
|
||||||
ObjectPool<Block>& block_pool;
|
boost::container::stable_vector<Block>& block_pool;
|
||||||
boost::container::small_vector<Function, 1> functions;
|
boost::container::small_vector<Function, 1> functions;
|
||||||
Location program_start;
|
Location program_start;
|
||||||
bool exits_to_dispatcher{};
|
bool exits_to_dispatcher{};
|
||||||
|
|
|
||||||
|
|
@ -13,143 +13,19 @@
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include <boost/intrusive/list.hpp>
|
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include "shader_recompiler/shader_pool.h"
|
||||||
#include "shader_recompiler/environment.h"
|
#include "shader_recompiler/environment.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
|
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
#include "shader_recompiler/host_translate_info.h"
|
#include "shader_recompiler/host_translate_info.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
|
|
||||||
namespace Shader::Maxwell {
|
namespace Shader::Maxwell {
|
||||||
namespace {
|
namespace {
|
||||||
struct Statement;
|
|
||||||
|
|
||||||
// Use normal_link because we are not guaranteed to destroy the tree in order
|
|
||||||
using ListBaseHook =
|
|
||||||
boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
|
||||||
|
|
||||||
using Tree = boost::intrusive::list<Statement,
|
|
||||||
// Allow using Statement without a definition
|
|
||||||
boost::intrusive::base_hook<ListBaseHook>,
|
|
||||||
// Avoid linear complexity on splice, size is never called
|
|
||||||
boost::intrusive::constant_time_size<false>>;
|
|
||||||
using Node = Tree::iterator;
|
|
||||||
|
|
||||||
enum class StatementType {
|
|
||||||
Code,
|
|
||||||
Goto,
|
|
||||||
Label,
|
|
||||||
If,
|
|
||||||
Loop,
|
|
||||||
Break,
|
|
||||||
Return,
|
|
||||||
Kill,
|
|
||||||
Unreachable,
|
|
||||||
Function,
|
|
||||||
Identity,
|
|
||||||
Not,
|
|
||||||
Or,
|
|
||||||
SetVariable,
|
|
||||||
SetIndirectBranchVariable,
|
|
||||||
Variable,
|
|
||||||
IndirectBranchCond,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool HasChildren(StatementType type) {
|
|
||||||
switch (type) {
|
|
||||||
case StatementType::If:
|
|
||||||
case StatementType::Loop:
|
|
||||||
case StatementType::Function:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Goto {};
|
|
||||||
struct Label {};
|
|
||||||
struct If {};
|
|
||||||
struct Loop {};
|
|
||||||
struct Break {};
|
|
||||||
struct Return {};
|
|
||||||
struct Kill {};
|
|
||||||
struct Unreachable {};
|
|
||||||
struct FunctionTag {};
|
|
||||||
struct Identity {};
|
|
||||||
struct Not {};
|
|
||||||
struct Or {};
|
|
||||||
struct SetVariable {};
|
|
||||||
struct SetIndirectBranchVariable {};
|
|
||||||
struct Variable {};
|
|
||||||
struct IndirectBranchCond {};
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
|
||||||
#endif
|
|
||||||
struct Statement : ListBaseHook {
|
|
||||||
Statement(const Flow::Block* block_, Statement* up_)
|
|
||||||
: block{block_}, up{up_}, type{StatementType::Code} {}
|
|
||||||
Statement(Goto, Statement* cond_, Node label_, Statement* up_)
|
|
||||||
: label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
|
||||||
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
|
||||||
Statement(If, Statement* cond_, Tree&& children_, Statement* up_)
|
|
||||||
: children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
|
|
||||||
Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_)
|
|
||||||
: children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
|
|
||||||
Statement(Break, Statement* cond_, Statement* up_)
|
|
||||||
: cond{cond_}, up{up_}, type{StatementType::Break} {}
|
|
||||||
Statement(Return, Statement* up_) : up{up_}, type{StatementType::Return} {}
|
|
||||||
Statement(Kill, Statement* up_) : up{up_}, type{StatementType::Kill} {}
|
|
||||||
Statement(Unreachable, Statement* up_) : up{up_}, type{StatementType::Unreachable} {}
|
|
||||||
Statement(FunctionTag) : children{}, type{StatementType::Function} {}
|
|
||||||
Statement(Identity, IR::Condition cond_, Statement* up_)
|
|
||||||
: guest_cond{cond_}, up{up_}, type{StatementType::Identity} {}
|
|
||||||
Statement(Not, Statement* op_, Statement* up_) : op{op_}, up{up_}, type{StatementType::Not} {}
|
|
||||||
Statement(Or, Statement* op_a_, Statement* op_b_, Statement* up_)
|
|
||||||
: op_a{op_a_}, op_b{op_b_}, up{up_}, type{StatementType::Or} {}
|
|
||||||
Statement(SetVariable, u32 id_, Statement* op_, Statement* up_)
|
|
||||||
: op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
|
|
||||||
Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_)
|
|
||||||
: branch_offset{branch_offset_},
|
|
||||||
branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {}
|
|
||||||
Statement(Variable, u32 id_, Statement* up_)
|
|
||||||
: id{id_}, up{up_}, type{StatementType::Variable} {}
|
|
||||||
Statement(IndirectBranchCond, u32 location_, Statement* up_)
|
|
||||||
: location{location_}, up{up_}, type{StatementType::IndirectBranchCond} {}
|
|
||||||
|
|
||||||
~Statement() {
|
|
||||||
if (HasChildren(type)) {
|
|
||||||
std::destroy_at(&children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
union {
|
|
||||||
const Flow::Block* block;
|
|
||||||
Node label;
|
|
||||||
Tree children;
|
|
||||||
IR::Condition guest_cond;
|
|
||||||
Statement* op;
|
|
||||||
Statement* op_a;
|
|
||||||
u32 location;
|
|
||||||
s32 branch_offset;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
Statement* cond;
|
|
||||||
Statement* op_b;
|
|
||||||
u32 id;
|
|
||||||
IR::Reg branch_reg;
|
|
||||||
};
|
|
||||||
Statement* up{};
|
|
||||||
StatementType type;
|
|
||||||
};
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string DumpExpr(const Statement* stmt) {
|
std::string DumpExpr(const Statement* stmt) {
|
||||||
switch (stmt->type) {
|
switch (stmt->type) {
|
||||||
|
|
@ -314,7 +190,9 @@ bool NeedsLift(Node goto_stmt, Node label_stmt) noexcept {
|
||||||
|
|
||||||
class GotoPass {
|
class GotoPass {
|
||||||
public:
|
public:
|
||||||
explicit GotoPass(Flow::CFG& cfg, ObjectPool<Statement>& stmt_pool) : pool{stmt_pool} {
|
explicit GotoPass(Flow::CFG& cfg, ShaderPools& pools_)
|
||||||
|
: pools{pools_}
|
||||||
|
{
|
||||||
std::vector gotos{BuildTree(cfg)};
|
std::vector gotos{BuildTree(cfg)};
|
||||||
const auto end{gotos.rend()};
|
const auto end{gotos.rend()};
|
||||||
for (auto goto_stmt = gotos.rbegin(); goto_stmt != end; ++goto_stmt) {
|
for (auto goto_stmt = gotos.rbegin(); goto_stmt != end; ++goto_stmt) {
|
||||||
|
|
@ -385,16 +263,14 @@ private:
|
||||||
return gotos;
|
return gotos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id,
|
void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id, std::vector<Node>& gotos, Node function_insert_point, std::optional<Node> return_label) {
|
||||||
std::vector<Node>& gotos, Node function_insert_point,
|
Statement* const false_stmt{&pools.stmt.emplace_back(Identity{}, IR::Condition{false}, &root_stmt)};
|
||||||
std::optional<Node> return_label) {
|
|
||||||
Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false}, &root_stmt)};
|
|
||||||
Tree& root{root_stmt.children};
|
Tree& root{root_stmt.children};
|
||||||
ankerl::unordered_dense::map<Flow::Block*, Node> local_labels;
|
ankerl::unordered_dense::map<Flow::Block*, Node> local_labels;
|
||||||
local_labels.reserve(function.blocks.size());
|
local_labels.reserve(function.blocks.size());
|
||||||
|
|
||||||
for (Flow::Block& block : function.blocks) {
|
for (Flow::Block& block : function.blocks) {
|
||||||
Statement* const label{pool.Create(Label{}, label_id, &root_stmt)};
|
Statement* const label{&pools.stmt.emplace_back(Label{}, label_id, &root_stmt)};
|
||||||
const Node label_it{root.insert(function_insert_point, *label)};
|
const Node label_it{root.insert(function_insert_point, *label)};
|
||||||
local_labels.emplace(&block, label_it);
|
local_labels.emplace(&block, label_it);
|
||||||
++label_id;
|
++label_id;
|
||||||
|
|
@ -406,46 +282,39 @@ private:
|
||||||
|
|
||||||
// Reset goto variables before the first block and after its respective label
|
// Reset goto variables before the first block and after its respective label
|
||||||
const auto make_reset_variable{[&]() -> Statement& {
|
const auto make_reset_variable{[&]() -> Statement& {
|
||||||
return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt);
|
return pools.stmt.emplace_back(SetVariable{}, label->id, false_stmt, &root_stmt);
|
||||||
}};
|
}};
|
||||||
root.push_front(make_reset_variable());
|
root.push_front(make_reset_variable());
|
||||||
root.insert(ip, make_reset_variable());
|
root.insert(ip, make_reset_variable());
|
||||||
root.insert(ip, *pool.Create(&block, &root_stmt));
|
root.insert(ip, pools.stmt.emplace_back(&block, &root_stmt));
|
||||||
|
|
||||||
switch (block.end_class) {
|
switch (block.end_class) {
|
||||||
case Flow::EndClass::Branch: {
|
case Flow::EndClass::Branch: {
|
||||||
Statement* const always_cond{
|
Statement* const always_cond{&pools.stmt.emplace_back(Identity{}, IR::Condition{true}, &root_stmt)};
|
||||||
pool.Create(Identity{}, IR::Condition{true}, &root_stmt)};
|
|
||||||
if (block.cond == IR::Condition{true}) {
|
if (block.cond == IR::Condition{true}) {
|
||||||
const Node true_label{local_labels.at(block.branch_true)};
|
const Node true_label{local_labels.at(block.branch_true)};
|
||||||
gotos.push_back(
|
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, true_label, &root_stmt)));
|
||||||
root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt)));
|
|
||||||
} else if (block.cond == IR::Condition{false}) {
|
} else if (block.cond == IR::Condition{false}) {
|
||||||
const Node false_label{local_labels.at(block.branch_false)};
|
const Node false_label{local_labels.at(block.branch_false)};
|
||||||
gotos.push_back(root.insert(
|
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, false_label, &root_stmt)));
|
||||||
ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
|
|
||||||
} else {
|
} else {
|
||||||
const Node true_label{local_labels.at(block.branch_true)};
|
const Node true_label{local_labels.at(block.branch_true)};
|
||||||
const Node false_label{local_labels.at(block.branch_false)};
|
const Node false_label{local_labels.at(block.branch_false)};
|
||||||
Statement* const true_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
|
Statement* const true_cond{&pools.stmt.emplace_back(Identity{}, block.cond, &root_stmt)};
|
||||||
gotos.push_back(
|
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, true_cond, true_label, &root_stmt)));
|
||||||
root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt)));
|
gotos.push_back(root.insert(ip, pools.stmt.emplace_back(Goto{}, always_cond, false_label, &root_stmt)));
|
||||||
gotos.push_back(root.insert(
|
|
||||||
ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Flow::EndClass::IndirectBranch:
|
case Flow::EndClass::IndirectBranch:
|
||||||
root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg,
|
root.insert(ip, pools.stmt.emplace_back(SetIndirectBranchVariable{}, block.branch_reg, block.branch_offset, &root_stmt));
|
||||||
block.branch_offset, &root_stmt));
|
|
||||||
for (const Flow::IndirectBranch& indirect : block.indirect_branches) {
|
for (const Flow::IndirectBranch& indirect : block.indirect_branches) {
|
||||||
const Node indirect_label{local_labels.at(indirect.block)};
|
const Node indirect_label{local_labels.at(indirect.block)};
|
||||||
Statement* cond{
|
Statement* cond{&pools.stmt.emplace_back(IndirectBranchCond{}, indirect.address, &root_stmt)};
|
||||||
pool.Create(IndirectBranchCond{}, indirect.address, &root_stmt)};
|
Statement* goto_stmt{&pools.stmt.emplace_back(Goto{}, cond, indirect_label, &root_stmt)};
|
||||||
Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)};
|
|
||||||
gotos.push_back(root.insert(ip, *goto_stmt));
|
gotos.push_back(root.insert(ip, *goto_stmt));
|
||||||
}
|
}
|
||||||
root.insert(ip, *pool.Create(Unreachable{}, &root_stmt));
|
root.insert(ip, pools.stmt.emplace_back(Unreachable{}, &root_stmt));
|
||||||
break;
|
break;
|
||||||
case Flow::EndClass::Call: {
|
case Flow::EndClass::Call: {
|
||||||
Flow::Function& call{cfg.Functions()[block.function_call]};
|
Flow::Function& call{cfg.Functions()[block.function_call]};
|
||||||
|
|
@ -454,16 +323,16 @@ private:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Flow::EndClass::Exit:
|
case Flow::EndClass::Exit:
|
||||||
root.insert(ip, *pool.Create(Return{}, &root_stmt));
|
root.insert(ip, pools.stmt.emplace_back(Return{}, &root_stmt));
|
||||||
break;
|
break;
|
||||||
case Flow::EndClass::Return: {
|
case Flow::EndClass::Return: {
|
||||||
Statement* const always_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
|
Statement* const always_cond{&pools.stmt.emplace_back(Identity{}, block.cond, &root_stmt)};
|
||||||
auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)};
|
auto goto_stmt{&pools.stmt.emplace_back(Goto{}, always_cond, return_label.value(), &root_stmt)};
|
||||||
gotos.push_back(root.insert(ip, *goto_stmt));
|
gotos.push_back(root.insert(ip, *goto_stmt));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Flow::EndClass::Kill:
|
case Flow::EndClass::Kill:
|
||||||
root.insert(ip, *pool.Create(Kill{}, &root_stmt));
|
root.insert(ip, pools.stmt.emplace_back(Kill{}, &root_stmt));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -479,8 +348,8 @@ private:
|
||||||
Tree& body{goto_stmt->up->children};
|
Tree& body{goto_stmt->up->children};
|
||||||
Tree if_body;
|
Tree if_body;
|
||||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_stmt);
|
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_stmt);
|
||||||
Statement* const cond{pool.Create(Not{}, goto_stmt->cond, &root_stmt)};
|
Statement* const cond{&pools.stmt.emplace_back(Not{}, goto_stmt->cond, &root_stmt)};
|
||||||
Statement* const if_stmt{pool.Create(If{}, cond, std::move(if_body), goto_stmt->up)};
|
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, cond, std::move(if_body), goto_stmt->up)};
|
||||||
UpdateTreeUp(if_stmt);
|
UpdateTreeUp(if_stmt);
|
||||||
body.insert(goto_stmt, *if_stmt);
|
body.insert(goto_stmt, *if_stmt);
|
||||||
body.erase(goto_stmt);
|
body.erase(goto_stmt);
|
||||||
|
|
@ -491,7 +360,7 @@ private:
|
||||||
Tree loop_body;
|
Tree loop_body;
|
||||||
loop_body.splice(loop_body.begin(), body, label_stmt, goto_stmt);
|
loop_body.splice(loop_body.begin(), body, label_stmt, goto_stmt);
|
||||||
Statement* const cond{goto_stmt->cond};
|
Statement* const cond{goto_stmt->cond};
|
||||||
Statement* const loop{pool.Create(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
|
Statement* const loop{&pools.stmt.emplace_back(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
|
||||||
UpdateTreeUp(loop);
|
UpdateTreeUp(loop);
|
||||||
body.insert(goto_stmt, *loop);
|
body.insert(goto_stmt, *loop);
|
||||||
body.erase(goto_stmt);
|
body.erase(goto_stmt);
|
||||||
|
|
@ -516,15 +385,15 @@ private:
|
||||||
const u32 label_id{label->id};
|
const u32 label_id{label->id};
|
||||||
|
|
||||||
Statement* const goto_cond{goto_stmt->cond};
|
Statement* const goto_cond{goto_stmt->cond};
|
||||||
Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
|
Statement* const set_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, parent)};
|
||||||
body.insert(goto_stmt, *set_var);
|
body.insert(goto_stmt, *set_var);
|
||||||
|
|
||||||
Tree if_body;
|
Tree if_body;
|
||||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_nested_stmt);
|
if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_nested_stmt);
|
||||||
Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const variable{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const neg_var{pool.Create(Not{}, variable, &root_stmt)};
|
Statement* const neg_var{&pools.stmt.emplace_back(Not{}, variable, &root_stmt)};
|
||||||
if (!if_body.empty()) {
|
if (!if_body.empty()) {
|
||||||
Statement* const if_stmt{pool.Create(If{}, neg_var, std::move(if_body), parent)};
|
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, neg_var, std::move(if_body), parent)};
|
||||||
UpdateTreeUp(if_stmt);
|
UpdateTreeUp(if_stmt);
|
||||||
body.insert(goto_stmt, *if_stmt);
|
body.insert(goto_stmt, *if_stmt);
|
||||||
}
|
}
|
||||||
|
|
@ -533,8 +402,7 @@ private:
|
||||||
switch (label_nested_stmt->type) {
|
switch (label_nested_stmt->type) {
|
||||||
case StatementType::If:
|
case StatementType::If:
|
||||||
// Update nested if condition
|
// Update nested if condition
|
||||||
label_nested_stmt->cond =
|
label_nested_stmt->cond = &pools.stmt.emplace_back(Or{}, variable, label_nested_stmt->cond, &root_stmt);
|
||||||
pool.Create(Or{}, variable, label_nested_stmt->cond, &root_stmt);
|
|
||||||
break;
|
break;
|
||||||
case StatementType::Loop:
|
case StatementType::Loop:
|
||||||
break;
|
break;
|
||||||
|
|
@ -542,7 +410,7 @@ private:
|
||||||
throw LogicError("Invalid inward movement");
|
throw LogicError("Invalid inward movement");
|
||||||
}
|
}
|
||||||
Tree& nested_tree{label_nested_stmt->children};
|
Tree& nested_tree{label_nested_stmt->children};
|
||||||
Statement* const new_goto{pool.Create(Goto{}, variable, label, &*label_nested_stmt)};
|
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, variable, label, &*label_nested_stmt)};
|
||||||
return nested_tree.insert(nested_tree.begin(), *new_goto);
|
return nested_tree.insert(nested_tree.begin(), *new_goto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,16 +424,16 @@ private:
|
||||||
Tree loop_body;
|
Tree loop_body;
|
||||||
loop_body.splice(loop_body.begin(), body, label_nested_stmt, goto_stmt);
|
loop_body.splice(loop_body.begin(), body, label_nested_stmt, goto_stmt);
|
||||||
SanitizeNoBreaks(loop_body);
|
SanitizeNoBreaks(loop_body);
|
||||||
Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const variable{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const loop_stmt{pool.Create(Loop{}, variable, std::move(loop_body), parent)};
|
Statement* const loop_stmt{&pools.stmt.emplace_back(Loop{}, variable, std::move(loop_body), parent)};
|
||||||
UpdateTreeUp(loop_stmt);
|
UpdateTreeUp(loop_stmt);
|
||||||
body.insert(goto_stmt, *loop_stmt);
|
body.insert(goto_stmt, *loop_stmt);
|
||||||
|
|
||||||
Statement* const new_goto{pool.Create(Goto{}, variable, label, loop_stmt)};
|
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, variable, label, loop_stmt)};
|
||||||
loop_stmt->children.push_front(*new_goto);
|
loop_stmt->children.push_front(*new_goto);
|
||||||
const Node new_goto_node{loop_stmt->children.begin()};
|
const Node new_goto_node{loop_stmt->children.begin()};
|
||||||
|
|
||||||
Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
|
Statement* const set_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
|
||||||
loop_stmt->children.push_back(*set_var);
|
loop_stmt->children.push_back(*set_var);
|
||||||
|
|
||||||
body.erase(goto_stmt);
|
body.erase(goto_stmt);
|
||||||
|
|
@ -577,22 +445,22 @@ private:
|
||||||
Tree& body{parent->children};
|
Tree& body{parent->children};
|
||||||
const u32 label_id{goto_stmt->label->id};
|
const u32 label_id{goto_stmt->label->id};
|
||||||
Statement* const goto_cond{goto_stmt->cond};
|
Statement* const goto_cond{goto_stmt->cond};
|
||||||
Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, &*parent)};
|
Statement* const set_goto_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, &*parent)};
|
||||||
body.insert(goto_stmt, *set_goto_var);
|
body.insert(goto_stmt, *set_goto_var);
|
||||||
|
|
||||||
Tree if_body;
|
Tree if_body;
|
||||||
if_body.splice(if_body.begin(), body, std::next(goto_stmt), body.end());
|
if_body.splice(if_body.begin(), body, std::next(goto_stmt), body.end());
|
||||||
if_body.pop_front();
|
if_body.pop_front();
|
||||||
Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const neg_cond{pool.Create(Not{}, cond, &root_stmt)};
|
Statement* const neg_cond{&pools.stmt.emplace_back(Not{}, cond, &root_stmt)};
|
||||||
Statement* const if_stmt{pool.Create(If{}, neg_cond, std::move(if_body), &*parent)};
|
Statement* const if_stmt{&pools.stmt.emplace_back(If{}, neg_cond, std::move(if_body), &*parent)};
|
||||||
UpdateTreeUp(if_stmt);
|
UpdateTreeUp(if_stmt);
|
||||||
body.insert(goto_stmt, *if_stmt);
|
body.insert(goto_stmt, *if_stmt);
|
||||||
|
|
||||||
body.erase(goto_stmt);
|
body.erase(goto_stmt);
|
||||||
|
|
||||||
Statement* const new_cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const new_cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const new_goto{pool.Create(Goto{}, new_cond, goto_stmt->label, parent->up)};
|
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, new_cond, goto_stmt->label, parent->up)};
|
||||||
Tree& parent_tree{parent->up->children};
|
Tree& parent_tree{parent->up->children};
|
||||||
return parent_tree.insert(std::next(parent), *new_goto);
|
return parent_tree.insert(std::next(parent), *new_goto);
|
||||||
}
|
}
|
||||||
|
|
@ -602,21 +470,21 @@ private:
|
||||||
Tree& body{parent->children};
|
Tree& body{parent->children};
|
||||||
const u32 label_id{goto_stmt->label->id};
|
const u32 label_id{goto_stmt->label->id};
|
||||||
Statement* const goto_cond{goto_stmt->cond};
|
Statement* const goto_cond{goto_stmt->cond};
|
||||||
Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
|
Statement* const set_goto_var{&pools.stmt.emplace_back(SetVariable{}, label_id, goto_cond, parent)};
|
||||||
Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const break_stmt{pool.Create(Break{}, cond, parent)};
|
Statement* const break_stmt{&pools.stmt.emplace_back(Break{}, cond, parent)};
|
||||||
body.insert(goto_stmt, *set_goto_var);
|
body.insert(goto_stmt, *set_goto_var);
|
||||||
body.insert(goto_stmt, *break_stmt);
|
body.insert(goto_stmt, *break_stmt);
|
||||||
body.erase(goto_stmt);
|
body.erase(goto_stmt);
|
||||||
|
|
||||||
const Node loop{Tree::s_iterator_to(*goto_stmt->up)};
|
const Node loop{Tree::s_iterator_to(*goto_stmt->up)};
|
||||||
Statement* const new_goto_cond{pool.Create(Variable{}, label_id, &root_stmt)};
|
Statement* const new_goto_cond{&pools.stmt.emplace_back(Variable{}, label_id, &root_stmt)};
|
||||||
Statement* const new_goto{pool.Create(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
|
Statement* const new_goto{&pools.stmt.emplace_back(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
|
||||||
Tree& parent_tree{loop->up->children};
|
Tree& parent_tree{loop->up->children};
|
||||||
return parent_tree.insert(std::next(loop), *new_goto);
|
return parent_tree.insert(std::next(loop), *new_goto);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectPool<Statement>& pool;
|
ShaderPools& pools;
|
||||||
Statement root_stmt{FunctionTag{}};
|
Statement root_stmt{FunctionTag{}};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -652,11 +520,11 @@ private:
|
||||||
|
|
||||||
class TranslatePass {
|
class TranslatePass {
|
||||||
public:
|
public:
|
||||||
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
TranslatePass(ShaderPools& pools_, Environment& env_, Statement& root_stmt, IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
|
||||||
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
|
: pools{pools_}
|
||||||
IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
|
, env{env_}
|
||||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
|
, syntax_list{syntax_list_}
|
||||||
syntax_list{syntax_list_} {
|
{
|
||||||
Visit(root_stmt, nullptr, nullptr);
|
Visit(root_stmt, nullptr, nullptr);
|
||||||
|
|
||||||
IR::Block& first_block{*syntax_list.front().data.block};
|
IR::Block& first_block{*syntax_list.front().data.block};
|
||||||
|
|
@ -674,7 +542,7 @@ private:
|
||||||
if (current_block) {
|
if (current_block) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current_block = block_pool.Create(inst_pool);
|
current_block = &pools.block.emplace_back(pools.inst);
|
||||||
auto& node{syntax_list.emplace_back()};
|
auto& node{syntax_list.emplace_back()};
|
||||||
node.type = IR::AbstractSyntaxNode::Type::Block;
|
node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
node.data.block = current_block;
|
node.data.block = current_block;
|
||||||
|
|
@ -740,7 +608,7 @@ private:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Loop: {
|
case StatementType::Loop: {
|
||||||
IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
|
IR::Block* const loop_header_block{&pools.block.emplace_back(pools.inst)};
|
||||||
if (current_block) {
|
if (current_block) {
|
||||||
current_block->AddBranch(loop_header_block);
|
current_block->AddBranch(loop_header_block);
|
||||||
}
|
}
|
||||||
|
|
@ -748,7 +616,7 @@ private:
|
||||||
header_node.type = IR::AbstractSyntaxNode::Type::Block;
|
header_node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
header_node.data.block = loop_header_block;
|
header_node.data.block = loop_header_block;
|
||||||
|
|
||||||
IR::Block* const continue_block{block_pool.Create(inst_pool)};
|
IR::Block* const continue_block{&pools.block.emplace_back(pools.inst)};
|
||||||
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
||||||
|
|
||||||
const size_t loop_node_index{syntax_list.size()};
|
const size_t loop_node_index{syntax_list.size()};
|
||||||
|
|
@ -814,7 +682,7 @@ private:
|
||||||
}
|
}
|
||||||
case StatementType::Return: {
|
case StatementType::Return: {
|
||||||
ensure_block();
|
ensure_block();
|
||||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
IR::Block* return_block{&pools.block.emplace_back(pools.inst)};
|
||||||
IR::IREmitter{*return_block}.Epilogue();
|
IR::IREmitter{*return_block}.Epilogue();
|
||||||
current_block->AddBranch(return_block);
|
current_block->AddBranch(return_block);
|
||||||
|
|
||||||
|
|
@ -862,10 +730,10 @@ private:
|
||||||
Statement* merge_stmt{TryFindForwardBlock(stmt)};
|
Statement* merge_stmt{TryFindForwardBlock(stmt)};
|
||||||
if (!merge_stmt) {
|
if (!merge_stmt) {
|
||||||
// Create a merge block we can visit later
|
// Create a merge block we can visit later
|
||||||
merge_stmt = stmt_pool.Create(&dummy_flow_block, &parent);
|
merge_stmt = &pools.stmt.emplace_back(&dummy_flow_block, &parent);
|
||||||
parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
|
parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
|
||||||
}
|
}
|
||||||
return block_pool.Create(inst_pool);
|
return &pools.block.emplace_back(pools.inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DemoteCombinationPass() {
|
void DemoteCombinationPass() {
|
||||||
|
|
@ -973,9 +841,7 @@ private:
|
||||||
asl.insert(next_it_2, demote_if_node);
|
asl.insert(next_it_2, demote_if_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectPool<Statement>& stmt_pool;
|
ShaderPools& pools;
|
||||||
ObjectPool<IR::Inst>& inst_pool;
|
|
||||||
ObjectPool<IR::Block>& block_pool;
|
|
||||||
Environment& env;
|
Environment& env;
|
||||||
IR::AbstractSyntaxList& syntax_list;
|
IR::AbstractSyntaxList& syntax_list;
|
||||||
bool uses_demote_to_helper{};
|
bool uses_demote_to_helper{};
|
||||||
|
|
@ -983,15 +849,12 @@ private:
|
||||||
};
|
};
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
IR::AbstractSyntaxList BuildASL(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||||
Environment& env, Flow::CFG& cfg,
|
GotoPass goto_pass{cfg, pools};
|
||||||
const HostTranslateInfo& host_info) {
|
|
||||||
ObjectPool<Statement> stmt_pool{64};
|
|
||||||
GotoPass goto_pass{cfg, stmt_pool};
|
|
||||||
Statement& root{goto_pass.RootStatement()};
|
Statement& root{goto_pass.RootStatement()};
|
||||||
IR::AbstractSyntaxList syntax_list;
|
IR::AbstractSyntaxList syntax_list;
|
||||||
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};
|
TranslatePass pass{pools, env, root, syntax_list, host_info};
|
||||||
stmt_pool.ReleaseContents();
|
pools.stmt.clear();
|
||||||
return syntax_list;
|
return syntax_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -8,15 +11,13 @@
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/value.h"
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
|
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
struct HostTranslateInfo;
|
struct HostTranslateInfo;
|
||||||
namespace Maxwell {
|
namespace Maxwell {
|
||||||
|
|
||||||
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
|
struct ShaderPools;
|
||||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
|
||||||
|
|
||||||
} // namespace Maxwell
|
} // namespace Maxwell
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "shader_recompiler/shader_pool.h"
|
||||||
#include "shader_recompiler/exception.h"
|
#include "shader_recompiler/exception.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||||
|
|
@ -240,10 +241,9 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
IR::Program TranslateProgram(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
|
||||||
IR::Program program;
|
IR::Program program;
|
||||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
|
program.syntax_list = BuildASL(pools, env, cfg, host_info);
|
||||||
program.blocks = GenerateBlocks(program.syntax_list);
|
program.blocks = GenerateBlocks(program.syntax_list);
|
||||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||||
program.stage = env.ShaderStage();
|
program.stage = env.ShaderStage();
|
||||||
|
|
@ -413,11 +413,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
IR::Program GenerateGeometryPassthrough(ShaderPools& pools, const HostTranslateInfo& host_info, IR::Program& source_program, Shader::OutputTopology output_topology) {
|
||||||
ObjectPool<IR::Block>& block_pool,
|
|
||||||
const HostTranslateInfo& host_info,
|
|
||||||
IR::Program& source_program,
|
|
||||||
Shader::OutputTopology output_topology) {
|
|
||||||
IR::Program program;
|
IR::Program program;
|
||||||
program.stage = Stage::Geometry;
|
program.stage = Stage::Geometry;
|
||||||
program.output_topology = output_topology;
|
program.output_topology = output_topology;
|
||||||
|
|
@ -429,16 +425,15 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||||
program.info.stores.Set(IR::Attribute::Layer, true);
|
program.info.stores.Set(IR::Attribute::Layer, true);
|
||||||
program.info.stores.Set(source_program.info.emulated_layer, false);
|
program.info.stores.Set(source_program.info.emulated_layer, false);
|
||||||
|
|
||||||
IR::Block* current_block = block_pool.Create(inst_pool);
|
IR::Block* current_block = &pools.block.emplace_back(pools.inst);
|
||||||
auto& node{program.syntax_list.emplace_back()};
|
auto& node{program.syntax_list.emplace_back()};
|
||||||
node.type = IR::AbstractSyntaxNode::Type::Block;
|
node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
node.data.block = current_block;
|
node.data.block = current_block;
|
||||||
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
EmitGeometryPassthrough(ir, program, program.info.stores, true,
|
EmitGeometryPassthrough(ir, program, program.info.stores, true, source_program.info.emulated_layer);
|
||||||
source_program.info.emulated_layer);
|
|
||||||
|
|
||||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
IR::Block* return_block{&pools.block.emplace_back(pools.inst)};
|
||||||
IR::IREmitter{*return_block}.Epilogue();
|
IR::IREmitter{*return_block}.Epilogue();
|
||||||
current_block->AddBranch(return_block);
|
current_block->AddBranch(return_block);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -7,7 +10,7 @@
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/program.h"
|
#include "shader_recompiler/frontend/ir/program.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||||
#include "shader_recompiler/runtime_info.h"
|
#include "shader_recompiler/runtime_info.h"
|
||||||
|
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
|
|
@ -16,22 +19,17 @@ struct HostTranslateInfo;
|
||||||
|
|
||||||
namespace Shader::Maxwell {
|
namespace Shader::Maxwell {
|
||||||
|
|
||||||
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
|
struct ShaderPools;
|
||||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
|
||||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
|
||||||
|
|
||||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
[[nodiscard]] IR::Program TranslateProgram(ShaderPools& pools, Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||||
Environment& env_vertex_b);
|
|
||||||
|
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b);
|
||||||
|
|
||||||
void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
|
void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
|
||||||
|
|
||||||
// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
|
// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
|
||||||
// This creates a workaround by setting the layer as a generic output and creating a
|
// This creates a workaround by setting the layer as a generic output and creating a
|
||||||
// passthrough geometry shader that reads the generic and sets the layer.
|
// passthrough geometry shader that reads the generic and sets the layer.
|
||||||
[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
[[nodiscard]] IR::Program GenerateGeometryPassthrough(ShaderPools& pools, const HostTranslateInfo& host_info, IR::Program& source_program, Shader::OutputTopology output_topology);
|
||||||
ObjectPool<IR::Block>& block_pool,
|
|
||||||
const HostTranslateInfo& host_info,
|
|
||||||
IR::Program& source_program,
|
|
||||||
Shader::OutputTopology output_topology);
|
|
||||||
|
|
||||||
} // namespace Shader::Maxwell
|
} // namespace Shader::Maxwell
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace Shader {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires std::is_destructible_v<T>
|
|
||||||
class ObjectPool {
|
|
||||||
public:
|
|
||||||
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
|
|
||||||
node = &chunks.emplace_back(new_chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
requires std::is_constructible_v<T, Args...>
|
|
||||||
[[nodiscard]] T* Create(Args&&... args) {
|
|
||||||
return std::construct_at(Memory(), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReleaseContents() {
|
|
||||||
if (chunks.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Chunk& root{chunks.front()};
|
|
||||||
if (root.used_objects == root.num_objects) {
|
|
||||||
// Root chunk has been filled, squash allocations into it
|
|
||||||
const size_t total_objects{root.num_objects + new_chunk_size * (chunks.size() - 1)};
|
|
||||||
chunks.clear();
|
|
||||||
chunks.emplace_back(total_objects);
|
|
||||||
} else {
|
|
||||||
root.Release();
|
|
||||||
chunks.resize(1);
|
|
||||||
}
|
|
||||||
chunks.shrink_to_fit();
|
|
||||||
node = &chunks.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct NonTrivialDummy {
|
|
||||||
NonTrivialDummy() noexcept {}
|
|
||||||
};
|
|
||||||
|
|
||||||
union Storage {
|
|
||||||
Storage() noexcept {}
|
|
||||||
~Storage() noexcept {}
|
|
||||||
|
|
||||||
NonTrivialDummy dummy{};
|
|
||||||
T object;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Chunk {
|
|
||||||
explicit Chunk() = default;
|
|
||||||
explicit Chunk(size_t size)
|
|
||||||
: num_objects{size}, storage{std::make_unique<Storage[]>(size)} {}
|
|
||||||
|
|
||||||
Chunk& operator=(Chunk&& rhs) noexcept {
|
|
||||||
Release();
|
|
||||||
used_objects = std::exchange(rhs.used_objects, 0);
|
|
||||||
num_objects = std::exchange(rhs.num_objects, 0);
|
|
||||||
storage = std::move(rhs.storage);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk(Chunk&& rhs) noexcept
|
|
||||||
: used_objects{std::exchange(rhs.used_objects, 0)},
|
|
||||||
num_objects{std::exchange(rhs.num_objects, 0)}, storage{std::move(rhs.storage)} {}
|
|
||||||
|
|
||||||
~Chunk() {
|
|
||||||
Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Release() {
|
|
||||||
std::destroy_n(storage.get(), used_objects);
|
|
||||||
used_objects = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t used_objects{};
|
|
||||||
size_t num_objects{};
|
|
||||||
std::unique_ptr<Storage[]> storage;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] T* Memory() {
|
|
||||||
Chunk* const chunk{FreeChunk()};
|
|
||||||
return &chunk->storage[chunk->used_objects++].object;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Chunk* FreeChunk() {
|
|
||||||
if (node->used_objects != node->num_objects) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
node = &chunks.emplace_back(new_chunk_size);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk* node{};
|
|
||||||
std::vector<Chunk> chunks;
|
|
||||||
size_t new_chunk_size{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Shader
|
|
||||||
134
src/shader_recompiler/shader_pool.h
Normal file
134
src/shader_recompiler/shader_pool.h
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
|
|
||||||
|
namespace Shader::Maxwell {
|
||||||
|
|
||||||
|
struct Statement;
|
||||||
|
|
||||||
|
// Use normal_link because we are not guaranteed to destroy the tree in order
|
||||||
|
using ListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||||
|
using Tree = boost::intrusive::list<Statement,
|
||||||
|
// Allow using Statement without a definition
|
||||||
|
boost::intrusive::base_hook<ListBaseHook>,
|
||||||
|
// Avoid linear complexity on splice, size is never called
|
||||||
|
boost::intrusive::constant_time_size<false>>;
|
||||||
|
using Node = Tree::iterator;
|
||||||
|
|
||||||
|
enum class StatementType {
|
||||||
|
Code,
|
||||||
|
Goto,
|
||||||
|
Label,
|
||||||
|
If,
|
||||||
|
Loop,
|
||||||
|
Break,
|
||||||
|
Return,
|
||||||
|
Kill,
|
||||||
|
Unreachable,
|
||||||
|
Function,
|
||||||
|
Identity,
|
||||||
|
Not,
|
||||||
|
Or,
|
||||||
|
SetVariable,
|
||||||
|
SetIndirectBranchVariable,
|
||||||
|
Variable,
|
||||||
|
IndirectBranchCond,
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool HasChildren(StatementType type) {
|
||||||
|
switch (type) {
|
||||||
|
case StatementType::If:
|
||||||
|
case StatementType::Loop:
|
||||||
|
case StatementType::Function:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Goto {};
|
||||||
|
struct Label {};
|
||||||
|
struct If {};
|
||||||
|
struct Loop {};
|
||||||
|
struct Break {};
|
||||||
|
struct Return {};
|
||||||
|
struct Kill {};
|
||||||
|
struct Unreachable {};
|
||||||
|
struct FunctionTag {};
|
||||||
|
struct Identity {};
|
||||||
|
struct Not {};
|
||||||
|
struct Or {};
|
||||||
|
struct SetVariable {};
|
||||||
|
struct SetIndirectBranchVariable {};
|
||||||
|
struct Variable {};
|
||||||
|
struct IndirectBranchCond {};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
||||||
|
#endif
|
||||||
|
struct Statement : ListBaseHook {
|
||||||
|
Statement(const Flow::Block* block_, Statement* up_) : block{block_}, up{up_}, type{StatementType::Code} {}
|
||||||
|
Statement(Goto, Statement* cond_, Node label_, Statement* up_) : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
||||||
|
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
||||||
|
Statement(If, Statement* cond_, Tree&& children_, Statement* up_) : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
|
||||||
|
Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_) : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
|
||||||
|
Statement(Break, Statement* cond_, Statement* up_) : cond{cond_}, up{up_}, type{StatementType::Break} {}
|
||||||
|
Statement(Return, Statement* up_) : up{up_}, type{StatementType::Return} {}
|
||||||
|
Statement(Kill, Statement* up_) : up{up_}, type{StatementType::Kill} {}
|
||||||
|
Statement(Unreachable, Statement* up_) : up{up_}, type{StatementType::Unreachable} {}
|
||||||
|
Statement(FunctionTag) : children{}, type{StatementType::Function} {}
|
||||||
|
Statement(Identity, IR::Condition cond_, Statement* up_) : guest_cond{cond_}, up{up_}, type{StatementType::Identity} {}
|
||||||
|
Statement(Not, Statement* op_, Statement* up_) : op{op_}, up{up_}, type{StatementType::Not} {}
|
||||||
|
Statement(Or, Statement* op_a_, Statement* op_b_, Statement* up_) : op_a{op_a_}, op_b{op_b_}, up{up_}, type{StatementType::Or} {}
|
||||||
|
Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
|
||||||
|
Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_) : branch_offset{branch_offset_}, branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {}
|
||||||
|
Statement(Variable, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Variable} {}
|
||||||
|
Statement(IndirectBranchCond, u32 location_, Statement* up_) : location{location_}, up{up_}, type{StatementType::IndirectBranchCond} {}
|
||||||
|
~Statement() {
|
||||||
|
if (HasChildren(type)) {
|
||||||
|
std::destroy_at(&children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
union {
|
||||||
|
const Flow::Block* block;
|
||||||
|
Node label;
|
||||||
|
Tree children;
|
||||||
|
IR::Condition guest_cond;
|
||||||
|
Statement* op;
|
||||||
|
Statement* op_a;
|
||||||
|
u32 location;
|
||||||
|
s32 branch_offset;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
Statement* cond;
|
||||||
|
Statement* op_b;
|
||||||
|
u32 id;
|
||||||
|
IR::Reg branch_reg;
|
||||||
|
};
|
||||||
|
Statement* up{};
|
||||||
|
StatementType type;
|
||||||
|
};
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ShaderPools {
|
||||||
|
void clear() {
|
||||||
|
flow_block.clear();
|
||||||
|
block.clear();
|
||||||
|
inst.clear();
|
||||||
|
stmt.clear();
|
||||||
|
}
|
||||||
|
boost::container::stable_vector<Shader::IR::Inst> inst{};
|
||||||
|
boost::container::stable_vector<Shader::IR::Block> block{};
|
||||||
|
boost::container::stable_vector<Shader::Maxwell::Flow::Block> flow_block{};
|
||||||
|
boost::container::stable_vector<Shader::Maxwell::Statement> stmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,6 @@ using Shader::Backend::GLASM::EmitGLASM;
|
||||||
using Shader::Backend::GLSL::EmitGLSL;
|
using Shader::Backend::GLSL::EmitGLSL;
|
||||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
using Shader::Backend::SPIRV::EmitSPIRV;
|
||||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
using Shader::Maxwell::ConvertLegacyToGeneric;
|
||||||
using Shader::Maxwell::GenerateGeometryPassthrough;
|
|
||||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
using Shader::Maxwell::MergeDualVertexPrograms;
|
||||||
using Shader::Maxwell::TranslateProgram;
|
using Shader::Maxwell::TranslateProgram;
|
||||||
using VideoCommon::ComputeEnvironment;
|
using VideoCommon::ComputeEnvironment;
|
||||||
|
|
@ -298,7 +297,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||||
ComputePipelineKey key;
|
ComputePipelineKey key;
|
||||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||||
queue_work([this, key, env_ = std::move(env), &state, &callback](Context* ctx) mutable {
|
queue_work([this, key, env_ = std::move(env), &state, &callback](Context* ctx) mutable {
|
||||||
ctx->pools.ReleaseContents();
|
ctx->pools.clear();
|
||||||
auto pipeline{CreateComputePipeline(ctx->pools, key, env_, true)};
|
auto pipeline{CreateComputePipeline(ctx->pools, key, env_, true)};
|
||||||
std::scoped_lock lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
|
|
@ -319,7 +318,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||||
for (auto& env : envs_) {
|
for (auto& env : envs_) {
|
||||||
env_ptrs.push_back(&env);
|
env_ptrs.push_back(&env);
|
||||||
}
|
}
|
||||||
ctx->pools.ReleaseContents();
|
ctx->pools.clear();
|
||||||
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
|
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
|
||||||
std::scoped_lock lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
|
|
@ -433,9 +432,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
|
||||||
GraphicsEnvironments environments;
|
GraphicsEnvironments environments;
|
||||||
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
||||||
|
|
||||||
main_pools.ReleaseContents();
|
main_pools.clear();
|
||||||
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(),
|
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), use_asynchronous_shaders)};
|
||||||
use_asynchronous_shaders)};
|
|
||||||
if (!pipeline || shader_cache_filename.empty()) {
|
if (!pipeline || shader_cache_filename.empty()) {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
@ -449,10 +447,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs, bool use_shader_workers, bool force_context_flush) try {
|
||||||
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
|
|
||||||
std::span<Shader::Environment* const> envs, bool use_shader_workers,
|
|
||||||
bool force_context_flush) try {
|
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
LOG_INFO(Render_OpenGL, "0x{:016x}", hash);
|
LOG_INFO(Render_OpenGL, "0x{:016x}", hash);
|
||||||
size_t env_index{};
|
size_t env_index{};
|
||||||
|
|
@ -465,12 +460,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||||
Shader::IR::Program* layer_source_program{};
|
Shader::IR::Program* layer_source_program{};
|
||||||
|
|
||||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||||
const bool is_emulated_stage = layer_source_program != nullptr
|
const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry);
|
||||||
&& index == u32(Maxwell::ShaderType::Geometry);
|
|
||||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||||
auto topology = MaxwellToOutputTopology(key.gs_input_topology);
|
auto topology = MaxwellToOutputTopology(key.gs_input_topology);
|
||||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
programs[index] = Shader::Maxwell::GenerateGeometryPassthrough(pools, host_info, *layer_source_program, topology);
|
||||||
*layer_source_program, topology);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (key.unique_hashes[index] == 0) {
|
if (key.unique_hashes[index] == 0) {
|
||||||
|
|
@ -488,13 +481,13 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||||
|
|
||||||
if (!uses_vertex_a || index != 1) {
|
if (!uses_vertex_a || index != 1) {
|
||||||
// Normal path
|
// Normal path
|
||||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
programs[index] = TranslateProgram(pools, env, cfg, host_info);
|
||||||
|
|
||||||
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
|
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
|
||||||
} else {
|
} else {
|
||||||
// VertexB path when VertexA is present.
|
// VertexB path when VertexA is present.
|
||||||
auto& program_va{programs[0]};
|
auto& program_va{programs[0]};
|
||||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
auto program_vb{TranslateProgram(pools, env, cfg, host_info)};
|
||||||
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
|
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
|
||||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||||
}
|
}
|
||||||
|
|
@ -561,7 +554,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||||
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
||||||
env.SetCachedSize(shader->size_bytes);
|
env.SetCachedSize(shader->size_bytes);
|
||||||
|
|
||||||
main_pools.ReleaseContents();
|
main_pools.clear();
|
||||||
auto pipeline{CreateComputePipeline(main_pools, key, env)};
|
auto pipeline{CreateComputePipeline(main_pools, key, env)};
|
||||||
if (!pipeline || shader_cache_filename.empty()) {
|
if (!pipeline || shader_cache_filename.empty()) {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
|
|
@ -571,9 +564,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env, bool force_context_flush) try {
|
||||||
ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
|
|
||||||
bool force_context_flush) try {
|
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
LOG_INFO(Render_OpenGL, "0x{:016x}", hash);
|
LOG_INFO(Render_OpenGL, "0x{:016x}", hash);
|
||||||
|
|
||||||
|
|
@ -583,7 +574,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||||
env.Dump(hash, key.unique_hash);
|
env.Dump(hash, key.unique_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
auto program{TranslateProgram(pools, env, cfg, host_info)};
|
||||||
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
||||||
Shader::RuntimeInfo info;
|
Shader::RuntimeInfo info;
|
||||||
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
#include "shader_recompiler/host_translate_info.h"
|
#include "shader_recompiler/host_translate_info.h"
|
||||||
|
#include "shader_recompiler/shader_pool.h"
|
||||||
#include "shader_recompiler/profile.h"
|
#include "shader_recompiler/profile.h"
|
||||||
#include "video_core/renderer_opengl/gl_compute_pipeline.h"
|
#include "video_core/renderer_opengl/gl_compute_pipeline.h"
|
||||||
#include "video_core/renderer_opengl/gl_graphics_pipeline.h"
|
#include "video_core/renderer_opengl/gl_graphics_pipeline.h"
|
||||||
|
|
@ -51,20 +52,9 @@ private:
|
||||||
[[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept;
|
[[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept;
|
||||||
|
|
||||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
||||||
|
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs, bool use_shader_workers, bool force_context_flush = false);
|
||||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key, const VideoCommon::ShaderInfo* shader);
|
||||||
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
|
std::unique_ptr<ComputePipeline> CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env, bool force_context_flush = false);
|
||||||
std::span<Shader::Environment* const> envs, bool use_shader_workers,
|
|
||||||
bool force_context_flush = false);
|
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
|
|
||||||
const VideoCommon::ShaderInfo* shader);
|
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
|
|
||||||
const ComputePipelineKey& key,
|
|
||||||
Shader::Environment& env,
|
|
||||||
bool force_context_flush = false);
|
|
||||||
|
|
||||||
std::unique_ptr<ShaderWorker> CreateWorkers() const;
|
std::unique_ptr<ShaderWorker> CreateWorkers() const;
|
||||||
|
|
||||||
Core::Frontend::EmuWindow& emu_window;
|
Core::Frontend::EmuWindow& emu_window;
|
||||||
|
|
@ -81,7 +71,7 @@ private:
|
||||||
GraphicsPipelineKey graphics_key{};
|
GraphicsPipelineKey graphics_key{};
|
||||||
GraphicsPipeline* current_pipeline{};
|
GraphicsPipeline* current_pipeline{};
|
||||||
|
|
||||||
ShaderContext::ShaderPools main_pools;
|
Shader::Maxwell::ShaderPools main_pools;
|
||||||
ankerl::unordered_dense::map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
ankerl::unordered_dense::map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
||||||
ankerl::unordered_dense::map<ComputePipelineKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
ankerl::unordered_dense::map<ComputePipelineKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,26 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/frontend/graphics_context.h"
|
#include "core/frontend/graphics_context.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||||
|
|
||||||
namespace OpenGL::ShaderContext {
|
namespace OpenGL::ShaderContext {
|
||||||
struct ShaderPools {
|
|
||||||
void ReleaseContents() {
|
|
||||||
flow_block.ReleaseContents();
|
|
||||||
block.ReleaseContents();
|
|
||||||
inst.ReleaseContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader::ObjectPool<Shader::IR::Inst> inst{8192};
|
|
||||||
Shader::ObjectPool<Shader::IR::Block> block{32};
|
|
||||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
explicit Context(Core::Frontend::EmuWindow& emu_window)
|
explicit Context(Core::Frontend::EmuWindow& emu_window) : gl_context{emu_window.CreateSharedContext()}, scoped{*gl_context} {}
|
||||||
: gl_context{emu_window.CreateSharedContext()}, scoped{*gl_context} {}
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> gl_context;
|
std::unique_ptr<Core::Frontend::GraphicsContext> gl_context;
|
||||||
Core::Frontend::GraphicsContext::Scoped scoped;
|
Core::Frontend::GraphicsContext::Scoped scoped;
|
||||||
ShaderPools pools;
|
Shader::Maxwell::ShaderPools pools;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL::ShaderContext
|
} // namespace OpenGL::ShaderContext
|
||||||
|
|
|
||||||
|
|
@ -48,15 +48,6 @@
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using Shader::Backend::SPIRV::EmitSPIRV;
|
|
||||||
using Shader::Maxwell::ConvertLegacyToGeneric;
|
|
||||||
using Shader::Maxwell::GenerateGeometryPassthrough;
|
|
||||||
using Shader::Maxwell::MergeDualVertexPrograms;
|
|
||||||
using Shader::Maxwell::TranslateProgram;
|
|
||||||
using VideoCommon::ComputeEnvironment;
|
|
||||||
using VideoCommon::FileEnvironment;
|
|
||||||
using VideoCommon::GenericEnvironment;
|
|
||||||
using VideoCommon::GraphicsEnvironment;
|
|
||||||
|
|
||||||
constexpr u32 CACHE_VERSION = 16;
|
constexpr u32 CACHE_VERSION = 16;
|
||||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||||
|
|
@ -568,12 +559,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||||
if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
|
if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
|
||||||
state.statistics = std::make_unique<PipelineStatistics>(device);
|
state.statistics = std::make_unique<PipelineStatistics>(device);
|
||||||
}
|
}
|
||||||
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
|
const auto load_compute{[&](std::ifstream& file, VideoCommon::FileEnvironment env) {
|
||||||
ComputePipelineCacheKey key;
|
ComputePipelineCacheKey key;
|
||||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||||
|
|
||||||
workers.QueueWork([this, key, env_ = std::move(env), &state, &callback]() mutable {
|
workers.QueueWork([this, key, env_ = std::move(env), &state, &callback]() mutable {
|
||||||
ShaderPools pools;
|
Shader::Maxwell::ShaderPools pools;
|
||||||
auto pipeline{CreateComputePipeline(pools, key, env_, state.statistics.get(), false)};
|
auto pipeline{CreateComputePipeline(pools, key, env_, state.statistics.get(), false)};
|
||||||
std::scoped_lock lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
|
|
@ -586,7 +577,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||||
});
|
});
|
||||||
++state.total;
|
++state.total;
|
||||||
}};
|
}};
|
||||||
const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
|
const auto load_graphics{[&](std::ifstream& file, std::vector<VideoCommon::FileEnvironment> envs) {
|
||||||
GraphicsPipelineCacheKey key;
|
GraphicsPipelineCacheKey key;
|
||||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||||
|
|
||||||
|
|
@ -604,7 +595,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable {
|
workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable {
|
||||||
ShaderPools pools;
|
Shader::Maxwell::ShaderPools pools;
|
||||||
boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
|
boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
|
||||||
for (auto& env : envs_) {
|
for (auto& env : envs_) {
|
||||||
env_ptrs.push_back(&env);
|
env_ptrs.push_back(&env);
|
||||||
|
|
@ -682,10 +673,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineCacheKey& key, std::span<Shader::Environment* const> envs, PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
|
||||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
|
||||||
bool build_in_parallel) try {
|
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
LOG_INFO(Render_Vulkan, "0x{:016x}", hash);
|
LOG_INFO(Render_Vulkan, "0x{:016x}", hash);
|
||||||
size_t env_index{0};
|
size_t env_index{0};
|
||||||
|
|
@ -697,12 +685,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
Shader::IR::Program* layer_source_program{};
|
Shader::IR::Program* layer_source_program{};
|
||||||
|
|
||||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||||
const bool is_emulated_stage = layer_source_program != nullptr &&
|
const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry);
|
||||||
index == static_cast<u32>(Maxwell::ShaderType::Geometry);
|
|
||||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||||
auto topology = MaxwellToOutputTopology(key.state.topology);
|
auto topology = MaxwellToOutputTopology(key.state.topology);
|
||||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
programs[index] = Shader::Maxwell::GenerateGeometryPassthrough(pools, host_info, *layer_source_program, topology);
|
||||||
*layer_source_program, topology);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (key.unique_hashes[index] == 0) {
|
if (key.unique_hashes[index] == 0) {
|
||||||
|
|
@ -711,16 +697,16 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
Shader::Environment& env{*envs[env_index]};
|
Shader::Environment& env{*envs[env_index]};
|
||||||
++env_index;
|
++env_index;
|
||||||
|
|
||||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
const u32 cfg_offset{u32(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||||
if (!uses_vertex_a || index != 1) {
|
if (!uses_vertex_a || index != 1) {
|
||||||
// Normal path
|
// Normal path
|
||||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
programs[index] = Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info);
|
||||||
} else {
|
} else {
|
||||||
// VertexB path when VertexA is present.
|
// VertexB path when VertexA is present.
|
||||||
auto& program_va{programs[0]};
|
auto& program_va{programs[0]};
|
||||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
auto program_vb{Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info)};
|
||||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
programs[index] = Shader::Maxwell::MergeDualVertexPrograms(program_va, program_vb, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.dump_shaders) {
|
if (Settings::values.dump_shaders) {
|
||||||
|
|
@ -750,8 +736,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
infos[stage_index] = &program.info;
|
infos[stage_index] = &program.info;
|
||||||
|
|
||||||
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage, device)};
|
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage, device)};
|
||||||
ConvertLegacyToGeneric(program, runtime_info);
|
Shader::Maxwell::ConvertLegacyToGeneric(program, runtime_info);
|
||||||
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
|
const std::vector<u32> code{Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
|
||||||
device.SaveShader(code);
|
device.SaveShader(code);
|
||||||
modules[stage_index] = BuildShader(device, code);
|
modules[stage_index] = BuildShader(device, code);
|
||||||
|
|
||||||
|
|
@ -799,14 +785,14 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
||||||
GraphicsEnvironments environments;
|
GraphicsEnvironments environments;
|
||||||
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
||||||
|
|
||||||
main_pools.ReleaseContents();
|
main_pools.clear();
|
||||||
auto pipeline{
|
auto pipeline{
|
||||||
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
|
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
|
||||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] {
|
serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] {
|
||||||
boost::container::static_vector<const GenericEnvironment*, Maxwell::MaxShaderProgram>
|
boost::container::static_vector<const VideoCommon::GenericEnvironment*, Maxwell::MaxShaderProgram>
|
||||||
env_ptrs;
|
env_ptrs;
|
||||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||||
if (key.unique_hashes[index] != 0) {
|
if (key.unique_hashes[index] != 0) {
|
||||||
|
|
@ -822,24 +808,22 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||||
const ComputePipelineCacheKey& key, const ShaderInfo* shader) {
|
const ComputePipelineCacheKey& key, const ShaderInfo* shader) {
|
||||||
const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
|
const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
|
||||||
const auto& qmd{kepler_compute->launch_description};
|
const auto& qmd{kepler_compute->launch_description};
|
||||||
ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
VideoCommon::ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
|
||||||
env.SetCachedSize(shader->size_bytes);
|
env.SetCachedSize(shader->size_bytes);
|
||||||
|
|
||||||
main_pools.ReleaseContents();
|
main_pools.clear();
|
||||||
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
|
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
|
||||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
serialization_thread.QueueWork([this, key, env_ = std::move(env)] {
|
serialization_thread.QueueWork([this, key, env_ = std::move(env)] {
|
||||||
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
|
SerializePipeline(key, std::array<const VideoCommon::GenericEnvironment*, 1>{&env_},
|
||||||
pipeline_cache_filename, CACHE_VERSION);
|
pipeline_cache_filename, CACHE_VERSION);
|
||||||
});
|
});
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(Shader::Maxwell::ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||||
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
|
|
||||||
PipelineStatistics* statistics, bool build_in_parallel) try {
|
|
||||||
auto hash = key.Hash();
|
auto hash = key.Hash();
|
||||||
if (device.HasBrokenCompute()) {
|
if (device.HasBrokenCompute()) {
|
||||||
LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", hash);
|
LOG_ERROR(Render_Vulkan, "Skipping 0x{:016x}", hash);
|
||||||
|
|
@ -855,11 +839,9 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||||
env.Dump(hash, key.unique_hash);
|
env.Dump(hash, key.unique_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
auto program{Shader::Maxwell::TranslateProgram(pools, env, cfg, host_info)};
|
||||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||||
const bool needs_shared_mem_clamp =
|
const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
|
||||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
|
||||||
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
||||||
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
|
|
@ -869,7 +851,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||||
max_shared_memory / 1024);
|
max_shared_memory / 1024);
|
||||||
program.shared_memory_size = max_shared_memory;
|
program.shared_memory_size = max_shared_memory;
|
||||||
}
|
}
|
||||||
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
|
const std::vector<u32> code{Shader::Backend::SPIRV::EmitSPIRV(profile, program, this->optimize_spirv_output)};
|
||||||
device.SaveShader(code);
|
device.SaveShader(code);
|
||||||
vk::ShaderModule spv_module{BuildShader(device, code)};
|
vk::ShaderModule spv_module{BuildShader(device, code)};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,20 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <ankerl/unordered_dense.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <ankerl/unordered_dense.h>
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/value.h"
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
|
||||||
|
#include "shader_recompiler/frontend/maxwell/translate_program.h"
|
||||||
#include "shader_recompiler/host_translate_info.h"
|
#include "shader_recompiler/host_translate_info.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
#include "shader_recompiler/profile.h"
|
#include "shader_recompiler/profile.h"
|
||||||
|
#include "shader_recompiler/shader_pool.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||||
|
|
@ -88,18 +91,6 @@ class Scheduler;
|
||||||
|
|
||||||
using VideoCommon::ShaderInfo;
|
using VideoCommon::ShaderInfo;
|
||||||
|
|
||||||
struct ShaderPools {
|
|
||||||
void ReleaseContents() {
|
|
||||||
flow_block.ReleaseContents();
|
|
||||||
block.ReleaseContents();
|
|
||||||
inst.ReleaseContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader::ObjectPool<Shader::IR::Inst> inst{8192};
|
|
||||||
Shader::ObjectPool<Shader::IR::Block> block{32};
|
|
||||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
|
|
||||||
};
|
|
||||||
|
|
||||||
class PipelineCache : public VideoCommon::ShaderCache {
|
class PipelineCache : public VideoCommon::ShaderCache {
|
||||||
public:
|
public:
|
||||||
explicit PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device,
|
explicit PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device,
|
||||||
|
|
@ -124,14 +115,14 @@ private:
|
||||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline();
|
||||||
|
|
||||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
||||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
Shader::Maxwell::ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||||
bool build_in_parallel);
|
bool build_in_parallel);
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
|
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
|
||||||
const ShaderInfo* shader);
|
const ShaderInfo* shader);
|
||||||
|
|
||||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
|
std::unique_ptr<ComputePipeline> CreateComputePipeline(Shader::Maxwell::ShaderPools& pools,
|
||||||
const ComputePipelineCacheKey& key,
|
const ComputePipelineCacheKey& key,
|
||||||
Shader::Environment& env,
|
Shader::Environment& env,
|
||||||
PipelineStatistics* statistics,
|
PipelineStatistics* statistics,
|
||||||
|
|
@ -161,7 +152,7 @@ private:
|
||||||
ankerl::unordered_dense::map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
ankerl::unordered_dense::map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
||||||
ankerl::unordered_dense::map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
ankerl::unordered_dense::map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
||||||
|
|
||||||
ShaderPools main_pools;
|
Shader::Maxwell::ShaderPools main_pools;
|
||||||
|
|
||||||
Shader::Profile profile;
|
Shader::Profile profile;
|
||||||
Shader::HostTranslateInfo host_info;
|
Shader::HostTranslateInfo host_info;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
|
||||||
#include "video_core/control/channel_state.h"
|
#include "video_core/control/channel_state.h"
|
||||||
#include "video_core/dirty_flags.h"
|
#include "video_core/dirty_flags.h"
|
||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
|
|
@ -236,7 +235,7 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
|
||||||
} else {
|
} else {
|
||||||
// Slow path, not really hit on commercial games
|
// Slow path, not really hit on commercial games
|
||||||
// Build a control flow graph to get the real shader size
|
// Build a control flow graph to get the real shader size
|
||||||
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
|
boost::container::stable_vector<Shader::Maxwell::Flow::Block> flow_block;
|
||||||
Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
|
Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
|
||||||
info->unique_hash = env.CalculateHash();
|
info->unique_hash = env.CalculateHash();
|
||||||
info->size_bytes = env.ReadSizeBytes();
|
info->size_bytes = env.ReadSizeBytes();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue