mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-07 20:15:32 +00:00
GPU: Fill in unused/padded area in overlays
This commit is contained in:
parent
88b43370dc
commit
6131ddbefe
@ -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,16 +165,19 @@ 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)
|
||||||
{
|
{
|
||||||
// destination blend the main present, not source
|
if (m_border_overlay_alpha_blend)
|
||||||
plconfig.blend.enable = true;
|
{
|
||||||
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
|
// destination blend the main present, not source
|
||||||
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
|
plconfig.blend.enable = true;
|
||||||
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
|
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
|
||||||
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
|
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
|
||||||
plconfig.blend.alpha_blend_op = GPUPipeline::BlendOp::Add;
|
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
|
||||||
plconfig.blend.dst_alpha_blend = GPUPipeline::BlendFunc::Zero;
|
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();
|
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;
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user