PostProcessing: Move into GPU presenter

Means that the renderer can be changed without reloading shaders,
and speeds up config change detection.
This commit is contained in:
Stenzek 2025-01-18 15:44:21 +10:00
parent d589696eff
commit e0a9bbe600
No known key found for this signature in database
17 changed files with 401 additions and 293 deletions

View File

@ -356,7 +356,7 @@ static void DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, c
float step_value, float multiplier, const char* format = "%f", bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont);
static void DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
static bool DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* left_key, int default_left, const char* top_key, int default_top,
const char* right_key, int default_right, const char* bottom_key, int default_bottom,
int min_value, int max_value, const char* format = "%d", bool enabled = true,
@ -2762,7 +2762,7 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t
ImGui::PopFont();
}
void FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary,
bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary,
const char* section, const char* left_key, int default_left, const char* top_key,
int default_top, const char* right_key, int default_right, const char* bottom_key,
int default_bottom, int min_value, int max_value, const char* format,
@ -2799,6 +2799,7 @@ void FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title,
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f));
bool is_open = true;
bool changed = false;
if (ImGui::BeginPopupModal(title, &is_open,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
{
@ -2864,7 +2865,8 @@ void FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title,
bsi->SetIntValue(section, bottom_key, dlg_bottom_value);
}
if (left_modified || top_modified || right_modified || bottom_modified)
changed = (left_modified || top_modified || right_modified || bottom_modified);
if (changed)
SetSettingsChanged(bsi);
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
@ -2879,6 +2881,8 @@ void FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title,
ImGui::PopStyleVar(4);
ImGui::PopFont();
return changed;
}
void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary,
@ -5304,16 +5308,28 @@ enum
void FullscreenUI::DrawPostProcessingSettingsPage()
{
SettingsInterface* bsi = GetEditingSettingsInterface();
static constexpr const char* section = PostProcessing::Config::DISPLAY_CHAIN_SECTION;
static constexpr auto queue_reload = []() {
if (GPUThread::HasGPUBackend())
{
Host::RunOnCPUThread([]() {
if (System::IsValid())
GPUPresenter::ReloadPostProcessingSettings(true, false, false);
});
}
};
SettingsInterface* bsi = GetEditingSettingsInterface();
bool reload_pending = false;
BeginMenuButtons();
MenuHeading(FSUI_CSTR("Controls"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGIC, "Enable Post Processing"),
FSUI_CSTR("If not enabled, the current post processing chain will be ignored."), "PostProcessing",
"Enabled", false);
reload_pending |= DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGIC, "Enable Post Processing"),
FSUI_CSTR("If not enabled, the current post processing chain will be ignored."),
"PostProcessing", "Enabled", false);
if (MenuButton(FSUI_ICONSTR(ICON_FA_SEARCH, "Reload Shaders"),
FSUI_CSTR("Reloads the shaders from disk, applying any changes."),
@ -5322,12 +5338,8 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
// Have to defer because of the settings lock.
if (GPUThread::HasGPUBackend())
{
Host::RunOnCPUThread([]() {
GPUThread::RunOnThread([]() {
if (PostProcessing::ReloadShaders())
ShowToast(std::string(), FSUI_STR("Post-processing shaders reloaded."));
});
});
Host::RunOnCPUThread([]() { GPUPresenter::ReloadPostProcessingSettings(true, true, true); });
ShowToast(std::string(), FSUI_STR("Post-processing shaders reloaded."));
}
}
@ -5355,6 +5367,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
PostProcessing::Config::GetStageCount(*bsi, section)));
PopulatePostProcessingChain(bsi, section);
SetSettingsChanged(bsi);
queue_reload();
}
else
{
@ -5379,6 +5392,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
PopulatePostProcessingChain(bsi, section);
SetSettingsChanged(bsi);
ShowToast(std::string(), FSUI_STR("Post-processing chain cleared."));
queue_reload();
});
}
@ -5435,6 +5449,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
opt.value[0].int_value = (value != 0);
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
SetSettingsChanged(bsi);
queue_reload();
}
}
break;
@ -5520,6 +5535,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
{
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
SetSettingsChanged(bsi);
reload_pending = true;
}
#endif
@ -5619,6 +5635,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
{
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
SetSettingsChanged(bsi);
reload_pending = true;
}
#endif
@ -5656,6 +5673,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
PostProcessing::Config::RemoveStage(*bsi, section, postprocessing_action_index);
PopulatePostProcessingChain(bsi, section);
SetSettingsChanged(bsi);
reload_pending = true;
}
break;
case POSTPROCESSING_ACTION_MOVE_UP:
@ -5663,6 +5681,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
PostProcessing::Config::MoveStageUp(*bsi, section, postprocessing_action_index);
PopulatePostProcessingChain(bsi, section);
SetSettingsChanged(bsi);
reload_pending = true;
}
break;
case POSTPROCESSING_ACTION_MOVE_DOWN:
@ -5670,6 +5689,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
PostProcessing::Config::MoveStageDown(*bsi, section, postprocessing_action_index);
PopulatePostProcessingChain(bsi, section);
SetSettingsChanged(bsi);
reload_pending = true;
}
break;
default:
@ -5723,6 +5743,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
bsi->SetStringValue("BorderOverlay", "PresetName", new_value);
}
SetSettingsChanged(bsi);
queue_reload();
});
}
@ -5740,16 +5761,17 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
SettingsInterface* const bsi = GetEditingSettingsInterface(game_settings);
bsi->SetStringValue("BorderOverlay", "ImagePath", path.c_str());
SetSettingsChanged(bsi);
queue_reload();
},
GetImageFilters());
}
DrawIntRectSetting(bsi, FSUI_ICONSTR(ICON_FA_BORDER_STYLE, "Display Area"),
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");
reload_pending |= DrawIntRectSetting(
bsi, FSUI_ICONSTR(ICON_FA_BORDER_STYLE, "Display Area"),
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");
DrawToggleSetting(
reload_pending |= DrawToggleSetting(
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."),
"BorderOverlay", "AlphaBlend", false);
@ -5757,6 +5779,9 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
}
EndMenuButtons();
if (reload_pending)
queue_reload();
}
void FullscreenUI::DrawAudioSettingsPage()
@ -8409,7 +8434,7 @@ LoadingScreenProgressCallback::~LoadingScreenProgressCallback()
else
{
// since this was pushing frames, we need to restore the context. do that by pushing a frame ourselves
GPUThread::Internal::DoRunIdle();
GPUThread::Internal::PresentFrameAndRestoreContext();
}
}

View File

