diff --git a/src/xenia/base/string_buffer.cc b/src/xenia/base/string_buffer.cc index 5258ea7f5..d85bb411a 100644 --- a/src/xenia/base/string_buffer.cc +++ b/src/xenia/base/string_buffer.cc @@ -83,4 +83,10 @@ std::string StringBuffer::to_string() { char* StringBuffer::ToString() { return strdup(buffer_); } +std::vector StringBuffer::ToBytes() const { + std::vector bytes(buffer_offset_); + std::memcpy(bytes.data(), buffer_, buffer_offset_); + return bytes; +} + } // namespace xe diff --git a/src/xenia/base/string_buffer.h b/src/xenia/base/string_buffer.h index 457ce0756..8c1e32de9 100644 --- a/src/xenia/base/string_buffer.h +++ b/src/xenia/base/string_buffer.h @@ -12,6 +12,7 @@ #include #include +#include namespace xe { @@ -34,6 +35,7 @@ class StringBuffer { const char* GetString() const; std::string to_string(); char* ToString(); + std::vector ToBytes() const; private: void Grow(size_t additional_length); diff --git a/src/xenia/gpu/shader_compiler_main.cc b/src/xenia/gpu/shader_compiler_main.cc index dde7b98a5..f7a64681e 100644 --- a/src/xenia/gpu/shader_compiler_main.cc +++ b/src/xenia/gpu/shader_compiler_main.cc @@ -18,68 +18,83 @@ #include "xenia/base/string.h" #include "xenia/gpu/shader_translator.h" -DEFINE_string(input_shader, "", "Input shader binary file path."); -DEFINE_string( - shader_type, "", - "'vert', 'frag', or unspecified to infer from the given filename."); +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]."); namespace xe { namespace gpu { int shader_compiler_main(const std::vector& args) { ShaderType shader_type; - if (!FLAGS_shader_type.empty()) { - if (FLAGS_shader_type == "vert") { + if (!FLAGS_shader_input_type.empty()) { + if (FLAGS_shader_input_type == "vs") { shader_type = ShaderType::kVertex; - } else if (FLAGS_shader_type == "frag") { + } else if (FLAGS_shader_input_type == "ps") { shader_type = ShaderType::kPixel; } else { - XELOGE("Invalid --shader_type; must be 'vert' or 'frag'."); + XELOGE("Invalid --shader_input_type; must be 'vs' or 'ps'."); return 1; } } else { - auto last_dot = FLAGS_input_shader.find_last_of('.'); + auto last_dot = FLAGS_shader_input.find_last_of('.'); bool valid_type = false; if (last_dot != std::string::npos) { - if (FLAGS_input_shader.substr(last_dot) == ".vert") { + if (FLAGS_shader_input.substr(last_dot) == ".vs") { shader_type = ShaderType::kVertex; valid_type = true; - } else if (FLAGS_input_shader.substr(last_dot) == ".frag") { + } else if (FLAGS_shader_input.substr(last_dot) == ".ps") { shader_type = ShaderType::kPixel; valid_type = true; } } if (!valid_type) { XELOGE( - "File type not recognized (use .vert, .frag or " - "--shader_type=vert|frag)."); + "File type not recognized (use .vs, .ps or " + "--shader_input_type=vs|ps)."); return 1; } } - auto input_file = fopen(FLAGS_input_shader.c_str(), "r"); + auto input_file = fopen(FLAGS_shader_input.c_str(), "rb"); if (!input_file) { - XELOGE("Unable to open input file: %s", FLAGS_input_shader.c_str()); + XELOGE("Unable to open input file: %s", FLAGS_shader_input.c_str()); return 1; } fseek(input_file, 0, SEEK_END); size_t input_file_size = ftell(input_file); fseek(input_file, 0, SEEK_SET); - std::vector ucode_words(input_file_size / 4); - fread(ucode_words.data(), 4, ucode_words.size(), input_file); + std::vector ucode_dwords(input_file_size / 4); + fread(ucode_dwords.data(), 4, ucode_dwords.size(), input_file); fclose(input_file); XELOGI("Opened %s as a %s shader, %" PRId64 " words (%" PRId64 " bytes).", - FLAGS_input_shader.c_str(), - shader_type == ShaderType::kVertex ? "vertex" : "fragment", - ucode_words.size(), ucode_words.size() * 4); + FLAGS_shader_input.c_str(), + shader_type == ShaderType::kVertex ? "vertex" : "pixel", + ucode_dwords.size(), ucode_dwords.size() * 4); - ShaderTranslator translator; + std::unique_ptr translator; + if (FLAGS_shader_output_type == "spirvtext") { + // TODO(benvanik): SPIRV translator. + translator = std::make_unique(); + } else { + translator = std::make_unique(); + } // TODO(benvanik): hash? need to return the data to big-endian format first. uint64_t ucode_data_hash = 0; - auto translated_shader = translator.Translate( - shader_type, ucode_data_hash, ucode_words.data(), ucode_words.size()); + auto translated_shader = translator->Translate( + shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size()); + + 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); + fclose(output_file); + } return 0; } diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index 2c68c009e..b42cb1df0 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -9,92 +9,1250 @@ #include "xenia/gpu/shader_translator.h" +#include +#include #include -#include "third_party/spirv-tools/include/libspirv/libspirv.h" -#include "xenia/ui/spirv/spirv_assembler.h" -#include "xenia/ui/spirv/spirv_disassembler.h" -#include "xenia/ui/spirv/spirv_emitter.h" +#include "xenia/base/math.h" namespace xe { namespace gpu { +using namespace ucode; + +// The Xbox 360 GPU is effectively an Adreno A200: +// https://github.com/freedreno/freedreno/wiki/A2XX-Shader-Instruction-Set-Architecture +// +// A lot of this information is derived from the freedreno drivers, AMD's +// documentation, publicly available Xbox presentations (from GDC/etc), and +// other reverse engineering. +// +// Naming has been matched as closely as possible to the real thing by using the +// publicly available XNA Game Studio shader assembler. +// You can find a tool for exploring this under tools/shader-playground/, +// allowing interative assembling/disassembling of shader code. +// +// Though the 360's GPU is similar to the Adreno r200, the microcode format is +// slightly different. Though this is a great guide it cannot be assumed it +// matches the 360 in all areas: +// https://github.com/freedreno/freedreno/blob/master/util/disasm-a2xx.c +// +// Lots of naming comes from the disassembly spit out by the XNA GS compiler +// and dumps of d3dcompiler and games: http://pastebin.com/i4kAv7bB + TranslatedShader::TranslatedShader(ShaderType shader_type, uint64_t ucode_data_hash, - const uint32_t* ucode_words, - size_t ucode_word_count) - : shader_type_(shader_type), ucode_data_hash_(ucode_data_hash_) { - ucode_data_.resize(ucode_word_count); - std::memcpy(ucode_data_.data(), ucode_words, - ucode_word_count * sizeof(uint32_t)); + const uint32_t* ucode_dwords, + size_t ucode_dword_count, + std::vector errors) + : shader_type_(shader_type), + ucode_data_hash_(ucode_data_hash_), + errors_(std::move(errors)) { + ucode_data_.resize(ucode_dword_count); + std::memcpy(ucode_data_.data(), ucode_dwords, + ucode_dword_count * sizeof(uint32_t)); + + is_valid_ = true; + for (const auto& error : errors_) { + if (error.is_fatal) { + is_valid_ = false; + break; + } + } } TranslatedShader::~TranslatedShader() = default; -ShaderTranslator::ShaderTranslator() { - // HACK(benvanik): in-progress test code just to make sure things compile. - const std::string spirv_source = R"( -OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical Simple -OpEntryPoint Vertex %2 "main" -%3 = OpTypeVoid -%4 = OpTypeFunction %3 -%2 = OpFunction %3 None %4 -%5 = OpLabel -OpReturn -OpFunctionEnd -)"; +ShaderTranslator::ShaderTranslator() = default; - xe::ui::spirv::SpirvAssembler spirv_asm; - auto asm_result = spirv_asm.Assemble(spirv_source); - - xe::ui::spirv::SpirvDisassembler spirv_disasm; - auto disasm_result = - spirv_disasm.Disassemble(asm_result->words(), asm_result->word_count()); - - return; -} +ShaderTranslator::~ShaderTranslator() = default; std::unique_ptr ShaderTranslator::Translate( ShaderType shader_type, uint64_t ucode_data_hash, - const uint32_t* ucode_words, size_t ucode_word_count) { - auto translated_shader = std::make_unique( - shader_type, ucode_data_hash, ucode_words, ucode_word_count); + const uint32_t* ucode_dwords, size_t ucode_dword_count) { + shader_type_ = shader_type; + ucode_dwords_ = ucode_dwords; + ucode_dword_count_ = ucode_dword_count; - // run over once to generate info: - // vertex fetch - // fetch constant - // unnormalized [0-1] or [-1,1] if signed - // signed (integer formats only) - // round index (vs. floor) - // exp adjust [-32, 31] - all data multiplied by 2^(adj) - // stride words - // offset words - // texture fetch - // fetch constant - // unnormalized (0-N) or (0-1) - // alloc/export types/counts - // vs: output (16), position, point size - // ps: color (4), depth - - xe::ui::spirv::SpirvEmitter e; - auto glsl_std_450 = e.ImportExtendedInstructions("GLSL.std.450"); - auto fn = e.MakeMainEntry(); - auto float_1_0 = e.MakeFloatConstant(1.0f); - auto acos = e.CreateExtendedInstructionCall( - spv::Decoration::Invariant, e.MakeFloatType(32), glsl_std_450, - static_cast(spv::GLSLstd450::Acos), {{float_1_0}}); - e.MakeReturn(true); - - std::vector spirv_words; - e.Serialize(spirv_words); - - auto disasm_result = xe::ui::spirv::SpirvDisassembler().Disassemble( - spirv_words.data(), spirv_words.size()); + TranslateBlocks(); + std::unique_ptr translated_shader( + new TranslatedShader(shader_type, ucode_data_hash, ucode_dwords, + ucode_dword_count, std::move(errors_))); + translated_shader->binary_ = CompleteTranslation(); + translated_shader->vertex_bindings_ = std::move(vertex_bindings_); + translated_shader->texture_bindings_ = std::move(texture_bindings_); return translated_shader; } +void ShaderTranslator::MarkUcodeInstruction(uint32_t dword_offset) { + auto disasm = ucode_disasm_buffer_.GetString(); + size_t current_offset = ucode_disasm_buffer_.length(); + for (size_t i = previous_ucode_disasm_scan_offset_; i < current_offset; ++i) { + if (disasm[i] == '\n') { + ++ucode_disasm_line_number_; + } + } + previous_ucode_disasm_scan_offset_ = current_offset; +} + +void ShaderTranslator::AppendUcodeDisasm(char c) { + ucode_disasm_buffer_.Append(c); +} + +void ShaderTranslator::AppendUcodeDisasm(const char* value) { + ucode_disasm_buffer_.Append(value); +} + +void ShaderTranslator::AppendUcodeDisasmFormat(const char* format, ...) { + va_list va; + va_start(va, format); + ucode_disasm_buffer_.AppendVarargs(format, va); + va_end(va); +} + +void ShaderTranslator::EmitTranslationError(const char* message) { + TranslatedShader::Error error; + error.is_fatal = true; + error.message = message; + // TODO(benvanik): location information. + errors_.push_back(std::move(error)); +} + +void AddControlFlowTargetLabel(const ControlFlowInstruction& cf, + std::set* label_addresses) { + switch (cf.opcode()) { + case ControlFlowOpcode::kLoopStart: + label_addresses->insert(cf.loop_start.address()); + break; + case ControlFlowOpcode::kLoopEnd: + label_addresses->insert(cf.loop_end.address()); + break; + case ControlFlowOpcode::kCondCall: + label_addresses->insert(cf.cond_call.address()); + break; + case ControlFlowOpcode::kCondJmp: + label_addresses->insert(cf.cond_jmp.address()); + break; + } +} + +bool ShaderTranslator::TranslateBlocks() { + // Control flow instructions come paired in blocks of 3 dwords and all are + // listed at the top of the ucode. + // Each control flow instruction is executed sequentially until the final + // ending instruction. + + // Guess how long the control flow program is by scanning for the first + // kExec-ish and instruction and using its address as the upper bound. + // This is what freedreno does. + uint32_t max_cf_dword_index = static_cast(ucode_dword_count_); + std::set label_addresses; + 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); + } + AddControlFlowTargetLabel(cf_a, &label_addresses); + 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; + ControlFlowInstruction cf_b; + UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b); + + MarkUcodeInstruction(i); + if (label_addresses.count(cf_index)) { + AppendUcodeDisasmFormat(" label L%u\n", cf_index); + } + AppendUcodeDisasmFormat("/* %4u.0 */ ", cf_index / 2); + TranslateControlFlowInstruction(cf_a); + ++cf_index; + + MarkUcodeInstruction(i); + if (label_addresses.count(cf_index)) { + AppendUcodeDisasmFormat(" label L%u\n", cf_index); + } + AppendUcodeDisasmFormat("/* %4u.1 */ ", cf_index / 2); + TranslateControlFlowInstruction(cf_b); + ++cf_index; + } + + 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)); +} + +void ShaderTranslator::TranslateControlFlowInstruction( + const ControlFlowInstruction& cf) { + bool ends_shader = false; + switch (cf.opcode()) { + case ControlFlowOpcode::kNop: + TranslateControlFlowNop(cf); + break; + case ControlFlowOpcode::kExec: + TranslateControlFlowExec(cf.exec); + 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); + break; + case ControlFlowOpcode::kLoopEnd: + TranslateControlFlowLoopEnd(cf.loop_end); + break; + case ControlFlowOpcode::kCondCall: + TranslateControlFlowCondCall(cf.cond_call); + break; + case ControlFlowOpcode::kReturn: + TranslateControlFlowReturn(cf.ret); + break; + case ControlFlowOpcode::kCondJmp: + TranslateControlFlowCondJmp(cf.cond_jmp); + break; + case ControlFlowOpcode::kAlloc: + TranslateControlFlowAlloc(cf.alloc); + break; + case ControlFlowOpcode::kMarkVsFetchDone: + break; + default: + assert_unhandled_case(cf.opcode); + break; + } + if (ends_shader) { + // TODO(benvanik): return? + } +} + +void ShaderTranslator::TranslateControlFlowNop( + const ControlFlowInstruction& cf) { + AppendUcodeDisasm(" cnop\n"); + + ProcessControlFlowNop(cf); +} + +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'); + + ProcessControlFlowExec(cf); + + TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); +} + +void ShaderTranslator::TranslateControlFlowCondExec( + const ControlFlowCondExecInstruction& cf) { + const char* opcode_name = "cexec"; + switch (cf.opcode()) { + case ControlFlowOpcode::kCondExecEnd: + case ControlFlowOpcode::kCondExecPredCleanEnd: + 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"); + } + switch (cf.opcode()) { + case ControlFlowOpcode::kCondExec: + case ControlFlowOpcode::kCondExecEnd: + AppendUcodeDisasm(" // PredicateClean=false"); + break; + } + AppendUcodeDisasm('\n'); + + ProcessControlFlowCondExec(cf); + + TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); +} + +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'); + + ProcessControlFlowCondExecPred(cf); + + TranslateExecInstructions(cf.address(), cf.count(), cf.sequence()); +} + +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'); + + ProcessControlFlowLoopStart(cf); +} + +void ShaderTranslator::TranslateControlFlowLoopEnd( + const ControlFlowLoopEndInstruction& cf) { + AppendUcodeDisasm(cf.condition() ? " (p0) " : "(!p0) "); + AppendUcodeDisasmFormat("endloop i%u, L%u", cf.loop_id(), cf.address()); + AppendUcodeDisasm('\n'); + + ProcessControlFlowLoopEnd(cf); +} + +void ShaderTranslator::TranslateControlFlowCondCall( + const ControlFlowCondCallInstruction& cf) { + if (cf.is_unconditional()) { + AppendUcodeDisasm(" call "); + } 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()); + } + } + AppendUcodeDisasmFormat("L%u", cf.address()); + AppendUcodeDisasm('\n'); + + ProcessControlFlowCondCall(cf); +} + +void ShaderTranslator::TranslateControlFlowReturn( + const ControlFlowReturnInstruction& cf) { + AppendUcodeDisasm(" ret\n"); + + ProcessControlFlowReturn(cf); +} + +void ShaderTranslator::TranslateControlFlowCondJmp( + const ControlFlowCondJmpInstruction& cf) { + if (cf.is_unconditional()) { + AppendUcodeDisasm(" jmp "); + } 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()); + } + } + AppendUcodeDisasmFormat("L%u", cf.address()); + AppendUcodeDisasm('\n'); + + ProcessControlFlowCondJmp(cf); +} + +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'); + + ProcessControlFlowAlloc(cf); +} + +void ShaderTranslator::TranslateExecInstructions(uint32_t address, + uint32_t count, + uint32_t sequence) { + for (uint32_t instr_offset = address; instr_offset < address + count; + ++instr_offset, sequence >>= 2) { + MarkUcodeInstruction(instr_offset); + AppendUcodeDisasmFormat("/* %4u */ ", instr_offset); + bool is_sync = (sequence & 0x2) == 0x2; + bool is_fetch = (sequence & 0x1) == 0x1; + if (is_sync) { + AppendUcodeDisasm(" serialize\n "); + } + if (is_fetch) { + auto fetch_opcode = + static_cast(ucode_dwords_[instr_offset * 3] & 0x1F); + if (fetch_opcode == FetchOpcode::kVertexFetch) { + TranslateVertexFetchInstruction( + *reinterpret_cast(ucode_dwords_ + + instr_offset * 3)); + } else { + TranslateTextureFetchInstruction( + *reinterpret_cast( + ucode_dwords_ + instr_offset * 3)); + } + } else { + TranslateAluInstruction(*reinterpret_cast( + ucode_dwords_ + instr_offset * 3)); + } + } +} + +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 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); + } +} + +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'); + + if (!op.is_mini_fetch()) { + previous_vfetch_full_ = op; + } + + ProcessVertexFetchInstruction(op); +} + +void ShaderTranslator::TranslateTextureFetchInstruction( + const TextureFetchInstruction& op) { + struct TextureFetchOpcodeInfo { + const char* base_name; + bool name_has_dimension; + 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::kGetTextureGradients: + opcode_info = {"getGradients", false, true, true, true, true, 2}; + break; + case FetchOpcode::kGetTextureWeights: + opcode_info = {"getWeights", true, true, true, true, true, 0}; + break; + case FetchOpcode::kSetTextureLod: + opcode_info = {"setTexLOD", false, false, true, false, false, 1}; + break; + case FetchOpcode::kSetTextureGradientsHorz: + opcode_info = {"setGradientH", false, false, true, false, false, 3}; + break; + case FetchOpcode::kSetTextureGradientsVert: + opcode_info = {"setGradientV", false, false, true, 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; + if (opcode_info.has_dest) { + DisasmFetchDestReg(op.dest(), op.dest_swizzle(), op.is_dest_relative()); + needs_comma = true; + } + 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())); + } + if (opcode_info.has_const) { + if (needs_comma) { + AppendUcodeDisasm(", "); + } + AppendUcodeDisasmFormat("tf%u", 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()); + } +} + +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 +}; + +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 +}; + +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]; + } + 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"; + } + } + return "???"; +} + +void ShaderTranslator::DisasmAluSourceReg(const AluInstruction& op, int i, + int swizzle_component_count) { + int const_slot = 0; + switch (i) { + case 2: + const_slot = op.src_is_temp(1) ? 0 : 1; + break; + case 3: + const_slot = op.src_is_temp(1) && op.src_is_temp(2) ? 0 : 1; + break; + } + if (op.src_negate(i)) { + AppendUcodeDisasm('-'); + } + uint32_t reg = op.src_reg(i); + if (op.src_is_temp(i)) { + AppendUcodeDisasm(kTemporaryNames[reg & 0x7F]); + if (reg & 0x80) { + AppendUcodeDisasm("_abs"); + } + } else { + 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); + } else { + AppendUcodeDisasmFormat("c[%u+aL]", reg); + } + } else { + AppendUcodeDisasmFormat("c%u", reg); + } + if (op.abs_constants()) { + AppendUcodeDisasm("_abs"); + } + } + 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]); + } + } + } +} + +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( + 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; + } + + AppendUcodeDisasmFormat(opcode_info.name); + if (op.vector_clamp()) { + AppendUcodeDisasm("_sat"); + } + 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 & 0x1) { + if (const_1_mask & 0x1) { + AppendUcodeDisasm('1'); + } else { + AppendUcodeDisasm(kXyzwChannelNames[i]); + } + } 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]); + } + } + } + } + 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); + } + + AppendUcodeDisasm('\n'); +} + +void ShaderTranslator::DisasmAluScalarInstruction( + 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(' '); + + uint32_t dest_num; + uint32_t write_mask; + if (op.is_export()) { + dest_num = op.vector_dest(); + write_mask = op.scalar_write_mask() & ~op.vector_write_mask(); + } else { + 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]); + } + } + } + AppendUcodeDisasm(", "); + + if (opcode_info.argument_count == 1) { + DisasmAluSourceReg(op, 3, opcode_info.src_swizzle_component_count); + } else { + uint32_t src3_swizzle = op.src_swizzle(3); + uint32_t swiz_a = ((src3_swizzle >> 6) - 1) & 0x3; + uint32_t swiz_b = (src3_swizzle & 0x3); + 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); + } + + AppendUcodeDisasm('\n'); +} + +std::vector UcodeShaderTranslator::CompleteTranslation() { + return ucode_disasm_buffer().ToBytes(); +} + } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/shader_translator.h b/src/xenia/gpu/shader_translator.h index dd555f442..a9248081e 100644 --- a/src/xenia/gpu/shader_translator.h +++ b/src/xenia/gpu/shader_translator.h @@ -11,44 +11,316 @@ #define XENIA_GPU_SHADER_TRANSLATOR_H_ #include +#include #include +#include "xenia/base/string_buffer.h" +#include "xenia/gpu/ucode.h" #include "xenia/gpu/xenos.h" -#include "xenia/ui/spirv/spirv_util.h" namespace xe { namespace gpu { - class TranslatedShader { public: - TranslatedShader(ShaderType shader_type, uint64_t ucode_data_hash, - const uint32_t* ucode_words, size_t ucode_word_count); + struct Error { + bool is_fatal = false; + std::string message; + }; + + struct VertexBinding { + // Index within the vertex binding listing. + size_t binding_index; + // Fetch constant index [0-95]. + uint32_t fetch_constant; + // Fetch instruction with all parameters. + ucode::VertexFetchInstruction op; + }; + 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; + }; + ~TranslatedShader(); ShaderType type() const { return shader_type_; } - const uint32_t* ucode_words() const { return ucode_data_.data(); } - size_t ucode_word_count() const { return ucode_data_.size(); } + const std::vector& ucode_data() const { return ucode_data_; } + const uint32_t* ucode_dwords() const { return ucode_data_.data(); } + size_t ucode_dword_count() const { return ucode_data_.size(); } + + const std::vector& vertex_bindings() const { + return vertex_bindings_; + } + const std::vector& texture_bindings() const { + return texture_bindings_; + } bool is_valid() const { return is_valid_; } + const std::vector errors() const { return errors_; } + + const std::vector binary() const { return binary_; } private: + friend class ShaderTranslator; + + TranslatedShader(ShaderType shader_type, uint64_t ucode_data_hash, + const uint32_t* ucode_dwords, size_t ucode_dword_count, + std::vector errors); + ShaderType shader_type_; std::vector ucode_data_; uint64_t ucode_data_hash_; + std::vector vertex_bindings_; + std::vector texture_bindings_; + bool is_valid_ = false; + std::vector errors_; + + std::vector binary_; }; class ShaderTranslator { public: - ShaderTranslator(); + virtual ~ShaderTranslator(); std::unique_ptr Translate(ShaderType shader_type, uint64_t ucode_data_hash, - const uint32_t* ucode_words, - size_t ucode_word_count); + const uint32_t* ucode_dwords, + size_t ucode_dword_count); + + protected: + ShaderTranslator(); + + size_t ucode_disasm_line_number() const { return ucode_disasm_line_number_; } + StringBuffer& ucode_disasm_buffer() { return ucode_disasm_buffer_; } + void EmitTranslationError(const char* message); + + 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) {} + + 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) {} 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); + void AppendUcodeDisasm(char c); + void AppendUcodeDisasm(const char* value); + void AppendUcodeDisasmFormat(const char* format, ...); + + bool TranslateBlocks(); + void GatherBindingInformation(const ucode::ControlFlowInstruction& cf); + void GatherVertexBindingInformation(const ucode::VertexFetchInstruction& op); + void GatherTextureBindingInformation( + const ucode::TextureFetchInstruction& op); + void TranslateControlFlowInstruction(const ucode::ControlFlowInstruction& cf); + void TranslateControlFlowNop(const ucode::ControlFlowInstruction& cf); + void TranslateControlFlowExec(const ucode::ControlFlowExecInstruction& cf); + void TranslateControlFlowCondExec( + const ucode::ControlFlowCondExecInstruction& cf); + void TranslateControlFlowCondExecPred( + const ucode::ControlFlowCondExecPredInstruction& cf); + void TranslateControlFlowLoopStart( + const ucode::ControlFlowLoopStartInstruction& cf); + void TranslateControlFlowLoopEnd( + const ucode::ControlFlowLoopEndInstruction& cf); + void TranslateControlFlowCondCall( + const ucode::ControlFlowCondCallInstruction& cf); + void TranslateControlFlowReturn( + const ucode::ControlFlowReturnInstruction& cf); + void TranslateControlFlowCondJmp( + const ucode::ControlFlowCondJmpInstruction& cf); + void TranslateControlFlowAlloc(const ucode::ControlFlowAllocInstruction& cf); + + void TranslateExecInstructions(uint32_t address, uint32_t count, + uint32_t sequence); + + 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 TranslateTextureFetchInstruction( + const ucode::TextureFetchInstruction& op); + void DisasmVertexFetchAttributes(const ucode::VertexFetchInstruction& op); + void DisasmTextureFetchAttributes(const ucode::TextureFetchInstruction& op); + + 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); + + // Input shader metadata and microcode. + ShaderType shader_type_; + const uint32_t* ucode_dwords_; + size_t ucode_dword_count_; + + // Accumulated translation errors. + std::vector errors_; + + // Microcode disassembly buffer, accumulated throughout the translation. + StringBuffer ucode_disasm_buffer_; + // Current line number in the disasm, which can be used for source annotation. + size_t ucode_disasm_line_number_ = 0; + // Last offset used when scanning for line numbers. + size_t previous_ucode_disasm_scan_offset_ = 0; + + // Kept for supporting vfetch_mini. + ucode::VertexFetchInstruction previous_vfetch_full_; + + // Detected binding information gathered before translation. + std::vector vertex_bindings_; + std::vector texture_bindings_; + + static const AluOpcodeInfo alu_vector_opcode_infos_[0x20]; + static const AluOpcodeInfo alu_scalar_opcode_infos_[0x40]; +}; + +class UcodeShaderTranslator : public ShaderTranslator { + public: + UcodeShaderTranslator() = default; + + protected: + std::vector CompleteTranslation() override; }; } // namespace gpu diff --git a/src/xenia/gpu/ucode.h b/src/xenia/gpu/ucode.h index ff38b1d2a..89a7c82a0 100644 --- a/src/xenia/gpu/ucode.h +++ b/src/xenia/gpu/ucode.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2015 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -12,27 +12,9 @@ #include +#include "xenia/base/assert.h" #include "xenia/base/platform.h" - -namespace xe { -namespace gpu { - -namespace ucode { - -#if XE_COMPILER_MSVC -#define XEPACKEDSTRUCT(name, value) \ - __pragma(pack(push, 1)) struct name##_s value __pragma(pack(pop)); \ - typedef struct name##_s name; -#define XEPACKEDSTRUCTANONYMOUS(value) \ - __pragma(pack(push, 1)) struct value __pragma(pack(pop)); -#define XEPACKEDUNION(name, value) \ - __pragma(pack(push, 1)) union name##_s value __pragma(pack(pop)); \ - typedef union name##_s name; -#else -#define XEPACKEDSTRUCT(name, value) struct __attribute__((packed)) name value; -#define XEPACKEDSTRUCTANONYMOUS(value) struct __attribute__((packed)) value; -#define XEPACKEDUNION(name, value) union __attribute__((packed)) name value; -#endif // XE_PLATFORM_WIN32 +#include "xenia/gpu/xenos.h" // Closest AMD doc: // http://developer.amd.com/wordpress/media/2012/10/R600_Instruction_Set_Architecture.pdf @@ -63,499 +45,871 @@ namespace ucode { * SOFTWARE. */ -enum a2xx_sq_surfaceformat { - FMT_1_REVERSE = 0, - FMT_1 = 1, - FMT_8 = 2, - FMT_1_5_5_5 = 3, - FMT_5_6_5 = 4, - FMT_6_5_5 = 5, - FMT_8_8_8_8 = 6, - FMT_2_10_10_10 = 7, - FMT_8_A = 8, - FMT_8_B = 9, - FMT_8_8 = 10, - FMT_Cr_Y1_Cb_Y0 = 11, - FMT_Y1_Cr_Y0_Cb = 12, - FMT_5_5_5_1 = 13, - FMT_8_8_8_8_A = 14, - FMT_4_4_4_4 = 15, - FMT_10_11_11 = 16, - FMT_11_11_10 = 17, - FMT_DXT1 = 18, - FMT_DXT2_3 = 19, - FMT_DXT4_5 = 20, - FMT_24_8 = 22, - FMT_24_8_FLOAT = 23, - FMT_16 = 24, - FMT_16_16 = 25, - FMT_16_16_16_16 = 26, - FMT_16_EXPAND = 27, - FMT_16_16_EXPAND = 28, - FMT_16_16_16_16_EXPAND = 29, - FMT_16_FLOAT = 30, - FMT_16_16_FLOAT = 31, - FMT_16_16_16_16_FLOAT = 32, - FMT_32 = 33, - FMT_32_32 = 34, - FMT_32_32_32_32 = 35, - FMT_32_FLOAT = 36, - FMT_32_32_FLOAT = 37, - FMT_32_32_32_32_FLOAT = 38, - FMT_32_AS_8 = 39, - FMT_32_AS_8_8 = 40, - FMT_16_MPEG = 41, - FMT_16_16_MPEG = 42, - FMT_8_INTERLACED = 43, - FMT_32_AS_8_INTERLACED = 44, - FMT_32_AS_8_8_INTERLACED = 45, - FMT_16_INTERLACED = 46, - FMT_16_MPEG_INTERLACED = 47, - FMT_16_16_MPEG_INTERLACED = 48, - FMT_DXN = 49, - FMT_8_8_8_8_AS_16_16_16_16 = 50, - FMT_DXT1_AS_16_16_16_16 = 51, - FMT_DXT2_3_AS_16_16_16_16 = 52, - FMT_DXT4_5_AS_16_16_16_16 = 53, - FMT_2_10_10_10_AS_16_16_16_16 = 54, - FMT_10_11_11_AS_16_16_16_16 = 55, - FMT_11_11_10_AS_16_16_16_16 = 56, - FMT_32_32_32_FLOAT = 57, - FMT_DXT3A = 58, - FMT_DXT5A = 59, - FMT_CTX1 = 60, - FMT_DXT3A_AS_1_1_1_1 = 61, +namespace xe { +namespace gpu { +namespace ucode { + +// Defines control flow opcodes used to schedule instructions. +enum class ControlFlowOpcode : uint32_t { + // No-op - used to fill space. + kNop = 0, + // Executes fetch or ALU instructions. + kExec = 1, + // Executes fetch or ALU instructions then ends execution. + kExecEnd = 2, + // Conditionally executes based on a bool const. + kCondExec = 3, + // Conditionally executes based on a bool const then ends execution. + kCondExecEnd = 4, + // Conditionally executes based on the current predicate. + kCondExecPred = 5, + // Conditionally executes based on the current predicate then ends execution. + kCondExecPredEnd = 6, + // Starts a loop that must be terminated with kLoopEnd. + kLoopStart = 7, + // Continues or breaks out of a loop started with kLoopStart. + kLoopEnd = 8, + // Conditionally calls a function. + // A return address is pushed to the stack to be used by a kReturn. + kCondCall = 9, + // Returns from the current function as called by kCondCall. + // This is a no-op if not in a function. + kReturn = 10, + // Conditionally jumps to an arbitrary address based on a bool const. + kCondJmp = 11, + // Allocates output values. + kAlloc = 12, + // Conditionally executes based on the current predicate. + // Optionally resets the predicate value. + kCondExecPredClean = 13, + // Conditionally executes based on the current predicate then ends execution. + // Optionally resets the predicate value. + kCondExecPredCleanEnd = 14, + // Hints that no more vertex fetches will be performed. + kMarkVsFetchDone = 15, }; -/* - * ALU instructions: - */ +// Returns true if the given control flow opcode executes ALU or fetch +// instructions. +constexpr bool IsControlFlowOpcodeExec(ControlFlowOpcode opcode) { + return opcode == ControlFlowOpcode::kExec || + opcode == ControlFlowOpcode::kExecEnd || + opcode == ControlFlowOpcode::kCondExec || + opcode == ControlFlowOpcode::kCondExecEnd || + opcode == ControlFlowOpcode::kCondExecPred || + opcode == ControlFlowOpcode::kCondExecPredEnd || + opcode == ControlFlowOpcode::kCondExecPredClean || + opcode == ControlFlowOpcode::kCondExecPredCleanEnd; +} -typedef enum { - ADDs = 0, - ADD_PREVs = 1, - MULs = 2, - MUL_PREVs = 3, - MUL_PREV2s = 4, - MAXs = 5, - MINs = 6, - SETEs = 7, - SETGTs = 8, - SETGTEs = 9, - SETNEs = 10, - FRACs = 11, - TRUNCs = 12, - FLOORs = 13, - EXP_IEEE = 14, - LOG_CLAMP = 15, - LOG_IEEE = 16, - RECIP_CLAMP = 17, - RECIP_FF = 18, - RECIP_IEEE = 19, - RECIPSQ_CLAMP = 20, - RECIPSQ_FF = 21, - RECIPSQ_IEEE = 22, - MOVAs = 23, - MOVA_FLOORs = 24, - SUBs = 25, - SUB_PREVs = 26, - PRED_SETEs = 27, - PRED_SETNEs = 28, - PRED_SETGTs = 29, - PRED_SETGTEs = 30, - PRED_SET_INVs = 31, - PRED_SET_POPs = 32, - PRED_SET_CLRs = 33, - PRED_SET_RESTOREs = 34, - KILLEs = 35, - KILLGTs = 36, - KILLGTEs = 37, - KILLNEs = 38, - KILLONEs = 39, - SQRT_IEEE = 40, - MUL_CONST_0 = 42, - MUL_CONST_1 = 43, - ADD_CONST_0 = 44, - ADD_CONST_1 = 45, - SUB_CONST_0 = 46, - SUB_CONST_1 = 47, - SIN = 48, - COS = 49, - RETAIN_PREV = 50, -} instr_scalar_opc_t; +// Determines whether addressing is based on a0 or aL. +enum class AddressingMode : uint32_t { + // Indexing into register sets is done based on aL. + // This allows forms like c[aL + 5]. + kRelative = 0, + // Indexing into register sets is done based on a0. + // This allows forms like c[a0 + 5]. + kAbsolute = 1, +}; -typedef enum { - ADDv = 0, - MULv = 1, - MAXv = 2, - MINv = 3, - SETEv = 4, - SETGTv = 5, - SETGTEv = 6, - SETNEv = 7, - FRACv = 8, - TRUNCv = 9, - FLOORv = 10, - MULADDv = 11, - CNDEv = 12, - CNDGTEv = 13, - CNDGTv = 14, - DOT4v = 15, - DOT3v = 16, - DOT2ADDv = 17, - CUBEv = 18, - MAX4v = 19, - PRED_SETE_PUSHv = 20, - PRED_SETNE_PUSHv = 21, - PRED_SETGT_PUSHv = 22, - PRED_SETGTE_PUSHv = 23, - KILLEv = 24, - KILLGTv = 25, - KILLGTEv = 26, - KILLNEv = 27, - DSTv = 28, - MOVAv = 29, -} instr_vector_opc_t; +// Defines the type of a ControlFlowOpcode::kAlloc instruction. +// The allocation is just a size reservation and there may be multiple in a +// shader. +enum class AllocType : uint32_t { + // ? + kNoAlloc = 0, + // Vertex shader exports a position. + kVsPosition = 1, + // Vertex shader exports interpolators. + kVsInterpolators = 2, + // Pixel shader exports colors. + kPsColors = 2, + // MEMEXPORT? + kMemory = 3, +}; -XEPACKEDSTRUCT(instr_alu_t, { - /* dword0: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t vector_dest : 6; - uint32_t vector_dest_rel : 1; - uint32_t abs_constants : 1; - uint32_t scalar_dest : 6; - uint32_t scalar_dest_rel : 1; - uint32_t export_data : 1; - uint32_t vector_write_mask : 4; - uint32_t scalar_write_mask : 4; - uint32_t vector_clamp : 1; - uint32_t scalar_clamp : 1; - uint32_t scalar_opc : 6; // instr_scalar_opc_t - }); - /* dword1: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t src3_swiz : 8; - uint32_t src2_swiz : 8; - uint32_t src1_swiz : 8; - uint32_t src3_reg_negate : 1; - uint32_t src2_reg_negate : 1; - uint32_t src1_reg_negate : 1; - uint32_t pred_condition : 1; - uint32_t pred_select : 1; - uint32_t relative_addr : 1; - uint32_t const_1_rel_abs : 1; - uint32_t const_0_rel_abs : 1; - }); - /* dword2: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t src3_reg : 8; - uint32_t src2_reg : 8; - uint32_t src1_reg : 8; - uint32_t vector_opc : 5; // instr_vector_opc_t - uint32_t src3_sel : 1; - uint32_t src2_sel : 1; - uint32_t src1_sel : 1; - }); -}); - -/* - * CF instructions: - */ - -typedef enum { - NOP = 0, - EXEC = 1, - EXEC_END = 2, - COND_EXEC = 3, - COND_EXEC_END = 4, - COND_PRED_EXEC = 5, - COND_PRED_EXEC_END = 6, - LOOP_START = 7, - LOOP_END = 8, - COND_CALL = 9, - RETURN = 10, - COND_JMP = 11, - ALLOC = 12, - COND_EXEC_PRED_CLEAN = 13, - COND_EXEC_PRED_CLEAN_END = 14, - MARK_VS_FETCH_DONE = 15, -} instr_cf_opc_t; - -typedef enum { - RELATIVE_ADDR = 0, - ABSOLUTE_ADDR = 1, -} instr_addr_mode_t; - -typedef enum { - SQ_NO_ALLOC = 0, - SQ_POSITION = 1, - SQ_PARAMETER_PIXEL = 2, - SQ_MEMORY = 3, -} instr_alloc_type_t; - -XEPACKEDSTRUCT(instr_cf_exec_t, { - XEPACKEDSTRUCTANONYMOUS({ - uint32_t address : 12; - uint32_t count : 3; - uint32_t yeild : 1; - uint32_t serialize : 12; - uint32_t vc_hi : 4; - }); - XEPACKEDSTRUCTANONYMOUS({ - uint32_t vc_lo : 2; /* vertex cache? */ - uint32_t bool_addr : 8; - uint32_t pred_condition : 1; - uint32_t address_mode : 1; // instr_addr_mode_t - uint32_t opc : 4; // instr_cf_opc_t - }); - bool is_cond_exec() const { - return (this->opc == COND_EXEC) || (this->opc == COND_EXEC_END) || - (this->opc == COND_PRED_EXEC) || (this->opc == COND_PRED_EXEC_END) || - (this->opc == COND_EXEC_PRED_CLEAN) || - (this->opc == COND_EXEC_PRED_CLEAN_END); +// Instruction data for ControlFlowOpcode::kExec and kExecEnd. +struct ControlFlowExecInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); } -}); + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Address of the instructions to execute. + uint32_t address() const { return data_.address; } + // Number of instructions being executed. + uint32_t count() const { return data_.count; } + // Sequence bits, 2 per instruction, indicating whether ALU or fetch. + uint32_t sequence() const { return data_.serialize; } + // Whether to reset the current predicate. + bool clean() const { return data_.clean == 1; } + // ? + bool is_yield() const { return data_.is_yeild == 1; } -XEPACKEDSTRUCT(instr_cf_loop_t, { - XEPACKEDSTRUCTANONYMOUS({ - uint32_t address : 13; - uint32_t repeat : 1; - uint32_t reserved0 : 2; - uint32_t loop_id : 5; - uint32_t pred_break : 1; - uint32_t reserved1_hi : 10; + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 12; + uint32_t count : 3; + uint32_t is_yeild : 1; + uint32_t serialize : 12; + uint32_t vc_hi : 4; // Vertex cache? + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t vc_lo : 2; + uint32_t unused_0 : 7; + uint32_t clean : 1; + uint32_t unused_1 : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); }); - XEPACKEDSTRUCTANONYMOUS({ - uint32_t reserved1_lo : 10; - uint32_t condition : 1; - uint32_t address_mode : 1; // instr_addr_mode_t - uint32_t opc : 4; // instr_cf_opc_t - }); -}); + Data data_; +}; +static_assert_size(ControlFlowExecInstruction, 8); -XEPACKEDSTRUCT(instr_cf_jmp_call_t, { - XEPACKEDSTRUCTANONYMOUS({ - uint32_t address : 13; - uint32_t force_call : 1; - uint32_t predicated_jmp : 1; - uint32_t reserved1_hi : 17; - }); - XEPACKEDSTRUCTANONYMOUS({ - uint32_t reserved1_lo : 1; - uint32_t direction : 1; - uint32_t bool_addr : 8; - uint32_t condition : 1; - uint32_t address_mode : 1; // instr_addr_mode_t - uint32_t opc : 4; // instr_cf_opc_t - }); -}); +// Instruction data for ControlFlowOpcode::kCondExec and kCondExecEnd. +struct ControlFlowCondExecInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Address of the instructions to execute. + uint32_t address() const { return data_.address; } + // Number of instructions being executed. + uint32_t count() const { return data_.count; } + // Sequence bits, 2 per instruction, indicating whether ALU or fetch. + uint32_t sequence() const { return data_.serialize; } + // Constant index used as the conditional. + uint32_t bool_address() const { return data_.bool_address; } + // Required condition value of the comparision (true or false). + bool condition() const { return data_.condition == 1; } + // ? + bool is_yield() const { return data_.is_yeild == 1; } -XEPACKEDSTRUCT(instr_cf_alloc_t, { - XEPACKEDSTRUCTANONYMOUS({ - uint32_t size : 3; - uint32_t reserved0_hi : 29; + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 12; + uint32_t count : 3; + uint32_t is_yeild : 1; + uint32_t serialize : 12; + uint32_t vc_hi : 4; // Vertex cache? + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t vc_lo : 2; + uint32_t bool_address : 8; + uint32_t condition : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); }); - XEPACKEDSTRUCTANONYMOUS({ - uint32_t reserved0_lo : 8; - uint32_t no_serial : 1; - uint32_t buffer_select : 2; // instr_alloc_type_t - uint32_t alloc_mode : 1; - uint32_t opc : 4; // instr_cf_opc_t - }); -}); + Data data_; +}; +static_assert_size(ControlFlowCondExecInstruction, 8); + +// Instruction data for ControlFlowOpcode::kCondExecPred, kCondExecPredEnd, +// kCondExecPredClean, kCondExecPredCleanEnd. +struct ControlFlowCondExecPredInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Address of the instructions to execute. + uint32_t address() const { return data_.address; } + // Number of instructions being executed. + uint32_t count() const { return data_.count; } + // Sequence bits, 2 per instruction, indicating whether ALU or fetch. + uint32_t sequence() const { return data_.serialize; } + // Whether to reset the current predicate. + bool clean() const { return data_.clean == 1; } + // Required condition value of the comparision (true or false). + bool condition() const { return data_.condition == 1; } + // ? + bool is_yield() const { return data_.is_yeild == 1; } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 12; + uint32_t count : 3; + uint32_t is_yeild : 1; + uint32_t serialize : 12; + uint32_t vc_hi : 4; // Vertex cache? + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t vc_lo : 2; + uint32_t unused_0 : 7; + uint32_t clean : 1; + uint32_t condition : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowCondExecPredInstruction, 8); + +// Instruction data for ControlFlowOpcode::kLoopStart. +struct ControlFlowLoopStartInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Target address to jump to when skipping the loop. + uint32_t address() const { return data_.address; } + // Whether to reuse the current aL instead of reset it to loop start. + bool is_repeat() const { return data_.is_repeat; } + // Integer constant register that holds the loop parameters. + // Byte-wise: [loop count, start, step [-128, 127], ?] + uint32_t loop_id() const { return data_.loop_id; } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 13; + uint32_t is_repeat : 1; + uint32_t unused_0 : 2; + uint32_t loop_id : 5; + uint32_t unused_1 : 11; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_2 : 11; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowLoopStartInstruction, 8); + +// Instruction data for ControlFlowOpcode::kLoopEnd. +struct ControlFlowLoopEndInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Target address of the start of the loop body. + uint32_t address() const { return data_.address; } + // Integer constant register that holds the loop parameters. + // Byte-wise: [loop count, start, step [-128, 127], ?] + uint32_t loop_id() const { return data_.loop_id; } + // Break from the loop if the predicate matches the expected value. + bool is_predicated_break() const { return data_.is_predicated_break; } + // Required condition value of the comparision (true or false). + bool condition() const { return data_.condition == 1; } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 13; + uint32_t unused_0 : 3; + uint32_t loop_id : 5; + uint32_t is_predicated_break : 1; + uint32_t unused_1 : 10; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_2 : 10; + uint32_t condition : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowLoopEndInstruction, 8); + +// Instruction data for ControlFlowOpcode::kCondCall. +struct ControlFlowCondCallInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Target address. + uint32_t address() const { return data_.address; } + // Unconditional call - ignores condition/predication. + bool is_unconditional() const { return data_.is_unconditional; } + // Whether the call is predicated (or conditional). + bool is_predicated() const { return data_.is_predicated; } + // Constant index used as the conditional. + uint32_t bool_address() const { return data_.bool_address; } + // Required condition value of the comparision (true or false). + bool condition() const { return data_.condition == 1; } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 13; + uint32_t is_unconditional : 1; + uint32_t is_predicated : 1; + uint32_t unused_0 : 17; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_1 : 2; + uint32_t bool_address : 8; + uint32_t condition : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowCondCallInstruction, 8); + +// Instruction data for ControlFlowOpcode::kReturn. +struct ControlFlowReturnInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + + private: + XEPACKEDSTRUCT(Data, { + uint32_t unused_0; + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_1 : 11; + AddressingMode address_mode : 1; + ControlFlowOpcode opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowReturnInstruction, 8); + +// Instruction data for ControlFlowOpcode::kCondJmp. +struct ControlFlowCondJmpInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + AddressingMode addressing_mode() const { + return static_cast(data_.address_mode); + } + // Target address. + uint32_t address() const { return data_.address; } + // Unconditional jump - ignores condition/predication. + bool is_unconditional() const { return data_.is_unconditional; } + // Whether the jump is predicated (or conditional). + bool is_predicated() const { return data_.is_predicated; } + // Constant index used as the conditional. + uint32_t bool_address() const { return data_.bool_address; } + // Required condition value of the comparision (true or false). + bool condition() const { return data_.condition == 1; } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t address : 13; + uint32_t is_unconditional : 1; + uint32_t is_predicated : 1; + uint32_t unused_0 : 17; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_1 : 1; + uint32_t direction : 1; + uint32_t bool_address : 8; + uint32_t condition : 1; + uint32_t address_mode : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowCondJmpInstruction, 8); + +// Instruction data for ControlFlowOpcode::kAlloc. +struct ControlFlowAllocInstruction { + ControlFlowOpcode opcode() const { + return static_cast(data_.opcode); + } + // The total number of the given type allocated by this instruction. + uint32_t size() const { return data_.size; } + // Unconditional jump - ignores condition/predication. + AllocType alloc_type() const { + return static_cast(data_.alloc_type); + } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t size : 3; + uint32_t unused_0 : 29; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t unused_1 : 8; + uint32_t is_unserialized : 1; + uint32_t alloc_type : 2; + uint32_t unused_2 : 1; + uint32_t opcode : 4; + }); + }); + Data data_; +}; +static_assert_size(ControlFlowAllocInstruction, 8); + +XEPACKEDUNION(ControlFlowInstruction, { + ControlFlowOpcode opcode() const { + return static_cast(opcode_value); + } + + ControlFlowExecInstruction exec; // kExec* + ControlFlowCondExecInstruction cond_exec; // kCondExec* + ControlFlowCondExecPredInstruction cond_exec_pred; // kCondExecPred* + ControlFlowLoopStartInstruction loop_start; // kLoopStart + ControlFlowLoopEndInstruction loop_end; // kLoopEnd + ControlFlowCondCallInstruction cond_call; // kCondCall + ControlFlowReturnInstruction ret; // kReturn + ControlFlowCondJmpInstruction cond_jmp; // kCondJmp + ControlFlowAllocInstruction alloc; // kAlloc -XEPACKEDUNION(instr_cf_t, { - instr_cf_exec_t exec; - instr_cf_loop_t loop; - instr_cf_jmp_call_t jmp_call; - instr_cf_alloc_t alloc; XEPACKEDSTRUCTANONYMOUS({ - uint32_t: - 32; - uint32_t: - 12; - uint32_t opc : 4; // instr_cf_opc_t + uint32_t unused_0 : 32; + uint32_t unused_1 : 12; + uint32_t opcode_value : 4; }); XEPACKEDSTRUCTANONYMOUS({ uint32_t dword_0; uint32_t dword_1; }); +}); +static_assert_size(ControlFlowInstruction, 8); - bool is_exec() const { - return (this->opc == EXEC) || (this->opc == EXEC_END) || - (this->opc == COND_EXEC) || (this->opc == COND_EXEC_END) || - (this->opc == COND_PRED_EXEC) || (this->opc == COND_PRED_EXEC_END) || - (this->opc == COND_EXEC_PRED_CLEAN) || - (this->opc == COND_EXEC_PRED_CLEAN_END); +inline void UnpackControlFlowInstructions(const uint32_t* dwords, + ControlFlowInstruction* out_a, + ControlFlowInstruction* out_b) { + uint32_t dword_0 = dwords[0]; + uint32_t dword_1 = dwords[1]; + uint32_t dword_2 = dwords[2]; + out_a->dword_0 = dword_0; + out_a->dword_1 = dword_1 & 0xFFFF; + out_b->dword_0 = (dword_1 >> 16) | (dword_2 << 16); + out_b->dword_1 = dword_2 >> 16; +} + +enum class FetchOpcode { + kVertexFetch = 0, + kTextureFetch = 1, + kGetTextureBorderColorFrac = 16, + kGetTextureComputedLod = 17, + kGetTextureGradients = 18, + kGetTextureWeights = 19, + kSetTextureLod = 24, + kSetTextureGradientsHorz = 25, + kSetTextureGradientsVert = 26, + kUnknownTextureOp = 27, +}; + +struct VertexFetchInstruction { + FetchOpcode opcode() const { + return static_cast(data_.opcode_value); } - bool is_cond_exec() const { - return (this->opc == COND_EXEC) || (this->opc == COND_EXEC_END) || - (this->opc == COND_PRED_EXEC) || (this->opc == COND_PRED_EXEC_END) || - (this->opc == COND_EXEC_PRED_CLEAN) || - (this->opc == COND_EXEC_PRED_CLEAN_END); + + // Whether the jump is predicated (or conditional). + bool is_predicated() const { return data_.is_predicated; } + // Required condition value of the comparision (true or false). + bool predicate_condition() const { return data_.pred_condition == 1; } + // Vertex fetch constant index [0-95]. + uint32_t fetch_constant_index() const { + return data_.const_index * 3 + data_.const_index_sel; } -}); -/* - * FETCH instructions: - */ + uint32_t dest() const { return data_.dst_reg; } + uint32_t dest_swizzle() const { return data_.dst_swiz; } + bool is_dest_relative() const { return data_.dst_reg_am; } + uint32_t src() const { return data_.src_reg; } + uint32_t src_swizzle() const { return data_.src_swiz; } + bool is_src_relative() const { return data_.src_reg_am; } -typedef enum { - VTX_FETCH = 0, - TEX_FETCH = 1, - TEX_GET_BORDER_COLOR_FRAC = 16, - TEX_GET_COMP_TEX_LOD = 17, - TEX_GET_GRADIENTS = 18, - TEX_GET_WEIGHTS = 19, - TEX_SET_TEX_LOD = 24, - TEX_SET_GRADIENTS_H = 25, - TEX_SET_GRADIENTS_V = 26, - TEX_RESERVED_4 = 27, -} instr_fetch_opc_t; + uint32_t prefetch_count() const { return data_.prefetch_count; } + bool is_mini_fetch() const { return data_.is_mini_fetch == 1; } -typedef enum { - TEX_FILTER_POINT = 0, - TEX_FILTER_LINEAR = 1, - TEX_FILTER_BASEMAP = 2, /* only applicable for mip-filter */ - TEX_FILTER_USE_FETCH_CONST = 3, -} instr_tex_filter_t; + VertexFormat data_format() const { + return static_cast(data_.format); + } + // [-32, 31] + int exp_adjust() const { + return ((static_cast(data_.exp_adjust) << 26) >> 26); + } + bool is_signed() const { return data_.is_signed == 1; } + bool is_integer() const { return data_.is_integer == 1; } + bool is_index_rounded() const { return data_.is_index_rounded == 1; } + // Dword stride, [0-255]. + uint32_t stride() const { return data_.stride; } + // Dword offset, [ + uint32_t offset() const { return data_.offset; } -typedef enum { - ANISO_FILTER_DISABLED = 0, - ANISO_FILTER_MAX_1_1 = 1, - ANISO_FILTER_MAX_2_1 = 2, - ANISO_FILTER_MAX_4_1 = 3, - ANISO_FILTER_MAX_8_1 = 4, - ANISO_FILTER_MAX_16_1 = 5, - ANISO_FILTER_USE_FETCH_CONST = 7, -} instr_aniso_filter_t; + void AssignFromFull(const VertexFetchInstruction& full) { + data_.stride = full.data_.stride; + data_.const_index = full.data_.const_index; + data_.const_index_sel = full.data_.const_index_sel; + } -typedef enum { - ARBITRARY_FILTER_2X4_SYM = 0, - ARBITRARY_FILTER_2X4_ASYM = 1, - ARBITRARY_FILTER_4X2_SYM = 2, - ARBITRARY_FILTER_4X2_ASYM = 3, - ARBITRARY_FILTER_4X4_SYM = 4, - ARBITRARY_FILTER_4X4_ASYM = 5, - ARBITRARY_FILTER_USE_FETCH_CONST = 7, -} instr_arbitrary_filter_t; - -typedef enum { - SAMPLE_CENTROID = 0, - SAMPLE_CENTER = 1, -} instr_sample_loc_t; - -typedef enum { - DIMENSION_1D = 0, - DIMENSION_2D = 1, - DIMENSION_3D = 2, - DIMENSION_CUBE = 3, -} instr_dimension_t; - -typedef enum a2xx_sq_surfaceformat instr_surf_fmt_t; - -XEPACKEDSTRUCT(instr_fetch_tex_t, { - /* dword0: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t opc : 5; // instr_fetch_opc_t - uint32_t src_reg : 6; - uint32_t src_reg_am : 1; - uint32_t dst_reg : 6; - uint32_t dst_reg_am : 1; - uint32_t fetch_valid_only : 1; - uint32_t const_idx : 5; - uint32_t tx_coord_denorm : 1; - uint32_t src_swiz : 6; // xyz - }); - /* dword1: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t dst_swiz : 12; // xyzw - uint32_t mag_filter : 2; // instr_tex_filter_t - uint32_t min_filter : 2; // instr_tex_filter_t - uint32_t mip_filter : 2; // instr_tex_filter_t - uint32_t aniso_filter : 3; // instr_aniso_filter_t - uint32_t arbitrary_filter : 3; // instr_arbitrary_filter_t - uint32_t vol_mag_filter : 2; // instr_tex_filter_t - uint32_t vol_min_filter : 2; // instr_tex_filter_t - uint32_t use_comp_lod : 1; - uint32_t use_reg_lod : 1; - uint32_t unk : 1; - uint32_t pred_select : 1; - }); - /* dword2: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t use_reg_gradients : 1; - uint32_t sample_location : 1; // instr_sample_loc_t - uint32_t lod_bias : 7; - uint32_t unused : 5; - uint32_t dimension : 2; // instr_dimension_t - uint32_t offset_x : 5; - uint32_t offset_y : 5; - uint32_t offset_z : 5; - uint32_t pred_condition : 1; - }); -}); - -XEPACKEDSTRUCT(instr_fetch_vtx_t, { - /* dword0: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t opc : 5; // instr_fetch_opc_t - uint32_t src_reg : 6; - uint32_t src_reg_am : 1; - uint32_t dst_reg : 6; - uint32_t dst_reg_am : 1; - uint32_t must_be_one : 1; - uint32_t const_index : 5; - uint32_t const_index_sel : 2; - uint32_t reserved0 : 3; - uint32_t src_swiz : 2; - }); - /* dword1: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t dst_swiz : 12; - uint32_t format_comp_all : 1; /* '1' for signed, '0' for unsigned? */ - uint32_t num_format_all : 1; /* '0' for normalized, '1' for unnormalized */ - uint32_t signed_rf_mode_all : 1; - uint32_t reserved1 : 1; - uint32_t format : 6; // instr_surf_fmt_t - uint32_t reserved2 : 1; - uint32_t exp_adjust_all : 7; - uint32_t reserved3 : 1; - uint32_t pred_select : 1; - }); - /* dword2: */ - XEPACKEDSTRUCTANONYMOUS({ - uint32_t stride : 8; - uint32_t offset : 23; - uint32_t pred_condition : 1; - }); -}); - -XEPACKEDUNION(instr_fetch_t, { - instr_fetch_tex_t tex; - instr_fetch_vtx_t vtx; - XEPACKEDSTRUCTANONYMOUS({ - /* dword0: */ + private: + XEPACKEDSTRUCT(Data, { XEPACKEDSTRUCTANONYMOUS({ - uint32_t opc : 5; // instr_fetch_opc_t - uint32_t: - 27; + uint32_t opcode_value : 5; + uint32_t src_reg : 6; + uint32_t src_reg_am : 1; + uint32_t dst_reg : 6; + uint32_t dst_reg_am : 1; + uint32_t must_be_one : 1; + uint32_t const_index : 5; + uint32_t const_index_sel : 2; + uint32_t prefetch_count : 3; + uint32_t src_swiz : 2; }); - /* dword1: */ XEPACKEDSTRUCTANONYMOUS({ - uint32_t: - 32; + uint32_t dst_swiz : 12; + uint32_t is_signed : 1; + uint32_t is_integer : 1; + uint32_t signed_rf_mode_all : 1; + uint32_t is_index_rounded : 1; + uint32_t format : 6; + uint32_t reserved2 : 2; + uint32_t exp_adjust : 6; + uint32_t is_mini_fetch : 1; + uint32_t is_predicated : 1; }); - /* dword2: */ XEPACKEDSTRUCTANONYMOUS({ - uint32_t: - 32; + uint32_t stride : 8; + uint32_t offset : 23; + uint32_t pred_condition : 1; }); }); -}); + Data data_; +}; + +struct TextureFetchInstruction { + FetchOpcode opcode() const { + return static_cast(data_.opcode_value); + } + + // Whether the jump is predicated (or conditional). + bool is_predicated() const { return data_.is_predicated; } + // Required condition value of the comparision (true or false). + bool predicate_condition() const { return data_.pred_condition == 1; } + // Texture fetch constant index [0-31]. + uint32_t fetch_constant_index() const { return data_.const_index; } + + uint32_t dest() const { return data_.dst_reg; } + uint32_t dest_swizzle() const { return data_.dst_swiz; } + bool is_dest_relative() const { return data_.dst_reg_am; } + uint32_t src() const { return data_.src_reg; } + uint32_t src_swizzle() const { return data_.src_swiz; } + bool is_src_relative() const { return data_.src_reg_am; } + + TextureDimension dimension() const { + return static_cast(data_.dimension); + } + bool fetch_valid_only() const { return data_.fetch_valid_only == 1; } + bool unnormalized_coordinates() const { return data_.tx_coord_denorm == 1; } + bool has_mag_filter() const { return data_.mag_filter != 0x3; } + TextureFilter mag_filter() const { + return static_cast(data_.mag_filter); + } + bool has_min_filter() const { return data_.min_filter != 0x3; } + TextureFilter min_filter() const { + return static_cast(data_.min_filter); + } + bool has_mip_filter() const { return data_.mip_filter != 0x3; } + TextureFilter mip_filter() const { + return static_cast(data_.mip_filter); + } + bool has_aniso_filter() const { return data_.aniso_filter != 0x7; } + AnisoFilter aniso_filter() const { + return static_cast(data_.aniso_filter); + } + bool use_computed_lod() const { return data_.use_comp_lod == 1; } + bool use_register_lod() const { return data_.use_reg_lod == 1; } + bool use_register_gradients() const { return data_.use_reg_gradients == 1; } + SampleLocation sample_location() const { + return static_cast(data_.sample_location); + } + float offset_x() const { + return ((static_cast(data_.offset_x) << 27) >> 27) / 2.0f; + } + float offset_y() const { + return ((static_cast(data_.offset_y) << 27) >> 27) / 2.0f; + } + float offset_z() const { + return ((static_cast(data_.offset_z) << 27) >> 27) / 2.0f; + } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t opcode_value : 5; + uint32_t src_reg : 6; + uint32_t src_reg_am : 1; + uint32_t dst_reg : 6; + uint32_t dst_reg_am : 1; + uint32_t fetch_valid_only : 1; + uint32_t const_index : 5; + uint32_t tx_coord_denorm : 1; + uint32_t src_swiz : 6; // xyz + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t dst_swiz : 12; // xyzw + uint32_t mag_filter : 2; // instr_tex_filter_t + uint32_t min_filter : 2; // instr_tex_filter_t + uint32_t mip_filter : 2; // instr_tex_filter_t + uint32_t aniso_filter : 3; // instr_aniso_filter_t + uint32_t arbitrary_filter : 3; // instr_arbitrary_filter_t + uint32_t vol_mag_filter : 2; // instr_tex_filter_t + uint32_t vol_min_filter : 2; // instr_tex_filter_t + uint32_t use_comp_lod : 1; + uint32_t use_reg_lod : 1; + uint32_t unk : 1; + uint32_t is_predicated : 1; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t use_reg_gradients : 1; + uint32_t sample_location : 1; + uint32_t lod_bias : 7; + uint32_t unused : 5; + uint32_t dimension : 2; + uint32_t offset_x : 5; + uint32_t offset_y : 5; + uint32_t offset_z : 5; + uint32_t pred_condition : 1; + }); + }); + Data data_; +}; +static_assert_size(TextureFetchInstruction, 12); + +enum class AluScalarOpcode { + kADDs = 0, + kADD_PREVs = 1, + kMULs = 2, + kMUL_PREVs = 3, + kMUL_PREV2s = 4, + kMAXs = 5, + kMINs = 6, + kSETEs = 7, + kSETGTs = 8, + kSETGTEs = 9, + kSETNEs = 10, + kFRACs = 11, + kTRUNCs = 12, + kFLOORs = 13, + kEXP_IEEE = 14, + kLOG_CLAMP = 15, + kLOG_IEEE = 16, + kRECIP_CLAMP = 17, + kRECIP_FF = 18, + kRECIP_IEEE = 19, + kRECIPSQ_CLAMP = 20, + kRECIPSQ_FF = 21, + kRECIPSQ_IEEE = 22, + kMOVAs = 23, + kMOVA_FLOORs = 24, + kSUBs = 25, + kSUB_PREVs = 26, + kPRED_SETEs = 27, + kPRED_SETNEs = 28, + kPRED_SETGTs = 29, + kPRED_SETGTEs = 30, + kPRED_SET_INVs = 31, + kPRED_SET_POPs = 32, + kPRED_SET_CLRs = 33, + kPRED_SET_RESTOREs = 34, + kKILLEs = 35, + kKILLGTs = 36, + kKILLGTEs = 37, + kKILLNEs = 38, + kKILLONEs = 39, + kSQRT_IEEE = 40, + kMUL_CONST_0 = 42, + kMUL_CONST_1 = 43, + kADD_CONST_0 = 44, + kADD_CONST_1 = 45, + kSUB_CONST_0 = 46, + kSUB_CONST_1 = 47, + kSIN = 48, + kCOS = 49, + kRETAIN_PREV = 50, +}; + +enum class AluVectorOpcode { + kADDv = 0, + kMULv = 1, + kMAXv = 2, + kMINv = 3, + kSETEv = 4, + kSETGTv = 5, + kSETGTEv = 6, + kSETNEv = 7, + kFRACv = 8, + kTRUNCv = 9, + kFLOORv = 10, + kMULADDv = 11, + kCNDEv = 12, + kCNDGTEv = 13, + kCNDGTv = 14, + kDOT4v = 15, + kDOT3v = 16, + kDOT2ADDv = 17, + kCUBEv = 18, + kMAX4v = 19, + kPRED_SETE_PUSHv = 20, + kPRED_SETNE_PUSHv = 21, + kPRED_SETGT_PUSHv = 22, + kPRED_SETGTE_PUSHv = 23, + kKILLEv = 24, + kKILLGTv = 25, + kKILLGTEv = 26, + kKILLNEv = 27, + kDSTv = 28, + kMAXAv = 29, +}; + +struct AluInstruction { + // Whether data is being exported (or written to local registers). + bool is_export() const { return data_.export_data == 1; } + bool export_write_mask() const { return data_.scalar_dest_rel == 1; } + + // Whether the jump is predicated (or conditional). + bool is_predicated() const { return data_.is_predicated; } + // Required condition value of the comparision (true or false). + bool predicate_condition() const { return data_.pred_condition == 1; } + + bool abs_constants() const { return data_.abs_constants == 1; } + bool is_const_0_addressed() const { return data_.const_0_rel_abs == 1; } + 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()); + } + AluVectorOpcode vector_opcode() const { + return static_cast(data_.vector_opc); + } + uint32_t vector_write_mask() const { return data_.vector_write_mask; } + uint32_t vector_dest() const { return data_.vector_dest; } + bool is_vector_dest_relative() const { return data_.vector_dest_rel == 1; } + bool vector_clamp() const { return data_.vector_clamp == 1; } + + bool has_scalar_op() const { + return scalar_opcode() != AluScalarOpcode::kRETAIN_PREV || + scalar_write_mask() != 0; + } + AluScalarOpcode scalar_opcode() const { + return static_cast(data_.scalar_opc); + } + uint32_t scalar_write_mask() const { return data_.scalar_write_mask; } + uint32_t scalar_dest() const { return data_.scalar_dest; } + bool is_scalar_dest_relative() const { return data_.scalar_dest_rel == 1; } + bool scalar_clamp() const { return data_.scalar_clamp == 1; } + + uint32_t src_reg(size_t i) const { + switch (i) { + case 1: + return data_.src1_reg; + case 2: + return data_.src2_reg; + case 3: + return data_.src3_reg; + default: + assert_unhandled_case(i); + return 0; + } + } + bool src_is_temp(size_t i) const { + switch (i) { + case 1: + return data_.src1_sel == 1; + case 2: + return data_.src2_sel == 1; + case 3: + return data_.src3_sel == 1; + default: + assert_unhandled_case(i); + return 0; + } + } + uint32_t src_swizzle(size_t i) const { + switch (i) { + case 1: + return data_.src1_swiz; + case 2: + return data_.src2_swiz; + case 3: + return data_.src3_swiz; + default: + assert_unhandled_case(i); + return 0; + } + } + bool src_negate(size_t i) const { + switch (i) { + case 1: + return data_.src1_reg_negate == 1; + case 2: + return data_.src2_reg_negate == 1; + case 3: + return data_.src3_reg_negate == 1; + default: + assert_unhandled_case(i); + return 0; + } + } + + private: + XEPACKEDSTRUCT(Data, { + XEPACKEDSTRUCTANONYMOUS({ + uint32_t vector_dest : 6; + uint32_t vector_dest_rel : 1; + uint32_t abs_constants : 1; + uint32_t scalar_dest : 6; + uint32_t scalar_dest_rel : 1; + uint32_t export_data : 1; + uint32_t vector_write_mask : 4; + uint32_t scalar_write_mask : 4; + uint32_t vector_clamp : 1; + uint32_t scalar_clamp : 1; + uint32_t scalar_opc : 6; // instr_scalar_opc_t + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t src3_swiz : 8; + uint32_t src2_swiz : 8; + uint32_t src1_swiz : 8; + uint32_t src3_reg_negate : 1; + uint32_t src2_reg_negate : 1; + uint32_t src1_reg_negate : 1; + uint32_t pred_condition : 1; + uint32_t is_predicated : 1; + uint32_t address_absolute : 1; + uint32_t const_1_rel_abs : 1; + uint32_t const_0_rel_abs : 1; + }); + XEPACKEDSTRUCTANONYMOUS({ + uint32_t src3_reg : 8; + uint32_t src2_reg : 8; + uint32_t src1_reg : 8; + uint32_t vector_opc : 5; // instr_vector_opc_t + uint32_t src3_sel : 1; + uint32_t src2_sel : 1; + uint32_t src1_sel : 1; + }); + }); + Data data_; +}; +static_assert_size(AluInstruction, 12); } // namespace ucode - } // namespace gpu } // namespace xe diff --git a/src/xenia/ui/spirv/spirv_emitter.cc b/src/xenia/ui/spirv/spirv_emitter.cc index b8e6551ae..6be5b0a62 100644 --- a/src/xenia/ui/spirv/spirv_emitter.cc +++ b/src/xenia/ui/spirv/spirv_emitter.cc @@ -1151,6 +1151,17 @@ Id SpirvEmitter::CreateExtendedInstructionCall(spv::Decoration precision, return instr->result_id(); } +Id SpirvEmitter::CreateGlslStd450InstructionCall( + spv::Decoration precision, Id result_type, + spv::GLSLstd450 instruction_ordinal, std::initializer_list args) { + if (!glsl_std_450_instruction_set_) { + glsl_std_450_instruction_set_ = ImportExtendedInstructions("GLSL.std.450"); + } + return CreateExtendedInstructionCall( + precision, result_type, glsl_std_450_instruction_set_, + static_cast(instruction_ordinal), args); +} + // Accept all parameters needed to create a texture instruction. // Create the correct instruction based on the inputs, and make the call. Id SpirvEmitter::CreateTextureCall(spv::Decoration precision, Id result_type, diff --git a/src/xenia/ui/spirv/spirv_emitter.h b/src/xenia/ui/spirv/spirv_emitter.h index 51e0192fa..ccd0fcdbd 100644 --- a/src/xenia/ui/spirv/spirv_emitter.h +++ b/src/xenia/ui/spirv/spirv_emitter.h @@ -373,6 +373,10 @@ class SpirvEmitter { Id CreateExtendedInstructionCall(spv::Decoration precision, Id result_type, Id instruction_set, int instruction_ordinal, std::initializer_list args); + // Executes an instruction from the extended GLSL set. + Id CreateGlslStd450InstructionCall(spv::Decoration precision, Id result_type, + spv::GLSLstd450 instruction_ordinal, + std::initializer_list args); // List of parameters used to create a texture operation struct TextureParameters { @@ -655,6 +659,7 @@ class SpirvEmitter { Id unique_id_ = 0; Function* main_function_ = nullptr; AccessChain access_chain_; + Id glsl_std_450_instruction_set_ = 0; // special blocks of instructions for output std::vector imports_; diff --git a/tools/shader-playground/Editor.Designer.cs b/tools/shader-playground/Editor.Designer.cs index 0f369a566..bd75a38b6 100644 --- a/tools/shader-playground/Editor.Designer.cs +++ b/tools/shader-playground/Editor.Designer.cs @@ -23,94 +23,175 @@ /// the contents of this method with the code editor. /// private void InitializeComponent() { + this.wordsTextBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.sourceCodeTextBox = new System.Windows.Forms.TextBox(); this.outputTextBox = new System.Windows.Forms.TextBox(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.wordsTextBox = new System.Windows.Forms.TextBox(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); + 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.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); - // - // sourceCodeTextBox - // - this.sourceCodeTextBox.AcceptsReturn = true; - this.sourceCodeTextBox.AcceptsTab = true; - this.sourceCodeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.sourceCodeTextBox.Location = new System.Drawing.Point(0, 0); - this.sourceCodeTextBox.Multiline = true; - this.sourceCodeTextBox.Name = "sourceCodeTextBox"; - this.sourceCodeTextBox.Size = new System.Drawing.Size(352, 360); - this.sourceCodeTextBox.TabIndex = 0; - // - // outputTextBox - // - this.outputTextBox.AcceptsReturn = true; - this.outputTextBox.AcceptsTab = true; - this.outputTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.outputTextBox.Location = new System.Drawing.Point(0, 0); - this.outputTextBox.Multiline = true; - this.outputTextBox.Name = "outputTextBox"; - this.outputTextBox.ReadOnly = true; - this.outputTextBox.Size = new System.Drawing.Size(349, 360); - this.outputTextBox.TabIndex = 1; - // - // splitContainer1 - // - this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.splitContainer1.Location = new System.Drawing.Point(12, 12); - this.splitContainer1.Name = "splitContainer1"; - // - // splitContainer1.Panel1 - // - this.splitContainer1.Panel1.Controls.Add(this.sourceCodeTextBox); - // - // splitContainer1.Panel2 - // - this.splitContainer1.Panel2.Controls.Add(this.outputTextBox); - this.splitContainer1.Size = new System.Drawing.Size(705, 360); - this.splitContainer1.SplitterDistance = 352; - this.splitContainer1.TabIndex = 2; - // + // // wordsTextBox - // - this.wordsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + // + this.wordsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.wordsTextBox.Location = new System.Drawing.Point(12, 378); + this.wordsTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.wordsTextBox.Location = new System.Drawing.Point(12, 657); this.wordsTextBox.Multiline = true; this.wordsTextBox.Name = "wordsTextBox"; this.wordsTextBox.ReadOnly = true; - this.wordsTextBox.Size = new System.Drawing.Size(705, 251); + this.wordsTextBox.Size = new System.Drawing.Size(1631, 137); this.wordsTextBox.TabIndex = 4; - // + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 4; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.Controls.Add(this.compilerTranslatedTextBox, 3, 1); + this.tableLayoutPanel1.Controls.Add(this.sourceCodeTextBox, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.outputTextBox, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.compilerUcodeTextBox, 2, 1); + 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.Location = new System.Drawing.Point(12, 12); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(1631, 639); + this.tableLayoutPanel1.TabIndex = 5; + // + // sourceCodeTextBox + // + this.sourceCodeTextBox.AcceptsReturn = true; + this.sourceCodeTextBox.AcceptsTab = true; + this.sourceCodeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.sourceCodeTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sourceCodeTextBox.Location = new System.Drawing.Point(3, 23); + this.sourceCodeTextBox.Multiline = true; + this.sourceCodeTextBox.Name = "sourceCodeTextBox"; + this.sourceCodeTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.sourceCodeTextBox.Size = new System.Drawing.Size(401, 613); + this.sourceCodeTextBox.TabIndex = 6; + // + // outputTextBox + // + this.outputTextBox.AcceptsReturn = true; + this.outputTextBox.AcceptsTab = true; + this.outputTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.outputTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.outputTextBox.Location = new System.Drawing.Point(410, 23); + this.outputTextBox.Multiline = true; + this.outputTextBox.Name = "outputTextBox"; + this.outputTextBox.ReadOnly = true; + this.outputTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.outputTextBox.Size = new System.Drawing.Size(401, 613); + this.outputTextBox.TabIndex = 5; + // + // compilerUcodeTextBox + // + this.compilerUcodeTextBox.AcceptsReturn = true; + this.compilerUcodeTextBox.AcceptsTab = true; + this.compilerUcodeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.compilerUcodeTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.compilerUcodeTextBox.Location = new System.Drawing.Point(817, 23); + this.compilerUcodeTextBox.Multiline = true; + this.compilerUcodeTextBox.Name = "compilerUcodeTextBox"; + this.compilerUcodeTextBox.ReadOnly = true; + this.compilerUcodeTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.compilerUcodeTextBox.Size = new System.Drawing.Size(401, 613); + this.compilerUcodeTextBox.TabIndex = 4; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(78, 13); + this.label1.TabIndex = 7; + this.label1.Text = "Input Assembly"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(410, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(107, 13); + this.label2.TabIndex = 8; + this.label2.Text = "XNA Compiler Output"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(817, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(191, 13); + this.label3.TabIndex = 9; + this.label3.Text = "xenia-gpu-shader-compiler Disassembly"; + // + // label4 + // + 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; + // // Editor - // + // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(729, 641); + this.ClientSize = new System.Drawing.Size(1655, 806); + this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.wordsTextBox); - this.Controls.Add(this.splitContainer1); this.Name = "Editor"; this.Text = "Shader Playground"; - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel1.PerformLayout(); - this.splitContainer1.Panel2.ResumeLayout(false); - this.splitContainer1.Panel2.PerformLayout(); - this.splitContainer1.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion - + private System.Windows.Forms.TextBox wordsTextBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.TextBox sourceCodeTextBox; private System.Windows.Forms.TextBox outputTextBox; - private System.Windows.Forms.SplitContainer splitContainer1; - private System.Windows.Forms.TextBox wordsTextBox; + private System.Windows.Forms.TextBox compilerUcodeTextBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox compilerTranslatedTextBox; + private System.Windows.Forms.Label label4; } } diff --git a/tools/shader-playground/Editor.cs b/tools/shader-playground/Editor.cs index 81e7b3a5b..c27228cf2 100644 --- a/tools/shader-playground/Editor.cs +++ b/tools/shader-playground/Editor.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Text; @@ -9,34 +10,84 @@ using System.Windows.Forms; namespace shader_playground { public partial class Editor : Form { + string compilerPath_ = @"..\..\..\..\..\build\bin\Windows\Debug\xenia-gpu-shader-compiler.exe"; + + FileSystemWatcher compilerWatcher_; + public Editor() { InitializeComponent(); - wordsTextBox.Click += WordsTextBox_Click; + var compilerBinPath = Path.Combine(Directory.GetCurrentDirectory(), + Path.GetDirectoryName(compilerPath_)); + compilerWatcher_ = new FileSystemWatcher(compilerBinPath, "*.exe"); + compilerWatcher_.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size; + compilerWatcher_.Changed += (object sender, FileSystemEventArgs e) => { + if (e.Name == Path.GetFileName(compilerPath_)) { + Invoke((MethodInvoker)delegate { Process(sourceCodeTextBox.Text); }); + } + }; + compilerWatcher_.EnableRaisingEvents = true; + + wordsTextBox.Click += (object sender, EventArgs e) => { + wordsTextBox.SelectAll(); + wordsTextBox.Copy(); + }; + + this.sourceCodeTextBox.Click += (object sender, EventArgs e) => { + Process(sourceCodeTextBox.Text); + }; + sourceCodeTextBox.TextChanged += (object sender, EventArgs e) => { + Process(sourceCodeTextBox.Text); + }; - sourceCodeTextBox.TextChanged += SourceCodeTextBox_TextChanged; sourceCodeTextBox.Text = string.Join( "\r\n", new string[] { - "xps_3_0", - "dcl_texcoord1 r0", - "dcl_color r1.xy", - "exec", - "alloc colors", - "exece", - "mad oC0, r0, r1.y, c0", - "mul r4.xyz, r1.xyz, c0.xyz", - "+ adds r5.w, r0.xy", - "cnop", - }); - } - - private void WordsTextBox_Click(object sender, EventArgs e) { - wordsTextBox.SelectAll(); - wordsTextBox.Copy(); - } - - void SourceCodeTextBox_TextChanged(object sender, EventArgs e) { - Assemble(sourceCodeTextBox.Text); +"xps_3_0", +"dcl_texcoord1 r0", +"dcl_color r1.xy", +"exec", +"alloc colors", +"exec", +"tfetch1D r2, r0.y, tf0, FetchValidOnly=false", +"tfetch1D r2, r0.x, tf2", +"tfetch2D r3, r3.wx, tf13", +"tfetch2D r[aL+3], r[aL+5].wx, tf13, FetchValidOnly=false, UnnormalizedTextureCoords=true, MagFilter=linear, MinFilter=linear, MipFilter=point, AnisoFilter=max1to1, UseRegisterGradients=true, UseComputedLOD=false, UseRegisterLOD=true, OffsetX=-1.5, OffsetY=1.0", +"tfetch3D r31.w_01, r0.xyw, tf15", +"tfetchCube r5, r1.xyw, tf31", +" setTexLOD r1.z", +" setGradientH r1.zyx", +"(!p0) setGradientV r1.zyx", +" getGradients r5, r1.xy, tf3", +" mad oC0, 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", +" mul r4.xyz, r1.xyzz, c[0 + a0].xyzz", +" mul r4.xyz, r1.xyzz, c[8 + a0].xyzz", +" + adds r5.w, r0.xz", +" cos r6.w, r0.x", +" adds r5.w, r0.zx", +" jmp l5", +"ccall b1, l5", +"nop", +" label l5", +"(!p0) exec", +"cexec b5, Yield=true", +"cexec !b6", +" mulsc r3.w, c1.z, r1.w", +"loop i7, L4", +" label L3", +" exec", +" setp_eq r15, c[aL].w", +" (!p0) add r0, r0, c[aL]", +"(p0) endloop i7, L3", +"label L4", +"exece", +" mulsc r3.w, c3.z, r6.x", +" mulsc r3.w, c200.z, r31.x", +" mov oDepth.x, c3.w", +" cnop", + }); } class NopIncludeHandler : CompilerIncludeHandler { @@ -46,7 +97,7 @@ namespace shader_playground { } } - void Assemble(string shaderSourceCode) { + void Process(string shaderSourceCode) { shaderSourceCode += "\ncnop"; shaderSourceCode += "\ncnop"; var preprocessorDefines = new CompilerMacro[2]; @@ -58,45 +109,171 @@ namespace shader_playground { shaderSourceCode, preprocessorDefines, includeHandler, options, Microsoft.Xna.Framework.TargetPlatform.Xbox360); - DumpWords(compiledShader.GetShaderCode()); - var disassembledSourceCode = compiledShader.ErrorsAndWarnings; disassembledSourceCode = disassembledSourceCode.Replace("\n", "\r\n"); if (disassembledSourceCode.IndexOf("// PDB hint 00000000-00000000-00000000") == -1) { outputTextBox.Text = disassembledSourceCode; - return; - } - var prefix = disassembledSourceCode.Substring( - 0, disassembledSourceCode.IndexOf(':')); - disassembledSourceCode = - disassembledSourceCode.Replace(prefix + ": warning X7102: ", ""); - disassembledSourceCode = disassembledSourceCode.Replace( - "// PDB hint 00000000-00000000-00000000\r\n", ""); - var firstLine = disassembledSourceCode.IndexOf("//"); - disassembledSourceCode = disassembledSourceCode.Substring(firstLine); - disassembledSourceCode = disassembledSourceCode.Trim(); - outputTextBox.Text = disassembledSourceCode; - } - - void DumpWords(byte[] shaderCode) { - if (shaderCode == null || shaderCode.Length == 0) { + compilerUcodeTextBox.Text = ""; wordsTextBox.Text = ""; return; } + var prefix = disassembledSourceCode.Substring( + 0, disassembledSourceCode.IndexOf( + ':', disassembledSourceCode.IndexOf(':') + 1)); + disassembledSourceCode = + disassembledSourceCode.Replace(prefix + ": ", ""); + disassembledSourceCode = disassembledSourceCode.Replace( + "// PDB hint 00000000-00000000-00000000\r\n", ""); + var firstLine = disassembledSourceCode.IndexOf("//"); + var warnings = "// " + + disassembledSourceCode.Substring(0, firstLine) + .Replace("\r\n", "\r\n// "); + disassembledSourceCode = + warnings + disassembledSourceCode.Substring(firstLine + 3); + disassembledSourceCode = disassembledSourceCode.Trim(); + outputTextBox.Text = disassembledSourceCode; - uint[] swappedCode = new uint[shaderCode.Length / sizeof(uint)]; - Buffer.BlockCopy(shaderCode, 0, swappedCode, 0, shaderCode.Length); + string shaderType = + shaderSourceCode.IndexOf("xvs_") == -1 ? "ps" : "vs"; + var ucodeWords = ExtractAndDumpWords(shaderType, compiledShader.GetShaderCode()); + if (ucodeWords != null) { + TryCompiler(shaderType, ucodeWords); + } else { + compilerUcodeTextBox.Text = ""; + } + + if (compilerUcodeTextBox.Text.Length > 0) { + var sourcePrefix = disassembledSourceCode.Substring(0, disassembledSourceCode.IndexOf("/*")); + TryRoundTrip(sourcePrefix, compilerUcodeTextBox.Text, compiledShader.GetShaderCode()); + } + } + + 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"); + if (File.Exists(ucodePath)) { + File.Delete(ucodePath); + } + if (File.Exists(ucodeDisasmPath)) { + File.Delete(ucodeDisasmPath); + } + if (File.Exists(spirvDisasmPath)) { + File.Delete(spirvDisasmPath); + } + + byte[] ucodeBytes = new byte[ucodeWords.Length * 4]; + Buffer.BlockCopy(ucodeWords, 0, ucodeBytes, 0, ucodeWords.Length * 4); + File.WriteAllBytes(ucodePath, ucodeBytes); + + if (!File.Exists(compilerPath_)) { + compilerUcodeTextBox.Text = "Compiler not found: " + compilerPath_; + return; + } + + var startInfo = new ProcessStartInfo(compilerPath_); + startInfo.Arguments = string.Join(" ", new string[]{ + "--shader_input=" + ucodePath, + "--shader_input_type=" + shaderType, + "--shader_output=" + ucodeDisasmPath, + "--shader_output_type=ucode", + }); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + try { + using (var process = System.Diagnostics.Process.Start(startInfo)) { + process.WaitForExit(); + } + string disasmText = File.ReadAllText(ucodeDisasmPath); + compilerUcodeTextBox.Text = disasmText; + } catch { + compilerUcodeTextBox.Text = "COMPILER FAILURE"; + } + + startInfo = new ProcessStartInfo(compilerPath_); + startInfo.Arguments = string.Join(" ", new string[]{ + "--shader_input=" + ucodePath, + "--shader_input_type=" + shaderType, + "--shader_output=" + spirvDisasmPath, + "--shader_output_type=spirvtext", + }); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + try { + using (var process = System.Diagnostics.Process.Start(startInfo)) { + process.WaitForExit(); + } + string disasmText = File.ReadAllText(spirvDisasmPath); + compilerTranslatedTextBox.Text = disasmText; + } catch { + compilerTranslatedTextBox.Text = "COMPILER FAILURE"; + } + } + + void TryRoundTrip(string sourcePrefix, string compilerSource, byte[] expectedBytes) { + var shaderSourceCode = sourcePrefix + compilerSource; + var preprocessorDefines = new CompilerMacro[2]; + preprocessorDefines[0].Name = "XBOX"; + preprocessorDefines[0].Name = "XBOX360"; + var includeHandler = new NopIncludeHandler(); + var options = CompilerOptions.None; + var compiledShader = ShaderCompiler.AssembleFromSource( + shaderSourceCode, preprocessorDefines, includeHandler, options, + Microsoft.Xna.Framework.TargetPlatform.Xbox360); + var compiledBytes = compiledShader.GetShaderCode(); + if (compiledBytes == null || + compiledBytes.Length != expectedBytes.Length || + !MemCmp(compiledBytes, expectedBytes)) { + compilerUcodeTextBox.BackColor = System.Drawing.Color.Red; + } else { + compilerUcodeTextBox.BackColor = System.Drawing.SystemColors.Control; + } + } + + bool MemCmp(byte[] a1, byte[] b1) { + if (a1 == null || b1 == null) { + return false; + } + int length = a1.Length; + if (b1.Length != length) { + return false; + } + while (length > 0) { + length--; + if (a1[length] != b1[length]) { + return false; + } + } + return true; + } + + uint[] ExtractAndDumpWords(string shaderType, byte[] shaderCode) { + if (shaderCode == null || shaderCode.Length == 0) { + wordsTextBox.Text = ""; + return null; + } + + // Find shader code. + int byteOffset = (shaderCode[4] << 24) | (shaderCode[5] << 16) | + (shaderCode[6] << 8) | (shaderCode[7] << 0); + int wordOffset = byteOffset / 4; + + uint[] swappedCode = new uint[(shaderCode.Length - wordOffset) / sizeof(uint)]; + Buffer.BlockCopy(shaderCode, wordOffset * 4, swappedCode, 0, shaderCode.Length - wordOffset * 4); for (int i = 0; i < swappedCode.Length; ++i) { swappedCode[i] = SwapBytes(swappedCode[i]); } var sb = new StringBuilder(); - sb.Append("const uint32_t shader_words[] = {"); + sb.Append("const uint32_t shader_dwords[] = {"); for (int i = 0; i < swappedCode.Length; ++i) { sb.AppendFormat("0x{0:X8}, ", swappedCode[i]); } - sb.Append("};"); + sb.Append("};\r\n"); + sb.Append("shader_type = ShaderType::" + (shaderType == "vs" ? "kVertex" : "kPixel") + ";\r\n"); wordsTextBox.Text = sb.ToString(); wordsTextBox.SelectAll(); + + return swappedCode; } uint SwapBytes(uint x) { @@ -105,6 +282,5 @@ namespace shader_playground { ((x & 0x00ff0000) >> 8) + ((x & 0xff000000) >> 24); } - } } diff --git a/tools/shader-playground/Editor.resx b/tools/shader-playground/Editor.resx index d2320fab8..7080a7d11 100644 --- a/tools/shader-playground/Editor.resx +++ b/tools/shader-playground/Editor.resx @@ -1,17 +1,17 @@  - @@ -117,4 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + \ No newline at end of file