// Copyright 2018 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include "common/common_types.h" #include "video_core/shader/shader_ir.h" namespace VideoCommon::Shader { namespace { std::pair FindOperation(const NodeBlock& code, s64 cursor, OperationCode operation_code) { for (; cursor >= 0; --cursor) { Node node = code.at(cursor); if (const auto operation = std::get_if(&*node)) { if (operation->GetCode() == operation_code) { return {std::move(node), cursor}; } } if (const auto conditional = std::get_if(&*node)) { const auto& conditional_code = conditional->GetCode(); auto [found, internal_cursor] = FindOperation( conditional_code, static_cast(conditional_code.size() - 1), operation_code); if (found) { return {std::move(found), cursor}; } } } return {}; } } // Anonymous namespace std::tuple ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const { if (const auto cbuf = std::get_if(&*tracked)) { // Constant buffer found, test if it's an immediate const auto offset = cbuf->GetOffset(); if (const auto immediate = std::get_if(&*offset)) { return {tracked, cbuf->GetIndex(), immediate->GetValue()}; } return {}; } if (const auto gpr = std::get_if(&*tracked)) { if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { return {}; } // Reduce the cursor in one to avoid infinite loops when the instruction sets the same // register that it uses as operand const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); if (!source) { return {}; } return TrackCbuf(source, code, new_cursor); } if (const auto operation = std::get_if(&*tracked)) { for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { if (auto found = TrackCbuf((*operation)[i - 1], code, cursor); std::get<0>(found)) { // Cbuf found in operand. return found; } } return {}; } if (const auto conditional = std::get_if(&*tracked)) { const auto& conditional_code = conditional->GetCode(); return TrackCbuf(tracked, conditional_code, static_cast(conditional_code.size())); } return {}; } std::optional ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const { // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register // that it uses as operand const auto [found, found_cursor] = TrackRegister(&std::get(*tracked), code, cursor - 1); if (!found) { return {}; } if (const auto immediate = std::get_if(&*found)) { return immediate->GetValue(); } return {}; } std::pair ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor) const { for (; cursor >= 0; --cursor) { const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); if (!found_node) { return {}; } const auto operation = std::get_if(&*found_node); ASSERT(operation); const auto& target = (*operation)[0]; if (const auto gpr_target = std::get_if(&*target)) { if (gpr_target->GetIndex() == tracked->GetIndex()) { return {(*operation)[1], new_cursor}; } } } return {}; } } // namespace VideoCommon::Shader