diff options
| author | 2021-11-17 04:19:29 +0100 | |
|---|---|---|
| committer | 2022-01-04 02:39:00 +0100 | |
| commit | f58ee3f15f7427a8b834286384931bcf821ed771 (patch) | |
| tree | 1ef8367f64cbee6220c4e5600249697e5cd8e826 | |
| parent | Merge pull request #7648 from bunnei/thread-pinning (diff) | |
| download | yuzu-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.h | 1 | ||||
| -rw-r--r-- | src/shader_recompiler/environment.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.cpp | 11 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 9 | ||||
| -rw-r--r-- | src/video_core/shader_environment.cpp | 54 | ||||
| -rw-r--r-- | src/video_core/shader_environment.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_debug.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_debug.ui | 13 |
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 | ||
| 62 | static 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 | |||
| 83 | static 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 | |||
| 60 | GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, | 103 | GenericEnvironment::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 | ||
| 174 | void GenericEnvironment::Dump(u64 hash) { | ||
| 175 | DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage); | ||
| 176 | } | ||
| 177 | |||
| 131 | void GenericEnvironment::Serialize(std::ofstream& file) const { | 178 | void 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 | ||
| 384 | void FileEnvironment::Dump(u64 [[maybe_unused]] hash) { | ||
| 385 | DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage); | ||
| 386 | } | ||
| 387 | |||
| 334 | u64 FileEnvironment::ReadInstruction(u32 address) { | 388 | u64 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 | ||
| 62 | protected: | 64 | protected: |
| @@ -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 | |||
| 152 | private: | 157 | private: |
| 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 | ||
| 164 | void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, | 170 | void 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"> |