Skip to content

Commit

Permalink
GPU: Fill in unused/padded area in overlays
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Jan 18, 2025
1 parent 88b4337 commit 6131ddb
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 32 deletions.
94 changes: 67 additions & 27 deletions src/core/gpu_presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,6 @@ bool GPUPresenter::UpdateSettings(const GPUSettings& old_settings, Error* error)
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)
{
const GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend,
Expand Down Expand Up @@ -170,16 +165,19 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
GL_OBJECT_NAME(m_present_copy_pipeline, "Display Rotate/Copy Pipeline");

// blended variants
if (m_border_overlay_texture && m_border_overlay_alpha_blend)
if (m_border_overlay_texture)
{
// destination blend the main present, not source
plconfig.blend.enable = true;
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
plconfig.blend.alpha_blend_op = GPUPipeline::BlendOp::Add;
plconfig.blend.dst_alpha_blend = GPUPipeline::BlendFunc::Zero;
if (m_border_overlay_alpha_blend)
{
// destination blend the main present, not source
plconfig.blend.enable = true;
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
plconfig.blend.alpha_blend_op = GPUPipeline::BlendOp::Add;
plconfig.blend.dst_alpha_blend = GPUPipeline::BlendFunc::Zero;
}

plconfig.fragment_shader = fso.get();
if (!(m_display_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
Expand All @@ -191,6 +189,18 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
if (!(m_present_copy_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
return false;
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");
}
}

Expand Down Expand Up @@ -383,10 +393,12 @@ GPUDevice::PresentResult GPUPresenter::PresentDisplay()
display_rect = display_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,
const GSVector4i overlay_display_rect,
const GSVector4i display_rect, const GSVector4i draw_rect,
bool postfx)
{
Expand All @@ -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);

// 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 &&
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");
Expand All @@ -436,7 +448,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (really_postfx)
{
// 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.
GPUTexture* const postfx_input = m_display_postfx->GetInputTexture();
Expand All @@ -453,8 +465,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (have_prerotation || have_overlay)
{
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_display_rect);
ApplyDisplayPostProcess(postfx_output, postfx_input, real_draw_rect);
postfx_output->MakeReadyForSampling();

// Start draw to final buffer.
Expand All @@ -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 (have_overlay)
{
DrawTextureCopy(target_size, overlay_rect, m_border_overlay_texture.get(), false, true, prerotation);
DrawTextureCopy(target_size, draw_rect, postfx_output, m_border_overlay_alpha_blend, false, prerotation);
GL_SCOPE_FMT("Draw overlay and postfx buffer");
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
{
// Ohterwise, just copy the framebuffer.
DrawTextureCopy(target_size, draw_rect, postfx_output, false, false, prerotation);
// Otherwise, just copy the framebuffer.
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
Expand All @@ -490,7 +514,22 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
return pres;

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)
{
Expand Down Expand Up @@ -676,7 +715,8 @@ void GPUPresenter::SendDisplayToMediaCapture(MediaCapture* cap)
// Not cleared by RenderDisplay().
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]]
{
WARNING_LOG("Failed to render/deliver video capture frame.");
Expand Down Expand Up @@ -1003,7 +1043,7 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, const GSVecto
g_gpu_device->ClearRenderTarget(render_texture.get(), GPUDevice::DEFAULT_CLEAR_COLOR);

// 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);

Expand Down Expand Up @@ -1102,7 +1142,7 @@ bool GPUPresenter::UpdatePostProcessingSettings(bool force_reload, Error* error)
{
// something changed, need to recompile pipelines
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))
{
return false;
Expand Down
8 changes: 4 additions & 4 deletions src/core/gpu_presenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ class ALIGN_TO_CACHE_LINE GPUPresenter final
bool Initialize(Error* error);

bool UpdateSettings(const GPUSettings& old_settings, Error* error);

bool IsDisplayPostProcessingActive() const;
bool UpdatePostProcessingSettings(bool force_reload, Error* error);

void ClearDisplay();
Expand Down Expand Up @@ -106,7 +104,8 @@ class ALIGN_TO_CACHE_LINE GPUPresenter final
bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error);

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,
DisplayRotation rotation, WindowInfo::PreRotation prerotation);
Expand Down Expand Up @@ -158,12 +157,13 @@ class ALIGN_TO_CACHE_LINE GPUPresenter final
std::unique_ptr<GPUPipeline> m_present_copy_pipeline;

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
std::unique_ptr<GPUPipeline> m_display_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();

// Low-traffic variables down here.
Expand Down
4 changes: 3 additions & 1 deletion src/duckstation-qt/postprocessingsettingswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "common/error.h"

#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout>
Expand Down Expand Up @@ -539,7 +540,8 @@ void PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged(int ind

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)"));
if (path.isEmpty())
return;
Expand Down
14 changes: 14 additions & 0 deletions src/util/shadergen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,20 @@ std::string ShaderGen::GenerateFillFragmentShader() const
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::stringstream ss;
Expand Down
1 change: 1 addition & 0 deletions src/util/shadergen.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ShaderGen
std::string GenerateScreenQuadVertexShader(float z = 0.0f) const;
std::string GenerateUVQuadVertexShader() const;
std::string GenerateFillFragmentShader() const;
std::string GenerateFillFragmentShader(const GSVector4i fixed_color) const;
std::string GenerateCopyFragmentShader(bool offset = true) const;

std::string GenerateImGuiVertexShader() const;
Expand Down

0 comments on commit 6131ddb

Please sign in to comment.