diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index a412a80a2e..bfa9820939 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -83,9 +83,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { ENABLE_OVERLAY("enable_overlay"), // GPU Logging - GPU_LOGGING_ENABLED("gpu_logging_enabled"), GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"), GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"), + DUMP_GUEST_SHADERS("dump_guest_shaders"), + DUMP_MACROS("dump_macros"), GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"), GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index e3cd458a39..d8caa2828d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -931,13 +931,6 @@ abstract class SettingsItem( ) // GPU Logging settings - put( - SwitchSetting( - BooleanSetting.GPU_LOGGING_ENABLED, - titleId = R.string.gpu_logging_enabled, - descriptionId = R.string.gpu_logging_enabled_description - ) - ) put( SingleChoiceSetting( ByteSetting.GPU_LOG_LEVEL, @@ -954,6 +947,13 @@ abstract class SettingsItem( descriptionId = R.string.gpu_log_vulkan_calls_description ) ) + put( + SwitchSetting( + BooleanSetting.DUMP_GUEST_SHADERS, + titleId = R.string.dump_guest_shaders, + descriptionId = R.string.dump_guest_shaders_description + ) + ) put( SwitchSetting( BooleanSetting.GPU_LOG_SHADER_DUMPS, @@ -961,6 +961,13 @@ abstract class SettingsItem( descriptionId = R.string.gpu_log_shader_dumps_description ) ) + put( + SwitchSetting( + BooleanSetting.DUMP_MACROS, + titleId = R.string.dump_macros, + descriptionId = R.string.dump_macros_description + ) + ) put( SwitchSetting( BooleanSetting.GPU_LOG_MEMORY_TRACKING, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index e2d70f1670..07dd987f1d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -1288,14 +1288,17 @@ class SettingsFragmentPresenter( add(ShortSetting.DEBUG_KNOBS.key) add(StringSetting.PROGRAM_ARGS.key) - add(HeaderSetting(R.string.gpu_logging_header)) - add(BooleanSetting.GPU_LOGGING_ENABLED.key) - add(ByteSetting.GPU_LOG_LEVEL.key) - add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key) - add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key) - add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key) - add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key) - add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key) + if (!NativeConfig.isPerGameConfigLoaded()) { + add(HeaderSetting(R.string.gpu_logging_header)) + add(ByteSetting.GPU_LOG_LEVEL.key) + add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key) + add(BooleanSetting.DUMP_GUEST_SHADERS.key) + add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key) + add(BooleanSetting.DUMP_MACROS.key) + add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key) + add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key) + add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key) + } } } diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 044930eacc..c66c20d66a 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -569,8 +569,6 @@ تسجيل وحدة معالجة الرسومات - تمكين تسجيل وحدة معالجة الرسومات - تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno مستوى السجل مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية) تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 6e12baa4a1..8b505df846 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -563,8 +563,6 @@ Registros de la GPU - Activar los registros de la GPU - Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno Nivel de registros Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga) Registros de llamadas del API de Vulkan diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 30f73fea84..328141d70c 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -525,7 +525,6 @@ Journalisation GPU - Activer la journalisation GPU Niveau de journalisation Journaliser les appels API Vulkan Extraire les shaders diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index d1a9857469..badb24bc09 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -562,8 +562,6 @@ Ведение журнала ГПУ - Включить ведение журнала ГПУ - Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno Уровень журналирования Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка) Записывать вызовы Vulkan API diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index bef629def3..284cd5ba2e 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -565,8 +565,6 @@ Журналювання ГП - Увімкнути журналювання ГП - Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno Рівень журналювання Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію) Записувати виклики API Vulkan diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 1e203a4d2a..8a669986e6 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -559,8 +559,6 @@ GPU 日志 - 启用 GPU 日志 - 将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动 日志等级 GPU 日志的详细级别(数值越高 = 细节越多,开销越大) 记录 Vulkan API 调用 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index a425ce36ef..133ac4ff06 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -575,14 +575,16 @@ GPU Logging - Enable GPU Logging - Log GPU operations to eden_gpu.log for debugging Adreno drivers Log Level Detail level for GPU logs (higher = more detail, more overhead) Log Vulkan API Calls Track all Vulkan API calls in ring buffer - Dump Shaders - Save compiled shader SPIR-V to files + Dump SPIR-V Shaders + Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val. + Dump Guest (Maxwell) Shaders + Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm. + Dump Maxwell Macros + Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis. Track GPU Memory Monitor GPU memory allocations and deallocations Driver Debug Info diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 61f9acb1e7..9b582f1c2a 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true); SWITCHABLE(CpuAccuracy, true); SWITCHABLE(FullscreenMode, true); SWITCHABLE(GpuAccuracy, true); -SWITCHABLE(GpuLogLevel, true); SWITCHABLE(Language, true); SWITCHABLE(MemoryLayout, true); SWITCHABLE(NvdecEmulation, false); @@ -213,6 +212,16 @@ bool IsNceEnabled() { return is_nce_enabled; } +static u64 current_program_id = 0; + +void SetCurrentProgramID(u64 program_id) { + current_program_id = program_id; +} + +u64 GetCurrentProgramID() { + return current_program_id; +} + bool IsDockedMode() { return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; } diff --git a/src/common/settings.h b/src/common/settings.h index 5f6e1a2206..61979d836e 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -788,8 +788,8 @@ struct Values { false}; // runtime_modifiable_ — startup-only Setting dump_exefs{linkage, false, "dump_exefs", Category::Debugging}; Setting dump_nso{linkage, false, "dump_nso", Category::Debugging}; - Setting dump_shaders{ - linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default, + Setting dump_guest_shaders{ + linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default, false}; Setting dump_macros{ linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false}; @@ -813,9 +813,8 @@ struct Values { Setting disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging}; // GPU Logging - Setting gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging}; - SwitchableSetting gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level", - Category::Debugging}; + Setting gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level", + Category::Debugging}; Setting gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging}; Setting gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging}; Setting gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking", @@ -876,6 +875,9 @@ bool IsFastmemEnabled(); void SetNceEnabled(bool is_64bit); bool IsNceEnabled(); +void SetCurrentProgramID(u64 program_id); +u64 GetCurrentProgramID(); + bool IsOpenGL(); bool IsDockedMode(); diff --git a/src/core/core.cpp b/src/core/core.cpp index 33fece39f6..be856b90ef 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -326,6 +326,9 @@ struct System::Impl { LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id); + // Expose program id to dump sites and other global readers. + Settings::SetCurrentProgramID(params.program_id); + // Track launch time for frontend launches LaunchTimestampCache::SaveLaunchTimestamp(params.program_id); diff --git a/src/video_core/gpu_logging/gpu_logging.cpp b/src/video_core/gpu_logging/gpu_logging.cpp index 5f5e376e99..72e48e4996 100644 --- a/src/video_core/gpu_logging/gpu_logging.cpp +++ b/src/video_core/gpu_logging/gpu_logging.cpp @@ -4,6 +4,7 @@ #include "video_core/gpu_logging/gpu_logging.h" #include +#include #include #include "common/fs/file.h" @@ -280,13 +281,12 @@ void GPULogger::LogMemoryDeallocation(uintptr_t memory) { } void GPULogger::LogShaderCompilation(const std::string& shader_name, - const std::string& shader_info, - std::span spirv_code) { + const std::string& shader_info) { if (!initialized || current_level == LogLevel::Off) { return; } - if (!dump_shaders && current_level < LogLevel::Verbose) { + if (current_level < LogLevel::Verbose) { return; } @@ -294,38 +294,36 @@ void GPULogger::LogShaderCompilation(const std::string& shader_name, std::chrono::steady_clock::now().time_since_epoch()); const auto log_entry = fmt::format("[{}] [Shader] Compiled: {} ({})\n", - FormatTimestamp(timestamp), shader_name, shader_info); + FormatTimestamp(timestamp), shader_name, shader_info); WriteToLog(log_entry); +} - // Dump SPIR-V binary if enabled and we have data - if (dump_shaders && !spirv_code.empty()) { - using namespace Common::FS; - const auto& log_dir = GetEdenPath(EdenPath::LogDir); - const auto shaders_dir = log_dir / "shaders"; +bool IsActive() noexcept { + return Settings::values.gpu_log_level.GetValue() != Settings::GpuLogLevel::Off; +} - // Create directory on first dump - if (!shader_dump_dir_created) { - [[maybe_unused]] const bool created = CreateDir(shaders_dir); - shader_dump_dir_created = true; - } - - // Write SPIR-V binary file - const auto shader_path = shaders_dir / fmt::format("{}.spv", shader_name); - auto shader_file = std::make_unique( - shader_path, FileAccessMode::Write, FileType::BinaryFile); - - if (shader_file->IsOpen()) { - const size_t bytes_to_write = spirv_code.size() * sizeof(u32); - static_cast(shader_file->WriteSpan(spirv_code)); - shader_file->Close(); - - const auto dump_log = fmt::format("[{}] [Shader] Dumped SPIR-V: {} ({} bytes)\n", - FormatTimestamp(timestamp), shader_path.string(), bytes_to_write); - WriteToLog(dump_log); - } else { - LOG_WARNING(Render_Vulkan, "[GPU Logging] Failed to dump shader: {}", shader_path.string()); - } +void DumpSpirvShader(u64 shader_hash, std::span spirv_code) { + if (spirv_code.empty()) { + return; } + + using namespace Common::FS; + const auto& dump_dir = GetEdenPath(EdenPath::DumpDir); + + // Ensure DumpDir exists once. CreateDir is idempotent, so guarded to skip the syscall. + static std::once_flag dump_dir_flag; + std::call_once(dump_dir_flag, [&dump_dir]() { + [[maybe_unused]] const bool created = CreateDir(dump_dir); + }); + + const auto shader_path = dump_dir / fmt::format("{:016x}_{:016x}.spv", + Settings::GetCurrentProgramID(), shader_hash); + Common::FS::IOFile shader_file(shader_path, FileAccessMode::Write, FileType::BinaryFile); + if (!shader_file.IsOpen()) { + LOG_WARNING(Render_Vulkan, "[Shader Dump] Failed to open {}", shader_path.string()); + return; + } + static_cast(shader_file.WriteSpan(spirv_code)); } void GPULogger::LogPipelineStateChange(const std::string& state_info) { @@ -657,10 +655,6 @@ void GPULogger::EnableVulkanCallTracking(bool enabled) { track_vulkan_calls = enabled; } -void GPULogger::EnableShaderDumps(bool enabled) { - dump_shaders = enabled; -} - void GPULogger::EnableMemoryTracking(bool enabled) { track_memory = enabled; } diff --git a/src/video_core/gpu_logging/gpu_logging.h b/src/video_core/gpu_logging/gpu_logging.h index 134fa94c0f..b16988ed0b 100644 --- a/src/video_core/gpu_logging/gpu_logging.h +++ b/src/video_core/gpu_logging/gpu_logging.h @@ -87,8 +87,7 @@ public: void LogVulkanCall(const std::string& call_name, const std::string& params, int result); void LogMemoryAllocation(uintptr_t memory, u64 size, u32 memory_flags); void LogMemoryDeallocation(uintptr_t memory); - void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info, - std::span spirv_code = {}); + void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info); void LogPipelineStateChange(const std::string& state_info); void LogDriverDebugInfo(const std::string& debug_info); @@ -121,7 +120,6 @@ public: // Settings void SetLogLevel(LogLevel level); void EnableVulkanCallTracking(bool enabled); - void EnableShaderDumps(bool enabled); void EnableMemoryTracking(bool enabled); void EnableDriverDebugInfo(bool enabled); void SetRingBufferSize(size_t entries); @@ -171,7 +169,6 @@ private: // Feature flags bool track_vulkan_calls = true; - bool dump_shaders = false; bool track_memory = false; bool capture_driver_debug = false; @@ -179,15 +176,16 @@ private: std::set used_extensions; mutable std::mutex extension_mutex; - // Shader dump directory (created on demand) - bool shader_dump_dir_created = false; - // Stored state for crash dumps std::string stored_driver_debug_info; std::string stored_pipeline_state; mutable std::mutex state_mutex; }; +[[nodiscard]] bool IsActive() noexcept; + +void DumpSpirvShader(u64 shader_hash, std::span spirv_code); + // Helper to get stage name from index inline const char* GetShaderStageName(size_t stage_index) { static constexpr std::array stage_names{ diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp index 0d026a1ce5..1632d03664 100644 --- a/src/video_core/macro.cpp +++ b/src/video_core/macro.cpp @@ -1328,22 +1328,14 @@ Macro::Opcode MacroJITx64Impl::GetOpCode() const { #endif static void Dump(u64 hash, std::span code, bool decompiled = false) { - const auto base_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; - const auto macro_dir{base_dir / "macros"}; - if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { - LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); + const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; + if (!Common::FS::CreateDir(dump_dir)) { + LOG_ERROR(Common_Filesystem, "Failed to create dump directory"); return; } - auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; - - if (decompiled) { - auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)}; - if (Common::FS::Exists(name)) { - (void)Common::FS::RenameFile(name, new_name); - return; - } - name = new_name; - } + const char* const variant_suffix = decompiled ? "jit" : "raw"; + const auto name{dump_dir / fmt::format("{:016x}_{:016x}_{}.macro", + Settings::GetCurrentProgramID(), hash, variant_suffix)}; std::fstream macro_file(name, std::ios::out | std::ios::binary); if (!macro_file) { diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index a6aa7bf159..bc88796e3f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -481,7 +481,7 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const u32 cfg_offset = u32(env.StartAddress() + sizeof(Shader::ProgramHeader)); Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hashes[index]); } @@ -578,7 +578,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hash); } diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 2c1fd94e67..46a3b4bcb8 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -87,7 +87,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk }, *pipeline_cache); // Log compute pipeline creation - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogPipelineStateChange( "ComputePipeline created" ); @@ -223,7 +223,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, } // Log compute pipeline binding - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogPipelineBind(true, "compute pipeline"); } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 3b5338a058..01495b4515 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -532,7 +532,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; // Log graphics pipeline binding - if (bind_pipeline && Settings::values.gpu_logging_enabled.GetValue() && + if (bind_pipeline && GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string pipeline_info = fmt::format("hash=0x{:016x}", key.Hash()); GPU::Logging::GPULogger::GetInstance().LogPipelineBind(false, pipeline_info); @@ -986,7 +986,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }, *pipeline_cache); // Log graphics pipeline creation - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { const std::string pipeline_info = fmt::format( "GraphicsPipeline created: stages={}, attachments={}", shader_stages.size(), diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 798499141e..bcae3d5b5a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -751,7 +751,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hashes[index]); } @@ -783,14 +783,22 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( device.SaveShader(code); modules[stage_index] = BuildShader(device, code); - // Log shader compilation to GPU logger (with SPIR-V binary dump if enabled) - if (Settings::values.gpu_logging_enabled.GetValue()) { + // Text log + .spv dump. Text log is gated by gpu_log_level != Off; .spv dump + // is independent and gated only by gpu_log_shader_dumps. + const bool should_log = GPU::Logging::IsActive(); + const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue(); + if (should_log || should_dump) { static constexpr std::array stage_names{"vertex", "tess_control", "tess_eval", "geometry", "fragment"}; const std::string shader_name = fmt::format("shader_{:016x}_{}", key.unique_hashes[index], stage_names[stage_index]); - const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", - code.size() * sizeof(u32), key.unique_hashes[index]); - GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info, - std::span(code.data(), code.size())); + if (should_log) { + const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", + code.size() * sizeof(u32), key.unique_hashes[index]); + GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info); + } + if (should_dump) { + GPU::Logging::DumpSpirvShader(key.unique_hashes[index], + std::span(code.data(), code.size())); + } } if (device.HasDebuggingToolAttached()) { @@ -879,7 +887,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; // Dump it before error. - if (Settings::values.dump_shaders) { + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hash); } @@ -901,13 +909,20 @@ std::unique_ptr PipelineCache::CreateComputePipeline( device.SaveShader(code); vk::ShaderModule spv_module{BuildShader(device, code)}; - // Log compute shader compilation to GPU logger (with SPIR-V binary dump if enabled) - if (Settings::values.gpu_logging_enabled.GetValue()) { + // Text log + .spv dump. Same split as the graphics path. + const bool should_log = GPU::Logging::IsActive(); + const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue(); + if (should_log || should_dump) { const std::string shader_name = fmt::format("shader_{:016x}_compute", key.unique_hash); - const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", - code.size() * sizeof(u32), key.unique_hash); - GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info, - std::span(code.data(), code.size())); + if (should_log) { + const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}", + code.size() * sizeof(u32), key.unique_hash); + GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info); + } + if (should_dump) { + GPU::Logging::DumpSpirvShader(key.unique_hash, + std::span(code.data(), code.size())); + } } if (device.HasDebuggingToolAttached()) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 35b7aed552..dc84f5c0ad 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -270,7 +270,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { }); // Log draw call - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string params = is_indexed ? fmt::format("vertices={}, instances={}, firstIndex={}, baseVertex={}, baseInstance={}", @@ -331,7 +331,7 @@ void RasterizerVulkan::DrawIndirect() { }); // Log indirect draw call - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string log_params = fmt::format("drawCount={}, stride={}", params.max_draw_counts, params.stride); @@ -585,7 +585,7 @@ void RasterizerVulkan::DispatchCompute() { scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); // Log compute dispatch - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string params = fmt::format("groupCountX={}, groupCountY={}, groupCountZ={}", dim[0], dim[1], dim[2]); @@ -1110,7 +1110,7 @@ void RasterizerVulkan::HandleTransformFeedback() { regs.transform_feedback_enabled); if (regs.transform_feedback_enabled != 0) { // Log extension usage for transform feedback - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_transform_feedback", "HandleTransformFeedback"); } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index ec8c852284..d1e064c34a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -181,7 +181,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { state.render_area = render_area; // Log render pass begin - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string render_pass_info = fmt::format( "renderArea={}x{}, numImages={}", @@ -292,7 +292,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { case VK_SUCCESS: // Log successful queue submission - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogVulkanCall( "vkQueueSubmit", "", VK_SUCCESS); @@ -335,7 +335,7 @@ void Scheduler::EndRenderPass() query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); // Log render pass end - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 7cd6aa3e7c..567fd560af 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2332,7 +2332,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (has_custom_border_colors) { pnext = &border_ci; // Log extension usage for custom border color - if (Settings::values.gpu_logging_enabled.GetValue()) { + if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_custom_border_color", "Sampler::Sampler"); } diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 2f21a6f492..cf9c80fba6 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -18,6 +18,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging.h" +#include "common/settings.h" #include #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" @@ -73,36 +74,36 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture static std::string_view StageToPrefix(Shader::Stage stage) { switch (stage) { case Shader::Stage::VertexB: - return "VB"; + return "vs"; case Shader::Stage::TessellationControl: - return "TC"; + return "tc"; case Shader::Stage::TessellationEval: - return "TE"; + return "te"; case Shader::Stage::Geometry: - return "GS"; + return "gs"; case Shader::Stage::Fragment: - return "FS"; + return "fs"; case Shader::Stage::Compute: - return "CS"; + return "cs"; case Shader::Stage::VertexA: - return "VA"; + return "va"; default: - return "UK"; + return "uk"; } } -static void DumpImpl(u64 pipeline_hash, u64 shader_hash, std::span code, +static void DumpImpl(u64 /*pipeline_hash*/, u64 shader_hash, std::span code, [[maybe_unused]] u32 read_highest, [[maybe_unused]] u32 read_lowest, u32 initial_offset, Shader::Stage stage) { - const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::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"); + const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)}; + if (!Common::FS::CreateDir(dump_dir)) { + LOG_ERROR(Common_Filesystem, "Failed to create dump directory"); return; } const auto prefix = StageToPrefix(stage); - const auto name{base_dir / - fmt::format("{:016x}_{}_{:016x}.ash", pipeline_hash, prefix, shader_hash)}; + const auto name{dump_dir / + fmt::format("{:016x}_{:016x}_{}.ash", + Settings::GetCurrentProgramID(), shader_hash, prefix)}; std::fstream shader_file(name, std::ios::out | std::ios::binary); ASSERT(initial_offset % sizeof(u64) == 0); const size_t jump_index = initial_offset / sizeof(u64); diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 8d59a74a30..0f9aaa9a0b 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -76,7 +76,7 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, } // Route to GPU logger for tracking Vulkan validation messages - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { // Convert severity to result code for logging (negative = error) int result_code = 0; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index cd42bd22af..7bf73dcead 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1518,7 +1518,10 @@ std::vector Device::GetDeviceQueueCreateInfos() const { } void Device::InitializeGPULogging() { - if (!Settings::values.gpu_logging_enabled.GetValue()) { + // Get log level from settings — Off is the disable. + const auto log_level = static_cast( + static_cast(Settings::values.gpu_log_level.GetValue())); + if (log_level == GPU::Logging::LogLevel::Off) { return; } @@ -1532,18 +1535,12 @@ void Device::InitializeGPULogging() { detected_driver = GPU::Logging::DriverType::Qualcomm; } - // Get log level from settings - const auto log_level = static_cast( - static_cast(Settings::values.gpu_log_level.GetValue())); - // Initialize GPU logger GPU::Logging::GPULogger::GetInstance().Initialize(log_level, detected_driver); // Configure feature flags GPU::Logging::GPULogger::GetInstance().EnableVulkanCallTracking( Settings::values.gpu_log_vulkan_calls.GetValue()); - GPU::Logging::GPULogger::GetInstance().EnableShaderDumps( - Settings::values.gpu_log_shader_dumps.GetValue()); GPU::Logging::GPULogger::GetInstance().EnableMemoryTracking( Settings::values.gpu_log_memory_tracking.GetValue()); GPU::Logging::GPULogger::GetInstance().EnableDriverDebugInfo( diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 9a382fc0eb..e57864ede8 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -111,7 +111,7 @@ namespace Vulkan { : allocator{alloc}, allocation{a}, memory{info.deviceMemory}, offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { // Log GPU memory allocation - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(memory), @@ -179,7 +179,7 @@ namespace Vulkan { void MemoryCommit::Release() { if (allocation && allocator) { // Log GPU memory deallocation - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue() && memory != VK_NULL_HANDLE) { GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( @@ -243,7 +243,7 @@ namespace Vulkan { vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); // Log GPU memory allocation for images - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(alloc_info.deviceMemory), @@ -281,7 +281,7 @@ namespace Vulkan { vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); // Log GPU memory allocation for buffers - if (Settings::values.gpu_logging_enabled.GetValue() && + if (GPU::Logging::IsActive() && Settings::values.gpu_log_memory_tracking.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( reinterpret_cast(alloc_info.deviceMemory), diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index bc4ea5bd13..47abf66036 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -81,8 +81,8 @@ void ConfigureDebug::SetConfiguration() { ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue()); ui->enable_nsight_aftermath->setEnabled(runtime_lock); ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue()); - ui->dump_shaders->setEnabled(runtime_lock); - ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue()); + ui->dump_guest_shaders->setEnabled(runtime_lock); + ui->dump_guest_shaders->setChecked(Settings::values.dump_guest_shaders.GetValue()); ui->dump_macros->setEnabled(runtime_lock); ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue()); ui->disable_macro_jit->setEnabled(runtime_lock); @@ -94,6 +94,12 @@ void ConfigureDebug::SetConfiguration() { Settings::values.disable_shader_loop_safety_checks.GetValue()); ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); ui->debug_knobs_spinbox->setValue(Settings::values.debug_knobs.GetValue()); + + ui->gpu_log_level->setEnabled(runtime_lock); + ui->gpu_log_level->setCurrentIndex( + static_cast(Settings::values.gpu_log_level.GetValue())); + ui->gpu_log_shader_dumps->setEnabled(runtime_lock); + ui->gpu_log_shader_dumps->setChecked(Settings::values.gpu_log_shader_dumps.GetValue()); #ifdef YUZU_USE_QT_WEB_ENGINE ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue()); #else @@ -123,7 +129,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked(); Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); - Settings::values.dump_shaders = ui->dump_shaders->isChecked(); + Settings::values.dump_guest_shaders = ui->dump_guest_shaders->isChecked(); Settings::values.dump_macros = ui->dump_macros->isChecked(); Settings::values.disable_shader_loop_safety_checks = ui->disable_loop_safety_checks->isChecked(); @@ -135,6 +141,9 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.serial_battery = ui->serial_battery_edit->text().toUInt(); Settings::values.serial_unit = ui->serial_board_edit->text().toUInt(); Settings::values.debug_knobs = ui->debug_knobs_spinbox->value(); + Settings::values.gpu_log_level = + static_cast(ui->gpu_log_level->currentIndex()); + Settings::values.gpu_log_shader_dumps = ui->gpu_log_shader_dumps->isChecked(); Debugger::ToggleConsole(); Common::Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter.GetValue()); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index f76f3b012d..4e49c55f37 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -157,7 +157,7 @@ Logging - + @@ -197,7 +197,77 @@ - + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + GPU Logging/Level + + + + + + + Detail level for GPU logs. Off disables logging entirely. + + + + Off + + + + + Errors + + + + + Standard + + + + + Verbose + + + + + All + + + + + + + + + + + Show Log in Console + + + + true @@ -210,15 +280,20 @@ - - - - Show Log in Console - - - - + + + + 1 + 0 + + + + + 250 + 0 + + Open Log Location @@ -270,7 +345,7 @@ - + true @@ -294,15 +369,15 @@ - + true - When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + When checked, it will dump original Maxwell guest shader bytecode (the input to the recompiler) as .ash files under DumpDir/shaders/. Useful for inspection with nvdisasm. - Dump Game Shaders + Dump Guest (Maxwell) Shaders @@ -313,7 +388,7 @@ - + true @@ -326,7 +401,7 @@ - + true @@ -352,7 +427,7 @@ - + Qt::Orientation::Vertical @@ -388,6 +463,16 @@ + + + + When checked, it will dump the recompiler's output SPIR-V binaries (.spv) under LogDir/shaders/. Inspect via SPIRV-Tools (spirv-dis / spirv-cross / spirv-val) or RenderDoc. + + + Dump SPIR-V Shaders + + + @@ -514,31 +599,65 @@ - - - - 0 - - - 65535 - - - Bitmask for quick development toggles - - - Set debug knobs (bitmask) - - - 16-bit debug knob set for quick development toggles - - - (bitmask) - - - Debug Knobs: - - - + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Debug Knobs: + + + + + + + 0 + + + 65535 + + + Bitmask for quick development toggles + + + Set debug knobs (bitmask) + + + 16-bit debug knob set for quick development toggles + + + (bitmask) + + + + + + @@ -579,7 +698,7 @@ - + Qt::Orientation::Vertical