@ -133,6 +133,10 @@ bool GPUBackend::UpdateSettings(const GPUSettings& old_settings, Error* error)
return true;
}
void GPUBackend::UpdatePostProcessingSettings(bool force_reload)
{
}
GPUThreadCommand* GPUBackend::NewClearVRAMCommand()
{
return static_cast<GPUThreadCommand*>(

View File

@ -75,10 +75,12 @@ public:
virtual ~GPUBackend();
ALWAYS_INLINE const GPUPresenter& GetPresenter() const { return m_presenter; }
ALWAYS_INLINE GPUPresenter& GetPresenter() { return m_presenter; }
virtual bool Initialize(bool upload_vram, Error* error);
virtual bool UpdateSettings(const GPUSettings& old_settings, Error* error);
virtual void UpdatePostProcessingSettings(bool force_reload);
/// Returns the current resolution scale.
virtual u32 GetResolutionScale() const = 0;

View File

@ -295,6 +295,7 @@ bool GPU_HW::Initialize(bool upload_vram, Error* error)
UpdateVRAMOnGPU(0, 0, VRAM_WIDTH, VRAM_HEIGHT, g_vram, VRAM_WIDTH * sizeof(u16), false, false, VRAM_SIZE_RECT);
m_drawing_area_changed = true;
LoadInternalPostProcessing();
return true;
}
@ -1995,7 +1996,7 @@ void GPU_HW::CopyAndClearDepthBuffer()
{
// Take a copy of the current depth buffer so it can be used when the previous frame/buffer gets scanned out.
// Don't bother when we're not postprocessing, it'd just be a wasted copy.
if (PostProcessing::InternalChain.NeedsDepthBuffer())
if (m_internal_postfx && m_internal_postfx->NeedsDepthBuffer())
{
// TODO: Shrink this to only the active area.
GL_SCOPE("Copy Depth Buffer");
@ -3849,12 +3850,12 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture(!m_vram_dirty_draw_rect.eq(INVALID_RECT), !m_vram_dirty_write_rect.eq(INVALID_RECT));
m_presenter.SetDisplayTexture(m_vram_read_texture.get(), nullptr, 0, 0, m_vram_read_texture->GetWidth(),
m_presenter.SetDisplayTexture(m_vram_read_texture.get(), 0, 0, m_vram_read_texture->GetWidth(),
m_vram_read_texture->GetHeight());
}
else
{
m_presenter.SetDisplayTexture(m_vram_texture.get(), nullptr, 0, 0, m_vram_texture->GetWidth(),
m_presenter.SetDisplayTexture(m_vram_texture.get(), 0, 0, m_vram_texture->GetWidth(),
m_vram_texture->GetHeight());
}
@ -3871,12 +3872,6 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
const u32 scaled_display_height = cmd->display_vram_height * resolution_scale;
bool drew_anything = false;
// Don't bother grabbing depth if postfx doesn't need it.
GPUTexture* depth_source =
(!cmd->display_24bit && m_pgxp_depth_buffer && PostProcessing::InternalChain.NeedsDepthBuffer()) ?
(m_depth_was_copied ? m_vram_depth_copy_texture.get() : m_vram_depth_texture.get()) :
nullptr;
if (cmd->display_disabled)
{
m_presenter.ClearDisplayTexture();
@ -3885,9 +3880,9 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
else if (!cmd->display_24bit && line_skip == 0 && !IsUsingMultisampling() &&
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight() &&
!PostProcessing::InternalChain.IsActive())
(!m_internal_postfx || !m_internal_postfx->IsActive()))
{
m_presenter.SetDisplayTexture(m_vram_texture.get(), depth_source, scaled_vram_offset_x, scaled_vram_offset_y,
m_presenter.SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
// Fast path if no copies are needed.
@ -3915,6 +3910,11 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
m_vram_texture->MakeReadyForSampling();
g_gpu_device->InvalidateRenderTarget(m_vram_extract_texture.get());
// Don't bother grabbing depth if postfx doesn't need it.
GPUTexture* depth_source =
(!cmd->display_24bit && m_pgxp_depth_buffer && m_internal_postfx && m_internal_postfx->NeedsDepthBuffer()) ?
(m_depth_was_copied ? m_vram_depth_copy_texture.get() : m_vram_depth_texture.get()) :
nullptr;
if (depth_source &&
g_gpu_device->ResizeTexture(&m_vram_extract_depth_texture, scaled_display_width, scaled_display_height,
GPUTexture::Type::RenderTarget, VRAM_DS_COLOR_FORMAT, GPUTexture::Flags::None))
@ -3968,9 +3968,21 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
drew_anything = true;
m_presenter.SetDisplayTexture(m_vram_extract_texture.get(),
depth_source ? m_vram_extract_depth_texture.get() : nullptr, 0, 0,
scaled_display_width, scaled_display_height);
m_presenter.SetDisplayTexture(m_vram_extract_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
// Apply internal postfx if enabled.
if (m_internal_postfx && m_internal_postfx->IsActive() &&
m_internal_postfx->CheckTargets(m_vram_texture->GetFormat(), scaled_display_width, scaled_display_height))
{
GPUTexture* const postfx_output = m_internal_postfx->GetOutputTexture();
m_internal_postfx->Apply(
m_vram_extract_texture.get(), depth_source ? m_vram_extract_depth_texture.get() : nullptr,
m_internal_postfx->GetOutputTexture(), GSVector4i(0, 0, scaled_display_width, scaled_display_height),
m_presenter.GetDisplayWidth(), m_presenter.GetDisplayHeight(), cmd->display_vram_width,
cmd->display_vram_height);
m_presenter.SetDisplayTexture(postfx_output, 0, 0, postfx_output->GetWidth(), postfx_output->GetHeight());
}
if (g_settings.display_24bit_chroma_smoothing)
{
if (m_presenter.ApplyChromaSmoothing())
@ -4158,7 +4170,7 @@ void GPU_HW::DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top
RestoreDeviceContext();
m_presenter.SetDisplayTexture(m_downsample_texture.get(), m_presenter.GetDisplayDepthBuffer(), 0, 0, width, height);
m_presenter.SetDisplayTexture(m_downsample_texture.get(), 0, 0, width, height);
}
void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
@ -4190,8 +4202,48 @@ void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 to
RestoreDeviceContext();
m_presenter.SetDisplayTexture(m_downsample_texture.get(), m_presenter.GetDisplayDepthBuffer(), 0, 0, ds_width,
ds_height);
m_presenter.SetDisplayTexture(m_downsample_texture.get(), 0, 0, ds_width, ds_height);
}
void GPU_HW::LoadInternalPostProcessing()
{
static constexpr const char* section = PostProcessing::Config::INTERNAL_CHAIN_SECTION;
auto lock = Host::GetSettingsLock();
const SettingsInterface& si = GPUPresenter::GetPostProcessingSettingsInterface(section);
if (PostProcessing::Config::GetStageCount(si, section) == 0 || !PostProcessing::Config::IsEnabled(si, section))
return;
m_internal_postfx = std::make_unique<PostProcessing::Chain>(section);
m_internal_postfx->LoadStages(lock, si, false);
}
void GPU_HW::UpdatePostProcessingSettings(bool force_reload)
{
static constexpr const char* section = PostProcessing::Config::INTERNAL_CHAIN_SECTION;
auto lock = Host::GetSettingsLock();
const SettingsInterface& si = *Host::GetSettingsInterface();
// Don't delete the chain if we're just temporarily disabling.
if (PostProcessing::Config::GetStageCount(si, section) == 0)
{
m_internal_postfx.reset();
}
else
{
if (!m_internal_postfx || force_reload)
{
if (!m_internal_postfx)
m_internal_postfx = std::make_unique<PostProcessing::Chain>(section);
m_internal_postfx->LoadStages(lock, si, true);
}
else
{
m_internal_postfx->UpdateSettings(lock, si);
}
}
}
std::unique_ptr<GPUBackend> GPUBackend::CreateHardwareBackend(GPUPresenter& presenter)

View File

@ -15,6 +15,10 @@
#include <tuple>
#include <utility>
namespace PostProcessing {
class Chain;
}
// TODO: Move to cpp
// TODO: Rename to GPUHWBackend, preserved to avoid conflicts.
class GPU_HW final : public GPUBackend
@ -68,6 +72,7 @@ public:
void FlushRender() override;
bool UpdateSettings(const GPUSettings& old_settings, Error* error) override;
void UpdatePostProcessingSettings(bool force_reload) override;
bool UpdateResolutionScale(Error* error) override;
@ -264,6 +269,8 @@ private:
void DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
void DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 top, u32 width, u32 height);
void LoadInternalPostProcessing();
std::unique_ptr<GPUTexture> m_vram_texture;
std::unique_ptr<GPUTexture> m_vram_depth_texture;
std::unique_ptr<GPUTexture> m_vram_depth_copy_texture;
@ -360,6 +367,7 @@ private:
std::unique_ptr<GPUTexture> m_vram_extract_texture;
std::unique_ptr<GPUTexture> m_vram_extract_depth_texture;
std::unique_ptr<GPUPipeline> m_copy_depth_pipeline;
std::unique_ptr<PostProcessing::Chain> m_internal_postfx;
std::unique_ptr<GPUTexture> m_downsample_texture;
std::unique_ptr<GPUPipeline> m_downsample_pass_pipeline;

View File

@ -24,11 +24,13 @@
#include "util/state_wrapper.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/gsvector_formatter.h"
#include "common/log.h"
#include "common/path.h"
#include "common/ryml_helpers.h"
#include "common/settings_interface.h"
#include "common/small_string.h"
#include "common/string_util.h"
@ -39,10 +41,6 @@
LOG_CHANNEL(GPU);
#include "common/ryml_helpers.h"
static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture::Format::RGBA8;
GPUPresenter::GPUPresenter() = default;
GPUPresenter::~GPUPresenter()
@ -65,6 +63,8 @@ bool GPUPresenter::Initialize(Error* error)
if (!CompileDisplayPipelines(true, true, g_gpu_settings.display_24bit_chroma_smoothing, error))
return false;
LoadPostProcessingSettings(false);
return true;
}
@ -91,20 +91,9 @@ bool GPUPresenter::UpdateSettings(const GPUSettings& old_settings, Error* error)
return true;
}
bool GPUPresenter::UpdatePostProcessingSettings(Error* error)
bool GPUPresenter::IsDisplayPostProcessingActive() const
{
if (LoadOverlaySettings())
{
// something changed, need to recompile pipelines
if (LoadOverlayTexture() && m_border_overlay_alpha_blend &&
(!m_present_copy_blend_pipeline || !m_display_blend_pipeline) &&
!CompileDisplayPipelines(true, false, false, error))
{
return false;
}
}
return true;
return (m_display_postfx && m_display_postfx->IsActive());
}
bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error)
@ -349,8 +338,7 @@ void GPUPresenter::SetDisplayParameters(u16 display_width, u16 display_height, u
m_display_pixel_aspect_ratio = display_pixel_aspect_ratio;
}
void GPUPresenter::SetDisplayTexture(GPUTexture* texture, GPUTexture* depth_buffer, s32 view_x, s32 view_y,
s32 view_width, s32 view_height)
void GPUPresenter::SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height)
{
DebugAssert(texture);
@ -361,7 +349,6 @@ void GPUPresenter::SetDisplayTexture(GPUTexture* texture, GPUTexture* depth_buff
}
m_display_texture = texture;
m_display_depth_buffer = depth_buffer;
m_display_texture_view_x = view_x;
m_display_texture_view_y = view_y;
m_display_texture_view_width = view_width;
@ -408,30 +395,6 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (m_display_texture)
m_display_texture->MakeReadyForSampling();
// Internal post-processing.
GPUTexture* display_texture = m_display_texture;
s32 display_texture_view_x = m_display_texture_view_x;
s32 display_texture_view_y = m_display_texture_view_y;
s32 display_texture_view_width = m_display_texture_view_width;
s32 display_texture_view_height = m_display_texture_view_height;
if (postfx && display_texture && PostProcessing::InternalChain.IsActive() &&
PostProcessing::InternalChain.CheckTargets(DISPLAY_INTERNAL_POSTFX_FORMAT, display_texture_view_width,
display_texture_view_height))
{
// Now we can apply the post chain.
GPUTexture* post_output_texture = PostProcessing::InternalChain.GetOutputTexture();
if (PostProcessing::InternalChain.Apply(display_texture, m_display_depth_buffer, post_output_texture,
GSVector4i(0, 0, display_texture_view_width, display_texture_view_height),
display_texture_view_width, display_texture_view_height, m_display_width,
m_display_height) == GPUDevice::PresentResult::OK)
{
display_texture_view_x = 0;
display_texture_view_y = 0;
display_texture = post_output_texture;
display_texture->MakeReadyForSampling();
}
}
// There's a bunch of scenarios where we need to use intermediate buffers.
// If we have post-processing and overlays enabled, postfx needs to happen on an intermediate buffer first.
// If pre-rotation is enabled with post-processing, we need to draw to an intermediate buffer, and apply the
@ -447,9 +410,8 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
// Postfx active?
const GSVector2i postfx_size = have_overlay ? display_rect.rsize() : target_size;
const bool really_postfx =
(postfx && PostProcessing::DisplayChain.IsActive() &&
PostProcessing::DisplayChain.CheckTargets(m_present_format, postfx_size.x, postfx_size.y));
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");
GL_INS_FMT("Post-processing render target size: {}x{}", postfx_size.x, postfx_size.y);
@ -477,21 +439,20 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
const GSVector4i real_draw_rect = have_overlay ? draw_rect.sub32(display_rect.xyxy()) : draw_rect;
// Display is always drawn to the postfx input.
GPUTexture* const postfx_input = PostProcessing::DisplayChain.GetInputTexture();
GPUTexture* const postfx_input = m_display_postfx->GetInputTexture();
g_gpu_device->ClearRenderTarget(postfx_input, GPUDevice::DEFAULT_CLEAR_COLOR);
g_gpu_device->SetRenderTarget(postfx_input);
if (display_texture)
if (m_display_texture)
{
DrawDisplay(postfx_size, display_texture, display_texture_view_x, display_texture_view_y,
display_texture_view_width, display_texture_view_height, real_draw_rect, false,
g_gpu_settings.display_rotation, WindowInfo::PreRotation::Identity);
DrawDisplay(postfx_size, real_draw_rect, false, g_gpu_settings.display_rotation,
WindowInfo::PreRotation::Identity);
}
postfx_input->MakeReadyForSampling();
// Apply postprocessing to an intermediate texture if we're prerotating or have an overlay.
if (have_prerotation || have_overlay)
{
GPUTexture* const postfx_output = PostProcessing::DisplayChain.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_display_rect);
postfx_output->MakeReadyForSampling();
@ -531,20 +492,17 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
if (have_overlay)
DrawTextureCopy(target_size, overlay_rect, m_border_overlay_texture.get(), false, true, prerotation);
if (display_texture)
if (m_display_texture)
{
DrawDisplay(target_size, display_texture, display_texture_view_x, display_texture_view_y,
display_texture_view_width, display_texture_view_height, display_rect, m_border_overlay_alpha_blend,
g_gpu_settings.display_rotation, prerotation);
DrawDisplay(target_size, display_rect, m_border_overlay_alpha_blend, g_gpu_settings.display_rotation,
prerotation);
}
return GPUDevice::PresentResult::OK;
}
}
void GPUPresenter::DrawDisplay(const GSVector2i target_size, GPUTexture* display_texture, s32 display_texture_view_x,
s32 display_texture_view_y, s32 display_texture_view_width,
s32 display_texture_view_height, const GSVector4i display_rect, bool dst_alpha_blend,
void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
DisplayRotation rotation, WindowInfo::PreRotation prerotation)
{
bool texture_filter_linear = false;
@ -588,22 +546,22 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, GPUTexture* display
g_gpu_device->SetPipeline(dst_alpha_blend ? m_display_blend_pipeline.get() : m_display_pipeline.get());
g_gpu_device->SetTextureSampler(
0, display_texture, texture_filter_linear ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());
0, m_display_texture, texture_filter_linear ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());
// For bilinear, clamp to 0.5/SIZE-0.5 to avoid bleeding from the adjacent texels in VRAM. This is because
// 1.0 in UV space is not the bottom-right texel, but a mix of the bottom-right and wrapped/next texel.
const GSVector2 display_texture_size = GSVector2(display_texture->GetSizeVec());
const GSVector2 display_texture_size = GSVector2(m_display_texture->GetSizeVec());
const GSVector4 display_texture_size4 = GSVector4::xyxy(display_texture_size);
const GSVector4 uv_rect = GSVector4(GSVector4i(display_texture_view_x, display_texture_view_y,
display_texture_view_x + display_texture_view_width,
display_texture_view_y + display_texture_view_height)) /
const GSVector4 uv_rect = GSVector4(GSVector4i(m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_x + m_display_texture_view_width,
m_display_texture_view_y + m_display_texture_view_height)) /
display_texture_size4;
GSVector4::store<true>(uniforms.clamp_rect,
GSVector4(static_cast<float>(display_texture_view_x) + 0.5f,
static_cast<float>(display_texture_view_y) + 0.5f,
static_cast<float>(display_texture_view_x + display_texture_view_width) - 0.5f,
static_cast<float>(display_texture_view_y + display_texture_view_height) - 0.5f) /
display_texture_size4);
GSVector4::store<true>(
uniforms.clamp_rect,
GSVector4(static_cast<float>(m_display_texture_view_x) + 0.5f, static_cast<float>(m_display_texture_view_y) + 0.5f,
static_cast<float>(m_display_texture_view_x + m_display_texture_view_width) - 0.5f,
static_cast<float>(m_display_texture_view_y + m_display_texture_view_height) - 0.5f) /
display_texture_size4);
GSVector4::store<true>(uniforms.src_size,
GSVector4::xyxy(display_texture_size, GSVector2::cxpr(1.0f) / display_texture_size));
@ -682,8 +640,8 @@ GPUDevice::PresentResult GPUPresenter::ApplyDisplayPostProcess(GPUTexture* targe
const s32 orig_width = static_cast<s32>(std::ceil(static_cast<float>(m_display_width) * upscale_x));
const s32 orig_height = static_cast<s32>(std::ceil(static_cast<float>(m_display_height) * upscale_y));
return PostProcessing::DisplayChain.Apply(PostProcessing::DisplayChain.GetInputTexture(), nullptr, target,
display_rect, orig_width, orig_height, m_display_width, m_display_height);
return m_display_postfx->Apply(input, nullptr, target, display_rect, orig_width, orig_height, m_display_width,
m_display_height);
}
void GPUPresenter::DrawTextureCopy(const GSVector2i target_size, const GSVector4i draw_rect, GPUTexture* input,
@ -790,7 +748,7 @@ bool GPUPresenter::Deinterlace(u32 field)
g_gpu_device->Draw(3, 0);
m_deinterlace_texture->MakeReadyForSampling();
SetDisplayTexture(m_deinterlace_texture.get(), m_display_depth_buffer, 0, 0, width, full_height);
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, full_height);
return true;
}
@ -822,7 +780,7 @@ bool GPUPresenter::Deinterlace(u32 field)
g_gpu_device->Draw(3, 0);
m_deinterlace_texture->MakeReadyForSampling();
SetDisplayTexture(m_deinterlace_texture.get(), m_display_depth_buffer, 0, 0, width, height);
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, height);
return true;
}
@ -855,7 +813,7 @@ bool GPUPresenter::Deinterlace(u32 field)
g_gpu_device->Draw(3, 0);
m_deinterlace_texture->MakeReadyForSampling();
SetDisplayTexture(m_deinterlace_texture.get(), m_display_depth_buffer, 0, 0, width, full_height);
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, full_height);
return true;
}
@ -904,7 +862,7 @@ bool GPUPresenter::ApplyChromaSmoothing()
g_gpu_device->Draw(3, 0);
m_chroma_smoothing_texture->MakeReadyForSampling();
SetDisplayTexture(m_chroma_smoothing_texture.get(), m_display_depth_buffer, 0, 0, width, height);
SetDisplayTexture(m_chroma_smoothing_texture.get(), 0, 0, width, height);
return true;
}
@ -1120,6 +1078,139 @@ void GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode, u32* widt
}
}
void GPUPresenter::LoadPostProcessingSettings(bool force_load)
{
static constexpr const char* section = PostProcessing::Config::DISPLAY_CHAIN_SECTION;
auto lock = Host::GetSettingsLock();
const SettingsInterface& si = GetPostProcessingSettingsInterface(section);
// This is the initial load, defer creating the chain until it's actually enabled if disabled.
if (!force_load &&
(!PostProcessing::Config::IsEnabled(si, section) || PostProcessing::Config::GetStageCount(si, section) == 0))
{
return;
}
m_display_postfx = std::make_unique<PostProcessing::Chain>(section);
m_display_postfx->LoadStages(lock, si, true);
}
bool GPUPresenter::UpdatePostProcessingSettings(bool force_reload, Error* error)
{
if (LoadOverlaySettings())
{
// something changed, need to recompile pipelines
if (LoadOverlayTexture() && m_border_overlay_alpha_blend &&
(!m_present_copy_blend_pipeline || !m_display_blend_pipeline) &&
!CompileDisplayPipelines(true, false, false, error))
{
return false;
}
}
// Update postfx settings
{
static constexpr const char* section = PostProcessing::Config::DISPLAY_CHAIN_SECTION;
auto lock = Host::GetSettingsLock();
const SettingsInterface& si = *Host::GetSettingsInterface();
// Don't delete the chain if we're just temporarily disabling.
if (PostProcessing::Config::GetStageCount(si, section) == 0)
{
m_display_postfx.reset();
}
else
{
if (!m_display_postfx || force_reload)
{
if (!m_display_postfx)
m_display_postfx = std::make_unique<PostProcessing::Chain>(section);
m_display_postfx->LoadStages(lock, si, true);
}
else if (!force_reload)
{
m_display_postfx->UpdateSettings(lock, si);
}
}
}
return true;
}
SettingsInterface& GPUPresenter::GetPostProcessingSettingsInterface(const char* section)
{
// If PostProcessing/Enable is set in the game settings interface, use that.
// Otherwise, use the base settings.
SettingsInterface* game_si = Host::Internal::GetGameSettingsLayer();
if (game_si && game_si->ContainsValue(section, "Enabled"))
return *game_si;
else
return *Host::Internal::GetBaseSettingsLayer();
}
void GPUPresenter::TogglePostProcessing()
{
DebugAssert(!GPUThread::IsOnThread());
GPUThread::RunOnBackend(
[](GPUBackend* backend) {
if (!backend)
return;
GPUPresenter& presenter = backend->GetPresenter();
// if it is being lazy loaded, we have to load it here
if (!presenter.m_display_postfx)
{
presenter.LoadPostProcessingSettings(true);
if (presenter.m_display_postfx && presenter.m_display_postfx->IsActive())
return;
}
if (presenter.m_display_postfx)
presenter.m_display_postfx->Toggle();
},
false, true);
}
void GPUPresenter::ReloadPostProcessingSettings(bool display, bool internal, bool reload_shaders)
{
DebugAssert(!GPUThread::IsOnThread());
GPUThread::RunOnBackend(
[display, internal, reload_shaders](GPUBackend* backend) {
if (!backend)
return;
// OSD message first in case any errors occur.
if (reload_shaders)
{
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."),
Host::OSD_QUICK_DURATION);
}
if (display)
{
Error error;
if (!backend->GetPresenter().UpdatePostProcessingSettings(reload_shaders, &error))
{
GPUThread::ReportFatalErrorAndShutdown(fmt::format("Failed to update settings: {}", error.GetDescription()));
return;
}
}
if (internal)
backend->UpdatePostProcessingSettings(reload_shaders);
// trigger represent of frame
if (GPUThread::IsSystemPaused())
GPUThread::Internal::PresentFrameAndRestoreContext();
},
false, true);
}
bool GPUPresenter::LoadOverlaySettings()
{
std::string preset_name = Host::GetStringSettingValue("BorderOverlay", "PresetName");

View File

@ -14,6 +14,7 @@
class Error;
class Image;
class MediaCapture;
class SettingsInterface;
enum class DisplayScreenshotMode : u8;
@ -23,18 +24,16 @@ struct GPUSettings;
struct GPUBackendUpdateDisplayCommand;
struct GPUBackendFramePresentationParameters;
namespace PostProcessing {
class Chain;
}
class ALIGN_TO_CACHE_LINE GPUPresenter final
{
public:
GPUPresenter();
virtual ~GPUPresenter();
/// Main frame presenter - used both when a game is and is not running.
static bool PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bool allow_skip_present, u64 present_time);
/// Returns a list of border overlay presets.
static std::vector<std::string> EnumerateBorderOverlayPresets();
ALWAYS_INLINE s32 GetDisplayWidth() const { return m_display_width; }
ALWAYS_INLINE s32 GetDisplayHeight() const { return m_display_height; }
ALWAYS_INLINE s32 GetDisplayVRAMWidth() const { return m_display_vram_width; }
@ -44,21 +43,20 @@ public:
ALWAYS_INLINE s32 GetDisplayTextureViewWidth() const { return m_display_texture_view_width; }
ALWAYS_INLINE s32 GetDisplayTextureViewHeight() const { return m_display_texture_view_height; }
ALWAYS_INLINE GPUTexture* GetDisplayTexture() const { return m_display_texture; }
ALWAYS_INLINE GPUTexture* GetDisplayDepthBuffer() const { return m_display_depth_buffer; }
ALWAYS_INLINE bool HasDisplayTexture() const { return m_display_texture; }
bool Initialize(Error* error);
bool UpdateSettings(const GPUSettings& old_settings, Error* error);
bool UpdatePostProcessingSettings(Error* error);
bool IsDisplayPostProcessingActive() const;
bool UpdatePostProcessingSettings(bool force_reload, Error* error);
void ClearDisplay();
void ClearDisplayTexture();
void SetDisplayParameters(u16 display_width, u16 display_height, u16 display_origin_left, u16 display_origin_top,
u16 display_vram_width, u16 display_vram_height, float display_pixel_aspect_ratio);
void SetDisplayTexture(GPUTexture* texture, GPUTexture* depth_buffer, s32 view_x, s32 view_y, s32 view_width,
s32 view_height);
void SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height);
bool Deinterlace(u32 field);
bool ApplyChromaSmoothing();
@ -77,6 +75,22 @@ public:
/// Sends the current frame to media capture.
void SendDisplayToMediaCapture(MediaCapture* cap);
/// Main frame presenter - used both when a game is and is not running.
static bool PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bool allow_skip_present, u64 present_time);
/// Returns a list of border overlay presets.
static std::vector<std::string> EnumerateBorderOverlayPresets();
/// Returns the settings interface to use for loading post-processing shader configuration.
/// Assumes the settings lock is being held.
static SettingsInterface& GetPostProcessingSettingsInterface(const char* section);
/// Toggles post-processing. Only callable from the CPU thread.
static void TogglePostProcessing();
/// Reloads post-processing settings. Only callable from the CPU thread.
static void ReloadPostProcessingSettings(bool display, bool internal, bool reload_shaders);
private:
enum : u32
{
@ -94,10 +108,8 @@ private:
GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect,
const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx);
void DrawDisplay(const GSVector2i target_size, GPUTexture* display_texture, s32 display_texture_view_x,
s32 display_texture_view_y, s32 display_texture_view_width, s32 display_texture_view_height,
const GSVector4i display_rect, bool dst_alpha_blend, DisplayRotation rotation,
WindowInfo::PreRotation prerotation);
void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend,
DisplayRotation rotation, WindowInfo::PreRotation prerotation);
GPUDevice::PresentResult ApplyDisplayPostProcess(GPUTexture* target, GPUTexture* input,
const GSVector4i display_rect);
void DrawTextureCopy(const GSVector2i target_size, const GSVector4i draw_rect, GPUTexture* input,
@ -108,6 +120,8 @@ private:
bool DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve);
void DestroyDeinterlaceTextures();
void LoadPostProcessingSettings(bool force_load);
/// Returns true if the image path or alpha blend option has changed.
bool LoadOverlaySettings();
bool LoadOverlayTexture();
@ -132,7 +146,6 @@ private:
std::unique_ptr<GPUPipeline> m_display_pipeline;
GPUTexture* m_display_texture = nullptr;
GPUTexture* m_display_depth_buffer = nullptr;
s32 m_display_texture_view_x = 0;
s32 m_display_texture_view_y = 0;
s32 m_display_texture_view_width = 0;
@ -140,9 +153,12 @@ private:
u32 m_skipped_present_count = 0;
GPUTexture::Format m_present_format = GPUTexture::Format::Unknown;
bool m_border_overlay_alpha_blend = false;
std::unique_ptr<GPUPipeline> m_present_copy_pipeline;
std::unique_ptr<PostProcessing::Chain> m_display_postfx;
// 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;
@ -152,7 +168,6 @@ private:
// Low-traffic variables down here.
std::string m_border_overlay_image_path;
bool m_border_overlay_alpha_blend = false;
};
namespace Host {

View File

@ -411,7 +411,7 @@ void GPU_SW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
{
if (CopyOut(src_x, src_y, skip_x, width, height, line_skip, is_24bit))
{
m_presenter.SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, width, height);
m_presenter.SetDisplayTexture(m_upload_texture.get(), 0, 0, width, height);
if (is_24bit && g_settings.display_24bit_chroma_smoothing)
{
if (m_presenter.ApplyChromaSmoothing())
@ -427,7 +427,7 @@ void GPU_SW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
{
if (CopyOut(src_x, src_y, skip_x, width, height, 0, is_24bit))
{
m_presenter.SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, width, height);
m_presenter.SetDisplayTexture(m_upload_texture.get(), 0, 0, width, height);
if (is_24bit && g_settings.display_24bit_chroma_smoothing)
m_presenter.ApplyChromaSmoothing();
}
@ -436,7 +436,7 @@ void GPU_SW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
else
{
if (CopyOut(0, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false))
m_presenter.SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
m_presenter.SetDisplayTexture(m_upload_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
}
}

View File

@ -81,7 +81,6 @@ static void ReconfigureOnThread(GPUThreadReconfigureCommand* cmd);
static bool CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram, Error* error);
static void DestroyGPUBackendOnThread();
static void DestroyGPUPresenterOnThread();
static bool PresentFrameAndRestoreContext();
static void UpdateSettingsOnThread(const GPUSettings& old_settings);
@ -756,6 +755,8 @@ bool GPUThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram,
s_state.gpu_presenter.reset();
return false;
}
ImGuiManager::UpdateDebugWindowConfig();
}
const bool is_hardware = (renderer != GPURenderer::Software);
@ -793,8 +794,6 @@ bool GPUThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram,
}
g_gpu_device->SetGPUTimingEnabled(g_gpu_settings.display_show_gpu_usage);
PostProcessing::Initialize();
ImGuiManager::UpdateDebugWindowConfig();
s_state.gpu_backend->RestoreDeviceContext();
SetRunIdleReason(RunIdleReason::NoGPUBackend, false);
std::atomic_thread_fence(std::memory_order_release);
@ -919,9 +918,6 @@ void GPUThread::DestroyGPUBackendOnThread()
SetRunIdleReason(RunIdleReason::NoGPUBackend, true);
ImGuiManager::DestroyAllDebugWindows();
ImGuiManager::DestroyOverlayTextures();
PostProcessing::Shutdown();
s_state.gpu_backend.reset();
}
@ -932,6 +928,9 @@ void GPUThread::DestroyGPUPresenterOnThread()
VERBOSE_LOG("Shutting down GPU presenter...");
ImGuiManager::DestroyAllDebugWindows();
ImGuiManager::DestroyOverlayTextures();
// Should have no queued frames by this point. Backend can get replaced with null.
Assert(!s_state.gpu_backend);
Assert(GPUBackend::GetQueuedFrameCount() == 0);
@ -939,8 +938,10 @@ void GPUThread::DestroyGPUPresenterOnThread()
s_state.gpu_presenter.reset();
}
bool GPUThread::PresentFrameAndRestoreContext()
bool GPUThread::Internal::PresentFrameAndRestoreContext()
{
DebugAssert(IsOnThread());
if (s_state.gpu_backend)
s_state.gpu_backend->FlushRender();
@ -970,19 +971,16 @@ void GPUThread::UpdateSettingsOnThread(const GPUSettings& old_settings)
if (g_gpu_settings.display_show_gpu_usage != old_settings.display_show_gpu_usage)
g_gpu_device->SetGPUTimingEnabled(g_gpu_settings.display_show_gpu_usage);
PostProcessing::UpdateSettings();
Error error;
if (!s_state.gpu_presenter->UpdatePostProcessingSettings(&error) ||
!s_state.gpu_presenter->UpdateSettings(old_settings, &error) ||
if (!s_state.gpu_presenter->UpdateSettings(old_settings, &error) ||
!s_state.gpu_backend->UpdateSettings(old_settings, &error)) [[unlikely]]
{
ReportFatalErrorAndShutdown(fmt::format("Failed to update settings: {}", error.GetDescription()));
return;
}
if (ImGuiManager::UpdateDebugWindowConfig() || (PostProcessing::DisplayChain.IsActive() && !IsSystemPaused()))
PresentFrameAndRestoreContext();
if (ImGuiManager::UpdateDebugWindowConfig())
Internal::PresentFrameAndRestoreContext();
else
s_state.gpu_backend->RestoreDeviceContext();
}
@ -1071,17 +1069,8 @@ void GPUThread::UpdateSettings(bool gpu_settings_changed, bool device_settings_c
RunOnThread([]() {
if (s_state.gpu_backend)
{
PostProcessing::UpdateSettings();
Error error;
if (!s_state.gpu_presenter->UpdatePostProcessingSettings(&error))
{
ReportFatalErrorAndShutdown(fmt::format("Failed to update settings: {}", error.GetDescription()));
return;
}
if (ImGuiManager::UpdateDebugWindowConfig() || (PostProcessing::DisplayChain.IsActive() && !IsSystemPaused()))
PresentFrameAndRestoreContext();
if (ImGuiManager::UpdateDebugWindowConfig())
Internal::PresentFrameAndRestoreContext();
}
});
}
@ -1219,8 +1208,8 @@ void GPUThread::DisplayWindowResizedOnThread()
{
// Hackity hack, on some systems, presenting a single frame isn't enough to actually get it
// displayed. Two seems to be good enough. Maybe something to do with direct scanout.
PresentFrameAndRestoreContext();
PresentFrameAndRestoreContext();
Internal::PresentFrameAndRestoreContext();
Internal::PresentFrameAndRestoreContext();
}
if (g_gpu_settings.gpu_resolution_scale == 0)
@ -1276,7 +1265,7 @@ void GPUThread::PresentCurrentFrame()
// But we shouldn't be not running idle without a GPU backend.
if (s_state.gpu_backend)
PresentFrameAndRestoreContext();
Internal::PresentFrameAndRestoreContext();
});
}

