diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 603a2edaf2..db12fc7025 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -15,6 +15,7 @@ const std::unordered_map Maxwell3D::method_handlers Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {} void Maxwell3D::CallMethod(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); @@ -42,6 +43,26 @@ void Maxwell3D::WriteReg(u32 method, u32 value) { ASSERT_MSG(regs.code_address.CodeAddress() == 0, "Unexpected CODE_ADDRESS register value."); break; } + case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): { + ProcessCBBind(Regs::ShaderStage::Vertex); + break; + } + case MAXWELL3D_REG_INDEX(cb_bind[1].raw_config): { + ProcessCBBind(Regs::ShaderStage::TesselationControl); + break; + } + case MAXWELL3D_REG_INDEX(cb_bind[2].raw_config): { + ProcessCBBind(Regs::ShaderStage::TesselationEval); + break; + } + case MAXWELL3D_REG_INDEX(cb_bind[3].raw_config): { + ProcessCBBind(Regs::ShaderStage::Geometry); + break; + } + case MAXWELL3D_REG_INDEX(cb_bind[4].raw_config): { + ProcessCBBind(Regs::ShaderStage::Fragment); + break; + } case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): { DrawArrays(); break; @@ -83,22 +104,54 @@ void Maxwell3D::SetShader(const std::vector& parameters) { /** * Parameters description: * [0] = Shader Program. - * [1] = Unknown. + * [1] = Unknown, presumably the shader id. * [2] = Offset to the start of the shader, after the 0x30 bytes header. - * [3] = Shader Type. - * [4] = Shader End Address >> 8. + * [3] = Shader Stage. + * [4] = Const Buffer Address >> 8. */ auto shader_program = static_cast(parameters[0]); // TODO(Subv): This address is probably an offset from the CODE_ADDRESS register. - GPUVAddr begin_address = parameters[2]; - auto shader_type = static_cast(parameters[3]); - GPUVAddr end_address = parameters[4] << 8; + GPUVAddr address = parameters[2]; + auto shader_stage = static_cast(parameters[3]); + GPUVAddr cb_address = parameters[4] << 8; - auto& shader = state.shaders[static_cast(shader_program)]; + auto& shader = state.shader_programs[static_cast(shader_program)]; shader.program = shader_program; - shader.type = shader_type; - shader.begin_address = begin_address; - shader.end_address = end_address; + 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(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(shader_stage)].valid.Assign(1); + regs.cb_bind[static_cast(shader_stage)].index.Assign(1); + + ProcessCBBind(shader_stage); +} + +void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { + // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. + auto& shader = state.shader_stages[static_cast(stage)]; + auto& bind_data = regs.cb_bind[static_cast(stage)]; + + auto& buffer = shader.const_buffers[bind_data.index]; + + buffer.enabled = bind_data.valid.Value() != 0; + buffer.index = bind_data.index; + buffer.address = regs.const_buffer.BufferAddress(); + buffer.size = regs.const_buffer.cb_size; } } // namespace Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index c979d4e615..98137f94b2 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -35,8 +35,12 @@ public: struct Regs { static constexpr size_t NUM_REGS = 0xE36; + static constexpr size_t NumCBData = 16; static constexpr size_t NumVertexArrays = 32; static constexpr size_t MaxShaderProgram = 6; + static constexpr size_t MaxShaderStage = 5; + // Maximum number of const buffers per shader stage. + static constexpr size_t MaxConstBuffers = 16; enum class QueryMode : u32 { Write = 0, @@ -52,7 +56,7 @@ public: Fragment = 5, }; - enum class ShaderType : u32 { + enum class ShaderStage : u32 { Vertex = 0, TesselationControl = 1, TesselationEval = 2, @@ -132,17 +136,37 @@ public: u32 start_id; INSERT_PADDING_WORDS(1); u32 gpr_alloc; - ShaderType type; + ShaderStage type; INSERT_PADDING_WORDS(9); } shader_config[MaxShaderProgram]; - INSERT_PADDING_WORDS(0x5D0); + INSERT_PADDING_WORDS(0x8C); struct { - u32 shader_code_call; - u32 shader_code_args; - } shader_code; + u32 cb_size; + u32 cb_address_high; + u32 cb_address_low; + u32 cb_pos; + u32 cb_data[NumCBData]; + + GPUVAddr BufferAddress() const { + return static_cast( + (static_cast(cb_address_high) << 32) | cb_address_low); + } + } const_buffer; + INSERT_PADDING_WORDS(0x10); + + struct { + union { + u32 raw_config; + BitField<0, 1, u32> valid; + BitField<4, 5, u32> index; + }; + INSERT_PADDING_WORDS(7); + } cb_bind[MaxShaderStage]; + + INSERT_PADDING_WORDS(0x50A); }; std::array reg_array; }; @@ -151,17 +175,28 @@ public: static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); struct State { - struct ShaderInfo { - Regs::ShaderType type; - Regs::ShaderProgram program; - GPUVAddr begin_address; - GPUVAddr end_address; + struct ConstBufferInfo { + GPUVAddr address; + u32 index; + u32 size; + bool enabled; }; - std::array shaders; + struct ShaderProgramInfo { + Regs::ShaderStage stage; + Regs::ShaderProgram program; + GPUVAddr address; + }; + + struct ShaderStageInfo { + std::array const_buffers; + }; + + std::array shader_stages; + std::array shader_programs; }; - State state; + State state{}; private: MemoryManager& memory_manager; @@ -169,6 +204,9 @@ private: /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); + /// Handles a write to the CB_BIND register. + void ProcessCBBind(Regs::ShaderStage stage); + /// Handles a write to the VERTEX_END_GL register, triggering a draw. void DrawArrays(); @@ -194,7 +232,8 @@ ASSERT_REG_POSITION(query, 0x6C0); ASSERT_REG_POSITION(vertex_array[0], 0x700); ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0); ASSERT_REG_POSITION(shader_config[0], 0x800); -ASSERT_REG_POSITION(shader_code, 0xE24); +ASSERT_REG_POSITION(const_buffer, 0x8E0); +ASSERT_REG_POSITION(cb_bind[0], 0x904); #undef ASSERT_REG_POSITION