diff --git a/src/xenia/gpu/shader_compiler_main.cc b/src/xenia/gpu/shader_compiler_main.cc index f7a64681e..cc7886b72 100644 --- a/src/xenia/gpu/shader_compiler_main.cc +++ b/src/xenia/gpu/shader_compiler_main.cc @@ -17,13 +17,15 @@ #include "xenia/base/main.h" #include "xenia/base/string.h" #include "xenia/gpu/shader_translator.h" +#include "xenia/gpu/spirv_shader_translator.h" +#include "xenia/ui/spirv/spirv_disassembler.h" DEFINE_string(shader_input, "", "Input shader binary file path."); DEFINE_string(shader_input_type, "", "'vs', 'ps', or unspecified to infer from the given filename."); DEFINE_string(shader_output, "", "Output shader file path."); DEFINE_string(shader_output_type, "ucode", - "Translator to use: [ucode, spirvtext]."); + "Translator to use: [ucode, spirv, spirvtext]."); namespace xe { namespace gpu { @@ -77,9 +79,9 @@ int shader_compiler_main(const std::vector& args) { ucode_dwords.size(), ucode_dwords.size() * 4); std::unique_ptr translator; - if (FLAGS_shader_output_type == "spirvtext") { - // TODO(benvanik): SPIRV translator. - translator = std::make_unique(); + if (FLAGS_shader_output_type == "spirv" || + FLAGS_shader_output_type == "spirvtext") { + translator = std::make_unique(); } else { translator = std::make_unique(); } @@ -89,10 +91,20 @@ int shader_compiler_main(const std::vector& args) { auto translated_shader = translator->Translate( shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size()); + const void* source_data = translated_shader->binary().data(); + size_t source_data_size = translated_shader->binary().size(); + + if (FLAGS_shader_output_type == "spirvtext") { + // Disassemble SPIRV. + auto disasm_result = xe::ui::spirv::SpirvDisassembler().Disassemble( + reinterpret_cast(source_data), source_data_size / 4); + source_data = disasm_result->text(); + source_data_size = std::strlen(disasm_result->text()) + 1; + } + if (!FLAGS_shader_output.empty()) { auto output_file = fopen(FLAGS_shader_output.c_str(), "w"); - fwrite(translated_shader->binary().data(), - translated_shader->binary().size(), 1, output_file); + fwrite(source_data, source_data_size, 1, output_file); fclose(output_file); } diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index b42cb1df0..0fb16221b 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -74,6 +74,27 @@ std::unique_ptr ShaderTranslator::Translate( ucode_dwords_ = ucode_dwords; ucode_dword_count_ = ucode_dword_count; + // Run through and gather all binding information. + // Translators may need this before they start codegen. + uint32_t max_cf_dword_index = static_cast(ucode_dword_count_); + for (uint32_t i = 0; i < max_cf_dword_index; i += 3) { + ControlFlowInstruction cf_a; + ControlFlowInstruction cf_b; + UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b); + if (IsControlFlowOpcodeExec(cf_a.opcode())) { + max_cf_dword_index = + std::min(max_cf_dword_index, cf_a.exec.address() * 3); + } + if (IsControlFlowOpcodeExec(cf_b.opcode())) { + max_cf_dword_index = + std::min(max_cf_dword_index, cf_b.exec.address() * 3); + } + GatherBindingInformation(cf_a); + GatherBindingInformation(cf_b); + } + + StartTranslation(); + TranslateBlocks(); std::unique_ptr translated_shader( @@ -119,6 +140,65 @@ void ShaderTranslator::EmitTranslationError(const char* message) { errors_.push_back(std::move(error)); } +void ShaderTranslator::GatherBindingInformation( + const ControlFlowInstruction& cf) { + switch (cf.opcode()) { + case ControlFlowOpcode::kExec: + case ControlFlowOpcode::kExecEnd: + case ControlFlowOpcode::kCondExec: + case ControlFlowOpcode::kCondExecEnd: + case ControlFlowOpcode::kCondExecPred: + case ControlFlowOpcode::kCondExecPredEnd: + case ControlFlowOpcode::kCondExecPredClean: + case ControlFlowOpcode::kCondExecPredCleanEnd: + uint32_t sequence = cf.exec.sequence(); + for (uint32_t instr_offset = cf.exec.address(); + instr_offset < cf.exec.address() + cf.exec.count(); + ++instr_offset, sequence >>= 2) { + bool is_fetch = (sequence & 0x1) == 0x1; + if (is_fetch) { + auto fetch_opcode = + static_cast(ucode_dwords_[instr_offset * 3] & 0x1F); + if (fetch_opcode == FetchOpcode::kVertexFetch) { + GatherVertexBindingInformation( + *reinterpret_cast( + ucode_dwords_ + instr_offset * 3)); + } else { + GatherTextureBindingInformation( + *reinterpret_cast( + ucode_dwords_ + instr_offset * 3)); + } + } + } + break; + } +} + +void ShaderTranslator::GatherVertexBindingInformation( + const VertexFetchInstruction& op) { + TranslatedShader::VertexBinding binding; + binding.binding_index = vertex_bindings_.size(); + ParseVertexFetchInstruction(op, &binding.fetch_instr); + binding.fetch_constant = binding.fetch_instr.operands[1].storage_index; + vertex_bindings_.emplace_back(std::move(binding)); +} + +void ShaderTranslator::GatherTextureBindingInformation( + const TextureFetchInstruction& op) { + switch (op.opcode()) { + case FetchOpcode::kSetTextureLod: + case FetchOpcode::kSetTextureGradientsHorz: + case FetchOpcode::kSetTextureGradientsVert: + // Doesn't use bindings. + return; + } + TranslatedShader::TextureBinding binding; + binding.binding_index = texture_bindings_.size(); + ParseTextureFetchInstruction(op, &binding.fetch_instr); + binding.fetch_constant = binding.fetch_instr.operands[1].storage_index; + texture_bindings_.emplace_back(std::move(binding)); +} + void AddControlFlowTargetLabel(const ControlFlowInstruction& cf, std::set* label_addresses) { switch (cf.opcode()) { @@ -164,16 +244,6 @@ bool ShaderTranslator::TranslateBlocks() { AddControlFlowTargetLabel(cf_b, &label_addresses); } - // Run through and gather all binding information. - // Translators may need this before they start codegen. - for (uint32_t i = 0, cf_index = 0; i < max_cf_dword_index; i += 3) { - ControlFlowInstruction cf_a; - ControlFlowInstruction cf_b; - UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b); - GatherBindingInformation(cf_a); - GatherBindingInformation(cf_b); - } - // Translate all instructions. for (uint32_t i = 0, cf_index = 0; i < max_cf_dword_index; i += 3) { ControlFlowInstruction cf_a; @@ -183,6 +253,7 @@ bool ShaderTranslator::TranslateBlocks() { MarkUcodeInstruction(i); if (label_addresses.count(cf_index)) { AppendUcodeDisasmFormat(" label L%u\n", cf_index); + ProcessLabel(cf_index); } AppendUcodeDisasmFormat("/* %4u.0 */ ", cf_index / 2); TranslateControlFlowInstruction(cf_a); @@ -191,6 +262,7 @@ bool ShaderTranslator::TranslateBlocks() { MarkUcodeInstruction(i); if (label_addresses.count(cf_index)) { AppendUcodeDisasmFormat(" label L%u\n", cf_index); + ProcessLabel(cf_index); } AppendUcodeDisasmFormat("/* %4u.1 */ ", cf_index / 2); TranslateControlFlowInstruction(cf_b); @@ -200,74 +272,12 @@ bool ShaderTranslator::TranslateBlocks() { return true; } -void ShaderTranslator::GatherBindingInformation( - const ControlFlowInstruction& cf) { - switch (cf.opcode()) { - case ControlFlowOpcode::kExec: - case ControlFlowOpcode::kExecEnd: - case ControlFlowOpcode::kCondExec: - case ControlFlowOpcode::kCondExecEnd: - case ControlFlowOpcode::kCondExecPred: - case ControlFlowOpcode::kCondExecPredEnd: - case ControlFlowOpcode::kCondExecPredClean: - case ControlFlowOpcode::kCondExecPredCleanEnd: - uint32_t sequence = cf.exec.sequence(); - for (uint32_t instr_offset = cf.exec.address(); - instr_offset < cf.exec.address() + cf.exec.count(); - ++instr_offset, sequence >>= 2) { - bool is_fetch = (sequence & 0x1) == 0x1; - if (is_fetch) { - auto fetch_opcode = - static_cast(ucode_dwords_[instr_offset * 3] & 0x1F); - if (fetch_opcode == FetchOpcode::kVertexFetch) { - GatherVertexBindingInformation( - *reinterpret_cast( - ucode_dwords_ + instr_offset * 3)); - } else { - GatherTextureBindingInformation( - *reinterpret_cast( - ucode_dwords_ + instr_offset * 3)); - } - } - } - break; - } -} - -void ShaderTranslator::GatherVertexBindingInformation( - const VertexFetchInstruction& op) { - TranslatedShader::VertexBinding binding; - binding.binding_index = vertex_bindings_.size(); - binding.op = op; - if (op.is_mini_fetch()) { - // Reuses an existing fetch constant. - binding.op.AssignFromFull(previous_vfetch_full_); - } else { - previous_vfetch_full_ = op; - } - binding.fetch_constant = binding.op.fetch_constant_index(); - vertex_bindings_.emplace_back(std::move(binding)); -} - -void ShaderTranslator::GatherTextureBindingInformation( - const TextureFetchInstruction& op) { - switch (op.opcode()) { - case FetchOpcode::kSetTextureLod: - case FetchOpcode::kSetTextureGradientsHorz: - case FetchOpcode::kSetTextureGradientsVert: - // Doesn't use bindings. - return; - } - TranslatedShader::TextureBinding binding; - binding.binding_index = texture_bindings_.size(); - binding.fetch_constant = op.fetch_constant_index(); - binding.op = op; - texture_bindings_.emplace_back(std::move(binding)); +std::vector UcodeShaderTranslator::CompleteTranslation() { + return ucode_disasm_buffer().ToBytes(); } void ShaderTranslator::TranslateControlFlowInstruction( const ControlFlowInstruction& cf) { - bool ends_shader = false; switch (cf.opcode()) { case ControlFlowOpcode::kNop: TranslateControlFlowNop(cf); @@ -277,28 +287,24 @@ void ShaderTranslator::TranslateControlFlowInstruction( break; case ControlFlowOpcode::kExecEnd: TranslateControlFlowExec(cf.exec); - ends_shader = true; break; case ControlFlowOpcode::kCondExec: TranslateControlFlowCondExec(cf.cond_exec); break; case ControlFlowOpcode::kCondExecEnd: TranslateControlFlowCondExec(cf.cond_exec); - ends_shader = true; break; case ControlFlowOpcode::kCondExecPred: TranslateControlFlowCondExecPred(cf.cond_exec_pred); break; case ControlFlowOpcode::kCondExecPredEnd: TranslateControlFlowCondExecPred(cf.cond_exec_pred); - ends_shader = true; break; case ControlFlowOpcode::kCondExecPredClean: TranslateControlFlowCondExec(cf.cond_exec); break; case ControlFlowOpcode::kCondExecPredCleanEnd: TranslateControlFlowCondExec(cf.cond_exec); - ends_shader = true; break; case ControlFlowOpcode::kLoopStart: TranslateControlFlowLoopStart(cf.loop_start); @@ -324,6 +330,7 @@ void ShaderTranslator::TranslateControlFlowInstruction( assert_unhandled_case(cf.opcode); break; } + bool ends_shader = DoesControlFlowOpcodeEndShader(cf.opcode()); if (ends_shader) { // TODO(benvanik): return? } @@ -331,180 +338,175 @@ void ShaderTranslator::TranslateControlFlowInstruction( void ShaderTranslator::TranslateControlFlowNop( const ControlFlowInstruction& cf) { - AppendUcodeDisasm(" cnop\n"); + ucode_disasm_buffer_.Append(" cnop\n"); - ProcessControlFlowNop(cf); + ProcessControlFlowNopInstruction(); } void ShaderTranslator::TranslateControlFlowExec( const ControlFlowExecInstruction& cf) { - AppendUcodeDisasm(" "); - AppendUcodeDisasm(cf.opcode() == ControlFlowOpcode::kExecEnd ? "exece" - : "exec"); - if (cf.is_yield()) { - AppendUcodeDisasm(" Yield=true"); - } - if (!cf.clean()) { - AppendUcodeDisasm(" // PredicateClean=false"); - } - AppendUcodeDisasm('\n'); + ParsedExecInstruction i; + i.dword_index = 0; + 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.clean = cf.clean(); + i.is_yield = cf.is_yield(); + i.sequence = cf.sequence(); - ProcessControlFlowExec(cf); - - TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); + TranslateExecInstructions(i); } void ShaderTranslator::TranslateControlFlowCondExec( const ControlFlowCondExecInstruction& cf) { - const char* opcode_name = "cexec"; + ParsedExecInstruction i; + i.dword_index = 0; + i.opcode = cf.opcode(); + i.opcode_name = "cexec"; switch (cf.opcode()) { case ControlFlowOpcode::kCondExecEnd: case ControlFlowOpcode::kCondExecPredCleanEnd: - opcode_name = "cexece"; + i.opcode_name = "cexece"; break; } - AppendUcodeDisasm(" "); - AppendUcodeDisasm(opcode_name); - AppendUcodeDisasm(' '); - if (!cf.condition()) { - AppendUcodeDisasm('!'); - } - AppendUcodeDisasmFormat("b%u", cf.bool_address()); - if (cf.is_yield()) { - AppendUcodeDisasm(", Yield=true"); - } + i.instruction_address = cf.address(); + i.instruction_count = cf.count(); + i.type = ParsedExecInstruction::Type::kConditional; + i.bool_constant_index = cf.bool_address(); + i.condition = cf.condition(); switch (cf.opcode()) { case ControlFlowOpcode::kCondExec: case ControlFlowOpcode::kCondExecEnd: - AppendUcodeDisasm(" // PredicateClean=false"); + i.clean = false; break; } - AppendUcodeDisasm('\n'); + i.is_yield = cf.is_yield(); + i.sequence = cf.sequence(); - ProcessControlFlowCondExec(cf); - - TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); + TranslateExecInstructions(i); } void ShaderTranslator::TranslateControlFlowCondExecPred( const ControlFlowCondExecPredInstruction& cf) { - AppendUcodeDisasm(cf.condition() ? " (p0) " : "(!p0) "); - AppendUcodeDisasm( - cf.opcode() == ControlFlowOpcode::kCondExecPredEnd ? "exece" : "exec"); - if (cf.is_yield()) { - AppendUcodeDisasm(" Yield=true"); - } - if (!cf.clean()) { - AppendUcodeDisasm(" // PredicateClean=false"); - } - AppendUcodeDisasm('\n'); + ParsedExecInstruction i; + i.dword_index = 0; + i.opcode = cf.opcode(); + i.opcode_name = + cf.opcode() == ControlFlowOpcode::kCondExecPredEnd ? "exece" : "exec"; + i.instruction_address = cf.address(); + i.instruction_count = cf.count(); + i.type = ParsedExecInstruction::Type::kPredicated; + i.condition = cf.condition(); + i.clean = cf.clean(); + i.is_yield = cf.is_yield(); + i.sequence = cf.sequence(); - ProcessControlFlowCondExecPred(cf); - - TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); + TranslateExecInstructions(i); } void ShaderTranslator::TranslateControlFlowLoopStart( const ControlFlowLoopStartInstruction& cf) { - AppendUcodeDisasm(" loop "); - AppendUcodeDisasmFormat("i%u, L%u", cf.loop_id(), cf.address()); - if (cf.is_repeat()) { - AppendUcodeDisasm(", Repeat=true"); - } - AppendUcodeDisasm('\n'); + ParsedLoopStartInstruction i; + i.dword_index = 0; + i.loop_constant_index = cf.loop_id(); + i.is_repeat = cf.is_repeat(); + i.loop_skip_address = cf.address(); - ProcessControlFlowLoopStart(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessLoopStartInstruction(i); } void ShaderTranslator::TranslateControlFlowLoopEnd( const ControlFlowLoopEndInstruction& cf) { - AppendUcodeDisasm(cf.condition() ? " (p0) " : "(!p0) "); - AppendUcodeDisasmFormat("endloop i%u, L%u", cf.loop_id(), cf.address()); - AppendUcodeDisasm('\n'); + ParsedLoopEndInstruction i; + i.dword_index = 0; + i.is_predicated_break = cf.is_predicated_break(); + i.predicate_condition = cf.condition(); + i.loop_constant_index = cf.loop_id(); + i.loop_body_address = cf.address(); - ProcessControlFlowLoopEnd(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessLoopEndInstruction(i); } void ShaderTranslator::TranslateControlFlowCondCall( const ControlFlowCondCallInstruction& cf) { + ParsedCallInstruction i; + i.dword_index = 0; + i.target_address = cf.address(); if (cf.is_unconditional()) { - AppendUcodeDisasm(" call "); + i.type = ParsedCallInstruction::Type::kUnconditional; + } else if (cf.is_predicated()) { + i.type = ParsedCallInstruction::Type::kPredicated; + i.condition = cf.condition(); } else { - if (cf.is_predicated()) { - AppendUcodeDisasm(cf.condition() ? " (p0) " : "(!p0) "); - AppendUcodeDisasm("call "); - } else { - AppendUcodeDisasm(" ccall "); - if (!cf.condition()) { - AppendUcodeDisasm('!'); - } - AppendUcodeDisasmFormat("b%u, ", cf.bool_address()); - } + i.type = ParsedCallInstruction::Type::kConditional; + i.bool_constant_index = cf.bool_address(); + i.condition = cf.condition(); } - AppendUcodeDisasmFormat("L%u", cf.address()); - AppendUcodeDisasm('\n'); - ProcessControlFlowCondCall(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessCallInstruction(i); } void ShaderTranslator::TranslateControlFlowReturn( const ControlFlowReturnInstruction& cf) { - AppendUcodeDisasm(" ret\n"); + ParsedReturnInstruction i; + i.dword_index = 0; - ProcessControlFlowReturn(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessReturnInstruction(i); } void ShaderTranslator::TranslateControlFlowCondJmp( const ControlFlowCondJmpInstruction& cf) { + ParsedJumpInstruction i; + i.dword_index = 0; + i.target_address = cf.address(); if (cf.is_unconditional()) { - AppendUcodeDisasm(" jmp "); + i.type = ParsedJumpInstruction::Type::kUnconditional; + } else if (cf.is_predicated()) { + i.type = ParsedJumpInstruction::Type::kPredicated; + i.condition = cf.condition(); } else { - if (cf.is_predicated()) { - AppendUcodeDisasm(cf.condition() ? " (p0) " : "(!p0) "); - AppendUcodeDisasm("jmp "); - } else { - AppendUcodeDisasm(" cjmp "); - if (!cf.condition()) { - AppendUcodeDisasm('!'); - } - AppendUcodeDisasmFormat("b%u, ", cf.bool_address()); - } + i.type = ParsedJumpInstruction::Type::kConditional; + i.bool_constant_index = cf.bool_address(); + i.condition = cf.condition(); } - AppendUcodeDisasmFormat("L%u", cf.address()); - AppendUcodeDisasm('\n'); - ProcessControlFlowCondJmp(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessJumpInstruction(i); } void ShaderTranslator::TranslateControlFlowAlloc( const ControlFlowAllocInstruction& cf) { - AppendUcodeDisasm(" alloc "); - switch (cf.alloc_type()) { - case AllocType::kNoAlloc: - break; - case AllocType::kVsPosition: - AppendUcodeDisasmFormat("position"); - break; - case AllocType::kVsInterpolators: // or AllocType::kPsColors - if (shader_type_ == ShaderType::kVertex) { - AppendUcodeDisasm("interpolators"); - } else { - AppendUcodeDisasm("colors"); - } - break; - case AllocType::kMemory: - AppendUcodeDisasmFormat("export = %d", cf.size()); - break; - } - AppendUcodeDisasm('\n'); + ParsedAllocInstruction i; + i.dword_index = 0; + i.type = cf.alloc_type(); + i.count = cf.size(); + i.is_vertex_shader = is_vertex_shader(); - ProcessControlFlowAlloc(cf); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessAllocInstruction(i); } -void ShaderTranslator::TranslateExecInstructions(uint32_t address, - uint32_t count, - uint32_t sequence) { - for (uint32_t instr_offset = address; instr_offset < address + count; +void ShaderTranslator::TranslateExecInstructions( + const ParsedExecInstruction& instr) { + instr.Disassemble(&ucode_disasm_buffer_); + + ProcessExecInstructionBegin(instr); + + uint32_t sequence = instr.sequence; + for (uint32_t instr_offset = instr.instruction_address; + instr_offset < instr.instruction_address + instr.instruction_count; ++instr_offset, sequence >>= 2) { MarkUcodeInstruction(instr_offset); AppendUcodeDisasmFormat("/* %4u */ ", instr_offset); @@ -517,517 +519,337 @@ void ShaderTranslator::TranslateExecInstructions(uint32_t address, auto fetch_opcode = static_cast(ucode_dwords_[instr_offset * 3] & 0x1F); if (fetch_opcode == FetchOpcode::kVertexFetch) { - TranslateVertexFetchInstruction( - *reinterpret_cast(ucode_dwords_ + - instr_offset * 3)); + auto& op = *reinterpret_cast( + ucode_dwords_ + instr_offset * 3); + TranslateVertexFetchInstruction(op); } else { - TranslateTextureFetchInstruction( - *reinterpret_cast( - ucode_dwords_ + instr_offset * 3)); + auto& op = *reinterpret_cast( + ucode_dwords_ + instr_offset * 3); + TranslateTextureFetchInstruction(op); } } else { - TranslateAluInstruction(*reinterpret_cast( - ucode_dwords_ + instr_offset * 3)); + auto& op = *reinterpret_cast(ucode_dwords_ + + instr_offset * 3); + TranslateAluInstruction(op); } } + + ProcessExecInstructionEnd(instr); } -const char kXyzwChannelNames[] = {'x', 'y', 'z', 'w'}; -const char kFetchChannelNames[] = {'x', 'y', 'z', 'w', '0', '1', '?', '_'}; -const char* kTemporaryNames[32] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", - "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", - "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", -}; -const char* kInterpolantNames[16] = { - "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", - "o8", "o9", "o10", "o11", "o12", "o13", "o14", "o15", -}; -const char* kDimensionNames[] = { - "1D", "2D", "3D", "Cube", -}; -const char* kTextureFilterNames[] = { - "point", "linear", "BASEMAP", "keep", -}; -const char* kAnisoFilterNames[] = { - "disabled", "max1to1", "max2to1", "max4to1", "max8to1", "max16to1", "keep", -}; - -int GetComponentCount(TextureDimension dimension) { - switch (dimension) { - case TextureDimension::k1D: - return 1; - case TextureDimension::k2D: - return 2; - case TextureDimension::k3D: - case TextureDimension::kCube: - return 3; - default: - assert_unhandled_case(dimension); - return 1; - } -} - -void ShaderTranslator::DisasmFetchDestReg(uint32_t dest, uint32_t swizzle, - bool is_relative) { - if (is_relative) { - AppendUcodeDisasmFormat("r[%u+aL]", dest); - } else { - AppendUcodeDisasmFormat("r%u", dest); - } - if (swizzle != 0x688) { - AppendUcodeDisasm('.'); - for (int i = 0; i < 4; ++i) { - if ((swizzle & 0x7) == 4) { - AppendUcodeDisasm('0'); - } else if ((swizzle & 0x7) == 5) { - AppendUcodeDisasm('1'); - } else if ((swizzle & 0x7) == 6) { - AppendUcodeDisasm('?'); - } else if ((swizzle & 0x7) == 7) { - AppendUcodeDisasm('_'); - } else { - AppendUcodeDisasm(kXyzwChannelNames[swizzle & 0x3]); - } - swizzle >>= 3; +void ParseFetchInstructionResult(uint32_t dest, uint32_t swizzle, + bool is_relative, + InstructionResult* out_result) { + out_result->storage_target = InstructionStorageTarget::kRegister; + out_result->storage_index = dest; + out_result->is_export = false; + out_result->is_clamped = false; + out_result->storage_addressing_mode = + is_relative ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; + for (int i = 0; i < 4; ++i) { + out_result->write_mask[i] = true; + if ((swizzle & 0x7) == 4) { + out_result->components[i] = SwizzleSource::k0; + } else if ((swizzle & 0x7) == 5) { + out_result->components[i] = SwizzleSource::k1; + } else if ((swizzle & 0x7) == 6) { + out_result->components[i] = SwizzleSource::k0; + } else if ((swizzle & 0x7) == 7) { + out_result->write_mask[i] = false; + } else { + out_result->components[i] = GetSwizzleFromComponentIndex(swizzle & 0x3); } - } -} - -void ShaderTranslator::DisasmFetchSourceReg(uint32_t src, uint32_t swizzle, - bool is_relative, - int component_count) { - if (is_relative) { - AppendUcodeDisasmFormat("r[%u+aL]", src); - } else { - AppendUcodeDisasmFormat("r%u", src); - } - if (swizzle != 0xFF) { - AppendUcodeDisasm('.'); - for (int i = 0; i < component_count; ++i) { - AppendUcodeDisasm(kXyzwChannelNames[swizzle & 0x3]); - swizzle >>= 2; - } - } -} - -static const struct { - const char* name; -} kVertexFetchDataFormats[0xff] = { -#define TYPE(id) \ - { #id } - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - TYPE(FMT_8_8_8_8), // 6 - TYPE(FMT_2_10_10_10), // 7 - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - TYPE(FMT_10_11_11), // 16 - TYPE(FMT_11_11_10), // 17 - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - TYPE(FMT_16_16), // 25 - TYPE(FMT_16_16_16_16), // 26 - {0}, - {0}, - {0}, - {0}, - TYPE(FMT_16_16_FLOAT), // 31 - TYPE(FMT_16_16_16_16_FLOAT), // 32 - TYPE(FMT_32), // 33 - TYPE(FMT_32_32), // 34 - TYPE(FMT_32_32_32_32), // 35 - TYPE(FMT_32_FLOAT), // 36 - TYPE(FMT_32_32_FLOAT), // 37 - TYPE(FMT_32_32_32_32_FLOAT), // 38 - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - TYPE(FMT_32_32_32_FLOAT), // 57 -#undef TYPE -}; - -void ShaderTranslator::DisasmVertexFetchAttributes( - const VertexFetchInstruction& op) { - if (op.is_index_rounded()) { - AppendUcodeDisasm(", RoundIndex=true"); - } - if (op.exp_adjust()) { - AppendUcodeDisasmFormat(", ExpAdjust=%d", op.exp_adjust()); - } - if (op.offset()) { - AppendUcodeDisasmFormat(", Offset=%u", op.offset()); - } - if (op.data_format() != VertexFormat::kUndefined) { - AppendUcodeDisasmFormat( - ", DataFormat=%s", - kVertexFetchDataFormats[static_cast(op.data_format())]); - } - if (op.stride()) { - AppendUcodeDisasmFormat(", Stride=%u", op.stride()); - } - if (op.is_signed()) { - AppendUcodeDisasm(", Signed=true"); - } - if (op.is_integer()) { - AppendUcodeDisasm(", NumFormat=integer"); - } - if (op.prefetch_count() != 0) { - AppendUcodeDisasmFormat(", PrefetchCount=%u", op.prefetch_count() + 1); + swizzle >>= 3; } } void ShaderTranslator::TranslateVertexFetchInstruction( const VertexFetchInstruction& op) { - AppendUcodeDisasm(" "); - if (op.is_predicated()) { - AppendUcodeDisasm(op.predicate_condition() ? " (p0) " : "(!p0) "); - } else { - AppendUcodeDisasm(" "); - } - AppendUcodeDisasmFormat(op.is_mini_fetch() ? "vfetch_mini" : "vfetch_full"); - AppendUcodeDisasm(' '); - DisasmFetchDestReg(op.dest(), op.dest_swizzle(), op.is_dest_relative()); - if (!op.is_mini_fetch()) { - AppendUcodeDisasm(", "); - DisasmFetchSourceReg(op.src(), op.src_swizzle(), op.is_src_relative(), 1); - AppendUcodeDisasm(", "); - AppendUcodeDisasmFormat("vf%u", 95 - op.fetch_constant_index()); - } - DisasmVertexFetchAttributes(op); - AppendUcodeDisasm('\n'); + ParsedVertexFetchInstruction instr; + ParseVertexFetchInstruction(op, &instr); + instr.Disassemble(&ucode_disasm_buffer_); + ProcessVertexFetchInstruction(instr); +} +void ShaderTranslator::ParseVertexFetchInstruction( + const VertexFetchInstruction& op, ParsedVertexFetchInstruction* out_instr) { + auto& i = *out_instr; + i.dword_index = 0; + i.opcode = FetchOpcode::kVertexFetch; + i.opcode_name = op.is_mini_fetch() ? "vfetch_mini" : "vfetch_full"; + i.is_mini_fetch = op.is_mini_fetch(); + i.is_predicated = op.is_predicated(); + i.predicate_condition = op.predicate_condition(); + + ParseFetchInstructionResult(op.dest(), op.dest_swizzle(), + op.is_dest_relative(), &i.result); + + // Reuse previous vfetch_full if this is a mini. + const auto& full_op = op.is_mini_fetch() ? previous_vfetch_full_ : op; + auto& src_op = i.operands[i.operand_count++]; + src_op.storage_source = InstructionStorageSource::kRegister; + src_op.storage_index = full_op.src(); + src_op.storage_addressing_mode = + full_op.is_src_relative() + ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; + src_op.is_negated = false; + src_op.is_absolute_value = false; + src_op.component_count = 1; + uint32_t swizzle = full_op.src_swizzle(); + for (int j = 0; j < src_op.component_count; ++j, swizzle >>= 2) { + src_op.components[j] = GetSwizzleFromComponentIndex(swizzle & 0x3); + } + + auto& const_op = i.operands[i.operand_count++]; + const_op.storage_source = InstructionStorageSource::kVertexFetchConstant; + const_op.storage_index = full_op.fetch_constant_index(); + + i.attributes.data_format = op.data_format(); + i.attributes.offset = op.offset(); + i.attributes.stride = op.stride(); + i.attributes.exp_adjust = op.exp_adjust(); + i.attributes.is_index_rounded = op.is_index_rounded(); + i.attributes.is_signed = op.is_signed(); + i.attributes.is_integer = op.is_integer(); + i.attributes.prefetch_count = op.prefetch_count(); + + // Store for later use by mini fetches. if (!op.is_mini_fetch()) { previous_vfetch_full_ = op; } - - ProcessVertexFetchInstruction(op); } void ShaderTranslator::TranslateTextureFetchInstruction( const TextureFetchInstruction& op) { + ParsedTextureFetchInstruction instr; + ParseTextureFetchInstruction(op, &instr); + instr.Disassemble(&ucode_disasm_buffer_); + ProcessTextureFetchInstruction(instr); +} + +void ShaderTranslator::ParseTextureFetchInstruction( + const TextureFetchInstruction& op, + ParsedTextureFetchInstruction* out_instr) { struct TextureFetchOpcodeInfo { - const char* base_name; - bool name_has_dimension; + const char* name; bool has_dest; - bool has_src; bool has_const; bool has_attributes; int override_component_count; } opcode_info; switch (op.opcode()) { - case FetchOpcode::kTextureFetch: - opcode_info = {"tfetch", true, true, true, true, true, 0}; - break; - case FetchOpcode::kGetTextureBorderColorFrac: - opcode_info = {"getBCF", true, true, true, true, true, 0}; - break; - case FetchOpcode::kGetTextureComputedLod: - opcode_info = {"getCompTexLOD", true, true, true, true, true, 0}; - break; + case FetchOpcode::kTextureFetch: { + static const char* kNames[] = {"tfetch1D", "tfetch2D", "tfetch3D", + "tfetchCube"}; + opcode_info = {kNames[static_cast(op.dimension())], true, true, true, + 0}; + } break; + case FetchOpcode::kGetTextureBorderColorFrac: { + static const char* kNames[] = {"getBCF1D", "getBCF2D", "getBCF3D", + "getBCFCube"}; + opcode_info = {kNames[static_cast(op.dimension())], true, true, true, + 0}; + } break; + case FetchOpcode::kGetTextureComputedLod: { + static const char* kNames[] = {"getCompTexLOD1D", "getCompTexLOD2D", + "getCompTexLOD3D", "getCompTexLODCube"}; + opcode_info = {kNames[static_cast(op.dimension())], true, true, true, + 0}; + } break; case FetchOpcode::kGetTextureGradients: - opcode_info = {"getGradients", false, true, true, true, true, 2}; - break; - case FetchOpcode::kGetTextureWeights: - opcode_info = {"getWeights", true, true, true, true, true, 0}; + opcode_info = {"getGradients", true, true, true, 2}; break; + case FetchOpcode::kGetTextureWeights: { + static const char* kNames[] = {"getWeights1D", "getWeights2D", + "getWeights3D", "getWeightsCube"}; + opcode_info = {kNames[static_cast(op.dimension())], true, true, true, + 0}; + } break; case FetchOpcode::kSetTextureLod: - opcode_info = {"setTexLOD", false, false, true, false, false, 1}; + opcode_info = {"setTexLOD", false, false, false, 1}; break; case FetchOpcode::kSetTextureGradientsHorz: - opcode_info = {"setGradientH", false, false, true, false, false, 3}; + opcode_info = {"setGradientH", false, false, false, 3}; break; case FetchOpcode::kSetTextureGradientsVert: - opcode_info = {"setGradientV", false, false, true, false, false, 3}; + opcode_info = {"setGradientV", false, false, false, 3}; break; default: case FetchOpcode::kUnknownTextureOp: assert_unhandled_case(fetch_opcode); return; } - AppendUcodeDisasm(" "); - if (op.is_predicated()) { - AppendUcodeDisasm(op.predicate_condition() ? " (p0) " : "(!p0) "); - } else { - AppendUcodeDisasm(" "); - } - AppendUcodeDisasm(opcode_info.base_name); - if (opcode_info.name_has_dimension) { - AppendUcodeDisasmFormat(kDimensionNames[static_cast(op.dimension())]); - } - AppendUcodeDisasm(' '); - bool needs_comma = false; + + auto& i = *out_instr; + i.dword_index = 0; + i.opcode = op.opcode(); + i.opcode_name = opcode_info.name; + i.dimension = op.dimension(); + i.is_predicated = op.is_predicated(); + i.predicate_condition = op.predicate_condition(); + if (opcode_info.has_dest) { - DisasmFetchDestReg(op.dest(), op.dest_swizzle(), op.is_dest_relative()); - needs_comma = true; + ParseFetchInstructionResult(op.dest(), op.dest_swizzle(), + op.is_dest_relative(), &i.result); + } else { + i.result.storage_target = InstructionStorageTarget::kNone; } - if (opcode_info.has_src) { - if (needs_comma) { - AppendUcodeDisasm(", "); - } - DisasmFetchSourceReg(op.src(), op.src_swizzle(), op.is_src_relative(), - opcode_info.override_component_count - ? opcode_info.override_component_count - : GetComponentCount(op.dimension())); + + auto& src_op = i.operands[i.operand_count++]; + src_op.storage_source = InstructionStorageSource::kRegister; + src_op.storage_index = op.src(); + src_op.storage_addressing_mode = + op.is_src_relative() ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; + src_op.is_negated = false; + src_op.is_absolute_value = false; + src_op.component_count = + opcode_info.override_component_count + ? opcode_info.override_component_count + : GetTextureDimensionComponentCount(op.dimension()); + uint32_t swizzle = op.src_swizzle(); + for (int j = 0; j < src_op.component_count; ++j, swizzle >>= 2) { + src_op.components[j] = GetSwizzleFromComponentIndex(swizzle & 0x3); } + if (opcode_info.has_const) { - if (needs_comma) { - AppendUcodeDisasm(", "); - } - AppendUcodeDisasmFormat("tf%u", op.fetch_constant_index()); + auto& const_op = i.operands[i.operand_count++]; + const_op.storage_source = InstructionStorageSource::kTextureFetchConstant; + const_op.storage_index = op.fetch_constant_index(); } + if (opcode_info.has_attributes) { - DisasmTextureFetchAttributes(op); - } - AppendUcodeDisasm('\n'); - - switch (op.opcode()) { - case FetchOpcode::kTextureFetch: - ProcessTextureFetchTextureFetch(op); - return; - case FetchOpcode::kGetTextureBorderColorFrac: - ProcessTextureFetchGetTextureBorderColorFrac(op); - return; - case FetchOpcode::kGetTextureComputedLod: - ProcessTextureFetchGetTextureComputedLod(op); - return; - case FetchOpcode::kGetTextureGradients: - ProcessTextureFetchGetTextureGradients(op); - return; - case FetchOpcode::kGetTextureWeights: - ProcessTextureFetchGetTextureWeights(op); - return; - case FetchOpcode::kSetTextureLod: - ProcessTextureFetchSetTextureLod(op); - return; - case FetchOpcode::kSetTextureGradientsHorz: - ProcessTextureFetchSetTextureGradientsHorz(op); - return; - case FetchOpcode::kSetTextureGradientsVert: - ProcessTextureFetchSetTextureGradientsVert(op); - return; - default: - case FetchOpcode::kUnknownTextureOp: - assert_unhandled_case(fetch_opcode); - return; - } -} - -void ShaderTranslator::DisasmTextureFetchAttributes( - const TextureFetchInstruction& op) { - if (!op.fetch_valid_only()) { - AppendUcodeDisasm(", FetchValidOnly=false"); - } - if (op.unnormalized_coordinates()) { - AppendUcodeDisasm(", UnnormalizedTextureCoords=true"); - } - if (op.has_mag_filter()) { - AppendUcodeDisasmFormat( - ", MagFilter=%s", - kTextureFilterNames[static_cast(op.mag_filter())]); - } - if (op.has_min_filter()) { - AppendUcodeDisasmFormat( - ", MinFilter=%s", - kTextureFilterNames[static_cast(op.min_filter())]); - } - if (op.has_mip_filter()) { - AppendUcodeDisasmFormat( - ", MipFilter=%s", - kTextureFilterNames[static_cast(op.mip_filter())]); - } - if (op.has_aniso_filter()) { - AppendUcodeDisasmFormat( - ", AnisoFilter=%s", - kAnisoFilterNames[static_cast(op.aniso_filter())]); - } - if (!op.use_computed_lod()) { - AppendUcodeDisasm(", UseComputedLOD=false"); - } - if (op.use_register_lod()) { - AppendUcodeDisasm(", UseRegisterLOD=true"); - } - if (op.use_register_gradients()) { - AppendUcodeDisasm(", UseRegisterGradients=true"); - } - int component_count = GetComponentCount(op.dimension()); - if (op.offset_x() != 0.0f) { - AppendUcodeDisasmFormat(", OffsetX=%g", op.offset_x()); - } - if (component_count > 1 && op.offset_y() != 0.0f) { - AppendUcodeDisasmFormat(", OffsetY=%g", op.offset_y()); - } - if (component_count > 2 && op.offset_z() != 0.0f) { - AppendUcodeDisasmFormat(", OffsetZ=%g", op.offset_z()); + i.attributes.fetch_valid_only = op.fetch_valid_only(); + i.attributes.unnormalized_coordinates = op.unnormalized_coordinates(); + i.attributes.mag_filter = op.mag_filter(); + i.attributes.min_filter = op.min_filter(); + i.attributes.mip_filter = op.mip_filter(); + i.attributes.aniso_filter = op.aniso_filter(); + i.attributes.use_computed_lod = op.use_computed_lod(); + i.attributes.use_register_lod = op.use_register_lod(); + i.attributes.use_register_gradients = op.use_register_gradients(); + i.attributes.offset_x = op.offset_x(); + i.attributes.offset_y = op.offset_y(); + i.attributes.offset_z = op.offset_z(); } } const ShaderTranslator::AluOpcodeInfo ShaderTranslator::alu_vector_opcode_infos_[0x20] = { - {"add", 2, 4, &ShaderTranslator::ProcessAluVectorAdd}, // 0 - {"mul", 2, 4, &ShaderTranslator::ProcessAluVectorMul}, // 1 - {"max", 2, 4, &ShaderTranslator::ProcessAluVectorMax}, // 2 - {"min", 2, 4, &ShaderTranslator::ProcessAluVectorMin}, // 3 - {"seq", 2, 4, &ShaderTranslator::ProcessAluVectorSetEQ}, // 4 - {"sgt", 2, 4, &ShaderTranslator::ProcessAluVectorSetGT}, // 5 - {"sge", 2, 4, &ShaderTranslator::ProcessAluVectorSetGE}, // 6 - {"sne", 2, 4, &ShaderTranslator::ProcessAluVectorSetNE}, // 7 - {"frc", 1, 4, &ShaderTranslator::ProcessAluVectorFrac}, // 8 - {"trunc", 1, 4, &ShaderTranslator::ProcessAluVectorTrunc}, // 9 - {"floor", 1, 4, &ShaderTranslator::ProcessAluVectorFloor}, // 10 - {"mad", 3, 4, &ShaderTranslator::ProcessAluVectorMad}, // 11 - {"cndeq", 3, 4, &ShaderTranslator::ProcessAluVectorCndEQ}, // 12 - {"cndge", 3, 4, &ShaderTranslator::ProcessAluVectorCndGE}, // 13 - {"cndgt", 3, 4, &ShaderTranslator::ProcessAluVectorCndGT}, // 14 - {"dp4", 2, 4, &ShaderTranslator::ProcessAluVectorDp4}, // 15 - {"dp3", 2, 4, &ShaderTranslator::ProcessAluVectorDp3}, // 16 - {"dp2add", 3, 4, &ShaderTranslator::ProcessAluVectorDp2Add}, // 17 - {"cube", 2, 4, &ShaderTranslator::ProcessAluVectorCube}, // 18 - {"max4", 1, 4, &ShaderTranslator::ProcessAluVectorMax4}, // 19 - {"setp_eq_push", 2, 4, - &ShaderTranslator::ProcessAluVectorPredSetEQPush}, // 20 - {"setp_ne_push", 2, 4, - &ShaderTranslator::ProcessAluVectorPredSetNEPush}, // 21 - {"setp_gt_push", 2, 4, - &ShaderTranslator::ProcessAluVectorPredSetGTPush}, // 22 - {"setp_ge_push", 2, 4, - &ShaderTranslator::ProcessAluVectorPredSetGEPush}, // 23 - {"kill_eq", 2, 4, &ShaderTranslator::ProcessAluVectorKillEQ}, // 24 - {"kill_gt", 2, 4, &ShaderTranslator::ProcessAluVectorKillGT}, // 25 - {"kill_ge", 2, 4, &ShaderTranslator::ProcessAluVectorKillLGE}, // 26 - {"kill_ne", 2, 4, &ShaderTranslator::ProcessAluVectorKillNE}, // 27 - {"dst", 2, 4, &ShaderTranslator::ProcessAluVectorDst}, // 28 - {"maxa", 2, 4, &ShaderTranslator::ProcessAluVectorMaxA}, // 29 + {"add", 2, 4}, // 0 + {"mul", 2, 4}, // 1 + {"max", 2, 4}, // 2 + {"min", 2, 4}, // 3 + {"seq", 2, 4}, // 4 + {"sgt", 2, 4}, // 5 + {"sge", 2, 4}, // 6 + {"sne", 2, 4}, // 7 + {"frc", 1, 4}, // 8 + {"trunc", 1, 4}, // 9 + {"floor", 1, 4}, // 10 + {"mad", 3, 4}, // 11 + {"cndeq", 3, 4}, // 12 + {"cndge", 3, 4}, // 13 + {"cndgt", 3, 4}, // 14 + {"dp4", 2, 4}, // 15 + {"dp3", 2, 4}, // 16 + {"dp2add", 3, 4}, // 17 + {"cube", 2, 4}, // 18 + {"max4", 1, 4}, // 19 + {"setp_eq_push", 2, 4}, // 20 + {"setp_ne_push", 2, 4}, // 21 + {"setp_gt_push", 2, 4}, // 22 + {"setp_ge_push", 2, 4}, // 23 + {"kill_eq", 2, 4}, // 24 + {"kill_gt", 2, 4}, // 25 + {"kill_ge", 2, 4}, // 26 + {"kill_ne", 2, 4}, // 27 + {"dst", 2, 4}, // 28 + {"maxa", 2, 4}, // 29 }; const ShaderTranslator::AluOpcodeInfo ShaderTranslator::alu_scalar_opcode_infos_[0x40] = { - {"adds", 1, 2, &ShaderTranslator::ProcessAluScalarAdd}, // 0 - {"adds_prev", 1, 1, &ShaderTranslator::ProcessAluScalarAddPrev}, // 1 - {"muls", 1, 2, &ShaderTranslator::ProcessAluScalarMul}, // 2 - {"muls_prev", 1, 1, &ShaderTranslator::ProcessAluScalarMulPrev}, // 3 - {"muls_prev2", 1, 2, &ShaderTranslator::ProcessAluScalarMulPrev2}, // 4 - {"maxs", 1, 2, &ShaderTranslator::ProcessAluScalarMax}, // 5 - {"mins", 1, 2, &ShaderTranslator::ProcessAluScalarMin}, // 6 - {"seqs", 1, 1, &ShaderTranslator::ProcessAluScalarSetEQ}, // 7 - {"sgts", 1, 1, &ShaderTranslator::ProcessAluScalarSetGT}, // 8 - {"sges", 1, 1, &ShaderTranslator::ProcessAluScalarSetGE}, // 9 - {"snes", 1, 1, &ShaderTranslator::ProcessAluScalarSetNE}, // 10 - {"frcs", 1, 1, &ShaderTranslator::ProcessAluScalarFrac}, // 11 - {"truncs", 1, 1, &ShaderTranslator::ProcessAluScalarTrunc}, // 12 - {"floors", 1, 1, &ShaderTranslator::ProcessAluScalarFloor}, // 13 - {"exp", 1, 1, &ShaderTranslator::ProcessAluScalarExp}, // 14 - {"logc", 1, 1, &ShaderTranslator::ProcessAluScalarLogClamp}, // 15 - {"log", 1, 1, &ShaderTranslator::ProcessAluScalarLog}, // 16 - {"rcpc", 1, 1, &ShaderTranslator::ProcessAluScalarRecipClamp}, // 17 - {"rcpf", 1, 1, - &ShaderTranslator::ProcessAluScalarRecipFixedFunc}, // 18 - {"rcp", 1, 1, &ShaderTranslator::ProcessAluScalarRecip}, // 19 - {"rsqc", 1, 1, &ShaderTranslator::ProcessAluScalarRSqrtClamp}, // 20 - {"rsqf", 1, 1, - &ShaderTranslator::ProcessAluScalarRSqrtFixedFunc}, // 21 - {"rsq", 1, 1, &ShaderTranslator::ProcessAluScalarRSqrt}, // 22 - {"movas", 1, 1, &ShaderTranslator::ProcessAluScalarMovA}, // 23 - {"movasf", 1, 1, &ShaderTranslator::ProcessAluScalarMovAFloor}, // 24 - {"subs", 1, 2, &ShaderTranslator::ProcessAluScalarSub}, // 25 - {"subs_prev", 1, 1, &ShaderTranslator::ProcessAluScalarSubPrev}, // 26 - {"setp_eq", 1, 1, &ShaderTranslator::ProcessAluScalarPredSetEQ}, // 27 - {"setp_ne", 1, 1, &ShaderTranslator::ProcessAluScalarPredSetNE}, // 28 - {"setp_gt", 1, 1, &ShaderTranslator::ProcessAluScalarPredSetGT}, // 29 - {"setp_ge", 1, 1, &ShaderTranslator::ProcessAluScalarPredSetGE}, // 30 - {"setp_inv", 1, 1, - &ShaderTranslator::ProcessAluScalarPredSetInv}, // 31 - {"setp_pop", 1, 1, - &ShaderTranslator::ProcessAluScalarPredSetPop}, // 32 - {"setp_clr", 1, 1, - &ShaderTranslator::ProcessAluScalarPredSetClear}, // 33 - {"setp_rstr", 1, 1, - &ShaderTranslator::ProcessAluScalarPredSetRestore}, // 34 - {"kills_eq", 1, 1, &ShaderTranslator::ProcessAluScalarKillEQ}, // 35 - {"kills_gt", 1, 1, &ShaderTranslator::ProcessAluScalarKillGT}, // 36 - {"kills_ge", 1, 1, &ShaderTranslator::ProcessAluScalarKillGE}, // 37 - {"kills_ne", 1, 1, &ShaderTranslator::ProcessAluScalarKillNE}, // 38 - {"kills_one", 1, 1, &ShaderTranslator::ProcessAluScalarKillOne}, // 39 - {"sqrt", 1, 1, &ShaderTranslator::ProcessAluScalarSqrt}, // 40 - {"UNKNOWN", 0, 0, nullptr}, // 41 - {"mulsc", 2, 1, &ShaderTranslator::ProcessAluScalarMulConst0}, // 42 - {"mulsc", 2, 1, &ShaderTranslator::ProcessAluScalarMulConst1}, // 43 - {"addsc", 2, 1, &ShaderTranslator::ProcessAluScalarAddConst0}, // 44 - {"addsc", 2, 1, &ShaderTranslator::ProcessAluScalarAddConst1}, // 45 - {"subsc", 2, 1, &ShaderTranslator::ProcessAluScalarSubConst0}, // 46 - {"subsc", 2, 1, &ShaderTranslator::ProcessAluScalarSubConst1}, // 47 - {"sin", 1, 1, &ShaderTranslator::ProcessAluScalarSin}, // 48 - {"cos", 1, 1, &ShaderTranslator::ProcessAluScalarCos}, // 49 - {"retain_prev", 1, 1, - &ShaderTranslator::ProcessAluScalarRetainPrev}, // 50 + {"adds", 1, 2}, // 0 + {"adds_prev", 1, 1}, // 1 + {"muls", 1, 2}, // 2 + {"muls_prev", 1, 1}, // 3 + {"muls_prev2", 1, 2}, // 4 + {"maxs", 1, 2}, // 5 + {"mins", 1, 2}, // 6 + {"seqs", 1, 1}, // 7 + {"sgts", 1, 1}, // 8 + {"sges", 1, 1}, // 9 + {"snes", 1, 1}, // 10 + {"frcs", 1, 1}, // 11 + {"truncs", 1, 1}, // 12 + {"floors", 1, 1}, // 13 + {"exp", 1, 1}, // 14 + {"logc", 1, 1}, // 15 + {"log", 1, 1}, // 16 + {"rcpc", 1, 1}, // 17 + {"rcpf", 1, 1}, // 18 + {"rcp", 1, 1}, // 19 + {"rsqc", 1, 1}, // 20 + {"rsqf", 1, 1}, // 21 + {"rsq", 1, 1}, // 22 + {"movas", 1, 1}, // 23 + {"movasf", 1, 1}, // 24 + {"subs", 1, 2}, // 25 + {"subs_prev", 1, 1}, // 26 + {"setp_eq", 1, 1}, // 27 + {"setp_ne", 1, 1}, // 28 + {"setp_gt", 1, 1}, // 29 + {"setp_ge", 1, 1}, // 30 + {"setp_inv", 1, 1}, // 31 + {"setp_pop", 1, 1}, // 32 + {"setp_clr", 1, 1}, // 33 + {"setp_rstr", 1, 1}, // 34 + {"kills_eq", 1, 1}, // 35 + {"kills_gt", 1, 1}, // 36 + {"kills_ge", 1, 1}, // 37 + {"kills_ne", 1, 1}, // 38 + {"kills_one", 1, 1}, // 39 + {"sqrt", 1, 1}, // 40 + {"UNKNOWN", 0, 0}, // 41 + {"mulsc", 2, 1}, // 42 + {"mulsc", 2, 1}, // 43 + {"addsc", 2, 1}, // 44 + {"addsc", 2, 1}, // 45 + {"subsc", 2, 1}, // 46 + {"subsc", 2, 1}, // 47 + {"sin", 1, 1}, // 48 + {"cos", 1, 1}, // 49 + {"retain_prev", 1, 1}, // 50 }; -const char* GetDestRegName(ShaderType shader_type, uint32_t dest_num, - bool is_export) { - if (!is_export) { - assert_true(dest_num < xe::countof(kTemporaryNames)); - return kTemporaryNames[dest_num]; +void ShaderTranslator::TranslateAluInstruction(const AluInstruction& op) { + if (!op.has_vector_op() && !op.has_scalar_op()) { + ParsedAluInstruction instr; + instr.type = ParsedAluInstruction::Type::kNop; + instr.Disassemble(&ucode_disasm_buffer_); + ProcessAluInstruction(instr); + return; } - if (shader_type == ShaderType::kVertex) { - switch (dest_num) { - case 62: - return "oPos"; - case 63: - return "oPts"; - default: - assert_true(dest_num < xe::countof(kInterpolantNames)); - return kInterpolantNames[dest_num]; - } - } else { - switch (dest_num) { - case 0: - case 63: // ? masked? - return "oC0"; - case 1: - return "oC1"; - case 2: - return "oC2"; - case 3: - return "oC3"; - case 61: - return "oDepth"; - } + + if (op.has_vector_op()) { + const auto& opcode_info = + alu_vector_opcode_infos_[static_cast(op.vector_opcode())]; + ParseAluVectorInstruction(op, opcode_info); + } + + if (op.has_scalar_op()) { + const auto& opcode_info = + alu_scalar_opcode_infos_[static_cast(op.scalar_opcode())]; + ParseAluScalarInstruction(op, opcode_info); } - return "???"; } -void ShaderTranslator::DisasmAluSourceReg(const AluInstruction& op, int i, - int swizzle_component_count) { +void ParseAluInstructionOperand(const AluInstruction& op, int i, + int swizzle_component_count, + InstructionOperand* out_op) { int const_slot = 0; switch (i) { case 2: @@ -1037,177 +859,198 @@ void ShaderTranslator::DisasmAluSourceReg(const AluInstruction& op, int i, const_slot = op.src_is_temp(1) && op.src_is_temp(2) ? 0 : 1; break; } - if (op.src_negate(i)) { - AppendUcodeDisasm('-'); - } + out_op->is_negated = op.src_negate(i); uint32_t reg = op.src_reg(i); if (op.src_is_temp(i)) { - AppendUcodeDisasm(kTemporaryNames[reg & 0x7F]); - if (reg & 0x80) { - AppendUcodeDisasm("_abs"); - } + out_op->storage_source = InstructionStorageSource::kRegister; + out_op->storage_index = reg & 0x1F; + out_op->is_absolute_value = (reg & 0x80) == 0x80; + out_op->storage_addressing_mode = + (reg & 0x40) ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; } else { + out_op->storage_source = InstructionStorageSource::kConstantFloat; + out_op->storage_index = reg; if ((const_slot == 0 && op.is_const_0_addressed()) || (const_slot == 1 && op.is_const_1_addressed())) { if (op.is_address_relative()) { - AppendUcodeDisasmFormat("c[%u+a0]", reg); + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kAddressAbsolute; } else { - AppendUcodeDisasmFormat("c[%u+aL]", reg); + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kAddressRelative; } } else { - AppendUcodeDisasmFormat("c%u", reg); - } - if (op.abs_constants()) { - AppendUcodeDisasm("_abs"); + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kStatic; } + out_op->is_absolute_value = op.abs_constants(); } + out_op->component_count = swizzle_component_count; uint32_t swizzle = op.src_swizzle(i); - if (swizzle) { - AppendUcodeDisasm('.'); - if (swizzle_component_count == 1) { - uint32_t a = swizzle & 0x3; - AppendUcodeDisasm(kXyzwChannelNames[a]); - } else if (swizzle_component_count == 2) { - swizzle >>= 4; - uint32_t a = swizzle & 0x3; - uint32_t b = ((swizzle >> 2) + 1) & 0x3; - ; - AppendUcodeDisasm(kXyzwChannelNames[a]); - AppendUcodeDisasm(kXyzwChannelNames[b]); - } else { - for (int i = 0; i < swizzle_component_count; ++i, swizzle >>= 2) { - AppendUcodeDisasm(kXyzwChannelNames[(swizzle + i) & 0x3]); + if (swizzle_component_count == 1) { + uint32_t a = swizzle & 0x3; + out_op->components[0] = GetSwizzleFromComponentIndex(a); + } else if (swizzle_component_count == 2) { + swizzle >>= 4; + uint32_t a = swizzle & 0x3; + uint32_t b = ((swizzle >> 2) + 1) & 0x3; + out_op->components[0] = GetSwizzleFromComponentIndex(a); + out_op->components[1] = GetSwizzleFromComponentIndex(b); + } else { + for (int j = 0; j < swizzle_component_count; ++j, swizzle >>= 2) { + out_op->components[j] = GetSwizzleFromComponentIndex((swizzle + j) & 0x3); + } + } +} + +void ParseAluInstructionOperandSpecial(const AluInstruction& op, + InstructionStorageSource storage_source, + uint32_t reg, bool negate, + int const_slot, uint32_t swizzle, + InstructionOperand* out_op) { + out_op->is_negated = negate; + out_op->is_absolute_value = op.abs_constants(); + out_op->storage_source = storage_source; + if (storage_source == InstructionStorageSource::kRegister) { + out_op->storage_index = reg & 0x7F; + } else { + out_op->storage_index = reg; + if ((const_slot == 0 && op.is_const_0_addressed()) || + (const_slot == 1 && op.is_const_1_addressed())) { + if (op.is_address_relative()) { + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kAddressAbsolute; + } else { + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kAddressRelative; } + } else { + out_op->storage_addressing_mode = + InstructionStorageAddressingMode::kStatic; } } + out_op->component_count = 1; + uint32_t a = swizzle & 0x3; + out_op->components[0] = GetSwizzleFromComponentIndex(a); } -void ShaderTranslator::DisasmAluSourceRegScalarSpecial( - const AluInstruction& op, uint32_t reg, bool is_temp, bool negate, - int const_slot, uint32_t swizzle) { - if (negate) { - AppendUcodeDisasm('-'); - } - if (is_temp) { - AppendUcodeDisasm(kTemporaryNames[reg & 0x7F]); - if (reg & 0x80) { - AppendUcodeDisasm("_abs"); - } - } else { - AppendUcodeDisasmFormat("c%u", reg); - if (op.abs_constants()) { - AppendUcodeDisasm("_abs"); - } - } - AppendUcodeDisasm('.'); - AppendUcodeDisasm(kXyzwChannelNames[swizzle]); -} - -void ShaderTranslator::TranslateAluInstruction(const AluInstruction& op) { - if (!op.has_vector_op() && !op.has_scalar_op()) { - AppendUcodeDisasm(" nop\n"); - ProcessAluNop(op); - return; - } - - if (op.has_vector_op()) { - const auto& opcode_info = - alu_vector_opcode_infos_[static_cast(op.vector_opcode())]; - DisasmAluVectorInstruction(op, opcode_info); - (this->*(opcode_info.fn))(op); - } - - if (op.has_scalar_op()) { - const auto& opcode_info = - alu_scalar_opcode_infos_[static_cast(op.scalar_opcode())]; - DisasmAluScalarInstruction(op, opcode_info); - (this->*(opcode_info.fn))(op); - } -} - -void ShaderTranslator::DisasmAluVectorInstruction( +void ShaderTranslator::ParseAluVectorInstruction( const AluInstruction& op, const AluOpcodeInfo& opcode_info) { - AppendUcodeDisasm(" "); - if (op.is_predicated()) { - AppendUcodeDisasm(op.predicate_condition() ? " (p0) " : "(!p0) "); - } else { - AppendUcodeDisasm(" "); - } - if (!op.has_vector_op()) { - AppendUcodeDisasmFormat("nop\n"); - return; - } + ParsedAluInstruction i; + i.dword_index = 0; + i.type = ParsedAluInstruction::Type::kVector; + i.vector_opcode = op.vector_opcode(); + i.opcode_name = opcode_info.name; + i.is_paired = op.has_scalar_op(); + i.is_predicated = op.is_predicated(); + i.predicate_condition = op.predicate_condition(); - AppendUcodeDisasmFormat(opcode_info.name); - if (op.vector_clamp()) { - AppendUcodeDisasm("_sat"); + i.result.is_export = op.is_export(); + i.result.is_clamped = op.vector_clamp(); + i.result.storage_target = InstructionStorageTarget::kRegister; + i.result.storage_index = 0; + uint32_t dest_num = op.vector_dest(); + if (!op.is_export()) { + assert_true(dest_num < 32); + i.result.storage_target = InstructionStorageTarget::kRegister; + i.result.storage_index = dest_num; + i.result.storage_addressing_mode = + op.is_vector_dest_relative() + ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; + } else if (is_vertex_shader()) { + switch (dest_num) { + case 62: + i.result.storage_target = InstructionStorageTarget::kPosition; + break; + case 63: + i.result.storage_target = InstructionStorageTarget::kPointSize; + break; + default: + assert_true(dest_num < 16); + i.result.storage_target = InstructionStorageTarget::kInterpolant; + i.result.storage_index = dest_num; + break; + } + } else if (is_pixel_shader()) { + switch (dest_num) { + case 0: + case 63: // ? masked? + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 0; + break; + case 1: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 1; + break; + case 2: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 2; + break; + case 3: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 3; + break; + case 61: + i.result.storage_target = InstructionStorageTarget::kDepth; + break; + } } - AppendUcodeDisasm(' '); - - AppendUcodeDisasm( - GetDestRegName(shader_type_, op.vector_dest(), op.is_export())); if (op.is_export()) { uint32_t write_mask = op.vector_write_mask(); uint32_t const_1_mask = op.scalar_write_mask(); - if (!(write_mask == 0xF && const_1_mask == 0)) { - AppendUcodeDisasm('.'); - for (int i = 0; i < 4; ++i, write_mask >>= 1, const_1_mask >>= 1) { + if (!write_mask) { + for (int j = 0; j < 4; ++j) { + i.result.write_mask[j] = false; + } + } else { + for (int j = 0; j < 4; ++j, write_mask >>= 1, const_1_mask >>= 1) { + i.result.write_mask[j] = true; if (write_mask & 0x1) { if (const_1_mask & 0x1) { - AppendUcodeDisasm('1'); + i.result.components[j] = SwizzleSource::k1; } else { - AppendUcodeDisasm(kXyzwChannelNames[i]); + i.result.components[j] = GetSwizzleFromComponentIndex(j); + } + } else { + if (op.is_scalar_dest_relative()) { + i.result.components[j] = SwizzleSource::k0; + } else { + i.result.write_mask[j] = false; } - } else if (op.is_scalar_dest_relative()) { - AppendUcodeDisasm('0'); } } } } else { - if (!op.vector_write_mask()) { - AppendUcodeDisasm("._"); - } else if (op.vector_write_mask() != 0xF) { - uint32_t write_mask = op.vector_write_mask(); - AppendUcodeDisasm('.'); - for (int i = 0; i < 4; ++i, write_mask >>= 1) { - if (write_mask & 0x1) { - AppendUcodeDisasm(kXyzwChannelNames[i]); - } - } + uint32_t write_mask = op.vector_write_mask(); + for (int j = 0; j < 4; ++j, write_mask >>= 1) { + i.result.write_mask[j] = (write_mask & 0x1) == 0x1; + i.result.components[j] = GetSwizzleFromComponentIndex(j); } } - AppendUcodeDisasm(", "); - DisasmAluSourceReg(op, 1, opcode_info.src_swizzle_component_count); - if (opcode_info.argument_count > 1) { - AppendUcodeDisasm(", "); - DisasmAluSourceReg(op, 2, opcode_info.src_swizzle_component_count); - } - if (opcode_info.argument_count > 2) { - AppendUcodeDisasm(", "); - DisasmAluSourceReg(op, 3, opcode_info.src_swizzle_component_count); + i.operand_count = opcode_info.argument_count; + for (int j = 0; j < i.operand_count; ++j) { + ParseAluInstructionOperand( + op, j + 1, opcode_info.src_swizzle_component_count, &i.operands[j]); } - AppendUcodeDisasm('\n'); + i.Disassemble(&ucode_disasm_buffer_); + + ProcessAluInstruction(i); } -void ShaderTranslator::DisasmAluScalarInstruction( +void ShaderTranslator::ParseAluScalarInstruction( const AluInstruction& op, const AluOpcodeInfo& opcode_info) { - if (op.has_vector_op()) { - AppendUcodeDisasm(" + "); - } else { - AppendUcodeDisasm(" "); - } - if (op.is_predicated()) { - AppendUcodeDisasm(op.predicate_condition() ? " (p0) " : "(!p0) "); - } else { - AppendUcodeDisasm(" "); - } - AppendUcodeDisasmFormat(opcode_info.name); - if (op.scalar_clamp()) { - AppendUcodeDisasm("_sat"); - } - AppendUcodeDisasm(' '); + ParsedAluInstruction i; + i.dword_index = 0; + i.type = ParsedAluInstruction::Type::kScalar; + i.scalar_opcode = op.scalar_opcode(); + i.opcode_name = opcode_info.name; + i.is_paired = op.has_vector_op(); + i.is_predicated = op.is_predicated(); + i.predicate_condition = op.predicate_condition(); uint32_t dest_num; uint32_t write_mask; @@ -1218,21 +1061,65 @@ void ShaderTranslator::DisasmAluScalarInstruction( dest_num = op.scalar_dest(); write_mask = op.scalar_write_mask(); } - AppendUcodeDisasm(GetDestRegName(shader_type_, dest_num, op.is_export())); - if (!write_mask) { - AppendUcodeDisasm("._"); - } else if (write_mask != 0xF) { - AppendUcodeDisasm('.'); - for (int i = 0; i < 4; ++i, write_mask >>= 1) { - if (write_mask & 0x1) { - AppendUcodeDisasm(kXyzwChannelNames[i]); - } + i.result.is_export = op.is_export(); + i.result.is_clamped = op.scalar_clamp(); + i.result.storage_target = InstructionStorageTarget::kRegister; + i.result.storage_index = 0; + if (!op.is_export()) { + assert_true(dest_num < 32); + i.result.storage_target = InstructionStorageTarget::kRegister; + i.result.storage_index = dest_num; + i.result.storage_addressing_mode = + op.is_scalar_dest_relative() + ? InstructionStorageAddressingMode::kAddressRelative + : InstructionStorageAddressingMode::kStatic; + } else if (is_vertex_shader()) { + switch (dest_num) { + case 62: + i.result.storage_target = InstructionStorageTarget::kPosition; + break; + case 63: + i.result.storage_target = InstructionStorageTarget::kPointSize; + break; + default: + assert_true(dest_num < 16); + i.result.storage_target = InstructionStorageTarget::kInterpolant; + i.result.storage_index = dest_num; + break; + } + } else if (is_pixel_shader()) { + switch (dest_num) { + case 0: + case 63: // ? masked? + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 0; + break; + case 1: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 1; + break; + case 2: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 2; + break; + case 3: + i.result.storage_target = InstructionStorageTarget::kColorTarget; + i.result.storage_index = 3; + break; + case 61: + i.result.storage_target = InstructionStorageTarget::kDepth; + break; } } - AppendUcodeDisasm(", "); + for (int j = 0; j < 4; ++j, write_mask >>= 1) { + i.result.write_mask[j] = (write_mask & 0x1) == 0x1; + i.result.components[j] = GetSwizzleFromComponentIndex(j); + } + i.operand_count = opcode_info.argument_count; if (opcode_info.argument_count == 1) { - DisasmAluSourceReg(op, 3, opcode_info.src_swizzle_component_count); + ParseAluInstructionOperand(op, 3, opcode_info.src_swizzle_component_count, + &i.operands[0]); } else { uint32_t src3_swizzle = op.src_swizzle(3); uint32_t swiz_a = ((src3_swizzle >> 6) - 1) & 0x3; @@ -1240,18 +1127,17 @@ void ShaderTranslator::DisasmAluScalarInstruction( uint32_t reg2 = (static_cast(op.scalar_opcode()) & 1) | (src3_swizzle & 0x3C) | (op.src_is_temp(3) << 1); int const_slot = (op.src_is_temp(1) || op.src_is_temp(2)) ? 1 : 0; - DisasmAluSourceRegScalarSpecial(op, op.src_reg(3), false, op.src_negate(3), - 0, swiz_a); - AppendUcodeDisasm(", "); - DisasmAluSourceRegScalarSpecial(op, reg2, true, op.src_negate(3), - const_slot, swiz_b); + ParseAluInstructionOperandSpecial( + op, InstructionStorageSource::kConstantFloat, op.src_reg(3), + op.src_negate(3), 0, swiz_a, &i.operands[0]); + ParseAluInstructionOperandSpecial(op, InstructionStorageSource::kRegister, + reg2, op.src_negate(3), const_slot, + swiz_b, &i.operands[1]); } - AppendUcodeDisasm('\n'); -} + i.Disassemble(&ucode_disasm_buffer_); -std::vector UcodeShaderTranslator::CompleteTranslation() { - return ucode_disasm_buffer().ToBytes(); + ProcessAluInstruction(i); } } // namespace gpu diff --git a/src/xenia/gpu/shader_translator.h b/src/xenia/gpu/shader_translator.h index a9248081e..fb8305bbe 100644 --- a/src/xenia/gpu/shader_translator.h +++ b/src/xenia/gpu/shader_translator.h @@ -20,6 +20,428 @@ namespace xe { namespace gpu { + +enum class InstructionStorageTarget { + // Result is not stored. + kNone, + // Result is stored to a temporary register indexed by storage_index [0-31]. + kRegister, + // Result is stored into a vertex shader interpolant export [0-15]. + kInterpolant, + // Result is stored to the position export (gl_Position). + kPosition, + // Result is stored to the point size export (gl_PointSize). + kPointSize, + // Result is stored to a color target export indexed by storage_index [0-3]. + kColorTarget, + // Result is stored to the depth export (gl_FragDepth). + kDepth, +}; + +enum class InstructionStorageAddressingMode { + // The storage index is not dynamically addressed. + kStatic, + // The storage index is addressed by a0. + kAddressAbsolute, + // The storage index is addressed by aL. + kAddressRelative, +}; + +// Describes the source value of a particular component. +enum class SwizzleSource { + // Component receives the source X. + kX, + // Component receives the source Y. + kY, + // Component receives the source Z. + kZ, + // Component receives the source W. + kW, + // Component receives constant 0. + k0, + // Component receives constant 1. + k1, +}; + +constexpr SwizzleSource GetSwizzleFromComponentIndex(int i) { + return static_cast(i); +} +inline char GetCharForSwizzle(SwizzleSource swizzle_source) { + const static char kChars[] = {'x', 'y', 'z', 'w', '0', '1'}; + return kChars[static_cast(swizzle_source)]; +} + +struct InstructionResult { + // Where the result is going. + InstructionStorageTarget storage_target = InstructionStorageTarget::kNone; + // Index into the storage_target, if it is indexed. + int storage_index = 0; + // How the storage index is dynamically addressed, if it is. + InstructionStorageAddressingMode storage_addressing_mode = + InstructionStorageAddressingMode::kStatic; + // True if the result is exporting from the shader. + bool is_export = false; + // True to clamp the result value to [0-1]. + bool is_clamped = false; + // Defines whether each output component is written. + bool write_mask[4] = {false, false, false, false}; + // Defines the source for each output component xyzw. + SwizzleSource components[4] = {SwizzleSource::kX, SwizzleSource::kY, + SwizzleSource::kZ, SwizzleSource::kW}; + // Returns true if any component is written to. + bool has_any_writes() const { + return write_mask[0] || write_mask[1] || write_mask[2] || write_mask[3]; + } + // Returns true if all components are written to. + bool has_all_writes() const { + return write_mask[0] && write_mask[1] && write_mask[2] && write_mask[3]; + } + // True if the components are in their 'standard' swizzle arrangement (xyzw). + bool is_standard_swizzle() const { + return has_all_writes() && components[0] == SwizzleSource::kX && + components[1] == SwizzleSource::kY && + components[2] == SwizzleSource::kZ && + components[3] == SwizzleSource::kW; + } +}; + +enum class InstructionStorageSource { + // Source is stored in a temporary register indexed by storage_index [0-31]. + kRegister, + // Source is stored in a float constant indexed by storage_index [0-511]. + kConstantFloat, + // Source is stored in a float constant indexed by storage_index [0-31]. + kConstantInt, + // Source is stored in a float constant indexed by storage_index [0-255]. + kConstantBool, + // Source is stored in a vertex fetch constant indexed by storage_index + // [0-95]. + kVertexFetchConstant, + // Source is stored in a texture fetch constant indexed by storage_index + // [0-31]. + kTextureFetchConstant, +}; + +struct InstructionOperand { + // Where the source comes from. + InstructionStorageSource storage_source = InstructionStorageSource::kRegister; + // Index into the storage_target, if it is indexed. + int storage_index = 0; + // How the storage index is dynamically addressed, if it is. + InstructionStorageAddressingMode storage_addressing_mode = + InstructionStorageAddressingMode::kStatic; + // True to negate the operand value. + bool is_negated = false; + // True to take the absolute value of the source (before any negation). + bool is_absolute_value = false; + // Number of components taken from the source operand. + int component_count = 0; + // Defines the source for each component xyzw (up to the given + // component_count). + SwizzleSource components[4] = {SwizzleSource::kX, SwizzleSource::kY, + SwizzleSource::kZ, SwizzleSource::kW}; + // True if the components are in their 'standard' swizzle arrangement (xyzw). + bool is_standard_swizzle() const { + switch (component_count) { + case 4: + return components[0] == SwizzleSource::kX && + components[1] == SwizzleSource::kY && + components[2] == SwizzleSource::kZ && + components[3] == SwizzleSource::kW; + } + return false; + } +}; + +struct ParsedExecInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Opcode for the instruction. + ucode::ControlFlowOpcode opcode; + // Friendly name of the instruction. + const char* opcode_name = nullptr; + + // Instruction address where ALU/fetch instructions reside. + uint32_t instruction_address = 0; + // Number of instructions to execute. + uint32_t instruction_count = 0; + + enum class Type { + // Block is always executed. + kUnconditional, + // Execution is conditional on the value of the boolean constant. + kConditional, + // Execution is predicated. + kPredicated, + }; + // Condition required to execute the instructions. + Type type = Type::kUnconditional; + // Constant index used as the conditional if kConditional. + uint32_t bool_constant_index = 0; + // Required condition value of the comparision (true or false). + bool condition = false; + + // Whether to reset the current predicate. + bool clean = true; + // ? + bool is_yield = false; + + // Sequence bits, 2 per instruction, indicating whether ALU or fetch. + uint32_t sequence = 0; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedLoopStartInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Integer constant register that holds the loop parameters. + // Byte-wise: [loop count, start, step [-128, 127], ?] + uint32_t loop_constant_index = 0; + // Whether to reuse the current aL instead of reset it to loop start. + bool is_repeat = false; + + // Target address to jump to when skipping the loop. + uint32_t loop_skip_address = 0; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedLoopEndInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Break from the loop if the predicate matches the expected value. + bool is_predicated_break = false; + // Required condition value of the comparision (true or false). + bool predicate_condition = false; + + // Integer constant register that holds the loop parameters. + // Byte-wise: [loop count, start, step [-128, 127], ?] + uint32_t loop_constant_index = 0; + + // Target address of the start of the loop body. + uint32_t loop_body_address = 0; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedCallInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Target address. + uint32_t target_address = 0; + + enum class Type { + // Call is always made. + kUnconditional, + // Call is conditional on the value of the boolean constant. + kConditional, + // Call is predicated. + kPredicated, + }; + // Condition required to make the call. + Type type = Type::kUnconditional; + // Constant index used as the conditional if kConditional. + uint32_t bool_constant_index = 0; + // Required condition value of the comparision (true or false). + bool condition = false; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedReturnInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedJumpInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Target address. + uint32_t target_address = 0; + + enum class Type { + // Jump is always taken. + kUnconditional, + // Jump is conditional on the value of the boolean constant. + kConditional, + // Jump is predicated. + kPredicated, + }; + // Condition required to make the jump. + Type type = Type::kUnconditional; + // Constant index used as the conditional if kConditional. + uint32_t bool_constant_index = 0; + // Required condition value of the comparision (true or false). + bool condition = false; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedAllocInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // The type of resource being allocated. + ucode::AllocType type = ucode::AllocType::kNone; + // Total count associated with the allocation. + int count = 0; + + // True if this allocation is in a vertex shader. + bool is_vertex_shader = false; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedVertexFetchInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Opcode for the instruction. + ucode::FetchOpcode opcode; + // Friendly name of the instruction. + const char* opcode_name = nullptr; + + // True if the fetch is reusing a previous full fetch. + // The previous fetch source and constant data will be populated. + bool is_mini_fetch = false; + + // True if the instruction is predicated on the specified + // predicate_condition. + bool is_predicated = false; + // Expected predication condition value if predicated. + bool predicate_condition = false; + + // Describes how the instruction result is stored. + InstructionResult result; + + // Number of source operands. + size_t operand_count = 0; + // Describes each source operand. + InstructionOperand operands[2]; + + struct Attributes { + VertexFormat data_format = VertexFormat::kUndefined; + int offset = 0; + int stride = 0; + int exp_adjust = 0; + bool is_index_rounded = false; + bool is_signed = false; + bool is_integer = false; + int prefetch_count = 0; + }; + // Attributes describing the fetch operation. + Attributes attributes; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedTextureFetchInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + // Opcode for the instruction. + ucode::FetchOpcode opcode; + // Friendly name of the instruction. + const char* opcode_name = nullptr; + // Texture dimension for opcodes that have multiple dimension forms. + TextureDimension dimension = TextureDimension::k1D; + + // True if the instruction is predicated on the specified + // predicate_condition. + bool is_predicated = false; + // Expected predication condition value if predicated. + bool predicate_condition = false; + + // True if the instruction has a result. + bool has_result() const { + return result.storage_target != InstructionStorageTarget::kNone; + } + // Describes how the instruction result is stored. + InstructionResult result; + + // Number of source operands. + size_t operand_count = 0; + // Describes each source operand. + InstructionOperand operands[2]; + + struct Attributes { + bool fetch_valid_only = true; + bool unnormalized_coordinates = false; + TextureFilter mag_filter = TextureFilter::kUseFetchConst; + TextureFilter min_filter = TextureFilter::kUseFetchConst; + TextureFilter mip_filter = TextureFilter::kUseFetchConst; + AnisoFilter aniso_filter = AnisoFilter::kUseFetchConst; + bool use_computed_lod = true; + bool use_register_lod = false; + bool use_register_gradients = false; + float offset_x = 0.0f; + float offset_y = 0.0f; + float offset_z = 0.0f; + }; + // Attributes describing the fetch operation. + Attributes attributes; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + +struct ParsedAluInstruction { + // Index into the ucode dword source. + uint32_t dword_index = 0; + + enum class Type { + kNop, + kVector, + kScalar, + }; + // Type of the instruction. + Type type = Type::kNop; + bool is_nop() const { return type == Type::kNop; } + bool is_vector_type() const { return type == Type::kVector; } + bool is_scalar_type() const { return type == Type::kScalar; } + // Opcode for the instruction if it is a vector type. + ucode::AluVectorOpcode vector_opcode = ucode::AluVectorOpcode::kADDv; + // Opcode for the instruction if it is a scalar type. + ucode::AluScalarOpcode scalar_opcode = ucode::AluScalarOpcode::kADDs; + // Friendly name of the instruction. + const char* opcode_name = nullptr; + + // True if the instruction is paired with another instruction. + bool is_paired = false; + // True if the instruction is predicated on the specified + // predicate_condition. + bool is_predicated = false; + // Expected predication condition value if predicated. + bool predicate_condition = false; + + // Describes how the instruction result is stored. + InstructionResult result; + + // Number of source operands. + size_t operand_count = 0; + // Describes each source operand. + InstructionOperand operands[3]; + + // Disassembles the instruction into ucode assembly text. + void Disassemble(StringBuffer* out) const; +}; + class TranslatedShader { public: struct Error { @@ -33,15 +455,16 @@ class TranslatedShader { // Fetch constant index [0-95]. uint32_t fetch_constant; // Fetch instruction with all parameters. - ucode::VertexFetchInstruction op; + ParsedVertexFetchInstruction fetch_instr; }; + struct TextureBinding { // Index within the texture binding listing. size_t binding_index; // Fetch constant index [0-31]. uint32_t fetch_constant; // Fetch instruction with all parameters. - ucode::TextureFetchInstruction op; + ParsedTextureFetchInstruction fetch_instr; }; ~TranslatedShader(); @@ -59,9 +482,9 @@ class TranslatedShader { } bool is_valid() const { return is_valid_; } - const std::vector errors() const { return errors_; } + const std::vector& errors() const { return errors_; } - const std::vector binary() const { return binary_; } + const std::vector& binary() const { return binary_; } private: friend class ShaderTranslator; @@ -95,146 +518,80 @@ class ShaderTranslator { protected: ShaderTranslator(); + // True if the current shader is a vertex shader. + bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; } + // True if the current shader is a pixel shader. + bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; } + // A list of all vertex bindings, populated before translation occurs. + const std::vector& vertex_bindings() const { + return vertex_bindings_; + } + // A list of all texture bindings, populated before translation occurs. + const std::vector& texture_bindings() + const { + return texture_bindings_; + } + + // Current line number in the ucode disassembly. size_t ucode_disasm_line_number() const { return ucode_disasm_line_number_; } + // Ucode disassembly buffer accumulated during translation. StringBuffer& ucode_disasm_buffer() { return ucode_disasm_buffer_; } + // Emits a translation error that will be passed back in the result. void EmitTranslationError(const char* message); + // Handles the start of translation. + // At this point the vertex and texture bindings have been gathered. + virtual void StartTranslation() {} + + // Handles the end of translation when all ucode has been processed. + // Returns the translated shader binary. virtual std::vector CompleteTranslation() { return std::vector(); } - virtual void ProcessControlFlowNop(const ucode::ControlFlowInstruction& cf) {} - virtual void ProcessControlFlowExec( - const ucode::ControlFlowExecInstruction& cf) {} - virtual void ProcessControlFlowCondExec( - const ucode::ControlFlowCondExecInstruction& cf) {} - virtual void ProcessControlFlowCondExecPred( - const ucode::ControlFlowCondExecPredInstruction& cf) {} - virtual void ProcessControlFlowLoopStart( - const ucode::ControlFlowLoopStartInstruction& cf) {} - virtual void ProcessControlFlowLoopEnd( - const ucode::ControlFlowLoopEndInstruction& cf) {} - virtual void ProcessControlFlowCondCall( - const ucode::ControlFlowCondCallInstruction& cf) {} - virtual void ProcessControlFlowReturn( - const ucode::ControlFlowReturnInstruction& cf) {} - virtual void ProcessControlFlowCondJmp( - const ucode::ControlFlowCondJmpInstruction& cf) {} - virtual void ProcessControlFlowAlloc( - const ucode::ControlFlowAllocInstruction& cf) {} + // Handles translation for control flow label addresses. + // This is triggered once for each label required (due to control flow + // operations) before any of the instructions within the target exec. + virtual void ProcessLabel(uint32_t cf_index) {} + // Handles translation for control flow nop instructions. + virtual void ProcessControlFlowNopInstruction() {} + // Handles translation for control flow exec instructions prior to their + // contained ALU/fetch instructions. + virtual void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) { + } + // Handles translation for control flow exec instructions after their + // contained ALU/fetch instructions. + virtual void ProcessExecInstructionEnd(const ParsedExecInstruction& instr) {} + // Handles translation for loop start instructions. + virtual void ProcessLoopStartInstruction( + const ParsedLoopStartInstruction& instr) {} + // Handles translation for loop end instructions. + virtual void ProcessLoopEndInstruction( + const ParsedLoopEndInstruction& instr) {} + // Handles translation for function call instructions. + virtual void ProcessCallInstruction(const ParsedCallInstruction& instr) {} + // Handles translation for function return instructions. + virtual void ProcessReturnInstruction(const ParsedReturnInstruction& instr) {} + // Handles translation for jump instructions. + virtual void ProcessJumpInstruction(const ParsedJumpInstruction& instr) {} + // Handles translation for alloc instructions. + virtual void ProcessAllocInstruction(const ParsedAllocInstruction& instr) {} + + // Handles translation for vertex fetch instructions. virtual void ProcessVertexFetchInstruction( - const ucode::VertexFetchInstruction& op) {} - virtual void ProcessTextureFetchTextureFetch( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchGetTextureBorderColorFrac( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchGetTextureComputedLod( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchGetTextureGradients( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchGetTextureWeights( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchSetTextureLod( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchSetTextureGradientsHorz( - const ucode::TextureFetchInstruction& op) {} - virtual void ProcessTextureFetchSetTextureGradientsVert( - const ucode::TextureFetchInstruction& op) {} - - virtual void ProcessAluNop(const ucode::AluInstruction& op) {} - - virtual void ProcessAluVectorAdd(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMul(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMax(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMin(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorSetEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorSetGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorSetGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorSetNE(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorFrac(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorTrunc(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorFloor(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMad(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorCndEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorCndGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorCndGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorDp4(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorDp3(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorDp2Add(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorCube(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMax4(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorPredSetEQPush(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorPredSetNEPush(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorPredSetGTPush(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorPredSetGEPush(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorKillEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorKillGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorKillLGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorKillNE(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorDst(const ucode::AluInstruction& op) {} - virtual void ProcessAluVectorMaxA(const ucode::AluInstruction& op) {} - - virtual void ProcessAluScalarAdd(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarAddPrev(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMul(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMulPrev(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMulPrev2(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMax(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMin(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSetEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSetGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSetGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSetNE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarFrac(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarTrunc(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarFloor(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarExp(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarLogClamp(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarLog(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarRecipClamp(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarRecipFixedFunc(const ucode::AluInstruction& op) { - } - virtual void ProcessAluScalarRecip(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarRSqrtClamp(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarRSqrtFixedFunc(const ucode::AluInstruction& op) { - } - virtual void ProcessAluScalarRSqrt(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMovA(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMovAFloor(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSub(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSubPrev(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetNE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetInv(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetPop(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetClear(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarPredSetRestore(const ucode::AluInstruction& op) { - } - virtual void ProcessAluScalarKillEQ(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarKillGT(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarKillGE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarKillNE(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarKillOne(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSqrt(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMulConst0(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarMulConst1(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarAddConst0(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarAddConst1(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSubConst0(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSubConst1(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarSin(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarCos(const ucode::AluInstruction& op) {} - virtual void ProcessAluScalarRetainPrev(const ucode::AluInstruction& op) {} + const ParsedVertexFetchInstruction& instr) {} + // Handles translation for texture fetch instructions. + virtual void ProcessTextureFetchInstruction( + const ParsedTextureFetchInstruction& instr) {} + // Handles translation for ALU instructions. + virtual void ProcessAluInstruction(const ParsedAluInstruction& instr) {} private: struct AluOpcodeInfo { const char* name; size_t argument_count; int src_swizzle_component_count; - void (ShaderTranslator::*fn)(const ucode::AluInstruction& op); }; void MarkUcodeInstruction(uint32_t dword_offset); @@ -266,28 +623,22 @@ class ShaderTranslator { const ucode::ControlFlowCondJmpInstruction& cf); void TranslateControlFlowAlloc(const ucode::ControlFlowAllocInstruction& cf); - void TranslateExecInstructions(uint32_t address, uint32_t count, - uint32_t sequence); + void TranslateExecInstructions(const ParsedExecInstruction& instr); - void DisasmFetchDestReg(uint32_t dest, uint32_t swizzle, bool is_relative); - void DisasmFetchSourceReg(uint32_t src, uint32_t swizzle, bool is_relative, - int component_count); void TranslateVertexFetchInstruction(const ucode::VertexFetchInstruction& op); + void ParseVertexFetchInstruction(const ucode::VertexFetchInstruction& op, + ParsedVertexFetchInstruction* out_instr); + void TranslateTextureFetchInstruction( const ucode::TextureFetchInstruction& op); - void DisasmVertexFetchAttributes(const ucode::VertexFetchInstruction& op); - void DisasmTextureFetchAttributes(const ucode::TextureFetchInstruction& op); + void ParseTextureFetchInstruction(const ucode::TextureFetchInstruction& op, + ParsedTextureFetchInstruction* out_instr); void TranslateAluInstruction(const ucode::AluInstruction& op); - void DisasmAluVectorInstruction(const ucode::AluInstruction& op, - const AluOpcodeInfo& opcode_info); - void DisasmAluScalarInstruction(const ucode::AluInstruction& op, - const AluOpcodeInfo& opcode_info); - void DisasmAluSourceReg(const ucode::AluInstruction& op, int i, - int swizzle_component_count); - void DisasmAluSourceRegScalarSpecial(const ucode::AluInstruction& op, - uint32_t reg, bool is_temp, bool negate, - int const_slot, uint32_t swizzle); + void ParseAluVectorInstruction(const ucode::AluInstruction& op, + const AluOpcodeInfo& opcode_info); + void ParseAluScalarInstruction(const ucode::AluInstruction& op, + const AluOpcodeInfo& opcode_info); // Input shader metadata and microcode. ShaderType shader_type_; diff --git a/src/xenia/gpu/shader_translator_disasm.cc b/src/xenia/gpu/shader_translator_disasm.cc new file mode 100644 index 000000000..2539d2911 --- /dev/null +++ b/src/xenia/gpu/shader_translator_disasm.cc @@ -0,0 +1,458 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/shader_translator.h" + +#include +#include +#include + +#include "xenia/base/math.h" + +namespace xe { +namespace gpu { + +using namespace ucode; + +void DisassembleResultOperand(const InstructionResult& result, + StringBuffer* out) { + bool uses_storage_index = false; + switch (result.storage_target) { + case InstructionStorageTarget::kRegister: + out->Append('r'); + uses_storage_index = true; + break; + case InstructionStorageTarget::kInterpolant: + out->Append('o'); + uses_storage_index = true; + break; + case InstructionStorageTarget::kPosition: + out->Append("oPos"); + break; + case InstructionStorageTarget::kPointSize: + out->Append("oPts"); + break; + case InstructionStorageTarget::kColorTarget: + out->AppendFormat("oC"); + uses_storage_index = true; + break; + case InstructionStorageTarget::kDepth: + out->Append("oDepth"); + break; + } + if (uses_storage_index) { + switch (result.storage_addressing_mode) { + case InstructionStorageAddressingMode::kStatic: + out->AppendFormat("%d", result.storage_index); + break; + case InstructionStorageAddressingMode::kAddressAbsolute: + out->AppendFormat("[%d+a0]", result.storage_index); + break; + case InstructionStorageAddressingMode::kAddressRelative: + out->AppendFormat("[%d+aL]", result.storage_index); + break; + } + } + if (!result.has_any_writes()) { + out->Append("._"); + } else if (!result.is_standard_swizzle()) { + out->Append('.'); + for (int i = 0; i < 4; ++i) { + if (result.write_mask[i]) { + out->Append(GetCharForSwizzle(result.components[i])); + } else { + out->Append('_'); + } + } + } +} + +void DisassembleSourceOperand(const InstructionOperand& op, StringBuffer* out) { + if (op.is_negated) { + out->Append('-'); + } + switch (op.storage_source) { + case InstructionStorageSource::kRegister: + out->Append('r'); + break; + case InstructionStorageSource::kConstantFloat: + out->Append('c'); + break; + case InstructionStorageSource::kConstantInt: + out->Append('i'); + break; + case InstructionStorageSource::kConstantBool: + out->Append('b'); + break; + } + switch (op.storage_addressing_mode) { + case InstructionStorageAddressingMode::kStatic: + out->AppendFormat("%d", op.storage_index); + break; + case InstructionStorageAddressingMode::kAddressAbsolute: + out->AppendFormat("[%d+a0]", op.storage_index); + break; + case InstructionStorageAddressingMode::kAddressRelative: + out->AppendFormat("[%d+aL]", op.storage_index); + break; + } + if (op.is_absolute_value) { + out->Append("_abs"); + } + if (!op.is_standard_swizzle()) { + out->Append('.'); + if (op.component_count == 1) { + out->Append(GetCharForSwizzle(op.components[0])); + } else if (op.component_count == 2) { + out->Append(GetCharForSwizzle(op.components[0])); + out->Append(GetCharForSwizzle(op.components[1])); + } else { + for (int j = 0; j < op.component_count; ++j) { + out->Append(GetCharForSwizzle(op.components[j])); + } + } + } +} + +void ParsedExecInstruction::Disassemble(StringBuffer* out) const { + switch (type) { + case Type::kUnconditional: + out->AppendFormat(" %s ", opcode_name); + break; + case Type::kPredicated: + out->Append(condition ? " (p0) " : "(!p0) "); + out->AppendFormat("%s ", opcode_name); + break; + case Type::kConditional: + out->AppendFormat(" %s ", opcode_name); + if (!condition) { + out->Append('!'); + } + out->AppendFormat("b%u", bool_constant_index); + break; + } + if (is_yield) { + out->Append(", Yield=true"); + } + if (!clean) { + out->Append(" // PredicateClean=false"); + } + out->Append('\n'); +} + +void ParsedLoopStartInstruction::Disassemble(StringBuffer* out) const { + out->Append(" loop "); + out->AppendFormat("i%u, L%u", loop_constant_index, loop_skip_address); + if (is_repeat) { + out->Append(", Repeat=true"); + } + out->Append('\n'); +} + +void ParsedLoopEndInstruction::Disassemble(StringBuffer* out) const { + if (is_predicated_break) { + out->Append(predicate_condition ? " (p0) " : "(!p0) "); + } + out->AppendFormat("endloop i%u, L%u", loop_constant_index, loop_body_address); + out->Append('\n'); +} + +void ParsedCallInstruction::Disassemble(StringBuffer* out) const { + switch (type) { + case Type::kUnconditional: + out->Append(" call "); + break; + case Type::kPredicated: + out->Append(condition ? " (p0) " : "(!p0) "); + out->Append("call "); + break; + case Type::kConditional: + out->Append(" ccall "); + if (!condition) { + out->Append('!'); + } + out->AppendFormat("b%u, ", bool_constant_index); + break; + } + out->AppendFormat("L%u", target_address); + out->Append('\n'); +} + +void ParsedReturnInstruction::Disassemble(StringBuffer* out) const { + out->Append(" ret\n"); +} + +void ParsedJumpInstruction::Disassemble(StringBuffer* out) const { + switch (type) { + case Type::kUnconditional: + out->Append(" jmp "); + break; + case Type::kPredicated: + out->Append(condition ? " (p0) " : "(!p0) "); + out->Append("jmp "); + break; + case Type::kConditional: + out->Append(" cjmp "); + if (!condition) { + out->Append('!'); + } + out->AppendFormat("b%u, ", bool_constant_index); + break; + } + out->AppendFormat("L%u", target_address); + out->Append('\n'); +} + +void ParsedAllocInstruction::Disassemble(StringBuffer* out) const { + out->Append(" alloc "); + switch (type) { + case AllocType::kNone: + break; + case AllocType::kVsPosition: + out->AppendFormat("position"); + break; + case AllocType::kVsInterpolators: // or AllocType::kPsColors + if (is_vertex_shader) { + out->Append("interpolators"); + } else { + out->Append("colors"); + } + break; + case AllocType::kMemory: + out->AppendFormat("export = %d", count); + break; + } + out->Append('\n'); +} + +void ParsedVertexFetchInstruction::Disassemble(StringBuffer* out) const { + static const struct { + const char* name; + } kVertexFetchDataFormats[0xff] = { +#define TYPE(id) {#id} + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + TYPE(FMT_8_8_8_8), // 6 + TYPE(FMT_2_10_10_10), // 7 + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + TYPE(FMT_10_11_11), // 16 + TYPE(FMT_11_11_10), // 17 + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + TYPE(FMT_16_16), // 25 + TYPE(FMT_16_16_16_16), // 26 + {0}, + {0}, + {0}, + {0}, + TYPE(FMT_16_16_FLOAT), // 31 + TYPE(FMT_16_16_16_16_FLOAT), // 32 + TYPE(FMT_32), // 33 + TYPE(FMT_32_32), // 34 + TYPE(FMT_32_32_32_32), // 35 + TYPE(FMT_32_FLOAT), // 36 + TYPE(FMT_32_32_FLOAT), // 37 + TYPE(FMT_32_32_32_32_FLOAT), // 38 + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + TYPE(FMT_32_32_32_FLOAT), // 57 +#undef TYPE + }; + out->Append(" "); + if (is_predicated) { + out->Append(predicate_condition ? " (p0) " : "(!p0) "); + } else { + out->Append(" "); + } + out->AppendFormat(opcode_name); + out->Append(' '); + DisassembleResultOperand(result, out); + if (!is_mini_fetch) { + out->Append(", "); + DisassembleSourceOperand(operands[0], out); + out->Append(", "); + out->AppendFormat("vf%d", 95 - operands[1].storage_index); + } + + if (attributes.is_index_rounded) { + out->Append(", RoundIndex=true"); + } + if (attributes.exp_adjust) { + out->AppendFormat(", ExpAdjust=%d", attributes.exp_adjust); + } + if (attributes.offset) { + out->AppendFormat(", Offset=%d", attributes.offset); + } + if (attributes.data_format != VertexFormat::kUndefined) { + out->AppendFormat( + ", DataFormat=%s", + kVertexFetchDataFormats[static_cast(attributes.data_format)]); + } + if (attributes.stride) { + out->AppendFormat(", Stride=%d", attributes.stride); + } + if (attributes.is_signed) { + out->Append(", Signed=true"); + } + if (attributes.is_integer) { + out->Append(", NumFormat=integer"); + } + if (attributes.prefetch_count) { + out->AppendFormat(", PrefetchCount=%u", attributes.prefetch_count + 1); + } + + out->Append('\n'); +} + +void ParsedTextureFetchInstruction::Disassemble(StringBuffer* out) const { + static const char* kTextureFilterNames[] = { + "point", "linear", "BASEMAP", "keep", + }; + static const char* kAnisoFilterNames[] = { + "disabled", "max1to1", "max2to1", "max4to1", + "max8to1", "max16to1", "keep", + }; + + out->Append(" "); + if (is_predicated) { + out->Append(predicate_condition ? " (p0) " : "(!p0) "); + } else { + out->Append(" "); + } + out->Append(opcode_name); + out->Append(' '); + bool needs_comma = false; + if (has_result()) { + DisassembleResultOperand(result, out); + needs_comma = true; + } + if (needs_comma) { + out->Append(", "); + } + DisassembleSourceOperand(operands[0], out); + if (operand_count > 1) { + if (needs_comma) { + out->Append(", "); + } + out->AppendFormat("tf%u", operands[1].storage_index); + } + + if (!attributes.fetch_valid_only) { + out->Append(", FetchValidOnly=false"); + } + if (attributes.unnormalized_coordinates) { + out->Append(", UnnormalizedTextureCoords=true"); + } + if (attributes.mag_filter != TextureFilter::kUseFetchConst) { + out->AppendFormat( + ", MagFilter=%s", + kTextureFilterNames[static_cast(attributes.mag_filter)]); + } + if (attributes.min_filter != TextureFilter::kUseFetchConst) { + out->AppendFormat( + ", MinFilter=%s", + kTextureFilterNames[static_cast(attributes.min_filter)]); + } + if (attributes.mip_filter != TextureFilter::kUseFetchConst) { + out->AppendFormat( + ", MipFilter=%s", + kTextureFilterNames[static_cast(attributes.mip_filter)]); + } + if (attributes.aniso_filter != AnisoFilter::kUseFetchConst) { + out->AppendFormat( + ", AnisoFilter=%s", + kAnisoFilterNames[static_cast(attributes.aniso_filter)]); + } + if (!attributes.use_computed_lod) { + out->Append(", UseComputedLOD=false"); + } + if (attributes.use_register_lod) { + out->Append(", UseRegisterLOD=true"); + } + if (attributes.use_register_gradients) { + out->Append(", UseRegisterGradients=true"); + } + int component_count = GetTextureDimensionComponentCount(dimension); + if (attributes.offset_x != 0.0f) { + out->AppendFormat(", OffsetX=%g", attributes.offset_x); + } + if (component_count > 1 && attributes.offset_y != 0.0f) { + out->AppendFormat(", OffsetY=%g", attributes.offset_y); + } + if (component_count > 2 && attributes.offset_z != 0.0f) { + out->AppendFormat(", OffsetZ=%g", attributes.offset_z); + } + + out->Append('\n'); +} + +void ParsedAluInstruction::Disassemble(StringBuffer* out) const { + if (is_nop()) { + out->Append(" nop\n"); + return; + } + if (is_scalar_type() && is_paired) { + out->Append(" + "); + } else { + out->Append(" "); + } + if (is_predicated) { + out->Append(predicate_condition ? " (p0) " : "(!p0) "); + } else { + out->Append(" "); + } + out->Append(opcode_name); + if (result.is_clamped) { + out->Append("_sat"); + } + out->Append(' '); + + DisassembleResultOperand(result, out); + + for (int i = 0; i < operand_count; ++i) { + out->Append(", "); + DisassembleSourceOperand(operands[i], out); + } + out->Append('\n'); +} + +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc new file mode 100644 index 000000000..5f89b3882 --- /dev/null +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -0,0 +1,24 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/spirv_shader_translator.h" + +namespace xe { +namespace gpu { + +SpirvShaderTranslator::SpirvShaderTranslator() = default; + +SpirvShaderTranslator::~SpirvShaderTranslator() = default; + +std::vector SpirvShaderTranslator::CompleteTranslation() { + return std::vector(); +} + +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h new file mode 100644 index 000000000..2548c80d1 --- /dev/null +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -0,0 +1,34 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_ +#define XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_ + +#include +#include +#include + +#include "xenia/gpu/shader_translator.h" + +namespace xe { +namespace gpu { + +class SpirvShaderTranslator : public ShaderTranslator { + public: + SpirvShaderTranslator(); + ~SpirvShaderTranslator() override; + + protected: + std::vector CompleteTranslation() override; +}; + +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_ diff --git a/src/xenia/gpu/ucode.h b/src/xenia/gpu/ucode.h index 89a7c82a0..411b40ea9 100644 --- a/src/xenia/gpu/ucode.h +++ b/src/xenia/gpu/ucode.h @@ -102,6 +102,22 @@ constexpr bool IsControlFlowOpcodeExec(ControlFlowOpcode opcode) { opcode == ControlFlowOpcode::kCondExecPredCleanEnd; } +// Returns true if the given control flow opcode terminates the shader after +// executing. +constexpr bool DoesControlFlowOpcodeEndShader(ControlFlowOpcode opcode) { + return opcode == ControlFlowOpcode::kExecEnd || + opcode == ControlFlowOpcode::kCondExecEnd || + opcode == ControlFlowOpcode::kCondExecPredEnd || + opcode == ControlFlowOpcode::kCondExecPredCleanEnd; +} + +// Returns true if the given control flow opcode resets the predicate prior to +// execution. +constexpr bool DoesControlFlowOpcodeCleanPredicate(ControlFlowOpcode opcode) { + return opcode == ControlFlowOpcode::kCondExecPredClean || + opcode == ControlFlowOpcode::kCondExecPredCleanEnd; +} + // Determines whether addressing is based on a0 or aL. enum class AddressingMode : uint32_t { // Indexing into register sets is done based on aL. @@ -117,7 +133,7 @@ enum class AddressingMode : uint32_t { // shader. enum class AllocType : uint32_t { // ? - kNoAlloc = 0, + kNone = 0, // Vertex shader exports a position. kVsPosition = 1, // Vertex shader exports interpolators. @@ -791,9 +807,7 @@ struct AluInstruction { bool is_const_1_addressed() const { return data_.const_1_rel_abs == 1; } bool is_address_relative() const { return data_.address_absolute == 1; } - bool has_vector_op() const { - return vector_write_mask() || (is_export() && export_write_mask()); - } + bool has_vector_op() const { return vector_write_mask() || is_export(); } AluVectorOpcode vector_opcode() const { return static_cast(data_.vector_opc); } @@ -804,7 +818,7 @@ struct AluInstruction { bool has_scalar_op() const { return scalar_opcode() != AluScalarOpcode::kRETAIN_PREV || - scalar_write_mask() != 0; + (!is_export() && scalar_write_mask() != 0); } AluScalarOpcode scalar_opcode() const { return static_cast(data_.scalar_opc); diff --git a/src/xenia/gpu/xenos.h b/src/xenia/gpu/xenos.h index 0e5daa92e..98b022653 100644 --- a/src/xenia/gpu/xenos.h +++ b/src/xenia/gpu/xenos.h @@ -93,6 +93,21 @@ enum class TextureDimension : uint32_t { kCube = 3, }; +inline int GetTextureDimensionComponentCount(TextureDimension dimension) { + switch (dimension) { + case TextureDimension::k1D: + return 1; + case TextureDimension::k2D: + return 2; + case TextureDimension::k3D: + case TextureDimension::kCube: + return 3; + default: + assert_unhandled_case(dimension); + return 1; + } +} + enum class SampleLocation : uint32_t { kCentroid = 0, kCenter = 1, diff --git a/third_party/spirv-tools b/third_party/spirv-tools index 1ee9d71d2..e8de9fe81 160000 --- a/third_party/spirv-tools +++ b/third_party/spirv-tools @@ -1 +1 @@ -Subproject commit 1ee9d71d2e3843c602ea4e547c13a1744df5fc76 +Subproject commit e8de9fe81c334c2e166d0beb5ba3df2d7ff32715 diff --git a/tools/shader-playground/Editor.Designer.cs b/tools/shader-playground/Editor.Designer.cs index bd75a38b6..2615af1cc 100644 --- a/tools/shader-playground/Editor.Designer.cs +++ b/tools/shader-playground/Editor.Designer.cs @@ -25,14 +25,14 @@ private void InitializeComponent() { this.wordsTextBox = new System.Windows.Forms.TextBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.compilerTranslatedTextBox = new System.Windows.Forms.TextBox(); this.sourceCodeTextBox = new System.Windows.Forms.TextBox(); this.outputTextBox = new System.Windows.Forms.TextBox(); this.compilerUcodeTextBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.compilerTranslatedTextBox = new System.Windows.Forms.TextBox(); + this.translationComboBox = new System.Windows.Forms.ComboBox(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // @@ -65,7 +65,7 @@ this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.label2, 1, 0); this.tableLayoutPanel1.Controls.Add(this.label3, 2, 0); - this.tableLayoutPanel1.Controls.Add(this.label4, 3, 0); + this.tableLayoutPanel1.Controls.Add(this.translationComboBox, 3, 0); this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; @@ -74,6 +74,20 @@ this.tableLayoutPanel1.Size = new System.Drawing.Size(1631, 639); this.tableLayoutPanel1.TabIndex = 5; // + // compilerTranslatedTextBox + // + this.compilerTranslatedTextBox.AcceptsReturn = true; + this.compilerTranslatedTextBox.AcceptsTab = true; + this.compilerTranslatedTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.compilerTranslatedTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.compilerTranslatedTextBox.Location = new System.Drawing.Point(1224, 23); + this.compilerTranslatedTextBox.Multiline = true; + this.compilerTranslatedTextBox.Name = "compilerTranslatedTextBox"; + this.compilerTranslatedTextBox.ReadOnly = true; + this.compilerTranslatedTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.compilerTranslatedTextBox.Size = new System.Drawing.Size(404, 613); + this.compilerTranslatedTextBox.TabIndex = 11; + // // sourceCodeTextBox // this.sourceCodeTextBox.AcceptsReturn = true; @@ -142,28 +156,19 @@ this.label3.TabIndex = 9; this.label3.Text = "xenia-gpu-shader-compiler Disassembly"; // - // label4 + // translationComboBox // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(1224, 0); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(183, 13); - this.label4.TabIndex = 10; - this.label4.Text = "xenia-gpu-shader-compiler Translated"; - // - // compilerTranslatedTextBox - // - this.compilerTranslatedTextBox.AcceptsReturn = true; - this.compilerTranslatedTextBox.AcceptsTab = true; - this.compilerTranslatedTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.compilerTranslatedTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.compilerTranslatedTextBox.Location = new System.Drawing.Point(1224, 23); - this.compilerTranslatedTextBox.Multiline = true; - this.compilerTranslatedTextBox.Name = "compilerTranslatedTextBox"; - this.compilerTranslatedTextBox.ReadOnly = true; - this.compilerTranslatedTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.compilerTranslatedTextBox.Size = new System.Drawing.Size(404, 613); - this.compilerTranslatedTextBox.TabIndex = 11; + this.translationComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.translationComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.translationComboBox.FormattingEnabled = true; + this.translationComboBox.Items.AddRange(new object[] { + "SPIRV"}); + this.translationComboBox.Location = new System.Drawing.Point(1224, 0); + this.translationComboBox.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.translationComboBox.Name = "translationComboBox"; + this.translationComboBox.Size = new System.Drawing.Size(404, 21); + this.translationComboBox.TabIndex = 12; // // Editor // @@ -191,7 +196,7 @@ private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox compilerTranslatedTextBox; - private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox translationComboBox; } } diff --git a/tools/shader-playground/Editor.cs b/tools/shader-playground/Editor.cs index c27228cf2..1161cf83d 100644 --- a/tools/shader-playground/Editor.cs +++ b/tools/shader-playground/Editor.cs @@ -13,6 +13,7 @@ namespace shader_playground { string compilerPath_ = @"..\..\..\..\..\build\bin\Windows\Debug\xenia-gpu-shader-compiler.exe"; FileSystemWatcher compilerWatcher_; + bool pendingTimer_ = false; public Editor() { InitializeComponent(); @@ -23,7 +24,20 @@ namespace shader_playground { compilerWatcher_.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size; compilerWatcher_.Changed += (object sender, FileSystemEventArgs e) => { if (e.Name == Path.GetFileName(compilerPath_)) { - Invoke((MethodInvoker)delegate { Process(sourceCodeTextBox.Text); }); + Invoke((MethodInvoker)delegate { + if (pendingTimer_) { + return; + } + pendingTimer_ = true; + var timer = new Timer(); + timer.Interval = 1000; + timer.Tick += (object sender1, EventArgs e1) => { + pendingTimer_ = false; + timer.Dispose(); + Process(sourceCodeTextBox.Text); + }; + timer.Start(); + }); } }; compilerWatcher_.EnableRaisingEvents = true; @@ -33,14 +47,19 @@ namespace shader_playground { wordsTextBox.Copy(); }; - this.sourceCodeTextBox.Click += (object sender, EventArgs e) => { + sourceCodeTextBox.Click += (object sender, EventArgs e) => { Process(sourceCodeTextBox.Text); }; sourceCodeTextBox.TextChanged += (object sender, EventArgs e) => { Process(sourceCodeTextBox.Text); }; - sourceCodeTextBox.Text = string.Join( + translationComboBox.SelectedIndex = 0; + translationComboBox.SelectedIndexChanged += (object sender, EventArgs e) => { + Process(sourceCodeTextBox.Text); + }; + + sourceCodeTextBox.Text = string.Join( "\r\n", new string[] { "xps_3_0", "dcl_texcoord1 r0", @@ -59,6 +78,9 @@ namespace shader_playground { "(!p0) setGradientV r1.zyx", " getGradients r5, r1.xy, tf3", " mad oC0, r0, r1.yyyy, c0", +" mad oC0._, r0, r1.yyyy, c0", +" mad oC0.x1_, r0, r1.yyyy, c0", +" mad oC0.x10w, r0, r1.yyyy, c0", " mul r4.xyz, r1.xyzz, c5.xyzz", " mul r4.xyz, r1.xyzz, c[0 + aL].xyzz", " mul r4.xyz, r1.xyzz, c[6 + aL].xyzz", @@ -67,6 +89,8 @@ namespace shader_playground { " + adds r5.w, r0.xz", " cos r6.w, r0.x", " adds r5.w, r0.zx", +" mul r4.xyz, r[aL+1].xyzz, c[8 + a0].xyzz", +" adds r5.w, r[aL+0].zx", " jmp l5", "ccall b1, l5", "nop", @@ -151,15 +175,15 @@ namespace shader_playground { void TryCompiler(string shaderType, uint[] ucodeWords) { string ucodePath = Path.Combine(Path.GetTempPath(), "shader_playground_ucode.bin." + shaderType); string ucodeDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.ucode.txt"); - string spirvDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.spirv.txt"); + string translatedDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.translated.txt"); if (File.Exists(ucodePath)) { File.Delete(ucodePath); } if (File.Exists(ucodeDisasmPath)) { File.Delete(ucodeDisasmPath); } - if (File.Exists(spirvDisasmPath)) { - File.Delete(spirvDisasmPath); + if (File.Exists(translatedDisasmPath)) { + File.Delete(translatedDisasmPath); } byte[] ucodeBytes = new byte[ucodeWords.Length * 4]; @@ -190,12 +214,20 @@ namespace shader_playground { compilerUcodeTextBox.Text = "COMPILER FAILURE"; } + string outputType; + switch (translationComboBox.SelectedIndex) { + default: + case 0: + outputType = "spirvtext"; + break; + } + startInfo = new ProcessStartInfo(compilerPath_); startInfo.Arguments = string.Join(" ", new string[]{ "--shader_input=" + ucodePath, "--shader_input_type=" + shaderType, - "--shader_output=" + spirvDisasmPath, - "--shader_output_type=spirvtext", + "--shader_output=" + translatedDisasmPath, + "--shader_output_type=" + outputType, }); startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.CreateNoWindow = true; @@ -203,7 +235,7 @@ namespace shader_playground { using (var process = System.Diagnostics.Process.Start(startInfo)) { process.WaitForExit(); } - string disasmText = File.ReadAllText(spirvDisasmPath); + string disasmText = File.ReadAllText(translatedDisasmPath); compilerTranslatedTextBox.Text = disasmText; } catch { compilerTranslatedTextBox.Text = "COMPILER FAILURE";