From 7ac8657432f2dad14c985ef3df3972cd126fc9d8 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 18 Mar 2018 03:13:22 -0500 Subject: [PATCH 1/2] GPU: Macros are specific to the Maxwell3D engine, so handle them internally. --- src/video_core/command_processor.cpp | 29 --------------- src/video_core/engines/fermi_2d.cpp | 1 - src/video_core/engines/fermi_2d.h | 8 ---- src/video_core/engines/maxwell_3d.cpp | 43 +++++++++++++++++++--- src/video_core/engines/maxwell_3d.h | 27 ++++++++++---- src/video_core/engines/maxwell_compute.cpp | 1 - src/video_core/engines/maxwell_compute.h | 8 ---- src/video_core/gpu.h | 3 -- 8 files changed, 56 insertions(+), 64 deletions(-) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 26ba8c40b..1d578582e 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -64,35 +64,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) { } } -void GPU::CallMethod(u32 method, u32 subchannel, const std::vector& parameters) { - LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u num params %zu", method, - subchannel, parameters.size()); - - if (method < static_cast(BufferMethods::CountBufferMethods)) { - // TODO(Subv): Research and implement these methods. - LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); - return; - } - - ASSERT(bound_engines.find(subchannel) != bound_engines.end()); - - const EngineID engine = bound_engines[subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMethod(method, parameters); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMethod(method, parameters); - break; - case EngineID::MAXWELL_COMPUTE_B: - maxwell_compute->CallMethod(method, parameters); - break; - default: - UNIMPLEMENTED(); - } -} - void GPU::ProcessCommandList(GPUVAddr address, u32 size) { // TODO(Subv): PhysicalToVirtualAddress is a misnomer, it converts a GPU VAddr into an // application VAddr. diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 6c6162cf3..7aab163dc 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -8,7 +8,6 @@ namespace Tegra { namespace Engines { void Fermi2D::WriteReg(u32 method, u32 value) {} -void Fermi2D::CallMethod(u32 method, const std::vector& parameters) {} } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index ce8920cee..8967ddede 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -4,7 +4,6 @@ #pragma once -#include #include "common/common_types.h" namespace Tegra { @@ -17,13 +16,6 @@ public: /// Write the value to the register identified by method. void WriteReg(u32 method, u32 value); - - /** - * Handles a method call to this engine. - * @param method Method to call - * @param parameters Arguments to the method call - */ - void CallMethod(u32 method, const std::vector& parameters); }; } // namespace Engines diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index db12fc702..67b1b4e7f 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -8,28 +8,59 @@ namespace Tegra { namespace Engines { +/// First register id that is actually a Macro call. +constexpr u32 MacroRegistersStart = 0xE00; + const std::unordered_map Maxwell3D::method_handlers = { {0xE24, {"SetShader", 5, &Maxwell3D::SetShader}}, }; Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {} -void Maxwell3D::CallMethod(u32 method, const std::vector& parameters) { +void Maxwell3D::AttemptMethodCall(u32 method, const std::vector& parameters) { // TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47 auto itr = method_handlers.find(method); - if (itr == method_handlers.end()) { - LOG_ERROR(HW_GPU, "Unhandled method call %08X", method); - return; - } + ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method); + + // Only execute the macro handler once we've been fed the expected number of parameters. + if (itr->second.arguments != parameters.size()) + return; - ASSERT(itr->second.arguments == parameters.size()); (this->*itr->second.handler)(parameters); + + // Reset the current macro and its parameters. + executing_macro = 0; + macro_params.clear(); } void Maxwell3D::WriteReg(u32 method, u32 value) { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register, increase the size of the Regs structure"); + // It is an error to write to a register other than the current macro's ARG register before it + // has finished execution. + if (executing_macro != 0) { + ASSERT(method == executing_macro + 1); + } + + // Methods after 0xE00 are special, they're actually triggers for some microcode that was + // uploaded to the GPU during initialization. + if (method >= MacroRegistersStart) { + // We're trying to execute a macro + if (executing_macro == 0) { + // A macro call must begin by writing the macro method's register, not its argument. + ASSERT_MSG((method % 2) == 0, + "Can't start macro execution by writing to the ARGS register"); + executing_macro = method; + } + + macro_params.push_back(value); + + // Try to call the macro with the current number of parameters. + AttemptMethodCall(executing_macro, macro_params); + return; + } + regs.reg_array[method] = value; #define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32)) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 98137f94b..75a1c05bc 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -23,13 +23,6 @@ public: /// Write the value to the register identified by method. void WriteReg(u32 method, u32 value); - /** - * Handles a method call to this engine. - * @param method Method to call - * @param parameters Arguments to the method call - */ - void CallMethod(u32 method, const std::vector& parameters); - /// Register structure of the Maxwell3D engine. /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. struct Regs { @@ -166,7 +159,11 @@ public: INSERT_PADDING_WORDS(7); } cb_bind[MaxShaderStage]; - INSERT_PADDING_WORDS(0x50A); + INSERT_PADDING_WORDS(0x56); + + u32 tex_cb_index; + + INSERT_PADDING_WORDS(0x4B3); }; std::array reg_array; }; @@ -201,6 +198,19 @@ public: private: MemoryManager& memory_manager; + /// Macro method that is currently being executed / being fed parameters. + u32 executing_macro = 0; + /// Parameters that have been submitted to the macro call so far. + std::vector macro_params; + + /** + * Attempts a method call to this engine. Will return without doing anything if the number of + * parameters doesn't match what is expected for the method. + * @param method Method to call + * @param parameters Arguments to the method call + */ + void AttemptMethodCall(u32 method, const std::vector& parameters); + /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); @@ -234,6 +244,7 @@ ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); ASSERT_REG_POSITION(shader_config[0], 0x800); ASSERT_REG_POSITION(const_buffer, 0x8E0); ASSERT_REG_POSITION(cb_bind[0], 0x904); +ASSERT_REG_POSITION(tex_cb_index, 0x982); #undef ASSERT_REG_POSITION diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp index 3bef7fe86..e4e5f9e5e 100644 --- a/src/video_core/engines/maxwell_compute.cpp +++ b/src/video_core/engines/maxwell_compute.cpp @@ -8,7 +8,6 @@ namespace Tegra { namespace Engines { void MaxwellCompute::WriteReg(u32 method, u32 value) {} -void MaxwellCompute::CallMethod(u32 method, const std::vector& parameters) {} } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h index 5fc7ed635..7262e1bcb 100644 --- a/src/video_core/engines/maxwell_compute.h +++ b/src/video_core/engines/maxwell_compute.h @@ -4,7 +4,6 @@ #pragma once -#include #include "common/common_types.h" namespace Tegra { @@ -17,13 +16,6 @@ public: /// Write the value to the register identified by method. void WriteReg(u32 method, u32 value); - - /** - * Handles a method call to this engine. - * @param method Method to call - * @param parameters Arguments to the method call - */ - void CallMethod(u32 method, const std::vector& parameters); }; } // namespace Engines diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c5ec6fdef..ba7781756 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -41,9 +41,6 @@ private: /// Writes a single register in the engine bound to the specified subchannel void WriteReg(u32 method, u32 subchannel, u32 value); - /// Calls a method in the engine bound to the specified subchannel with the input parameters. - void CallMethod(u32 method, u32 subchannel, const std::vector& parameters); - /// Mapping of command subchannels to their bound engine ids. std::unordered_map bound_engines; From aa586fa26854cfe32b97aa99c2874945420bcfc4 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 18 Mar 2018 04:17:10 -0500 Subject: [PATCH 2/2] GPU: Store uploaded GPU macros and keep track of the number of method parameters. --- src/video_core/command_processor.cpp | 56 ++++++++++++++++++++------- src/video_core/engines/maxwell_3d.cpp | 23 +++++++---- src/video_core/engines/maxwell_3d.h | 12 ++++-- src/video_core/gpu.h | 10 ++++- 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 1d578582e..d4cdb4ab2 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -24,12 +24,37 @@ namespace Tegra { enum class BufferMethods { BindObject = 0, + SetGraphMacroCode = 0x45, + SetGraphMacroCodeArg = 0x46, + SetGraphMacroEntry = 0x47, CountBufferMethods = 0x100, }; -void GPU::WriteReg(u32 method, u32 subchannel, u32 value) { - LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel, - value); +void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) { + LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X remaining params %u", + method, subchannel, value, remaining_params); + + if (method == static_cast(BufferMethods::SetGraphMacroEntry)) { + // Prepare to upload a new macro, reset the upload counter. + LOG_DEBUG(HW_GPU, "Uploading GPU macro %08X", value); + current_macro_entry = value; + current_macro_code.clear(); + return; + } + + if (method == static_cast(BufferMethods::SetGraphMacroCodeArg)) { + // Append a new code word to the current macro. + current_macro_code.push_back(value); + + // There are no more params remaining, submit the code to the 3D engine. + if (remaining_params == 0) { + maxwell_3d->SubmitMacroCode(current_macro_entry, std::move(current_macro_code)); + current_macro_entry = InvalidGraphMacroEntry; + current_macro_code.clear(); + } + + return; + } if (method == static_cast(BufferMethods::BindObject)) { // Bind the current subchannel to the desired engine id. @@ -54,7 +79,7 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) { fermi_2d->WriteReg(method, value); break; case EngineID::MAXWELL_B: - maxwell_3d->WriteReg(method, value); + maxwell_3d->WriteReg(method, value, remaining_params); break; case EngineID::MAXWELL_COMPUTE_B: maxwell_compute->WriteReg(method, value); @@ -78,7 +103,8 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) { case SubmissionMode::Increasing: { // Increase the method value with each argument. for (unsigned i = 0; i < header.arg_count; ++i) { - WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr)); + WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr), + header.arg_count - i - 1); current_addr += sizeof(u32); } break; @@ -87,31 +113,31 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) { case SubmissionMode::NonIncreasing: { // Use the same method value for all arguments. for (unsigned i = 0; i < header.arg_count; ++i) { - WriteReg(header.method, header.subchannel, Memory::Read32(current_addr)); + WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), + header.arg_count - i - 1); current_addr += sizeof(u32); } break; } case SubmissionMode::IncreaseOnce: { ASSERT(header.arg_count.Value() >= 1); + // Use the original method for the first argument and then the next method for all other // arguments. + WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), + header.arg_count - 1); + current_addr += sizeof(u32); - // Process this command as a method call instead of a register write. Gather - // all the parameters first and then pass them at once to the CallMethod function. - std::vector parameters(header.arg_count); - - for (unsigned i = 0; i < header.arg_count; ++i) { - parameters[i] = Memory::Read32(current_addr); + for (unsigned i = 1; i < header.arg_count; ++i) { + WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), + header.arg_count - i - 1); current_addr += sizeof(u32); } - - CallMethod(header.method, header.subchannel, parameters); break; } case SubmissionMode::Inline: { // The register value is stored in the bits 16-28 as an immediate - WriteReg(header.method, header.subchannel, header.inline_data); + WriteReg(header.method, header.subchannel, header.inline_data, 0); break; } default: diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 67b1b4e7f..49a138c1d 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -17,14 +17,21 @@ const std::unordered_map Maxwell3D::method_handlers Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {} -void Maxwell3D::AttemptMethodCall(u32 method, const std::vector& parameters) { +void Maxwell3D::SubmitMacroCode(u32 entry, std::vector code) { + uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code); +} + +void Maxwell3D::CallMacroMethod(u32 method, const std::vector& parameters) { // TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47 + + // The requested macro must have been uploaded already. + ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded", + method); + auto itr = method_handlers.find(method); ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method); - // Only execute the macro handler once we've been fed the expected number of parameters. - if (itr->second.arguments != parameters.size()) - return; + ASSERT(itr->second.arguments == parameters.size()); (this->*itr->second.handler)(parameters); @@ -33,7 +40,7 @@ void Maxwell3D::AttemptMethodCall(u32 method, const std::vector& parameters macro_params.clear(); } -void Maxwell3D::WriteReg(u32 method, u32 value) { +void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register, increase the size of the Regs structure"); @@ -56,8 +63,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value) { macro_params.push_back(value); - // Try to call the macro with the current number of parameters. - AttemptMethodCall(executing_macro, macro_params); + // Call the macro when there are no more parameters in the command buffer + if (remaining_params == 0) { + CallMacroMethod(executing_macro, macro_params); + } return; } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 75a1c05bc..05820a21e 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -21,7 +21,10 @@ public: ~Maxwell3D() = default; /// Write the value to the register identified by method. - void WriteReg(u32 method, u32 value); + void WriteReg(u32 method, u32 value, u32 remaining_params); + + /// Uploads the code for a GPU macro program associated with the specified entry. + void SubmitMacroCode(u32 entry, std::vector code); /// Register structure of the Maxwell3D engine. /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. @@ -198,18 +201,19 @@ public: private: MemoryManager& memory_manager; + std::unordered_map> uploaded_macros; + /// Macro method that is currently being executed / being fed parameters. u32 executing_macro = 0; /// Parameters that have been submitted to the macro call so far. std::vector macro_params; /** - * Attempts a method call to this engine. Will return without doing anything if the number of - * parameters doesn't match what is expected for the method. + * Call a macro on this engine. * @param method Method to call * @param parameters Arguments to the method call */ - void AttemptMethodCall(u32 method, const std::vector& parameters); + void CallMacroMethod(u32 method, const std::vector& parameters); /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index ba7781756..d2e4ff52d 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/maxwell_3d.h" @@ -38,8 +39,10 @@ public: std::unique_ptr memory_manager; private: + static constexpr u32 InvalidGraphMacroEntry = 0xFFFFFFFF; + /// Writes a single register in the engine bound to the specified subchannel - void WriteReg(u32 method, u32 subchannel, u32 value); + void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params); /// Mapping of command subchannels to their bound engine ids. std::unordered_map bound_engines; @@ -50,6 +53,11 @@ private: std::unique_ptr fermi_2d; /// Compute engine std::unique_ptr maxwell_compute; + + /// Entry of the macro that is currently being uploaded + u32 current_macro_entry = InvalidGraphMacroEntry; + /// Code being uploaded for the current macro + std::vector current_macro_code; }; } // namespace Tegra