Implement "Skip" ubershader mode

Skip ubershader mode works the same as hybrid ubershaders in that the
shaders are compiled asynchronously. However, instead of using the
ubershader to draw the object, it skips it entirely until the
specialized shader is made available.

This mode will likely result in broken effects where a game creates an
EFB copy, and does not redraw it every frame. Therefore, it is not a
recommended option, however, it may result in better performance on
low-end systems.
This commit is contained in:
Stenzek 2018-03-16 23:10:22 +10:00
parent 5c83e18fbd
commit 2f1a7cbee1
22 changed files with 292 additions and 127 deletions

View File

@ -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

View File

@ -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";

View File

@ -92,15 +92,17 @@
</integer-array>
<!-- Ubershader Mode Preference -->
<string-array name="uberShaderModeEntries" translatable="false">
<item>Disabled</item>
<item>Hybrid</item>
<item>Exclusive</item>
<string-array name="shaderCompilationModeEntries" translatable="false">
<item>Synchronous</item>
<item>Synchronous (Ubershaders)</item>
<item>Asynchronous (Ubershaders)</item>
<item>Asynchronous (Skip Drawing)</item>
</string-array>
<integer-array name="uberShaderModeValues" translatable="false">
<integer-array name="shaderCompilationModeValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
<!-- Internal Resolution Preference -->

View File

@ -184,8 +184,9 @@
<string name="fast_depth_calculation_descrip">Uses a less accurate algorithm to calculate depth values.</string>
<string name="aspect_ratio">Aspect Ratio</string>
<string name="aspect_ratio_descrip">Select what aspect ratio to use when rendering</string>
<string name="ubershader_mode">Ubershader Mode</string>
<string name="ubershader_mode_descrip">Specifies when to use Ubershaders. Disabled - Never, Hybrid - Use ubershaders while compiling specialized shaders. Exclusive - Use only ubershaders, largest performance impact.</string>
<string name="shader_compilation_mode">Shader Compilation Mode</string>
<string name="shader_compilation_mode_descrip">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.</string>
<string name="wait_for_shaders">Compile Shaders Before Starting</string>
<!-- Miscellaneous -->
<string name="yes">Yes</string>

View File

@ -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<int>(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());

View File

@ -78,8 +78,9 @@ const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{
const ConfigInfo<bool> GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true};
const ConfigInfo<bool> GFX_WAIT_FOR_SHADERS_BEFORE_STARTING{
{System::GFX, "Settings", "WaitForShadersBeforeStarting"}, false};
const ConfigInfo<int> GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"},
static_cast<int>(UberShaderMode::Disabled)};
const ConfigInfo<int> GFX_SHADER_COMPILATION_MODE{
{System::GFX, "Settings", "ShaderCompilationMode"},
static_cast<int>(ShaderCompilationMode::Synchronous)};
const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS{
{System::GFX, "Settings", "ShaderCompilerThreads"}, 1};
const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS{

View File

@ -60,7 +60,7 @@ extern const ConfigInfo<bool> GFX_BACKEND_MULTITHREADING;
extern const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL;
extern const ConfigInfo<bool> GFX_SHADER_CACHE;
extern const ConfigInfo<bool> GFX_WAIT_FOR_SHADERS_BEFORE_STARTING;
extern const ConfigInfo<int> GFX_UBERSHADER_MODE;
extern const ConfigInfo<int> GFX_SHADER_COMPILATION_MODE;
extern const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS;
extern const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS;

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -11,6 +11,7 @@
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QRadioButton>
#include <QVBoxLayout>
#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<const char*, 4> 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<int>(i));
shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast<int>(i / 2),
static_cast<int>(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)
{

View File

@ -4,11 +4,13 @@
#pragma once
#include <array>
#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<QRadioButton*, 4> m_shader_compilation_mode{};
QCheckBox* m_wait_for_shaders;
X11Utils::XRRConfiguration* m_xrr_config;

View File

@ -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<int>& 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);
}

