summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2021-11-17 04:19:29 +0100
committerGravatar Fernando Sahmkow2022-01-04 02:39:00 +0100
commitf58ee3f15f7427a8b834286384931bcf821ed771 (patch)
tree1ef8367f64cbee6220c4e5600249697e5cd8e826
parentMerge pull request #7648 from bunnei/thread-pinning (diff)
downloadyuzu-f58ee3f15f7427a8b834286384931bcf821ed771.tar.gz
yuzu-f58ee3f15f7427a8b834286384931bcf821ed771.tar.xz
yuzu-f58ee3f15f7427a8b834286384931bcf821ed771.zip
ShaderDecompiler: Add a debug option to dump the game's shaders.
-rw-r--r--src/common/settings.h1
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp9
-rw-r--r--src/video_core/shader_environment.cpp54
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui13
8 files changed, 98 insertions, 1 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index 313f1fa7f..d01c0448c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -597,6 +597,7 @@ struct Values {
597 BasicSetting<std::string> program_args{std::string(), "program_args"}; 597 BasicSetting<std::string> program_args{std::string(), "program_args"};
598 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 598 BasicSetting<bool> dump_exefs{false, "dump_exefs"};
599 BasicSetting<bool> dump_nso{false, "dump_nso"}; 599 BasicSetting<bool> dump_nso{false, "dump_nso"};
600 BasicSetting<bool> dump_shaders{false, "dump_shaders"};
600 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 601 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
601 BasicSetting<bool> reporting_services{false, "reporting_services"}; 602 BasicSetting<bool> reporting_services{false, "reporting_services"};
602 BasicSetting<bool> quest_flag{false, "quest_flag"}; 603 BasicSetting<bool> quest_flag{false, "quest_flag"};
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index b4df73e8a..db16429d4 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -31,6 +31,8 @@ public:
31 31
32 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0; 32 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
33 33
34 virtual void Dump(u64 hash) = 0;
35
34 [[nodiscard]] const ProgramHeader& SPH() const noexcept { 36 [[nodiscard]] const ProgramHeader& SPH() const noexcept {
35 return sph; 37 return sph;
36 } 38 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index ec558a9af..f71e01a34 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -425,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
425 425
426 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 426 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
427 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 427 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
428
429 if (Settings::values.dump_shaders) {
430 env.Dump(key.unique_hashes[index]);
431 }
432
428 if (!uses_vertex_a || index != 1) { 433 if (!uses_vertex_a || index != 1) {
429 // Normal path 434 // Normal path
430 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 435 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -511,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
511 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 516 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
512 517
513 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 518 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
514 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
515 519
520 if (Settings::values.dump_shaders) {
521 env.Dump(key.Hash());
522 }
523
524 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
516 const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; 525 const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
517 Shader::RuntimeInfo info; 526 Shader::RuntimeInfo info;
518 info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); 527 info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 2728353c8..a633b73e5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -517,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
517 517
518 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 518 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
519 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 519 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
520 if (Settings::values.dump_shaders) {
521 env.Dump(key.unique_hashes[index]);
522 }
520 if (!uses_vertex_a || index != 1) { 523 if (!uses_vertex_a || index != 1) {
521 // Normal path 524 // Normal path
522 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 525 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -613,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
613 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 616 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
614 617
615 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 618 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
619
620 // Dump it before error.
621 if (Settings::values.dump_shaders) {
622 env.Dump(key.Hash());
623 }
624
616 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; 625 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
617 const std::vector<u32> code{EmitSPIRV(profile, program)}; 626 const std::vector<u32> code{EmitSPIRV(profile, program)};
618 device.SaveShader(code); 627 device.SaveShader(code);
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bit>
6#include <filesystem> 7#include <filesystem>
7#include <fstream> 8#include <fstream>
8#include <memory> 9#include <memory>
@@ -14,6 +15,7 @@
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "common/div_ceil.h" 16#include "common/div_ceil.h"
16#include "common/fs/fs.h" 17#include "common/fs/fs.h"
18#include "common/fs/path_util.h"
17#include "common/logging/log.h" 19#include "common/logging/log.h"
18#include "shader_recompiler/environment.h" 20#include "shader_recompiler/environment.h"
19#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
57 } 59 }
58} 60}
59 61
62static std::string_view StageToPrefix(Shader::Stage stage) {
63 switch (stage) {
64 case Shader::Stage::VertexB:
65 return "VB";
66 case Shader::Stage::TessellationControl:
67 return "TC";
68 case Shader::Stage::TessellationEval:
69 return "TE";
70 case Shader::Stage::Geometry:
71 return "GS";
72 case Shader::Stage::Fragment:
73 return "FS";
74 case Shader::Stage::Compute:
75 return "CS";
76 case Shader::Stage::VertexA:
77 return "VA";
78 default:
79 return "UK";
80 }
81}
82
83static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
84 u32 initial_offset, Shader::Stage stage) {
85 const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
86 const auto base_dir{shader_dir / "shaders"};
87 if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
88 LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
89 return;
90 }
91 const auto prefix = StageToPrefix(stage);
92 const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
93 const size_t real_size = read_highest - read_lowest + initial_offset;
94 const size_t padding_needed = ((32 - (real_size % 32)) % 32);
95 std::fstream shader_file(name, std::ios::out | std::ios::binary);
96 const size_t jump_index = initial_offset / sizeof(u64);
97 shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
98 for (size_t i = 0; i < padding_needed; i++) {
99 shader_file.put(0);
100 }
101}
102
60GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, 103GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
61 u32 start_address_) 104 u32 start_address_)
62 : gpu_memory{&gpu_memory_}, program_base{program_base_} { 105 : gpu_memory{&gpu_memory_}, program_base{program_base_} {
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
128 return Common::CityHash64(data.get(), size); 171 return Common::CityHash64(data.get(), size);
129} 172}
130 173
174void GenericEnvironment::Dump(u64 hash) {
175 DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
176}
177
131void GenericEnvironment::Serialize(std::ofstream& file) const { 178void GenericEnvironment::Serialize(std::ofstream& file) const {
132 const u64 code_size{static_cast<u64>(CachedSize())}; 179 const u64 code_size{static_cast<u64>(CachedSize())};
133 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 180 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
207 u32 start_address_) 254 u32 start_address_)
208 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { 255 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
209 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); 256 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
257 initial_offset = sizeof(sph);
210 gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; 258 gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
211 switch (program) { 259 switch (program) {
212 case Maxwell::ShaderProgram::VertexA: 260 case Maxwell::ShaderProgram::VertexA:
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
323 if (stage == Shader::Stage::Compute) { 371 if (stage == Shader::Stage::Compute) {
324 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) 372 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
325 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); 373 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
374 initial_offset = 0;
326 } else { 375 } else {
327 file.read(reinterpret_cast<char*>(&sph), sizeof(sph)); 376 file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
377 initial_offset = sizeof(sph);
328 if (stage == Shader::Stage::Geometry) { 378 if (stage == Shader::Stage::Geometry) {
329 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); 379 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
330 } 380 }
331 } 381 }
332} 382}
333 383
384void FileEnvironment::Dump(u64 [[maybe_unused]] hash) {
385 DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
386}
387
334u64 FileEnvironment::ReadInstruction(u32 address) { 388u64 FileEnvironment::ReadInstruction(u32 address) {
335 if (address < read_lowest || address > read_highest) { 389 if (address < read_lowest || address > read_highest) {
336 throw Shader::LogicError("Out of bounds address {}", address); 390 throw Shader::LogicError("Out of bounds address {}", address);
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 6640e53d0..aae762b27 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -57,6 +57,8 @@ public:
57 57
58 [[nodiscard]] u64 CalculateHash() const; 58 [[nodiscard]] u64 CalculateHash() const;
59 59
60 void Dump(u64 hash) override;
61
60 void Serialize(std::ofstream& file) const; 62 void Serialize(std::ofstream& file) const;
61 63
62protected: 64protected:
@@ -82,6 +84,7 @@ protected:
82 84
83 u32 cached_lowest = std::numeric_limits<u32>::max(); 85 u32 cached_lowest = std::numeric_limits<u32>::max();
84 u32 cached_highest = 0; 86 u32 cached_highest = 0;
87 u32 initial_offset = 0;
85 88
86 bool has_unbound_instructions = false; 89 bool has_unbound_instructions = false;
87}; 90};
@@ -149,6 +152,8 @@ public:
149 152
150 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; 153 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
151 154
155 void Dump(u64 hash) override;
156
152private: 157private:
153 std::unique_ptr<u64[]> code; 158 std::unique_ptr<u64[]> code;
154 std::unordered_map<u32, Shader::TextureType> texture_types; 159 std::unordered_map<u32, Shader::TextureType> texture_types;
@@ -159,6 +164,7 @@ private:
159 u32 texture_bound{}; 164 u32 texture_bound{};
160 u32 read_lowest{}; 165 u32 read_lowest{};
161 u32 read_highest{}; 166 u32 read_highest{};
167 u32 initial_offset{};
162}; 168};
163 169
164void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 170void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 633fc295b..c1cf4050c 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
51 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); 51 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
52 ui->enable_nsight_aftermath->setEnabled(runtime_lock); 52 ui->enable_nsight_aftermath->setEnabled(runtime_lock);
53 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue()); 53 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
54 ui->dump_shaders->setEnabled(runtime_lock);
55 ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
54 ui->disable_macro_jit->setEnabled(runtime_lock); 56 ui->disable_macro_jit->setEnabled(runtime_lock);
55 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 57 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
56 ui->disable_loop_safety_checks->setEnabled(runtime_lock); 58 ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
73 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 75 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
74 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 76 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
75 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 77 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
78 Settings::values.dump_shaders = ui->dump_shaders->isChecked();
76 Settings::values.disable_shader_loop_safety_checks = 79 Settings::values.disable_shader_loop_safety_checks =
77 ui->disable_loop_safety_checks->isChecked(); 80 ui->disable_loop_safety_checks->isChecked();
78 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 81 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 0f3b51c8d..4dd870855 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -105,6 +105,19 @@
105 </property> 105 </property>
106 </widget> 106 </widget>
107 </item> 107 </item>
108 <item row="2" column="1">
109 <widget class="QCheckBox" name="dump_shaders">
110 <property name="enabled">
111 <bool>true</bool>
112 </property>
113 <property name="toolTip">
114 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
115 </property>
116 <property name="text">
117 <string>Dump Game Shaders</string>
118 </property>
119 </widget>
120 </item>
108 <item row="0" column="1"> 121 <item row="0" column="1">
109 <widget class="QCheckBox" name="disable_macro_jit"> 122 <widget class="QCheckBox" name="disable_macro_jit">
110 <property name="enabled"> 123 <property name="enabled">