mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 14:19:46 -06:00
Video: implement output resampling (upscaling/downscaling) methods
This commit is contained in:
@ -118,6 +118,8 @@ const Info<std::string> GFX_DRIVER_LIB_NAME{{System::GFX, "Settings", "DriverLib
|
||||
const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING{
|
||||
{System::GFX, "Enhancements", "ForceTextureFiltering"}, TextureFilteringMode::Default};
|
||||
const Info<int> GFX_ENHANCE_MAX_ANISOTROPY{{System::GFX, "Enhancements", "MaxAnisotropy"}, 0};
|
||||
const Info<OutputResamplingMode> GFX_ENHANCE_OUTPUT_RESAMPLING{
|
||||
{System::GFX, "Enhancements", "OutputResampling"}, OutputResamplingMode::Default};
|
||||
const Info<std::string> GFX_ENHANCE_POST_SHADER{
|
||||
{System::GFX, "Enhancements", "PostProcessingShader"}, ""};
|
||||
const Info<bool> GFX_ENHANCE_FORCE_TRUE_COLOR{{System::GFX, "Enhancements", "ForceTrueColor"},
|
||||
|
@ -11,6 +11,7 @@ enum class AspectMode : int;
|
||||
enum class ShaderCompilationMode : int;
|
||||
enum class StereoMode : int;
|
||||
enum class TextureFilteringMode : int;
|
||||
enum class OutputResamplingMode : int;
|
||||
enum class ColorCorrectionRegion : int;
|
||||
enum class TriState : int;
|
||||
|
||||
@ -101,6 +102,7 @@ extern const Info<bool> GFX_MODS_ENABLE;
|
||||
|
||||
extern const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING;
|
||||
extern const Info<int> GFX_ENHANCE_MAX_ANISOTROPY; // NOTE - this is x in (1 << x)
|
||||
extern const Info<OutputResamplingMode> GFX_ENHANCE_OUTPUT_RESAMPLING;
|
||||
extern const Info<std::string> GFX_ENHANCE_POST_SHADER;
|
||||
extern const Info<bool> GFX_ENHANCE_FORCE_TRUE_COLOR;
|
||||
extern const Info<bool> GFX_ENHANCE_DISABLE_COPY_FILTER;
|
||||
|
@ -105,6 +105,22 @@ void EnhancementsWidget::CreateWidgets()
|
||||
m_texture_filtering_combo->addItem(tr("Force Linear and 16x Anisotropic"),
|
||||
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X);
|
||||
|
||||
m_output_resampling_combo = new ToolTipComboBox();
|
||||
m_output_resampling_combo->addItem(tr("Default"),
|
||||
static_cast<int>(OutputResamplingMode::Default));
|
||||
m_output_resampling_combo->addItem(tr("Bilinear"),
|
||||
static_cast<int>(OutputResamplingMode::Bilinear));
|
||||
m_output_resampling_combo->addItem(tr("Bicubic"),
|
||||
static_cast<int>(OutputResamplingMode::Bicubic));
|
||||
m_output_resampling_combo->addItem(tr("Hermite"),
|
||||
static_cast<int>(OutputResamplingMode::Hermite));
|
||||
m_output_resampling_combo->addItem(tr("Catmull-Rom"),
|
||||
static_cast<int>(OutputResamplingMode::CatmullRom));
|
||||
m_output_resampling_combo->addItem(tr("Nearest Neighbor"),
|
||||
static_cast<int>(OutputResamplingMode::NearestNeighbor));
|
||||
m_output_resampling_combo->addItem(tr("Sharp Bilinear"),
|
||||
static_cast<int>(OutputResamplingMode::SharpBilinear));
|
||||
|
||||
m_configure_color_correction = new ToolTipPushButton(tr("Configure"));
|
||||
|
||||
m_pp_effect = new ToolTipComboBox();
|
||||
@ -136,6 +152,10 @@ void EnhancementsWidget::CreateWidgets()
|
||||
enhancements_layout->addWidget(m_texture_filtering_combo, row, 1, 1, -1);
|
||||
++row;
|
||||
|
||||
enhancements_layout->addWidget(new QLabel(tr("Output Resampling:")), row, 0);
|
||||
enhancements_layout->addWidget(m_output_resampling_combo, row, 1, 1, -1);
|
||||
++row;
|
||||
|
||||
enhancements_layout->addWidget(new QLabel(tr("Color Correction:")), row, 0);
|
||||
enhancements_layout->addWidget(m_configure_color_correction, row, 1, 1, -1);
|
||||
++row;
|
||||
@ -195,6 +215,8 @@ void EnhancementsWidget::ConnectWidgets()
|
||||
[this](int) { SaveSettings(); });
|
||||
connect(m_texture_filtering_combo, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int) { SaveSettings(); });
|
||||
connect(m_output_resampling_combo, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int) { SaveSettings(); });
|
||||
connect(m_pp_effect, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int) { SaveSettings(); });
|
||||
connect(m_3d_mode, qOverload<int>(&QComboBox::currentIndexChanged), [this] {
|
||||
@ -325,6 +347,14 @@ void EnhancementsWidget::LoadSettings()
|
||||
break;
|
||||
}
|
||||
|
||||
// Resampling
|
||||
const OutputResamplingMode output_resampling_mode =
|
||||
Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
|
||||
m_output_resampling_combo->setCurrentIndex(static_cast<int>(output_resampling_mode));
|
||||
|
||||
m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
|
||||
|
||||
// Color Correction
|
||||
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
|
||||
|
||||
// Post Processing Shader
|
||||
@ -413,6 +443,10 @@ void EnhancementsWidget::SaveSettings()
|
||||
break;
|
||||
}
|
||||
|
||||
const int output_resampling_selection = m_output_resampling_combo->currentData().toInt();
|
||||
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_OUTPUT_RESAMPLING,
|
||||
static_cast<OutputResamplingMode>(output_resampling_selection));
|
||||
|
||||
const bool anaglyph = g_Config.stereo_mode == StereoMode::Anaglyph;
|
||||
const bool passive = g_Config.stereo_mode == StereoMode::Passive;
|
||||
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER,
|
||||
@ -455,6 +489,16 @@ void EnhancementsWidget::AddDescriptions()
|
||||
"scaling filter selected by the game.<br><br>Any option except 'Default' will alter the look "
|
||||
"of the game's textures and might cause issues in a small number of "
|
||||
"games.<br><br><dolphin_emphasis>If unsure, select 'Default'.</dolphin_emphasis>");
|
||||
static const char TR_OUTPUT_RESAMPLING_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Affects how the game output image is upscaled or downscaled to the window resolution.<br>"
|
||||
"\"Default\" will rely on the GPU internal bilinear sampler which isn't gamma corrected."
|
||||
"<br>\"Bilinear\" (gamma corrected) is a good compromise between quality and performance."
|
||||
"<br>\"Bicubic\" is smoother than \"Bilinear\"."
|
||||
"<br>\"Hermite\" might offer the best quality when upscaling,"
|
||||
" at a slightly bigger perform cost.<br>\"Catmull-Rom\" is best for downscaling."
|
||||
"<br>\"Nearest Neighbor\" doesn't do any resampling, select if you like a pixelated look."
|
||||
"<br>\"Sharp Bilinear\" works best with 2D games at low resolutions, use if you like a"
|
||||
" sharp look.<br><br><dolphin_emphasis>If unsure, select 'Default'.</dolphin_emphasis>");
|
||||
static const char TR_COLOR_CORRECTION_DESCRIPTION[] =
|
||||
QT_TR_NOOP("A group of features to make the colors more accurate, matching the color space "
|
||||
"Wii and GC games were meant for.");
|
||||
@ -537,6 +581,9 @@ void EnhancementsWidget::AddDescriptions()
|
||||
m_texture_filtering_combo->SetTitle(tr("Texture Filtering"));
|
||||
m_texture_filtering_combo->SetDescription(tr(TR_FORCE_TEXTURE_FILTERING_DESCRIPTION));
|
||||
|
||||
m_output_resampling_combo->SetTitle(tr("Output Resampling"));
|
||||
m_output_resampling_combo->SetDescription(tr(TR_OUTPUT_RESAMPLING_DESCRIPTION));
|
||||
|
||||
m_configure_color_correction->SetTitle(tr("Color Correction"));
|
||||
m_configure_color_correction->SetDescription(tr(TR_COLOR_CORRECTION_DESCRIPTION));
|
||||
|
||||
|
@ -39,6 +39,7 @@ private:
|
||||
ConfigChoice* m_ir_combo;
|
||||
ToolTipComboBox* m_aa_combo;
|
||||
ToolTipComboBox* m_texture_filtering_combo;
|
||||
ToolTipComboBox* m_output_resampling_combo;
|
||||
ToolTipComboBox* m_pp_effect;
|
||||
ToolTipPushButton* m_configure_color_correction;
|
||||
QPushButton* m_configure_pp_effect;
|
||||
|
@ -486,23 +486,29 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
|
||||
MathUtil::Rectangle<int> src_rect = src;
|
||||
g_gfx->SetSamplerState(0, RenderState::GetLinearSamplerState());
|
||||
g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState());
|
||||
g_gfx->SetTexture(0, src_tex);
|
||||
g_gfx->SetTexture(1, src_tex);
|
||||
|
||||
const bool is_color_correction_active = IsColorCorrectionActive();
|
||||
const bool needs_color_correction = IsColorCorrectionActive();
|
||||
// Rely on the default (bi)linear sampler with the default mode
|
||||
// (it might not be gamma corrected).
|
||||
const bool needs_resampling =
|
||||
g_ActiveConfig.output_resampling_mode > OutputResamplingMode::Default;
|
||||
const bool needs_intermediary_buffer = NeedsIntermediaryBuffer();
|
||||
const bool needs_default_pipeline = needs_color_correction || needs_resampling;
|
||||
const AbstractPipeline* final_pipeline = m_pipeline.get();
|
||||
std::vector<u8>* uniform_staging_buffer = &m_default_uniform_staging_buffer;
|
||||
bool default_uniform_staging_buffer = true;
|
||||
const MathUtil::Rectangle<int> present_rect = g_presenter->GetTargetRectangle();
|
||||
|
||||
// Intermediary pass.
|
||||
// We draw to a high quality intermediary texture for two reasons:
|
||||
// We draw to a high quality intermediary texture for a couple reasons:
|
||||
// -Consistently do high quality gamma corrected resampling (upscaling/downscaling)
|
||||
// -Keep quality for gamma and gamut conversions, and HDR output
|
||||
// (low bit depths lose too much quality with gamma conversions)
|
||||
// -We make a texture of the exact same res as the source one,
|
||||
// because all the post process shaders we already had assume that
|
||||
// the source texture size (EFB) is different from the swap chain
|
||||
// texture size (which matches the window size).
|
||||
if (m_default_pipeline && is_color_correction_active && needs_intermediary_buffer)
|
||||
// -Keep the post process phase in linear space, to better operate with colors
|
||||
if (m_default_pipeline && needs_default_pipeline && needs_intermediary_buffer)
|
||||
{
|
||||
AbstractFramebuffer* const previous_framebuffer = g_gfx->GetCurrentFramebuffer();
|
||||
|
||||
@ -512,13 +518,18 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
// so it would be a waste to allocate two layers (see "bUsesExplictQuadBuffering").
|
||||
const u32 target_layers = copy_all_layers ? src_tex->GetLayers() : 1;
|
||||
|
||||
const u32 target_width =
|
||||
needs_resampling ? present_rect.GetWidth() : static_cast<u32>(src_rect.GetWidth());
|
||||
const u32 target_height =
|
||||
needs_resampling ? present_rect.GetHeight() : static_cast<u32>(src_rect.GetHeight());
|
||||
|
||||
if (!m_intermediary_frame_buffer || !m_intermediary_color_texture ||
|
||||
m_intermediary_color_texture.get()->GetWidth() != static_cast<u32>(src_rect.GetWidth()) ||
|
||||
m_intermediary_color_texture.get()->GetHeight() != static_cast<u32>(src_rect.GetHeight()) ||
|
||||
m_intermediary_color_texture.get()->GetWidth() != target_width ||
|
||||
m_intermediary_color_texture.get()->GetHeight() != target_height ||
|
||||
m_intermediary_color_texture.get()->GetLayers() != target_layers)
|
||||
{
|
||||
const TextureConfig intermediary_color_texture_config(
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1, target_layers, src_tex->GetSamples(),
|
||||
target_width, target_height, 1, target_layers, src_tex->GetSamples(),
|
||||
s_intermediary_buffer_format, AbstractTextureFlag_RenderTarget);
|
||||
m_intermediary_color_texture = g_gfx->CreateTexture(intermediary_color_texture_config,
|
||||
"Intermediary post process texture");
|
||||
@ -530,7 +541,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
g_gfx->SetFramebuffer(m_intermediary_frame_buffer.get());
|
||||
|
||||
FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(),
|
||||
g_presenter->GetTargetRectangle(), uniform_staging_buffer->data(),
|
||||
present_rect, uniform_staging_buffer->data(),
|
||||
!default_uniform_staging_buffer);
|
||||
g_vertex_manager->UploadUtilityUniforms(uniform_staging_buffer->data(),
|
||||
static_cast<u32>(uniform_staging_buffer->size()));
|
||||
@ -544,6 +555,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
src_rect = m_intermediary_color_texture->GetRect();
|
||||
src_tex = m_intermediary_color_texture.get();
|
||||
g_gfx->SetTexture(0, src_tex);
|
||||
g_gfx->SetTexture(1, src_tex);
|
||||
// The "m_intermediary_color_texture" has already copied
|
||||
// from the specified source layer onto its first one.
|
||||
// If we query for a layer that the source texture doesn't have,
|
||||
@ -557,7 +569,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
// If we have no custom user shader selected, and color correction
|
||||
// is active, directly run the fixed pipeline shader instead of
|
||||
// doing two passes, with the second one doing nothing useful.
|
||||
if (m_default_pipeline && is_color_correction_active)
|
||||
if (m_default_pipeline && needs_default_pipeline)
|
||||
{
|
||||
final_pipeline = m_default_pipeline.get();
|
||||
}
|
||||
@ -580,7 +592,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
|
||||
if (final_pipeline)
|
||||
{
|
||||
FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(),
|
||||
g_presenter->GetTargetRectangle(), uniform_staging_buffer->data(),
|
||||
present_rect, uniform_staging_buffer->data(),
|
||||
!default_uniform_staging_buffer);
|
||||
g_vertex_manager->UploadUtilityUniforms(uniform_staging_buffer->data(),
|
||||
static_cast<u32>(uniform_staging_buffer->size()));
|
||||
@ -610,6 +622,7 @@ std::string PostProcessing::GetUniformBufferHeader(bool user_post_process) const
|
||||
ss << " int src_layer;\n";
|
||||
ss << " uint time;\n";
|
||||
|
||||
ss << " int resampling_method;\n";
|
||||
ss << " int correct_color_space;\n";
|
||||
ss << " int game_color_space;\n";
|
||||
ss << " int correct_gamma;\n";
|
||||
@ -816,6 +829,7 @@ struct BuiltinUniforms
|
||||
std::array<float, 4> src_rect;
|
||||
s32 src_layer;
|
||||
u32 time;
|
||||
s32 resampling_method;
|
||||
s32 correct_color_space;
|
||||
s32 game_color_space;
|
||||
s32 correct_gamma;
|
||||
@ -861,6 +875,7 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
|
||||
builtin_uniforms.src_layer = static_cast<s32>(src_layer);
|
||||
builtin_uniforms.time = static_cast<u32>(m_timer.ElapsedMs());
|
||||
|
||||
builtin_uniforms.resampling_method = static_cast<s32>(g_ActiveConfig.output_resampling_mode);
|
||||
// Color correction related uniforms.
|
||||
// These are mainly used by the "m_default_pixel_shader",
|
||||
// but should also be accessible to all other shaders.
|
||||
|
@ -133,6 +133,7 @@ void VideoConfig::Refresh()
|
||||
|
||||
texture_filtering_mode = Config::Get(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING);
|
||||
iMaxAnisotropy = Config::Get(Config::GFX_ENHANCE_MAX_ANISOTROPY);
|
||||
output_resampling_mode = Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
|
||||
sPostProcessingShader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
|
||||
bForceTrueColor = Config::Get(Config::GFX_ENHANCE_FORCE_TRUE_COLOR);
|
||||
bDisableCopyFilter = Config::Get(Config::GFX_ENHANCE_DISABLE_COPY_FILTER);
|
||||
|
@ -52,6 +52,17 @@ enum class TextureFilteringMode : int
|
||||
Linear,
|
||||
};
|
||||
|
||||
enum class OutputResamplingMode : int
|
||||
{
|
||||
Default,
|
||||
Bilinear,
|
||||
Bicubic,
|
||||
Hermite,
|
||||
CatmullRom,
|
||||
NearestNeighbor,
|
||||
SharpBilinear,
|
||||
};
|
||||
|
||||
enum class ColorCorrectionRegion : int
|
||||
{
|
||||
SMPTE_NTSCM,
|
||||
@ -103,6 +114,7 @@ struct VideoConfig final
|
||||
bool bSSAA = false;
|
||||
int iEFBScale = 0;
|
||||
TextureFilteringMode texture_filtering_mode = TextureFilteringMode::Default;
|
||||
OutputResamplingMode output_resampling_mode = OutputResamplingMode::Default;
|
||||
int iMaxAnisotropy = 0;
|
||||
std::string sPostProcessingShader;
|
||||
bool bForceTrueColor = false;
|
||||
|
Reference in New Issue
Block a user