mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-29 17:15:40 +00:00
PostProcessing: Split into internal and display chains
This commit is contained in:
parent
810ce1ce57
commit
0c3cf1f5f8
|
@ -390,7 +390,7 @@ static void DrawFolderSetting(SettingsInterface* bsi, const char* title, const c
|
||||||
|
|
||||||
static void PopulateGraphicsAdapterList();
|
static void PopulateGraphicsAdapterList();
|
||||||
static void PopulateGameListDirectoryCache(SettingsInterface* si);
|
static void PopulateGameListDirectoryCache(SettingsInterface* si);
|
||||||
static void PopulatePostProcessingChain(SettingsInterface* si);
|
static void PopulatePostProcessingChain(SettingsInterface* si, const char* section);
|
||||||
static void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, std::string_view section,
|
static void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, std::string_view section,
|
||||||
std::string_view key, std::string_view display_name);
|
std::string_view key, std::string_view display_name);
|
||||||
static void DrawInputBindingWindow();
|
static void DrawInputBindingWindow();
|
||||||
|
@ -2733,7 +2733,7 @@ void FullscreenUI::SwitchToSettings()
|
||||||
s_game_settings_interface.reset();
|
s_game_settings_interface.reset();
|
||||||
|
|
||||||
PopulateGraphicsAdapterList();
|
PopulateGraphicsAdapterList();
|
||||||
PopulatePostProcessingChain(GetEditingSettingsInterface());
|
PopulatePostProcessingChain(GetEditingSettingsInterface(), PostProcessing::Config::DISPLAY_CHAIN_SECTION);
|
||||||
|
|
||||||
s_current_main_window = MainWindowType::Settings;
|
s_current_main_window = MainWindowType::Settings;
|
||||||
s_settings_page = SettingsPage::Interface;
|
s_settings_page = SettingsPage::Interface;
|
||||||
|
@ -4489,16 +4489,16 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
||||||
EndMenuButtons();
|
EndMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::PopulatePostProcessingChain(SettingsInterface* si)
|
void FullscreenUI::PopulatePostProcessingChain(SettingsInterface* si, const char* section)
|
||||||
{
|
{
|
||||||
const u32 stages = PostProcessing::Config::GetStageCount(*si);
|
const u32 stages = PostProcessing::Config::GetStageCount(*si, section);
|
||||||
s_postprocessing_stages.clear();
|
s_postprocessing_stages.clear();
|
||||||
s_postprocessing_stages.reserve(stages);
|
s_postprocessing_stages.reserve(stages);
|
||||||
for (u32 i = 0; i < stages; i++)
|
for (u32 i = 0; i < stages; i++)
|
||||||
{
|
{
|
||||||
PostProcessingStageInfo psi;
|
PostProcessingStageInfo psi;
|
||||||
psi.name = PostProcessing::Config::GetStageShaderName(*si, i);
|
psi.name = PostProcessing::Config::GetStageShaderName(*si, section, i);
|
||||||
psi.options = PostProcessing::Config::GetStageOptions(*si, i);
|
psi.options = PostProcessing::Config::GetStageOptions(*si, section, i);
|
||||||
s_postprocessing_stages.push_back(std::move(psi));
|
s_postprocessing_stages.push_back(std::move(psi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4514,6 +4514,7 @@ enum
|
||||||
void FullscreenUI::DrawPostProcessingSettingsPage()
|
void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
{
|
{
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
static constexpr const char* section = PostProcessing::Config::DISPLAY_CHAIN_SECTION;
|
||||||
|
|
||||||
BeginMenuButtons();
|
BeginMenuButtons();
|
||||||
|
|
||||||
|
@ -4549,11 +4550,11 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
const std::string& shader_name = shaders[index].second;
|
const std::string& shader_name = shaders[index].second;
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
Error error;
|
Error error;
|
||||||
if (PostProcessing::Config::AddStage(*bsi, shader_name, &error))
|
if (PostProcessing::Config::AddStage(*bsi, section, shader_name, &error))
|
||||||
{
|
{
|
||||||
ShowToast(std::string(), fmt::format(FSUI_FSTR("Shader {} added as stage {}."), title,
|
ShowToast(std::string(), fmt::format(FSUI_FSTR("Shader {} added as stage {}."), title,
|
||||||
PostProcessing::Config::GetStageCount(*bsi)));
|
PostProcessing::Config::GetStageCount(*bsi, section)));
|
||||||
PopulatePostProcessingChain(bsi);
|
PopulatePostProcessingChain(bsi, section);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4577,8 +4578,8 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
PostProcessing::Config::ClearStages(*bsi);
|
PostProcessing::Config::ClearStages(*bsi, section);
|
||||||
PopulatePostProcessingChain(bsi);
|
PopulatePostProcessingChain(bsi, section);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
ShowToast(std::string(), FSUI_STR("Post-processing chain cleared."));
|
ShowToast(std::string(), FSUI_STR("Post-processing chain cleared."));
|
||||||
});
|
});
|
||||||
|
@ -4635,7 +4636,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
&value))
|
&value))
|
||||||
{
|
{
|
||||||
opt.value[0].int_value = (value != 0);
|
opt.value[0].int_value = (value != 0);
|
||||||
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4719,7 +4720,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4817,7 +4818,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
PostProcessing::Config::SetStageOption(*bsi, section, stage_index, opt);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4853,22 +4854,22 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
const PostProcessingStageInfo& si = s_postprocessing_stages[postprocessing_action_index];
|
const PostProcessingStageInfo& si = s_postprocessing_stages[postprocessing_action_index];
|
||||||
ShowToast(std::string(),
|
ShowToast(std::string(),
|
||||||
fmt::format(FSUI_FSTR("Removed stage {} ({})."), postprocessing_action_index + 1, si.name));
|
fmt::format(FSUI_FSTR("Removed stage {} ({})."), postprocessing_action_index + 1, si.name));
|
||||||
PostProcessing::Config::RemoveStage(*bsi, postprocessing_action_index);
|
PostProcessing::Config::RemoveStage(*bsi, section, postprocessing_action_index);
|
||||||
PopulatePostProcessingChain(bsi);
|
PopulatePostProcessingChain(bsi, section);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POSTPROCESSING_ACTION_MOVE_UP:
|
case POSTPROCESSING_ACTION_MOVE_UP:
|
||||||
{
|
{
|
||||||
PostProcessing::Config::MoveStageUp(*bsi, postprocessing_action_index);
|
PostProcessing::Config::MoveStageUp(*bsi, section, postprocessing_action_index);
|
||||||
PopulatePostProcessingChain(bsi);
|
PopulatePostProcessingChain(bsi, section);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POSTPROCESSING_ACTION_MOVE_DOWN:
|
case POSTPROCESSING_ACTION_MOVE_DOWN:
|
||||||
{
|
{
|
||||||
PostProcessing::Config::MoveStageDown(*bsi, postprocessing_action_index);
|
PostProcessing::Config::MoveStageDown(*bsi, section, postprocessing_action_index);
|
||||||
PopulatePostProcessingChain(bsi);
|
PopulatePostProcessingChain(bsi, section);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -56,6 +56,8 @@ static u64 s_active_gpu_cycles = 0;
|
||||||
static u32 s_active_gpu_cycles_frames = 0;
|
static u32 s_active_gpu_cycles_frames = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr GPUTexture::Format DISPLAY_INTERNAL_POSTFX_FORMAT = GPUTexture::Format::RGBA8;
|
||||||
|
|
||||||
GPU::GPU()
|
GPU::GPU()
|
||||||
{
|
{
|
||||||
ResetStatistics();
|
ResetStatistics();
|
||||||
|
@ -1932,24 +1934,53 @@ bool GPU::PresentDisplay()
|
||||||
|
|
||||||
bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_rect, bool postfx)
|
bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_rect, bool postfx)
|
||||||
{
|
{
|
||||||
GL_SCOPE_FMT("RenderDisplay: {}x{} at {},{}", draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
|
GL_SCOPE_FMT("RenderDisplay: {}x{} at {},{}", draw_rect.GetWidth(), draw_rect.GetHeight(), draw_rect.left,
|
||||||
draw_rect.GetHeight());
|
draw_rect.top);
|
||||||
|
|
||||||
if (m_display_texture)
|
if (m_display_texture)
|
||||||
m_display_texture->MakeReadyForSampling();
|
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))
|
||||||
|
{
|
||||||
|
DebugAssert(display_texture_view_x == 0 && display_texture_view_y == 0 &&
|
||||||
|
static_cast<s32>(display_texture->GetWidth()) == display_texture_view_width &&
|
||||||
|
static_cast<s32>(display_texture->GetHeight()) == display_texture_view_height);
|
||||||
|
|
||||||
|
// Now we can apply the post chain.
|
||||||
|
GPUTexture* post_output_texture = PostProcessing::InternalChain.GetOutputTexture();
|
||||||
|
if (PostProcessing::InternalChain.Apply(display_texture, post_output_texture, 0, 0, display_texture_view_width,
|
||||||
|
display_texture_view_height, display_texture_view_width,
|
||||||
|
display_texture_view_height, m_crtc_state.display_width,
|
||||||
|
m_crtc_state.display_height))
|
||||||
|
{
|
||||||
|
display_texture_view_x = 0;
|
||||||
|
display_texture_view_y = 0;
|
||||||
|
display_texture = post_output_texture;
|
||||||
|
display_texture->MakeReadyForSampling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const GPUTexture::Format hdformat = target ? target->GetFormat() : g_gpu_device->GetWindowFormat();
|
const GPUTexture::Format hdformat = target ? target->GetFormat() : g_gpu_device->GetWindowFormat();
|
||||||
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetWindowWidth();
|
const u32 target_width = target ? target->GetWidth() : g_gpu_device->GetWindowWidth();
|
||||||
const u32 target_height = target ? target->GetHeight() : g_gpu_device->GetWindowHeight();
|
const u32 target_height = target ? target->GetHeight() : g_gpu_device->GetWindowHeight();
|
||||||
const bool really_postfx = (postfx && PostProcessing::IsActive() && !g_gpu_device->GetWindowInfo().IsSurfaceless() &&
|
const bool really_postfx =
|
||||||
hdformat != GPUTexture::Format::Unknown && target_width > 0 && target_height > 0 &&
|
(postfx && PostProcessing::DisplayChain.IsActive() && !g_gpu_device->GetWindowInfo().IsSurfaceless() &&
|
||||||
PostProcessing::CheckTargets(hdformat, target_width, target_height));
|
hdformat != GPUTexture::Format::Unknown && target_width > 0 && target_height > 0 &&
|
||||||
|
PostProcessing::DisplayChain.CheckTargets(hdformat, target_width, target_height));
|
||||||
const Common::Rectangle<s32> real_draw_rect =
|
const Common::Rectangle<s32> real_draw_rect =
|
||||||
g_gpu_device->UsesLowerLeftOrigin() ? GPUDevice::FlipToLowerLeft(draw_rect, target_height) : draw_rect;
|
g_gpu_device->UsesLowerLeftOrigin() ? GPUDevice::FlipToLowerLeft(draw_rect, target_height) : draw_rect;
|
||||||
if (really_postfx)
|
if (really_postfx)
|
||||||
{
|
{
|
||||||
g_gpu_device->ClearRenderTarget(PostProcessing::GetInputTexture(), 0);
|
g_gpu_device->ClearRenderTarget(PostProcessing::DisplayChain.GetInputTexture(), 0);
|
||||||
g_gpu_device->SetRenderTarget(PostProcessing::GetInputTexture());
|
g_gpu_device->SetRenderTarget(PostProcessing::DisplayChain.GetInputTexture());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1959,7 +1990,7 @@ bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_r
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_display_texture)
|
if (display_texture)
|
||||||
{
|
{
|
||||||
bool texture_filter_linear = false;
|
bool texture_filter_linear = false;
|
||||||
|
|
||||||
|
@ -2003,26 +2034,25 @@ bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_r
|
||||||
}
|
}
|
||||||
|
|
||||||
g_gpu_device->SetPipeline(m_display_pipeline.get());
|
g_gpu_device->SetPipeline(m_display_pipeline.get());
|
||||||
g_gpu_device->SetTextureSampler(0, m_display_texture,
|
g_gpu_device->SetTextureSampler(
|
||||||
texture_filter_linear ? g_gpu_device->GetLinearSampler() :
|
0, display_texture, texture_filter_linear ? g_gpu_device->GetLinearSampler() : g_gpu_device->GetNearestSampler());
|
||||||
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
|
// 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.
|
// 1.0 in UV space is not the bottom-right texel, but a mix of the bottom-right and wrapped/next texel.
|
||||||
const float rcp_width = 1.0f / static_cast<float>(m_display_texture->GetWidth());
|
const float rcp_width = 1.0f / static_cast<float>(display_texture->GetWidth());
|
||||||
const float rcp_height = 1.0f / static_cast<float>(m_display_texture->GetHeight());
|
const float rcp_height = 1.0f / static_cast<float>(display_texture->GetHeight());
|
||||||
uniforms.src_rect[0] = static_cast<float>(m_display_texture_view_x) * rcp_width;
|
uniforms.src_rect[0] = static_cast<float>(display_texture_view_x) * rcp_width;
|
||||||
uniforms.src_rect[1] = static_cast<float>(m_display_texture_view_y) * rcp_height;
|
uniforms.src_rect[1] = static_cast<float>(display_texture_view_y) * rcp_height;
|
||||||
uniforms.src_rect[2] = static_cast<float>(m_display_texture_view_width) * rcp_width;
|
uniforms.src_rect[2] = static_cast<float>(display_texture_view_width) * rcp_width;
|
||||||
uniforms.src_rect[3] = static_cast<float>(m_display_texture_view_height) * rcp_height;
|
uniforms.src_rect[3] = static_cast<float>(display_texture_view_height) * rcp_height;
|
||||||
uniforms.clamp_rect[0] = (static_cast<float>(m_display_texture_view_x) + 0.5f) * rcp_width;
|
uniforms.clamp_rect[0] = (static_cast<float>(display_texture_view_x) + 0.5f) * rcp_width;
|
||||||
uniforms.clamp_rect[1] = (static_cast<float>(m_display_texture_view_y) + 0.5f) * rcp_height;
|
uniforms.clamp_rect[1] = (static_cast<float>(display_texture_view_y) + 0.5f) * rcp_height;
|
||||||
uniforms.clamp_rect[2] =
|
uniforms.clamp_rect[2] =
|
||||||
(static_cast<float>(m_display_texture_view_x + m_display_texture_view_width) - 0.5f) * rcp_width;
|
(static_cast<float>(display_texture_view_x + display_texture_view_width) - 0.5f) * rcp_width;
|
||||||
uniforms.clamp_rect[3] =
|
uniforms.clamp_rect[3] =
|
||||||
(static_cast<float>(m_display_texture_view_y + m_display_texture_view_height) - 0.5f) * rcp_height;
|
(static_cast<float>(display_texture_view_y + display_texture_view_height) - 0.5f) * rcp_height;
|
||||||
uniforms.src_size[0] = static_cast<float>(m_display_texture->GetWidth());
|
uniforms.src_size[0] = static_cast<float>(display_texture->GetWidth());
|
||||||
uniforms.src_size[1] = static_cast<float>(m_display_texture->GetHeight());
|
uniforms.src_size[1] = static_cast<float>(display_texture->GetHeight());
|
||||||
uniforms.src_size[2] = rcp_width;
|
uniforms.src_size[2] = rcp_width;
|
||||||
uniforms.src_size[3] = rcp_height;
|
uniforms.src_size[3] = rcp_height;
|
||||||
g_gpu_device->PushUniformBuffer(&uniforms, sizeof(uniforms));
|
g_gpu_device->PushUniformBuffer(&uniforms, sizeof(uniforms));
|
||||||
|
@ -2044,9 +2074,10 @@ bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_r
|
||||||
const s32 orig_width = static_cast<s32>(std::ceil(static_cast<float>(m_crtc_state.display_width) * upscale_x));
|
const s32 orig_width = static_cast<s32>(std::ceil(static_cast<float>(m_crtc_state.display_width) * upscale_x));
|
||||||
const s32 orig_height = static_cast<s32>(std::ceil(static_cast<float>(m_crtc_state.display_height) * upscale_y));
|
const s32 orig_height = static_cast<s32>(std::ceil(static_cast<float>(m_crtc_state.display_height) * upscale_y));
|
||||||
|
|
||||||
return PostProcessing::Apply(target, real_draw_rect.left, real_draw_rect.top, real_draw_rect.GetWidth(),
|
return PostProcessing::DisplayChain.Apply(PostProcessing::DisplayChain.GetInputTexture(), target,
|
||||||
real_draw_rect.GetHeight(), orig_width, orig_height, m_crtc_state.display_width,
|
real_draw_rect.left, real_draw_rect.top, real_draw_rect.GetWidth(),
|
||||||
m_crtc_state.display_height);
|
real_draw_rect.GetHeight(), orig_width, orig_height,
|
||||||
|
m_crtc_state.display_width, m_crtc_state.display_height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
|
||||||
#include "util/imgui_manager.h"
|
#include "util/imgui_manager.h"
|
||||||
|
#include "util/postprocessing.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
|
||||||
#include "common/align.h"
|
#include "common/align.h"
|
||||||
|
@ -3311,7 +3312,8 @@ void GPU_HW::UpdateDisplay()
|
||||||
}
|
}
|
||||||
else if (!m_GPUSTAT.display_area_color_depth_24 && !IsUsingMultisampling() &&
|
else if (!m_GPUSTAT.display_area_color_depth_24 && !IsUsingMultisampling() &&
|
||||||
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
|
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
|
||||||
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight())
|
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight() &&
|
||||||
|
!PostProcessing::InternalChain.IsActive())
|
||||||
{
|
{
|
||||||
SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
||||||
read_height);
|
read_height);
|
||||||
|
|
|
@ -406,7 +406,13 @@ DEFINE_HOTKEY("DecreaseResolutionScale", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||||
DEFINE_HOTKEY("TogglePostProcessing", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
DEFINE_HOTKEY("TogglePostProcessing", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||||
TRANSLATE_NOOP("Hotkeys", "Toggle Post-Processing"), [](s32 pressed) {
|
TRANSLATE_NOOP("Hotkeys", "Toggle Post-Processing"), [](s32 pressed) {
|
||||||
if (!pressed && System::IsValid())
|
if (!pressed && System::IsValid())
|
||||||
PostProcessing::Toggle();
|
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();
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_HOTKEY("ReloadPostProcessingShaders", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
DEFINE_HOTKEY("ReloadPostProcessingShaders", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||||
|
|
|
@ -126,7 +126,7 @@ set(SRCS
|
||||||
memoryviewwidget.h
|
memoryviewwidget.h
|
||||||
postprocessingsettingswidget.cpp
|
postprocessingsettingswidget.cpp
|
||||||
postprocessingsettingswidget.h
|
postprocessingsettingswidget.h
|
||||||
postprocessingsettingswidget.ui
|
postprocessingchainconfigwidget.ui
|
||||||
qthost.cpp
|
qthost.cpp
|
||||||
qthost.h
|
qthost.h
|
||||||
qtkeycodes.cpp
|
qtkeycodes.cpp
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
<QtUi Include="advancedsettingswidget.ui">
|
<QtUi Include="advancedsettingswidget.ui">
|
||||||
<FileType>Document</FileType>
|
<FileType>Document</FileType>
|
||||||
</QtUi>
|
</QtUi>
|
||||||
<QtUi Include="postprocessingsettingswidget.ui">
|
<QtUi Include="postprocessingchainconfigwidget.ui">
|
||||||
<FileType>Document</FileType>
|
<FileType>Document</FileType>
|
||||||
</QtUi>
|
</QtUi>
|
||||||
<QtUi Include="memorycardeditorwindow.ui">
|
<QtUi Include="memorycardeditorwindow.ui">
|
||||||
|
|
|
@ -255,7 +255,7 @@
|
||||||
<QtUi Include="inputbindingdialog.ui" />
|
<QtUi Include="inputbindingdialog.ui" />
|
||||||
<QtUi Include="autoupdaterdialog.ui" />
|
<QtUi Include="autoupdaterdialog.ui" />
|
||||||
<QtUi Include="biossettingswidget.ui" />
|
<QtUi Include="biossettingswidget.ui" />
|
||||||
<QtUi Include="postprocessingsettingswidget.ui" />
|
<QtUi Include="postprocessingchainconfigwidget.ui" />
|
||||||
<QtUi Include="memorycardeditorwindow.ui" />
|
<QtUi Include="memorycardeditorwindow.ui" />
|
||||||
<QtUi Include="cheatmanagerwindow.ui" />
|
<QtUi Include="cheatmanagerwindow.ui" />
|
||||||
<QtUi Include="cheatcodeeditordialog.ui" />
|
<QtUi Include="cheatcodeeditordialog.ui" />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>PostProcessingSettingsWidget</class>
|
<class>PostProcessingChainConfigWidget</class>
|
||||||
<widget class="QWidget" name="PostProcessingSettingsWidget">
|
<widget class="QWidget" name="PostProcessingChainConfigWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
|
@ -11,18 +11,6 @@
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
@ -35,7 +23,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
@ -51,8 +39,7 @@
|
||||||
<string>&Reload Shaders</string>
|
<string>&Reload Shaders</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="refresh-line">
|
<iconset theme="refresh-line"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -94,11 +81,10 @@
|
||||||
<string>Add</string>
|
<string>Add</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="add-line">
|
<iconset theme="add-line"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -114,11 +100,10 @@
|
||||||
<string>Remove</string>
|
<string>Remove</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="minus-line">
|
<iconset theme="minus-line"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -134,11 +119,10 @@
|
||||||
<string>Clear</string>
|
<string>Clear</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="trash-fill">
|
<iconset theme="trash-fill"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -154,11 +138,10 @@
|
||||||
<string>Move Up</string>
|
<string>Move Up</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="arrow-up-line">
|
<iconset theme="arrow-up-line"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -174,11 +157,10 @@
|
||||||
<string>Move Down</string>
|
<string>Move Down</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="arrow-down-line">
|
<iconset theme="arrow-down-line"/>
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -196,10 +178,10 @@
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::WinPanel</enum>
|
<enum>QFrame::Shape::WinPanel</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShadow">
|
<property name="frameShadow">
|
||||||
<enum>QFrame::Sunken</enum>
|
<enum>QFrame::Shadow::Sunken</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="widgetResizable">
|
<property name="widgetResizable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -209,8 +191,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>679</width>
|
<width>661</width>
|
||||||
<height>238</height>
|
<height>220</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -219,7 +201,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
|
@ -16,28 +16,40 @@
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
#include <QtWidgets/QSlider>
|
#include <QtWidgets/QSlider>
|
||||||
|
|
||||||
PostProcessingSettingsWidget::PostProcessingSettingsWidget(SettingsWindow* dialog, QWidget* parent)
|
PostProcessingSettingsWidget::PostProcessingSettingsWidget(SettingsWindow* dialog, QWidget* parent) : QTabWidget(parent)
|
||||||
: QWidget(parent), m_dialog(dialog)
|
{
|
||||||
|
addTab(new PostProcessingChainConfigWidget(dialog, this, PostProcessing::Config::DISPLAY_CHAIN_SECTION),
|
||||||
|
tr("Display"));
|
||||||
|
addTab(new PostProcessingChainConfigWidget(dialog, this, PostProcessing::Config::INTERNAL_CHAIN_SECTION),
|
||||||
|
tr("Internal"));
|
||||||
|
setDocumentMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessingSettingsWidget::~PostProcessingSettingsWidget() = default;
|
||||||
|
|
||||||
|
PostProcessingChainConfigWidget::PostProcessingChainConfigWidget(SettingsWindow* dialog, QWidget* parent,
|
||||||
|
const char* section)
|
||||||
|
: QWidget(parent), m_dialog(dialog), m_section(section)
|
||||||
{
|
{
|
||||||
SettingsInterface* sif = dialog->getSettingsInterface();
|
SettingsInterface* sif = dialog->getSettingsInterface();
|
||||||
|
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enablePostProcessing, "PostProcessing", "Enabled", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enablePostProcessing, section, "Enabled", false);
|
||||||
|
|
||||||
updateList();
|
updateList();
|
||||||
updateButtonsAndConfigPane(std::nullopt);
|
updateButtonsAndConfigPane(std::nullopt);
|
||||||
connectUi();
|
connectUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingSettingsWidget::~PostProcessingSettingsWidget() = default;
|
PostProcessingChainConfigWidget::~PostProcessingChainConfigWidget() = default;
|
||||||
|
|
||||||
SettingsInterface& PostProcessingSettingsWidget::getSettingsInterfaceToUpdate()
|
SettingsInterface& PostProcessingChainConfigWidget::getSettingsInterfaceToUpdate()
|
||||||
{
|
{
|
||||||
return m_dialog->isPerGameSettings() ? *m_dialog->getSettingsInterface() : *Host::Internal::GetBaseSettingsLayer();
|
return m_dialog->isPerGameSettings() ? *m_dialog->getSettingsInterface() : *Host::Internal::GetBaseSettingsLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::commitSettingsUpdate()
|
void PostProcessingChainConfigWidget::commitSettingsUpdate()
|
||||||
{
|
{
|
||||||
if (m_dialog->isPerGameSettings())
|
if (m_dialog->isPerGameSettings())
|
||||||
{
|
{
|
||||||
|
@ -50,26 +62,26 @@ void PostProcessingSettingsWidget::commitSettingsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::connectUi()
|
void PostProcessingChainConfigWidget::connectUi()
|
||||||
{
|
{
|
||||||
connect(m_ui.reload, &QPushButton::clicked, this, &PostProcessingSettingsWidget::onReloadButtonClicked);
|
connect(m_ui.reload, &QPushButton::clicked, this, &PostProcessingChainConfigWidget::onReloadButtonClicked);
|
||||||
connect(m_ui.add, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onAddButtonClicked);
|
connect(m_ui.add, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onAddButtonClicked);
|
||||||
connect(m_ui.remove, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onRemoveButtonClicked);
|
connect(m_ui.remove, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onRemoveButtonClicked);
|
||||||
connect(m_ui.clear, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onClearButtonClicked);
|
connect(m_ui.clear, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onClearButtonClicked);
|
||||||
connect(m_ui.moveUp, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onMoveUpButtonClicked);
|
connect(m_ui.moveUp, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onMoveUpButtonClicked);
|
||||||
connect(m_ui.moveDown, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onMoveDownButtonClicked);
|
connect(m_ui.moveDown, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onMoveDownButtonClicked);
|
||||||
connect(m_ui.stages, &QListWidget::itemSelectionChanged, this,
|
connect(m_ui.stages, &QListWidget::itemSelectionChanged, this,
|
||||||
&PostProcessingSettingsWidget::onSelectedShaderChanged);
|
&PostProcessingChainConfigWidget::onSelectedShaderChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> PostProcessingSettingsWidget::getSelectedIndex() const
|
std::optional<u32> PostProcessingChainConfigWidget::getSelectedIndex() const
|
||||||
{
|
{
|
||||||
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
||||||
return selected_items.empty() ? std::nullopt :
|
return selected_items.empty() ? std::nullopt :
|
||||||
std::optional<u32>(selected_items.first()->data(Qt::UserRole).toUInt());
|
std::optional<u32>(selected_items.first()->data(Qt::UserRole).toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::selectIndex(s32 index)
|
void PostProcessingChainConfigWidget::selectIndex(s32 index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_ui.stages->count())
|
if (index < 0 || index >= m_ui.stages->count())
|
||||||
return;
|
return;
|
||||||
|
@ -79,7 +91,7 @@ void PostProcessingSettingsWidget::selectIndex(s32 index)
|
||||||
updateButtonsAndConfigPane(index);
|
updateButtonsAndConfigPane(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::updateList()
|
void PostProcessingChainConfigWidget::updateList()
|
||||||
{
|
{
|
||||||
const auto lock = Host::GetSettingsLock();
|
const auto lock = Host::GetSettingsLock();
|
||||||
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
@ -87,15 +99,15 @@ void PostProcessingSettingsWidget::updateList()
|
||||||
updateList(si);
|
updateList(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::updateList(const SettingsInterface& si)
|
void PostProcessingChainConfigWidget::updateList(const SettingsInterface& si)
|
||||||
{
|
{
|
||||||
m_ui.stages->clear();
|
m_ui.stages->clear();
|
||||||
|
|
||||||
const u32 stage_count = PostProcessing::Config::GetStageCount(si);
|
const u32 stage_count = PostProcessing::Config::GetStageCount(si, m_section);
|
||||||
|
|
||||||
for (u32 i = 0; i < stage_count; i++)
|
for (u32 i = 0; i < stage_count; i++)
|
||||||
{
|
{
|
||||||
const std::string stage_name = PostProcessing::Config::GetStageShaderName(si, i);
|
const std::string stage_name = PostProcessing::Config::GetStageShaderName(si, m_section, i);
|
||||||
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(stage_name), m_ui.stages);
|
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(stage_name), m_ui.stages);
|
||||||
item->setData(Qt::UserRole, QVariant(i));
|
item->setData(Qt::UserRole, QVariant(i));
|
||||||
}
|
}
|
||||||
|
@ -105,7 +117,7 @@ void PostProcessingSettingsWidget::updateList(const SettingsInterface& si)
|
||||||
updateButtonsAndConfigPane(std::nullopt);
|
updateButtonsAndConfigPane(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::updateButtonsAndConfigPane(std::optional<u32> index)
|
void PostProcessingChainConfigWidget::updateButtonsAndConfigPane(std::optional<u32> index)
|
||||||
{
|
{
|
||||||
m_ui.remove->setEnabled(index.has_value());
|
m_ui.remove->setEnabled(index.has_value());
|
||||||
|
|
||||||
|
@ -134,16 +146,18 @@ void PostProcessingSettingsWidget::updateButtonsAndConfigPane(std::optional<u32>
|
||||||
|
|
||||||
const auto lock = Host::GetSettingsLock();
|
const auto lock = Host::GetSettingsLock();
|
||||||
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
std::vector<PostProcessing::ShaderOption> options = PostProcessing::Config::GetStageOptions(si, index.value());
|
std::vector<PostProcessing::ShaderOption> options =
|
||||||
|
PostProcessing::Config::GetStageOptions(si, m_section, index.value());
|
||||||
if (options.empty())
|
if (options.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_shader_config = new PostProcessingShaderConfigWidget(m_ui.scrollArea, this, index.value(), std::move(options));
|
m_shader_config =
|
||||||
|
new PostProcessingShaderConfigWidget(m_ui.scrollArea, this, m_section, index.value(), std::move(options));
|
||||||
m_ui.scrollArea->setWidget(m_shader_config);
|
m_ui.scrollArea->setWidget(m_shader_config);
|
||||||
m_ui.scrollArea->setVisible(true);
|
m_ui.scrollArea->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onAddButtonClicked()
|
void PostProcessingChainConfigWidget::onAddButtonClicked()
|
||||||
{
|
{
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
|
|
||||||
|
@ -162,7 +176,7 @@ void PostProcessingSettingsWidget::onAddButtonClicked()
|
||||||
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!PostProcessing::Config::AddStage(si, shader, &error))
|
if (!PostProcessing::Config::AddStage(si, m_section, shader, &error))
|
||||||
{
|
{
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
QMessageBox::critical(this, tr("Error"),
|
QMessageBox::critical(this, tr("Error"),
|
||||||
|
@ -180,7 +194,7 @@ void PostProcessingSettingsWidget::onAddButtonClicked()
|
||||||
menu.exec(QCursor::pos());
|
menu.exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onRemoveButtonClicked()
|
void PostProcessingChainConfigWidget::onRemoveButtonClicked()
|
||||||
{
|
{
|
||||||
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
||||||
if (selected_items.empty())
|
if (selected_items.empty())
|
||||||
|
@ -191,37 +205,37 @@ void PostProcessingSettingsWidget::onRemoveButtonClicked()
|
||||||
|
|
||||||
QListWidgetItem* item = selected_items.first();
|
QListWidgetItem* item = selected_items.first();
|
||||||
u32 index = item->data(Qt::UserRole).toUInt();
|
u32 index = item->data(Qt::UserRole).toUInt();
|
||||||
if (index < PostProcessing::Config::GetStageCount(si))
|
if (index < PostProcessing::Config::GetStageCount(si, m_section))
|
||||||
{
|
{
|
||||||
PostProcessing::Config::RemoveStage(si, index);
|
PostProcessing::Config::RemoveStage(si, m_section, index);
|
||||||
updateList(si);
|
updateList(si);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
commitSettingsUpdate();
|
commitSettingsUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onClearButtonClicked()
|
void PostProcessingChainConfigWidget::onClearButtonClicked()
|
||||||
{
|
{
|
||||||
if (QMessageBox::question(this, tr("Question"), tr("Are you sure you want to clear all shader stages?"),
|
if (QMessageBox::question(this, tr("Question"), tr("Are you sure you want to clear all shader stages?"),
|
||||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||||
{
|
{
|
||||||
auto lock = Host::GetSettingsLock();
|
auto lock = Host::GetSettingsLock();
|
||||||
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
PostProcessing::Config::ClearStages(si);
|
PostProcessing::Config::ClearStages(si, m_section);
|
||||||
updateList(si);
|
updateList(si);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
commitSettingsUpdate();
|
commitSettingsUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onMoveUpButtonClicked()
|
void PostProcessingChainConfigWidget::onMoveUpButtonClicked()
|
||||||
{
|
{
|
||||||
std::optional<u32> index = getSelectedIndex();
|
std::optional<u32> index = getSelectedIndex();
|
||||||
if (index.has_value() && index.value() > 0)
|
if (index.has_value() && index.value() > 0)
|
||||||
{
|
{
|
||||||
auto lock = Host::GetSettingsLock();
|
auto lock = Host::GetSettingsLock();
|
||||||
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
PostProcessing::Config::MoveStageUp(si, index.value());
|
PostProcessing::Config::MoveStageUp(si, m_section, index.value());
|
||||||
updateList(si);
|
updateList(si);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
selectIndex(index.value() - 1);
|
selectIndex(index.value() - 1);
|
||||||
|
@ -229,14 +243,14 @@ void PostProcessingSettingsWidget::onMoveUpButtonClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onMoveDownButtonClicked()
|
void PostProcessingChainConfigWidget::onMoveDownButtonClicked()
|
||||||
{
|
{
|
||||||
std::optional<u32> index = getSelectedIndex();
|
std::optional<u32> index = getSelectedIndex();
|
||||||
if (index.has_value() || index.value() < (static_cast<u32>(m_ui.stages->count() - 1)))
|
if (index.has_value() || index.value() < (static_cast<u32>(m_ui.stages->count() - 1)))
|
||||||
{
|
{
|
||||||
auto lock = Host::GetSettingsLock();
|
auto lock = Host::GetSettingsLock();
|
||||||
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
PostProcessing::Config::MoveStageDown(si, index.value());
|
PostProcessing::Config::MoveStageDown(si, m_section, index.value());
|
||||||
updateList(si);
|
updateList(si);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
selectIndex(index.value() + 1);
|
selectIndex(index.value() + 1);
|
||||||
|
@ -244,22 +258,22 @@ void PostProcessingSettingsWidget::onMoveDownButtonClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onReloadButtonClicked()
|
void PostProcessingChainConfigWidget::onReloadButtonClicked()
|
||||||
{
|
{
|
||||||
g_emu_thread->reloadPostProcessingShaders();
|
g_emu_thread->reloadPostProcessingShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onSelectedShaderChanged()
|
void PostProcessingChainConfigWidget::onSelectedShaderChanged()
|
||||||
{
|
{
|
||||||
std::optional<u32> index = getSelectedIndex();
|
std::optional<u32> index = getSelectedIndex();
|
||||||
updateButtonsAndConfigPane(index);
|
updateButtonsAndConfigPane(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
|
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
|
||||||
PostProcessingSettingsWidget* widget,
|
PostProcessingChainConfigWidget* widget,
|
||||||
u32 stage_index,
|
const char* section, u32 stage_index,
|
||||||
std::vector<PostProcessing::ShaderOption> options)
|
std::vector<PostProcessing::ShaderOption> options)
|
||||||
: QWidget(parent), m_widget(widget), m_stage_index(stage_index), m_options(std::move(options))
|
: QWidget(parent), m_widget(widget), m_section(section), m_stage_index(stage_index), m_options(std::move(options))
|
||||||
{
|
{
|
||||||
m_layout = new QGridLayout(this);
|
m_layout = new QGridLayout(this);
|
||||||
|
|
||||||
|
@ -272,7 +286,7 @@ void PostProcessingShaderConfigWidget::updateConfigForOption(const PostProcessin
|
||||||
{
|
{
|
||||||
const auto lock = Host::GetSettingsLock();
|
const auto lock = Host::GetSettingsLock();
|
||||||
SettingsInterface& si = m_widget->getSettingsInterfaceToUpdate();
|
SettingsInterface& si = m_widget->getSettingsInterfaceToUpdate();
|
||||||
PostProcessing::Config::SetStageOption(si, m_stage_index, option);
|
PostProcessing::Config::SetStageOption(si, m_section, m_stage_index, option);
|
||||||
m_widget->commitSettingsUpdate();
|
m_widget->commitSettingsUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +301,7 @@ void PostProcessingShaderConfigWidget::onResetDefaultsClicked()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
option.value = option.default_value;
|
option.value = option.default_value;
|
||||||
PostProcessing::Config::UnsetStageOption(si, m_stage_index, option);
|
PostProcessing::Config::UnsetStageOption(si, m_section, m_stage_index, option);
|
||||||
}
|
}
|
||||||
m_widget->commitSettingsUpdate();
|
m_widget->commitSettingsUpdate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,36 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ui_postprocessingsettingswidget.h"
|
#include "ui_postprocessingchainconfigwidget.h"
|
||||||
|
|
||||||
#include "util/postprocessing.h"
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
|
#include <QtWidgets/QTableWidget>
|
||||||
#include <QtWidgets/QWidget>
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
class SettingsWindow;
|
class SettingsWindow;
|
||||||
class PostProcessingShaderConfigWidget;
|
class PostProcessingShaderConfigWidget;
|
||||||
|
|
||||||
class PostProcessingSettingsWidget : public QWidget
|
class PostProcessingSettingsWidget : public QTabWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostProcessingSettingsWidget(SettingsWindow* dialog, QWidget* parent);
|
||||||
|
~PostProcessingSettingsWidget();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PostProcessingChainConfigWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend PostProcessingShaderConfigWidget;
|
friend PostProcessingShaderConfigWidget;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PostProcessingSettingsWidget(SettingsWindow* dialog, QWidget* parent);
|
PostProcessingChainConfigWidget(SettingsWindow* dialog, QWidget* parent, const char* section);
|
||||||
~PostProcessingSettingsWidget();
|
~PostProcessingChainConfigWidget();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onAddButtonClicked();
|
void onAddButtonClicked();
|
||||||
|
@ -44,7 +54,9 @@ private:
|
||||||
|
|
||||||
SettingsWindow* m_dialog;
|
SettingsWindow* m_dialog;
|
||||||
|
|
||||||
Ui::PostProcessingSettingsWidget m_ui;
|
Ui::PostProcessingChainConfigWidget m_ui;
|
||||||
|
|
||||||
|
const char* m_section;
|
||||||
|
|
||||||
PostProcessingShaderConfigWidget* m_shader_config = nullptr;
|
PostProcessingShaderConfigWidget* m_shader_config = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -54,8 +66,8 @@ class PostProcessingShaderConfigWidget : public QWidget
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingSettingsWidget* widget, u32 stage_index,
|
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingChainConfigWidget* widget, const char* section,
|
||||||
std::vector<PostProcessing::ShaderOption> options);
|
u32 stage_index, std::vector<PostProcessing::ShaderOption> options);
|
||||||
~PostProcessingShaderConfigWidget();
|
~PostProcessingShaderConfigWidget();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
@ -67,9 +79,10 @@ protected:
|
||||||
|
|
||||||
QGridLayout* m_layout;
|
QGridLayout* m_layout;
|
||||||
|
|
||||||
PostProcessingSettingsWidget* m_widget;
|
PostProcessingChainConfigWidget* m_widget;
|
||||||
std::vector<QWidget*> m_widgets;
|
std::vector<QWidget*> m_widgets;
|
||||||
|
|
||||||
|
const char* m_section;
|
||||||
u32 m_stage_index;
|
u32 m_stage_index;
|
||||||
std::vector<PostProcessing::ShaderOption> m_options;
|
std::vector<PostProcessing::ShaderOption> m_options;
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,27 +37,24 @@ static u32 ParseVector(std::string_view line, ShaderOption::ValueVector* values)
|
||||||
|
|
||||||
static TinyString ValueToString(ShaderOption::Type type, u32 vector_size, const ShaderOption::ValueVector& value);
|
static TinyString ValueToString(ShaderOption::Type type, u32 vector_size, const ShaderOption::ValueVector& value);
|
||||||
|
|
||||||
static TinyString GetStageConfigSection(u32 index);
|
static TinyString GetStageConfigSection(const char* section, u32 index);
|
||||||
static void CopyStageConfig(SettingsInterface& si, u32 old_index, u32 new_index);
|
static void CopyStageConfig(SettingsInterface& si, const char* section, u32 old_index, u32 new_index);
|
||||||
static void SwapStageConfig(SettingsInterface& si, u32 lhs_index, u32 rhs_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 std::unique_ptr<Shader> TryLoadingShader(const std::string& shader_name, bool only_config, Error* error);
|
||||||
static void ClearStagesWithError(const Error& error);
|
static SettingsInterface& GetLoadSettingsInterface(const char* section);
|
||||||
static SettingsInterface& GetLoadSettingsInterface();
|
|
||||||
static void LoadStages();
|
|
||||||
static void DestroyTextures();
|
|
||||||
|
|
||||||
static std::vector<std::unique_ptr<PostProcessing::Shader>> s_stages;
|
template<typename T>
|
||||||
static bool s_enabled = false;
|
ALWAYS_INLINE void ForAllChains(const T& F)
|
||||||
|
{
|
||||||
|
F(DisplayChain);
|
||||||
|
F(InternalChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chain DisplayChain(Config::DISPLAY_CHAIN_SECTION);
|
||||||
|
Chain InternalChain(Config::INTERNAL_CHAIN_SECTION);
|
||||||
|
|
||||||
static GPUTexture::Format s_target_format = GPUTexture::Format::Unknown;
|
|
||||||
static u32 s_target_width = 0;
|
|
||||||
static u32 s_target_height = 0;
|
|
||||||
static Common::Timer s_timer;
|
static Common::Timer s_timer;
|
||||||
|
|
||||||
static std::unique_ptr<GPUTexture> s_input_texture;
|
|
||||||
|
|
||||||
static std::unique_ptr<GPUTexture> s_output_texture;
|
|
||||||
|
|
||||||
static std::unordered_map<u64, std::unique_ptr<GPUSampler>> s_samplers;
|
static std::unordered_map<u64, std::unique_ptr<GPUSampler>> s_samplers;
|
||||||
static std::unique_ptr<GPUTexture> s_dummy_texture;
|
static std::unique_ptr<GPUTexture> s_dummy_texture;
|
||||||
} // namespace PostProcessing
|
} // namespace PostProcessing
|
||||||
|
@ -213,15 +210,15 @@ std::vector<std::pair<std::string, std::string>> PostProcessing::GetAvailableSha
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
TinyString PostProcessing::GetStageConfigSection(u32 index)
|
TinyString PostProcessing::GetStageConfigSection(const char* section, u32 index)
|
||||||
{
|
{
|
||||||
return TinyString::from_format("PostProcessing/Stage{}", index + 1);
|
return TinyString::from_format("{}/Stage{}", section, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::CopyStageConfig(SettingsInterface& si, u32 old_index, u32 new_index)
|
void PostProcessing::CopyStageConfig(SettingsInterface& si, const char* section, u32 old_index, u32 new_index)
|
||||||
{
|
{
|
||||||
const TinyString old_section = GetStageConfigSection(old_index);
|
const TinyString old_section = GetStageConfigSection(section, old_index);
|
||||||
const TinyString new_section = GetStageConfigSection(new_index);
|
const TinyString new_section = GetStageConfigSection(section, new_index);
|
||||||
|
|
||||||
si.ClearSection(new_section);
|
si.ClearSection(new_section);
|
||||||
|
|
||||||
|
@ -229,10 +226,10 @@ void PostProcessing::CopyStageConfig(SettingsInterface& si, u32 old_index, u32 n
|
||||||
si.SetStringValue(new_section, key.c_str(), value.c_str());
|
si.SetStringValue(new_section, key.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::SwapStageConfig(SettingsInterface& si, u32 lhs_index, u32 rhs_index)
|
void PostProcessing::SwapStageConfig(SettingsInterface& si, const char* section, u32 lhs_index, u32 rhs_index)
|
||||||
{
|
{
|
||||||
const TinyString lhs_section = GetStageConfigSection(lhs_index);
|
const TinyString lhs_section = GetStageConfigSection(section, lhs_index);
|
||||||
const TinyString rhs_section = GetStageConfigSection(rhs_index);
|
const TinyString rhs_section = GetStageConfigSection(section, rhs_index);
|
||||||
|
|
||||||
const std::vector<std::pair<std::string, std::string>> lhs_kvs = si.GetKeyValueList(lhs_section);
|
const std::vector<std::pair<std::string, std::string>> lhs_kvs = si.GetKeyValueList(lhs_section);
|
||||||
si.ClearSection(lhs_section);
|
si.ClearSection(lhs_section);
|
||||||
|
@ -247,23 +244,23 @@ void PostProcessing::SwapStageConfig(SettingsInterface& si, u32 lhs_index, u32 r
|
||||||
si.SetStringValue(rhs_section, key.c_str(), value.c_str());
|
si.SetStringValue(rhs_section, key.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 PostProcessing::Config::GetStageCount(const SettingsInterface& si)
|
u32 PostProcessing::Config::GetStageCount(const SettingsInterface& si, const char* section)
|
||||||
{
|
{
|
||||||
return si.GetUIntValue("PostProcessing", "StageCount", 0u);
|
return si.GetUIntValue(section, "StageCount", 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PostProcessing::Config::GetStageShaderName(const SettingsInterface& si, u32 index)
|
std::string PostProcessing::Config::GetStageShaderName(const SettingsInterface& si, const char* section, u32 index)
|
||||||
{
|
{
|
||||||
return si.GetStringValue(GetStageConfigSection(index), "ShaderName");
|
return si.GetStringValue(GetStageConfigSection(section, index), "ShaderName");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetStageOptions(const SettingsInterface& si,
|
std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetStageOptions(const SettingsInterface& si,
|
||||||
u32 index)
|
const char* section, u32 index)
|
||||||
{
|
{
|
||||||
std::vector<PostProcessing::ShaderOption> ret;
|
std::vector<PostProcessing::ShaderOption> ret;
|
||||||
|
|
||||||
const TinyString section = GetStageConfigSection(index);
|
const TinyString stage_section = GetStageConfigSection(section, index);
|
||||||
const std::string shader_name = si.GetStringValue(section, "ShaderName");
|
const std::string shader_name = si.GetStringValue(stage_section, "ShaderName");
|
||||||
if (shader_name.empty())
|
if (shader_name.empty())
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -271,7 +268,7 @@ std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetStageOption
|
||||||
if (!shader)
|
if (!shader)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
shader->LoadOptions(si, section);
|
shader->LoadOptions(si, stage_section);
|
||||||
ret = shader->TakeOptions();
|
ret = shader->TakeOptions();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -288,17 +285,18 @@ std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetShaderOptio
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessing::Config::AddStage(SettingsInterface& si, const std::string& shader_name, Error* error)
|
bool PostProcessing::Config::AddStage(SettingsInterface& si, const char* section, const std::string& shader_name,
|
||||||
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, error);
|
std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, error);
|
||||||
if (!shader)
|
if (!shader)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const u32 index = GetStageCount(si);
|
const u32 index = GetStageCount(si, section);
|
||||||
si.SetUIntValue("PostProcessing", "StageCount", index + 1);
|
si.SetUIntValue(section, "StageCount", index + 1);
|
||||||
|
|
||||||
const TinyString section = GetStageConfigSection(index);
|
const TinyString stage_section = GetStageConfigSection(section, index);
|
||||||
si.SetStringValue(section, "ShaderName", shader->GetName().c_str());
|
si.SetStringValue(stage_section, "ShaderName", shader->GetName().c_str());
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Leave options unset for now.
|
// Leave options unset for now.
|
||||||
|
@ -312,70 +310,367 @@ bool PostProcessing::Config::AddStage(SettingsInterface& si, const std::string&
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::RemoveStage(SettingsInterface& si, u32 index)
|
void PostProcessing::Config::RemoveStage(SettingsInterface& si, const char* section, u32 index)
|
||||||
{
|
{
|
||||||
const u32 stage_count = GetStageCount(si);
|
const u32 stage_count = GetStageCount(si, section);
|
||||||
if (index >= stage_count)
|
if (index >= stage_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (u32 i = index; i < (stage_count - 1); i++)
|
for (u32 i = index; i < (stage_count - 1); i++)
|
||||||
CopyStageConfig(si, i + 1, i);
|
CopyStageConfig(si, section, i + 1, i);
|
||||||
|
|
||||||
si.ClearSection(GetStageConfigSection(stage_count - 1));
|
si.ClearSection(GetStageConfigSection(section, stage_count - 1));
|
||||||
si.SetUIntValue("PostProcessing", "StageCount", stage_count - 1);
|
si.SetUIntValue(section, "StageCount", stage_count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::MoveStageUp(SettingsInterface& si, u32 index)
|
void PostProcessing::Config::MoveStageUp(SettingsInterface& si, const char* section, u32 index)
|
||||||
{
|
{
|
||||||
const u32 stage_count = GetStageCount(si);
|
const u32 stage_count = GetStageCount(si, section);
|
||||||
if (index == 0 || index >= stage_count)
|
if (index == 0 || index >= stage_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SwapStageConfig(si, index, index - 1);
|
SwapStageConfig(si, section, index, index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::MoveStageDown(SettingsInterface& si, u32 index)
|
void PostProcessing::Config::MoveStageDown(SettingsInterface& si, const char* section, u32 index)
|
||||||
{
|
{
|
||||||
const u32 stage_count = GetStageCount(si);
|
const u32 stage_count = GetStageCount(si, section);
|
||||||
if ((index + 1) >= stage_count)
|
if ((index + 1) >= stage_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SwapStageConfig(si, index, index + 1);
|
SwapStageConfig(si, section, index, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::SetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option)
|
void PostProcessing::Config::SetStageOption(SettingsInterface& si, const char* section, u32 index,
|
||||||
|
const ShaderOption& option)
|
||||||
{
|
{
|
||||||
const TinyString section = GetStageConfigSection(index);
|
const TinyString stage_section = GetStageConfigSection(section, index);
|
||||||
si.SetStringValue(section, option.name.c_str(), ValueToString(option.type, option.vector_size, option.value));
|
si.SetStringValue(stage_section, option.name.c_str(), ValueToString(option.type, option.vector_size, option.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::UnsetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option)
|
void PostProcessing::Config::UnsetStageOption(SettingsInterface& si, const char* section, u32 index,
|
||||||
|
const ShaderOption& option)
|
||||||
{
|
{
|
||||||
const TinyString section = GetStageConfigSection(index);
|
const TinyString stage_section = GetStageConfigSection(section, index);
|
||||||
si.DeleteValue(section, option.name.c_str());
|
si.DeleteValue(stage_section, option.name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Config::ClearStages(SettingsInterface& si)
|
void PostProcessing::Config::ClearStages(SettingsInterface& si, const char* section)
|
||||||
{
|
{
|
||||||
const u32 count = GetStageCount(si);
|
const u32 count = GetStageCount(si, section);
|
||||||
for (s32 i = static_cast<s32>(count - 1); i >= 0; i--)
|
for (s32 i = static_cast<s32>(count - 1); i >= 0; i--)
|
||||||
si.ClearSection(GetStageConfigSection(static_cast<u32>(i)));
|
si.ClearSection(GetStageConfigSection(section, static_cast<u32>(i)));
|
||||||
si.SetUIntValue("PostProcessing", "StageCount", 0);
|
si.SetUIntValue(section, "StageCount", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessing::IsActive()
|
PostProcessing::Chain::Chain(const char* section) : m_section(section)
|
||||||
{
|
{
|
||||||
return s_enabled && !s_stages.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessing::IsEnabled()
|
PostProcessing::Chain::~Chain() = default;
|
||||||
|
|
||||||
|
bool PostProcessing::Chain::IsActive() const
|
||||||
{
|
{
|
||||||
return s_enabled;
|
return m_enabled && !m_stages.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::SetEnabled(bool enabled)
|
bool PostProcessing::Chain::IsInternalChain() const
|
||||||
{
|
{
|
||||||
s_enabled = enabled;
|
return (this == &InternalChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Chain::ClearStagesWithError(const Error& error)
|
||||||
|
{
|
||||||
|
std::string msg = error.GetDescription();
|
||||||
|
Host::AddIconOSDMessage(
|
||||||
|
"PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||||
|
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);
|
||||||
|
m_stages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Chain::LoadStages()
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = GetLoadSettingsInterface(m_section);
|
||||||
|
|
||||||
|
m_enabled = si.GetBoolValue(m_section, "Enabled", false);
|
||||||
|
|
||||||
|
const u32 stage_count = Config::GetStageCount(si, m_section);
|
||||||
|
if (stage_count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
HostInterfaceProgressCallback progress;
|
||||||
|
progress.SetProgressRange(stage_count);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < stage_count; i++)
|
||||||
|
{
|
||||||
|
std::string stage_name = Config::GetStageShaderName(si, m_section, i);
|
||||||
|
if (stage_name.empty())
|
||||||
|
{
|
||||||
|
error.SetString(fmt::format("No stage name in stage {}.", i + 1));
|
||||||
|
ClearStagesWithError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
progress.SetFormattedStatusText("Loading shader %s...", stage_name.c_str());
|
||||||
|
|
||||||
|
std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error);
|
||||||
|
if (!shader)
|
||||||
|
{
|
||||||
|
ClearStagesWithError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.lock();
|
||||||
|
shader->LoadOptions(si, GetStageConfigSection(m_section, i));
|
||||||
|
m_stages.push_back(std::move(shader));
|
||||||
|
|
||||||
|
progress.IncrementProgressValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage_count > 0)
|
||||||
|
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
||||||
|
|
||||||
|
// precompile shaders
|
||||||
|
if (!IsInternalChain() && g_gpu_device && g_gpu_device->GetWindowFormat() != GPUTexture::Format::Unknown)
|
||||||
|
{
|
||||||
|
CheckTargets(g_gpu_device->GetWindowFormat(), g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(),
|
||||||
|
&progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Chain::ClearStages()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (stage_count == 0)
|
||||||
|
{
|
||||||
|
m_stages.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
|
||||||
|
m_stages.resize(stage_count);
|
||||||
|
|
||||||
|
HostInterfaceProgressCallback progress;
|
||||||
|
progress.SetProgressRange(stage_count);
|
||||||
|
|
||||||
|
const GPUTexture::Format prev_format = m_target_format;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < stage_count; i++)
|
||||||
|
{
|
||||||
|
std::string stage_name = Config::GetStageShaderName(si, m_section, i);
|
||||||
|
if (stage_name.empty())
|
||||||
|
{
|
||||||
|
error.SetString(fmt::format("No stage name in stage {}.", i + 1));
|
||||||
|
ClearStagesWithError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_stages[i] || stage_name != m_stages[i]->GetName())
|
||||||
|
{
|
||||||
|
if (i < m_stages.size())
|
||||||
|
m_stages[i].reset();
|
||||||
|
|
||||||
|
// Force recompile.
|
||||||
|
m_target_format = GPUTexture::Format::Unknown;
|
||||||
|
|
||||||
|
settings_lock.unlock();
|
||||||
|
|
||||||
|
std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error);
|
||||||
|
if (!shader)
|
||||||
|
{
|
||||||
|
ClearStagesWithError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < m_stages.size())
|
||||||
|
m_stages[i] = std::move(shader);
|
||||||
|
else
|
||||||
|
m_stages.push_back(std::move(shader));
|
||||||
|
|
||||||
|
settings_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stages[i]->LoadOptions(si, GetStageConfigSection(m_section, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_format != GPUTexture::Format::Unknown)
|
||||||
|
CheckTargets(prev_format, m_target_width, m_target_height, &progress);
|
||||||
|
|
||||||
|
if (stage_count > 0)
|
||||||
|
{
|
||||||
|
s_timer.Reset();
|
||||||
|
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Chain::Toggle()
|
||||||
|
{
|
||||||
|
if (m_stages.empty())
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
||||||
|
TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."),
|
||||||
|
Host::OSD_QUICK_DURATION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool new_enabled = !m_enabled;
|
||||||
|
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
||||||
|
new_enabled ? TRANSLATE_STR("OSDMessage", "Post-processing is now enabled.") :
|
||||||
|
TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."),
|
||||||
|
Host::OSD_QUICK_DURATION);
|
||||||
|
m_enabled = new_enabled;
|
||||||
|
if (m_enabled)
|
||||||
|
s_timer.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::Chain::CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height,
|
||||||
|
ProgressCallback* progress)
|
||||||
|
{
|
||||||
|
if (m_target_format == target_format && m_target_width == target_width && m_target_height == target_height)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// In case any allocs fail.
|
||||||
|
DestroyTextures();
|
||||||
|
|
||||||
|
if (!(m_input_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1,
|
||||||
|
GPUTexture::Type::RenderTarget, target_format)) ||
|
||||||
|
!(m_output_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1,
|
||||||
|
GPUTexture::Type::RenderTarget, target_format)))
|
||||||
|
{
|
||||||
|
DestroyTextures();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!progress)
|
||||||
|
progress = ProgressCallback::NullProgressCallback;
|
||||||
|
|
||||||
|
progress->SetProgressRange(static_cast<u32>(m_stages.size()));
|
||||||
|
progress->SetProgressValue(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_stages.size(); i++)
|
||||||
|
{
|
||||||
|
Shader* const shader = m_stages[i].get();
|
||||||
|
|
||||||
|
progress->SetFormattedStatusText("Compiling %s...", shader->GetName().c_str());
|
||||||
|
|
||||||
|
if (!shader->CompilePipeline(target_format, target_width, target_height, progress) ||
|
||||||
|
!shader->ResizeOutput(target_format, target_width, target_height))
|
||||||
|
{
|
||||||
|
ERROR_LOG("Failed to compile one or more post-processing shaders, disabling.");
|
||||||
|
Host::AddIconOSDMessage(
|
||||||
|
"PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||||
|
fmt::format("Failed to compile post-processing shader '{}'. Disabling post-processing.", shader->GetName()));
|
||||||
|
m_enabled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->SetProgressValue(static_cast<u32>(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_target_format = target_format;
|
||||||
|
m_target_width = target_width;
|
||||||
|
m_target_height = target_height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Chain::DestroyTextures()
|
||||||
|
{
|
||||||
|
m_target_format = GPUTexture::Format::Unknown;
|
||||||
|
m_target_width = 0;
|
||||||
|
m_target_height = 0;
|
||||||
|
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_output_texture));
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_input_texture));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* final_target, s32 final_left, s32 final_top,
|
||||||
|
s32 final_width, s32 final_height, s32 orig_width, s32 orig_height, s32 native_width,
|
||||||
|
s32 native_height)
|
||||||
|
{
|
||||||
|
GL_SCOPE_FMT("{} Apply", m_section);
|
||||||
|
|
||||||
|
GPUTexture* output = m_output_texture.get();
|
||||||
|
input_color->MakeReadyForSampling();
|
||||||
|
|
||||||
|
for (const std::unique_ptr<Shader>& stage : m_stages)
|
||||||
|
{
|
||||||
|
const bool is_final = (stage.get() == m_stages.back().get());
|
||||||
|
|
||||||
|
if (!stage->Apply(input_color, is_final ? final_target : output, final_left, final_top, final_width, final_height,
|
||||||
|
orig_width, orig_height, native_width, native_height, m_target_width, m_target_height))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_final)
|
||||||
|
{
|
||||||
|
output->MakeReadyForSampling();
|
||||||
|
input_color = output;
|
||||||
|
output = (output == m_output_texture.get()) ? m_input_texture.get() : m_output_texture.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Initialize()
|
||||||
|
{
|
||||||
|
DisplayChain.LoadStages();
|
||||||
|
InternalChain.LoadStages();
|
||||||
|
s_timer.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::UpdateSettings()
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
ForAllChains([&lock](Chain& chain) { chain.UpdateSettings(lock); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Shutdown()
|
||||||
|
{
|
||||||
|
g_gpu_device->RecycleTexture(std::move(s_dummy_texture));
|
||||||
|
s_samplers.clear();
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
s_timer.Reset();
|
||||||
|
|
||||||
|
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,
|
std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name,
|
||||||
|
@ -429,214 +724,18 @@ std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const s
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::ClearStagesWithError(const Error& error)
|
SettingsInterface& PostProcessing::GetLoadSettingsInterface(const char* section)
|
||||||
{
|
|
||||||
std::string msg = error.GetDescription();
|
|
||||||
Host::AddIconOSDMessage(
|
|
||||||
"PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE,
|
|
||||||
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);
|
|
||||||
s_stages.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsInterface& PostProcessing::GetLoadSettingsInterface()
|
|
||||||
{
|
{
|
||||||
// If PostProcessing/Enable is set in the game settings interface, use that.
|
// If PostProcessing/Enable is set in the game settings interface, use that.
|
||||||
// Otherwise, use the base settings.
|
// Otherwise, use the base settings.
|
||||||
|
|
||||||
SettingsInterface* game_si = Host::Internal::GetGameSettingsLayer();
|
SettingsInterface* game_si = Host::Internal::GetGameSettingsLayer();
|
||||||
if (game_si && game_si->ContainsValue("PostProcessing", "Enabled"))
|
if (game_si && game_si->ContainsValue(section, "Enabled"))
|
||||||
return *game_si;
|
return *game_si;
|
||||||
else
|
else
|
||||||
return *Host::Internal::GetBaseSettingsLayer();
|
return *Host::Internal::GetBaseSettingsLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::Initialize()
|
|
||||||
{
|
|
||||||
LoadStages();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessing::LoadStages()
|
|
||||||
{
|
|
||||||
auto lock = Host::GetSettingsLock();
|
|
||||||
SettingsInterface& si = GetLoadSettingsInterface();
|
|
||||||
|
|
||||||
s_enabled = si.GetBoolValue("PostProcessing", "Enabled", false);
|
|
||||||
|
|
||||||
const u32 stage_count = Config::GetStageCount(si);
|
|
||||||
if (stage_count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Error error;
|
|
||||||
HostInterfaceProgressCallback progress;
|
|
||||||
progress.SetProgressRange(stage_count);
|
|
||||||
|
|
||||||
for (u32 i = 0; i < stage_count; i++)
|
|
||||||
{
|
|
||||||
std::string stage_name = Config::GetStageShaderName(si, i);
|
|
||||||
if (stage_name.empty())
|
|
||||||
{
|
|
||||||
error.SetString(fmt::format("No stage name in stage {}.", i + 1));
|
|
||||||
ClearStagesWithError(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
progress.SetFormattedStatusText("Loading shader %s...", stage_name.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error);
|
|
||||||
if (!shader)
|
|
||||||
{
|
|
||||||
ClearStagesWithError(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
shader->LoadOptions(si, GetStageConfigSection(i));
|
|
||||||
s_stages.push_back(std::move(shader));
|
|
||||||
|
|
||||||
progress.IncrementProgressValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stage_count > 0)
|
|
||||||
{
|
|
||||||
s_timer.Reset();
|
|
||||||
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// precompile shaders
|
|
||||||
if (g_gpu_device && g_gpu_device->GetWindowFormat() != GPUTexture::Format::Unknown)
|
|
||||||
{
|
|
||||||
CheckTargets(g_gpu_device->GetWindowFormat(), g_gpu_device->GetWindowWidth(), g_gpu_device->GetWindowHeight(),
|
|
||||||
&progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessing::UpdateSettings()
|
|
||||||
{
|
|
||||||
auto lock = Host::GetSettingsLock();
|
|
||||||
SettingsInterface& si = GetLoadSettingsInterface();
|
|
||||||
|
|
||||||
s_enabled = si.GetBoolValue("PostProcessing", "Enabled", false);
|
|
||||||
|
|
||||||
const u32 stage_count = Config::GetStageCount(si);
|
|
||||||
if (stage_count == 0)
|
|
||||||
{
|
|
||||||
s_stages.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error error;
|
|
||||||
|
|
||||||
s_stages.resize(stage_count);
|
|
||||||
|
|
||||||
HostInterfaceProgressCallback progress;
|
|
||||||
progress.SetProgressRange(stage_count);
|
|
||||||
|
|
||||||
const GPUTexture::Format prev_format = s_target_format;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < stage_count; i++)
|
|
||||||
{
|
|
||||||
std::string stage_name = Config::GetStageShaderName(si, i);
|
|
||||||
if (stage_name.empty())
|
|
||||||
{
|
|
||||||
error.SetString(fmt::format("No stage name in stage {}.", i + 1));
|
|
||||||
ClearStagesWithError(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s_stages[i] || stage_name != s_stages[i]->GetName())
|
|
||||||
{
|
|
||||||
if (i < s_stages.size())
|
|
||||||
s_stages[i].reset();
|
|
||||||
|
|
||||||
// Force recompile.
|
|
||||||
s_target_format = GPUTexture::Format::Unknown;
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
std::unique_ptr<Shader> shader = TryLoadingShader(stage_name, false, &error);
|
|
||||||
if (!shader)
|
|
||||||
{
|
|
||||||
ClearStagesWithError(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < s_stages.size())
|
|
||||||
s_stages[i] = std::move(shader);
|
|
||||||
else
|
|
||||||
s_stages.push_back(std::move(shader));
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
s_stages[i]->LoadOptions(si, GetStageConfigSection(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev_format != GPUTexture::Format::Unknown)
|
|
||||||
CheckTargets(prev_format, s_target_width, s_target_height, &progress);
|
|
||||||
|
|
||||||
if (stage_count > 0)
|
|
||||||
{
|
|
||||||
s_timer.Reset();
|
|
||||||
DEV_LOG("Loaded {} post-processing stages.", stage_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessing::Toggle()
|
|
||||||
{
|
|
||||||
if (s_stages.empty())
|
|
||||||
{
|
|
||||||
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
|
||||||
TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."),
|
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool new_enabled = !s_enabled;
|
|
||||||
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
|
||||||
new_enabled ? TRANSLATE_STR("OSDMessage", "Post-processing is now enabled.") :
|
|
||||||
TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."),
|
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
s_enabled = new_enabled;
|
|
||||||
if (s_enabled)
|
|
||||||
s_timer.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessing::ReloadShaders()
|
|
||||||
{
|
|
||||||
if (s_stages.empty())
|
|
||||||
{
|
|
||||||
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
|
||||||
TRANSLATE_STR("OSDMessage", "No post-processing shaders are selected."),
|
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
decltype(s_stages)().swap(s_stages);
|
|
||||||
DestroyTextures();
|
|
||||||
LoadStages();
|
|
||||||
|
|
||||||
Host::AddIconOSDMessage("PostProcessing", ICON_FA_PAINT_ROLLER,
|
|
||||||
TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), Host::OSD_QUICK_DURATION);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessing::Shutdown()
|
|
||||||
{
|
|
||||||
g_gpu_device->RecycleTexture(std::move(s_dummy_texture));
|
|
||||||
s_samplers.clear();
|
|
||||||
s_enabled = false;
|
|
||||||
decltype(s_stages)().swap(s_stages);
|
|
||||||
DestroyTextures();
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUTexture* PostProcessing::GetInputTexture()
|
|
||||||
{
|
|
||||||
return s_input_texture.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Common::Timer& PostProcessing::GetTimer()
|
const Common::Timer& PostProcessing::GetTimer()
|
||||||
{
|
{
|
||||||
return s_timer;
|
return s_timer;
|
||||||
|
@ -669,92 +768,3 @@ GPUTexture* PostProcessing::GetDummyTexture()
|
||||||
|
|
||||||
return s_dummy_texture.get();
|
return s_dummy_texture.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessing::CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height,
|
|
||||||
ProgressCallback* progress)
|
|
||||||
{
|
|
||||||
if (s_target_format == target_format && s_target_width == target_width && s_target_height == target_height)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// In case any allocs fail.
|
|
||||||
DestroyTextures();
|
|
||||||
|
|
||||||
if (!(s_input_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1,
|
|
||||||
GPUTexture::Type::RenderTarget, target_format)) ||
|
|
||||||
!(s_output_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1,
|
|
||||||
GPUTexture::Type::RenderTarget, target_format)))
|
|
||||||
{
|
|
||||||
DestroyTextures();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!progress)
|
|
||||||
progress = ProgressCallback::NullProgressCallback;
|
|
||||||
|
|
||||||
progress->SetProgressRange(static_cast<u32>(s_stages.size()));
|
|
||||||
progress->SetProgressValue(0);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < s_stages.size(); i++)
|
|
||||||
{
|
|
||||||
Shader* const shader = s_stages[i].get();
|
|
||||||
|
|
||||||
progress->SetFormattedStatusText("Compiling %s...", shader->GetName().c_str());
|
|
||||||
|
|
||||||
if (!shader->CompilePipeline(target_format, target_width, target_height, progress) ||
|
|
||||||
!shader->ResizeOutput(target_format, target_width, target_height))
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to compile one or more post-processing shaders, disabling.");
|
|
||||||
Host::AddIconOSDMessage(
|
|
||||||
"PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE,
|
|
||||||
fmt::format("Failed to compile post-processing shader '{}'. Disabling post-processing.", shader->GetName()));
|
|
||||||
s_enabled = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress->SetProgressValue(static_cast<u32>(i + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
s_target_format = target_format;
|
|
||||||
s_target_width = target_width;
|
|
||||||
s_target_height = target_height;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessing::DestroyTextures()
|
|
||||||
{
|
|
||||||
s_target_format = GPUTexture::Format::Unknown;
|
|
||||||
s_target_width = 0;
|
|
||||||
s_target_height = 0;
|
|
||||||
|
|
||||||
g_gpu_device->RecycleTexture(std::move(s_output_texture));
|
|
||||||
g_gpu_device->RecycleTexture(std::move(s_input_texture));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessing::Apply(GPUTexture* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
|
||||||
s32 orig_width, s32 orig_height, s32 native_width, s32 native_height)
|
|
||||||
{
|
|
||||||
GL_SCOPE("PostProcessing Apply");
|
|
||||||
|
|
||||||
GPUTexture* input = s_input_texture.get();
|
|
||||||
GPUTexture* output = s_output_texture.get();
|
|
||||||
input->MakeReadyForSampling();
|
|
||||||
|
|
||||||
for (const std::unique_ptr<Shader>& stage : s_stages)
|
|
||||||
{
|
|
||||||
const bool is_final = (stage.get() == s_stages.back().get());
|
|
||||||
|
|
||||||
if (!stage->Apply(input, is_final ? final_target : output, final_left, final_top, final_width, final_height,
|
|
||||||
orig_width, orig_height, native_width, native_height, s_target_width, s_target_height))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_final)
|
|
||||||
{
|
|
||||||
output->MakeReadyForSampling();
|
|
||||||
std::swap(input, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -22,6 +23,8 @@ class SettingsInterface;
|
||||||
class ProgressCallback;
|
class ProgressCallback;
|
||||||
|
|
||||||
namespace PostProcessing {
|
namespace PostProcessing {
|
||||||
|
class Shader;
|
||||||
|
|
||||||
struct ShaderOption
|
struct ShaderOption
|
||||||
{
|
{
|
||||||
enum : u32
|
enum : u32
|
||||||
|
@ -87,51 +90,86 @@ struct ShaderOption
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// [display_name, filename]
|
|
||||||
std::vector<std::pair<std::string, std::string>> GetAvailableShaderNames();
|
|
||||||
|
|
||||||
namespace Config {
|
namespace Config {
|
||||||
u32 GetStageCount(const SettingsInterface& si);
|
static constexpr const char* DISPLAY_CHAIN_SECTION = "PostProcessing";
|
||||||
std::string GetStageShaderName(const SettingsInterface& si, u32 index);
|
static constexpr const char* INTERNAL_CHAIN_SECTION = "InternalPostProcessing";
|
||||||
std::vector<ShaderOption> GetStageOptions(const SettingsInterface& si, u32 index);
|
|
||||||
|
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);
|
||||||
std::vector<ShaderOption> GetShaderOptions(const std::string& shader_name, Error* error);
|
std::vector<ShaderOption> GetShaderOptions(const std::string& shader_name, Error* error);
|
||||||
|
|
||||||
bool AddStage(SettingsInterface& si, const std::string& shader_name, Error* error);
|
bool AddStage(SettingsInterface& si, const char* section, const std::string& shader_name, Error* error);
|
||||||
void RemoveStage(SettingsInterface& si, u32 index);
|
void RemoveStage(SettingsInterface& si, const char* section, u32 index);
|
||||||
void MoveStageUp(SettingsInterface& si, u32 index);
|
void MoveStageUp(SettingsInterface& si, const char* section, u32 index);
|
||||||
void MoveStageDown(SettingsInterface& si, u32 index);
|
void MoveStageDown(SettingsInterface& si, const char* section, u32 index);
|
||||||
void SetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option);
|
void SetStageOption(SettingsInterface& si, const char* section, u32 index, const ShaderOption& option);
|
||||||
void UnsetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option);
|
void UnsetStageOption(SettingsInterface& si, const char* section, u32 index, const ShaderOption& option);
|
||||||
void ClearStages(SettingsInterface& si);
|
void ClearStages(SettingsInterface& si, const char* section);
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
||||||
bool IsActive();
|
class Chain
|
||||||
bool IsEnabled();
|
{
|
||||||
void SetEnabled(bool enabled);
|
public:
|
||||||
|
Chain(const char* section);
|
||||||
|
~Chain();
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool HasStages() const { return m_stages.empty(); }
|
||||||
|
ALWAYS_INLINE GPUTexture* GetInputTexture() const { return m_input_texture.get(); }
|
||||||
|
ALWAYS_INLINE GPUTexture* GetOutputTexture() const { return m_output_texture.get(); }
|
||||||
|
|
||||||
|
bool IsActive() const;
|
||||||
|
bool IsInternalChain() const;
|
||||||
|
|
||||||
|
void UpdateSettings(std::unique_lock<std::mutex>& settings_lock);
|
||||||
|
|
||||||
|
void LoadStages();
|
||||||
|
void ClearStages();
|
||||||
|
void DestroyTextures();
|
||||||
|
|
||||||
|
/// Temporarily toggles post-processing on/off.
|
||||||
|
void Toggle();
|
||||||
|
|
||||||
|
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height,
|
||||||
|
ProgressCallback* progress = nullptr);
|
||||||
|
|
||||||
|
bool Apply(GPUTexture* input_color, GPUTexture* final_target, s32 final_left, s32 final_top, s32 final_width,
|
||||||
|
s32 final_height, s32 orig_width, s32 orig_height, s32 native_width, s32 native_height);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ClearStagesWithError(const Error& error);
|
||||||
|
|
||||||
|
const char* m_section;
|
||||||
|
|
||||||
|
GPUTexture::Format m_target_format = GPUTexture::Format::Unknown;
|
||||||
|
u32 m_target_width = 0;
|
||||||
|
u32 m_target_height = 0;
|
||||||
|
bool m_enabled = false;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<PostProcessing::Shader>> m_stages;
|
||||||
|
std::unique_ptr<GPUTexture> m_input_texture;
|
||||||
|
std::unique_ptr<GPUTexture> m_output_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
// [display_name, filename]
|
||||||
|
std::vector<std::pair<std::string, std::string>> GetAvailableShaderNames();
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
/// Reloads configuration.
|
/// Reloads configuration.
|
||||||
void UpdateSettings();
|
void UpdateSettings();
|
||||||
|
|
||||||
/// Temporarily toggles post-processing on/off.
|
|
||||||
void Toggle();
|
|
||||||
|
|
||||||
/// Reloads post processing shaders with the current configuration.
|
/// Reloads post processing shaders with the current configuration.
|
||||||
bool ReloadShaders();
|
bool ReloadShaders();
|
||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
GPUTexture* GetInputTexture();
|
|
||||||
const Common::Timer& GetTimer();
|
|
||||||
|
|
||||||
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height,
|
|
||||||
ProgressCallback* progress = nullptr);
|
|
||||||
|
|
||||||
bool Apply(GPUTexture* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height, s32 orig_width,
|
|
||||||
s32 orig_height, s32 native_width, s32 native_height);
|
|
||||||
|
|
||||||
GPUSampler* GetSampler(const GPUSampler::Config& config);
|
GPUSampler* GetSampler(const GPUSampler::Config& config);
|
||||||
GPUTexture* GetDummyTexture();
|
GPUTexture* GetDummyTexture();
|
||||||
|
|
||||||
|
const Common::Timer& GetTimer();
|
||||||
|
|
||||||
|
extern Chain DisplayChain;
|
||||||
|
extern Chain InternalChain;
|
||||||
|
|
||||||
}; // namespace PostProcessing
|
}; // namespace PostProcessing
|
||||||
|
|
Loading…
Reference in a new issue