From b798d8f1aa6f9315c5a0d42dd03dbda23c9cd976 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 30 Mar 2025 23:20:39 +1000 Subject: [PATCH] Settings: Add option for enabling GPU-based validation --- src/core/fullscreen_ui.cpp | 7 ++ src/core/gpu_thread.cpp | 8 +- src/core/settings.cpp | 3 + src/core/settings.h | 1 + src/duckstation-qt/graphicssettingswidget.cpp | 4 + src/duckstation-qt/graphicssettingswidget.ui | 75 ++++++++++--------- src/util/d3d12_device.cpp | 16 +++- src/util/gpu_device.cpp | 3 +- src/util/gpu_device.h | 6 +- 9 files changed, 80 insertions(+), 43 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index c3495fb12..dee64297d 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -6434,6 +6434,11 @@ void FullscreenUI::DrawAdvancedSettingsPage() FSUI_CSTR("Enable debugging when supported by the host's renderer API. Only for developer use."), "GPU", "UseDebugDevice", false); + DrawToggleSetting( + bsi, FSUI_CSTR("Enable GPU-Based Validation"), + FSUI_CSTR("Enable GPU-based validation when supported by the host's renderer API. Only for developer use."), "GPU", + "UseGPUBasedValidation", false); + DrawToggleSetting( bsi, FSUI_CSTR("Load Devices From Save States"), FSUI_CSTR("When enabled, memory cards and controllers will be overwritten when save states are loaded."), "Main", @@ -9202,6 +9207,8 @@ TRANSLATE_NOOP("FullscreenUI", "Enable Achievements"); TRANSLATE_NOOP("FullscreenUI", "Enable Cheats"); TRANSLATE_NOOP("FullscreenUI", "Enable Discord Presence"); TRANSLATE_NOOP("FullscreenUI", "Enable Fast Boot"); +TRANSLATE_NOOP("FullscreenUI", "Enable GPU-Based Validation"); +TRANSLATE_NOOP("FullscreenUI", "Enable GPU-based validation when supported by the host's renderer API. Only for developer use."); TRANSLATE_NOOP("FullscreenUI", "Enable In-Game Overlays"); TRANSLATE_NOOP("FullscreenUI", "Enable Overclocking"); TRANSLATE_NOOP("FullscreenUI", "Enable Post Processing"); diff --git a/src/core/gpu_thread.cpp b/src/core/gpu_thread.cpp index 36c701cbd..5ee5eaef7 100644 --- a/src/core/gpu_thread.cpp +++ b/src/core/gpu_thread.cpp @@ -621,7 +621,7 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_ if (g_gpu_settings.gpu_disable_compressed_textures) disabled_features |= GPUDevice::FEATURE_MASK_COMPRESSED_TEXTURES; - // Don't dump shaders on debug builds for Android, users will complain about storage... + // Don't dump shaders on debug builds for Android, users will complain about storage... #if !defined(__ANDROID__) || defined(_DEBUG) const std::string_view shader_dump_directory(EmuFolders::DataRoot); #else @@ -636,9 +636,9 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_ Host::GetStringSettingValue("GPU", "Adapter"), static_cast(disabled_features), shader_dump_directory, g_gpu_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache), - SHADER_CACHE_VERSION, g_gpu_settings.gpu_use_debug_device, wi.value(), s_state.requested_vsync, - s_state.requested_allow_present_throttle, fullscreen_mode.has_value() ? &fullscreen_mode.value() : nullptr, - exclusive_fullscreen_control, &create_error)) + SHADER_CACHE_VERSION, g_gpu_settings.gpu_use_debug_device, g_gpu_settings.gpu_use_debug_device_gpu_validation, + wi.value(), s_state.requested_vsync, s_state.requested_allow_present_throttle, + fullscreen_mode.has_value() ? &fullscreen_mode.value() : nullptr, exclusive_fullscreen_control, &create_error)) { ERROR_LOG("Failed to create GPU device: {}", create_error.GetDescription()); if (g_gpu_device) diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 734a62be3..d61ba6058 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -216,6 +216,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro gpu_automatic_resolution_scale = (gpu_resolution_scale == 0); gpu_multisamples = static_cast(si.GetUIntValue("GPU", "Multisamples", 1u)); gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false); + gpu_use_debug_device_gpu_validation = si.GetBoolValue("GPU", "UseGPUBasedValidation", false); gpu_disable_shader_cache = si.GetBoolValue("GPU", "DisableShaderCache", false); gpu_disable_dual_source_blend = si.GetBoolValue("GPU", "DisableDualSourceBlend", false); gpu_disable_framebuffer_fetch = si.GetBoolValue("GPU", "DisableFramebufferFetch", false); @@ -576,6 +577,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const if (!ignore_base) { si.SetBoolValue("GPU", "UseDebugDevice", gpu_use_debug_device); + si.SetBoolValue("GPU", "UseGPUBasedValidation", gpu_use_debug_device_gpu_validation); si.SetBoolValue("GPU", "DisableShaderCache", gpu_disable_shader_cache); si.SetBoolValue("GPU", "DisableDualSourceBlend", gpu_disable_dual_source_blend); si.SetBoolValue("GPU", "DisableFramebufferFetch", gpu_disable_framebuffer_fetch); @@ -1127,6 +1129,7 @@ bool Settings::AreGPUDeviceSettingsChanged(const Settings& old_settings) const { return (gpu_adapter != old_settings.gpu_adapter || gpu_use_thread != old_settings.gpu_use_thread || gpu_use_debug_device != old_settings.gpu_use_debug_device || + gpu_use_debug_device_gpu_validation != old_settings.gpu_use_debug_device_gpu_validation || gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache || gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend || gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch || diff --git a/src/core/settings.h b/src/core/settings.h index b50cd8563..7f45d1367 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -94,6 +94,7 @@ struct GPUSettings bool gpu_use_thread : 1 = true; bool gpu_use_software_renderer_for_readbacks : 1 = false; bool gpu_use_debug_device : 1 = false; + bool gpu_use_debug_device_gpu_validation : 1 = false; bool gpu_disable_shader_cache : 1 = false; bool gpu_disable_dual_source_blend : 1 = false; bool gpu_disable_framebuffer_fetch : 1 = false; diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index ffd7f694e..c8e6d2c81 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -310,6 +310,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuDumpFastReplayMode, "GPU", "DumpFastReplayMode", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "GPU", "UseDebugDevice", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useGPUBasedValidation, "GPU", "UseGPUBasedValidation", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableShaderCache, "GPU", "DisableShaderCache", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "GPU", "DisableDualSourceBlend", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "GPU", "DisableFramebufferFetch", @@ -631,6 +632,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* dialog->registerWidgetHelp( m_ui.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"), tr("Enable debugging when supported by the host's renderer API. Only for developer use.")); + dialog->registerWidgetHelp( + m_ui.useGPUBasedValidation, tr("Use GPU-Based Validation"), tr("Unchecked"), + tr("Enable GPU-based validation when supported by the renderer API. Only for developer use.")); dialog->registerWidgetHelp( m_ui.disableShaderCache, tr("Disable Shader Cache"), tr("Unchecked"), tr("Forces shaders to be compiled for every run of the program. Only for developer use.")); diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index dca0a0134..ddf596f5c 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -1350,6 +1350,20 @@ + + + + Disable Compute Shaders + + + + + + + Disable Dual-Source Blending + + + @@ -1357,35 +1371,21 @@ - - + + - Disable Texture Buffers + Disable Memory Import - - + + - Disable Dual-Source Blending + Disable Rasterizer Order Views - - - - Disable Shader Cache - - - - - - - Disable Framebuffer Fetch - - - - + Disable Texture Copy To Self @@ -1393,33 +1393,40 @@ - + - Disable Memory Import + Disable Texture Buffers - - + + - Disable Rasterizer Order Views + Disable Framebuffer Fetch - - - - Disable Compute Shaders - - - - + Disable Compressed Textures + + + + Disable Shader Cache + + + + + + + Use GPU-Based Validation + + + diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index ff61a32f5..8d46d834b 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -186,10 +186,22 @@ bool D3D12Device::CreateDeviceAndMainSwapChain(std::string_view adapter, Feature if (m_debug_device) { ComPtr debug12; - hr = D3D12GetDebugInterface(IID_PPV_ARGS(debug12.GetAddressOf())); - if (SUCCEEDED(hr)) + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug12.GetAddressOf())))) { + INFO_LOG("Enabling debug layer."); debug12->EnableDebugLayer(); + + ComPtr debug12_1; + if (SUCCEEDED(debug12.As(&debug12_1))) + { + INFO_LOG("Enabling GPU-based validation."); + debug12_1->SetEnableGPUBasedValidation(true); + } + else + { + ERROR_LOG("GPU-based validation requested but not available."); + m_debug_device_gpu_validation = false; + } } else { diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 261e0da5f..d6bd40675 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -449,11 +449,12 @@ GPUDevice::AdapterInfoList GPUDevice::GetAdapterListForAPI(RenderAPI api) bool GPUDevice::Create(std::string_view adapter, FeatureMask disabled_features, std::string_view shader_dump_path, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, - const WindowInfo& wi, GPUVSyncMode vsync, bool allow_present_throttle, + bool gpu_validation, const WindowInfo& wi, GPUVSyncMode vsync, bool allow_present_throttle, const ExclusiveFullscreenMode* exclusive_fullscreen_mode, std::optional exclusive_fullscreen_control, Error* error) { m_debug_device = debug_device; + m_debug_device_gpu_validation = debug_device && gpu_validation; s_shader_dump_path = shader_dump_path; INFO_LOG("Main render window is {}x{}.", wi.surface_width, wi.surface_height); diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index fc61a1def..1a587f24a 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -727,8 +727,9 @@ public: ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; } bool Create(std::string_view adapter, FeatureMask disabled_features, std::string_view shader_dump_path, - std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, const WindowInfo& wi, - GPUVSyncMode vsync, bool allow_present_throttle, const ExclusiveFullscreenMode* exclusive_fullscreen_mode, + std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, bool gpu_validation, + const WindowInfo& wi, GPUVSyncMode vsync, bool allow_present_throttle, + const ExclusiveFullscreenMode* exclusive_fullscreen_mode, std::optional exclusive_fullscreen_control, Error* error); void Destroy(); @@ -995,6 +996,7 @@ protected: bool m_gpu_timing_enabled = false; bool m_debug_device = false; + bool m_debug_device_gpu_validation = false; }; extern std::unique_ptr g_gpu_device;