diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 327db68a54..e9e3243868 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -59,6 +59,33 @@ add_library(video_core STATIC renderer_opengl/renderer_opengl.h renderer_opengl/utils.cpp renderer_opengl/utils.h + shader/decode/arithmetic.cpp + shader/decode/arithmetic_immediate.cpp + shader/decode/bfe.cpp + shader/decode/bfi.cpp + shader/decode/shift.cpp + shader/decode/arithmetic_integer.cpp + shader/decode/arithmetic_integer_immediate.cpp + shader/decode/arithmetic_half.cpp + shader/decode/arithmetic_half_immediate.cpp + shader/decode/ffma.cpp + shader/decode/hfma2.cpp + shader/decode/conversion.cpp + shader/decode/memory.cpp + shader/decode/float_set_predicate.cpp + shader/decode/integer_set_predicate.cpp + shader/decode/half_set_predicate.cpp + shader/decode/predicate_set_register.cpp + shader/decode/predicate_set_predicate.cpp + shader/decode/register_set_predicate.cpp + shader/decode/float_set.cpp + shader/decode/integer_set.cpp + shader/decode/half_set.cpp + shader/decode/xmad.cpp + shader/decode/other.cpp + shader/decode.cpp + shader/shader_ir.cpp + shader/shader_ir.h surface.cpp surface.h textures/astc.cpp diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e62b66acdf..b1e0d763e8 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -397,6 +397,10 @@ struct IpaMode { bool operator!=(const IpaMode& a) const { return !operator==(a); } + bool operator<(const IpaMode& a) const { + return std::tie(interpolation_mode, sampling_mode) < + std::tie(a.interpolation_mode, a.sampling_mode); + } }; enum class SystemVariable : u64 { diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp new file mode 100644 index 0000000000..c973328abd --- /dev/null +++ b/src/video_core/shader/decode.cpp @@ -0,0 +1,199 @@ +// 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/engines/shader_bytecode.h" +#include "video_core/engines/shader_header.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +/// Merges exit method of two parallel branches. +constexpr ExitMethod ParallelExit(ExitMethod a, ExitMethod b) { + if (a == ExitMethod::Undetermined) { + return b; + } + if (b == ExitMethod::Undetermined) { + return a; + } + if (a == b) { + return a; + } + return ExitMethod::Conditional; +} + +/** + * Returns whether the instruction at the specified offset is a 'sched' instruction. + * Sched instructions always appear before a sequence of 3 instructions. + */ +constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { + constexpr u32 SchedPeriod = 4; + u32 absolute_offset = offset - main_offset; + + return (absolute_offset % SchedPeriod) == 0; +} + +void ShaderIR::Decode() { + std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); + + std::set labels; + const ExitMethod exit_method = Scan(main_offset, MAX_PROGRAM_LENGTH, labels); + if (exit_method != ExitMethod::AlwaysEnd) { + UNREACHABLE_MSG("Program does not always end"); + } + + if (labels.empty()) { + basic_blocks.insert({main_offset, DecodeRange(main_offset, MAX_PROGRAM_LENGTH)}); + return; + } + + labels.insert(main_offset); + + for (const u32 label : labels) { + const auto next_it = labels.lower_bound(label + 1); + const u32 next_label = next_it == labels.end() ? MAX_PROGRAM_LENGTH : *next_it; + + basic_blocks.insert({label, DecodeRange(label, next_label)}); + } +} + +ExitMethod ShaderIR::Scan(u32 begin, u32 end, std::set& labels) { + const auto [iter, inserted] = + exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined); + ExitMethod& exit_method = iter->second; + if (!inserted) + return exit_method; + + for (u32 offset = begin; offset != end && offset != MAX_PROGRAM_LENGTH; ++offset) { + coverage_begin = std::min(coverage_begin, offset); + coverage_end = std::max(coverage_end, offset + 1); + + const Instruction instr = {program_code[offset]}; + const auto opcode = OpCode::Decode(instr); + if (!opcode) + continue; + switch (opcode->get().GetId()) { + case OpCode::Id::EXIT: { + // The EXIT instruction can be predicated, which means that the shader can conditionally + // end on this instruction. We have to consider the case where the condition is not met + // and check the exit method of that other basic block. + using Tegra::Shader::Pred; + if (instr.pred.pred_index == static_cast(Pred::UnusedIndex)) { + return exit_method = ExitMethod::AlwaysEnd; + } else { + const ExitMethod not_met = Scan(offset + 1, end, labels); + return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met); + } + } + case OpCode::Id::BRA: { + const u32 target = offset + instr.bra.GetBranchTarget(); + labels.insert(target); + const ExitMethod no_jmp = Scan(offset + 1, end, labels); + const ExitMethod jmp = Scan(target, end, labels); + return exit_method = ParallelExit(no_jmp, jmp); + } + case OpCode::Id::SSY: + case OpCode::Id::PBK: { + // The SSY and PBK use a similar encoding as the BRA instruction. + UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, + "Constant buffer branching is not supported"); + const u32 target = offset + instr.bra.GetBranchTarget(); + labels.insert(target); + // Continue scanning for an exit method. + break; + } + } + } + return exit_method = ExitMethod::AlwaysReturn; +} + +BasicBlock ShaderIR::DecodeRange(u32 begin, u32 end) { + BasicBlock basic_block; + for (u32 pc = begin; pc < (begin > end ? MAX_PROGRAM_LENGTH : end);) { + pc = DecodeInstr(basic_block, pc); + } + return std::move(basic_block); +} + +u32 ShaderIR::DecodeInstr(BasicBlock& bb, u32 pc) { + // Ignore sched instructions when generating code. + if (IsSchedInstruction(pc, main_offset)) { + return pc + 1; + } + + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + // Decoding failure + if (!opcode) { + UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value); + return pc + 1; + } + + bb.push_back( + Comment(fmt::format("{}: {} (0x{:016x})", pc, opcode->get().GetName(), instr.value))); + + using Tegra::Shader::Pred; + UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute, + "NeverExecute predicate not implemented"); + + static const std::map decoders = { + {OpCode::Type::Arithmetic, &ShaderIR::DecodeArithmetic}, + {OpCode::Type::ArithmeticImmediate, &ShaderIR::DecodeArithmeticImmediate}, + {OpCode::Type::Bfe, &ShaderIR::DecodeBfe}, + {OpCode::Type::Bfi, &ShaderIR::DecodeBfi}, + {OpCode::Type::Shift, &ShaderIR::DecodeShift}, + {OpCode::Type::ArithmeticInteger, &ShaderIR::DecodeArithmeticInteger}, + {OpCode::Type::ArithmeticIntegerImmediate, &ShaderIR::DecodeArithmeticIntegerImmediate}, + {OpCode::Type::ArithmeticHalf, &ShaderIR::DecodeArithmeticHalf}, + {OpCode::Type::ArithmeticHalfImmediate, &ShaderIR::DecodeArithmeticHalfImmediate}, + {OpCode::Type::Ffma, &ShaderIR::DecodeFfma}, + {OpCode::Type::Hfma2, &ShaderIR::DecodeHfma2}, + {OpCode::Type::Conversion, &ShaderIR::DecodeConversion}, + {OpCode::Type::Memory, &ShaderIR::DecodeMemory}, + {OpCode::Type::FloatSetPredicate, &ShaderIR::DecodeFloatSetPredicate}, + {OpCode::Type::IntegerSetPredicate, &ShaderIR::DecodeIntegerSetPredicate}, + {OpCode::Type::HalfSetPredicate, &ShaderIR::DecodeHalfSetPredicate}, + {OpCode::Type::PredicateSetRegister, &ShaderIR::DecodePredicateSetRegister}, + {OpCode::Type::PredicateSetPredicate, &ShaderIR::DecodePredicateSetPredicate}, + {OpCode::Type::RegisterSetPredicate, &ShaderIR::DecodeRegisterSetPredicate}, + {OpCode::Type::FloatSet, &ShaderIR::DecodeFloatSet}, + {OpCode::Type::IntegerSet, &ShaderIR::DecodeIntegerSet}, + {OpCode::Type::HalfSet, &ShaderIR::DecodeHalfSet}, + {OpCode::Type::Xmad, &ShaderIR::DecodeXmad}, + }; + + std::vector code; + if (const auto decoder = decoders.find(opcode->get().GetType()); decoder != decoders.end()) { + pc = (this->*decoder->second)(code, pc); + } else { + pc = DecodeOther(code, pc); + } + + // Some instructions (like SSY) don't have a predicate field, they are always unconditionally + // executed. + const bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->get().GetId()); + const auto pred_index = static_cast(instr.pred.pred_index); + + if (can_be_predicated && pred_index != static_cast(Pred::UnusedIndex)) { + bb.push_back( + Conditional(GetPredicate(pred_index, instr.negate_pred != 0), std::move(code))); + } else { + for (auto& node : code) { + bb.push_back(std::move(node)); + } + } + + return pc + 1; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp new file mode 100644 index 0000000000..9242a73890 --- /dev/null +++ b/src/video_core/shader/decode/arithmetic.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmetic(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp new file mode 100644 index 0000000000..3b189b0d17 --- /dev/null +++ b/src/video_core/shader/decode/arithmetic_half.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmeticHalf(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp new file mode 100644 index 0000000000..8d8a2dad96 --- /dev/null +++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmeticHalfImmediate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic_immediate.cpp b/src/video_core/shader/decode/arithmetic_immediate.cpp new file mode 100644 index 0000000000..18fd2082e3 --- /dev/null +++ b/src/video_core/shader/decode/arithmetic_immediate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmeticImmediate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp new file mode 100644 index 0000000000..12c64e97a6 --- /dev/null +++ b/src/video_core/shader/decode/arithmetic_integer.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmeticInteger(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp new file mode 100644 index 0000000000..46f340235f --- /dev/null +++ b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeArithmeticIntegerImmediate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/bfe.cpp b/src/video_core/shader/decode/bfe.cpp new file mode 100644 index 0000000000..ffd904c54d --- /dev/null +++ b/src/video_core/shader/decode/bfe.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeBfe(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp new file mode 100644 index 0000000000..b94d46ce6c --- /dev/null +++ b/src/video_core/shader/decode/bfi.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeBfi(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp new file mode 100644 index 0000000000..c6eb2952c9 --- /dev/null +++ b/src/video_core/shader/decode/conversion.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeConversion(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/decode_integer_set.cpp b/src/video_core/shader/decode/decode_integer_set.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/video_core/shader/decode/ffma.cpp b/src/video_core/shader/decode/ffma.cpp new file mode 100644 index 0000000000..2044113f03 --- /dev/null +++ b/src/video_core/shader/decode/ffma.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeFfma(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/float_set.cpp b/src/video_core/shader/decode/float_set.cpp new file mode 100644 index 0000000000..17d47c17aa --- /dev/null +++ b/src/video_core/shader/decode/float_set.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeFloatSet(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/float_set_predicate.cpp b/src/video_core/shader/decode/float_set_predicate.cpp new file mode 100644 index 0000000000..1dbe34353c --- /dev/null +++ b/src/video_core/shader/decode/float_set_predicate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeFloatSetPredicate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp new file mode 100644 index 0000000000..af363d5d21 --- /dev/null +++ b/src/video_core/shader/decode/half_set.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeHalfSet(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp new file mode 100644 index 0000000000..5fe123ea52 --- /dev/null +++ b/src/video_core/shader/decode/half_set_predicate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeHalfSetPredicate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/hfma2.cpp b/src/video_core/shader/decode/hfma2.cpp new file mode 100644 index 0000000000..5ce08481e7 --- /dev/null +++ b/src/video_core/shader/decode/hfma2.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeHfma2(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/integer_set.cpp b/src/video_core/shader/decode/integer_set.cpp new file mode 100644 index 0000000000..316a7d8ad8 --- /dev/null +++ b/src/video_core/shader/decode/integer_set.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeIntegerSet(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/integer_set_predicate.cpp b/src/video_core/shader/decode/integer_set_predicate.cpp new file mode 100644 index 0000000000..10975c394f --- /dev/null +++ b/src/video_core/shader/decode/integer_set_predicate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeIntegerSetPredicate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp new file mode 100644 index 0000000000..d6086004ba --- /dev/null +++ b/src/video_core/shader/decode/memory.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeMemory(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp new file mode 100644 index 0000000000..d84702a4f2 --- /dev/null +++ b/src/video_core/shader/decode/other.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/predicate_set_predicate.cpp b/src/video_core/shader/decode/predicate_set_predicate.cpp new file mode 100644 index 0000000000..1ad853fda8 --- /dev/null +++ b/src/video_core/shader/decode/predicate_set_predicate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodePredicateSetPredicate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/predicate_set_register.cpp b/src/video_core/shader/decode/predicate_set_register.cpp new file mode 100644 index 0000000000..67a06b5b4a --- /dev/null +++ b/src/video_core/shader/decode/predicate_set_register.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodePredicateSetRegister(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/register_set_predicate.cpp b/src/video_core/shader/decode/register_set_predicate.cpp new file mode 100644 index 0000000000..29a348cf59 --- /dev/null +++ b/src/video_core/shader/decode/register_set_predicate.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeRegisterSetPredicate(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp new file mode 100644 index 0000000000..41f5b8cb04 --- /dev/null +++ b/src/video_core/shader/decode/shift.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeShift(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp new file mode 100644 index 0000000000..27a2fc05dc --- /dev/null +++ b/src/video_core/shader/decode/xmad.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Instruction; +using Tegra::Shader::OpCode; + +u32 ShaderIR::DecodeXmad(BasicBlock& bb, u32 pc) { + const Instruction instr = {program_code[pc]}; + const auto opcode = OpCode::Decode(instr); + + UNIMPLEMENTED(); + + return pc; +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp new file mode 100644 index 0000000000..db00c89028 --- /dev/null +++ b/src/video_core/shader/shader_ir.cpp @@ -0,0 +1,105 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::Attribute; +using Tegra::Shader::Instruction; +using Tegra::Shader::IpaMode; +using Tegra::Shader::Pred; +using Tegra::Shader::PredCondition; +using Tegra::Shader::PredOperation; +using Tegra::Shader::Register; + +Node ShaderIR::StoreNode(NodeData&& node_data) { + auto store = std::make_unique(node_data); + const Node node = store.get(); + stored_nodes.push_back(std::move(store)); + return node; +} + +Node ShaderIR::Conditional(Node condition, std::vector&& code) { + return StoreNode(ConditionalNode(condition, std::move(code))); +} + +Node ShaderIR::Comment(const std::string& text) { + return StoreNode(CommentNode(text)); +} + +Node ShaderIR::GetPredicate(u64 pred_, bool negated) { + const auto pred = static_cast(pred_); + if (pred != Pred::UnusedIndex && pred != Pred::NeverExecute) { + used_predicates.insert(pred); + } + + return StoreNode(PredicateNode(pred, negated)); +} + +/*static*/ OperationCode ShaderIR::SignedToUnsignedCode(OperationCode operation_code, + bool is_signed) { + if (is_signed) { + return operation_code; + } + switch (operation_code) { + case OperationCode::FCastInteger: + return OperationCode::FCastUInteger; + case OperationCode::IAdd: + return OperationCode::UAdd; + case OperationCode::IMul: + return OperationCode::UMul; + case OperationCode::IDiv: + return OperationCode::UDiv; + case OperationCode::IMin: + return OperationCode::UMin; + case OperationCode::IMax: + return OperationCode::UMax; + case OperationCode::ICastFloat: + return OperationCode::UCastFloat; + case OperationCode::ICastUnsigned: + return OperationCode::UCastSigned; + case OperationCode::ILogicalShiftLeft: + return OperationCode::ULogicalShiftLeft; + case OperationCode::ILogicalShiftRight: + return OperationCode::ULogicalShiftRight; + case OperationCode::IArithmeticShiftRight: + return OperationCode::UArithmeticShiftRight; + case OperationCode::IBitwiseAnd: + return OperationCode::UBitwiseAnd; + case OperationCode::IBitwiseOr: + return OperationCode::UBitwiseOr; + case OperationCode::IBitwiseXor: + return OperationCode::UBitwiseXor; + case OperationCode::IBitwiseNot: + return OperationCode::UBitwiseNot; + case OperationCode::IBitfieldInsert: + return OperationCode::UBitfieldInsert; + case OperationCode::LogicalILessThan: + return OperationCode::LogicalULessThan; + case OperationCode::LogicalIEqual: + return OperationCode::LogicalUEqual; + case OperationCode::LogicalILessEqual: + return OperationCode::LogicalULessEqual; + case OperationCode::LogicalIGreaterThan: + return OperationCode::LogicalUGreaterThan; + case OperationCode::LogicalINotEqual: + return OperationCode::LogicalUNotEqual; + case OperationCode::LogicalIGreaterEqual: + return OperationCode::LogicalUGreaterEqual; + case OperationCode::INegate: + UNREACHABLE_MSG("Can't negate an unsigned integer"); + case OperationCode::IAbsolute: + UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); + } + UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast(operation_code)); +} + +} // namespace VideoCommon::Shader \ No newline at end of file diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h new file mode 100644 index 0000000000..300cf1083a --- /dev/null +++ b/src/video_core/shader/shader_ir.h @@ -0,0 +1,662 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/engines/shader_header.h" + +namespace VideoCommon::Shader { + +class OperationNode; +class ConditionalNode; +class GprNode; +class ImmediateNode; +class InternalFlagNode; +class PredicateNode; +class AbufNode; ///< Attribute buffer +class CbufNode; ///< Constant buffer +class LmemNode; ///< Local memory +class GmemNode; ///< Global memory +class CommentNode; + +using ProgramCode = std::vector; + +using NodeData = + std::variant; +using Node = const NodeData*; +using BasicBlock = std::vector; + +constexpr u32 MAX_PROGRAM_LENGTH = 0x1000; + +constexpr u32 RZ = 0xff; + +enum class OperationCode { + Assign, /// (float& dest, float src) -> void + AssignComposite, /// (MetaComponents, float4 src, float&[4] dst) -> void + + Composite, /// (float[4] values) -> float4 + Select, /// (MetaArithmetic, bool pred, float a, float b) -> float + + FAdd, /// (MetaArithmetic, float a, float b) -> float + FMul, /// (MetaArithmetic, float a, float b) -> float + FDiv, /// (MetaArithmetic, float a, float b) -> float + FFma, /// (MetaArithmetic, float a, float b, float c) -> float + FNegate, /// (MetaArithmetic, float a) -> float + FAbsolute, /// (MetaArithmetic, float a) -> float + FClamp, /// (MetaArithmetic, float value, float min, float max) -> float + FMin, /// (MetaArithmetic, float a, float b) -> float + FMax, /// (MetaArithmetic, float a, float b) -> float + FCos, /// (MetaArithmetic, float a) -> float + FSin, /// (MetaArithmetic, float a) -> float + FExp2, /// (MetaArithmetic, float a) -> float + FLog2, /// (MetaArithmetic, float a) -> float + FInverseSqrt, /// (MetaArithmetic, float a) -> float + FSqrt, /// (MetaArithmetic, float a) -> float + FRoundEven, /// (MetaArithmetic, float a) -> float + FFloor, /// (MetaArithmetic, float a) -> float + FCeil, /// (MetaArithmetic, float a) -> float + FTrunc, /// (MetaArithmetic, float a) -> float + FCastInteger, /// (MetaArithmetic, int a) -> float + FCastUInteger, /// (MetaArithmetic, uint a) -> float + + IAdd, /// (MetaArithmetic, int a, int b) -> int + IMul, /// (MetaArithmetic, int a, int b) -> int + IDiv, /// (MetaArithmetic, int a, int b) -> int + INegate, /// (MetaArithmetic, int a) -> int + IAbsolute, /// (MetaArithmetic, int a) -> int + IMin, /// (MetaArithmetic, int a, int b) -> int + IMax, /// (MetaArithmetic, int a, int b) -> int + ICastFloat, /// (MetaArithmetic, float a) -> int + ICastUnsigned, /// (MetaArithmetic, uint a) -> int + ILogicalShiftLeft, /// (MetaArithmetic, int a, uint b) -> int + ILogicalShiftRight, /// (MetaArithmetic, int a, uint b) -> int + IArithmeticShiftRight, /// (MetaArithmetic, int a, uint b) -> int + IBitwiseAnd, /// (MetaArithmetic, int a, int b) -> int + IBitwiseOr, /// (MetaArithmetic, int a, int b) -> int + IBitwiseXor, /// (MetaArithmetic, int a, int b) -> int + IBitwiseNot, /// (MetaArithmetic, int a) -> int + IBitfieldInsert, /// (MetaArithmetic, int base, int insert, int offset, int bits) -> int + + UAdd, /// (MetaArithmetic, uint a, uint b) -> uint + UMul, /// (MetaArithmetic, uint a, uint b) -> uint + UDiv, /// (MetaArithmetic, uint a, uint b) -> uint + UMin, /// (MetaArithmetic, uint a, uint b) -> uint + UMax, /// (MetaArithmetic, uint a, uint b) -> uint + UCastFloat, /// (MetaArithmetic, float a) -> uint + UCastSigned, /// (MetaArithmetic, int a) -> uint + ULogicalShiftLeft, /// (MetaArithmetic, uint a, uint b) -> uint + ULogicalShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint + UArithmeticShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint + UBitwiseAnd, /// (MetaArithmetic, uint a, uint b) -> uint + UBitwiseOr, /// (MetaArithmetic, uint a, uint b) -> uint + UBitwiseXor, /// (MetaArithmetic, uint a, uint b) -> uint + UBitwiseNot, /// (MetaArithmetic, uint a) -> int + UBitfieldInsert, /// (MetaArithmetic, uint base, uint insert, int offset, int bits) -> uint + + HAdd, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 + HMul, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 + HAbsolute, /// (f16vec2 a) -> f16vec2 + HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2 + HMergeF32, /// (f16vec2 src) -> float + HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2 + HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2 + + LogicalAssign, /// (bool& dst, bool src) -> void + LogicalAnd, /// (bool a, bool b) -> bool + LogicalOr, /// (bool a, bool b) -> bool + LogicalXor, /// (bool a, bool b) -> bool + LogicalNegate, /// (bool a) -> bool + + LogicalFLessThan, /// (float a, float b) -> bool + LogicalFEqual, /// (float a, float b) -> bool + LogicalFLessEqual, /// (float a, float b) -> bool + LogicalFGreaterThan, /// (float a, float b) -> bool + LogicalFNotEqual, /// (float a, float b) -> bool + LogicalFGreaterEqual, /// (float a, float b) -> bool + LogicalFIsNan, /// (float a) -> bool + + LogicalILessThan, /// (int a, int b) -> bool + LogicalIEqual, /// (int a, int b) -> bool + LogicalILessEqual, /// (int a, int b) -> bool + LogicalIGreaterThan, /// (int a, int b) -> bool + LogicalINotEqual, /// (int a, int b) -> bool + LogicalIGreaterEqual, /// (int a, int b) -> bool + + LogicalULessThan, /// (uint a, uint b) -> bool + LogicalUEqual, /// (uint a, uint b) -> bool + LogicalULessEqual, /// (uint a, uint b) -> bool + LogicalUGreaterThan, /// (uint a, uint b) -> bool + LogicalUNotEqual, /// (uint a, uint b) -> bool + LogicalUGreaterEqual, /// (uint a, uint b) -> bool + + LogicalHLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + LogicalHEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + LogicalHLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + LogicalHGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + LogicalHNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + LogicalHGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool + + F4Texture, /// (MetaTexture, float[N] coords, float[M] params) -> float4 + F4TextureLod, /// (MetaTexture, float[N] coords, float[M] params) -> float4 + F4TextureGather, /// (MetaTexture, float[N] coords, float[M] params) -> float4 + F4TextureQueryDimensions, /// (MetaTexture, float a) -> float4 + F4TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 + + Ipa, /// (abuf src) -> float + + Bra, /// (uint branch_target) -> void + Ssy, /// (uint branch_target) -> void + Pbk, /// (uint branch_target) -> void + Sync, /// () -> void + Brk, /// () -> void + Exit, /// () -> void + Kil, /// () -> void + + YNegate, /// () -> float + + Amount, +}; + +enum class InternalFlag { + Zero = 0, + Sign = 1, + Carry = 2, + Overflow = 3, + Amount = 4, +}; + +/// Describes the behaviour of code path of a given entry point and a return point. +enum class ExitMethod { + Undetermined, ///< Internal value. Only occur when analyzing JMP loop. + AlwaysReturn, ///< All code paths reach the return point. + Conditional, ///< Code path reaches the return point or an END instruction conditionally. + AlwaysEnd, ///< All code paths reach a END instruction. +}; + +class Sampler { +public: + explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type, + bool is_array, bool is_shadow) + : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow} {} + + std::size_t GetOffset() const { + return offset; + } + + u32 GetIndex() const { + return static_cast(index); + } + + Tegra::Shader::TextureType GetType() const { + return type; + } + + bool IsArray() const { + return is_array; + } + + bool IsShadow() const { + return is_shadow; + } + + bool operator<(const Sampler& rhs) const { + return std::tie(offset, index, type, is_array, is_shadow) < + std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_array, rhs.is_shadow); + } + +private: + /// Offset in TSC memory from which to read the sampler object, as specified by the sampling + /// instruction. + std::size_t offset{}; + std::size_t index{}; ///< Value used to index into the generated GLSL sampler array. + Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) + bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. + bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. +}; + +class ConstBuffer { +public: + void MarkAsUsed(u64 offset) { + max_offset = std::max(max_offset, static_cast(offset)); + } + + void MarkAsUsedIndirect() { + is_indirect = true; + } + + bool IsIndirect() const { + return is_indirect; + } + + u32 GetSize() const { + return max_offset + 1; + } + +private: + u32 max_offset{}; + bool is_indirect{}; +}; + +struct MetaArithmetic { + bool precise{}; +}; + +struct MetaHalfArithmetic { + bool precise{}; + std::array types = {Tegra::Shader::HalfType::H0_H1, + Tegra::Shader::HalfType::H0_H1, + Tegra::Shader::HalfType::H0_H1}; + bool and_comparison{}; +}; + +struct MetaTexture { + const Sampler& sampler; + u32 coords_count{}; +}; + +struct MetaComponents { + std::array components_map{}; + + u32 GetSourceComponent(u32 dest_index) const { + return components_map[dest_index]; + } +}; + +constexpr MetaArithmetic PRECISE = {true}; +constexpr MetaArithmetic NO_PRECISE = {false}; +constexpr MetaHalfArithmetic HALF_NO_PRECISE = {false}; + +using Meta = std::variant; + +/// Holds any kind of operation that can be done in the IR +class OperationNode final { +public: + template + explicit constexpr OperationNode(OperationCode code) : code{code}, meta{} {} + + template + explicit constexpr OperationNode(OperationCode code, Meta&& meta) + : code{code}, meta{std::move(meta)} {} + + template + explicit constexpr OperationNode(OperationCode code, const T*... operands) + : OperationNode(code, {}, operands...) {} + + template + explicit constexpr OperationNode(OperationCode code, Meta&& meta, const T*... operands_) + : code{code}, meta{std::move(meta)} { + + auto operands_list = {operands_...}; + for (auto& operand : operands_list) { + operands.push_back(operand); + } + } + + explicit OperationNode(OperationCode code, Meta&& meta, std::vector&& operands) + : code{code}, meta{meta}, operands{std::move(operands)} {} + + explicit OperationNode(OperationCode code, std::vector&& operands) + : code{code}, meta{}, operands{std::move(operands)} {} + + OperationCode GetCode() const { + return code; + } + + const Meta& GetMeta() const { + return meta; + } + + std::size_t GetOperandsCount() const { + return operands.size(); + } + + Node operator[](std::size_t operand_index) const { + return operands.at(operand_index); + } + +private: + const OperationCode code; + const Meta meta; + std::vector operands; +}; + +/// Encloses inside any kind of node that returns a boolean conditionally-executed code +class ConditionalNode final { +public: + explicit ConditionalNode(Node condition, std::vector&& code) + : condition{condition}, code{std::move(code)} {} + + Node GetCondition() const { + return condition; + } + + const std::vector& GetCode() const { + return code; + } + +private: + const Node condition; ///< Condition to be satisfied + std::vector code; ///< Code to execute +}; + +/// A general purpose register +class GprNode final { +public: + explicit constexpr GprNode(Tegra::Shader::Register index) : index{index} {} + + u32 GetIndex() const { + return static_cast(index); + } + +private: + const Tegra::Shader::Register index; +}; + +/// A 32-bits value that represents an immediate value +class ImmediateNode final { +public: + explicit constexpr ImmediateNode(u32 value) : value{value} {} + + u32 GetValue() const { + return value; + } + +private: + const u32 value; +}; + +/// One of Maxwell's internal flags +class InternalFlagNode final { +public: + explicit constexpr InternalFlagNode(InternalFlag flag) : flag{flag} {} + + InternalFlag GetFlag() const { + return flag; + } + +private: + const InternalFlag flag; +}; + +/// A predicate register, it can be negated without aditional nodes +class PredicateNode final { +public: + explicit constexpr PredicateNode(Tegra::Shader::Pred index, bool negated) + : index{index}, negated{negated} {} + + Tegra::Shader::Pred GetIndex() const { + return index; + } + + bool IsNegated() const { + return negated; + } + +private: + const Tegra::Shader::Pred index; + const bool negated; +}; + +/// Attribute buffer memory (known as attributes or varyings in GLSL terms) +class AbufNode final { +public: + explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, + const Tegra::Shader::IpaMode& input_mode, Node buffer = {}) + : input_mode{input_mode}, index{index}, element{element}, buffer{buffer} {} + + explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, + Node buffer = {}) + : input_mode{}, index{index}, element{element}, buffer{buffer} {} + + Tegra::Shader::IpaMode GetInputMode() const { + return input_mode; + } + + Tegra::Shader::Attribute::Index GetIndex() const { + return index; + } + + u32 GetElement() const { + return element; + } + + Node GetBuffer() const { + return buffer; + } + +private: + const Tegra::Shader::IpaMode input_mode; + const Node buffer; + const Tegra::Shader::Attribute::Index index; + const u32 element; +}; + +/// Constant buffer node, usually mapped to uniform buffers in GLSL +class CbufNode final { +public: + explicit constexpr CbufNode(u32 index, Node offset) : index{index}, offset{offset} {} + + u32 GetIndex() const { + return index; + } + + Node GetOffset() const { + return offset; + } + +private: + const u32 index; + const Node offset; +}; + +/// Local memory node +class LmemNode final { +public: + explicit constexpr LmemNode(Node address) : address{address} {} + + Node GetAddress() const { + return address; + } + +private: + const Node address; +}; + +/// Global memory node +class GmemNode final { +public: + explicit GmemNode(Node address) : address{address} {} + + Node GetAddress() const { + return address; + } + +private: + const Node address; +}; + +/// Commentary, can be dropped +class CommentNode final { +public: + explicit CommentNode(const std::string& text) : text{text} {} + + const std::string& GetText() const { + return text; + } + +private: + const std::string text; +}; + +class ShaderIR final { +public: + explicit ShaderIR(const ProgramCode& program_code, u32 main_offset) + : program_code{program_code}, main_offset{main_offset} { + + Decode(); + } + + const std::map& GetBasicBlocks() const { + return basic_blocks; + } + + const std::set& GetRegisters() const { + return used_registers; + } + + const std::set& GetPredicates() const { + return used_predicates; + } + + const std::map>& + GetInputAttributes() const { + return used_input_attributes; + } + + const std::set& GetOutputAttributes() const { + return used_output_attributes; + } + + const std::map& GetConstantBuffers() const { + return used_cbufs; + } + + const std::set& GetSamplers() const { + return used_samplers; + } + + const std::array& GetClipDistances() + const { + return used_clip_distances; + } + + std::size_t GetLength() const { + return static_cast(coverage_end * sizeof(u64)); + } + + const Tegra::Shader::Header& GetHeader() const { + return header; + } + +private: + void Decode(); + + ExitMethod Scan(u32 begin, u32 end, std::set& labels); + + BasicBlock DecodeRange(u32 begin, u32 end); + + /** + * Decodes a single instruction from Tegra to IR. + * @param bb Basic block where the nodes will be written to. + * @param pc Program counter. Offset to decode. + * @return Next address to decode. + */ + u32 DecodeInstr(BasicBlock& bb, u32 pc); + + u32 DecodeArithmetic(BasicBlock& bb, u32 pc); + u32 DecodeArithmeticImmediate(BasicBlock& bb, u32 pc); + u32 DecodeBfe(BasicBlock& bb, u32 pc); + u32 DecodeBfi(BasicBlock& bb, u32 pc); + u32 DecodeShift(BasicBlock& bb, u32 pc); + u32 DecodeArithmeticInteger(BasicBlock& bb, u32 pc); + u32 DecodeArithmeticIntegerImmediate(BasicBlock& bb, u32 pc); + u32 DecodeArithmeticHalf(BasicBlock& bb, u32 pc); + u32 DecodeArithmeticHalfImmediate(BasicBlock& bb, u32 pc); + u32 DecodeFfma(BasicBlock& bb, u32 pc); + u32 DecodeHfma2(BasicBlock& bb, u32 pc); + u32 DecodeConversion(BasicBlock& bb, u32 pc); + u32 DecodeMemory(BasicBlock& bb, u32 pc); + u32 DecodeFloatSetPredicate(BasicBlock& bb, u32 pc); + u32 DecodeIntegerSetPredicate(BasicBlock& bb, u32 pc); + u32 DecodeHalfSetPredicate(BasicBlock& bb, u32 pc); + u32 DecodePredicateSetRegister(BasicBlock& bb, u32 pc); + u32 DecodePredicateSetPredicate(BasicBlock& bb, u32 pc); + u32 DecodeRegisterSetPredicate(BasicBlock& bb, u32 pc); + u32 DecodeFloatSet(BasicBlock& bb, u32 pc); + u32 DecodeIntegerSet(BasicBlock& bb, u32 pc); + u32 DecodeHalfSet(BasicBlock& bb, u32 pc); + u32 DecodeXmad(BasicBlock& bb, u32 pc); + u32 DecodeOther(BasicBlock& bb, u32 pc); + + /// Internalizes node's data and returns a managed pointer to a clone of that node + Node StoreNode(NodeData&& node_data); + + /// Creates a conditional node + Node Conditional(Node condition, std::vector&& code); + /// Creates a commentary + Node Comment(const std::string& text); + + /// Generates a node for a passed predicate. It can be optionally negated + Node GetPredicate(u64 pred, bool negated = false); + + template + inline Node Operation(OperationCode code, const T*... operands) { + return StoreNode(OperationNode(code, operands...)); + } + + template + inline Node Operation(OperationCode code, Meta&& meta, const T*... operands) { + return StoreNode(OperationNode(code, std::move(meta), operands...)); + } + + template + inline Node Operation(OperationCode code, std::vector&& operands) { + return StoreNode(OperationNode(code, std::move(operands))); + } + + template + inline Node Operation(OperationCode code, Meta&& meta, std::vector&& operands) { + return StoreNode(OperationNode(code, std::move(meta), std::move(operands))); + } + + template + inline Node SignedOperation(OperationCode code, bool is_signed, const T*... operands) { + return StoreNode(OperationNode(SignedToUnsignedCode(code, is_signed), operands...)); + } + + template + inline Node SignedOperation(OperationCode code, bool is_signed, Meta&& meta, + const T*... operands) { + return StoreNode( + OperationNode(SignedToUnsignedCode(code, is_signed), std::move(meta), operands...)); + } + + static OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed); + + const ProgramCode& program_code; + const u32 main_offset; + + u32 coverage_begin{}; + u32 coverage_end{}; + std::map, ExitMethod> exit_method_map; + + std::map basic_blocks; + + std::vector> stored_nodes; + + std::set used_registers; + std::set used_predicates; + std::map> + used_input_attributes; + std::set used_output_attributes; + std::map used_cbufs; + std::set used_samplers; + std::array used_clip_distances{}; + + Tegra::Shader::Header header; +}; + +} // namespace VideoCommon::Shader \ No newline at end of file