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