From 33ba604fd903d9511a414a54b91ebe818df338ef Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 12 Jul 2015 01:57:59 +0200 Subject: Introduce a shader tracer to allow inspection of input/output values for each processed instruction. --- src/video_core/shader/shader.h | 195 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 189 insertions(+), 6 deletions(-) (limited to 'src/video_core/shader/shader.h') diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 2007a2844..58d21f7cd 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -4,7 +4,10 @@ #pragma once +#include + #include + #include #include "common/common_funcs.h" @@ -72,12 +75,185 @@ struct OutputVertex { static_assert(std::is_pod::value, "Structure is not POD"); static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); + +// Helper structure used to keep track of data useful for inspection of shader emulation +template +struct DebugData; + +template<> +struct DebugData { + // TODO: Hide these behind and interface and move them to DebugData + u32 max_offset; // maximum program counter ever reached + u32 max_opdesc_id; // maximum swizzle pattern index ever used +}; + +template<> +struct DebugData { + // Records store the input and output operands of a particular instruction. + struct Record { + enum Type { + // Floating point arithmetic operands + SRC1 = 0x1, + SRC2 = 0x2, + SRC3 = 0x4, + + // Initial and final output operand value + DEST_IN = 0x8, + DEST_OUT = 0x10, + + // Current and next instruction offset (in words) + CUR_INSTR = 0x20, + NEXT_INSTR = 0x40, + + // Output address register value + ADDR_REG_OUT = 0x80, + + // Result of a comparison instruction + CMP_RESULT = 0x100, + + // Input values for conditional flow control instructions + COND_BOOL_IN = 0x200, + COND_CMP_IN = 0x400, + + // Input values for a loop + LOOP_INT_IN = 0x800, + }; + + Math::Vec4 src1; + Math::Vec4 src2; + Math::Vec4 src3; + + Math::Vec4 dest_in; + Math::Vec4 dest_out; + + s32 address_registers[2]; + bool conditional_code[2]; + bool cond_bool; + bool cond_cmp[2]; + Math::Vec4 loop_int; + + u32 instruction_offset; + u32 next_instruction; + + // set of enabled fields (as a combination of Type flags) + unsigned mask = 0; + }; + + u32 max_offset; // maximum program counter ever reached + u32 max_opdesc_id; // maximum swizzle pattern index ever used + + // List of records for each executed shader instruction + std::vector::Record> records; +}; + +// Type alias for better readability +using DebugDataRecord = DebugData::Record; + +// Helper function to set a DebugData::Record field based on the template enum parameter. +template +inline void SetField(DebugDataRecord& record, ValueType value); + +template<> +inline void SetField(DebugDataRecord& record, float24* value) { + record.src1.x = value[0]; + record.src1.y = value[1]; + record.src1.z = value[2]; + record.src1.w = value[3]; +} + +template<> +inline void SetField(DebugDataRecord& record, float24* value) { + record.src2.x = value[0]; + record.src2.y = value[1]; + record.src2.z = value[2]; + record.src2.w = value[3]; +} + +template<> +inline void SetField(DebugDataRecord& record, float24* value) { + record.src3.x = value[0]; + record.src3.y = value[1]; + record.src3.z = value[2]; + record.src3.w = value[3]; +} + +template<> +inline void SetField(DebugDataRecord& record, float24* value) { + record.dest_in.x = value[0]; + record.dest_in.y = value[1]; + record.dest_in.z = value[2]; + record.dest_in.w = value[3]; +} + +template<> +inline void SetField(DebugDataRecord& record, float24* value) { + record.dest_out.x = value[0]; + record.dest_out.y = value[1]; + record.dest_out.z = value[2]; + record.dest_out.w = value[3]; +} + +template<> +inline void SetField(DebugDataRecord& record, s32* value) { + record.address_registers[0] = value[0]; + record.address_registers[1] = value[1]; +} + +template<> +inline void SetField(DebugDataRecord& record, bool* value) { + record.conditional_code[0] = value[0]; + record.conditional_code[1] = value[1]; +} + +template<> +inline void SetField(DebugDataRecord& record, bool value) { + record.cond_bool = value; +} + +template<> +inline void SetField(DebugDataRecord& record, bool* value) { + record.cond_cmp[0] = value[0]; + record.cond_cmp[1] = value[1]; +} + +template<> +inline void SetField(DebugDataRecord& record, Math::Vec4 value) { + record.loop_int = value; +} + +template<> +inline void SetField(DebugDataRecord& record, u32 value) { + record.instruction_offset = value; +} + +template<> +inline void SetField(DebugDataRecord& record, u32 value) { + record.next_instruction = value; +} + +// Helper function to set debug information on the current shader iteration. +template +inline void Record(DebugData& debug_data, u32 offset, ValueType value) { + // Debugging disabled => nothing to do +} + +template +inline void Record(DebugData& debug_data, u32 offset, ValueType value) { + if (offset >= debug_data.records.size()) + debug_data.records.resize(offset + 1); + + SetField(debug_data.records[offset], value); + debug_data.records[offset].mask |= type; +} + + /** * This structure contains the state information that needs to be unique for a shader unit. The 3DS * has four shader units that process shaders in parallel. At the present, Citra only implements a * single shader unit that processes all shaders serially. Putting the state information in a struct * here will make it easier for us to parallelize the shader processing later. */ +template struct UnitState { struct Registers { // The registers are accessed by the shader JIT using SSE instructions, and are therefore @@ -111,10 +287,7 @@ struct UnitState { // TODO: Is there a maximal size for this? boost::container::static_vector call_stack; - struct { - u32 max_offset; // maximum program counter ever reached - u32 max_opdesc_id; // maximum swizzle pattern index ever used - } debug; + DebugData debug; static int InputOffset(const SourceRegister& reg) { switch (reg.GetRegisterType()) { @@ -150,7 +323,7 @@ struct UnitState { * vertex, which would happen within the `Run` function). * @param state Shader unit state, must be setup per shader and per shader unit */ -void Setup(UnitState& state); +void Setup(UnitState& state); /// Performs any cleanup when the emulator is shutdown void Shutdown(); @@ -162,7 +335,17 @@ void Shutdown(); * @param num_attributes The number of vertex shader attributes * @return The output vertex, after having been processed by the vertex shader */ -OutputVertex Run(UnitState& state, const InputVertex& input, int num_attributes); +OutputVertex Run(UnitState& state, const InputVertex& input, int num_attributes); + +/** + * Produce debug information based on the given shader and input vertex + * @param input Input vertex into the shader + * @param num_attributes The number of vertex shader attributes + * @param config Configuration object for the shader pipeline + * @param setup Setup object for the shader pipeline + * @return Debug information for this shader with regards to the given vertex + */ +DebugData ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup); } // namespace Shader -- cgit v1.2.3