GPU: Fill in unused/padded area in overlays

This commit is contained in:
Stenzek 2025-01-18 18:54:18 +10:00
parent 88b43370dc
commit 6131ddbefe
No known key found for this signature in database
5 changed files with 89 additions and 32 deletions

View File

@ -91,11 +91,6 @@ bool GPUPresenter::UpdateSettings(const GPUSettings& old_settings, Error* error)
return true; return true;
} }
bool GPUPresenter::IsDisplayPostProcessingActive() const
{
return (m_display_postfx && m_display_postfx->IsActive());
}
bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error) bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error)
{ {
const GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend, const GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend,
@ -170,7 +165,9 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
GL_OBJECT_NAME(m_present_copy_pipeline, "Display Rotate/Copy Pipeline"); GL_OBJECT_NAME(m_present_copy_pipeline, "Display Rotate/Copy Pipeline");
// blended variants // blended variants
if (m_border_overlay_texture && m_border_overlay_alpha_blend) if (m_border_overlay_texture)
{
if (m_border_overlay_alpha_blend)
{ {
// destination blend the main present, not source // destination blend the main present, not source
plconfig.blend.enable = true; plconfig.blend.enable = true;
@ -180,6 +177,7 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One; plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
plconfig.blend.alpha_blend_op = GPUPipeline::BlendOp::Add; plconfig.blend.alpha_blend_op = GPUPipeline::BlendOp::Add;
plconfig.blend.dst_alpha_blend = GPUPipeline::BlendFunc::Zero; plconfig.blend.dst_alpha_blend = GPUPipeline::BlendFunc::Zero;
}
plconfig.fragment_shader = fso.get(); plconfig.fragment_shader = fso.get();
if (!(m_display_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error))) if (!(m_display_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
@ -191,6 +189,18 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
if (!(m_present_copy_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error))) if (!(m_present_copy_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
return false; return false;
GL_OBJECT_NAME(m_present_copy_blend_pipeline, "Display Rotate/Copy Pipeline [Blended]"); GL_OBJECT_NAME(m_present_copy_blend_pipeline, "Display Rotate/Copy Pipeline [Blended]");
std::unique_ptr<GPUShader> clear_fso =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateFillFragmentShader(GSVector4i::zero()), error);
if (!clear_fso)
return false;
GL_OBJECT_NAME(clear_fso, "Display Clear Fragment Shader");
plconfig.fragment_shader = clear_fso.get();
if (!(m_present_clear_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
return false;
GL_OBJECT_NAME(m_present_clear_pipeline, "Display Clear Pipeline");
} }
} }
@ -383,10 +393,12 @@ GPUDevice::PresentResult GPUPresenter::PresentDisplay()
display_rect = display_rect.add32(overlay_display_rect.xyxy()); display_rect = display_rect.add32(overlay_display_rect.xyxy());
draw_rect = draw_rect.add32(overlay_display_rect.xyxy()); draw_rect = draw_rect.add32(overlay_display_rect.xyxy());
return RenderDisplay(nullptr, overlay_rect, display_rect, draw_rect, !g_gpu_settings.gpu_show_vram); return RenderDisplay(nullptr, overlay_rect, overlay_display_rect, display_rect, draw_rect,
!g_gpu_settings.gpu_show_vram);
} }
GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect, GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
const GSVector4i overlay_display_rect,
const GSVector4i display_rect, const GSVector4i draw_rect, const GSVector4i display_rect, const GSVector4i draw_rect,
bool postfx) bool postfx)
{ {
@ -409,7 +421,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
GL_INS_FMT("Final target size: {}x{}", target_size.x, target_size.y); GL_INS_FMT("Final target size: {}x{}", target_size.x, target_size.y);
// Postfx active? // Postfx active?
const GSVector2i postfx_size = have_overlay ? display_rect.rsize() : target_size; const GSVector2i postfx_size = have_overlay ? overlay_display_rect.rsize() : target_size;
const bool really_postfx = (postfx && m_display_postfx && m_display_postfx->IsActive() && m_display_postfx && const bool really_postfx = (postfx && m_display_postfx && m_display_postfx->IsActive() && m_display_postfx &&
m_display_postfx->CheckTargets(m_present_format, postfx_size.x, postfx_size.y)); m_display_postfx->CheckTargets(m_present_format, postfx_size.x, postfx_size.y));
GL_INS(really_postfx ? "Post-processing is ENABLED" : "Post-processing is disabled"); GL_INS(really_postfx ? "Post-processing is ENABLED" : "Post-processing is disabled");
@ -436,7 +448,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (really_postfx) if (really_postfx)
{ {
// Remove draw offset if we're using an overlay. // Remove draw offset if we're using an overlay.
const GSVector4i real_draw_rect = have_overlay ? draw_rect.sub32(display_rect.xyxy()) : draw_rect; const GSVector4i real_draw_rect = have_overlay ? draw_rect.sub32(overlay_display_rect.xyxy()) : draw_rect;
// Display is always drawn to the postfx input. // Display is always drawn to the postfx input.
GPUTexture* const postfx_input = m_display_postfx->GetInputTexture(); GPUTexture* const postfx_input = m_display_postfx->GetInputTexture();
@ -453,8 +465,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (have_prerotation || have_overlay) if (have_prerotation || have_overlay)
{ {
GPUTexture* const postfx_output = m_display_postfx->GetTextureUnusedAtEndOfChain(); GPUTexture* const postfx_output = m_display_postfx->GetTextureUnusedAtEndOfChain();
const GSVector4i real_display_rect = have_overlay ? display_rect.sub32(display_rect.xyxy()) : display_rect; ApplyDisplayPostProcess(postfx_output, postfx_input, real_draw_rect);
ApplyDisplayPostProcess(postfx_output, postfx_input, real_display_rect);
postfx_output->MakeReadyForSampling(); postfx_output->MakeReadyForSampling();
// Start draw to final buffer. // Start draw to final buffer.
@ -464,13 +475,26 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
// If we have an overlay, draw it, and then copy the postprocessed framebuffer in. // If we have an overlay, draw it, and then copy the postprocessed framebuffer in.
if (have_overlay) if (have_overlay)
{ {
DrawTextureCopy(target_size, overlay_rect, m_border_overlay_texture.get(), false, true, prerotation); GL_SCOPE_FMT("Draw overlay and postfx buffer");
DrawTextureCopy(target_size, draw_rect, postfx_output, m_border_overlay_alpha_blend, false, prerotation); g_gpu_device->SetPipeline(m_present_copy_pipeline.get());
g_gpu_device->SetTextureSampler(0, m_border_overlay_texture.get(), g_gpu_device->GetLinearSampler());
DrawScreenQuad(overlay_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size, DisplayRotation::Normal,
prerotation);
g_gpu_device->SetPipeline(m_border_overlay_alpha_blend ? m_present_copy_blend_pipeline.get() :
m_present_copy_pipeline.get());
g_gpu_device->SetTextureSampler(0, postfx_output, g_gpu_device->GetNearestSampler());
DrawScreenQuad(overlay_display_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size,
DisplayRotation::Normal, prerotation);
} }
else else
{ {
// Ohterwise, just copy the framebuffer. // Otherwise, just copy the framebuffer.
DrawTextureCopy(target_size, draw_rect, postfx_output, false, false, prerotation); GL_SCOPE_FMT("Copy framebuffer for prerotation");
g_gpu_device->SetPipeline(m_present_copy_pipeline.get());
g_gpu_device->SetTextureSampler(0, postfx_output, g_gpu_device->GetNearestSampler());
DrawScreenQuad(draw_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size, DisplayRotation::Normal,
prerotation);
} }
// All done // All done
@ -490,7 +514,22 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
return pres; return pres;
if (have_overlay) if (have_overlay)
DrawTextureCopy(target_size, overlay_rect, m_border_overlay_texture.get(), false, true, prerotation); {
GL_SCOPE_FMT("Draw overlay to {}", overlay_rect);
g_gpu_device->SetPipeline(m_present_copy_pipeline.get());
g_gpu_device->SetTextureSampler(0, m_border_overlay_texture.get(), g_gpu_device->GetLinearSampler());
DrawScreenQuad(overlay_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size, DisplayRotation::Normal,
prerotation);
if (!overlay_display_rect.eq(draw_rect))
{
// Need to fill in the borders.
GL_SCOPE_FMT("Fill in overlay borders - odisplay={}, draw={}", overlay_display_rect, draw_rect);
g_gpu_device->SetPipeline(m_present_clear_pipeline.get());
DrawScreenQuad(overlay_display_rect, GSVector4::zero(), target_size, g_settings.display_rotation, prerotation);
}
}
if (m_display_texture) if (m_display_texture)
{ {
@ -676,7 +715,8 @@ void GPUPresenter::SendDisplayToMediaCapture(MediaCapture* cap)
// Not cleared by RenderDisplay(). // Not cleared by RenderDisplay().
g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR); g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR);
if (RenderDisplay(target, GSVector4i::zero(), display_rect, draw_rect, postfx) != GPUDevice::PresentResult::OK || if (RenderDisplay(target, GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx) !=
GPUDevice::PresentResult::OK ||
!cap->DeliverVideoFrame(target)) [[unlikely]] !cap->DeliverVideoFrame(target)) [[unlikely]]
{ {
WARNING_LOG("Failed to render/deliver video capture frame."); WARNING_LOG("Failed to render/deliver video capture frame.");
@ -1003,7 +1043,7 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR); g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR);
// TODO: this should use copy shader instead. // TODO: this should use copy shader instead.
RenderDisplay(render_texture.get(), GSVector4i::zero(), display_rect, draw_rect, postfx); RenderDisplay(render_texture.get(), GSVector4i::zero(), GSVector4i::zero(), display_rect, draw_rect, postfx);
Image image(width, height, image_format); Image image(width, height, image_format);
@ -1102,7 +1142,7 @@ bool GPUPresenter::UpdatePostProcessingSettings(bool force_reload, Error* error)
{ {
// something changed, need to recompile pipelines // something changed, need to recompile pipelines
if (LoadOverlayTexture() && m_border_overlay_alpha_blend && if (LoadOverlayTexture() && m_border_overlay_alpha_blend &&
(!m_present_copy_blend_pipeline || !m_display_blend_pipeline) && (!m_present_copy_blend_pipeline || !m_display_blend_pipeline || !m_present_clear_pipeline) &&
!CompileDisplayPipelines(true, false, false, error)) !CompileDisplayPipelines(true, false, false, error))
{ {
return false; return false;

View File

@ -48,8 +48,6 @@ public:
bool Initialize(Error* error); bool Initialize(Error* error);
bool UpdateSettings(const GPUSettings& old_settings, Error* error); bool UpdateSettings(const GPUSettings& old_settings, Error* error);
bool IsDisplayPostProcessingActive() const;
bool UpdatePostProcessingSettings(bool force_reload, Error* error); bool UpdatePostProcessingSettings(bool force_reload, Error* error);
void ClearDisplay(); void ClearDisplay();
@ -106,7 +104,8 @@ private:
bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error); bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error);
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect, GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx); const GSVector4i overlay_display_rect, const GSVector4i display_rect,
const GSVector4i draw_rect, bool postfx);
void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend, void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
DisplayRotation rotation, WindowInfo::PreRotation prerotation); DisplayRotation rotation, WindowInfo::PreRotation prerotation);
@ -158,12 +157,13 @@ private:
std::unique_ptr<GPUPipeline> m_present_copy_pipeline; std::unique_ptr<GPUPipeline> m_present_copy_pipeline;
std::unique_ptr<PostProcessing::Chain> m_display_postfx; std::unique_ptr<PostProcessing::Chain> m_display_postfx;
std::unique_ptr<GPUTexture> m_border_overlay_texture;
// blended variants of pipelines, used when overlays are enabled // blended variants of pipelines, used when overlays are enabled
std::unique_ptr<GPUPipeline> m_display_blend_pipeline; std::unique_ptr<GPUPipeline> m_display_blend_pipeline;
std::unique_ptr<GPUPipeline> m_present_copy_blend_pipeline; std::unique_ptr<GPUPipeline> m_present_copy_blend_pipeline;
std::unique_ptr<GPUPipeline> m_present_clear_pipeline;
std::unique_ptr<GPUTexture> m_border_overlay_texture;
GSVector4i m_border_overlay_display_rect = GSVector4i::zero(); GSVector4i m_border_overlay_display_rect = GSVector4i::zero();
// Low-traffic variables down here. // Low-traffic variables down here.

View File

@ -12,6 +12,7 @@
#include "common/error.h" #include "common/error.h"
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtWidgets/QCheckBox> #include <QtWidgets/QCheckBox>
#include <QtWidgets/QDialogButtonBox> #include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout> #include <QtWidgets/QGridLayout>
@ -539,7 +540,8 @@ void PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged(int ind
void PostProcessingOverlayConfigWidget::onImagePathBrowseClicked() void PostProcessingOverlayConfigWidget::onImagePathBrowseClicked()
{ {
const QString path = QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select Image"), QString(), const QString path = QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select Image"),
QFileInfo(m_ui.imagePath->text()).dir().path(),
tr("All Cover Image Types (*.jpg *.jpeg *.png *.webp)")); tr("All Cover Image Types (*.jpg *.jpeg *.png *.webp)"));
if (path.isEmpty()) if (path.isEmpty())
return; return;

View File

@ -845,6 +845,20 @@ std::string ShaderGen::GenerateFillFragmentShader() const
return ss.str(); return ss.str();
} }
std::string ShaderGen::GenerateFillFragmentShader(const GSVector4i fixed_color) const
{
std::stringstream ss;
WriteHeader(ss);
DeclareFragmentEntryPoint(ss, 0, 0);
ss << "{\n";
ss << " o_col0 = float4(" << std::fixed << fixed_color.x << ", " << fixed_color.y << ", " << fixed_color.z << ", "
<< fixed_color.w << ");\n";
ss << "}\n";
return ss.str();
}
std::string ShaderGen::GenerateCopyFragmentShader(bool offset) const std::string ShaderGen::GenerateCopyFragmentShader(bool offset) const
{ {
std::stringstream ss; std::stringstream ss;

View File

@ -30,6 +30,7 @@ public:
std::string GenerateScreenQuadVertexShader(float z = 0.0f) const; std::string GenerateScreenQuadVertexShader(float z = 0.0f) const;
std::string GenerateUVQuadVertexShader() const; std::string GenerateUVQuadVertexShader() const;
std::string GenerateFillFragmentShader() const; std::string GenerateFillFragmentShader() const;
std::string GenerateFillFragmentShader(const GSVector4i fixed_color) const;
std::string GenerateCopyFragmentShader(bool offset = true) const; std::string GenerateCopyFragmentShader(bool offset = true) const;
std::string GenerateImGuiVertexShader() const; std::string GenerateImGuiVertexShader() const;