mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-07 12:05:52 +00:00
GPU: Add alpha blending support to overlay
This commit is contained in:
parent
9b8d2a88de
commit
9fdeeb3fb6
@ -4,3 +4,4 @@ displayStartY: 260
|
|||||||
displayEndX: 2850
|
displayEndX: 2850
|
||||||
displayEndY: 1655
|
displayEndY: 1655
|
||||||
alphaBlend: false
|
alphaBlend: false
|
||||||
|
destinationAlphaBlend: false
|
||||||
|
@ -5771,10 +5771,15 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
|||||||
FSUI_CSTR("Determines the area of the overlay image that the display will be drawn within."), "BorderOverlay",
|
FSUI_CSTR("Determines the area of the overlay image that the display will be drawn within."), "BorderOverlay",
|
||||||
"DisplayStartX", 0, "DisplayStartY", 0, "DisplayEndX", 0, "DisplayEndY", 0, 0, 65535, "%dpx");
|
"DisplayStartX", 0, "DisplayStartY", 0, "DisplayEndX", 0, "DisplayEndY", 0, 0, 65535, "%dpx");
|
||||||
|
|
||||||
|
reload_pending |=
|
||||||
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BLENDER, "Alpha Blending"),
|
||||||
|
FSUI_CSTR("If enabled, the transparency of the overlay image will be applied."),
|
||||||
|
"BorderOverlay", "AlphaBlend", false);
|
||||||
|
|
||||||
reload_pending |= DrawToggleSetting(
|
reload_pending |= DrawToggleSetting(
|
||||||
bsi, FSUI_ICONSTR(ICON_FA_BLENDER, "Destination Alpha Blending"),
|
bsi, FSUI_ICONSTR(ICON_FA_BLENDER, "Destination Alpha Blending"),
|
||||||
FSUI_CSTR("If enabled, the display will be blended with the transparency of the overlay image."),
|
FSUI_CSTR("If enabled, the display will be blended with the transparency of the overlay image."),
|
||||||
"BorderOverlay", "AlphaBlend", false);
|
"BorderOverlay", "DestinationAlphaBlend", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8660,6 +8665,7 @@ TRANSLATE_NOOP("FullscreenUI", "Advanced Settings");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "All Time: {}");
|
TRANSLATE_NOOP("FullscreenUI", "All Time: {}");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Allow Booting Without SBI File");
|
TRANSLATE_NOOP("FullscreenUI", "Allow Booting Without SBI File");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Allows loading protected games without subchannel information.");
|
TRANSLATE_NOOP("FullscreenUI", "Allows loading protected games without subchannel information.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Alpha Blending");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Always Track Uploads");
|
TRANSLATE_NOOP("FullscreenUI", "Always Track Uploads");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "An error occurred while deleting empty game settings:\n{}");
|
TRANSLATE_NOOP("FullscreenUI", "An error occurred while deleting empty game settings:\n{}");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "An error occurred while saving game settings:\n{}");
|
TRANSLATE_NOOP("FullscreenUI", "An error occurred while saving game settings:\n{}");
|
||||||
@ -8925,6 +8931,7 @@ TRANSLATE_NOOP("FullscreenUI", "How many saves will be kept for rewinding. Highe
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "How often a rewind state will be created. Higher frequencies have greater system requirements.");
|
TRANSLATE_NOOP("FullscreenUI", "How often a rewind state will be created. Higher frequencies have greater system requirements.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Identifies any new files added to the game directories.");
|
TRANSLATE_NOOP("FullscreenUI", "Identifies any new files added to the game directories.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "If enabled, the display will be blended with the transparency of the overlay image.");
|
TRANSLATE_NOOP("FullscreenUI", "If enabled, the display will be blended with the transparency of the overlay image.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "If enabled, the transparency of the overlay image will be applied.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "If not enabled, the current post processing chain will be ignored.");
|
TRANSLATE_NOOP("FullscreenUI", "If not enabled, the current post processing chain will be ignored.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Image Path");
|
TRANSLATE_NOOP("FullscreenUI", "Image Path");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Increases the field of view from 4:3 to the chosen display aspect ratio in 3D games.");
|
TRANSLATE_NOOP("FullscreenUI", "Increases the field of view from 4:3 to the chosen display aspect ratio in 3D games.");
|
||||||
|
@ -153,42 +153,51 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
|
|||||||
GL_OBJECT_NAME_FMT(m_display_pipeline, "Display Pipeline [{}]",
|
GL_OBJECT_NAME_FMT(m_display_pipeline, "Display Pipeline [{}]",
|
||||||
Settings::GetDisplayScalingName(g_gpu_settings.display_scaling));
|
Settings::GetDisplayScalingName(g_gpu_settings.display_scaling));
|
||||||
|
|
||||||
std::unique_ptr<GPUShader> rotate_copy_fso = g_gpu_device->CreateShader(
|
std::unique_ptr<GPUShader> copy_fso = g_gpu_device->CreateShader(
|
||||||
GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateCopyFragmentShader(false), error);
|
GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateCopyFragmentShader(false), error);
|
||||||
if (!rotate_copy_fso)
|
if (!copy_fso)
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(rotate_copy_fso, "Display Rotate/Copy Fragment Shader");
|
GL_OBJECT_NAME(copy_fso, "Display Copy Fragment Shader");
|
||||||
|
|
||||||
plconfig.fragment_shader = rotate_copy_fso.get();
|
plconfig.fragment_shader = copy_fso.get();
|
||||||
if (!(m_present_copy_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
|
if (!(m_present_copy_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(m_present_copy_pipeline, "Display Rotate/Copy Pipeline");
|
GL_OBJECT_NAME(m_present_copy_pipeline, "Display Copy Pipeline");
|
||||||
|
|
||||||
// blended variants
|
// blended variants
|
||||||
if (m_border_overlay_texture)
|
if (m_border_overlay_texture)
|
||||||
{
|
{
|
||||||
std::unique_ptr<GPUShader> clear_fso =
|
std::unique_ptr<GPUShader> clear_fso = g_gpu_device->CreateShader(
|
||||||
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
|
GPUShaderStage::Fragment, shadergen.GetLanguage(),
|
||||||
shadergen.GenerateFillFragmentShader(GSVector4i::zero()), error);
|
shadergen.GenerateFillFragmentShader(GSVector4::cxpr(0.0f, 0.0f, 0.0f, 1.0f)), error);
|
||||||
if (!clear_fso)
|
if (!clear_fso)
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(clear_fso, "Display Clear Fragment Shader");
|
GL_OBJECT_NAME(clear_fso, "Display Clear Fragment Shader");
|
||||||
|
|
||||||
plconfig.fragment_shader = clear_fso.get();
|
plconfig.fragment_shader = copy_fso.get();
|
||||||
if (!(m_present_clear_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
|
plconfig.blend = m_border_overlay_alpha_blend ? GPUPipeline::BlendState::GetAlphaBlendingState() :
|
||||||
|
GPUPipeline::BlendState::GetNoBlendingState();
|
||||||
|
if (!(m_border_overlay_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(m_present_clear_pipeline, "Display Clear Pipeline");
|
GL_OBJECT_NAME(m_border_overlay_pipeline, "Border Overlay Pipeline");
|
||||||
|
|
||||||
if (m_border_overlay_alpha_blend)
|
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
||||||
|
if (m_border_overlay_destination_alpha_blend)
|
||||||
{
|
{
|
||||||
// destination blend the main present, not source
|
// destination blend the main present, not source
|
||||||
plconfig.blend.enable = true;
|
plconfig.blend.enable = true;
|
||||||
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
|
plconfig.blend.src_blend = GPUPipeline::BlendFunc::InvDstAlpha;
|
||||||
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
|
plconfig.blend.blend_op = GPUPipeline::BlendOp::Add;
|
||||||
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
|
plconfig.blend.dst_blend = GPUPipeline::BlendFunc::One;
|
||||||
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::One;
|
plconfig.blend.src_alpha_blend = GPUPipeline::BlendFunc::Zero;
|
||||||
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::One;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
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)))
|
||||||
@ -196,23 +205,17 @@ bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool
|
|||||||
GL_OBJECT_NAME_FMT(m_display_blend_pipeline, "Display Pipeline [Blended, {}]",
|
GL_OBJECT_NAME_FMT(m_display_blend_pipeline, "Display Pipeline [Blended, {}]",
|
||||||
Settings::GetDisplayScalingName(g_gpu_settings.display_scaling));
|
Settings::GetDisplayScalingName(g_gpu_settings.display_scaling));
|
||||||
|
|
||||||
plconfig.fragment_shader = rotate_copy_fso.get();
|
plconfig.fragment_shader = copy_fso.get();
|
||||||
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 Copy Pipeline [Blended]");
|
||||||
|
|
||||||
plconfig.fragment_shader = clear_fso.get();
|
|
||||||
if (!(m_present_clear_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error)))
|
|
||||||
return false;
|
|
||||||
GL_OBJECT_NAME(m_present_clear_blend_pipeline, "Display Clear Pipeline [Blended]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
m_border_overlay_pipeline.reset();
|
||||||
m_present_clear_pipeline.reset();
|
m_present_clear_pipeline.reset();
|
||||||
m_display_blend_pipeline.reset();
|
m_display_blend_pipeline.reset();
|
||||||
m_present_copy_blend_pipeline.reset();
|
m_present_copy_blend_pipeline.reset();
|
||||||
m_present_clear_blend_pipeline.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,13 +486,12 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
|
|||||||
if (have_overlay)
|
if (have_overlay)
|
||||||
{
|
{
|
||||||
GL_SCOPE_FMT("Draw overlay and postfx buffer");
|
GL_SCOPE_FMT("Draw overlay and postfx buffer");
|
||||||
g_gpu_device->SetPipeline(m_present_copy_pipeline.get());
|
g_gpu_device->SetPipeline(m_border_overlay_pipeline.get());
|
||||||
g_gpu_device->SetTextureSampler(0, m_border_overlay_texture.get(), g_gpu_device->GetLinearSampler());
|
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,
|
DrawScreenQuad(overlay_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size, DisplayRotation::Normal,
|
||||||
prerotation);
|
prerotation);
|
||||||
|
|
||||||
g_gpu_device->SetPipeline(m_border_overlay_alpha_blend ? m_present_copy_blend_pipeline.get() :
|
g_gpu_device->SetPipeline(m_border_overlay_pipeline.get());
|
||||||
m_present_copy_pipeline.get());
|
|
||||||
g_gpu_device->SetTextureSampler(0, postfx_output, g_gpu_device->GetNearestSampler());
|
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,
|
DrawScreenQuad(overlay_display_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size,
|
||||||
DisplayRotation::Normal, prerotation);
|
DisplayRotation::Normal, prerotation);
|
||||||
@ -523,7 +525,7 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
|
|||||||
if (have_overlay)
|
if (have_overlay)
|
||||||
{
|
{
|
||||||
GL_SCOPE_FMT("Draw overlay to {}", overlay_rect);
|
GL_SCOPE_FMT("Draw overlay to {}", overlay_rect);
|
||||||
g_gpu_device->SetPipeline(m_present_copy_pipeline.get());
|
g_gpu_device->SetPipeline(m_border_overlay_pipeline.get());
|
||||||
g_gpu_device->SetTextureSampler(0, m_border_overlay_texture.get(), g_gpu_device->GetLinearSampler());
|
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,
|
DrawScreenQuad(overlay_rect, GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f), target_size, DisplayRotation::Normal,
|
||||||
@ -533,15 +535,14 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
|
|||||||
{
|
{
|
||||||
// Need to fill in the borders.
|
// Need to fill in the borders.
|
||||||
GL_SCOPE_FMT("Fill in overlay borders - odisplay={}, draw={}", overlay_display_rect, draw_rect);
|
GL_SCOPE_FMT("Fill in overlay borders - odisplay={}, draw={}", overlay_display_rect, draw_rect);
|
||||||
g_gpu_device->SetPipeline(m_border_overlay_alpha_blend ? m_present_clear_blend_pipeline.get() :
|
g_gpu_device->SetPipeline(m_present_clear_pipeline.get());
|
||||||
m_present_clear_pipeline.get());
|
|
||||||
DrawScreenQuad(overlay_display_rect, GSVector4::zero(), target_size, g_settings.display_rotation, prerotation);
|
DrawScreenQuad(overlay_display_rect, GSVector4::zero(), target_size, g_settings.display_rotation, prerotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_display_texture)
|
if (m_display_texture)
|
||||||
{
|
{
|
||||||
DrawDisplay(target_size, display_rect, m_border_overlay_alpha_blend, g_gpu_settings.display_rotation,
|
DrawDisplay(target_size, display_rect, m_border_overlay_destination_alpha_blend, g_gpu_settings.display_rotation,
|
||||||
prerotation);
|
prerotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,15 +1128,10 @@ bool GPUPresenter::UpdatePostProcessingSettings(bool force_reload, Error* error)
|
|||||||
if (LoadOverlaySettings())
|
if (LoadOverlaySettings())
|
||||||
{
|
{
|
||||||
// something changed, need to recompile pipelines, the needed pipelines are based on alpha blend
|
// something changed, need to recompile pipelines, the needed pipelines are based on alpha blend
|
||||||
if (LoadOverlayTexture() &&
|
LoadOverlayTexture();
|
||||||
((m_border_overlay_alpha_blend &&
|
if (!CompileDisplayPipelines(true, false, false, error))
|
||||||
(!m_present_copy_blend_pipeline || !m_display_blend_pipeline || !m_present_clear_blend_pipeline)) ||
|
|
||||||
!m_present_clear_pipeline) &&
|
|
||||||
!CompileDisplayPipelines(true, false, false, error))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update postfx settings
|
// Update postfx settings
|
||||||
{
|
{
|
||||||
@ -1245,6 +1241,7 @@ bool GPUPresenter::LoadOverlaySettings()
|
|||||||
std::string image_path;
|
std::string image_path;
|
||||||
GSVector4i display_rect = m_border_overlay_display_rect;
|
GSVector4i display_rect = m_border_overlay_display_rect;
|
||||||
bool alpha_blend = m_border_overlay_alpha_blend;
|
bool alpha_blend = m_border_overlay_alpha_blend;
|
||||||
|
bool destination_alpha_blend = m_border_overlay_destination_alpha_blend;
|
||||||
if (preset_name == "Custom")
|
if (preset_name == "Custom")
|
||||||
{
|
{
|
||||||
image_path = Host::GetStringSettingValue("BorderOverlay", "ImagePath");
|
image_path = Host::GetStringSettingValue("BorderOverlay", "ImagePath");
|
||||||
@ -1253,6 +1250,7 @@ bool GPUPresenter::LoadOverlaySettings()
|
|||||||
Host::GetIntSettingValue("BorderOverlay", "DisplayEndX", 0),
|
Host::GetIntSettingValue("BorderOverlay", "DisplayEndX", 0),
|
||||||
Host::GetIntSettingValue("BorderOverlay", "DisplayEndY", 0));
|
Host::GetIntSettingValue("BorderOverlay", "DisplayEndY", 0));
|
||||||
alpha_blend = Host::GetBoolSettingValue("BorderOverlay", "AlphaBlend", false);
|
alpha_blend = Host::GetBoolSettingValue("BorderOverlay", "AlphaBlend", false);
|
||||||
|
destination_alpha_blend = Host::GetBoolSettingValue("BorderOverlay", "DestinationAlphaBlend", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check rect validity.. ignore everything if it's bogus
|
// check rect validity.. ignore everything if it's bogus
|
||||||
@ -1281,15 +1279,16 @@ bool GPUPresenter::LoadOverlaySettings()
|
|||||||
m_border_overlay_display_rect = display_rect;
|
m_border_overlay_display_rect = display_rect;
|
||||||
|
|
||||||
// but images and alphablend require pipeline/texture changes
|
// but images and alphablend require pipeline/texture changes
|
||||||
if (m_border_overlay_image_path == image_path && (image_path.empty() || alpha_blend == m_border_overlay_alpha_blend))
|
const bool image_changed = (m_border_overlay_image_path != image_path);
|
||||||
{
|
const bool changed =
|
||||||
m_border_overlay_alpha_blend = alpha_blend;
|
(image_changed || (!image_path.empty() && (alpha_blend == m_border_overlay_alpha_blend ||
|
||||||
return false;
|
destination_alpha_blend == m_border_overlay_destination_alpha_blend)));
|
||||||
}
|
if (image_changed)
|
||||||
|
|
||||||
m_border_overlay_image_path = std::move(image_path);
|
m_border_overlay_image_path = std::move(image_path);
|
||||||
|
|
||||||
m_border_overlay_alpha_blend = alpha_blend;
|
m_border_overlay_alpha_blend = alpha_blend;
|
||||||
return true;
|
m_border_overlay_destination_alpha_blend = destination_alpha_blend;
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUPresenter::LoadOverlayTexture()
|
bool GPUPresenter::LoadOverlayTexture()
|
||||||
@ -1371,13 +1370,15 @@ bool GPUPresenter::LoadOverlayPreset(Error* error, Image* image)
|
|||||||
|
|
||||||
std::string_view image_filename;
|
std::string_view image_filename;
|
||||||
GSVector4i display_area = GSVector4i::zero();
|
GSVector4i display_area = GSVector4i::zero();
|
||||||
bool display_alpha_blend = false;
|
bool alpha_blend = false;
|
||||||
|
bool destination_alpha_blend = false;
|
||||||
if (!GetStringFromObject(root, "image", &image_filename) ||
|
if (!GetStringFromObject(root, "image", &image_filename) ||
|
||||||
!GetUIntFromObject(root, "displayStartX", &display_area.x) ||
|
!GetUIntFromObject(root, "displayStartX", &display_area.x) ||
|
||||||
!GetUIntFromObject(root, "displayStartY", &display_area.y) ||
|
!GetUIntFromObject(root, "displayStartY", &display_area.y) ||
|
||||||
!GetUIntFromObject(root, "displayEndX", &display_area.z) ||
|
!GetUIntFromObject(root, "displayEndX", &display_area.z) ||
|
||||||
!GetUIntFromObject(root, "displayEndY", &display_area.w) ||
|
!GetUIntFromObject(root, "displayEndY", &display_area.w) ||
|
||||||
!GetUIntFromObject(root, "alphaBlend", &display_alpha_blend))
|
!GetUIntFromObject(root, "alphaBlend", &alpha_blend) ||
|
||||||
|
!GetUIntFromObject(root, "destinationAlphaBlend", &destination_alpha_blend))
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "One or more parameters is missing.");
|
Error::SetStringView(error, "One or more parameters is missing.");
|
||||||
return false;
|
return false;
|
||||||
@ -1389,6 +1390,7 @@ bool GPUPresenter::LoadOverlayPreset(Error* error, Image* image)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_border_overlay_display_rect = display_area;
|
m_border_overlay_display_rect = display_area;
|
||||||
m_border_overlay_alpha_blend = display_alpha_blend;
|
m_border_overlay_alpha_blend = alpha_blend;
|
||||||
|
m_border_overlay_destination_alpha_blend = destination_alpha_blend;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -147,17 +147,17 @@ private:
|
|||||||
u32 m_skipped_present_count = 0;
|
u32 m_skipped_present_count = 0;
|
||||||
GPUTexture::Format m_present_format = GPUTexture::Format::Unknown;
|
GPUTexture::Format m_present_format = GPUTexture::Format::Unknown;
|
||||||
bool m_border_overlay_alpha_blend = false;
|
bool m_border_overlay_alpha_blend = false;
|
||||||
|
bool m_border_overlay_destination_alpha_blend = false;
|
||||||
|
|
||||||
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;
|
std::unique_ptr<GPUTexture> m_border_overlay_texture;
|
||||||
std::unique_ptr<GPUPipeline> m_present_clear_pipeline;
|
|
||||||
|
|
||||||
// blended variants of pipelines, used when overlays are enabled
|
std::unique_ptr<GPUPipeline> m_border_overlay_pipeline;
|
||||||
|
std::unique_ptr<GPUPipeline> m_present_clear_pipeline;
|
||||||
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_blend_pipeline;
|
|
||||||
|
|
||||||
GSVector4i m_border_overlay_display_rect = GSVector4i::zero();
|
GSVector4i m_border_overlay_display_rect = GSVector4i::zero();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>540</width>
|
<width>540</width>
|
||||||
<height>355</height>
|
<height>381</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -115,10 +115,10 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="alphaBlend">
|
<widget class="QLabel" name="label_9">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Destination Alpha Blending</string>
|
<string>Display End:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -160,13 +160,48 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_9">
|
<layout class="QGridLayout" name="gridLayout" columnstretch="1,1">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="destinationAlphaBlend">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Display End:</string>
|
<string>Destination Alpha Blending</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="alphaBlend">
|
||||||
|
<property name="text">
|
||||||
|
<string>Alpha Blending</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>1</width>
|
||||||
|
<height>1</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="exportCustomConfig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -511,6 +511,8 @@ PostProcessingOverlayConfigWidget::PostProcessingOverlayConfigWidget(SettingsWin
|
|||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayEndX, "BorderOverlay", "DisplayEndX", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayEndX, "BorderOverlay", "DisplayEndX", 0);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayEndY, "BorderOverlay", "DisplayEndY", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayEndY, "BorderOverlay", "DisplayEndY", 0);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alphaBlend, "BorderOverlay", "AlphaBlend", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alphaBlend, "BorderOverlay", "AlphaBlend", false);
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.destinationAlphaBlend, "BorderOverlay",
|
||||||
|
"DestinationAlphaBlend", false);
|
||||||
|
|
||||||
connect(m_ui.overlayName, &QComboBox::currentIndexChanged, this,
|
connect(m_ui.overlayName, &QComboBox::currentIndexChanged, this,
|
||||||
&PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged);
|
&PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged);
|
||||||
@ -525,12 +527,36 @@ PostProcessingOverlayConfigWidget::PostProcessingOverlayConfigWidget(SettingsWin
|
|||||||
connect(m_ui.displayEndY, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
|
connect(m_ui.displayEndY, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
|
||||||
connect(m_ui.alphaBlend, &QCheckBox::checkStateChanged, this,
|
connect(m_ui.alphaBlend, &QCheckBox::checkStateChanged, this,
|
||||||
&PostProcessingOverlayConfigWidget::triggerSettingsReload);
|
&PostProcessingOverlayConfigWidget::triggerSettingsReload);
|
||||||
|
connect(m_ui.destinationAlphaBlend, &QCheckBox::checkStateChanged, this,
|
||||||
|
&PostProcessingOverlayConfigWidget::triggerSettingsReload);
|
||||||
|
|
||||||
onOverlayNameCurrentIndexChanged(m_ui.overlayName->currentIndex());
|
onOverlayNameCurrentIndexChanged(m_ui.overlayName->currentIndex());
|
||||||
|
|
||||||
|
dialog->registerWidgetHelp(m_ui.imagePath, tr("Image Path"), tr("Unspecified"),
|
||||||
|
tr("Defines the path of the custom overlay image that will be loaded."));
|
||||||
|
const QString display_rect_title = tr("Display Rectangle");
|
||||||
|
const QString display_rect_rec_value = tr("Unspecified");
|
||||||
|
const QString display_rect_help = tr("Defines the area in the overlay image that the game image will be drawn into.");
|
||||||
|
dialog->registerWidgetHelp(m_ui.displayStartX, display_rect_title, display_rect_rec_value, display_rect_help);
|
||||||
|
dialog->registerWidgetHelp(m_ui.displayStartY, display_rect_title, display_rect_rec_value, display_rect_help);
|
||||||
|
dialog->registerWidgetHelp(m_ui.displayEndX, display_rect_title, display_rect_rec_value, display_rect_help);
|
||||||
|
dialog->registerWidgetHelp(m_ui.displayEndY, display_rect_title, display_rect_rec_value, display_rect_help);
|
||||||
|
dialog->registerWidgetHelp(
|
||||||
|
m_ui.alphaBlend, tr("Alpha Blending"), tr("Unchecked"),
|
||||||
|
tr("If checked, the overlay image will be alpha blended with the framebuffer, i.e. transparency will be applied."));
|
||||||
|
dialog->registerWidgetHelp(
|
||||||
|
m_ui.destinationAlphaBlend, tr("Destination Alpha Blending"), tr("Unchecked"),
|
||||||
|
tr("If checked, the game image will be blended with the inverse amount of alpha in the overlay image. For example, "
|
||||||
|
"an image with alpha of 0.75 will draw the game image at 25% brightness."));
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingOverlayConfigWidget::~PostProcessingOverlayConfigWidget() = default;
|
PostProcessingOverlayConfigWidget::~PostProcessingOverlayConfigWidget() = default;
|
||||||
|
|
||||||
|
void PostProcessingOverlayConfigWidget::triggerSettingsReload()
|
||||||
|
{
|
||||||
|
g_emu_thread->updatePostProcessingSettings(true, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
void PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged(int index)
|
void PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
const int custom_idx = m_dialog->isPerGameSettings() ? 2 : 1;
|
const int custom_idx = m_dialog->isPerGameSettings() ? 2 : 1;
|
||||||
@ -549,7 +575,33 @@ void PostProcessingOverlayConfigWidget::onImagePathBrowseClicked()
|
|||||||
m_ui.imagePath->setText(QDir::toNativeSeparators(path));
|
m_ui.imagePath->setText(QDir::toNativeSeparators(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingOverlayConfigWidget::triggerSettingsReload()
|
void PostProcessingOverlayConfigWidget::onExportCustomConfigClicked()
|
||||||
{
|
{
|
||||||
g_emu_thread->updatePostProcessingSettings(true, false, false);
|
const QString path =
|
||||||
|
QFileDialog::getSaveFileName(QtUtils::GetRootWidget(this), tr("Export to YAML"),
|
||||||
|
QFileInfo(m_ui.imagePath->text()).dir().path(), tr("YAML Files (*.yml)"));
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString output = QStringLiteral("imagePath: \"%1\"\n"
|
||||||
|
"displayStartX: %2\n"
|
||||||
|
"displayStartY: %3\n"
|
||||||
|
"displayEndX: %4\n"
|
||||||
|
"displayEndY: %5\n"
|
||||||
|
"alphaBlend: %6\n"
|
||||||
|
"destinationAlphaBlend: %7\n")
|
||||||
|
.arg(QFileInfo(m_ui.imagePath->text()).fileName(), m_ui.displayStartX->value())
|
||||||
|
.arg(m_ui.displayStartY->value())
|
||||||
|
.arg(m_ui.displayEndX->value())
|
||||||
|
.arg(m_ui.displayEndY->value())
|
||||||
|
.arg(m_ui.alphaBlend->isChecked() ? "true" : "false")
|
||||||
|
.arg(m_ui.destinationAlphaBlend->isChecked() ? "true" : "false");
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
if (!FileSystem::WriteStringToFile(QDir::toNativeSeparators(path).toStdString().c_str(), output.toStdString(),
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Export Error"),
|
||||||
|
tr("Failed to save file: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,10 @@ public:
|
|||||||
~PostProcessingOverlayConfigWidget();
|
~PostProcessingOverlayConfigWidget();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void triggerSettingsReload();
|
||||||
void onOverlayNameCurrentIndexChanged(int index);
|
void onOverlayNameCurrentIndexChanged(int index);
|
||||||
void onImagePathBrowseClicked();
|
void onImagePathBrowseClicked();
|
||||||
void triggerSettingsReload();
|
void onExportCustomConfigClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::PostProcessingOverlayConfigWidget m_ui;
|
Ui::PostProcessingOverlayConfigWidget m_ui;
|
||||||
|
@ -845,7 +845,7 @@ std::string ShaderGen::GenerateFillFragmentShader() const
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ShaderGen::GenerateFillFragmentShader(const GSVector4i fixed_color) const
|
std::string ShaderGen::GenerateFillFragmentShader(const GSVector4 fixed_color) const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
WriteHeader(ss);
|
WriteHeader(ss);
|
||||||
|
@ -30,7 +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 GenerateFillFragmentShader(const GSVector4 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