View File

@ -99,6 +99,7 @@ void SetThreadEnabled(bool enabled);
void DoRunIdle();
void RequestShutdown();
void GPUThreadEntryPoint();
bool PresentFrameAndRestoreContext();
} // namespace Internal
} // namespace GPUThread

View File

@ -8,6 +8,7 @@
#include "fullscreen_ui.h"
#include "gpu.h"
#include "gpu_hw_texture_cache.h"
#include "gpu_presenter.h"
#include "gpu_thread.h"
#include "gte.h"
#include "host.h"
@ -415,24 +416,13 @@ DEFINE_HOTKEY("DecreaseResolutionScale", TRANSLATE_NOOP("Hotkeys", "Graphics"),
DEFINE_HOTKEY("TogglePostProcessing", TRANSLATE_NOOP("Hotkeys", "Graphics"),
TRANSLATE_NOOP("Hotkeys", "Toggle Post-Processing"), [](s32 pressed) {
if (!pressed && System::IsValid())
PostProcessing::DisplayChain.Toggle();
})
DEFINE_HOTKEY("ToggleInternalPostProcessing", TRANSLATE_NOOP("Hotkeys", "Graphics"),
TRANSLATE_NOOP("Hotkeys", "Toggle Internal Post-Processing"), [](s32 pressed) {
if (!pressed && System::IsValid())
PostProcessing::InternalChain.Toggle();
GPUPresenter::TogglePostProcessing();
})
DEFINE_HOTKEY("ReloadPostProcessingShaders", TRANSLATE_NOOP("Hotkeys", "Graphics"),
TRANSLATE_NOOP("Hotkeys", "Reload Post Processing Shaders"), [](s32 pressed) {
if (!pressed && System::IsValid())
{
GPUThread::RunOnThread([]() {
if (GPUThread::HasGPUBackend())
PostProcessing::ReloadShaders();
});
}
GPUPresenter::ReloadPostProcessingSettings(true, true, true);
})
DEFINE_HOTKEY("ReloadTextureReplacements", TRANSLATE_NOOP("Hotkeys", "Graphics"),

View File

@ -41,6 +41,8 @@ PostProcessingChainConfigWidget::PostProcessingChainConfigWidget(SettingsWindow*
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enablePostProcessing, section, "Enabled", false);
connect(m_ui.enablePostProcessing, &QCheckBox::checkStateChanged, this,
&PostProcessingChainConfigWidget::triggerSettingsReload);
updateList();
updateButtonsAndConfigPane(std::nullopt);
@ -57,14 +59,17 @@ SettingsInterface& PostProcessingChainConfigWidget::getSettingsInterfaceToUpdate
void PostProcessingChainConfigWidget::commitSettingsUpdate()
{
if (m_dialog->isPerGameSettings())
{
m_dialog->saveAndReloadGameSettings();
}
else
{
Host::CommitBaseSettingChanges();
g_emu_thread->updatePostProcessingSettings();
}
triggerSettingsReload();
}
void PostProcessingChainConfigWidget::triggerSettingsReload()
{
g_emu_thread->updatePostProcessingSettings(m_section == PostProcessing::Config::DISPLAY_CHAIN_SECTION,
m_section == PostProcessing::Config::INTERNAL_CHAIN_SECTION, false);
}
void PostProcessingChainConfigWidget::connectUi()
@ -508,8 +513,17 @@ PostProcessingOverlayConfigWidget::PostProcessingOverlayConfigWidget(SettingsWin
connect(m_ui.overlayName, &QComboBox::currentIndexChanged, this,
&PostProcessingOverlayConfigWidget::onOverlayNameCurrentIndexChanged);
connect(m_ui.overlayName, &QComboBox::currentIndexChanged, this,
&PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.imagePathBrowse, &QPushButton::clicked, this,
&PostProcessingOverlayConfigWidget::onImagePathBrowseClicked);
connect(m_ui.imagePath, &QLineEdit::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.displayStartX, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.displayStartY, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.displayEndX, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.displayEndY, &QSpinBox::textChanged, this, &PostProcessingOverlayConfigWidget::triggerSettingsReload);
connect(m_ui.alphaBlend, &QCheckBox::checkStateChanged, this,
&PostProcessingOverlayConfigWidget::triggerSettingsReload);
onOverlayNameCurrentIndexChanged(m_ui.overlayName->currentIndex());
}
@ -532,3 +546,8 @@ void PostProcessingOverlayConfigWidget::onImagePathBrowseClicked()
m_ui.imagePath->setText(QDir::toNativeSeparators(path));
}
void PostProcessingOverlayConfigWidget::triggerSettingsReload()
{
g_emu_thread->updatePostProcessingSettings(true, false, false);
}

View File

@ -41,6 +41,7 @@ private Q_SLOTS:
void onMoveDownButtonClicked();
void onReloadButtonClicked();
void onSelectedShaderChanged();
void triggerSettingsReload();
private:
SettingsInterface& getSettingsInterfaceToUpdate();
@ -99,6 +100,7 @@ public:
private Q_SLOTS:
void onOverlayNameCurrentIndexChanged(int index);
void onImagePathBrowseClicked();
void triggerSettingsReload();
private:
Ui::PostProcessingOverlayConfigWidget m_ui;

View File

@ -21,6 +21,7 @@
#include "core/gpu.h"
#include "core/gpu_backend.h"
#include "core/gpu_hw_texture_cache.h"
#include "core/gpu_presenter.h"
#include "core/gpu_thread.h"
#include "core/host.h"
#include "core/imgui_overlays.h"
@ -1275,29 +1276,20 @@ void EmuThread::reloadPostProcessingShaders()
}
if (System::IsValid())
{
GPUThread::RunOnThread([]() {
if (GPUThread::HasGPUBackend())
PostProcessing::ReloadShaders();
});
}
GPUPresenter::ReloadPostProcessingSettings(true, true, true);
}
void EmuThread::updatePostProcessingSettings()
void EmuThread::updatePostProcessingSettings(bool display, bool internal, bool force_reload)
{
if (!isCurrentThread())
{
QMetaObject::invokeMethod(this, "updatePostProcessingSettings", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "updatePostProcessingSettings", Qt::QueuedConnection, Q_ARG(bool, display),
Q_ARG(bool, internal), Q_ARG(bool, force_reload));
return;
}
if (System::IsValid())
{
GPUThread::RunOnThread([]() {
if (GPUThread::HasGPUBackend())
PostProcessing::UpdateSettings();
});
}
GPUPresenter::ReloadPostProcessingSettings(display, internal, force_reload);
}
void EmuThread::clearInputBindStateFromSource(InputBindingKey key)

