// 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/node.h" #include "video_core/shader/node_helper.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 result = FindOperation( conditional_code, static_cast(conditional_code.size() - 1), operation_code); auto& found = result.first; if (found) { return {std::move(found), cursor}; } } } return {}; } std::optional> DecoupleIndirectRead(const OperationNode& operation) { if (operation.GetCode() != OperationCode::UAdd) { return std::nullopt; } Node gpr; Node offset; ASSERT(operation.GetOperandsCount() == 2); for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) { Node operand = operation[i]; if (std::holds_alternative(*operand)) { offset = operation[i]; } else if (std::holds_alternative(*operand)) { gpr = operation[i]; } } if (offset && gpr) { return std::make_pair(gpr, offset); } return std::nullopt; } bool AmendNodeCv(std::size_t amend_index, Node node) { if (const auto operation = std::get_if(&*node)) { operation->SetAmendIndex(amend_index); return true; } else if (const auto conditional = std::get_if(&*node)) { conditional->SetAmendIndex(amend_index); return true; } return false; } } // Anonymous namespace std::tuple ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, s64 cursor) { 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)) { auto track = MakeTrackSampler(cbuf->GetIndex(), immediate->GetValue()); return {tracked, track}; } else if (const auto operation = std::get_if(&*offset)) { const u32 bound_buffer = registry.GetBoundBuffer(); if (bound_buffer != cbuf->GetIndex()) { return {}; } const auto pair = DecoupleIndirectRead(*operation); if (!pair) { return {}; } auto [gpr, base_offset] = *pair; const auto offset_inm = std::get_if(&*base_offset); const auto& gpu_driver = registry.AccessGuestDriverProfile(); const u32 bindless_cv = NewCustomVariable(); const Node op = Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize())); const Node cv_node = GetCustomVariable(bindless_cv); Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op)); const std::size_t amend_index = DeclareAmend(amend_op); AmendNodeCv(amend_index, code[cursor]); // TODO Implement Bindless Index custom variable auto track = MakeTrackSampler(cbuf->GetIndex(), offset_inm->GetValue(), bindless_cv); return {tracked, track}; } 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 TrackBindlessSampler(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 = TrackBindlessSampler((*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 TrackBindlessSampler(tracked, conditional_code, static_cast(conditional_code.size())); } return {}; } 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 {}; } s64 current_cursor = cursor; while (current_cursor > 0) { // 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, current_cursor - 1); current_cursor = new_cursor; if (!source) { continue; } const auto [base_address, index, offset] = TrackCbuf(source, code, current_cursor); if (base_address != nullptr) { return {base_address, index, offset}; } } return {}; } 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 result = TrackRegister(&std::get(*tracked), code, cursor - 1); const auto& found = result.first; 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