summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/PostProcessing.cpp103
-rw-r--r--Source/Core/VideoCommon/PostProcessing.h3
-rw-r--r--Source/Core/VideoCommon/VideoConfig.cpp1
-rw-r--r--Source/Core/VideoCommon/VideoConfig.h12
4 files changed, 83 insertions, 36 deletions
diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp
index c3a245629f..49b05e6762 100644
--- a/Source/Core/VideoCommon/PostProcessing.cpp
+++ b/Source/Core/VideoCommon/PostProcessing.cpp
@@ -419,9 +419,9 @@ std::vector<std::string> PostProcessing::GetPassiveShaderList()
bool PostProcessing::Initialize(AbstractTextureFormat format)
{
m_framebuffer_format = format;
- // CompilePixelShader must be run first if configuration options are used.
+ // CompilePixelShader() must be run first if configuration options are used.
// Otherwise the UBO has a different member list between vertex and pixel
- // shaders, which is a link error.
+ // shaders, which is a link error on some backends.
if (!CompilePixelShader() || !CompileVertexShader() || !CompilePipeline())
return false;
@@ -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,8 +541,8 @@ 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(),
- !default_uniform_staging_buffer);
+ present_rect, uniform_staging_buffer->data(), !default_uniform_staging_buffer,
+ true);
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,8 +592,8 @@ 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(),
- !default_uniform_staging_buffer);
+ present_rect, uniform_staging_buffer->data(), !default_uniform_staging_buffer,
+ false);
g_vertex_manager->UploadUtilityUniforms(uniform_staging_buffer->data(),
static_cast<u32>(uniform_staging_buffer->size()));
@@ -609,7 +621,11 @@ std::string PostProcessing::GetUniformBufferHeader(bool user_post_process) const
// The first (but not necessarily only) source layer we target
ss << " int src_layer;\n";
ss << " uint time;\n";
+ ss << " int graphics_api;\n";
+ // If true, it's an intermediary buffer (including the first), if false, it's the final one
+ ss << " int intermediary_buffer;\n";
+ ss << " int resampling_method;\n";
ss << " int correct_color_space;\n";
ss << " int game_color_space;\n";
ss << " int correct_gamma;\n";
@@ -742,6 +758,7 @@ void SetOutput(float4 color)
#define GetOption(x) (x)
#define OptionEnabled(x) ((x) != 0)
+#define OptionDisabled(x) ((x) == 0)
)";
return ss.str();
@@ -752,13 +769,9 @@ std::string PostProcessing::GetFooter() const
return {};
}
-bool PostProcessing::CompileVertexShader()
+std::string GetVertexShaderBody()
{
std::ostringstream ss;
- // We never need the user selected post process custom uniforms in the vertex shader
- const bool user_post_process = false;
- ss << GetUniformBufferHeader(user_post_process);
-
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{
ss << "VARYING_LOCATION(0) out VertexData {\n";
@@ -779,21 +792,34 @@ bool PostProcessing::CompileVertexShader()
// Vulkan Y needs to be inverted on every pass
if (g_ActiveConfig.backend_info.api_type == APIType::Vulkan)
+ {
ss << " opos.y = -opos.y;\n";
-
- std::string s2 = ss.str();
- s2 += "}\n";
- m_default_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, s2,
- "Default post-processing vertex shader");
-
- // OpenGL Y needs to be inverted once only (in the last pass)
- if (g_ActiveConfig.backend_info.api_type == APIType::OpenGL)
- ss << " opos.y = -opos.y;\n";
+ }
+ // OpenGL Y needs to be inverted in all passes except the last one
+ else if (g_ActiveConfig.backend_info.api_type == APIType::OpenGL)
+ {
+ ss << " if (intermediary_buffer != 0)\n";
+ ss << " opos.y = -opos.y;\n";
+ }
ss << "}\n";
+ return ss.str();
+}
+bool PostProcessing::CompileVertexShader()
+{
+ std::ostringstream ss_default;
+ ss_default << GetUniformBufferHeader(false);
+ ss_default << GetVertexShaderBody();
+ m_default_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss_default.str(),
+ "Default post-processing vertex shader");
+
+ std::ostringstream ss;
+ ss << GetUniformBufferHeader(true);
+ ss << GetVertexShaderBody();
m_vertex_shader =
g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), "Post-processing vertex shader");
+
if (!m_default_vertex_shader || !m_vertex_shader)
{
PanicAlertFmt("Failed to compile post-processing vertex shader");
@@ -816,6 +842,9 @@ struct BuiltinUniforms
std::array<float, 4> src_rect;
s32 src_layer;
u32 time;
+ s32 graphics_api;
+ s32 intermediary_buffer;
+ s32 resampling_method;
s32 correct_color_space;
s32 game_color_space;
s32 correct_gamma;
@@ -839,7 +868,7 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
const AbstractTexture* src_tex, int src_layer,
const MathUtil::Rectangle<int>& dst,
const MathUtil::Rectangle<int>& wnd, u8* buffer,
- bool user_post_process)
+ bool user_post_process, bool intermediary_buffer)
{
const float rcp_src_width = 1.0f / src_tex->GetWidth();
const float rcp_src_height = 1.0f / src_tex->GetHeight();
@@ -860,7 +889,10 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
static_cast<float>(src.GetHeight()) * rcp_src_height};
builtin_uniforms.src_layer = static_cast<s32>(src_layer);
builtin_uniforms.time = static_cast<u32>(m_timer.ElapsedMs());
+ builtin_uniforms.graphics_api = static_cast<s32>(g_ActiveConfig.backend_info.api_type);
+ builtin_uniforms.intermediary_buffer = static_cast<s32>(intermediary_buffer);
+ 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.
@@ -883,6 +915,8 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
std::memcpy(buffer, &builtin_uniforms, sizeof(builtin_uniforms));
buffer += sizeof(builtin_uniforms);
+ // Don't include the custom pp shader options if they are not necessary,
+ // having mismatching uniforms between different shaders can cause issues on some backends
if (!user_post_process)
return;
@@ -1000,8 +1034,7 @@ bool PostProcessing::CompilePipeline()
const bool needs_intermediary_buffer = NeedsIntermediaryBuffer();
AbstractPipelineConfig config = {};
- config.vertex_shader =
- needs_intermediary_buffer ? m_vertex_shader.get() : m_default_vertex_shader.get();
+ config.vertex_shader = m_default_vertex_shader.get();
// This geometry shader will take care of reading both layer 0 and 1 on the source texture,
// and writing to both layer 0 and 1 on the render target.
config.geometry_shader = UseGeometryShaderForPostProcess(needs_intermediary_buffer) ?
@@ -1018,7 +1051,7 @@ bool PostProcessing::CompilePipeline()
if (config.pixel_shader)
m_default_pipeline = g_gfx->CreatePipeline(config);
- config.vertex_shader = m_default_vertex_shader.get();
+ config.vertex_shader = m_vertex_shader.get();
config.geometry_shader = UseGeometryShaderForPostProcess(false) ?
g_shader_cache->GetTexcoordGeometryShader() :
nullptr;
diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h
index b8b648d2d0..b636f3b133 100644
--- a/Source/Core/VideoCommon/PostProcessing.h
+++ b/Source/Core/VideoCommon/PostProcessing.h
@@ -124,7 +124,8 @@ protected:
size_t CalculateUniformsSize(bool user_post_process) const;
void FillUniformBuffer(const MathUtil::Rectangle<int>& src, const AbstractTexture* src_tex,
int src_layer, const MathUtil::Rectangle<int>& dst,
- const MathUtil::Rectangle<int>& wnd, u8* buffer, bool user_post_process);
+ const MathUtil::Rectangle<int>& wnd, u8* buffer, bool user_post_process,
+ bool intermediary_buffer);
// Timer for determining our time value
Common::Timer m_timer;
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index 42bf178047..83dc5eb53e 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -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);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 20f4aa8a28..6e34a43934 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -52,6 +52,17 @@ enum class TextureFilteringMode : int
Linear,
};
+enum class OutputResamplingMode : int
+{
+ Default,
+ Bilinear,
+ BSpline,
+ MitchellNetravali,
+ CatmullRom,
+ SharpBilinear,
+ AreaSampling,
+};
+
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;