View File

@ -206,7 +206,7 @@ public Q_SLOTS:
void requestDisplaySize(float scale);
void applyCheat(const QString& name);
void reloadPostProcessingShaders();
void updatePostProcessingSettings();
void updatePostProcessingSettings(bool display, bool internal, bool force_reload);
void clearInputBindStateFromSource(InputBindingKey key);
void reloadTextureReplacements();
void captureGPUFrameDump();

View File

@ -40,17 +40,6 @@ static TinyString GetStageConfigSection(const char* section, u32 index);
static void CopyStageConfig(SettingsInterface& si, const char* section, u32 old_index, u32 new_index);
static void SwapStageConfig(SettingsInterface& si, const char* section, u32 lhs_index, u32 rhs_index);
static std::unique_ptr<Shader> TryLoadingShader(const std::string& shader_name, bool only_config, Error* error);
static SettingsInterface& GetLoadSettingsInterface(const char* section);
template<typename T>
ALWAYS_INLINE void ForAllChains(const T& F)
{
F(DisplayChain);
F(InternalChain);
}
Chain DisplayChain(Config::DISPLAY_CHAIN_SECTION);
Chain InternalChain(Config::INTERNAL_CHAIN_SECTION);
Timer::Value Chain::s_start_time;
@ -241,6 +230,11 @@ void PostProcessing::SwapStageConfig(SettingsInterface& si, const char* section,
si.SetStringValue(rhs_section, key.c_str(), value.c_str());
}
bool PostProcessing::Config::IsEnabled(const SettingsInterface& si, const char* section)
{
return si.GetBoolValue(section, "Enabled", false);
}
u32 PostProcessing::Config::GetStageCount(const SettingsInterface& si, const char* section)
{
return si.GetUIntValue(section, "StageCount", 0u);
@ -389,11 +383,6 @@ bool PostProcessing::Chain::IsActive() const
return m_enabled && !m_stages.empty();
}
bool PostProcessing::Chain::IsInternalChain() const
{
return (this == &InternalChain);
}
void PostProcessing::Chain::ClearStagesWithError(const Error& error)
{
std::string msg = error.GetDescription();
@ -402,13 +391,15 @@ void PostProcessing::Chain::ClearStagesWithError(const Error& error)
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to load post-processing chain: {}"),
msg.empty() ? TRANSLATE_SV("PostProcessing", "Unknown Error") : std::string_view(msg)),
Host::OSD_ERROR_DURATION);
DestroyTextures();
m_stages.clear();
}
void PostProcessing::Chain::LoadStages()
void PostProcessing::Chain::LoadStages(std::unique_lock<std::mutex>& settings_lock, const SettingsInterface& si,
bool preload_swap_chain_size)
{
auto lock = Host::GetSettingsLock();
SettingsInterface& si = GetLoadSettingsInterface(m_section);
m_stages.clear();
DestroyTextures();
m_enabled = si.GetBoolValue(m_section, "Enabled", false);
m_wants_depth_buffer = false;
@ -431,7 +422,7 @@ void PostProcessing::Chain::LoadStages()
return;
}
lock.unlock();
settings_lock.unlock();
progress.FormatStatusText("Loading shader {}...", stage_name);
std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error);
@ -441,7 +432,7 @@ void PostProcessing::Chain::LoadStages()
return;
}
lock.lock();
settings_lock.lock();
shader->LoadOptions(si, GetStageConfigSection(m_section, i));
m_stages.push_back(std::move(shader));
@ -452,7 +443,7 @@ void PostProcessing::Chain::LoadStages()
DEV_LOG("Loaded {} post-processing stages.", stage_count);
// precompile shaders
if (!IsInternalChain() && g_gpu_device && g_gpu_device->HasMainSwapChain())
if (preload_swap_chain_size && g_gpu_device && g_gpu_device->HasMainSwapChain())
{
CheckTargets(g_gpu_device->GetMainSwapChain()->GetFormat(), g_gpu_device->GetMainSwapChain()->GetWidth(),
g_gpu_device->GetMainSwapChain()->GetHeight(), &progress);
@ -466,15 +457,8 @@ void PostProcessing::Chain::LoadStages()
DEV_LOG("Depth buffer is needed.");
}
void PostProcessing::Chain::ClearStages()
void PostProcessing::Chain::UpdateSettings(std::unique_lock<std::mutex>& settings_lock, const SettingsInterface& si)
{
decltype(m_stages)().swap(m_stages);
}
void PostProcessing::Chain::UpdateSettings(std::unique_lock<std::mutex>& settings_lock)
{
SettingsInterface& si = GetLoadSettingsInterface(m_section);
m_enabled = si.GetBoolValue(m_section, "Enabled", false);
const u32 stage_count = Config::GetStageCount(si, m_section);
@ -674,47 +658,6 @@ GPUDevice::PresentResult PostProcessing::Chain::Apply(GPUTexture* input_color, G
return GPUDevice::PresentResult::OK;
}
void PostProcessing::Initialize()
{
DisplayChain.LoadStages();
InternalChain.LoadStages();
}
void PostProcessing::UpdateSettings()
{
auto lock = Host::GetSettingsLock();
ForAllChains([&lock](Chain& chain) { chain.UpdateSettings(lock); });
}
void PostProcessing::Shutdown()
{
ForAllChains([](Chain& chain) {
chain.ClearStages();
chain.DestroyTextures();
});
}
bool PostProcessing::ReloadShaders()
{
if (!DisplayChain.HasStages() && !InternalChain.HasStages())
{
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."),
Host::OSD_QUICK_DURATION);
return false;
}
ForAllChains([](Chain& chain) {
chain.ClearStages();
chain.DestroyTextures();
chain.LoadStages();
});
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), Host::OSD_QUICK_DURATION);
return true;
}
std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name,
bool only_config, Error* error)
{
@ -784,15 +727,3 @@ std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const s
Error::SetStringFmt(error, "Failed to locate shader '{}'", shader_name);
return {};
}
SettingsInterface& PostProcessing::GetLoadSettingsInterface(const char* section)
{
// If PostProcessing/Enable is set in the game settings interface, use that.
// Otherwise, use the base settings.
SettingsInterface* game_si = Host::Internal::GetGameSettingsLayer();
if (game_si && game_si->ContainsValue(section, "Enabled"))
return *game_si;
else
return *Host::Internal::GetBaseSettingsLayer();
}

