30 changed files with 1573 additions and 0 deletions
-
27src/video_core/CMakeLists.txt
-
4src/video_core/engines/shader_bytecode.h
-
199src/video_core/shader/decode.cpp
-
24src/video_core/shader/decode/arithmetic.cpp
-
24src/video_core/shader/decode/arithmetic_half.cpp
-
24src/video_core/shader/decode/arithmetic_half_immediate.cpp
-
24src/video_core/shader/decode/arithmetic_immediate.cpp
-
24src/video_core/shader/decode/arithmetic_integer.cpp
-
24src/video_core/shader/decode/arithmetic_integer_immediate.cpp
-
24src/video_core/shader/decode/bfe.cpp
-
24src/video_core/shader/decode/bfi.cpp
-
24src/video_core/shader/decode/conversion.cpp
-
0src/video_core/shader/decode/decode_integer_set.cpp
-
24src/video_core/shader/decode/ffma.cpp
-
24src/video_core/shader/decode/float_set.cpp
-
24src/video_core/shader/decode/float_set_predicate.cpp
-
24src/video_core/shader/decode/half_set.cpp
-
24src/video_core/shader/decode/half_set_predicate.cpp
-
24src/video_core/shader/decode/hfma2.cpp
-
24src/video_core/shader/decode/integer_set.cpp
-
24src/video_core/shader/decode/integer_set_predicate.cpp
-
24src/video_core/shader/decode/memory.cpp
-
24src/video_core/shader/decode/other.cpp
-
24src/video_core/shader/decode/predicate_set_predicate.cpp
-
24src/video_core/shader/decode/predicate_set_register.cpp
-
24src/video_core/shader/decode/register_set_predicate.cpp
-
24src/video_core/shader/decode/shift.cpp
-
24src/video_core/shader/decode/xmad.cpp
-
105src/video_core/shader/shader_ir.cpp
-
662src/video_core/shader/shader_ir.h
@ -0,0 +1,199 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cstring>
|
|||
#include <set>
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#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<u32> 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<u32>& 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<u64>(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<OpCode::Type, u32 (ShaderIR::*)(BasicBlock & code, u32 pc)> 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<Node> 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<u32>(instr.pred.pred_index); |
|||
|
|||
if (can_be_predicated && pred_index != static_cast<u32>(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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -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
|
|||
@ -0,0 +1,105 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cmath>
|
|||
#include <unordered_map>
|
|||
|
|||
#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<NodeData>(node_data); |
|||
const Node node = store.get(); |
|||
stored_nodes.push_back(std::move(store)); |
|||
return node; |
|||
} |
|||
|
|||
Node ShaderIR::Conditional(Node condition, std::vector<Node>&& 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>(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<u32>(operation_code)); |
|||
} |
|||
|
|||
} // namespace VideoCommon::Shader
|
|||
@ -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 <map> |
|||
#include <set> |
|||
#include <string> |
|||
#include <tuple> |
|||
#include <variant> |
|||
#include <vector> |
|||
|
|||
#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<u64>; |
|||
|
|||
using NodeData = |
|||
std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode, |
|||
PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>; |
|||
using Node = const NodeData*; |
|||
using BasicBlock = std::vector<Node>; |
|||
|
|||
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<u32>(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<u32>(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<Tegra::Shader::HalfType, 3> 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<u32, 4> 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<MetaArithmetic, MetaHalfArithmetic, MetaTexture, MetaComponents>; |
|||
|
|||
/// Holds any kind of operation that can be done in the IR |
|||
class OperationNode final { |
|||
public: |
|||
template <typename... T> |
|||
explicit constexpr OperationNode(OperationCode code) : code{code}, meta{} {} |
|||
|
|||
template <typename... T> |
|||
explicit constexpr OperationNode(OperationCode code, Meta&& meta) |
|||
: code{code}, meta{std::move(meta)} {} |
|||
|
|||
template <typename... T> |
|||
explicit constexpr OperationNode(OperationCode code, const T*... operands) |
|||
: OperationNode(code, {}, operands...) {} |
|||
|
|||
template <typename... T> |
|||
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<Node>&& operands) |
|||
: code{code}, meta{meta}, operands{std::move(operands)} {} |
|||
|
|||
explicit OperationNode(OperationCode code, std::vector<Node>&& 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<Node> operands; |
|||
}; |
|||
|
|||
/// Encloses inside any kind of node that returns a boolean conditionally-executed code |
|||
class ConditionalNode final { |
|||
public: |
|||
explicit ConditionalNode(Node condition, std::vector<Node>&& code) |
|||
: condition{condition}, code{std::move(code)} {} |
|||
|
|||
Node GetCondition() const { |
|||
return condition; |
|||
} |
|||
|
|||
const std::vector<Node>& GetCode() const { |
|||
return code; |
|||
} |
|||
|
|||
private: |
|||
const Node condition; ///< Condition to be satisfied |
|||
std::vector<Node> 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<u32>(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<u32, BasicBlock>& GetBasicBlocks() const { |
|||
return basic_blocks; |
|||
} |
|||
|
|||
const std::set<u32>& GetRegisters() const { |
|||
return used_registers; |
|||
} |
|||
|
|||
const std::set<Tegra::Shader::Pred>& GetPredicates() const { |
|||
return used_predicates; |
|||
} |
|||
|
|||
const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>& |
|||
GetInputAttributes() const { |
|||
return used_input_attributes; |
|||
} |
|||
|
|||
const std::set<Tegra::Shader::Attribute::Index>& GetOutputAttributes() const { |
|||
return used_output_attributes; |
|||
} |
|||
|
|||
const std::map<u32, ConstBuffer>& GetConstantBuffers() const { |
|||
return used_cbufs; |
|||
} |
|||
|
|||
const std::set<Sampler>& GetSamplers() const { |
|||
return used_samplers; |
|||
} |
|||
|
|||
const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& GetClipDistances() |
|||
const { |
|||
return used_clip_distances; |
|||
} |
|||
|
|||
std::size_t GetLength() const { |
|||
return static_cast<std::size_t>(coverage_end * sizeof(u64)); |
|||
} |
|||
|
|||
const Tegra::Shader::Header& GetHeader() const { |
|||
return header; |
|||
} |
|||
|
|||
private: |
|||
void Decode(); |
|||
|
|||
ExitMethod Scan(u32 begin, u32 end, std::set<u32>& 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<Node>&& 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 <typename... T> |
|||
inline Node Operation(OperationCode code, const T*... operands) { |
|||
return StoreNode(OperationNode(code, operands...)); |
|||
} |
|||
|
|||
template <typename... T> |
|||
inline Node Operation(OperationCode code, Meta&& meta, const T*... operands) { |
|||
return StoreNode(OperationNode(code, std::move(meta), operands...)); |
|||
} |
|||
|
|||
template <typename... T> |
|||
inline Node Operation(OperationCode code, std::vector<Node>&& operands) { |
|||
return StoreNode(OperationNode(code, std::move(operands))); |
|||
} |
|||
|
|||
template <typename... T> |
|||
inline Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) { |
|||
return StoreNode(OperationNode(code, std::move(meta), std::move(operands))); |
|||
} |
|||
|
|||
template <typename... T> |
|||
inline Node SignedOperation(OperationCode code, bool is_signed, const T*... operands) { |
|||
return StoreNode(OperationNode(SignedToUnsignedCode(code, is_signed), operands...)); |
|||
} |
|||
|
|||
template <typename... T> |
|||
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<std::pair<u32, u32>, ExitMethod> exit_method_map; |
|||
|
|||
std::map<u32, BasicBlock> basic_blocks; |
|||
|
|||
std::vector<std::unique_ptr<NodeData>> stored_nodes; |
|||
|
|||
std::set<u32> used_registers; |
|||
std::set<Tegra::Shader::Pred> used_predicates; |
|||
std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>> |
|||
used_input_attributes; |
|||
std::set<Tegra::Shader::Attribute::Index> used_output_attributes; |
|||
std::map<u32, ConstBuffer> used_cbufs; |
|||
std::set<Sampler> used_samplers; |
|||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; |
|||
|
|||
Tegra::Shader::Header header; |
|||
}; |
|||
|
|||
} // namespace VideoCommon::Shader |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue