diff options
Diffstat (limited to 'src/video_core/engines')
-rw-r--r-- | src/video_core/engines/maxwell_3d.cpp | 223 | ||||
-rw-r--r-- | src/video_core/engines/maxwell_3d.h | 287 |
2 files changed, 373 insertions, 137 deletions
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 4d9745e48..124753032 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -2,8 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/assert.h" +#include "core/core.h" +#include "video_core/debug_utils/debug_utils.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/rasterizer_interface.h" +#include "video_core/renderer_base.h" +#include "video_core/textures/decoders.h" +#include "video_core/textures/texture.h" +#include "video_core/video_core.h" namespace Tegra { namespace Engines { @@ -11,41 +19,29 @@ namespace Engines { /// First register id that is actually a Macro call. constexpr u32 MacroRegistersStart = 0xE00; -const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = { - {0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}}, - {0xE24, {"SetShader", 5, &Maxwell3D::SetShader}}, - {0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}}, -}; - -Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {} +Maxwell3D::Maxwell3D(MemoryManager& memory_manager) + : memory_manager(memory_manager), macro_interpreter(*this) {} void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) { uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code); } -void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) { - // TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47 - +void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { + auto macro_code = uploaded_macros.find(method); // 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); - - ASSERT(itr->second.arguments == parameters.size()); + ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method); - (this->*itr->second.handler)(parameters); - - // Reset the current macro and its parameters. + // Reset the current macro and execute it. executing_macro = 0; - macro_params.clear(); + macro_interpreter.Execute(macro_code->second, std::move(parameters)); } 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"); + auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); + // 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) { @@ -67,11 +63,15 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { // Call the macro when there are no more parameters in the command buffer if (remaining_params == 0) { - CallMacroMethod(executing_macro, macro_params); + CallMacroMethod(executing_macro, std::move(macro_params)); } return; } + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); + } + regs.reg_array[method] = value; #define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32)) @@ -137,6 +137,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { } #undef MAXWELL3D_REG_INDEX + + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); + } } void Maxwell3D::ProcessQueryGet() { @@ -159,85 +163,20 @@ void Maxwell3D::ProcessQueryGet() { } void Maxwell3D::DrawArrays() { - LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring"); -} + LOG_DEBUG(HW_GPU, "called, topology=%d, count=%d", regs.draw.topology.Value(), + regs.vertex_buffer.count); -void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) { - /** - * Parameters description: - * [0] = Shader stage, usually 4 for FragmentShader - */ + auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); - u32 stage = parameters[0]; - - // Perform the same operations as the real macro code. - GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8; - u32 size = regs.tex_info_buffers.size[stage]; - - regs.const_buffer.cb_size = size; - regs.const_buffer.cb_address_high = address >> 32; - regs.const_buffer.cb_address_low = address & 0xFFFFFFFF; -} - -void Maxwell3D::SetShader(const std::vector<u32>& parameters) { - /** - * Parameters description: - * [0] = Shader Program. - * [1] = Unknown, presumably the shader id. - * [2] = Offset to the start of the shader, after the 0x30 bytes header. - * [3] = Shader Stage. - * [4] = Const Buffer Address >> 8. - */ - auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]); - // TODO(Subv): This address is probably an offset from the CODE_ADDRESS register. - GPUVAddr address = parameters[2]; - auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]); - GPUVAddr cb_address = parameters[4] << 8; - - auto& shader = state.shader_programs[static_cast<size_t>(shader_program)]; - shader.program = shader_program; - shader.stage = shader_stage; - shader.address = address; - - // Perform the same operations as the real macro code. - // TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1]. - auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)]; - shader_regs.start_id = address; - // TODO(Subv): Write params[1] to register 0xD1C + shader_program. - // TODO(Subv): Write params[2] to register 0xD22 + shader_program. - - // Note: This value is hardcoded in the macro's code. - static constexpr u32 DefaultCBSize = 0x10000; - regs.const_buffer.cb_size = DefaultCBSize; - regs.const_buffer.cb_address_high = cb_address >> 32; - regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF; - - // Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the - // shader. It's likely that these are the constants for the shader. - regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1); - regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1); - - ProcessCBBind(shader_stage); -} - -void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) { - /** - * Parameters description: - * [0] = Buffer offset >> 2 - */ - - u32 buffer_offset = parameters[0] << 2; - - // Perform the same operations as the real macro code. - // Note: This value is hardcoded in the macro's code. - static constexpr u32 DefaultCBSize = 0x5F00; - regs.const_buffer.cb_size = DefaultCBSize; + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); + } - GPUVAddr address = regs.ssbo_info.BufferAddress(); - regs.const_buffer.cb_address_high = address >> 32; - regs.const_buffer.cb_address_low = address & 0xFFFFFFFF; + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); + } - regs.const_buffer.cb_pos = buffer_offset; + VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(false /*is_indexed*/); } void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { @@ -270,5 +209,95 @@ void Maxwell3D::ProcessCBData(u32 value) { regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4; } +Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { + GPUVAddr tic_base_address = regs.tic.TICAddress(); + + GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry); + VAddr tic_address_cpu = memory_manager.PhysicalToVirtualAddress(tic_address_gpu); + + Texture::TICEntry tic_entry; + Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); + + ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear, + "TIC versions other than BlockLinear are unimplemented"); + + ASSERT_MSG(tic_entry.texture_type == Texture::TextureType::Texture2D, + "Texture types other than Texture2D are unimplemented"); + + auto r_type = tic_entry.r_type.Value(); + auto g_type = tic_entry.g_type.Value(); + auto b_type = tic_entry.b_type.Value(); + auto a_type = tic_entry.a_type.Value(); + + // TODO(Subv): Different data types for separate components are not supported + ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); + + return tic_entry; +} + +Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { + GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); + + GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry); + VAddr tsc_address_cpu = memory_manager.PhysicalToVirtualAddress(tsc_address_gpu); + + Texture::TSCEntry tsc_entry; + Memory::ReadBlock(tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); + return tsc_entry; +} + +std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { + std::vector<Texture::FullTextureInfo> textures; + + auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)]; + auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; + ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); + + GPUVAddr tic_base_address = regs.tic.TICAddress(); + + GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; + + // Offset into the texture constbuffer where the texture info begins. + static constexpr size_t TextureInfoOffset = 0x20; + + for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; + current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { + + Texture::TextureHandle tex_handle{ + Memory::Read32(memory_manager.PhysicalToVirtualAddress(current_texture))}; + + Texture::FullTextureInfo tex_info{}; + // TODO(Subv): Use the shader to determine which textures are actually accessed. + tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) / + sizeof(Texture::TextureHandle); + + // Load the TIC data. + if (tex_handle.tic_id != 0) { + tex_info.enabled = true; + + auto tic_entry = GetTICEntry(tex_handle.tic_id); + // TODO(Subv): Workaround for BitField's move constructor being deleted. + std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry)); + } + + // Load the TSC data + if (tex_handle.tsc_id != 0) { + auto tsc_entry = GetTSCEntry(tex_handle.tsc_id); + // TODO(Subv): Workaround for BitField's move constructor being deleted. + std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry)); + } + + if (tex_info.enabled) + textures.push_back(tex_info); + } + + return textures; +} + +u32 Maxwell3D::GetRegisterValue(u32 method) const { + ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register"); + return regs.reg_array[method]; +} + } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index aab282b77..98b39b2ff 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -7,10 +7,15 @@ #include <array> #include <unordered_map> #include <vector> +#include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/gpu.h" +#include "video_core/macro_interpreter.h" #include "video_core/memory_manager.h" +#include "video_core/textures/texture.h" namespace Tegra { namespace Engines { @@ -20,18 +25,13 @@ public: explicit Maxwell3D(MemoryManager& memory_manager); ~Maxwell3D() = default; - /// Write the value to the register identified by method. - 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<u32> code); - /// Register structure of the Maxwell3D engine. /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. struct Regs { static constexpr size_t NUM_REGS = 0xE36; static constexpr size_t NumRenderTargets = 8; + static constexpr size_t NumViewports = 16; static constexpr size_t NumCBData = 16; static constexpr size_t NumVertexArrays = 32; static constexpr size_t NumVertexAttributes = 32; @@ -62,6 +62,192 @@ public: Fragment = 4, }; + struct VertexAttribute { + enum class Size : u32 { + Size_32_32_32_32 = 0x01, + Size_32_32_32 = 0x02, + Size_16_16_16_16 = 0x03, + Size_32_32 = 0x04, + Size_16_16_16 = 0x05, + Size_8_8_8_8 = 0x0a, + Size_16_16 = 0x0f, + Size_32 = 0x12, + Size_8_8_8 = 0x13, + Size_8_8 = 0x18, + Size_16 = 0x1b, + Size_8 = 0x1d, + Size_10_10_10_2 = 0x30, + Size_11_11_10 = 0x31, + }; + + enum class Type : u32 { + SignedNorm = 1, + UnsignedNorm = 2, + SignedInt = 3, + UnsignedInt = 4, + UnsignedScaled = 5, + SignedScaled = 6, + Float = 7, + }; + + union { + BitField<0, 5, u32> buffer; + BitField<6, 1, u32> constant; + BitField<7, 14, u32> offset; + BitField<21, 6, Size> size; + BitField<27, 3, Type> type; + BitField<31, 1, u32> bgra; + }; + + u32 ComponentCount() const { + switch (size) { + case Size::Size_32_32_32_32: + return 4; + case Size::Size_32_32_32: + return 3; + case Size::Size_16_16_16_16: + return 4; + case Size::Size_32_32: + return 2; + case Size::Size_16_16_16: + return 3; + case Size::Size_8_8_8_8: + return 4; + case Size::Size_16_16: + return 2; + case Size::Size_32: + return 1; + case Size::Size_8_8_8: + return 3; + case Size::Size_8_8: + return 2; + case Size::Size_16: + return 1; + case Size::Size_8: + return 1; + case Size::Size_10_10_10_2: + return 4; + case Size::Size_11_11_10: + return 3; + default: + UNREACHABLE(); + } + } + + u32 SizeInBytes() const { + switch (size) { + case Size::Size_32_32_32_32: + return 16; + case Size::Size_32_32_32: + return 12; + case Size::Size_16_16_16_16: + return 8; + case Size::Size_32_32: + return 8; + case Size::Size_16_16_16: + return 6; + case Size::Size_8_8_8_8: + return 4; + case Size::Size_16_16: + return 4; + case Size::Size_32: + return 4; + case Size::Size_8_8_8: + return 3; + case Size::Size_8_8: + return 2; + case Size::Size_16: + return 2; + case Size::Size_8: + return 1; + case Size::Size_10_10_10_2: + return 4; + case Size::Size_11_11_10: + return 4; + default: + UNREACHABLE(); + } + } + + std::string SizeString() const { + switch (size) { + case Size::Size_32_32_32_32: + return "32_32_32_32"; + case Size::Size_32_32_32: + return "32_32_32"; + case Size::Size_16_16_16_16: + return "16_16_16_16"; + case Size::Size_32_32: + return "32_32"; + case Size::Size_16_16_16: + return "16_16_16"; + case Size::Size_8_8_8_8: + return "8_8_8_8"; + case Size::Size_16_16: + return "16_16"; + case Size::Size_32: + return "32"; + case Size::Size_8_8_8: + return "8_8_8"; + case Size::Size_8_8: + return "8_8"; + case Size::Size_16: + return "16"; + case Size::Size_8: + return "8"; + case Size::Size_10_10_10_2: + return "10_10_10_2"; + case Size::Size_11_11_10: + return "11_11_10"; + } + UNREACHABLE(); + return {}; + } + + std::string TypeString() const { + switch (type) { + case Type::SignedNorm: + return "SNORM"; + case Type::UnsignedNorm: + return "UNORM"; + case Type::SignedInt: + return "SINT"; + case Type::UnsignedInt: + return "UINT"; + case Type::UnsignedScaled: + return "USCALED"; + case Type::SignedScaled: + return "SSCALED"; + case Type::Float: + return "FLOAT"; + } + UNREACHABLE(); + return {}; + } + + bool IsNormalized() const { + return (type == Type::SignedNorm) || (type == Type::UnsignedNorm); + } + }; + + enum class PrimitiveTopology : u32 { + Points = 0x0, + Lines = 0x1, + LineLoop = 0x2, + LineStrip = 0x3, + Triangles = 0x4, + TriangleStrip = 0x5, + TriangleFan = 0x6, + Quads = 0x7, + QuadStrip = 0x8, + Polygon = 0x9, + LinesAdjacency = 0xa, + LineStripAdjacency = 0xb, + TrianglesAdjacency = 0xc, + TriangleStripAdjacency = 0xd, + Patches = 0xe, + }; + union { struct { INSERT_PADDING_WORDS(0x200); @@ -69,9 +255,9 @@ public: struct { u32 address_high; u32 address_low; - u32 horiz; - u32 vert; - u32 format; + u32 width; + u32 height; + Tegra::RenderTargetFormat format; u32 block_dimensions; u32 array_mode; u32 layer_stride; @@ -84,7 +270,31 @@ public: } } rt[NumRenderTargets]; - INSERT_PADDING_WORDS(0xDD); + INSERT_PADDING_WORDS(0x80); + + struct { + union { + BitField<0, 16, u32> x; + BitField<16, 16, u32> width; + }; + union { + BitField<0, 16, u32> y; + BitField<16, 16, u32> height; + }; + float depth_range_near; + float depth_range_far; + + MathUtil::Rectangle<s32> GetRect() const { + return { + static_cast<s32>(x), // left + static_cast<s32>(y + height), // top + static_cast<s32>(x + width), // right + static_cast<s32>(y) // bottom + }; + }; + } viewport[NumViewports]; + + INSERT_PADDING_WORDS(0x1D); struct { u32 first; @@ -108,14 +318,7 @@ public: INSERT_PADDING_WORDS(0x5B); - union { - BitField<0, 5, u32> buffer; - BitField<6, 1, u32> constant; - BitField<7, 14, u32> offset; - BitField<21, 6, u32> size; - BitField<27, 3, u32> type; - BitField<31, 1, u32> bgra; - } vertex_attrib_format[NumVertexAttributes]; + VertexAttribute vertex_attrib_format[NumVertexAttributes]; INSERT_PADDING_WORDS(0xF); @@ -163,13 +366,15 @@ public: } } code_address; INSERT_PADDING_WORDS(1); + struct { u32 vertex_end_gl; union { u32 vertex_begin_gl; - BitField<0, 16, u32> topology; + BitField<0, 16, PrimitiveTopology> topology; }; } draw; + INSERT_PADDING_WORDS(0x139); struct { u32 query_address_high; @@ -294,22 +499,27 @@ public: bool enabled; }; - struct ShaderProgramInfo { - Regs::ShaderStage stage; - Regs::ShaderProgram program; - GPUVAddr address; - }; - struct ShaderStageInfo { std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers; }; std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; - std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs; }; State state{}; + /// Reads a register value located at the input method address + u32 GetRegisterValue(u32 method) const; + + /// Write the value to the register identified by method. + 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<u32> code); + + /// Returns a list of enabled textures for the specified shader stage. + std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; + private: MemoryManager& memory_manager; @@ -320,12 +530,21 @@ private: /// Parameters that have been submitted to the macro call so far. std::vector<u32> macro_params; + /// Interpreter for the macro codes uploaded to the GPU. + MacroInterpreter macro_interpreter; + + /// Retrieves information about a specific TIC entry from the TIC buffer. + Texture::TICEntry GetTICEntry(u32 tic_index) const; + + /// Retrieves information about a specific TSC entry from the TSC buffer. + Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; + /** * Call a macro on this engine. * @param method Method to call * @param parameters Arguments to the method call */ - void CallMacroMethod(u32 method, const std::vector<u32>& parameters); + void CallMacroMethod(u32 method, std::vector<u32> parameters); /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); @@ -338,19 +557,6 @@ private: /// Handles a write to the VERTEX_END_GL register, triggering a draw. void DrawArrays(); - - /// Method call handlers - void BindTextureInfoBuffer(const std::vector<u32>& parameters); - void SetShader(const std::vector<u32>& parameters); - void BindStorageBuffer(const std::vector<u32>& parameters); - - struct MethodInfo { - const char* name; - u32 arguments; - void (Maxwell3D::*handler)(const std::vector<u32>& parameters); - }; - - static const std::unordered_map<u32, MethodInfo> method_handlers; }; #define ASSERT_REG_POSITION(field_name, position) \ @@ -358,6 +564,7 @@ private: "Field " #field_name " has invalid position") ASSERT_REG_POSITION(rt, 0x200); +ASSERT_REG_POSITION(viewport, 0x300); ASSERT_REG_POSITION(vertex_buffer, 0x35D); ASSERT_REG_POSITION(zeta, 0x3F8); ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); |