From 280c0b35f6ca4667b3f7a8515af1fcda568701b0 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 6 Dec 2015 11:44:22 -0800 Subject: [PATCH] Basic control flow skeleton and jumps implemented. A-train's trees draw right now! Helps a bit with #473 but still need to implement loops. --- src/xenia/gpu/glsl_shader_translator.cc | 71 +++++++++++++++++++++++-- src/xenia/gpu/glsl_shader_translator.h | 3 ++ src/xenia/gpu/shader.h | 2 + src/xenia/gpu/shader_translator.cc | 27 ++++++---- src/xenia/gpu/shader_translator.h | 8 +++ 5 files changed, 98 insertions(+), 13 deletions(-) diff --git a/src/xenia/gpu/glsl_shader_translator.cc b/src/xenia/gpu/glsl_shader_translator.cc index 73ac07f9e..e9ffbed88 100644 --- a/src/xenia/gpu/glsl_shader_translator.cc +++ b/src/xenia/gpu/glsl_shader_translator.cc @@ -287,23 +287,50 @@ void main() { EmitSource(" vec4 src0;\n"); EmitSource(" vec4 src1;\n"); EmitSource(" vec4 src2;\n"); + + // Master loop and switch for flow control. + EmitSourceDepth("int pc = 0;\n"); + EmitSourceDepth("while (pc != 0xFFFF) {\n"); + Indent(); + EmitSourceDepth("switch (pc) {\n"); } std::vector GlslShaderTranslator::CompleteTranslation() { + // End of master switch. + EmitSourceDepth("default: pc = 0xFFFF; break;\n"); + EmitSourceDepth("}; // switch\n"); + Unindent(); + EmitSourceDepth("} // while\n"); + // End of process*() function. EmitSource("}\n"); return source_.ToBytes(); } -void GlslShaderTranslator::ProcessLabel(uint32_t cf_index) { - EmitUnimplementedTranslationError(); -} +void GlslShaderTranslator::ProcessLabel(uint32_t cf_index) {} void GlslShaderTranslator::ProcessControlFlowNopInstruction() { EmitSource("// cnop\n"); } +void GlslShaderTranslator::ProcessControlFlowInstructionBegin( + uint32_t cf_index) { + cf_wrote_pc_ = false; + EmitSourceDepth("case 0x%X: // L%d\n", cf_index, cf_index); + Indent(); +} + +void GlslShaderTranslator::ProcessControlFlowInstructionEnd(uint32_t cf_index) { + if (!cf_wrote_pc_) { + uint32_t next_index = cf_index + 1; + EmitSourceDepth("pc = 0x%X; // Fallthrough to L%d\n", next_index, + next_index); + } + EmitSourceDepth("break;\n"); + Unindent(); +} + void GlslShaderTranslator::ProcessExecInstructionBegin( const ParsedExecInstruction& instr) { EmitSource("// "); @@ -330,6 +357,10 @@ void GlslShaderTranslator::ProcessExecInstructionEnd( const ParsedExecInstruction& instr) { Unindent(); EmitSourceDepth("}\n"); + if (instr.is_end) { + EmitSourceDepth("pc = 0xFFFF;\n"); + cf_wrote_pc_ = true; + } } void GlslShaderTranslator::ProcessLoopStartInstruction( @@ -369,7 +400,39 @@ void GlslShaderTranslator::ProcessJumpInstruction( EmitSource("// "); instr.Disassemble(&source_); - EmitUnimplementedTranslationError(); + bool needs_fallthrough = false; + switch (instr.type) { + case ParsedJumpInstruction::Type::kUnconditional: + EmitSourceDepth("{\n"); + break; + case ParsedJumpInstruction::Type::kConditional: + EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) == %c) {\n", + instr.bool_constant_index / 32, + instr.bool_constant_index % 32, + instr.condition ? '1' : '0'); + needs_fallthrough = true; + break; + case ParsedJumpInstruction::Type::kPredicated: + EmitSourceDepth("if (%cp0) {\n", instr.condition ? ' ' : '!'); + needs_fallthrough = true; + break; + } + Indent(); + + EmitSourceDepth("pc = 0x%X; // L%d\n", instr.target_address, + instr.target_address); + cf_wrote_pc_ = true; + + Unindent(); + if (needs_fallthrough) { + uint32_t next_address = instr.dword_index + 1; + EmitSourceDepth("} else {\n"); + EmitSourceDepth(" pc = 0x%X; // Fallthrough to L%d\n", next_address, + next_address); + EmitSourceDepth("}\n"); + } else { + EmitSourceDepth("}\n"); + } } void GlslShaderTranslator::ProcessAllocInstruction( diff --git a/src/xenia/gpu/glsl_shader_translator.h b/src/xenia/gpu/glsl_shader_translator.h index 043a61d15..af311bacc 100644 --- a/src/xenia/gpu/glsl_shader_translator.h +++ b/src/xenia/gpu/glsl_shader_translator.h @@ -40,6 +40,8 @@ class GlslShaderTranslator : public ShaderTranslator { void ProcessLabel(uint32_t cf_index) override; void ProcessControlFlowNopInstruction() override; + void ProcessControlFlowInstructionBegin(uint32_t cf_index) override; + void ProcessControlFlowInstructionEnd(uint32_t cf_index) override; void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) override; void ProcessExecInstructionEnd(const ParsedExecInstruction& instr) override; void ProcessLoopStartInstruction( @@ -70,6 +72,7 @@ class GlslShaderTranslator : public ShaderTranslator { StringBuffer source_; int depth_ = 0; char depth_prefix_[16] = {0}; + bool cf_wrote_pc_ = false; void ProcessVectorAluInstruction(const ParsedAluInstruction& instr); void ProcessScalarAluInstruction(const ParsedAluInstruction& instr); diff --git a/src/xenia/gpu/shader.h b/src/xenia/gpu/shader.h index 1c5cf1419..46f725475 100644 --- a/src/xenia/gpu/shader.h +++ b/src/xenia/gpu/shader.h @@ -195,6 +195,8 @@ struct ParsedExecInstruction { // Required condition value of the comparision (true or false). bool condition = false; + // Whether this exec ends the shader. + bool is_end = false; // Whether to reset the current predicate. bool clean = true; // ? diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index 75cab515d..60808f69a 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -303,22 +303,28 @@ bool ShaderTranslator::TranslateBlocks() { ControlFlowInstruction cf_b; UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b); + cf_index_ = cf_index; MarkUcodeInstruction(i); if (label_addresses.count(cf_index)) { AppendUcodeDisasmFormat(" label L%u\n", cf_index); ProcessLabel(cf_index); } AppendUcodeDisasmFormat("/* %4u.0 */ ", cf_index / 2); + ProcessControlFlowInstructionBegin(cf_index); TranslateControlFlowInstruction(cf_a); + ProcessControlFlowInstructionEnd(cf_index); ++cf_index; + cf_index_ = cf_index; MarkUcodeInstruction(i); if (label_addresses.count(cf_index)) { AppendUcodeDisasmFormat(" label L%u\n", cf_index); ProcessLabel(cf_index); } AppendUcodeDisasmFormat("/* %4u.1 */ ", cf_index / 2); + ProcessControlFlowInstructionBegin(cf_index); TranslateControlFlowInstruction(cf_b); + ProcessControlFlowInstructionEnd(cf_index); ++cf_index; } @@ -399,12 +405,13 @@ void ShaderTranslator::TranslateControlFlowNop( void ShaderTranslator::TranslateControlFlowExec( const ControlFlowExecInstruction& cf) { ParsedExecInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.opcode = cf.opcode(); i.opcode_name = cf.opcode() == ControlFlowOpcode::kExecEnd ? "exece" : "exec"; i.instruction_address = cf.address(); i.instruction_count = cf.count(); i.type = ParsedExecInstruction::Type::kUnconditional; + i.is_end = cf.opcode() == ControlFlowOpcode::kExecEnd; i.clean = cf.clean(); i.is_yield = cf.is_yield(); i.sequence = cf.sequence(); @@ -415,13 +422,14 @@ void ShaderTranslator::TranslateControlFlowExec( void ShaderTranslator::TranslateControlFlowCondExec( const ControlFlowCondExecInstruction& cf) { ParsedExecInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.opcode = cf.opcode(); i.opcode_name = "cexec"; switch (cf.opcode()) { case ControlFlowOpcode::kCondExecEnd: case ControlFlowOpcode::kCondExecPredCleanEnd: i.opcode_name = "cexece"; + i.is_end = true; break; } i.instruction_address = cf.address(); @@ -444,7 +452,7 @@ void ShaderTranslator::TranslateControlFlowCondExec( void ShaderTranslator::TranslateControlFlowCondExecPred( const ControlFlowCondExecPredInstruction& cf) { ParsedExecInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.opcode = cf.opcode(); i.opcode_name = cf.opcode() == ControlFlowOpcode::kCondExecPredEnd ? "exece" : "exec"; @@ -452,6 +460,7 @@ void ShaderTranslator::TranslateControlFlowCondExecPred( i.instruction_count = cf.count(); i.type = ParsedExecInstruction::Type::kPredicated; i.condition = cf.condition(); + i.is_end = cf.opcode() == ControlFlowOpcode::kCondExecPredEnd; i.clean = cf.clean(); i.is_yield = cf.is_yield(); i.sequence = cf.sequence(); @@ -462,7 +471,7 @@ void ShaderTranslator::TranslateControlFlowCondExecPred( void ShaderTranslator::TranslateControlFlowLoopStart( const ControlFlowLoopStartInstruction& cf) { ParsedLoopStartInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.loop_constant_index = cf.loop_id(); i.is_repeat = cf.is_repeat(); i.loop_skip_address = cf.address(); @@ -475,7 +484,7 @@ void ShaderTranslator::TranslateControlFlowLoopStart( void ShaderTranslator::TranslateControlFlowLoopEnd( const ControlFlowLoopEndInstruction& cf) { ParsedLoopEndInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.is_predicated_break = cf.is_predicated_break(); i.predicate_condition = cf.condition(); i.loop_constant_index = cf.loop_id(); @@ -489,7 +498,7 @@ void ShaderTranslator::TranslateControlFlowLoopEnd( void ShaderTranslator::TranslateControlFlowCondCall( const ControlFlowCondCallInstruction& cf) { ParsedCallInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.target_address = cf.address(); if (cf.is_unconditional()) { i.type = ParsedCallInstruction::Type::kUnconditional; @@ -510,7 +519,7 @@ void ShaderTranslator::TranslateControlFlowCondCall( void ShaderTranslator::TranslateControlFlowReturn( const ControlFlowReturnInstruction& cf) { ParsedReturnInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.Disassemble(&ucode_disasm_buffer_); @@ -520,7 +529,7 @@ void ShaderTranslator::TranslateControlFlowReturn( void ShaderTranslator::TranslateControlFlowCondJmp( const ControlFlowCondJmpInstruction& cf) { ParsedJumpInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.target_address = cf.address(); if (cf.is_unconditional()) { i.type = ParsedJumpInstruction::Type::kUnconditional; @@ -541,7 +550,7 @@ void ShaderTranslator::TranslateControlFlowCondJmp( void ShaderTranslator::TranslateControlFlowAlloc( const ControlFlowAllocInstruction& cf) { ParsedAllocInstruction i; - i.dword_index = 0; + i.dword_index = cf_index_; i.type = cf.alloc_type(); i.count = cf.size(); i.is_vertex_shader = is_vertex_shader(); diff --git a/src/xenia/gpu/shader_translator.h b/src/xenia/gpu/shader_translator.h index 5766b5d40..adf8f449d 100644 --- a/src/xenia/gpu/shader_translator.h +++ b/src/xenia/gpu/shader_translator.h @@ -74,6 +74,11 @@ class ShaderTranslator { // Handles translation for control flow nop instructions. virtual void ProcessControlFlowNopInstruction() {} + // Handles the start of a control flow instruction at the given address. + virtual void ProcessControlFlowInstructionBegin(uint32_t cf_index) {} + // Handles the end of a control flow instruction that began at the given + // address. + virtual void ProcessControlFlowInstructionEnd(uint32_t cf_index) {} // Handles translation for control flow exec instructions prior to their // contained ALU/fetch instructions. virtual void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) { @@ -166,6 +171,9 @@ class ShaderTranslator { // Accumulated translation errors. std::vector errors_; + // Current control flow dword index. + uint32_t cf_index_ = 0; + // Microcode disassembly buffer, accumulated throughout the translation. StringBuffer ucode_disasm_buffer_; // Current line number in the disasm, which can be used for source annotation.