diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentPresenter.java index 11ad71a9cf..3caeaa514b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentPresenter.java @@ -259,10 +259,14 @@ public final class SettingsFragmentPresenter { IntSetting videoBackend = new IntSetting(SettingsFile.KEY_VIDEO_BACKEND_INDEX, SettingsFile.SECTION_CORE, SettingsFile.SETTINGS_DOLPHIN, getVideoBackendValue()); Setting showFps = null; + Setting shaderCompilationMode = null; + Setting waitForShaders = null; if (!mSettings.get(SettingsFile.SETTINGS_GFX).isEmpty()) { showFps = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_SHOW_FPS); + shaderCompilationMode = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_SHADER_COMPILATION_MODE); + waitForShaders = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_WAIT_FOR_SHADERS); } else { @@ -276,6 +280,8 @@ public final class SettingsFragmentPresenter sl.add(new SingleChoiceSetting(SettingsFile.KEY_VIDEO_BACKEND_INDEX, SettingsFile.SECTION_CORE, SettingsFile.SETTINGS_DOLPHIN, R.string.video_backend, R.string.video_backend_descrip, R.array.videoBackendEntries, R.array.videoBackendValues, 0, videoBackend)); sl.add(new CheckBoxSetting(SettingsFile.KEY_SHOW_FPS, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.show_fps, 0, false, showFps)); + sl.add(new SingleChoiceSetting(SettingsFile.KEY_SHADER_COMPILATION_MODE, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.shader_compilation_mode, R.string.shader_compilation_mode_descrip, R.array.shaderCompilationModeEntries, R.array.shaderCompilationModeValues, 0, shaderCompilationMode)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_WAIT_FOR_SHADERS, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.wait_for_shaders, 0, false, waitForShaders)); sl.add(new SubmenuSetting(null, null, R.string.enhancements, 0, SettingsFile.SECTION_GFX_ENHANCEMENTS)); sl.add(new SubmenuSetting(null, null, R.string.hacks, 0, SettingsFile.SECTION_GFX_HACKS)); @@ -290,7 +296,8 @@ public final class SettingsFragmentPresenter Setting perPixel = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_PER_PIXEL); Setting forceFilter = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_ENHANCEMENTS).getSetting(SettingsFile.KEY_FORCE_FILTERING); Setting disableFog = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_DISABLE_FOG); - Setting uberShaderMode = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_UBERSHADER_MODE); + Setting shaderCompilationMode = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_SHADER_COMPILATION_MODE); + Setting waitForShaders = mSettings.get(SettingsFile.SETTINGS_GFX).get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_WAIT_FOR_SHADERS); sl.add(new SingleChoiceSetting(SettingsFile.KEY_INTERNAL_RES, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.internal_resolution, R.string.internal_resolution_descrip, R.array.internalResolutionEntries, R.array.internalResolutionValues, 0, resolution)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_FSAA, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.FSAA, R.string.FSAA_descrip, R.array.FSAAEntries, R.array.FSAAValues, 0, fsaa)); @@ -304,7 +311,6 @@ public final class SettingsFragmentPresenter sl.add(new CheckBoxSetting(SettingsFile.KEY_PER_PIXEL, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.per_pixel_lighting, R.string.per_pixel_lighting_descrip, false, perPixel)); sl.add(new CheckBoxSetting(SettingsFile.KEY_FORCE_FILTERING, SettingsFile.SECTION_GFX_ENHANCEMENTS, SettingsFile.SETTINGS_GFX, R.string.force_texture_filtering, R.string.force_texture_filtering_descrip, false, forceFilter)); sl.add(new CheckBoxSetting(SettingsFile.KEY_DISABLE_FOG, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.disable_fog, R.string.disable_fog_descrip, false, disableFog)); - sl.add(new SingleChoiceSetting(SettingsFile.KEY_UBERSHADER_MODE, SettingsFile.SECTION_GFX_SETTINGS, SettingsFile.SETTINGS_GFX, R.string.ubershader_mode, R.string.ubershader_mode_descrip, R.array.uberShaderModeEntries, R.array.uberShaderModeValues, 0, uberShaderMode)); /* Check if we support stereo diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SettingsFile.java index 3e5a1586a3..34d4e2fa71 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/SettingsFile.java @@ -107,7 +107,8 @@ public final class SettingsFile public static final String KEY_IMMEDIATE_XFB = "ImmediateXFBEnable"; public static final String KEY_FAST_DEPTH = "FastDepthCalc"; public static final String KEY_ASPECT_RATIO = "AspectRatio"; - public static final String KEY_UBERSHADER_MODE = "UberShaderMode"; + public static final String KEY_SHADER_COMPILATION_MODE = "ShaderCompilationMode"; + public static final String KEY_WAIT_FOR_SHADERS = "WaitForShadersBeforeStarting"; public static final String KEY_GCPAD_TYPE = "SIDevice"; diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index ce9c4a542c..c9d6f9a414 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -92,15 +92,17 @@ - - Disabled - Hybrid - Exclusive + + Synchronous + Synchronous (Ubershaders) + Asynchronous (Ubershaders) + Asynchronous (Skip Drawing) - + 0 1 2 + 3 diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index c975edad91..ea7edcfbcd 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -184,8 +184,9 @@ Uses a less accurate algorithm to calculate depth values. Aspect Ratio Select what aspect ratio to use when rendering - Ubershader Mode - Specifies when to use Ubershaders. Disabled - Never, Hybrid - Use ubershaders while compiling specialized shaders. Exclusive - Use only ubershaders, largest performance impact. + Shader Compilation Mode + Specifies when to use Ubershaders. Disabled - Never, Hybrid - Use ubershaders while compiling specialized shaders. Exclusive - Use only ubershaders, largest performance impact. Skip Drawing - Do not draw objects while shaders are compiling, will cause broken effects. + Compile Shaders Before Starting Yes diff --git a/Source/Core/Core/Analytics.cpp b/Source/Core/Core/Analytics.cpp index db881d61de..6c45c1e400 100644 --- a/Source/Core/Core/Analytics.cpp +++ b/Source/Core/Core/Analytics.cpp @@ -178,17 +178,19 @@ void DolphinAnalytics::MakeBaseBuilder() m_base_builder = builder; } -static const char* GetUbershaderMode(const VideoConfig& video_config) +static const char* GetShaderCompilationMode(const VideoConfig& video_config) { - switch (video_config.iUberShaderMode) + switch (video_config.iShaderCompilationMode) { - case UberShaderMode::Exclusive: - return "exclusive"; - case UberShaderMode::Hybrid: - return "hybrid"; - case UberShaderMode::Disabled: + case ShaderCompilationMode::AsynchronousUberShaders: + return "async-ubershaders"; + case ShaderCompilationMode::AsynchronousSkipRendering: + return "async-skip-rendering"; + case ShaderCompilationMode::SynchronousUberShaders: + return "sync-ubershaders"; + case ShaderCompilationMode::Synchronous: default: - return "disabled"; + return "sync"; } } @@ -234,7 +236,8 @@ void DolphinAnalytics::MakePerGameBuilder() builder.AddData("cfg-gfx-tc-samples", g_Config.iSafeTextureCache_ColorSamples); builder.AddData("cfg-gfx-stereo-mode", static_cast(g_Config.stereo_mode)); builder.AddData("cfg-gfx-per-pixel-lighting", g_Config.bEnablePixelLighting); - builder.AddData("cfg-gfx-ubershader-mode", GetUbershaderMode(g_Config)); + builder.AddData("cfg-gfx-shader-compilation-mode", GetShaderCompilationMode(g_Config)); + builder.AddData("cfg-gfx-wait-for-shaders", g_Config.bWaitForShadersBeforeStarting); builder.AddData("cfg-gfx-fast-depth", g_Config.bFastDepthCalc); builder.AddData("cfg-gfx-vertex-rounding", g_Config.UseVertexRounding()); diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 10a70c6020..7412c81740 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -78,8 +78,9 @@ const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{ const ConfigInfo GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true}; const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING{ {System::GFX, "Settings", "WaitForShadersBeforeStarting"}, false}; -const ConfigInfo GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"}, - static_cast(UberShaderMode::Disabled)}; +const ConfigInfo GFX_SHADER_COMPILATION_MODE{ + {System::GFX, "Settings", "ShaderCompilationMode"}, + static_cast(ShaderCompilationMode::Synchronous)}; const ConfigInfo GFX_SHADER_COMPILER_THREADS{ {System::GFX, "Settings", "ShaderCompilerThreads"}, 1}; const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS{ diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 5c1684777b..999192c578 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -60,7 +60,7 @@ extern const ConfigInfo GFX_BACKEND_MULTITHREADING; extern const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL; extern const ConfigInfo GFX_SHADER_CACHE; extern const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING; -extern const ConfigInfo GFX_UBERSHADER_MODE; +extern const ConfigInfo GFX_SHADER_COMPILATION_MODE; extern const ConfigInfo GFX_SHADER_COMPILER_THREADS; extern const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index ef71c085ab..d9e210deaa 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -46,8 +46,9 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location, Config::GFX_ENABLE_VALIDATION_LAYER.location, Config::GFX_BACKEND_MULTITHREADING.location, Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL.location, Config::GFX_SHADER_CACHE.location, - Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING.location, Config::GFX_UBERSHADER_MODE.location, - Config::GFX_SHADER_COMPILER_THREADS.location, Config::GFX_SHADER_PRECOMPILER_THREADS.location, + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING.location, + Config::GFX_SHADER_COMPILATION_MODE.location, Config::GFX_SHADER_COMPILER_THREADS.location, + Config::GFX_SHADER_PRECOMPILER_THREADS.location, Config::GFX_SW_ZCOMPLOC.location, Config::GFX_SW_ZFREEZE.location, Config::GFX_SW_DUMP_OBJECTS.location, Config::GFX_SW_DUMP_TEV_STAGES.location, diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index 52af9c951d..d00bc5ee1a 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -45,6 +45,7 @@ set(SRCS Config/Graphics/HacksWidget.cpp Config/Graphics/GraphicsBool.cpp Config/Graphics/GraphicsChoice.cpp + Config/Graphics/GraphicsRadio.cpp Config/Graphics/GraphicsSlider.cpp Config/Graphics/GraphicsWidget.cpp Config/Graphics/GraphicsWindow.cpp diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp index b6baa9819f..06f611f5d1 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp @@ -63,9 +63,6 @@ void EnhancementsWidget::CreateWidgets() m_af_combo = new GraphicsChoice({tr("1x"), tr("2x"), tr("4x"), tr("8x"), tr("16x")}, Config::GFX_ENHANCE_MAX_ANISOTROPY); - m_ubershader_combo = new GraphicsChoice({tr("Disabled"), tr("Hybrid"), tr("Exclusive")}, - Config::GFX_UBERSHADER_MODE); - m_pp_effect = new QComboBox(); m_configure_pp_effect = new QPushButton(tr("Configure")); m_scaled_efb_copy = new GraphicsBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED); @@ -85,9 +82,6 @@ void EnhancementsWidget::CreateWidgets() enhancements_layout->addWidget(new QLabel(tr("Anisotropic Filtering:")), 2, 0); enhancements_layout->addWidget(m_af_combo, 2, 1, 1, -1); - enhancements_layout->addWidget(new QLabel(tr("Ubershaders:")), 3, 0); - enhancements_layout->addWidget(m_ubershader_combo, 3, 1, 1, -1); - enhancements_layout->addWidget(new QLabel(tr("Post-Processing Effect:")), 4, 0); enhancements_layout->addWidget(m_pp_effect, 4, 1); enhancements_layout->addWidget(m_configure_pp_effect, 4, 2); @@ -243,15 +237,6 @@ void EnhancementsWidget::AddDescriptions() "Enable anisotropic filtering.\nEnhances visual quality of textures that are at oblique " "viewing angles.\nMight cause issues in a small number of games.\n\nIf unsure, select 1x."); - static const char* TR_UBERSHADER_DESCRIPTION = - QT_TR_NOOP("Disabled: Ubershaders are never used. Stuttering will occur during shader " - "compilation, but GPU demands are low. Recommended for low-end hardware.\n\n" - "Hybrid: Ubershaders will be used to prevent stuttering during shader " - "compilation, but traditional shaders will be used when they will not cause " - "stuttering. Balances performance and smoothness.\n\n" - "Exclusive: Ubershaders will always be used. Only recommended for high-end " - "systems."); - static const char* TR_POSTPROCESSING_DESCRIPTION = QT_TR_NOOP( "Apply a post-processing effect after finishing a frame.\n\nIf unsure, select (off)."); @@ -303,7 +288,6 @@ void EnhancementsWidget::AddDescriptions() AddDescription(m_ir_combo, TR_INTERNAL_RESOLUTION_DESCRIPTION); AddDescription(m_aa_combo, TR_ANTIALIAS_DESCRIPTION); AddDescription(m_af_combo, TR_ANISOTROPIC_FILTERING_DESCRIPTION); - AddDescription(m_ubershader_combo, TR_UBERSHADER_DESCRIPTION); AddDescription(m_pp_effect, TR_POSTPROCESSING_DESCRIPTION); AddDescription(m_scaled_efb_copy, TR_SCALED_EFB_COPY_DESCRIPTION); AddDescription(m_per_pixel_lighting, TR_PER_PIXEL_LIGHTING_DESCRIPTION); diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h index fd5b20a1b4..ac23ed84e9 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.h @@ -30,7 +30,6 @@ private: QComboBox* m_ir_combo; QComboBox* m_aa_combo; QComboBox* m_af_combo; - QComboBox* m_ubershader_combo; QComboBox* m_pp_effect; QPushButton* m_configure_pp_effect; QCheckBox* m_scaled_efb_copy; diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp index 21281488b8..1542ae535a 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "Core/Config/GraphicsSettings.h" @@ -18,6 +19,7 @@ #include "Core/Core.h" #include "DolphinQt2/Config/Graphics/GraphicsBool.h" #include "DolphinQt2/Config/Graphics/GraphicsChoice.h" +#include "DolphinQt2/Config/Graphics/GraphicsRadio.h" #include "DolphinQt2/Config/Graphics/GraphicsWindow.h" #include "DolphinQt2/Settings.h" #include "UICommon/VideoUtils.h" @@ -87,8 +89,6 @@ void GeneralWidget::CreateWidgets() m_keep_window_top = new QCheckBox(tr("Keep Window on Top")); m_hide_cursor = new QCheckBox(tr("Hide Mouse Cursor")); m_render_main_window = new QCheckBox(tr("Render to Main Window")); - m_wait_for_shaders = new GraphicsBool(tr("Immediately Compile Shaders"), - Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); m_options_box->setLayout(m_options_layout); @@ -103,10 +103,30 @@ void GeneralWidget::CreateWidgets() m_options_layout->addWidget(m_hide_cursor, 3, 0); m_options_layout->addWidget(m_render_main_window, 3, 1); - m_options_layout->addWidget(m_wait_for_shaders, 4, 0); + + // Other + auto* shader_compilation_box = new QGroupBox(tr("Shader Compilation")); + auto* shader_compilation_layout = new QGridLayout(); + + const std::array modes = {{ + "Synchronous", "Synchronous (Ubershaders)", "Asynchronous (Ubershaders)", + "Asynchronous (Skip Drawing)", + }}; + for (size_t i = 0; i < modes.size(); i++) + { + m_shader_compilation_mode[i] = new GraphicsRadioInt( + tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast(i)); + shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast(i / 2), + static_cast(i % 2)); + } + m_wait_for_shaders = new GraphicsBool(tr("Compile Shaders Before Starting"), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); + shader_compilation_layout->addWidget(m_wait_for_shaders); + shader_compilation_box->setLayout(shader_compilation_layout); main_layout->addWidget(m_video_box); main_layout->addWidget(m_options_box); + main_layout->addWidget(shader_compilation_box); main_layout->addStretch(); setLayout(main_layout); @@ -268,12 +288,27 @@ void GeneralWidget::AddDescriptions() static const char* TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION = QT_TR_NOOP("When playing on NetPlay, show chat messages, buffer changes and " "desync alerts.\n\nIf unsure, leave this unchecked."); - static const char* TR_WAIT_FOR_SHADERS_DESCRIPTION = QT_TR_NOOP( - "Waits for all shaders to finish compiling before starting a game. Enabling this " - "option may reduce stuttering or hitching for a short time after the game is " - "started, at the cost of a longer delay before the game starts.\n\nFor systems " - "with two or fewer cores, it is recommended to enable this option, as a large " - "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); + static const char* TR_SHADER_COMPILE_SYNC_DESCRIPTION = + QT_TR_NOOP("Ubershaders are never used. Stuttering will occur during shader " + "compilation, but GPU demands are low. Recommended for low-end hardware.\n\nIf " + "unsure, select this mode."); + static const char* TR_SHADER_COMPILE_UBER_ONLY_DESCRIPTION = + QT_TR_NOOP("Ubershaders will always be used. Provides a near stutter-free experience at the " + "cost of high GPU requirements. Only recommended for high-end systems."); + static const char* TR_SHADER_COMPILE_ASYNC_UBER_DESCRIPTION = + QT_TR_NOOP("Ubershaders will be used to prevent stuttering during shader compilation, but " + "specialized shaders will be used when they will not cause stuttering."); + static const char* TR_SHADER_COMPILE_ASYNC_SKIP_DESCRIPTION = + QT_TR_NOOP("Instead of using ubershaders during shader compilation, objects which use these " + "shaders will be not be rendered. This can further reduce stuttering and " + "performance requirements, compared to ubershaders, at the cost of introducing " + "visual glitches and broken effects. Not recommended."); + static const char* TR_SHADER_COMPILE_BEFORE_START_DESCRIPTION = + QT_TR_NOOP("Waits for all shaders to finish compiling before starting a game. Enabling this " + "option may reduce stuttering or hitching for a short time after the game is " + "started, at the cost of a longer delay before the game starts. For systems with " + "two or fewer cores, it is recommended to enable this option, as a large shader " + "queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); AddDescription(m_backend_combo, TR_BACKEND_DESCRIPTION); #ifdef _WIN32 @@ -291,7 +326,11 @@ void GeneralWidget::AddDescriptions() AddDescription(m_show_messages, TR_SHOW_FPS_DESCRIPTION); AddDescription(m_keep_window_top, TR_KEEP_WINDOW_ON_TOP_DESCRIPTION); AddDescription(m_show_messages, TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION); - AddDescription(m_wait_for_shaders, TR_WAIT_FOR_SHADERS_DESCRIPTION); + AddDescription(m_shader_compilation_mode[0], TR_SHADER_COMPILE_SYNC_DESCRIPTION); + AddDescription(m_shader_compilation_mode[1], TR_SHADER_COMPILE_UBER_ONLY_DESCRIPTION); + AddDescription(m_shader_compilation_mode[2], TR_SHADER_COMPILE_ASYNC_UBER_DESCRIPTION); + AddDescription(m_shader_compilation_mode[3], TR_SHADER_COMPILE_ASYNC_SKIP_DESCRIPTION); + AddDescription(m_wait_for_shaders, TR_SHADER_COMPILE_BEFORE_START_DESCRIPTION); } void GeneralWidget::OnBackendChanged(const QString& backend_name) { diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h index 8b2d22f331..b8dd799649 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h @@ -4,11 +4,13 @@ #pragma once +#include #include "DolphinQt2/Config/Graphics/GraphicsWidget.h" class GraphicsWindow; class QCheckBox; class QComboBox; +class QRadioButton; class QGridLayout; namespace X11Utils @@ -52,6 +54,7 @@ private: QCheckBox* m_keep_window_top; QCheckBox* m_hide_cursor; QCheckBox* m_render_main_window; + std::array m_shader_compilation_mode{}; QCheckBox* m_wait_for_shaders; X11Utils::XRRConfiguration* m_xrr_config; diff --git a/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.cpp b/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.cpp new file mode 100644 index 0000000000..c8f87a548b --- /dev/null +++ b/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/Config/Graphics/GraphicsRadio.h" + +#include "Common/Config/Config.h" +#include "DolphinQt2/Settings.h" + +GraphicsRadioInt::GraphicsRadioInt(const QString& label, const Config::ConfigInfo& setting, + int value) + : QRadioButton(label), m_setting(setting), m_value(value) +{ + setChecked(Config::Get(m_setting) == m_value); + connect(this, &QRadioButton::toggled, this, &GraphicsRadioInt::Update); +} + +void GraphicsRadioInt::Update() +{ + if (!isChecked()) + return; + + Config::SetBaseOrCurrent(m_setting, m_value); +} diff --git a/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.h b/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.h new file mode 100644 index 0000000000..dde18704ee --- /dev/null +++ b/Source/Core/DolphinQt2/Config/Graphics/GraphicsRadio.h @@ -0,0 +1,26 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Config +{ +template +struct ConfigInfo; +} + +class GraphicsRadioInt : public QRadioButton +{ + Q_OBJECT +public: + GraphicsRadioInt(const QString& label, const Config::ConfigInfo& setting, int value); + +private: + void Update(); + + const Config::ConfigInfo& m_setting; + int m_value; +}; diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index 3fe5e2613d..a87fa822cd 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -79,6 +79,7 @@ + @@ -161,6 +162,7 @@ + @@ -212,6 +214,7 @@ + diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index cdebfd27e9..ec6a434b8b 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -44,7 +44,6 @@ // template instantiation template class BoolSetting; -template class BoolSetting; template <> SettingCheckBox::BoolSetting(wxWindow* parent, const wxString& label, const wxString& tooltip, @@ -59,19 +58,6 @@ SettingCheckBox::BoolSetting(wxWindow* parent, const wxString& label, const wxSt Bind(wxEVT_CHECKBOX, &SettingCheckBox::UpdateValue, this); } -template <> -SettingRadioButton::BoolSetting(wxWindow* parent, const wxString& label, const wxString& tooltip, - const Config::ConfigInfo& setting, bool reverse, long style) - : wxRadioButton(parent, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, style), - m_setting(setting), m_reverse(reverse) -{ - SetToolTip(tooltip); - SetValue(Config::Get(m_setting) ^ m_reverse); - if (Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base) - SetFont(GetFont().MakeBold()); - Bind(wxEVT_RADIOBUTTON, &SettingRadioButton::UpdateValue, this); -} - template <> RefBoolSetting::RefBoolSetting(wxWindow* parent, const wxString& label, const wxString& tooltip, bool& setting, bool reverse, @@ -311,18 +297,35 @@ static wxString gpu_texture_decoding_desc = "bottleneck.\n\nIf unsure, leave this unchecked."); static wxString ubershader_desc = wxTRANSLATE("Disabled: Ubershaders are never used. Stuttering will occur during shader " - "compilation, but GPU demands are low. Recommended for low-end hardware.\n\n" + "compilation, but GPU demands are low. Recommended for low-end hardware.\n" "Hybrid: Ubershaders will be used to prevent stuttering during shader " "compilation, but traditional shaders will be used when they will not cause " - "stuttering. Balances performance and smoothness.\n\n" + "stuttering. Balances performance and smoothness.\n" "Exclusive: Ubershaders will always be used. Only recommended for high-end " - "systems."); -static wxString wait_for_shaders_desc = + "systems.\n" + "Skip Drawing: Does not draw objects during shader compilation. Reduces " + "stuttering at the cost of missing objects, or broken effects."); +static wxString shader_compile_sync_desc = + wxTRANSLATE("Ubershaders are never used. Stuttering will occur during shader " + "compilation, but GPU demands are low. Recommended for low-end hardware.\n\nIf " + "unsure, select this mode."); +static wxString shader_compile_uber_only_desc = + wxTRANSLATE("Ubershaders will always be used. Provides a near stutter-free experience at the " + "cost of high GPU requirements. Only recommended for high-end systems."); +static wxString shader_compile_async_uber_desc = + wxTRANSLATE("Ubershaders will be used to prevent stuttering during shader compilation, but " + "specialized shaders will be used when they will not cause stuttering."); +static wxString shader_compile_async_skip_desc = + wxTRANSLATE("Instead of using ubershaders during shader compilation, objects which use these " + "shaders will be not be rendered. This can further reduce stuttering and " + "performance requirements, compared to ubershaders, at the cost of introducing " + "visual glitches and broken effects. Not recommended."); +static wxString shader_compile_before_start_desc = wxTRANSLATE("Waits for all shaders to finish compiling before starting a game. Enabling this " "option may reduce stuttering or hitching for a short time after the game is " - "started, at the cost of a longer delay before the game starts.\n\nFor systems " - "with two or fewer cores, it is recommended to enable this option, as a large " - "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); + "started, at the cost of a longer delay before the game starts. For systems with " + "two or fewer cores, it is recommended to enable this option, as a large shader " + "queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) : wxDialog(parent, wxID_ANY, wxString::Format(_("Dolphin %s Graphics Configuration"), @@ -448,10 +451,29 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxGetTranslation(backend_multithreading_desc), Config::GFX_BACKEND_MULTITHREADING)); } + } - szr_other->Add(CreateCheckBox(page_general, _("Immediately Compile Shaders"), - wxGetTranslation(wait_for_shaders_desc), - Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING)); + // - shader compilation + wxGridBagSizer* const szr_shader_compilation = new wxGridBagSizer(space5, space5); + { + const std::array, 4> modes = { + {{_("Synchronous"), wxGetTranslation(shader_compile_sync_desc)}, + {_("Synchronous (Ubershaders)"), wxGetTranslation(shader_compile_uber_only_desc)}, + {_("Asynchronous (Ubershaders)"), wxGetTranslation(shader_compile_async_uber_desc)}, + {_("Asynchronous (Skip Drawing)"), wxGetTranslation(shader_compile_async_skip_desc)}}}; + for (size_t i = 0; i < modes.size(); i++) + { + szr_shader_compilation->Add( + CreateRadioButton(page_general, modes[i].first, modes[i].second, + Config::GFX_SHADER_COMPILATION_MODE, static_cast(i)), + wxGBPosition(static_cast(i / 2), static_cast(i % 2)), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); + } + szr_shader_compilation->Add( + CreateCheckBox(page_general, _("Compile Shaders Before Starting"), + wxGetTranslation(shader_compile_before_start_desc), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING), + wxGBPosition(2, 0), wxGBSpan(1, 2)); } wxStaticBoxSizer* const group_basic = @@ -469,12 +491,19 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); group_other->AddSpacer(space5); + wxStaticBoxSizer* const group_shader_compilation = + new wxStaticBoxSizer(wxVERTICAL, page_general, _("Shader Compilation")); + group_shader_compilation->Add(szr_shader_compilation, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_shader_compilation->AddSpacer(space5); + szr_general->AddSpacer(space5); szr_general->Add(group_basic, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); szr_general->AddSpacer(space5); szr_general->Add(group_display, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); szr_general->AddSpacer(space5); szr_general->Add(group_other, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_general->AddSpacer(space5); + szr_general->Add(group_shader_compilation, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } szr_general->AddSpacer(space5); @@ -541,18 +570,6 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) row += 1; } - // ubershaders - { - const std::array mode_choices = {{_("Disabled"), _("Hybrid"), _("Exclusive")}}; - szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Ubershaders:")), wxGBPosition(row, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - szr_enh->Add(CreateChoice(page_enh, Config::GFX_UBERSHADER_MODE, - wxGetTranslation(ubershader_desc), mode_choices.size(), - mode_choices.data()), - wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); - row += 1; - } - // postproc shader if (vconfig.backend_info.bSupportsPostProcessing) { @@ -1111,17 +1128,6 @@ SettingChoice* VideoConfigDiag::CreateChoice(wxWindow* parent, return ch; } -SettingRadioButton* VideoConfigDiag::CreateRadioButton(wxWindow* parent, const wxString& label, - const wxString& description, - const Config::ConfigInfo& setting, - bool reverse, long style) -{ - SettingRadioButton* const rb = - new SettingRadioButton(parent, label, wxString(), setting, reverse, style); - RegisterControl(rb, description); - return rb; -} - /* Use this to register descriptions for controls which have NOT been created using the Create* * functions from above */ wxControl* VideoConfigDiag::RegisterControl(wxControl* const control, const wxString& description) diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index a15472885a..af3cccc4cb 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -64,7 +64,6 @@ private: }; typedef BoolSetting SettingCheckBox; -typedef BoolSetting SettingRadioButton; class IntegerSetting : public wxSpinCtrl { @@ -93,6 +92,33 @@ private: Config::ConfigInfo m_setting; }; +template +class SettingRadioButton : public wxRadioButton +{ +public: + SettingRadioButton(wxWindow* parent, const wxString& label, const wxString& tooltip, + const Config::ConfigInfo& setting, const ValueType& value, + long style = 0) + : wxRadioButton(parent, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, style), + m_setting(setting), m_value(value) + { + SetToolTip(tooltip); + SetValue(Config::Get(m_setting) == m_value); + Bind(wxEVT_RADIOBUTTON, &SettingRadioButton::UpdateValue, this); + } + + void UpdateValue(wxCommandEvent& ev) + { + if (ev.IsChecked()) + Config::SetBaseOrCurrent(m_setting, m_value); + ev.Skip(); + } + +private: + Config::ConfigInfo m_setting; + ValueType m_value; +}; + class VideoConfigDiag : public wxDialog { public: @@ -125,10 +151,17 @@ protected: SettingChoice* CreateChoice(wxWindow* parent, const Config::ConfigInfo& setting, const wxString& description, int num = 0, const wxString choices[] = nullptr, long style = 0); - SettingRadioButton* CreateRadioButton(wxWindow* parent, const wxString& label, - const wxString& description, - const Config::ConfigInfo& setting, - bool reverse = false, long style = 0); + template + SettingRadioButton* CreateRadioButton(wxWindow* parent, const wxString& label, + const wxString& description, + const Config::ConfigInfo& setting, + const ValueType& value, long style = 0) + { + auto* const rb = + new SettingRadioButton(parent, label, wxString(), setting, value, style); + RegisterControl(rb, description); + return rb; + } // Same as above but only connects enter/leave window events wxControl* RegisterControl(wxControl* const control, const wxString& description); @@ -157,9 +190,6 @@ protected: SettingCheckBox* borderless_fullscreen; RefBoolSetting* render_to_main_checkbox; - SettingRadioButton* virtual_xfb; - SettingRadioButton* real_xfb; - SettingCheckBox* cache_hires_textures; wxCheckBox* progressive_scan_checkbox; diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp index 1e97cf6308..ea9b27351b 100644 --- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp @@ -166,16 +166,19 @@ void VertexManager::vFlush() } // Bind all pending state to the command buffer - g_renderer->SetPipeline(m_current_pipeline_object); - if (!StateTracker::GetInstance()->Bind()) + if (m_current_pipeline_object) { - WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count); - return; - } + g_renderer->SetPipeline(m_current_pipeline_object); + if (!StateTracker::GetInstance()->Bind()) + { + WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count); + return; + } - // Execute the draw - vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), index_count, 1, - m_current_draw_base_index, m_current_draw_base_vertex, 0); + // Execute the draw + vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), index_count, 1, + m_current_draw_base_index, m_current_draw_base_vertex, 0); + } StateTracker::GetInstance()->OnDraw(); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index b54b8060b9..8364b039c5 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -566,13 +566,25 @@ void VertexManagerBase::UpdatePipelineObject() m_current_pipeline_object = nullptr; m_pipeline_config_changed = false; - if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Disabled) + switch (g_ActiveConfig.iShaderCompilationMode) + { + case ShaderCompilationMode::Synchronous: { // Ubershaders disabled? Block and compile the specialized shader. m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config); - return; } - else if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Hybrid) + break; + + case ShaderCompilationMode::SynchronousUberShaders: + { + // Exclusive ubershader mode, always use ubershaders. + m_current_pipeline_object = + g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); + } + break; + + case ShaderCompilationMode::AsynchronousUberShaders: + case ShaderCompilationMode::AsynchronousSkipRendering: { // Can we background compile shaders? If so, get the pipeline asynchronously. auto res = g_shader_cache->GetPipelineForUidAsync(m_current_pipeline_config); @@ -582,8 +594,20 @@ void VertexManagerBase::UpdatePipelineObject() m_current_pipeline_object = *res; return; } - } - // Exclusive ubershader mode, or hybrid and shaders are still compiling. - m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); + if (g_ActiveConfig.iShaderCompilationMode == ShaderCompilationMode::AsynchronousUberShaders) + { + // Specialized shaders not ready, use the ubershaders. + m_current_pipeline_object = + g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); + } + else + { + // Ensure we try again next draw. Otherwise, if no registers change between frames, the + // object will never be drawn, even when the shader is ready. + m_pipeline_config_changed = true; + } + } + break; + } } diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 55abd69e75..9c90c009a0 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -103,7 +103,8 @@ void VideoConfig::Refresh() iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL); bShaderCache = Config::Get(Config::GFX_SHADER_CACHE); bWaitForShadersBeforeStarting = Config::Get(Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); - iUberShaderMode = static_cast(Config::Get(Config::GFX_UBERSHADER_MODE)); + iShaderCompilationMode = + static_cast(Config::Get(Config::GFX_SHADER_COMPILATION_MODE)); iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS); iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS); @@ -178,6 +179,12 @@ bool VideoConfig::IsVSync() const return bVSync && !Core::GetIsThrottlerTempDisabled(); } +bool VideoConfig::UsingUberShaders() const +{ + return iShaderCompilationMode == ShaderCompilationMode::SynchronousUberShaders || + iShaderCompilationMode == ShaderCompilationMode::AsynchronousUberShaders; +} + static u32 GetNumAutoShaderCompilerThreads() { // Automatic number. We use clamp(cpus - 3, 1, 4). diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index c8bded371b..8e22765695 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -42,11 +42,12 @@ enum class StereoMode : int Nvidia3DVision }; -enum class UberShaderMode : int +enum class ShaderCompilationMode : int { - Disabled, - Hybrid, - Exclusive + Synchronous, + SynchronousUberShaders, + AsynchronousUberShaders, + AsynchronousSkipRendering }; struct ProjectionHackConfig final @@ -170,7 +171,7 @@ struct VideoConfig final // Shader compilation settings. bool bWaitForShadersBeforeStarting; - UberShaderMode iUberShaderMode; + ShaderCompilationMode iShaderCompilationMode; // Number of shader compiler threads. // 0 disables background compilation. @@ -238,7 +239,7 @@ struct VideoConfig final return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding; } bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; } - bool UsingUberShaders() const { return iUberShaderMode != UberShaderMode::Disabled; } + bool UsingUberShaders() const; u32 GetShaderCompilerThreads() const; u32 GetShaderPrecompilerThreads() const; };