From f58ee3f15f7427a8b834286384931bcf821ed771 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 17 Nov 2021 04:19:29 +0100 Subject: ShaderDecompiler: Add a debug option to dump the game's shaders. --- src/video_core/shader_environment.cpp | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'src/video_core/shader_environment.cpp') diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 05850afd0..7d3ae0de4 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -14,6 +15,7 @@ #include "common/common_types.h" #include "common/div_ceil.h" #include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/logging/log.h" #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" @@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { } } +static std::string_view StageToPrefix(Shader::Stage stage) { + switch (stage) { + case Shader::Stage::VertexB: + return "VB"; + case Shader::Stage::TessellationControl: + return "TC"; + case Shader::Stage::TessellationEval: + return "TE"; + case Shader::Stage::Geometry: + return "GS"; + case Shader::Stage::Fragment: + return "FS"; + case Shader::Stage::Compute: + return "CS"; + case Shader::Stage::VertexA: + return "VA"; + default: + return "UK"; + } +} + +static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest, + u32 initial_offset, Shader::Stage stage) { + const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; + const auto base_dir{shader_dir / "shaders"}; + if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) { + LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories"); + return; + } + const auto prefix = StageToPrefix(stage); + const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)}; + const size_t real_size = read_highest - read_lowest + initial_offset; + const size_t padding_needed = ((32 - (real_size % 32)) % 32); + std::fstream shader_file(name, std::ios::out | std::ios::binary); + const size_t jump_index = initial_offset / sizeof(u64); + shader_file.write(reinterpret_cast(code + jump_index), real_size); + for (size_t i = 0; i < padding_needed; i++) { + shader_file.put(0); + } +} + GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, u32 start_address_) : gpu_memory{&gpu_memory_}, program_base{program_base_} { @@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const { return Common::CityHash64(data.get(), size); } +void GenericEnvironment::Dump(u64 hash) { + DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage); +} + void GenericEnvironment::Serialize(std::ofstream& file) const { const u64 code_size{static_cast(CachedSize())}; const u64 num_texture_types{static_cast(texture_types.size())}; @@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, u32 start_address_) : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); + initial_offset = sizeof(sph); gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; switch (program) { case Maxwell::ShaderProgram::VertexA: @@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) { if (stage == Shader::Stage::Compute) { file.read(reinterpret_cast(&workgroup_size), sizeof(workgroup_size)) .read(reinterpret_cast(&shared_memory_size), sizeof(shared_memory_size)); + initial_offset = 0; } else { file.read(reinterpret_cast(&sph), sizeof(sph)); + initial_offset = sizeof(sph); if (stage == Shader::Stage::Geometry) { file.read(reinterpret_cast(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); } } } +void FileEnvironment::Dump(u64 [[maybe_unused]] hash) { + DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage); +} + u64 FileEnvironment::ReadInstruction(u32 address) { if (address < read_lowest || address > read_highest) { throw Shader::LogicError("Out of bounds address {}", address); -- cgit v1.2.3