From 9009cba90abd956e2b65f765d2c70e70adbfb0e9 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Fri, 25 Dec 2020 21:46:07 +0300 Subject: [PATCH] [GPU] Refactor shader dumps - bin and txt files for both ucode and host --- src/xenia/gpu/d3d12/pipeline_cache.cc | 5 -- src/xenia/gpu/shader.cc | 113 ++++++++++++++++---------- src/xenia/gpu/shader.h | 20 +++-- src/xenia/gpu/shader_translator.cc | 7 ++ 4 files changed, 90 insertions(+), 55 deletions(-) diff --git a/src/xenia/gpu/d3d12/pipeline_cache.cc b/src/xenia/gpu/d3d12/pipeline_cache.cc index 5e2289306..cbaa5c02c 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.cc +++ b/src/xenia/gpu/d3d12/pipeline_cache.cc @@ -844,17 +844,12 @@ D3D12Shader* PipelineCache::LoadShader(xenos::ShaderType shader_type, // Shader has been previously loaded. return it->second; } - // Always create the shader and stash it away. // We need to track it even if it fails translation so we know not to try // again. D3D12Shader* shader = new D3D12Shader(shader_type, data_hash, host_address, dword_count); shaders_.emplace(data_hash, shader); - if (!cvars::dump_shaders.empty()) { - shader->DumpUcodeBinary(cvars::dump_shaders); - } - return shader; } diff --git a/src/xenia/gpu/shader.cc b/src/xenia/gpu/shader.cc index 78451035d..5d38dbfd7 100644 --- a/src/xenia/gpu/shader.cc +++ b/src/xenia/gpu/shader.cc @@ -11,6 +11,7 @@ #include #include +#include #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/filesystem.h" @@ -45,37 +46,50 @@ std::string Shader::Translation::GetTranslatedBinaryString() const { return result; } -std::filesystem::path Shader::Translation::Dump( - const std::filesystem::path& base_path, const char* path_prefix) { +std::pair +Shader::Translation::Dump(const std::filesystem::path& base_path, + const char* path_prefix) const { + if (!is_valid()) { + return std::make_pair(std::filesystem::path(), std::filesystem::path()); + } + std::filesystem::path path = base_path; // Ensure target path exists. - if (!path.empty()) { - path = std::filesystem::absolute(path); - std::filesystem::create_directories(path); + std::filesystem::path target_path = base_path; + if (!target_path.empty()) { + target_path = std::filesystem::absolute(target_path); + std::filesystem::create_directories(target_path); } - path = path / - fmt::format( - "shader_{:016X}_{:016X}.{}.{}", shader().ucode_data_hash(), - modification(), path_prefix, - shader().type() == xenos::ShaderType::kVertex ? "vert" : "frag"); - FILE* f = filesystem::OpenFile(path, "wb"); - if (f) { - fwrite(translated_binary_.data(), 1, translated_binary_.size(), f); - fprintf(f, "\n\n"); - auto ucode_disasm_ptr = shader().ucode_disassembly().c_str(); - while (*ucode_disasm_ptr) { - auto line_end = std::strchr(ucode_disasm_ptr, '\n'); - fprintf(f, "// "); - fwrite(ucode_disasm_ptr, 1, line_end - ucode_disasm_ptr + 1, f); - ucode_disasm_ptr = line_end + 1; - } - fprintf(f, "\n\n"); - if (!host_disassembly_.empty()) { - fprintf(f, "\n\n/*\n%s\n*/\n", host_disassembly_.c_str()); - } - fclose(f); + + const char* type_extension = + shader().type() == xenos::ShaderType::kVertex ? "vert" : "frag"; + + std::filesystem::path binary_path = + target_path / fmt::format("shader_{:016X}_{:016X}.{}.bin.{}", + shader().ucode_data_hash(), modification(), + path_prefix, type_extension); + FILE* binary_file = filesystem::OpenFile(binary_path, "wb"); + if (binary_file) { + fwrite(translated_binary_.data(), sizeof(*translated_binary_.data()), + translated_binary_.size(), binary_file); + fclose(binary_file); } - return std::move(path); + + std::filesystem::path disasm_path; + if (!host_disassembly_.empty()) { + disasm_path = + target_path / fmt::format("shader_{:016X}_{:016X}.{}.{}", + shader().ucode_data_hash(), modification(), + path_prefix, type_extension); + FILE* disasm_file = filesystem::OpenFile(disasm_path, "w"); + if (disasm_file) { + fwrite(host_disassembly_.data(), sizeof(*host_disassembly_.data()), + host_disassembly_.size(), disasm_file); + fclose(disasm_file); + } + } + + return std::make_pair(std::move(binary_path), std::move(disasm_path)); } Shader::Translation* Shader::GetOrCreateTranslation(uint64_t modification, @@ -104,24 +118,41 @@ void Shader::DestroyTranslation(uint64_t modification) { translations_.erase(it); } -std::filesystem::path Shader::DumpUcodeBinary( - const std::filesystem::path& base_path) { +std::pair Shader::DumpUcode( + const std::filesystem::path& base_path) const { // Ensure target path exists. - std::filesystem::path path = base_path; - if (!path.empty()) { - path = std::filesystem::absolute(path); - std::filesystem::create_directories(path); + std::filesystem::path target_path = base_path; + if (!target_path.empty()) { + target_path = std::filesystem::absolute(target_path); + std::filesystem::create_directories(target_path); } - path = path / - fmt::format("shader_{:016X}.ucode.bin.{}", ucode_data_hash(), - type() == xenos::ShaderType::kVertex ? "vert" : "frag"); - FILE* f = filesystem::OpenFile(path, "wb"); - if (f) { - fwrite(ucode_data().data(), 4, ucode_data().size(), f); - fclose(f); + const char* type_extension = + type() == xenos::ShaderType::kVertex ? "vert" : "frag"; + + std::filesystem::path binary_path = + target_path / fmt::format("shader_{:016X}.ucode.bin.{}", + ucode_data_hash(), type_extension); + FILE* binary_file = filesystem::OpenFile(binary_path, "wb"); + if (binary_file) { + fwrite(ucode_data().data(), sizeof(*ucode_data().data()), + ucode_data().size(), binary_file); + fclose(binary_file); } - return std::move(path); + + std::filesystem::path disasm_path; + if (is_ucode_analyzed()) { + disasm_path = target_path / fmt::format("shader_{:016X}.ucode.{}", + ucode_data_hash(), type_extension); + FILE* disasm_file = filesystem::OpenFile(disasm_path, "w"); + if (disasm_file) { + fwrite(ucode_disassembly().data(), sizeof(*ucode_disassembly().data()), + ucode_disassembly().size(), disasm_file); + fclose(disasm_file); + } + } + + return std::make_pair(std::move(binary_path), std::move(disasm_path)); } Shader::Translation* Shader::CreateTranslationInstance(uint64_t modification) { diff --git a/src/xenia/gpu/shader.h b/src/xenia/gpu/shader.h index c9e737a5f..ad9701b7d 100644 --- a/src/xenia/gpu/shader.h +++ b/src/xenia/gpu/shader.h @@ -763,11 +763,11 @@ class Shader { host_disassembly_ = std::move(disassembly); } - // For dumping after translation. Dumps the shader's disassembled microcode, - // translated code, and, if available, translated disassembly, to a file in - // the given path based on ucode hash. Returns the name of the written file. - std::filesystem::path Dump(const std::filesystem::path& base_path, - const char* path_prefix); + // For dumping after translation. Dumps the shader's translated code, and, + // if available, translated disassembly, to files in the given directory + // based on ucode hash. Returns {binary path, disassembly path if written}. + std::pair Dump( + const std::filesystem::path& base_path, const char* path_prefix) const; protected: Translation(Shader& shader, uint64_t modification) @@ -928,10 +928,12 @@ class Shader { ucode_storage_index_ = storage_index; } - // Dumps the shader's microcode binary to a file in the given path based on - // ucode hash. Returns the name of the written file. Can be called at any - // time, doesn't require the shader to be translated. - std::filesystem::path DumpUcodeBinary(const std::filesystem::path& base_path); + // Dumps the shader's microcode binary and, if analyzed, disassembly, to files + // in the given directory based on ucode hash. Returns the name of the written + // file. Can be called at any time, doesn't require the shader to be + // translated. Returns {binary path, disassembly path if written}. + std::pair DumpUcode( + const std::filesystem::path& base_path) const; protected: friend class ShaderTranslator; diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index e66e6013e..8f6c61399 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -18,6 +18,7 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" +#include "xenia/gpu/gpu_flags.h" namespace xe { namespace gpu { @@ -224,6 +225,12 @@ void Shader::AnalyzeUcode(StringBuffer& ucode_disasm_buffer) { } is_ucode_analyzed_ = true; + + // An empty shader can be created internally by shader translators as a dummy, + // don't dump it. + if (!cvars::dump_shaders.empty() && !ucode_data().empty()) { + DumpUcode(cvars::dump_shaders); + } } void Shader::GatherExecInformation(