View File

@ -93,6 +93,7 @@ namespace Config {
static constexpr const char* DISPLAY_CHAIN_SECTION = "PostProcessing";
static constexpr const char* INTERNAL_CHAIN_SECTION = "InternalPostProcessing";
bool IsEnabled(const SettingsInterface& si, const char* section);
u32 GetStageCount(const SettingsInterface& si, const char* section);
std::string GetStageShaderName(const SettingsInterface& si, const char* section, u32 index);
std::vector<ShaderOption> GetStageOptions(const SettingsInterface& si, const char* section, u32 index);
@ -122,13 +123,11 @@ public:
GPUTexture* GetTextureUnusedAtEndOfChain() const;
bool IsActive() const;
bool IsInternalChain() const;
void UpdateSettings(std::unique_lock<std::mutex>& settings_lock);
void UpdateSettings(std::unique_lock<std::mutex>& settings_lock, const SettingsInterface& si);
void LoadStages();
void ClearStages();
void DestroyTextures();
void LoadStages(std::unique_lock<std::mutex>& settings_lock, const SettingsInterface& si,
bool preload_swap_chain_size);
/// Temporarily toggles post-processing on/off.
void Toggle();
@ -142,6 +141,7 @@ public:
private:
void ClearStagesWithError(const Error& error);
void DestroyTextures();
const char* m_section;
@ -162,17 +162,4 @@ private:
// [display_name, filename]
std::vector<std::pair<std::string, std::string>> GetAvailableShaderNames();
void Initialize();
/// Reloads configuration.
void UpdateSettings();
/// Reloads post processing shaders with the current configuration.
bool ReloadShaders();
void Shutdown();
extern Chain DisplayChain;
extern Chain InternalChain;
}; // namespace PostProcessing