From 3aa8b644a921020beb91109f284ae86dc2f7e189 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 19 Oct 2018 12:17:02 -0400 Subject: Assert Control Flow Instructions using Control Codes --- src/video_core/engines/shader_bytecode.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 141b9159b..55763332e 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1232,6 +1232,7 @@ union Instruction { BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; BitField<20, 24, s64> smem_imm; + BitField<0, 5, ControlCode> flow_control_code; Attribute attribute; Sampler sampler; @@ -1658,4 +1659,4 @@ private: } }; -} // namespace Tegra::Shader \ No newline at end of file +} // namespace Tegra::Shader -- cgit v1.2.3 From 5bb80ab009a0ee070c4e2caefb6795ef9a28e355 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 19 Oct 2018 16:54:13 -0400 Subject: Assert Control Codes Generation --- src/video_core/engines/shader_bytecode.h | 6 +- .../renderer_opengl/gl_shader_decompiler.cpp | 98 ++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 141b9159b..b09ea3318 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -578,6 +578,10 @@ union Instruction { BitField<55, 1, u64> saturate; } fmul32; + union { + BitField<52, 1, u64> generates_cc; + } op_32; + union { BitField<48, 1, u64> is_signed; } shift; @@ -1658,4 +1662,4 @@ private: } }; -} // namespace Tegra::Shader \ No newline at end of file +} // namespace Tegra::Shader diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index dcf6941b0..d656e5d1c 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -373,6 +373,7 @@ public: if (sets_cc) { const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); + LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); } } @@ -1525,6 +1526,10 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::FADD_C: @@ -1535,6 +1540,10 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::MUFU: { @@ -1588,6 +1597,10 @@ private: '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', 1, 1, false, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::RRO_C: @@ -1617,6 +1630,10 @@ private: regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1, instr.fmul32.saturate, 0, true); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::FADD32I: { @@ -1640,6 +1657,10 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } } @@ -1660,6 +1681,10 @@ private: std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { @@ -1697,12 +1722,20 @@ private: // Cast to int is superfluous for arithmetic shift, it's only for a logical shift regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::SHL_C: case OpCode::Id::SHL_R: case OpCode::Id::SHL_IMM: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code"); + UNREACHABLE(); + } break; default: { LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); @@ -1722,6 +1755,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, instr.iadd32i.saturate != 0); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; case OpCode::Id::LOP32I: { if (instr.alu.lop32i.invert_a) @@ -1733,6 +1770,10 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, Tegra::Shader::PredicateResultMode::None, Tegra::Shader::Pred::UnusedIndex); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { @@ -1769,6 +1810,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, instr.alu.saturate_d); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::IADD3_C: @@ -1830,6 +1875,11 @@ private: } regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::ISCADD_C: @@ -1845,6 +1895,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::POPC_C: @@ -1876,6 +1930,10 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::LOP3_C: @@ -1891,6 +1949,10 @@ private: } WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::IMNMX_C: @@ -1905,6 +1967,10 @@ private: '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::LEA_R2: @@ -2103,6 +2169,10 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 1, 1, instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } @@ -2208,6 +2278,11 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::F2F_R: { @@ -2246,6 +2321,11 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::F2I_R: @@ -2295,6 +2375,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1, false, 0, instr.conversion.dest_size); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { @@ -3102,6 +3186,11 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); } + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code"); + UNREACHABLE(); + } + break; } case OpCode::Type::PredicateSetPredicate: { @@ -3366,6 +3455,10 @@ private: } regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { @@ -3537,6 +3630,11 @@ private: regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, instr.vmad.saturate == 1, 0, Register::Size::Word, instr.vmad.cc); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code"); + UNREACHABLE(); + } + break; } case OpCode::Id::VSETP: { -- cgit v1.2.3 From de0ab806df4575df93068e128d911dabbf396d2c Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 29 Oct 2018 23:36:03 -0400 Subject: maxwell_3d: Restructure macro upload to use a single macro code memory. - Fixes an issue where macros could be skipped. - Fixes rendering of distant objects in Super Mario Odyssey. --- src/video_core/engines/maxwell_3d.cpp | 26 ++++++++++++++++++-------- src/video_core/engines/maxwell_3d.h | 25 +++++++++++++++++++++---- src/video_core/macro_interpreter.cpp | 19 ++++++++++--------- src/video_core/macro_interpreter.h | 12 ++++++------ 4 files changed, 55 insertions(+), 27 deletions(-) (limited to 'src/video_core/engines') diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 7357d20d1..d79c50919 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -43,15 +43,17 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector parameters) { // Reset the current macro. executing_macro = 0; - // The requested macro must have been uploaded already. - auto macro_code = uploaded_macros.find(method); - if (macro_code == uploaded_macros.end()) { - LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); + // Lookup the macro offset + const u32 entry{(method - MacroRegistersStart) >> 1}; + const auto& search{macro_offsets.find(entry)}; + if (search == macro_offsets.end()) { + LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method); + UNREACHABLE(); return; } // Execute the current macro. - macro_interpreter.Execute(macro_code->second, std::move(parameters)); + macro_interpreter.Execute(search->second, std::move(parameters)); } void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { @@ -97,6 +99,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { ProcessMacroUpload(value); break; } + case MAXWELL3D_REG_INDEX(macros.bind): { + ProcessMacroBind(value); + break; + } case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): @@ -158,9 +164,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { } void Maxwell3D::ProcessMacroUpload(u32 data) { - // Store the uploaded macro code to interpret them when they're called. - auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart]; - macro.push_back(data); + ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), + "upload_address exceeded macro_memory size!"); + macro_memory[regs.macros.upload_address++] = data; +} + +void Maxwell3D::ProcessMacroBind(u32 data) { + macro_offsets[regs.macros.entry] = data; } void Maxwell3D::ProcessQueryGet() { diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 443affc36..50873813e 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -475,12 +475,13 @@ public: INSERT_PADDING_WORDS(0x45); struct { - INSERT_PADDING_WORDS(1); + u32 upload_address; u32 data; u32 entry; + u32 bind; } macros; - INSERT_PADDING_WORDS(0x189); + INSERT_PADDING_WORDS(0x188); u32 tfb_enabled; @@ -994,12 +995,25 @@ public: /// Returns the texture information for a specific texture in a specific shader stage. Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; + /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than + /// we've seen used. + using MacroMemory = std::array; + + /// Gets a reference to macro memory. + const MacroMemory& GetMacroMemory() const { + return macro_memory; + } + private: void InitializeRegisterDefaults(); VideoCore::RasterizerInterface& rasterizer; - std::unordered_map> uploaded_macros; + /// Start offsets of each macro in macro_memory + std::unordered_map macro_offsets; + + /// Memory for macro code + MacroMemory macro_memory; /// Macro method that is currently being executed / being fed parameters. u32 executing_macro = 0; @@ -1022,9 +1036,12 @@ private: */ void CallMacroMethod(u32 method, std::vector parameters); - /// Handles writes to the macro uploading registers. + /// Handles writes to the macro uploading register. void ProcessMacroUpload(u32 data); + /// Handles writes to the macro bind register. + void ProcessMacroBind(u32 data); + /// Handles a write to the CLEAR_BUFFERS register. void ProcessClearBuffers(); diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index f6af132fb..335a8d407 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -11,7 +11,7 @@ namespace Tegra { MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} -void MacroInterpreter::Execute(const std::vector& code, std::vector parameters) { +void MacroInterpreter::Execute(u32 offset, std::vector parameters) { Reset(); registers[1] = parameters[0]; this->parameters = std::move(parameters); @@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector& code, std::vector pa // Execute the code until we hit an exit condition. bool keep_executing = true; while (keep_executing) { - keep_executing = Step(code, false); + keep_executing = Step(offset, false); } // Assert the the macro used all the input parameters @@ -37,10 +37,10 @@ void MacroInterpreter::Reset() { next_parameter_index = 1; } -bool MacroInterpreter::Step(const std::vector& code, bool is_delay_slot) { +bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { u32 base_address = pc; - Opcode opcode = GetOpcode(code); + Opcode opcode = GetOpcode(offset); pc += 4; // Update the program counter if we were delayed @@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector& code, bool is_delay_slot) { delayed_pc = base_address + opcode.GetBranchTarget(); // Execute one more instruction due to the delay slot. - return Step(code, true); + return Step(offset, true); } break; } @@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector& code, bool is_delay_slot) { // Exit has a delay slot, execute the next instruction // Note: Executing an exit during a branch delay slot will cause the instruction at the // branch target to be executed before exiting. - Step(code, true); + Step(offset, true); return false; } return true; } -MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector& code) const { +MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { + const auto& macro_memory{maxwell3d.GetMacroMemory()}; ASSERT((pc % sizeof(u32)) == 0); - ASSERT(pc < code.size() * sizeof(u32)); - return {code[pc / sizeof(u32)]}; + ASSERT((pc + offset) < macro_memory.size() * sizeof(u32)); + return {macro_memory[offset + pc / sizeof(u32)]}; } u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index 773684bde..62d1ce289 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h @@ -22,10 +22,10 @@ public: /** * Executes the macro code with the specified input parameters. - * @param code The macro byte code to execute - * @param parameters The parameters of the macro + * @param offset Offset to start execution at. + * @param parameters The parameters of the macro. */ - void Execute(const std::vector& code, std::vector parameters); + void Execute(u32 offset, std::vector parameters); private: enum class Operation : u32 { @@ -110,11 +110,11 @@ private: /** * Executes a single macro instruction located at the current program counter. Returns whether * the interpreter should keep running. - * @param code The macro code to execute. + * @param offset Offset to start execution at. * @param is_delay_slot Whether the current step is being executed due to a delay slot in a * previous instruction. */ - bool Step(const std::vector& code, bool is_delay_slot); + bool Step(u32 offset, bool is_delay_slot); /// Calculates the result of an ALU operation. src_a OP src_b; u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; @@ -127,7 +127,7 @@ private: bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; /// Reads an opcode at the current program counter location. - Opcode GetOpcode(const std::vector& code) const; + Opcode GetOpcode(u32 offset) const; /// Returns the specified register's value. Register 0 is hardcoded to always return 0. u32 GetRegister(u32 register_id) const; -- cgit v1.2.3