diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index c68df1d26..3c9052b17 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -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(); } } diff --git a/src/core/gpu_backend.cpp b/src/core/gpu_backend.cpp index c61bc5027..58d0f9e53 100644 --- a/src/core/gpu_backend.cpp +++ b/src/core/gpu_backend.cpp @@ -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( diff --git a/src/core/gpu_backend.h b/src/core/gpu_backend.h index 511e06594..e55edea31 100644 --- a/src/core/gpu_backend.h +++ b/src/core/gpu_backend.h @@ -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; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 521a551c8..163629875 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -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(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(section); + m_internal_postfx->LoadStages(lock, si, true); + } + else + { + m_internal_postfx->UpdateSettings(lock, si); + } + } } std::unique_ptr GPUBackend::CreateHardwareBackend(GPUPresenter& presenter) diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index f696c1a67..8d94bf6d1 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -15,6 +15,10 @@ #include #include +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 m_vram_texture; std::unique_ptr m_vram_depth_texture; std::unique_ptr m_vram_depth_copy_texture; @@ -360,6 +367,7 @@ private: std::unique_ptr m_vram_extract_texture; std::unique_ptr m_vram_extract_depth_texture; std::unique_ptr m_copy_depth_pipeline; + std::unique_ptr m_internal_postfx; std::unique_ptr m_downsample_texture; std::unique_ptr m_downsample_pass_pipeline; diff --git a/src/core/gpu_presenter.cpp b/src/core/gpu_presenter.cpp index dcd0d0ea9..cd6ee2626 100644 --- a/src/core/gpu_presenter.cpp +++ b/src/core/gpu_presenter.cpp @@ -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(uniforms.clamp_rect, - GSVector4(static_cast(display_texture_view_x) + 0.5f, - static_cast(display_texture_view_y) + 0.5f, - static_cast(display_texture_view_x + display_texture_view_width) - 0.5f, - static_cast(display_texture_view_y + display_texture_view_height) - 0.5f) / - display_texture_size4); + GSVector4::store( + uniforms.clamp_rect, + GSVector4(static_cast(m_display_texture_view_x) + 0.5f, static_cast(m_display_texture_view_y) + 0.5f, + static_cast(m_display_texture_view_x + m_display_texture_view_width) - 0.5f, + static_cast(m_display_texture_view_y + m_display_texture_view_height) - 0.5f) / + display_texture_size4); GSVector4::store(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(std::ceil(static_cast(m_display_width) * upscale_x)); const s32 orig_height = static_cast(std::ceil(static_cast(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(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(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"); diff --git a/src/core/gpu_presenter.h b/src/core/gpu_presenter.h index d1185cc71..8acc519fe 100644 --- a/src/core/gpu_presenter.h +++ b/src/core/gpu_presenter.h @@ -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 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 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 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 m_present_copy_pipeline; + std::unique_ptr m_display_postfx; + // blended variants of pipelines, used when overlays are enabled std::unique_ptr m_display_blend_pipeline; std::unique_ptr 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 { diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 3e913446f..b666118fe 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -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); } } diff --git a/src/core/gpu_thread.cpp b/src/core/gpu_thread.cpp index 399a290c7..f40d15cac 100644 --- a/src/core/gpu_thread.cpp +++ b/src/core/gpu_thread.cpp @@ -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(); }); } diff --git a/src/core/gpu_thread.h b/src/core/gpu_thread.h index 73f349a22..9914adfca 100644 --- a/src/core/gpu_thread.h +++ b/src/core/gpu_thread.h @@ -99,6 +99,7 @@ void SetThreadEnabled(bool enabled); void DoRunIdle(); void RequestShutdown(); void GPUThreadEntryPoint(); +bool PresentFrameAndRestoreContext(); } // namespace Internal } // namespace GPUThread diff --git a/src/core/hotkeys.cpp b/src/core/hotkeys.cpp index 7d60ea83b..206eab875 100644 --- a/src/core/hotkeys.cpp +++ b/src/core/hotkeys.cpp @@ -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"), diff --git a/src/duckstation-qt/postprocessingsettingswidget.cpp b/src/duckstation-qt/postprocessingsettingswidget.cpp index 94bfe3f85..daa4d63a1 100644 --- a/src/duckstation-qt/postprocessingsettingswidget.cpp +++ b/src/duckstation-qt/postprocessingsettingswidget.cpp @@ -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); +} diff --git a/src/duckstation-qt/postprocessingsettingswidget.h b/src/duckstation-qt/postprocessingsettingswidget.h index 30c6d8d3c..c697c16c9 100644 --- a/src/duckstation-qt/postprocessingsettingswidget.h +++ b/src/duckstation-qt/postprocessingsettingswidget.h @@ -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; diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 6420744a1..df6a9769d 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -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) diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 8503e43fe..0b52a85bf 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -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(); diff --git a/src/util/postprocessing.cpp b/src/util/postprocessing.cpp index f80033e4d..9c78f9ccb 100644 --- a/src/util/postprocessing.cpp +++ b/src/util/postprocessing.cpp @@ -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 TryLoadingShader(const std::string& shader_name, bool only_config, Error* error); -static SettingsInterface& GetLoadSettingsInterface(const char* section); - -template -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& 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 = 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& settings_lock, const SettingsInterface& si) { - decltype(m_stages)().swap(m_stages); -} - -void PostProcessing::Chain::UpdateSettings(std::unique_lock& 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::TryLoadingShader(const std::string& shader_name, bool only_config, Error* error) { @@ -784,15 +727,3 @@ std::unique_ptr 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(); -} diff --git a/src/util/postprocessing.h b/src/util/postprocessing.h index 48e615630..4364b8b42 100644 --- a/src/util/postprocessing.h +++ b/src/util/postprocessing.h @@ -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 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& settings_lock); + void UpdateSettings(std::unique_lock& settings_lock, const SettingsInterface& si); - void LoadStages(); - void ClearStages(); - void DestroyTextures(); + void LoadStages(std::unique_lock& 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> 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