View File

@ -0,0 +1,26 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QRadioButton>
namespace Config
{
template <typename T>
struct ConfigInfo;
}
class GraphicsRadioInt : public QRadioButton
{
Q_OBJECT
public:
GraphicsRadioInt(const QString& label, const Config::ConfigInfo<int>& setting, int value);
private:
void Update();
const Config::ConfigInfo<int>& m_setting;
int m_value;
};

View File

@ -79,6 +79,7 @@
<QtMoc Include="Config\Graphics\GeneralWidget.h" />
<QtMoc Include="Config\Graphics\GraphicsBool.h" />
<QtMoc Include="Config\Graphics\GraphicsChoice.h" />
<QtMoc Include="Config\Graphics\GraphicsRadio.h" />
<QtMoc Include="Config\Graphics\GraphicsSlider.h" />
<QtMoc Include="Config\Graphics\GraphicsWidget.h" />
<QtMoc Include="Config\Graphics\GraphicsWindow.h" />
@ -161,6 +162,7 @@
<ClCompile Include="$(QtMocOutPrefix)GeneralWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsBool.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsChoice.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsRadio.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsSlider.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GraphicsWindow.cpp" />
@ -212,6 +214,7 @@
<ClCompile Include="Config\Graphics\GeneralWidget.cpp" />
<ClCompile Include="Config\Graphics\GraphicsBool.cpp" />
<ClCompile Include="Config\Graphics\GraphicsChoice.cpp" />
<ClCompile Include="Config\Graphics\GraphicsRadio.cpp" />
<ClCompile Include="Config\Graphics\GraphicsSlider.cpp" />
<ClCompile Include="Config\Graphics\GraphicsWidget.cpp" />
<ClCompile Include="Config\Graphics\GraphicsWindow.cpp" />

View File

@ -44,7 +44,6 @@
// template instantiation
template class BoolSetting<wxCheckBox>;
template class BoolSetting<wxRadioButton>;
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<bool>& 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<wxCheckBox>::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<std::pair<wxString, wxString>, 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<int>(i)),
wxGBPosition(static_cast<int>(i / 2), static_cast<int>(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<wxString, 3> 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<bool>& 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)

View File

@ -64,7 +64,6 @@ private:
};
typedef BoolSetting<wxCheckBox> SettingCheckBox;
typedef BoolSetting<wxRadioButton> SettingRadioButton;
class IntegerSetting : public wxSpinCtrl
{
@ -93,6 +92,33 @@ private:
Config::ConfigInfo<int> m_setting;
};
template <typename ValueType>
class SettingRadioButton : public wxRadioButton
{
public:
SettingRadioButton(wxWindow* parent, const wxString& label, const wxString& tooltip,
const Config::ConfigInfo<ValueType>& 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<ValueType> m_setting;
ValueType m_value;
};
class VideoConfigDiag : public wxDialog
{
public:
@ -125,10 +151,17 @@ protected:
SettingChoice* CreateChoice(wxWindow* parent, const Config::ConfigInfo<int>& 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<bool>& setting,
bool reverse = false, long style = 0);
template <typename ValueType>
SettingRadioButton<ValueType>* CreateRadioButton(wxWindow* parent, const wxString& label,
const wxString& description,
const Config::ConfigInfo<ValueType>& setting,
const ValueType& value, long style = 0)
{
auto* const rb =
new SettingRadioButton<ValueType>(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<wxCheckBox>* render_to_main_checkbox;
SettingRadioButton* virtual_xfb;
SettingRadioButton* real_xfb;
SettingCheckBox* cache_hires_textures;
wxCheckBox* progressive_scan_checkbox;

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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<UberShaderMode>(Config::Get(Config::GFX_UBERSHADER_MODE));
iShaderCompilationMode =
static_cast<ShaderCompilationMode>(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).

View File

@ -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;
};