From 2edf81cdb054c1db7a32e45c87514f8d4c11f5d9 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Tue, 27 Jun 2023 03:23:08 +0300 Subject: Video: implement output resampling (upscaling/downscaling) methods --- Source/Core/VideoCommon/PostProcessing.cpp | 41 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'Source/Core/VideoCommon/PostProcessing.cpp') diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index c3a245629f..1de4c311c2 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -486,23 +486,29 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst, MathUtil::Rectangle 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* uniform_staging_buffer = &m_default_uniform_staging_buffer; bool default_uniform_staging_buffer = true; + const MathUtil::Rectangle 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& 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(src_rect.GetWidth()); + const u32 target_height = + needs_resampling ? present_rect.GetHeight() : static_cast(src_rect.GetHeight()); + if (!m_intermediary_frame_buffer || !m_intermediary_color_texture || - m_intermediary_color_texture.get()->GetWidth() != static_cast(src_rect.GetWidth()) || - m_intermediary_color_texture.get()->GetHeight() != static_cast(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& 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(uniform_staging_buffer->size())); @@ -544,6 +555,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& 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& 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& 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(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 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& src, builtin_uniforms.src_layer = static_cast(src_layer); builtin_uniforms.time = static_cast(m_timer.ElapsedMs()); + builtin_uniforms.resampling_method = static_cast(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. -- cgit v1.2.3 From 80b453082dff9545e7b040fbe546055dac310b5c Mon Sep 17 00:00:00 2001 From: Filoppi Date: Sun, 6 Aug 2023 03:11:19 +0300 Subject: Video: fix post process shaders with parameters failing to build on OpenGL This was because the shader uniforms between the pixel and vertex shaders were willingly left different, to avoid filling the vertex shader with unnecessary params. Turns out all backends are fine with this except OGL. The new behaviour is now much more consistent and well explained, the "default" shaders are the ones that always run, and the non default ones are the user selected ones (if any). --- Source/Core/VideoCommon/PostProcessing.cpp | 66 +++++++++++++++++++----------- 1 file changed, 42 insertions(+), 24 deletions(-) (limited to 'Source/Core/VideoCommon/PostProcessing.cpp') diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 1de4c311c2..49b05e6762 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -419,9 +419,9 @@ std::vector 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; @@ -541,8 +541,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst, g_gfx->SetFramebuffer(m_intermediary_frame_buffer.get()); FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(), - present_rect, 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(uniform_staging_buffer->size())); @@ -592,8 +592,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst, if (final_pipeline) { FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(), - present_rect, 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(uniform_staging_buffer->size())); @@ -621,6 +621,9 @@ 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"; @@ -755,6 +758,7 @@ void SetOutput(float4 color) #define GetOption(x) (x) #define OptionEnabled(x) ((x) != 0) +#define OptionDisabled(x) ((x) == 0) )"; return ss.str(); @@ -765,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"; @@ -792,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"); @@ -829,6 +842,8 @@ struct BuiltinUniforms std::array src_rect; s32 src_layer; u32 time; + s32 graphics_api; + s32 intermediary_buffer; s32 resampling_method; s32 correct_color_space; s32 game_color_space; @@ -853,7 +868,7 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle& src, const AbstractTexture* src_tex, int src_layer, const MathUtil::Rectangle& dst, const MathUtil::Rectangle& 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(); @@ -874,6 +889,8 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle& src, static_cast(src.GetHeight()) * rcp_src_height}; builtin_uniforms.src_layer = static_cast(src_layer); builtin_uniforms.time = static_cast(m_timer.ElapsedMs()); + builtin_uniforms.graphics_api = static_cast(g_ActiveConfig.backend_info.api_type); + builtin_uniforms.intermediary_buffer = static_cast(intermediary_buffer); builtin_uniforms.resampling_method = static_cast(g_ActiveConfig.output_resampling_mode); // Color correction related uniforms. @@ -898,6 +915,8 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle& 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; @@ -1015,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) ? @@ -1033,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; -- cgit v1.2.3