Expand Up
@@ -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 ;
Expand Down
Expand Up
@@ -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 ();
Expand All
@@ -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" );
Expand All
@@ -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 ()));
Expand All
@@ -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,
Expand All
@@ -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 ();
}
Expand All
@@ -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 ()));
Expand Down
Expand Up
@@ -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 " ;
Expand Down
Expand Up
@@ -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 ();
Expand All
@@ -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 " ;
Expand All
@@ -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" );
Expand All
@@ -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;
Expand All
@@ -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 ();
Expand All
@@ -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.
Expand All
@@ -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 ;
Expand Down
Expand Up
@@ -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) ?
Expand All
@@ -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 ;
Expand Down