PostProcessing: Refactor config to use separate sections
This commit is contained in:
parent
8db8baf33f
commit
b217f64bcf
|
@ -743,9 +743,9 @@ s64 FileSystem::GetPathFileSize(const char* Path)
|
||||||
return sd.Size;
|
return sd.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::vector<u8>> FileSystem::ReadBinaryFile(const char* filename)
|
std::optional<std::vector<u8>> FileSystem::ReadBinaryFile(const char* filename, Error* error)
|
||||||
{
|
{
|
||||||
ManagedCFilePtr fp = OpenManagedCFile(filename, "rb");
|
ManagedCFilePtr fp = OpenManagedCFile(filename, "rb", error);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
@ -767,9 +767,9 @@ std::optional<std::vector<u8>> FileSystem::ReadBinaryFile(std::FILE* fp)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> FileSystem::ReadFileToString(const char* filename)
|
std::optional<std::string> FileSystem::ReadFileToString(const char* filename, Error* error)
|
||||||
{
|
{
|
||||||
ManagedCFilePtr fp = OpenManagedCFile(filename, "rb");
|
ManagedCFilePtr fp = OpenManagedCFile(filename, "rb", error);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
|
|
@ -140,9 +140,9 @@ private:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::optional<std::vector<u8>> ReadBinaryFile(const char* filename);
|
std::optional<std::vector<u8>> ReadBinaryFile(const char* filename, Error* error = nullptr);
|
||||||
std::optional<std::vector<u8>> ReadBinaryFile(std::FILE* fp);
|
std::optional<std::vector<u8>> ReadBinaryFile(std::FILE* fp);
|
||||||
std::optional<std::string> ReadFileToString(const char* filename);
|
std::optional<std::string> ReadFileToString(const char* filename, Error* error = nullptr);
|
||||||
std::optional<std::string> ReadFileToString(std::FILE* fp);
|
std::optional<std::string> ReadFileToString(std::FILE* fp);
|
||||||
bool WriteBinaryFile(const char* filename, const void* data, size_t data_length);
|
bool WriteBinaryFile(const char* filename, const void* data, size_t data_length);
|
||||||
bool WriteStringToFile(const char* filename, const std::string_view& sv);
|
bool WriteStringToFile(const char* filename, const std::string_view& sv);
|
||||||
|
|
|
@ -190,4 +190,4 @@
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -121,4 +121,4 @@
|
||||||
<ClInclude Include="fullscreen_ui.h" />
|
<ClInclude Include="fullscreen_ui.h" />
|
||||||
<ClInclude Include="shader_cache_version.h" />
|
<ClInclude Include="shader_cache_version.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -24,9 +24,10 @@
|
||||||
#include "util/imgui_manager.h"
|
#include "util/imgui_manager.h"
|
||||||
#include "util/ini_settings_interface.h"
|
#include "util/ini_settings_interface.h"
|
||||||
#include "util/input_manager.h"
|
#include "util/input_manager.h"
|
||||||
#include "util/postprocessing_chain.h"
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
#include "common/byte_stream.h"
|
#include "common/byte_stream.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/make_array.h"
|
#include "common/make_array.h"
|
||||||
|
@ -201,6 +202,12 @@ enum class GameListPage
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PostProcessingStageInfo
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<PostProcessing::ShaderOption> options;
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Utility
|
// Utility
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -382,8 +389,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();
|
static void PopulatePostProcessingChain(SettingsInterface* si);
|
||||||
static void SavePostProcessingChain();
|
|
||||||
static void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const std::string_view& section,
|
static void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const std::string_view& section,
|
||||||
const std::string_view& key, const std::string_view& display_name);
|
const std::string_view& key, const std::string_view& display_name);
|
||||||
static void DrawInputBindingWindow();
|
static void DrawInputBindingWindow();
|
||||||
|
@ -398,7 +404,7 @@ static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
||||||
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
||||||
static std::vector<std::string> s_graphics_adapter_list_cache;
|
static std::vector<std::string> s_graphics_adapter_list_cache;
|
||||||
static std::vector<std::string> s_fullscreen_mode_list_cache;
|
static std::vector<std::string> s_fullscreen_mode_list_cache;
|
||||||
static PostProcessingChain s_postprocessing_chain;
|
static std::vector<PostProcessingStageInfo> s_postprocessing_stages;
|
||||||
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||||
static std::atomic_bool s_settings_changed{false};
|
static std::atomic_bool s_settings_changed{false};
|
||||||
static std::atomic_bool s_game_settings_changed{false};
|
static std::atomic_bool s_game_settings_changed{false};
|
||||||
|
@ -741,7 +747,7 @@ void FullscreenUI::Shutdown()
|
||||||
s_cover_image_map.clear();
|
s_cover_image_map.clear();
|
||||||
s_game_list_sorted_entries = {};
|
s_game_list_sorted_entries = {};
|
||||||
s_game_list_directories_cache = {};
|
s_game_list_directories_cache = {};
|
||||||
s_postprocessing_chain.ClearStages();
|
s_postprocessing_stages = {};
|
||||||
s_fullscreen_mode_list_cache = {};
|
s_fullscreen_mode_list_cache = {};
|
||||||
s_graphics_adapter_list_cache = {};
|
s_graphics_adapter_list_cache = {};
|
||||||
s_hotkey_list_cache = {};
|
s_hotkey_list_cache = {};
|
||||||
|
@ -2386,7 +2392,7 @@ void FullscreenUI::SwitchToSettings()
|
||||||
s_game_settings_interface.reset();
|
s_game_settings_interface.reset();
|
||||||
|
|
||||||
PopulateGraphicsAdapterList();
|
PopulateGraphicsAdapterList();
|
||||||
PopulatePostProcessingChain();
|
PopulatePostProcessingChain(GetEditingSettingsInterface());
|
||||||
|
|
||||||
s_current_main_window = MainWindowType::Settings;
|
s_current_main_window = MainWindowType::Settings;
|
||||||
s_settings_page = SettingsPage::Interface;
|
s_settings_page = SettingsPage::Interface;
|
||||||
|
@ -3967,18 +3973,17 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
||||||
EndMenuButtons();
|
EndMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::PopulatePostProcessingChain()
|
void FullscreenUI::PopulatePostProcessingChain(SettingsInterface* si)
|
||||||
{
|
{
|
||||||
std::string chain_value(GetEditingSettingsInterface()->GetStringValue("Display", "PostProcessChain", ""));
|
const u32 stages = PostProcessing::Config::GetStageCount(*si);
|
||||||
s_postprocessing_chain.CreateFromString(chain_value);
|
s_postprocessing_stages.reserve(stages);
|
||||||
}
|
for (u32 i = 0; i < stages; i++)
|
||||||
|
{
|
||||||
void FullscreenUI::SavePostProcessingChain()
|
PostProcessingStageInfo psi;
|
||||||
{
|
psi.name = PostProcessing::Config::GetStageShaderName(*si, i);
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
psi.options = PostProcessing::Config::GetStageOptions(*si, i);
|
||||||
const std::string config(s_postprocessing_chain.GetConfigString());
|
s_postprocessing_stages.push_back(std::move(psi));
|
||||||
bsi->SetStringValue("Display", "PostProcessChain", config.c_str());
|
}
|
||||||
SetSettingsChanged(bsi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -4005,7 +4010,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
FSUI_CSTR("Reloads the shaders from disk, applying any changes."),
|
FSUI_CSTR("Reloads the shaders from disk, applying any changes."),
|
||||||
bsi->GetBoolValue("Display", "PostProcessing", false)))
|
bsi->GetBoolValue("Display", "PostProcessing", false)))
|
||||||
{
|
{
|
||||||
if (!g_gpu || g_gpu->UpdatePostProcessingChain())
|
if (System::IsValid() && PostProcessing::ReloadShaders())
|
||||||
ShowToast(std::string(), FSUI_STR("Post-processing shaders reloaded."));
|
ShowToast(std::string(), FSUI_STR("Post-processing shaders reloaded."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4013,25 +4018,32 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
|
|
||||||
if (MenuButton(FSUI_ICONSTR(ICON_FA_PLUS, "Add Shader"), FSUI_CSTR("Adds a new shader to the chain.")))
|
if (MenuButton(FSUI_ICONSTR(ICON_FA_PLUS, "Add Shader"), FSUI_CSTR("Adds a new shader to the chain.")))
|
||||||
{
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> shaders = PostProcessing::GetAvailableShaderNames();
|
||||||
ImGuiFullscreen::ChoiceDialogOptions options;
|
ImGuiFullscreen::ChoiceDialogOptions options;
|
||||||
for (std::string& name : PostProcessingChain::GetAvailableShaderNames())
|
options.reserve(shaders.size());
|
||||||
options.emplace_back(std::move(name), false);
|
for (auto& [display_name, name] : shaders)
|
||||||
|
options.emplace_back(std::move(display_name), false);
|
||||||
|
|
||||||
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_PLUS, "Add Shader"), false, std::move(options),
|
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_PLUS, "Add Shader"), false, std::move(options),
|
||||||
[](s32 index, const std::string& title, bool checked) {
|
[shaders = std::move(shaders)](s32 index, const std::string& title, bool checked) {
|
||||||
if (index < 0)
|
if (index < 0 || static_cast<u32>(index) >= shaders.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (s_postprocessing_chain.AddStage(title))
|
const std::string& shader_name = shaders[index].second;
|
||||||
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
Error error;
|
||||||
|
if (PostProcessing::Config::AddStage(*bsi, 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,
|
||||||
s_postprocessing_chain.GetStageCount()));
|
PostProcessing::Config::GetStageCount(*bsi)));
|
||||||
SavePostProcessingChain();
|
PopulatePostProcessingChain(bsi);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ShowToast(std::string(),
|
ShowToast(std::string(),
|
||||||
fmt::format(FSUI_FSTR("Failed to load shader {}. It may be invalid."), title));
|
fmt::format(FSUI_FSTR("Failed to load shader {}. It may be invalid.\nError was:"),
|
||||||
|
title, error.GetDescription()));
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseChoiceDialog();
|
CloseChoiceDialog();
|
||||||
|
@ -4047,9 +4059,11 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
if (!confirmed)
|
if (!confirmed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s_postprocessing_chain.ClearStages();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
PostProcessing::Config::ClearStages(*bsi);
|
||||||
|
PopulatePostProcessingChain(bsi);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
ShowToast(std::string(), FSUI_STR("Post-processing chain cleared."));
|
ShowToast(std::string(), FSUI_STR("Post-processing chain cleared."));
|
||||||
SavePostProcessingChain();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4058,11 +4072,12 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
|
|
||||||
SmallString str;
|
SmallString str;
|
||||||
SmallString tstr;
|
SmallString tstr;
|
||||||
for (u32 stage_index = 0; stage_index < s_postprocessing_chain.GetStageCount(); stage_index++)
|
for (u32 stage_index = 0; stage_index < static_cast<u32>(s_postprocessing_stages.size()); stage_index++)
|
||||||
{
|
{
|
||||||
|
PostProcessingStageInfo& si = s_postprocessing_stages[stage_index];
|
||||||
|
|
||||||
ImGui::PushID(stage_index);
|
ImGui::PushID(stage_index);
|
||||||
PostProcessingShader* stage = s_postprocessing_chain.GetShaderStage(stage_index);
|
str.Fmt(FSUI_FSTR("Stage {}: {}"), stage_index + 1, si.name);
|
||||||
str.Fmt(FSUI_FSTR("Stage {}: {}"), stage_index + 1, stage->GetName());
|
|
||||||
MenuHeading(str);
|
MenuHeading(str);
|
||||||
|
|
||||||
if (MenuButton(FSUI_ICONSTR(ICON_FA_TIMES, "Remove From Chain"), FSUI_CSTR("Removes this shader from the chain.")))
|
if (MenuButton(FSUI_ICONSTR(ICON_FA_TIMES, "Remove From Chain"), FSUI_CSTR("Removes this shader from the chain.")))
|
||||||
|
@ -4080,17 +4095,20 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
|
|
||||||
if (MenuButton(FSUI_ICONSTR(ICON_FA_ARROW_DOWN, "Move Down"),
|
if (MenuButton(FSUI_ICONSTR(ICON_FA_ARROW_DOWN, "Move Down"),
|
||||||
FSUI_CSTR("Moves this shader lower in the chain, applying it later."),
|
FSUI_CSTR("Moves this shader lower in the chain, applying it later."),
|
||||||
(stage_index != (s_postprocessing_chain.GetStageCount() - 1))))
|
(stage_index != (s_postprocessing_stages.size() - 1))))
|
||||||
{
|
{
|
||||||
postprocessing_action = POSTPROCESSING_ACTION_MOVE_DOWN;
|
postprocessing_action = POSTPROCESSING_ACTION_MOVE_DOWN;
|
||||||
postprocessing_action_index = stage_index;
|
postprocessing_action_index = stage_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PostProcessingShader::Option& opt : stage->GetOptions())
|
for (PostProcessing::ShaderOption& opt : si.options)
|
||||||
{
|
{
|
||||||
|
if (opt.ui_name.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
switch (opt.type)
|
switch (opt.type)
|
||||||
{
|
{
|
||||||
case PostProcessingShader::Option::Type::Bool:
|
case PostProcessing::ShaderOption::Type::Bool:
|
||||||
{
|
{
|
||||||
bool value = (opt.value[0].int_value != 0);
|
bool value = (opt.value[0].int_value != 0);
|
||||||
tstr.Fmt(ICON_FA_COGS "{}", opt.ui_name);
|
tstr.Fmt(ICON_FA_COGS "{}", opt.ui_name);
|
||||||
|
@ -4100,12 +4118,13 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
&value))
|
&value))
|
||||||
{
|
{
|
||||||
opt.value[0].int_value = (value != 0);
|
opt.value[0].int_value = (value != 0);
|
||||||
SavePostProcessingChain();
|
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PostProcessingShader::Option::Type::Float:
|
case PostProcessing::ShaderOption::Type::Float:
|
||||||
{
|
{
|
||||||
tstr.Fmt(ICON_FA_RULER_VERTICAL "{}##{}", opt.ui_name, opt.name);
|
tstr.Fmt(ICON_FA_RULER_VERTICAL "{}##{}", opt.ui_name, opt.name);
|
||||||
str.Fmt(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].float_value,
|
str.Fmt(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].float_value,
|
||||||
|
@ -4148,48 +4167,44 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ImGui::SetNextItemWidth(end);
|
ImGui::SetNextItemWidth(end);
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
switch (opt.vector_size)
|
switch (opt.vector_size)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderFloat("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
changed = ImGui::SliderFloat("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
||||||
opt.max_value[0].float_value))
|
opt.max_value[0].float_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderFloat2("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
changed = ImGui::SliderFloat2("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
||||||
opt.max_value[0].float_value))
|
opt.max_value[0].float_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderFloat3("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
changed = ImGui::SliderFloat3("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
||||||
opt.max_value[0].float_value))
|
opt.max_value[0].float_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderFloat4("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
changed = ImGui::SliderFloat4("##value", &opt.value[0].float_value, opt.min_value[0].float_value,
|
||||||
opt.max_value[0].float_value))
|
opt.max_value[0].float_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||||
|
@ -4208,7 +4223,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PostProcessingShader::Option::Type::Int:
|
case PostProcessing::ShaderOption::Type::Int:
|
||||||
{
|
{
|
||||||
tstr.Fmt(ICON_FA_RULER_VERTICAL "{}##{}", opt.ui_name, opt.name);
|
tstr.Fmt(ICON_FA_RULER_VERTICAL "{}##{}", opt.ui_name, opt.name);
|
||||||
str.Fmt(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].int_value,
|
str.Fmt(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].int_value,
|
||||||
|
@ -4250,49 +4265,44 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
bool changed = false;
|
||||||
ImGui::SetNextItemWidth(end);
|
ImGui::SetNextItemWidth(end);
|
||||||
switch (opt.vector_size)
|
switch (opt.vector_size)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderInt("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
changed = ImGui::SliderInt("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
||||||
opt.max_value[0].int_value))
|
opt.max_value[0].int_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderInt2("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
changed = ImGui::SliderInt2("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
||||||
opt.max_value[0].int_value))
|
opt.max_value[0].int_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderInt2("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
changed = ImGui::SliderInt2("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
||||||
opt.max_value[0].int_value))
|
opt.max_value[0].int_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
if (ImGui::SliderInt4("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
changed = ImGui::SliderInt4("##value", &opt.value[0].int_value, opt.min_value[0].int_value,
|
||||||
opt.max_value[0].int_value))
|
opt.max_value[0].int_value);
|
||||||
{
|
|
||||||
SavePostProcessingChain();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
PostProcessing::Config::SetStageOption(*bsi, stage_index, opt);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
|
||||||
|
@ -4320,23 +4330,26 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
|
||||||
{
|
{
|
||||||
case POSTPROCESSING_ACTION_REMOVE:
|
case POSTPROCESSING_ACTION_REMOVE:
|
||||||
{
|
{
|
||||||
PostProcessingShader* stage = s_postprocessing_chain.GetShaderStage(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, stage->GetName()));
|
fmt::format(FSUI_FSTR("Removed stage {} ({})."), postprocessing_action_index + 1, si.name));
|
||||||
s_postprocessing_chain.RemoveStage(postprocessing_action_index);
|
PostProcessing::Config::RemoveStage(*bsi, postprocessing_action_index);
|
||||||
SavePostProcessingChain();
|
PopulatePostProcessingChain(bsi);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POSTPROCESSING_ACTION_MOVE_UP:
|
case POSTPROCESSING_ACTION_MOVE_UP:
|
||||||
{
|
{
|
||||||
s_postprocessing_chain.MoveStageUp(postprocessing_action_index);
|
PostProcessing::Config::MoveStageUp(*bsi, postprocessing_action_index);
|
||||||
SavePostProcessingChain();
|
PopulatePostProcessingChain(bsi);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POSTPROCESSING_ACTION_MOVE_DOWN:
|
case POSTPROCESSING_ACTION_MOVE_DOWN:
|
||||||
{
|
{
|
||||||
s_postprocessing_chain.MoveStageDown(postprocessing_action_index);
|
PostProcessing::Config::MoveStageDown(*bsi, postprocessing_action_index);
|
||||||
SavePostProcessingChain();
|
PopulatePostProcessingChain(bsi);
|
||||||
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include "util/gpu_device.h"
|
#include "util/gpu_device.h"
|
||||||
#include "util/imgui_manager.h"
|
#include "util/imgui_manager.h"
|
||||||
#include "util/postprocessing_chain.h"
|
#include "util/postprocessing.h"
|
||||||
#include "util/shadergen.h"
|
#include "util/shadergen.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
|
||||||
|
@ -65,8 +65,6 @@ bool GPU::Initialize()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdatePostProcessingChain();
|
|
||||||
|
|
||||||
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu);
|
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1637,12 +1635,12 @@ bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& dr
|
||||||
(target && target->GetRT()) ? target->GetRT()->GetFormat() : g_gpu_device->GetWindowFormat();
|
(target && target->GetRT()) ? target->GetRT()->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 && HasDisplayTexture() && m_post_processing_chain &&
|
const bool really_postfx = (postfx && HasDisplayTexture() && PostProcessing::IsActive() &&
|
||||||
m_post_processing_chain->CheckTargets(hdformat, target_width, target_height));
|
PostProcessing::CheckTargets(hdformat, target_width, target_height));
|
||||||
if (really_postfx)
|
if (really_postfx)
|
||||||
{
|
{
|
||||||
g_gpu_device->ClearRenderTarget(m_post_processing_chain->GetInputTexture(), 0);
|
g_gpu_device->ClearRenderTarget(PostProcessing::GetInputTexture(), 0);
|
||||||
g_gpu_device->SetFramebuffer(m_post_processing_chain->GetInputFramebuffer());
|
g_gpu_device->SetFramebuffer(PostProcessing::GetInputFramebuffer());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1676,7 +1674,7 @@ bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& dr
|
||||||
|
|
||||||
if (really_postfx)
|
if (really_postfx)
|
||||||
{
|
{
|
||||||
return m_post_processing_chain->Apply(target, draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
|
return PostProcessing::Apply(target, draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
|
||||||
draw_rect.GetHeight(), m_display_texture_view_width,
|
draw_rect.GetHeight(), m_display_texture_view_width,
|
||||||
m_display_texture_view_height);
|
m_display_texture_view_height);
|
||||||
}
|
}
|
||||||
|
@ -1686,25 +1684,6 @@ bool GPU::RenderDisplay(GPUFramebuffer* target, const Common::Rectangle<s32>& dr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::UpdatePostProcessingChain()
|
|
||||||
{
|
|
||||||
if (!g_settings.display_post_processing || g_settings.display_post_process_chain.empty())
|
|
||||||
{
|
|
||||||
m_post_processing_chain.reset();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_post_processing_chain = std::make_unique<PostProcessingChain>();
|
|
||||||
if (!m_post_processing_chain->CreateFromString(g_settings.display_post_process_chain))
|
|
||||||
{
|
|
||||||
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
|
|
||||||
m_post_processing_chain.reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
|
Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
|
||||||
float* out_top_padding, float* out_scale, float* out_x_scale,
|
float* out_top_padding, float* out_scale, float* out_x_scale,
|
||||||
bool apply_aspect_ratio /* = true */) const
|
bool apply_aspect_ratio /* = true */) const
|
||||||
|
|
|
@ -27,8 +27,6 @@ class GPUFramebuffer;
|
||||||
class GPUTexture;
|
class GPUTexture;
|
||||||
class GPUPipeline;
|
class GPUPipeline;
|
||||||
|
|
||||||
class PostProcessingChain;
|
|
||||||
|
|
||||||
class TimingEvent;
|
class TimingEvent;
|
||||||
|
|
||||||
namespace Threading {
|
namespace Threading {
|
||||||
|
@ -207,8 +205,6 @@ public:
|
||||||
/// Draws the current display texture, with any post-processing.
|
/// Draws the current display texture, with any post-processing.
|
||||||
bool PresentDisplay();
|
bool PresentDisplay();
|
||||||
|
|
||||||
bool UpdatePostProcessingChain();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TickCount CRTCTicksToSystemTicks(TickCount crtc_ticks, TickCount fractional_ticks) const;
|
TickCount CRTCTicksToSystemTicks(TickCount crtc_ticks, TickCount fractional_ticks) const;
|
||||||
TickCount SystemTicksToCRTCTicks(TickCount sysclk_ticks, TickCount* fractional_ticks) const;
|
TickCount SystemTicksToCRTCTicks(TickCount sysclk_ticks, TickCount* fractional_ticks) const;
|
||||||
|
@ -603,8 +599,6 @@ protected:
|
||||||
s32 m_display_texture_view_width = 0;
|
s32 m_display_texture_view_width = 0;
|
||||||
s32 m_display_texture_view_height = 0;
|
s32 m_display_texture_view_height = 0;
|
||||||
|
|
||||||
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
|
||||||
|
|
||||||
struct Stats
|
struct Stats
|
||||||
{
|
{
|
||||||
u32 num_vram_reads;
|
u32 num_vram_reads;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "util/gpu_device.h"
|
#include "util/gpu_device.h"
|
||||||
#include "util/input_manager.h"
|
#include "util/input_manager.h"
|
||||||
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
|
|
||||||
|
@ -345,13 +346,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())
|
||||||
System::TogglePostProcessing();
|
PostProcessing::Toggle();
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_HOTKEY("ReloadPostProcessingShaders", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
DEFINE_HOTKEY("ReloadPostProcessingShaders", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||||
TRANSLATE_NOOP("Hotkeys", "Reload Post Processing Shaders"), [](s32 pressed) {
|
TRANSLATE_NOOP("Hotkeys", "Reload Post Processing Shaders"), [](s32 pressed) {
|
||||||
if (!pressed && System::IsValid())
|
if (!pressed && System::IsValid())
|
||||||
System::ReloadPostProcessingShaders();
|
PostProcessing::ReloadShaders();
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_HOTKEY("ReloadTextureReplacements", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
DEFINE_HOTKEY("ReloadTextureReplacements", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||||
|
|
|
@ -264,7 +264,6 @@ void Settings::Load(SettingsInterface& si)
|
||||||
display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
|
display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
|
||||||
display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
|
display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
|
||||||
display_stretch = si.GetBoolValue("Display", "Stretch", false);
|
display_stretch = si.GetBoolValue("Display", "Stretch", false);
|
||||||
display_post_processing = si.GetBoolValue("Display", "PostProcessing", false);
|
|
||||||
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
|
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
|
||||||
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
|
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
|
||||||
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
|
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
|
||||||
|
@ -279,7 +278,6 @@ void Settings::Load(SettingsInterface& si)
|
||||||
display_internal_resolution_screenshots = si.GetBoolValue("Display", "InternalResolutionScreenshots", false);
|
display_internal_resolution_screenshots = si.GetBoolValue("Display", "InternalResolutionScreenshots", false);
|
||||||
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
|
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
|
||||||
video_sync_enabled = si.GetBoolValue("Display", "VSync", DEFAULT_VSYNC_VALUE);
|
video_sync_enabled = si.GetBoolValue("Display", "VSync", DEFAULT_VSYNC_VALUE);
|
||||||
display_post_process_chain = si.GetStringValue("Display", "PostProcessChain", "");
|
|
||||||
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
|
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
|
||||||
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
|
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
|
||||||
|
|
||||||
|
@ -486,7 +484,6 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
|
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
|
||||||
si.SetBoolValue("Display", "IntegerScaling", display_integer_scaling);
|
si.SetBoolValue("Display", "IntegerScaling", display_integer_scaling);
|
||||||
si.SetBoolValue("Display", "Stretch", display_stretch);
|
si.SetBoolValue("Display", "Stretch", display_stretch);
|
||||||
si.SetBoolValue("Display", "PostProcessing", display_post_processing);
|
|
||||||
si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
|
si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
|
||||||
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
|
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
|
||||||
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
|
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
|
||||||
|
@ -501,10 +498,6 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("Display", "InternalResolutionScreenshots", display_internal_resolution_screenshots);
|
si.SetBoolValue("Display", "InternalResolutionScreenshots", display_internal_resolution_screenshots);
|
||||||
si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically);
|
si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically);
|
||||||
si.SetBoolValue("Display", "VSync", video_sync_enabled);
|
si.SetBoolValue("Display", "VSync", video_sync_enabled);
|
||||||
if (display_post_process_chain.empty())
|
|
||||||
si.DeleteValue("Display", "PostProcessChain");
|
|
||||||
else
|
|
||||||
si.SetStringValue("Display", "PostProcessChain", display_post_process_chain.c_str());
|
|
||||||
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
|
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
|
||||||
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
|
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ struct Settings
|
||||||
|
|
||||||
GPURenderer gpu_renderer = DEFAULT_GPU_RENDERER;
|
GPURenderer gpu_renderer = DEFAULT_GPU_RENDERER;
|
||||||
std::string gpu_adapter;
|
std::string gpu_adapter;
|
||||||
std::string display_post_process_chain;
|
|
||||||
u32 gpu_resolution_scale = 1;
|
u32 gpu_resolution_scale = 1;
|
||||||
u32 gpu_multisamples = 1;
|
u32 gpu_multisamples = 1;
|
||||||
bool gpu_use_thread = true;
|
bool gpu_use_thread = true;
|
||||||
|
@ -132,7 +131,6 @@ struct Settings
|
||||||
bool display_linear_filtering = true;
|
bool display_linear_filtering = true;
|
||||||
bool display_integer_scaling = false;
|
bool display_integer_scaling = false;
|
||||||
bool display_stretch = false;
|
bool display_stretch = false;
|
||||||
bool display_post_processing = false;
|
|
||||||
bool display_show_osd_messages = true;
|
bool display_show_osd_messages = true;
|
||||||
bool display_show_fps = false;
|
bool display_show_fps = false;
|
||||||
bool display_show_speed = false;
|
bool display_show_speed = false;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "util/input_manager.h"
|
#include "util/input_manager.h"
|
||||||
#include "util/iso_reader.h"
|
#include "util/iso_reader.h"
|
||||||
#include "util/platform_misc.h"
|
#include "util/platform_misc.h"
|
||||||
|
#include "util/postprocessing.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
|
@ -117,7 +118,7 @@ static void DestroySystem();
|
||||||
static std::string GetMediaPathFromSaveState(const char* path);
|
static std::string GetMediaPathFromSaveState(const char* path);
|
||||||
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
|
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
|
||||||
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
|
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
|
||||||
static bool CreateGPU(GPURenderer renderer);
|
static bool CreateGPU(GPURenderer renderer, bool is_switching);
|
||||||
static bool SaveUndoLoadState();
|
static bool SaveUndoLoadState();
|
||||||
|
|
||||||
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
||||||
|
@ -869,7 +870,7 @@ std::string System::GetInputProfilePath(const std::string_view& name)
|
||||||
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
|
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_display, bool update_display /* = true*/)
|
bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool update_display /* = true*/)
|
||||||
{
|
{
|
||||||
ClearMemorySaveStates();
|
ClearMemorySaveStates();
|
||||||
g_gpu->RestoreGraphicsAPIState();
|
g_gpu->RestoreGraphicsAPIState();
|
||||||
|
@ -883,10 +884,10 @@ bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_display, bool
|
||||||
|
|
||||||
// create new renderer
|
// create new renderer
|
||||||
g_gpu.reset();
|
g_gpu.reset();
|
||||||
if (force_recreate_display)
|
if (force_recreate_device)
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
|
|
||||||
if (!CreateGPU(renderer))
|
if (!CreateGPU(renderer, true))
|
||||||
{
|
{
|
||||||
if (!IsStartupCancelled())
|
if (!IsStartupCancelled())
|
||||||
Host::ReportErrorAsync("Error", "Failed to recreate GPU.");
|
Host::ReportErrorAsync("Error", "Failed to recreate GPU.");
|
||||||
|
@ -1530,7 +1531,7 @@ bool System::Initialize(bool force_software_renderer)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer))
|
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false))
|
||||||
{
|
{
|
||||||
Bus::Shutdown();
|
Bus::Shutdown();
|
||||||
CPU::Shutdown();
|
CPU::Shutdown();
|
||||||
|
@ -1571,6 +1572,7 @@ bool System::Initialize(bool force_software_renderer)
|
||||||
MDEC::Initialize();
|
MDEC::Initialize();
|
||||||
SIO::Initialize();
|
SIO::Initialize();
|
||||||
PCDrv::Initialize();
|
PCDrv::Initialize();
|
||||||
|
PostProcessing::Initialize();
|
||||||
|
|
||||||
static constexpr float WARNING_DURATION = 15.0f;
|
static constexpr float WARNING_DURATION = 15.0f;
|
||||||
|
|
||||||
|
@ -1619,6 +1621,8 @@ void System::DestroySystem()
|
||||||
|
|
||||||
Host::ClearOSDMessages();
|
Host::ClearOSDMessages();
|
||||||
|
|
||||||
|
PostProcessing::Shutdown();
|
||||||
|
|
||||||
SaveStateSelectorUI::Close(true);
|
SaveStateSelectorUI::Close(true);
|
||||||
FullscreenUI::OnSystemDestroyed();
|
FullscreenUI::OnSystemDestroyed();
|
||||||
|
|
||||||
|
@ -1971,7 +1975,7 @@ void System::RecreateSystem()
|
||||||
PauseSystem(true);
|
PauseSystem(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::CreateGPU(GPURenderer renderer)
|
bool System::CreateGPU(GPURenderer renderer, bool is_switching)
|
||||||
{
|
{
|
||||||
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
||||||
|
|
||||||
|
@ -1981,6 +1985,7 @@ bool System::CreateGPU(GPURenderer renderer)
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Recreating GPU device, expecting %s got %s", GPUDevice::RenderAPIToString(api),
|
Log_WarningPrintf("Recreating GPU device, expecting %s got %s", GPUDevice::RenderAPIToString(api),
|
||||||
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
||||||
|
PostProcessing::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
Host::ReleaseGPUDevice();
|
Host::ReleaseGPUDevice();
|
||||||
|
@ -1989,6 +1994,9 @@ bool System::CreateGPU(GPURenderer renderer)
|
||||||
Host::ReleaseRenderWindow();
|
Host::ReleaseRenderWindow();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_switching)
|
||||||
|
PostProcessing::Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderer == GPURenderer::Software)
|
if (renderer == GPURenderer::Software)
|
||||||
|
@ -3482,13 +3490,13 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation))
|
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation))
|
||||||
{
|
{
|
||||||
// if debug device/threaded presentation change, we need to recreate the whole display
|
// if debug device/threaded presentation change, we need to recreate the whole display
|
||||||
const bool recreate_display = (g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
|
const bool recreate_device = (g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
|
||||||
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation);
|
g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation);
|
||||||
|
|
||||||
Host::AddFormattedOSDMessage(5.0f, TRANSLATE("OSDMessage", "Switching to %s%s GPU renderer."),
|
Host::AddFormattedOSDMessage(5.0f, TRANSLATE("OSDMessage", "Switching to %s%s GPU renderer."),
|
||||||
Settings::GetRendererName(g_settings.gpu_renderer),
|
Settings::GetRendererName(g_settings.gpu_renderer),
|
||||||
g_settings.gpu_use_debug_device ? " (debug)" : "");
|
g_settings.gpu_use_debug_device ? " (debug)" : "");
|
||||||
RecreateGPU(g_settings.gpu_renderer, recreate_display);
|
RecreateGPU(g_settings.gpu_renderer, recreate_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsValid())
|
if (IsValid())
|
||||||
|
@ -3661,12 +3669,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
UpdateSpeedLimiterState();
|
UpdateSpeedLimiterState();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_settings.display_post_processing != old_settings.display_post_processing ||
|
|
||||||
g_settings.display_post_process_chain != old_settings.display_post_process_chain)
|
|
||||||
{
|
|
||||||
g_gpu->UpdatePostProcessingChain();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
|
||||||
{
|
{
|
||||||
if (g_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver)
|
||||||
|
@ -3674,6 +3676,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
else
|
else
|
||||||
PlatformMisc::ResumeScreensaver();
|
PlatformMisc::ResumeScreensaver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostProcessing::UpdateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_gpu_device && g_settings.display_osd_scale != old_settings.display_osd_scale)
|
if (g_gpu_device && g_settings.display_osd_scale != old_settings.display_osd_scale)
|
||||||
|
@ -4525,29 +4529,6 @@ void System::ApplyCheatCode(u32 index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::TogglePostProcessing()
|
|
||||||
{
|
|
||||||
if (!IsValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_settings.display_post_processing = !g_settings.display_post_processing;
|
|
||||||
Host::AddKeyedOSDMessage("PostProcessing",
|
|
||||||
g_settings.display_post_processing ?
|
|
||||||
TRANSLATE_STR("OSDMessage", "Post-processing is now enabled.") :
|
|
||||||
TRANSLATE_STR("OSDMessage", "Post-processing is now disabled."),
|
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
g_gpu->UpdatePostProcessingChain();
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::ReloadPostProcessingShaders()
|
|
||||||
{
|
|
||||||
if (!IsValid() || !g_settings.display_post_processing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!g_gpu->UpdatePostProcessingChain())
|
|
||||||
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Post-processing shaders reloaded."), Host::OSD_ERROR_DURATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::ToggleWidescreen()
|
void System::ToggleWidescreen()
|
||||||
{
|
{
|
||||||
g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack;
|
g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack;
|
||||||
|
|
|
@ -246,7 +246,7 @@ void Execute();
|
||||||
void RecreateSystem();
|
void RecreateSystem();
|
||||||
|
|
||||||
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
|
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
|
||||||
bool RecreateGPU(GPURenderer renderer, bool force_recreate_display = false, bool update_display = true);
|
bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true);
|
||||||
|
|
||||||
void SingleStepCPU();
|
void SingleStepCPU();
|
||||||
|
|
||||||
|
@ -425,12 +425,6 @@ void SetCheatCodeState(u32 index, bool enabled, bool save_to_file);
|
||||||
/// Immediately applies the specified cheat code.
|
/// Immediately applies the specified cheat code.
|
||||||
void ApplyCheatCode(u32 index);
|
void ApplyCheatCode(u32 index);
|
||||||
|
|
||||||
/// Temporarily toggles post-processing on/off.
|
|
||||||
void TogglePostProcessing();
|
|
||||||
|
|
||||||
/// Reloads post processing shaders with the current configuration.
|
|
||||||
void ReloadPostProcessingShaders();
|
|
||||||
|
|
||||||
/// Toggle Widescreen Hack and Aspect Ratio
|
/// Toggle Widescreen Hack and Aspect Ratio
|
||||||
void ToggleWidescreen();
|
void ToggleWidescreen();
|
||||||
|
|
||||||
|
|
|
@ -113,14 +113,9 @@ set(SRCS
|
||||||
memorycardsettingswidget.h
|
memorycardsettingswidget.h
|
||||||
memoryviewwidget.cpp
|
memoryviewwidget.cpp
|
||||||
memoryviewwidget.h
|
memoryviewwidget.h
|
||||||
postprocessingchainconfigwidget.cpp
|
|
||||||
postprocessingchainconfigwidget.h
|
|
||||||
postprocessingchainconfigwidget.ui
|
|
||||||
postprocessingsettingswidget.cpp
|
postprocessingsettingswidget.cpp
|
||||||
postprocessingsettingswidget.h
|
postprocessingsettingswidget.h
|
||||||
postprocessingsettingswidget.ui
|
postprocessingsettingswidget.ui
|
||||||
postprocessingshaderconfigwidget.cpp
|
|
||||||
postprocessingshaderconfigwidget.h
|
|
||||||
qthost.cpp
|
qthost.cpp
|
||||||
qthost.h
|
qthost.h
|
||||||
qtkeycodes.cpp
|
qtkeycodes.cpp
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "displaysettingswidget.h"
|
#include "displaysettingswidget.h"
|
||||||
#include "core/gpu.h"
|
#include "core/gpu.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "postprocessingchainconfigwidget.h"
|
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "settingwidgetbinder.h"
|
#include "settingwidgetbinder.h"
|
||||||
|
|
|
@ -41,8 +41,6 @@
|
||||||
<ClCompile Include="mainwindow.cpp" />
|
<ClCompile Include="mainwindow.cpp" />
|
||||||
<ClCompile Include="memorycardsettingswidget.cpp" />
|
<ClCompile Include="memorycardsettingswidget.cpp" />
|
||||||
<ClCompile Include="memorycardeditordialog.cpp" />
|
<ClCompile Include="memorycardeditordialog.cpp" />
|
||||||
<ClCompile Include="postprocessingchainconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="postprocessingshaderconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="postprocessingsettingswidget.cpp" />
|
<ClCompile Include="postprocessingsettingswidget.cpp" />
|
||||||
<ClCompile Include="qttranslations.cpp" />
|
<ClCompile Include="qttranslations.cpp" />
|
||||||
<ClCompile Include="qthost.cpp" />
|
<ClCompile Include="qthost.cpp" />
|
||||||
|
@ -93,8 +91,6 @@
|
||||||
<QtMoc Include="gamesummarywidget.h" />
|
<QtMoc Include="gamesummarywidget.h" />
|
||||||
<QtMoc Include="gdbconnection.h" />
|
<QtMoc Include="gdbconnection.h" />
|
||||||
<QtMoc Include="gdbserver.h" />
|
<QtMoc Include="gdbserver.h" />
|
||||||
<QtMoc Include="postprocessingchainconfigwidget.h" />
|
|
||||||
<QtMoc Include="postprocessingshaderconfigwidget.h" />
|
|
||||||
<QtMoc Include="postprocessingsettingswidget.h" />
|
<QtMoc Include="postprocessingsettingswidget.h" />
|
||||||
<QtMoc Include="mainwindow.h" />
|
<QtMoc Include="mainwindow.h" />
|
||||||
<QtMoc Include="qthost.h" />
|
<QtMoc Include="qthost.h" />
|
||||||
|
@ -139,9 +135,6 @@
|
||||||
<QtUi Include="advancedsettingswidget.ui">
|
<QtUi Include="advancedsettingswidget.ui">
|
||||||
<FileType>Document</FileType>
|
<FileType>Document</FileType>
|
||||||
</QtUi>
|
</QtUi>
|
||||||
<QtUi Include="postprocessingchainconfigwidget.ui">
|
|
||||||
<FileType>Document</FileType>
|
|
||||||
</QtUi>
|
|
||||||
<QtUi Include="postprocessingsettingswidget.ui">
|
<QtUi Include="postprocessingsettingswidget.ui">
|
||||||
<FileType>Document</FileType>
|
<FileType>Document</FileType>
|
||||||
</QtUi>
|
</QtUi>
|
||||||
|
@ -263,8 +256,6 @@
|
||||||
<ClCompile Include="$(IntDir)moc_memorycardsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_memorycardsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_memorycardeditordialog.cpp" />
|
<ClCompile Include="$(IntDir)moc_memorycardeditordialog.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_memoryviewwidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_memoryviewwidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingchainconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingshaderconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_qthost.cpp" />
|
<ClCompile Include="$(IntDir)moc_qthost.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
|
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
|
||||||
|
|
|
@ -48,15 +48,11 @@
|
||||||
<ClCompile Include="enhancementsettingswidget.cpp" />
|
<ClCompile Include="enhancementsettingswidget.cpp" />
|
||||||
<ClCompile Include="displaysettingswidget.cpp" />
|
<ClCompile Include="displaysettingswidget.cpp" />
|
||||||
<ClCompile Include="memorycardeditordialog.cpp" />
|
<ClCompile Include="memorycardeditordialog.cpp" />
|
||||||
<ClCompile Include="postprocessingchainconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="postprocessingshaderconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="postprocessingsettingswidget.cpp" />
|
<ClCompile Include="postprocessingsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_biossettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_biossettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_enhancementsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_enhancementsettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_displaysettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_displaysettingswidget.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_memorycardeditordialog.cpp" />
|
<ClCompile Include="$(IntDir)moc_memorycardeditordialog.cpp" />
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingchainconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingshaderconfigwidget.cpp" />
|
|
||||||
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
||||||
<ClCompile Include="cheatmanagerdialog.cpp" />
|
<ClCompile Include="cheatmanagerdialog.cpp" />
|
||||||
<ClCompile Include="cheatcodeeditordialog.cpp" />
|
<ClCompile Include="cheatcodeeditordialog.cpp" />
|
||||||
|
@ -136,8 +132,6 @@
|
||||||
<QtMoc Include="enhancementsettingswidget.h" />
|
<QtMoc Include="enhancementsettingswidget.h" />
|
||||||
<QtMoc Include="memorycardeditordialog.h" />
|
<QtMoc Include="memorycardeditordialog.h" />
|
||||||
<QtMoc Include="displaysettingswidget.h" />
|
<QtMoc Include="displaysettingswidget.h" />
|
||||||
<QtMoc Include="postprocessingchainconfigwidget.h" />
|
|
||||||
<QtMoc Include="postprocessingshaderconfigwidget.h" />
|
|
||||||
<QtMoc Include="postprocessingsettingswidget.h" />
|
<QtMoc Include="postprocessingsettingswidget.h" />
|
||||||
<QtMoc Include="cheatmanagerdialog.h" />
|
<QtMoc Include="cheatmanagerdialog.h" />
|
||||||
<QtMoc Include="cheatcodeeditordialog.h" />
|
<QtMoc Include="cheatcodeeditordialog.h" />
|
||||||
|
@ -171,7 +165,6 @@
|
||||||
<QtUi Include="biossettingswidget.ui" />
|
<QtUi Include="biossettingswidget.ui" />
|
||||||
<QtUi Include="enhancementsettingswidget.ui" />
|
<QtUi Include="enhancementsettingswidget.ui" />
|
||||||
<QtUi Include="displaysettingswidget.ui" />
|
<QtUi Include="displaysettingswidget.ui" />
|
||||||
<QtUi Include="postprocessingchainconfigwidget.ui" />
|
|
||||||
<QtUi Include="postprocessingsettingswidget.ui" />
|
<QtUi Include="postprocessingsettingswidget.ui" />
|
||||||
<QtUi Include="memorycardeditordialog.ui" />
|
<QtUi Include="memorycardeditordialog.ui" />
|
||||||
<QtUi Include="cheatmanagerdialog.ui" />
|
<QtUi Include="cheatmanagerdialog.ui" />
|
||||||
|
@ -264,4 +257,4 @@
|
||||||
<Filter>translations</Filter>
|
<Filter>translations</Filter>
|
||||||
</QtTs>
|
</QtTs>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,219 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "postprocessingchainconfigwidget.h"
|
|
||||||
#include "postprocessingshaderconfigwidget.h"
|
|
||||||
#include "qthost.h"
|
|
||||||
|
|
||||||
#include "util/postprocessing_chain.h"
|
|
||||||
|
|
||||||
#include <QtGui/QCursor>
|
|
||||||
#include <QtWidgets/QMenu>
|
|
||||||
#include <QtWidgets/QMessageBox>
|
|
||||||
|
|
||||||
PostProcessingChainConfigWidget::PostProcessingChainConfigWidget(QWidget* parent) : QWidget(parent)
|
|
||||||
{
|
|
||||||
m_ui.setupUi(this);
|
|
||||||
connectUi();
|
|
||||||
updateButtonStates(std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessingChainConfigWidget::~PostProcessingChainConfigWidget() = default;
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::connectUi()
|
|
||||||
{
|
|
||||||
connect(m_ui.add, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onAddButtonClicked);
|
|
||||||
connect(m_ui.remove, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onRemoveButtonClicked);
|
|
||||||
connect(m_ui.clear, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onClearButtonClicked);
|
|
||||||
connect(m_ui.moveUp, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onMoveUpButtonClicked);
|
|
||||||
connect(m_ui.moveDown, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onMoveDownButtonClicked);
|
|
||||||
// connect(m_ui.reload, &QToolButton::clicked, this, &PostProcessingChainConfigWidget::onReloadButtonClicked);
|
|
||||||
connect(m_ui.shaderSettings, &QToolButton::clicked, this,
|
|
||||||
&PostProcessingChainConfigWidget::onShaderConfigButtonClicked);
|
|
||||||
connect(m_ui.shaders, &QListWidget::itemSelectionChanged, this,
|
|
||||||
&PostProcessingChainConfigWidget::onSelectedShaderChanged);
|
|
||||||
|
|
||||||
// m_ui.loadPreset->setEnabled(false);
|
|
||||||
// m_ui.savePreset->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessingChainConfigWidget::setConfigString(const std::string_view& config_string)
|
|
||||||
{
|
|
||||||
if (!m_chain.CreateFromString(config_string))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
updateList();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::setOptionsButtonVisible(bool visible)
|
|
||||||
{
|
|
||||||
if (visible)
|
|
||||||
{
|
|
||||||
m_ui.shaderSettings->setVisible(true);
|
|
||||||
m_ui.horizontalLayout->addWidget(m_ui.shaderSettings);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ui.shaderSettings->setVisible(false);
|
|
||||||
m_ui.horizontalLayout->removeWidget(m_ui.shaderSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<u32> PostProcessingChainConfigWidget::getSelectedIndex() const
|
|
||||||
{
|
|
||||||
QList<QListWidgetItem*> selected_items = m_ui.shaders->selectedItems();
|
|
||||||
return selected_items.empty() ? std::nullopt :
|
|
||||||
std::optional<u32>(selected_items.first()->data(Qt::UserRole).toUInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::updateList()
|
|
||||||
{
|
|
||||||
m_ui.shaders->clear();
|
|
||||||
|
|
||||||
for (u32 i = 0; i < m_chain.GetStageCount(); i++)
|
|
||||||
{
|
|
||||||
const PostProcessingShader* shader = m_chain.GetShaderStage(i);
|
|
||||||
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(shader->GetName()), m_ui.shaders);
|
|
||||||
item->setData(Qt::UserRole, QVariant(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateButtonStates(std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::configChanged()
|
|
||||||
{
|
|
||||||
if (m_chain.IsEmpty())
|
|
||||||
chainConfigStringChanged(std::string());
|
|
||||||
else
|
|
||||||
chainConfigStringChanged(m_chain.GetConfigString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::updateButtonStates(std::optional<u32> index)
|
|
||||||
{
|
|
||||||
m_ui.remove->setEnabled(index.has_value());
|
|
||||||
m_ui.clear->setEnabled(!m_chain.IsEmpty());
|
|
||||||
// m_ui.reload->setEnabled(!m_chain.IsEmpty());
|
|
||||||
m_ui.shaderSettings->setEnabled(index.has_value() && (index.value() < m_chain.GetStageCount()) &&
|
|
||||||
m_chain.GetShaderStage(index.value())->HasOptions());
|
|
||||||
|
|
||||||
if (index.has_value())
|
|
||||||
{
|
|
||||||
m_ui.moveUp->setEnabled(index.value() > 0);
|
|
||||||
m_ui.moveDown->setEnabled(index.value() < (m_chain.GetStageCount() - 1u));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ui.moveUp->setEnabled(false);
|
|
||||||
m_ui.moveDown->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onAddButtonClicked()
|
|
||||||
{
|
|
||||||
QMenu menu;
|
|
||||||
|
|
||||||
const std::vector<std::string> shaders(PostProcessingChain::GetAvailableShaderNames());
|
|
||||||
if (shaders.empty())
|
|
||||||
{
|
|
||||||
menu.addAction(tr("No Shaders Available"))->setEnabled(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const std::string& shader : shaders)
|
|
||||||
{
|
|
||||||
QAction* action = menu.addAction(QString::fromStdString(shader));
|
|
||||||
connect(action, &QAction::triggered, [this, &shader]() {
|
|
||||||
chainAboutToChange();
|
|
||||||
|
|
||||||
if (!m_chain.AddStage(shader))
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to add shader. The log may contain more information."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateList();
|
|
||||||
configChanged();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.exec(QCursor::pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onRemoveButtonClicked()
|
|
||||||
{
|
|
||||||
QList<QListWidgetItem*> selected_items = m_ui.shaders->selectedItems();
|
|
||||||
if (selected_items.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QListWidgetItem* item = selected_items.first();
|
|
||||||
u32 index = item->data(Qt::UserRole).toUInt();
|
|
||||||
if (index < m_chain.GetStageCount())
|
|
||||||
{
|
|
||||||
chainAboutToChange();
|
|
||||||
m_chain.RemoveStage(index);
|
|
||||||
updateList();
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onClearButtonClicked()
|
|
||||||
{
|
|
||||||
if (QMessageBox::question(this, tr("Question"), tr("Are you sure you want to clear all shader stages?"),
|
|
||||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
|
||||||
{
|
|
||||||
chainAboutToChange();
|
|
||||||
m_chain.ClearStages();
|
|
||||||
updateList();
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onMoveUpButtonClicked()
|
|
||||||
{
|
|
||||||
std::optional<u32> index = getSelectedIndex();
|
|
||||||
if (index.has_value())
|
|
||||||
{
|
|
||||||
chainAboutToChange();
|
|
||||||
m_chain.MoveStageUp(index.value());
|
|
||||||
updateList();
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onMoveDownButtonClicked()
|
|
||||||
{
|
|
||||||
std::optional<u32> index = getSelectedIndex();
|
|
||||||
if (index.has_value())
|
|
||||||
{
|
|
||||||
chainAboutToChange();
|
|
||||||
m_chain.MoveStageDown(index.value());
|
|
||||||
updateList();
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onShaderConfigButtonClicked()
|
|
||||||
{
|
|
||||||
std::optional<u32> index = getSelectedIndex();
|
|
||||||
if (index.has_value() && index.value() < m_chain.GetStageCount())
|
|
||||||
{
|
|
||||||
PostProcessingShaderConfigDialog shader_config(this, m_chain.GetShaderStage(index.value()));
|
|
||||||
connect(&shader_config, &PostProcessingShaderConfigDialog::configChanged, [this]() { configChanged(); });
|
|
||||||
shader_config.exec();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onReloadButtonClicked()
|
|
||||||
{
|
|
||||||
g_emu_thread->reloadPostProcessingShaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChainConfigWidget::onSelectedShaderChanged()
|
|
||||||
{
|
|
||||||
std::optional<u32> index = getSelectedIndex();
|
|
||||||
selectedShaderChanged(index.has_value() ? static_cast<s32>(index.value()) : -1);
|
|
||||||
updateButtonStates(index);
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ui_postprocessingchainconfigwidget.h"
|
|
||||||
|
|
||||||
#include "util/postprocessing_chain.h"
|
|
||||||
|
|
||||||
#include "common/types.h"
|
|
||||||
|
|
||||||
#include <QtWidgets/QWidget>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace PlatformMisc {
|
|
||||||
class PostProcessingChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PostProcessingChainConfigWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
PostProcessingChainConfigWidget(QWidget* parent);
|
|
||||||
~PostProcessingChainConfigWidget();
|
|
||||||
|
|
||||||
ALWAYS_INLINE PostProcessingChain& getChain() { return m_chain; }
|
|
||||||
|
|
||||||
bool setConfigString(const std::string_view& config_string);
|
|
||||||
void setOptionsButtonVisible(bool visible);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void selectedShaderChanged(qint32 index);
|
|
||||||
void chainAboutToChange();
|
|
||||||
void chainConfigStringChanged(const std::string& new_config_string);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onAddButtonClicked();
|
|
||||||
void onRemoveButtonClicked();
|
|
||||||
void onClearButtonClicked();
|
|
||||||
void onMoveUpButtonClicked();
|
|
||||||
void onMoveDownButtonClicked();
|
|
||||||
void onShaderConfigButtonClicked();
|
|
||||||
void onReloadButtonClicked();
|
|
||||||
void onSelectedShaderChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void connectUi();
|
|
||||||
std::optional<u32> getSelectedIndex() const;
|
|
||||||
void updateList();
|
|
||||||
void configChanged();
|
|
||||||
void updateButtonStates(std::optional<u32> index);
|
|
||||||
|
|
||||||
Ui::PostProcessingChainConfigWidget m_ui;
|
|
||||||
|
|
||||||
PostProcessingChain m_chain;
|
|
||||||
};
|
|
|
@ -1,163 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>PostProcessingChainConfigWidget</class>
|
|
||||||
<widget class="QWidget" name="PostProcessingChainConfigWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>721</width>
|
|
||||||
<height>210</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="shaders">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>80</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="add">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Add</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="PostProcessingAdd"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="remove">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="PostProcessingRemove"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="clear">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Clear</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="Clear"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="moveUp">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Move Up</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="MoveUp"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="moveDown">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Move Down</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="MoveDown"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="shaderSettings">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Options...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="Options"/>
|
|
||||||
</property>
|
|
||||||
<property name="toolButtonStyle">
|
|
||||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources>
|
|
||||||
<include location="resources/resources.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
|
@ -1,10 +1,20 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2023 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)
|
||||||
|
|
||||||
#include "postprocessingsettingswidget.h"
|
#include "postprocessingsettingswidget.h"
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
#include "settingwidgetbinder.h"
|
#include "settingwidgetbinder.h"
|
||||||
|
|
||||||
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
|
||||||
|
#include <QtWidgets/QCheckBox>
|
||||||
|
#include <QtWidgets/QDialogButtonBox>
|
||||||
|
#include <QtWidgets/QGridLayout>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <QtWidgets/QSlider>
|
||||||
|
|
||||||
PostProcessingSettingsWidget::PostProcessingSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
PostProcessingSettingsWidget::PostProcessingSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||||
: QWidget(parent), m_dialog(dialog)
|
: QWidget(parent), m_dialog(dialog)
|
||||||
|
@ -12,53 +22,101 @@ PostProcessingSettingsWidget::PostProcessingSettingsWidget(SettingsDialog* dialo
|
||||||
SettingsInterface* sif = dialog->getSettingsInterface();
|
SettingsInterface* sif = dialog->getSettingsInterface();
|
||||||
|
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
m_ui.widget->setOptionsButtonVisible(false);
|
|
||||||
m_ui.reload->setEnabled(false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enablePostProcessing, "PostProcessing", "Enabled", false);
|
||||||
updateShaderConfigPanel(-1);
|
|
||||||
|
updateList();
|
||||||
|
updateButtonsAndConfigPane(std::nullopt);
|
||||||
connectUi();
|
connectUi();
|
||||||
|
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enablePostProcessing, "Display", "PostProcessing", false);
|
|
||||||
|
|
||||||
std::string post_chain = m_dialog->getStringValue("Display", "PostProcessChain", "").value_or(std::string());
|
|
||||||
if (!post_chain.empty())
|
|
||||||
{
|
|
||||||
if (!m_ui.widget->setConfigString(post_chain))
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("The current post-processing chain is invalid, it has been reset."));
|
|
||||||
m_dialog->removeSettingValue("Display", "PostProcessChain");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ui.reload->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingSettingsWidget::~PostProcessingSettingsWidget() = default;
|
PostProcessingSettingsWidget::~PostProcessingSettingsWidget() = default;
|
||||||
|
|
||||||
|
SettingsInterface& PostProcessingSettingsWidget::getSettingsInterfaceToUpdate()
|
||||||
|
{
|
||||||
|
return m_dialog->isPerGameSettings() ? *m_dialog->getSettingsInterface() : *Host::Internal::GetBaseSettingsLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::commitSettingsUpdate()
|
||||||
|
{
|
||||||
|
if (m_dialog->isPerGameSettings())
|
||||||
|
m_dialog->getSettingsInterface()->Save();
|
||||||
|
else
|
||||||
|
Host::CommitBaseSettingChanges();
|
||||||
|
|
||||||
|
g_emu_thread->updatePostProcessingSettings();
|
||||||
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::connectUi()
|
void PostProcessingSettingsWidget::connectUi()
|
||||||
{
|
{
|
||||||
connect(m_ui.reload, &QPushButton::clicked, this, &PostProcessingSettingsWidget::onReloadClicked);
|
connect(m_ui.reload, &QPushButton::clicked, this, &PostProcessingSettingsWidget::onReloadButtonClicked);
|
||||||
connect(m_ui.widget, &PostProcessingChainConfigWidget::chainAboutToChange, this,
|
connect(m_ui.add, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onAddButtonClicked);
|
||||||
&PostProcessingSettingsWidget::onChainAboutToChange);
|
connect(m_ui.remove, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onRemoveButtonClicked);
|
||||||
connect(m_ui.widget, &PostProcessingChainConfigWidget::selectedShaderChanged, this,
|
connect(m_ui.clear, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onClearButtonClicked);
|
||||||
&PostProcessingSettingsWidget::onChainSelectedShaderChanged);
|
connect(m_ui.moveUp, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onMoveUpButtonClicked);
|
||||||
connect(m_ui.widget, &PostProcessingChainConfigWidget::chainConfigStringChanged, this,
|
connect(m_ui.moveDown, &QToolButton::clicked, this, &PostProcessingSettingsWidget::onMoveDownButtonClicked);
|
||||||
&PostProcessingSettingsWidget::onConfigChanged);
|
connect(m_ui.stages, &QListWidget::itemSelectionChanged, this,
|
||||||
|
&PostProcessingSettingsWidget::onSelectedShaderChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onChainAboutToChange()
|
std::optional<u32> PostProcessingSettingsWidget::getSelectedIndex() const
|
||||||
{
|
{
|
||||||
updateShaderConfigPanel(-1);
|
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
||||||
|
return selected_items.empty() ? std::nullopt :
|
||||||
|
std::optional<u32>(selected_items.first()->data(Qt::UserRole).toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onChainSelectedShaderChanged(qint32 index)
|
void PostProcessingSettingsWidget::selectIndex(s32 index)
|
||||||
{
|
{
|
||||||
updateShaderConfigPanel(index);
|
if (index < 0 || index >= m_ui.stages->count())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QSignalBlocker sb(m_ui.stages);
|
||||||
|
m_ui.stages->setCurrentItem(m_ui.stages->item(index));
|
||||||
|
updateButtonsAndConfigPane(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::updateShaderConfigPanel(s32 index)
|
void PostProcessingSettingsWidget::updateList()
|
||||||
{
|
{
|
||||||
|
const auto lock = Host::GetSettingsLock();
|
||||||
|
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
|
||||||
|
updateList(si);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::updateList(const SettingsInterface& si)
|
||||||
|
{
|
||||||
|
m_ui.stages->clear();
|
||||||
|
|
||||||
|
const u32 stage_count = PostProcessing::Config::GetStageCount(si);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < stage_count; i++)
|
||||||
|
{
|
||||||
|
const std::string stage_name = PostProcessing::Config::GetStageShaderName(si, i);
|
||||||
|
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(stage_name), m_ui.stages);
|
||||||
|
item->setData(Qt::UserRole, QVariant(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui.clear->setEnabled(stage_count > 0);
|
||||||
|
m_ui.reload->setEnabled(stage_count > 0);
|
||||||
|
updateButtonsAndConfigPane(std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::updateButtonsAndConfigPane(std::optional<u32> index)
|
||||||
|
{
|
||||||
|
m_ui.remove->setEnabled(index.has_value());
|
||||||
|
|
||||||
|
if (index.has_value())
|
||||||
|
{
|
||||||
|
m_ui.moveUp->setEnabled(index.value() > 0);
|
||||||
|
m_ui.moveDown->setEnabled(index.value() < static_cast<u32>(m_ui.stages->count() - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui.moveUp->setEnabled(false);
|
||||||
|
m_ui.moveDown->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
m_ui.scrollArea->setWidget(nullptr);
|
m_ui.scrollArea->setWidget(nullptr);
|
||||||
m_ui.scrollArea->setVisible(false);
|
m_ui.scrollArea->setVisible(false);
|
||||||
|
|
||||||
|
@ -68,35 +126,282 @@ void PostProcessingSettingsWidget::updateShaderConfigPanel(s32 index)
|
||||||
m_shader_config = nullptr;
|
m_shader_config = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 0)
|
if (!index.has_value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PostProcessingShader* shader = m_ui.widget->getChain().GetShaderStage(static_cast<u32>(index));
|
const auto lock = Host::GetSettingsLock();
|
||||||
if (!shader->HasOptions())
|
const SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
std::vector<PostProcessing::ShaderOption> options = PostProcessing::Config::GetStageOptions(si, index.value());
|
||||||
|
if (options.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_shader_config = new PostProcessingShaderConfigWidget(m_ui.scrollArea, shader);
|
m_shader_config = new PostProcessingShaderConfigWidget(m_ui.scrollArea, this, index.value(), std::move(options));
|
||||||
connect(m_shader_config, &PostProcessingShaderConfigWidget::configChanged,
|
|
||||||
[this]() { onConfigChanged(m_ui.widget->getChain().GetConfigString()); });
|
|
||||||
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::onConfigChanged(const std::string& new_config)
|
void PostProcessingSettingsWidget::onAddButtonClicked()
|
||||||
{
|
{
|
||||||
if (new_config.empty())
|
QMenu menu;
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string>> shaders = PostProcessing::GetAvailableShaderNames();
|
||||||
|
if (shaders.empty())
|
||||||
{
|
{
|
||||||
m_dialog->removeSettingValue("Display", "PostProcessChain");
|
menu.addAction(tr("No Shaders Available"))->setEnabled(false);
|
||||||
m_ui.reload->setEnabled(false);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_dialog->setStringSettingValue("Display", "PostProcessChain", new_config.c_str());
|
for (auto& [display_name, name] : shaders)
|
||||||
m_ui.reload->setEnabled(true);
|
{
|
||||||
|
QAction* action = menu.addAction(QString::fromStdString(display_name));
|
||||||
|
connect(action, &QAction::triggered, [this, shader = std::move(name)]() {
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
if (!PostProcessing::Config::AddStage(si, shader, &error))
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
QMessageBox::critical(this, tr("Error"),
|
||||||
|
tr("Failed to add shader: %1").arg(QString::fromStdString(error.GetDescription())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateList(si);
|
||||||
|
lock.unlock();
|
||||||
|
commitSettingsUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.exec(QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::onRemoveButtonClicked()
|
||||||
|
{
|
||||||
|
QList<QListWidgetItem*> selected_items = m_ui.stages->selectedItems();
|
||||||
|
if (selected_items.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
|
||||||
|
QListWidgetItem* item = selected_items.first();
|
||||||
|
u32 index = item->data(Qt::UserRole).toUInt();
|
||||||
|
if (index < PostProcessing::Config::GetStageCount(si))
|
||||||
|
{
|
||||||
|
PostProcessing::Config::RemoveStage(si, index);
|
||||||
|
updateList(si);
|
||||||
|
lock.unlock();
|
||||||
|
commitSettingsUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingSettingsWidget::onReloadClicked()
|
void PostProcessingSettingsWidget::onClearButtonClicked()
|
||||||
|
{
|
||||||
|
if (QMessageBox::question(this, tr("Question"), tr("Are you sure you want to clear all shader stages?"),
|
||||||
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
PostProcessing::Config::ClearStages(si);
|
||||||
|
updateList(si);
|
||||||
|
lock.unlock();
|
||||||
|
commitSettingsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::onMoveUpButtonClicked()
|
||||||
|
{
|
||||||
|
std::optional<u32> index = getSelectedIndex();
|
||||||
|
if (index.has_value() && index.value() > 0)
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
PostProcessing::Config::MoveStageUp(si, index.value());
|
||||||
|
updateList(si);
|
||||||
|
lock.unlock();
|
||||||
|
selectIndex(index.value() - 1);
|
||||||
|
commitSettingsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::onMoveDownButtonClicked()
|
||||||
|
{
|
||||||
|
std::optional<u32> index = getSelectedIndex();
|
||||||
|
if (index.has_value() || index.value() < (static_cast<u32>(m_ui.stages->count() - 1)))
|
||||||
|
{
|
||||||
|
auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = getSettingsInterfaceToUpdate();
|
||||||
|
PostProcessing::Config::MoveStageDown(si, index.value());
|
||||||
|
updateList(si);
|
||||||
|
lock.unlock();
|
||||||
|
selectIndex(index.value() + 1);
|
||||||
|
commitSettingsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::onReloadButtonClicked()
|
||||||
{
|
{
|
||||||
g_emu_thread->reloadPostProcessingShaders();
|
g_emu_thread->reloadPostProcessingShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PostProcessingSettingsWidget::onSelectedShaderChanged()
|
||||||
|
{
|
||||||
|
std::optional<u32> index = getSelectedIndex();
|
||||||
|
updateButtonsAndConfigPane(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
|
||||||
|
PostProcessingSettingsWidget* widget,
|
||||||
|
u32 stage_index,
|
||||||
|
std::vector<PostProcessing::ShaderOption> options)
|
||||||
|
: QWidget(parent), m_widget(widget), m_stage_index(stage_index), m_options(std::move(options))
|
||||||
|
{
|
||||||
|
m_layout = new QGridLayout(this);
|
||||||
|
|
||||||
|
createUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessingShaderConfigWidget::~PostProcessingShaderConfigWidget() = default;
|
||||||
|
|
||||||
|
void PostProcessingShaderConfigWidget::updateConfigForOption(const PostProcessing::ShaderOption& option)
|
||||||
|
{
|
||||||
|
const auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = m_widget->getSettingsInterfaceToUpdate();
|
||||||
|
PostProcessing::Config::SetStageOption(si, m_stage_index, option);
|
||||||
|
m_widget->commitSettingsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingShaderConfigWidget::onResetDefaultsClicked()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const auto lock = Host::GetSettingsLock();
|
||||||
|
SettingsInterface& si = m_widget->getSettingsInterfaceToUpdate();
|
||||||
|
for (PostProcessing::ShaderOption& option : m_options)
|
||||||
|
{
|
||||||
|
if (std::memcmp(option.value.data(), option.default_value.data(), sizeof(option.value)) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
option.value = option.default_value;
|
||||||
|
PostProcessing::Config::UnsetStageOption(si, m_stage_index, option);
|
||||||
|
}
|
||||||
|
m_widget->commitSettingsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toss and recreate UI.
|
||||||
|
for (auto it = m_widgets.rbegin(); it != m_widgets.rend(); ++it)
|
||||||
|
{
|
||||||
|
m_layout->removeWidget(*it);
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
m_widgets.clear();
|
||||||
|
createUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingShaderConfigWidget::createUi()
|
||||||
|
{
|
||||||
|
u32 row = 0;
|
||||||
|
|
||||||
|
for (PostProcessing::ShaderOption& option : m_options)
|
||||||
|
{
|
||||||
|
if (option.ui_name.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (option.type == PostProcessing::ShaderOption::Type::Bool)
|
||||||
|
{
|
||||||
|
QCheckBox* checkbox = new QCheckBox(QString::fromStdString(option.ui_name), this);
|
||||||
|
checkbox->setChecked(option.value[0].int_value != 0);
|
||||||
|
connect(checkbox, &QCheckBox::stateChanged, [this, &option](int state) {
|
||||||
|
option.value[0].int_value = (state == Qt::Checked) ? 1 : 0;
|
||||||
|
updateConfigForOption(option);
|
||||||
|
});
|
||||||
|
m_layout->addWidget(checkbox, row, 0, 1, 3, Qt::AlignLeft);
|
||||||
|
m_widgets.push_back(checkbox);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < option.vector_size; i++)
|
||||||
|
{
|
||||||
|
QString label;
|
||||||
|
if (option.vector_size <= 1)
|
||||||
|
{
|
||||||
|
label = QString::fromStdString(option.ui_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static constexpr std::array<const char*, PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS + 1> suffixes = {
|
||||||
|
{QT_TR_NOOP("Red"), QT_TR_NOOP("Green"), QT_TR_NOOP("Blue"), QT_TR_NOOP("Alpha")}};
|
||||||
|
label = tr("%1 (%2)").arg(QString::fromStdString(option.ui_name)).arg(tr(suffixes[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* label_w = new QLabel(label, this);
|
||||||
|
m_layout->addWidget(label_w, row, 0, 1, 1, Qt::AlignLeft);
|
||||||
|
m_widgets.push_back(label_w);
|
||||||
|
|
||||||
|
QSlider* slider = new QSlider(Qt::Horizontal, this);
|
||||||
|
m_layout->addWidget(slider, row, 1, 1, 1, Qt::AlignLeft);
|
||||||
|
m_widgets.push_back(slider);
|
||||||
|
|
||||||
|
QLabel* slider_label = new QLabel(this);
|
||||||
|
m_layout->addWidget(slider_label, row, 2, 1, 1, Qt::AlignLeft);
|
||||||
|
m_widgets.push_back(slider_label);
|
||||||
|
|
||||||
|
if (option.type == PostProcessing::ShaderOption::Type::Int)
|
||||||
|
{
|
||||||
|
slider_label->setText(QString::number(option.value[i].int_value));
|
||||||
|
|
||||||
|
const int range = option.max_value[i].int_value - option.min_value[i].int_value;
|
||||||
|
const int step_value =
|
||||||
|
(option.step_value[i].int_value != 0) ? option.step_value[i].int_value : ((range + 99) / 100);
|
||||||
|
const int num_steps = range / step_value;
|
||||||
|
slider->setMinimum(0);
|
||||||
|
slider->setMaximum(num_steps);
|
||||||
|
slider->setSingleStep(1);
|
||||||
|
slider->setTickInterval(step_value);
|
||||||
|
slider->setValue((option.value[i].int_value - option.min_value[i].int_value) / step_value);
|
||||||
|
connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label](int value) {
|
||||||
|
const int new_value = std::clamp(option.min_value[i].int_value + (value * option.step_value[i].int_value),
|
||||||
|
option.min_value[i].int_value, option.max_value[i].int_value);
|
||||||
|
option.value[i].int_value = new_value;
|
||||||
|
slider_label->setText(QString::number(new_value));
|
||||||
|
updateConfigForOption(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slider_label->setText(QString::number(option.value[i].float_value));
|
||||||
|
|
||||||
|
const float range = option.max_value[i].float_value - option.min_value[i].float_value;
|
||||||
|
const float step_value =
|
||||||
|
(option.step_value[i].float_value != 0) ? option.step_value[i].float_value : ((range + 99.0f) / 100.0f);
|
||||||
|
const float num_steps = std::ceil(range / step_value);
|
||||||
|
slider->setMinimum(0);
|
||||||
|
slider->setMaximum(num_steps);
|
||||||
|
slider->setSingleStep(1);
|
||||||
|
slider->setTickInterval(step_value);
|
||||||
|
slider->setValue(
|
||||||
|
static_cast<int>((option.value[i].float_value - option.min_value[i].float_value) / step_value));
|
||||||
|
connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label](int value) {
|
||||||
|
const float new_value = std::clamp(option.min_value[i].float_value +
|
||||||
|
(static_cast<float>(value) * option.step_value[i].float_value),
|
||||||
|
option.min_value[i].float_value, option.max_value[i].float_value);
|
||||||
|
option.value[i].float_value = new_value;
|
||||||
|
slider_label->setText(QString::number(new_value));
|
||||||
|
updateConfigForOption(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::RestoreDefaults, this);
|
||||||
|
connect(button_box, &QDialogButtonBox::clicked, this, &PostProcessingShaderConfigWidget::onResetDefaultsClicked);
|
||||||
|
m_layout->addWidget(button_box, row, 0, 1, -1);
|
||||||
|
|
||||||
|
row++;
|
||||||
|
m_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding), row, 0, 1, 3);
|
||||||
|
}
|
||||||
|
|
|
@ -2,34 +2,74 @@
|
||||||
// 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 "postprocessingchainconfigwidget.h"
|
|
||||||
#include "postprocessingshaderconfigwidget.h"
|
|
||||||
#include "ui_postprocessingsettingswidget.h"
|
#include "ui_postprocessingsettingswidget.h"
|
||||||
|
|
||||||
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
#include <QtWidgets/QWidget>
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
class SettingsDialog;
|
class SettingsDialog;
|
||||||
|
class PostProcessingShaderConfigWidget;
|
||||||
|
|
||||||
class PostProcessingSettingsWidget : public QWidget
|
class PostProcessingSettingsWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend PostProcessingShaderConfigWidget;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PostProcessingSettingsWidget(SettingsDialog* dialog, QWidget* parent);
|
PostProcessingSettingsWidget(SettingsDialog* dialog, QWidget* parent);
|
||||||
~PostProcessingSettingsWidget();
|
~PostProcessingSettingsWidget();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onChainAboutToChange();
|
void onAddButtonClicked();
|
||||||
void onChainSelectedShaderChanged(qint32 index);
|
void onRemoveButtonClicked();
|
||||||
void onConfigChanged(const std::string& new_config);
|
void onClearButtonClicked();
|
||||||
void onReloadClicked();
|
void onMoveUpButtonClicked();
|
||||||
|
void onMoveDownButtonClicked();
|
||||||
|
void onReloadButtonClicked();
|
||||||
|
void onSelectedShaderChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SettingsInterface& getSettingsInterfaceToUpdate();
|
||||||
|
void commitSettingsUpdate();
|
||||||
|
|
||||||
void connectUi();
|
void connectUi();
|
||||||
void updateShaderConfigPanel(s32 index);
|
void updateButtonsAndConfigPane(std::optional<u32> index);
|
||||||
|
std::optional<u32> getSelectedIndex() const;
|
||||||
|
void selectIndex(s32 index);
|
||||||
|
void updateList(const SettingsInterface& si);
|
||||||
|
void updateList();
|
||||||
|
|
||||||
SettingsDialog* m_dialog;
|
SettingsDialog* m_dialog;
|
||||||
|
|
||||||
Ui::PostProcessingSettingsWidget m_ui;
|
Ui::PostProcessingSettingsWidget m_ui;
|
||||||
|
|
||||||
PostProcessingShaderConfigWidget* m_shader_config = nullptr;
|
PostProcessingShaderConfigWidget* m_shader_config = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PostProcessingShaderConfigWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingSettingsWidget* widget, u32 stage_index,
|
||||||
|
std::vector<PostProcessing::ShaderOption> options);
|
||||||
|
~PostProcessingShaderConfigWidget();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onResetDefaultsClicked();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void createUi();
|
||||||
|
void updateConfigForOption(const PostProcessing::ShaderOption& option);
|
||||||
|
|
||||||
|
QGridLayout* m_layout;
|
||||||
|
|
||||||
|
PostProcessingSettingsWidget* m_widget;
|
||||||
|
std::vector<QWidget*> m_widgets;
|
||||||
|
|
||||||
|
u32 m_stage_index;
|
||||||
|
std::vector<PostProcessing::ShaderOption> m_options;
|
||||||
|
};
|
||||||
|
|
|
@ -67,20 +67,125 @@
|
||||||
<string>Post Processing Chain</string>
|
<string>Post Processing Chain</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<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 row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="PostProcessingChainConfigWidget" name="widget" native="true"/>
|
<widget class="QListWidget" name="stages">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>80</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="add">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="PostProcessingAdd">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="remove">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="PostProcessingRemove">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="clear">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="Clear">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="moveUp">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move Up</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="MoveUp">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="moveDown">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move Down</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="MoveDown">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -108,7 +213,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>679</width>
|
<width>679</width>
|
||||||
<height>438</height>
|
<height>238</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -129,14 +234,6 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>PostProcessingChainConfigWidget</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>duckstation-qt/postprocessingchainconfigwidget.h</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources>
|
<resources>
|
||||||
<include location="resources/resources.qrc"/>
|
<include location="resources/resources.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,175 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "postprocessingshaderconfigwidget.h"
|
|
||||||
#include <QtWidgets/QCheckBox>
|
|
||||||
#include <QtWidgets/QDialogButtonBox>
|
|
||||||
#include <QtWidgets/QGridLayout>
|
|
||||||
#include <QtWidgets/QLabel>
|
|
||||||
#include <QtWidgets/QSlider>
|
|
||||||
|
|
||||||
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
|
|
||||||
PostProcessingShader* shader)
|
|
||||||
: QWidget(parent), m_shader(shader)
|
|
||||||
{
|
|
||||||
createUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessingShaderConfigWidget::~PostProcessingShaderConfigWidget() = default;
|
|
||||||
|
|
||||||
void PostProcessingShaderConfigWidget::createUi()
|
|
||||||
{
|
|
||||||
m_layout = new QGridLayout(this);
|
|
||||||
u32 row = 0;
|
|
||||||
|
|
||||||
for (PostProcessingShader::Option& option : m_shader->GetOptions())
|
|
||||||
{
|
|
||||||
if (option.type == PostProcessingShader::Option::Type::Bool)
|
|
||||||
{
|
|
||||||
QCheckBox* checkbox = new QCheckBox(QString::fromStdString(option.ui_name), this);
|
|
||||||
checkbox->setChecked(option.value[0].int_value != 0);
|
|
||||||
connect(checkbox, &QCheckBox::stateChanged, [this, &option](int state) {
|
|
||||||
option.value[0].int_value = (state == Qt::Checked) ? 1 : 0;
|
|
||||||
configChanged();
|
|
||||||
});
|
|
||||||
connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults, [&option, checkbox]() {
|
|
||||||
QSignalBlocker sb(checkbox);
|
|
||||||
checkbox->setChecked(option.default_value[0].int_value != 0);
|
|
||||||
option.value = option.default_value;
|
|
||||||
});
|
|
||||||
m_layout->addWidget(checkbox, row, 0, 1, 3, Qt::AlignLeft);
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < option.vector_size; i++)
|
|
||||||
{
|
|
||||||
QString label;
|
|
||||||
if (option.vector_size <= 1)
|
|
||||||
{
|
|
||||||
label = QString::fromStdString(option.ui_name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static constexpr std::array<const char*, PostProcessingShader::Option::MAX_VECTOR_COMPONENTS + 1> suffixes = {
|
|
||||||
{QT_TR_NOOP("Red"), QT_TR_NOOP("Green"), QT_TR_NOOP("Blue"), QT_TR_NOOP("Alpha")}};
|
|
||||||
label = tr("%1 (%2)").arg(QString::fromStdString(option.ui_name)).arg(tr(suffixes[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_layout->addWidget(new QLabel(label, this), row, 0, 1, 1, Qt::AlignLeft);
|
|
||||||
|
|
||||||
QSlider* slider = new QSlider(Qt::Horizontal, this);
|
|
||||||
m_layout->addWidget(slider, row, 1, 1, 1, Qt::AlignLeft);
|
|
||||||
|
|
||||||
QLabel* slider_label = new QLabel(this);
|
|
||||||
m_layout->addWidget(slider_label, row, 2, 1, 1, Qt::AlignLeft);
|
|
||||||
|
|
||||||
if (option.type == PostProcessingShader::Option::Type::Int)
|
|
||||||
{
|
|
||||||
slider_label->setText(QString::number(option.value[i].int_value));
|
|
||||||
|
|
||||||
const int range = std::max(option.max_value[i].int_value - option.min_value[i].int_value, 1);
|
|
||||||
const int step_value =
|
|
||||||
(option.step_value[i].int_value != 0) ? option.step_value[i].int_value : ((range + 99) / 100);
|
|
||||||
const int num_steps = range / step_value;
|
|
||||||
slider->setMinimum(0);
|
|
||||||
slider->setMaximum(num_steps);
|
|
||||||
slider->setSingleStep(1);
|
|
||||||
slider->setTickInterval(step_value);
|
|
||||||
slider->setValue((option.value[i].int_value - option.min_value[i].int_value) / step_value);
|
|
||||||
connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label](int value) {
|
|
||||||
const int new_value = std::clamp(option.min_value[i].int_value + (value * option.step_value[i].int_value),
|
|
||||||
option.min_value[i].int_value, option.max_value[i].int_value);
|
|
||||||
option.value[i].int_value = new_value;
|
|
||||||
slider_label->setText(QString::number(new_value));
|
|
||||||
configChanged();
|
|
||||||
});
|
|
||||||
connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults,
|
|
||||||
[&option, i, slider, slider_label, step_value]() {
|
|
||||||
QSignalBlocker sb(slider);
|
|
||||||
slider->setValue((option.default_value[i].int_value - option.min_value[i].int_value) / step_value);
|
|
||||||
slider_label->setText(QString::number(option.default_value[i].int_value));
|
|
||||||
option.value = option.default_value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slider_label->setText(QString::number(option.value[i].float_value));
|
|
||||||
|
|
||||||
const float range = std::max(option.max_value[i].float_value - option.min_value[i].float_value, 1.0f);
|
|
||||||
const float step_value =
|
|
||||||
(option.step_value[i].float_value != 0) ? option.step_value[i].float_value : ((range + 99.0f) / 100.0f);
|
|
||||||
const float num_steps = range / step_value;
|
|
||||||
slider->setMinimum(0);
|
|
||||||
slider->setMaximum(num_steps);
|
|
||||||
slider->setSingleStep(1);
|
|
||||||
slider->setTickInterval(step_value);
|
|
||||||
slider->setValue(
|
|
||||||
static_cast<int>((option.value[i].float_value - option.min_value[i].float_value) / step_value));
|
|
||||||
connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label](int value) {
|
|
||||||
const float new_value = std::clamp(option.min_value[i].float_value +
|
|
||||||
(static_cast<float>(value) * option.step_value[i].float_value),
|
|
||||||
option.min_value[i].float_value, option.max_value[i].float_value);
|
|
||||||
option.value[i].float_value = new_value;
|
|
||||||
slider_label->setText(QString::number(new_value));
|
|
||||||
configChanged();
|
|
||||||
});
|
|
||||||
connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults,
|
|
||||||
[&option, i, slider, slider_label, step_value]() {
|
|
||||||
QSignalBlocker sb(slider);
|
|
||||||
slider->setValue(static_cast<int>(
|
|
||||||
(option.default_value[i].float_value - option.min_value[i].float_value) / step_value));
|
|
||||||
slider_label->setText(QString::number(option.default_value[i].float_value));
|
|
||||||
option.value = option.default_value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::RestoreDefaults, this);
|
|
||||||
connect(button_box, &QDialogButtonBox::clicked, this, &PostProcessingShaderConfigWidget::onResetToDefaultsClicked);
|
|
||||||
m_layout->addWidget(button_box, row, 0, 1, -1);
|
|
||||||
|
|
||||||
row++;
|
|
||||||
m_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding), row, 0, 1, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingShaderConfigWidget::onResetToDefaultsClicked()
|
|
||||||
{
|
|
||||||
resettingtoDefaults();
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessingShaderConfigDialog::PostProcessingShaderConfigDialog(QWidget* parent,
|
|
||||||
PostProcessingShader* shader)
|
|
||||||
: QDialog(parent)
|
|
||||||
{
|
|
||||||
setWindowTitle(tr("%1 Shader Options").arg(QString::fromStdString(shader->GetName())));
|
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
||||||
|
|
||||||
QGridLayout* layout = new QGridLayout(this);
|
|
||||||
m_widget = new PostProcessingShaderConfigWidget(this, shader);
|
|
||||||
layout->addWidget(m_widget);
|
|
||||||
|
|
||||||
connect(m_widget, &PostProcessingShaderConfigWidget::configChanged, this,
|
|
||||||
&PostProcessingShaderConfigDialog::onConfigChanged);
|
|
||||||
|
|
||||||
QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Close, this);
|
|
||||||
connect(button_box, &QDialogButtonBox::rejected, this, &PostProcessingShaderConfigDialog::onCloseClicked);
|
|
||||||
m_widget->getLayout()->addWidget(button_box, m_widget->getLayout()->rowCount() - 1, 2, 1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessingShaderConfigDialog::~PostProcessingShaderConfigDialog() = default;
|
|
||||||
|
|
||||||
void PostProcessingShaderConfigDialog::onConfigChanged()
|
|
||||||
{
|
|
||||||
configChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingShaderConfigDialog::onCloseClicked()
|
|
||||||
{
|
|
||||||
done(0);
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "util/postprocessing_shader.h"
|
|
||||||
|
|
||||||
#include <QtWidgets/QDialog>
|
|
||||||
#include <QtWidgets/QWidget>
|
|
||||||
|
|
||||||
class QGridLayout;
|
|
||||||
|
|
||||||
class PostProcessingShaderConfigWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingShader* shader);
|
|
||||||
~PostProcessingShaderConfigWidget();
|
|
||||||
|
|
||||||
QGridLayout* getLayout() { return m_layout; }
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void configChanged();
|
|
||||||
void resettingtoDefaults();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onResetToDefaultsClicked();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void createUi();
|
|
||||||
|
|
||||||
PostProcessingShader* m_shader;
|
|
||||||
QGridLayout* m_layout;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PostProcessingShaderConfigDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
PostProcessingShaderConfigDialog(QWidget* parent, PostProcessingShader* shader);
|
|
||||||
~PostProcessingShaderConfigDialog();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void configChanged();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onConfigChanged();
|
|
||||||
void onCloseClicked();
|
|
||||||
|
|
||||||
private:
|
|
||||||
PostProcessingShaderConfigWidget* m_widget;
|
|
||||||
};
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "util/ini_settings_interface.h"
|
#include "util/ini_settings_interface.h"
|
||||||
#include "util/input_manager.h"
|
#include "util/input_manager.h"
|
||||||
#include "util/platform_misc.h"
|
#include "util/platform_misc.h"
|
||||||
|
#include "util/postprocessing.h"
|
||||||
|
|
||||||
#include "scmversion/scmversion.h"
|
#include "scmversion/scmversion.h"
|
||||||
|
|
||||||
|
@ -947,7 +948,20 @@ void EmuThread::reloadPostProcessingShaders()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System::ReloadPostProcessingShaders();
|
if (System::IsValid())
|
||||||
|
PostProcessing::ReloadShaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuThread::updatePostProcessingSettings()
|
||||||
|
{
|
||||||
|
if (!isOnThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "updatePostProcessingSettings", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System::IsValid())
|
||||||
|
PostProcessing::UpdateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::clearInputBindStateFromSource(InputBindingKey key)
|
void EmuThread::clearInputBindStateFromSource(InputBindingKey key)
|
||||||
|
|
|
@ -188,6 +188,7 @@ public Q_SLOTS:
|
||||||
void setCheatEnabled(quint32 index, bool enabled);
|
void setCheatEnabled(quint32 index, bool enabled);
|
||||||
void applyCheat(quint32 index);
|
void applyCheat(quint32 index);
|
||||||
void reloadPostProcessingShaders();
|
void reloadPostProcessingShaders();
|
||||||
|
void updatePostProcessingSettings();
|
||||||
void clearInputBindStateFromSource(InputBindingKey key);
|
void clearInputBindStateFromSource(InputBindingKey key);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
|
|
@ -48,8 +48,8 @@ add_library(util
|
||||||
page_fault_handler.cpp
|
page_fault_handler.cpp
|
||||||
page_fault_handler.h
|
page_fault_handler.h
|
||||||
platform_misc.h
|
platform_misc.h
|
||||||
postprocessing_chain.cpp
|
postprocessing.cpp
|
||||||
postprocessing_chain.h
|
postprocessing.h
|
||||||
postprocessing_shader.cpp
|
postprocessing_shader.cpp
|
||||||
postprocessing_shader.h
|
postprocessing_shader.h
|
||||||
postprocessing_shader_glsl.cpp
|
postprocessing_shader_glsl.cpp
|
||||||
|
|
|
@ -0,0 +1,677 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#include "postprocessing.h"
|
||||||
|
#include "gpu_device.h"
|
||||||
|
#include "host.h"
|
||||||
|
#include "imgui_manager.h"
|
||||||
|
#include "postprocessing_shader.h"
|
||||||
|
#include "postprocessing_shader_glsl.h"
|
||||||
|
|
||||||
|
// TODO: Remove me
|
||||||
|
#include "core/host.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
#include "IconsFontAwesome5.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/file_system.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/path.h"
|
||||||
|
#include "common/string.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "common/timer.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
Log_SetChannel(PostProcessing);
|
||||||
|
|
||||||
|
// TODO: ProgressCallbacks for shader compiling, it can be a bit slow.
|
||||||
|
// TODO: buffer width/height is wrong on resize, need to change it somehow.
|
||||||
|
|
||||||
|
namespace PostProcessing {
|
||||||
|
template<typename T>
|
||||||
|
static u32 ParseVector(const std::string_view& line, ShaderOption::ValueVector* values);
|
||||||
|
|
||||||
|
static TinyString ValueToString(ShaderOption::Type type, u32 vector_size, const ShaderOption::ValueVector& value);
|
||||||
|
|
||||||
|
static TinyString GetStageConfigSection(u32 index);
|
||||||
|
static void CopyStageConfig(SettingsInterface& si, u32 old_index, u32 new_index);
|
||||||
|
static void SwapStageConfig(SettingsInterface& si, u32 lhs_index, u32 rhs_index);
|
||||||
|
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();
|
||||||
|
static void LoadStages();
|
||||||
|
static void DestroyTextures();
|
||||||
|
|
||||||
|
static std::vector<std::unique_ptr<PostProcessing::Shader>> s_stages;
|
||||||
|
static bool s_enabled = false;
|
||||||
|
|
||||||
|
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 std::unique_ptr<GPUTexture> s_input_texture;
|
||||||
|
static std::unique_ptr<GPUFramebuffer> s_input_framebuffer;
|
||||||
|
|
||||||
|
static std::unique_ptr<GPUTexture> s_output_texture;
|
||||||
|
static std::unique_ptr<GPUFramebuffer> s_output_framebuffer;
|
||||||
|
|
||||||
|
static std::unordered_map<u64, std::unique_ptr<GPUSampler>> s_samplers;
|
||||||
|
static std::unique_ptr<GPUTexture> s_dummy_texture;
|
||||||
|
} // namespace PostProcessing
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
u32 PostProcessing::ParseVector(const std::string_view& line, ShaderOption::ValueVector* values)
|
||||||
|
{
|
||||||
|
u32 index = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
while (index < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS)
|
||||||
|
{
|
||||||
|
while (start < line.size() && std::isspace(line[start]))
|
||||||
|
start++;
|
||||||
|
|
||||||
|
if (start >= line.size())
|
||||||
|
break;
|
||||||
|
|
||||||
|
size_t end = line.find(',', start);
|
||||||
|
if (end == std::string_view::npos)
|
||||||
|
end = line.size();
|
||||||
|
|
||||||
|
const std::string_view component = line.substr(start, end - start);
|
||||||
|
T value = StringUtil::FromChars<T>(component).value_or(static_cast<T>(0));
|
||||||
|
if constexpr (std::is_same_v<T, float>)
|
||||||
|
(*values)[index++].float_value = value;
|
||||||
|
else if constexpr (std::is_same_v<T, s32>)
|
||||||
|
(*values)[index++].int_value = value;
|
||||||
|
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 size = index;
|
||||||
|
|
||||||
|
for (; index < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS; index++)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, float>)
|
||||||
|
(*values)[index++].float_value = 0.0f;
|
||||||
|
else if constexpr (std::is_same_v<T, s32>)
|
||||||
|
(*values)[index++].int_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 PostProcessing::ShaderOption::ParseFloatVector(const std::string_view& line, ValueVector* values)
|
||||||
|
{
|
||||||
|
return ParseVector<float>(line, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 PostProcessing::ShaderOption::ParseIntVector(const std::string_view& line, ValueVector* values)
|
||||||
|
{
|
||||||
|
return ParseVector<s32>(line, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyString PostProcessing::ValueToString(ShaderOption::Type type, u32 vector_size,
|
||||||
|
const ShaderOption::ValueVector& value)
|
||||||
|
{
|
||||||
|
TinyString ret;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < vector_size; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
ret.AppendCharacter(',');
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ShaderOption::Type::Bool:
|
||||||
|
ret.AppendString((value[i].int_value != 0) ? "true" : "false");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ShaderOption::Type::Int:
|
||||||
|
ret.AppendFmtString("{}", value[i].int_value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ShaderOption::Type::Float:
|
||||||
|
ret.AppendFmtString("{}", value[i].float_value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> PostProcessing::GetAvailableShaderNames()
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> names;
|
||||||
|
|
||||||
|
FileSystem::FindResultsArray results;
|
||||||
|
FileSystem::FindFiles(Path::Combine(EmuFolders::Resources, "shaders").c_str(), "*.glsl",
|
||||||
|
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
|
||||||
|
FileSystem::FindFiles(EmuFolders::Shaders.c_str(), "*.glsl",
|
||||||
|
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS |
|
||||||
|
FILESYSTEM_FIND_KEEP_ARRAY,
|
||||||
|
&results);
|
||||||
|
|
||||||
|
for (FILESYSTEM_FIND_DATA& fd : results)
|
||||||
|
{
|
||||||
|
size_t pos = fd.FileName.rfind('.');
|
||||||
|
if (pos != std::string::npos && pos > 0)
|
||||||
|
fd.FileName.erase(pos);
|
||||||
|
|
||||||
|
// swap any backslashes for forward slashes so the config is cross-platform
|
||||||
|
for (size_t i = 0; i < fd.FileName.size(); i++)
|
||||||
|
{
|
||||||
|
if (fd.FileName[i] == '\\')
|
||||||
|
fd.FileName[i] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::none_of(names.begin(), names.end(), [&fd](const auto& other) { return fd.FileName == other.second; }))
|
||||||
|
{
|
||||||
|
std::string display_name = fmt::format(TRANSLATE_FS("PostProcessing", "{} [GLSL]"), fd.FileName);
|
||||||
|
names.emplace_back(std::move(display_name), std::move(fd.FileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyString PostProcessing::GetStageConfigSection(u32 index)
|
||||||
|
{
|
||||||
|
return TinyString::FromFmt("PostProcessing/Stage{}", index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::CopyStageConfig(SettingsInterface& si, u32 old_index, u32 new_index)
|
||||||
|
{
|
||||||
|
const TinyString old_section = GetStageConfigSection(old_index);
|
||||||
|
const TinyString new_section = GetStageConfigSection(new_index);
|
||||||
|
|
||||||
|
si.ClearSection(new_section);
|
||||||
|
|
||||||
|
for (const auto& [key, value] : si.GetKeyValueList(old_section))
|
||||||
|
si.SetStringValue(new_section, key.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::SwapStageConfig(SettingsInterface& si, u32 lhs_index, u32 rhs_index)
|
||||||
|
{
|
||||||
|
const TinyString lhs_section = GetStageConfigSection(lhs_index);
|
||||||
|
const TinyString rhs_section = GetStageConfigSection(rhs_index);
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string>> lhs_kvs = si.GetKeyValueList(lhs_section);
|
||||||
|
si.ClearSection(lhs_section);
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string>> rhs_kvs = si.GetKeyValueList(rhs_section);
|
||||||
|
si.ClearSection(rhs_section);
|
||||||
|
|
||||||
|
for (const auto& [key, value] : rhs_kvs)
|
||||||
|
si.SetStringValue(lhs_section, key.c_str(), value.c_str());
|
||||||
|
|
||||||
|
for (const auto& [key, value] : lhs_kvs)
|
||||||
|
si.SetStringValue(rhs_section, key.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 PostProcessing::Config::GetStageCount(const SettingsInterface& si)
|
||||||
|
{
|
||||||
|
return si.GetUIntValue("PostProcessing", "StageCount", 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PostProcessing::Config::GetStageShaderName(const SettingsInterface& si, u32 index)
|
||||||
|
{
|
||||||
|
return si.GetStringValue(GetStageConfigSection(index), "ShaderName");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PostProcessing::ShaderOption> PostProcessing::Config::GetStageOptions(const SettingsInterface& si,
|
||||||
|
u32 index)
|
||||||
|
{
|
||||||
|
std::vector<PostProcessing::ShaderOption> ret;
|
||||||
|
|
||||||
|
const TinyString section = GetStageConfigSection(index);
|
||||||
|
const std::string shader_name = si.GetStringValue(section, "ShaderName");
|
||||||
|
if (shader_name.empty())
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, nullptr);
|
||||||
|
if (!shader)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = shader->TakeOptions();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::Config::AddStage(SettingsInterface& si, const std::string& shader_name, Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Shader> shader = TryLoadingShader(shader_name, true, error);
|
||||||
|
if (!shader)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const u32 index = GetStageCount(si);
|
||||||
|
si.SetUIntValue("PostProcessing", "StageCount", index + 1);
|
||||||
|
|
||||||
|
const TinyString section = GetStageConfigSection(index);
|
||||||
|
si.SetStringValue(section, "ShaderName", shader->GetName().c_str());
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Leave options unset for now.
|
||||||
|
for (const ShaderOption& option : shader->GetOptions())
|
||||||
|
{
|
||||||
|
si.SetStringValue(section, option.name.c_str(),
|
||||||
|
ValueToString(option.type, option.vector_size, option.default_value));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::RemoveStage(SettingsInterface& si, u32 index)
|
||||||
|
{
|
||||||
|
const u32 stage_count = GetStageCount(si);
|
||||||
|
if (index >= stage_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (u32 i = index; i < (stage_count - 1); i++)
|
||||||
|
CopyStageConfig(si, i + 1, i);
|
||||||
|
|
||||||
|
si.ClearSection(GetStageConfigSection(stage_count - 1));
|
||||||
|
si.SetUIntValue("PostProcessing", "StageCount", stage_count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::MoveStageUp(SettingsInterface& si, u32 index)
|
||||||
|
{
|
||||||
|
const u32 stage_count = GetStageCount(si);
|
||||||
|
if (index == 0 || index >= stage_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SwapStageConfig(si, index, index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::MoveStageDown(SettingsInterface& si, u32 index)
|
||||||
|
{
|
||||||
|
const u32 stage_count = GetStageCount(si);
|
||||||
|
if ((index + 1) >= stage_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SwapStageConfig(si, index, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::SetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option)
|
||||||
|
{
|
||||||
|
const TinyString section = GetStageConfigSection(index);
|
||||||
|
si.SetStringValue(section, option.name.c_str(), ValueToString(option.type, option.vector_size, option.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::UnsetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option)
|
||||||
|
{
|
||||||
|
const TinyString section = GetStageConfigSection(index);
|
||||||
|
si.DeleteValue(section, option.name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Config::ClearStages(SettingsInterface& si)
|
||||||
|
{
|
||||||
|
const u32 count = GetStageCount(si);
|
||||||
|
for (s32 i = static_cast<s32>(count - 1); i >= 0; i--)
|
||||||
|
si.ClearSection(GetStageConfigSection(static_cast<u32>(i)));
|
||||||
|
si.SetUIntValue("PostProcessing", "StageCount", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::IsActive()
|
||||||
|
{
|
||||||
|
return s_enabled && !s_stages.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::IsEnabled()
|
||||||
|
{
|
||||||
|
return s_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::SetEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
s_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name,
|
||||||
|
bool only_config, Error* error)
|
||||||
|
{
|
||||||
|
std::string filename(Path::Combine(EmuFolders::Shaders, fmt::format("{}.glsl", shader_name)));
|
||||||
|
if (FileSystem::FileExists(filename.c_str()))
|
||||||
|
{
|
||||||
|
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();
|
||||||
|
if (shader->LoadFromFile(std::string(shader_name), filename.c_str(), error))
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> resource_str(
|
||||||
|
Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str()));
|
||||||
|
if (resource_str.has_value())
|
||||||
|
{
|
||||||
|
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();
|
||||||
|
if (shader->LoadFromString(std::string(shader_name), std::move(resource_str.value()), error))
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_ErrorPrint(fmt::format("Failed to load shader '{}'", shader_name).c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::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);
|
||||||
|
s_stages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsInterface& PostProcessing::GetLoadSettingsInterface()
|
||||||
|
{
|
||||||
|
// 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("PostProcessing", "Enabled"))
|
||||||
|
return *game_si;
|
||||||
|
else
|
||||||
|
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;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage_count > 0)
|
||||||
|
{
|
||||||
|
s_timer.Reset();
|
||||||
|
Log_DevPrintf("Loaded %u post-processing stages.", stage_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 (stage_count > 0)
|
||||||
|
{
|
||||||
|
s_timer.Reset();
|
||||||
|
Log_DevPrintf("Loaded %u 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()
|
||||||
|
{
|
||||||
|
s_dummy_texture.reset();
|
||||||
|
s_samplers.clear();
|
||||||
|
s_enabled = false;
|
||||||
|
decltype(s_stages)().swap(s_stages);
|
||||||
|
DestroyTextures();
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUTexture* PostProcessing::GetInputTexture()
|
||||||
|
{
|
||||||
|
return s_input_texture.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUFramebuffer* PostProcessing::GetInputFramebuffer()
|
||||||
|
{
|
||||||
|
return s_input_framebuffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Common::Timer& PostProcessing::GetTimer()
|
||||||
|
{
|
||||||
|
return s_timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUSampler* PostProcessing::GetSampler(const GPUSampler::Config& config)
|
||||||
|
{
|
||||||
|
auto it = s_samplers.find(config.key);
|
||||||
|
if (it != s_samplers.end())
|
||||||
|
return it->second.get();
|
||||||
|
|
||||||
|
std::unique_ptr<GPUSampler> sampler = g_gpu_device->CreateSampler(config);
|
||||||
|
if (!sampler)
|
||||||
|
Log_ErrorPrint(fmt::format("Failed to create GPU sampler with config={:X}", config.key).c_str());
|
||||||
|
|
||||||
|
it = s_samplers.emplace(config.key, std::move(sampler)).first;
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUTexture* PostProcessing::GetDummyTexture()
|
||||||
|
{
|
||||||
|
if (s_dummy_texture)
|
||||||
|
return s_dummy_texture.get();
|
||||||
|
|
||||||
|
const u32 zero = 0;
|
||||||
|
s_dummy_texture = g_gpu_device->CreateTexture(1, 1, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
|
||||||
|
&zero, sizeof(zero));
|
||||||
|
if (!s_dummy_texture)
|
||||||
|
Log_ErrorPrint("Failed to create dummy texture.");
|
||||||
|
|
||||||
|
return s_dummy_texture.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height)
|
||||||
|
{
|
||||||
|
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->CreateTexture(target_width, target_height, 1, 1, 1,
|
||||||
|
GPUTexture::Type::RenderTarget, target_format)) ||
|
||||||
|
!(s_input_framebuffer = g_gpu_device->CreateFramebuffer(s_input_texture.get())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(s_output_texture = g_gpu_device->CreateTexture(target_width, target_height, 1, 1, 1,
|
||||||
|
GPUTexture::Type::RenderTarget, target_format)) ||
|
||||||
|
!(s_output_framebuffer = g_gpu_device->CreateFramebuffer(s_output_texture.get())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& shader : s_stages)
|
||||||
|
{
|
||||||
|
if (!shader->CompilePipeline(target_format, target_width, target_height) ||
|
||||||
|
!shader->ResizeOutput(target_format, target_width, target_height))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
s_output_framebuffer.reset();
|
||||||
|
s_output_texture.reset();
|
||||||
|
|
||||||
|
s_input_framebuffer.reset();
|
||||||
|
s_input_texture.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessing::Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
|
||||||
|
s32 final_height, s32 orig_width, s32 orig_height)
|
||||||
|
{
|
||||||
|
GL_SCOPE("PostProcessing Apply");
|
||||||
|
|
||||||
|
const u32 target_width = final_target ? final_target->GetWidth() : g_gpu_device->GetWindowWidth();
|
||||||
|
const u32 target_height = final_target ? final_target->GetHeight() : g_gpu_device->GetWindowHeight();
|
||||||
|
const GPUTexture::Format target_format =
|
||||||
|
final_target ? final_target->GetRT()->GetFormat() : g_gpu_device->GetWindowFormat();
|
||||||
|
if (!CheckTargets(target_format, target_width, target_height))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_gpu_device->SetViewportAndScissor(final_left, final_top, final_width, final_height);
|
||||||
|
|
||||||
|
GPUTexture* input = s_input_texture.get();
|
||||||
|
GPUFramebuffer* input_fb = s_input_framebuffer.get();
|
||||||
|
GPUTexture* output = s_output_texture.get();
|
||||||
|
GPUFramebuffer* output_fb = s_output_framebuffer.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_fb, final_left, final_top, final_width, final_height,
|
||||||
|
orig_width, orig_height, s_target_width, s_target_height))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_final)
|
||||||
|
{
|
||||||
|
output->MakeReadyForSampling();
|
||||||
|
std::swap(input, output);
|
||||||
|
std::swap(input_fb, output_fb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gpu_device.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Common
|
||||||
|
{
|
||||||
|
class Timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GPUSampler;
|
||||||
|
class GPUFramebuffer;
|
||||||
|
class GPUTexture;
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
class SettingsInterface;
|
||||||
|
|
||||||
|
namespace PostProcessing {
|
||||||
|
struct ShaderOption
|
||||||
|
{
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
MAX_VECTOR_COMPONENTS = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Float
|
||||||
|
};
|
||||||
|
|
||||||
|
union Value
|
||||||
|
{
|
||||||
|
s32 int_value;
|
||||||
|
float float_value;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Value) == sizeof(u32));
|
||||||
|
|
||||||
|
using ValueVector = std::array<Value, MAX_VECTOR_COMPONENTS>;
|
||||||
|
static_assert(sizeof(ValueVector) == sizeof(u32) * MAX_VECTOR_COMPONENTS);
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string ui_name;
|
||||||
|
std::string dependent_option;
|
||||||
|
Type type;
|
||||||
|
u32 vector_size;
|
||||||
|
u32 buffer_size;
|
||||||
|
u32 buffer_offset;
|
||||||
|
ValueVector default_value;
|
||||||
|
ValueVector min_value;
|
||||||
|
ValueVector max_value;
|
||||||
|
ValueVector step_value;
|
||||||
|
ValueVector value;
|
||||||
|
|
||||||
|
static u32 ParseIntVector(const std::string_view& line, ValueVector* values);
|
||||||
|
static u32 ParseFloatVector(const std::string_view& line, ValueVector* values);
|
||||||
|
|
||||||
|
static constexpr ValueVector MakeIntVector(s32 x, s32 y = 0, s32 z = 0, s32 w = 0)
|
||||||
|
{
|
||||||
|
ValueVector ret = {};
|
||||||
|
ret[0].int_value = x;
|
||||||
|
ret[1].int_value = y;
|
||||||
|
ret[2].int_value = z;
|
||||||
|
ret[3].int_value = w;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ValueVector MakeFloatVector(float x, float y = 0, float z = 0, float w = 0)
|
||||||
|
{
|
||||||
|
ValueVector ret = {};
|
||||||
|
ret[0].float_value = x;
|
||||||
|
ret[1].float_value = y;
|
||||||
|
ret[2].float_value = z;
|
||||||
|
ret[3].float_value = w;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// [display_name, filename]
|
||||||
|
std::vector<std::pair<std::string, std::string>> GetAvailableShaderNames();
|
||||||
|
|
||||||
|
namespace Config {
|
||||||
|
u32 GetStageCount(const SettingsInterface& si);
|
||||||
|
std::string GetStageShaderName(const SettingsInterface& si, u32 index);
|
||||||
|
std::vector<ShaderOption> GetStageOptions(const SettingsInterface& si, u32 index);
|
||||||
|
|
||||||
|
bool AddStage(SettingsInterface& si, const std::string& shader_name, Error* error);
|
||||||
|
void RemoveStage(SettingsInterface& si, u32 index);
|
||||||
|
void MoveStageUp(SettingsInterface& si, u32 index);
|
||||||
|
void MoveStageDown(SettingsInterface& si, u32 index);
|
||||||
|
void SetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option);
|
||||||
|
void UnsetStageOption(SettingsInterface& si, u32 index, const ShaderOption& option);
|
||||||
|
void ClearStages(SettingsInterface& si);
|
||||||
|
} // namespace Config
|
||||||
|
|
||||||
|
bool IsActive();
|
||||||
|
bool IsEnabled();
|
||||||
|
void SetEnabled(bool enabled);
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
/// Reloads configuration.
|
||||||
|
void UpdateSettings();
|
||||||
|
|
||||||
|
/// Temporarily toggles post-processing on/off.
|
||||||
|
void Toggle();
|
||||||
|
|
||||||
|
/// Reloads post processing shaders with the current configuration.
|
||||||
|
bool ReloadShaders();
|
||||||
|
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
GPUTexture* GetInputTexture();
|
||||||
|
GPUFramebuffer* GetInputFramebuffer();
|
||||||
|
const Common::Timer& GetTimer();
|
||||||
|
|
||||||
|
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height);
|
||||||
|
|
||||||
|
bool Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
||||||
|
s32 orig_width, s32 orig_height);
|
||||||
|
|
||||||
|
GPUSampler* GetSampler(const GPUSampler::Config& config);
|
||||||
|
GPUTexture* GetDummyTexture();
|
||||||
|
|
||||||
|
}; // namespace PostProcessing
|
|
@ -1,279 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#include "postprocessing_chain.h"
|
|
||||||
#include "gpu_device.h"
|
|
||||||
#include "postprocessing_shader_glsl.h"
|
|
||||||
|
|
||||||
#include "core/host.h"
|
|
||||||
#include "core/settings.h"
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/file_system.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
#include "common/path.h"
|
|
||||||
#include "common/string.h"
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
Log_SetChannel(PostProcessingChain);
|
|
||||||
|
|
||||||
static std::unique_ptr<PostProcessingShader> TryLoadingShader(const std::string_view& shader_name)
|
|
||||||
{
|
|
||||||
std::string filename(Path::Combine(EmuFolders::Shaders, fmt::format("{}.glsl", shader_name)));
|
|
||||||
if (FileSystem::FileExists(filename.c_str()))
|
|
||||||
{
|
|
||||||
std::unique_ptr<PostProcessingShaderGLSL> shader = std::make_unique<PostProcessingShaderGLSL>();
|
|
||||||
if (shader->LoadFromFile(std::string(shader_name), filename.c_str()))
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> resource_str(
|
|
||||||
Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str()));
|
|
||||||
if (resource_str.has_value())
|
|
||||||
{
|
|
||||||
std::unique_ptr<PostProcessingShaderGLSL> shader = std::make_unique<PostProcessingShaderGLSL>();
|
|
||||||
if (shader->LoadFromString(std::string(shader_name), std::move(resource_str.value())))
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log_ErrorPrintf(fmt::format("Failed to load shader '{}'", shader_name).c_str());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessingChain::PostProcessingChain() = default;
|
|
||||||
|
|
||||||
PostProcessingChain::~PostProcessingChain() = default;
|
|
||||||
|
|
||||||
void PostProcessingChain::AddShader(std::unique_ptr<PostProcessingShader> shader)
|
|
||||||
{
|
|
||||||
m_shaders.push_back(std::move(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessingChain::AddStage(const std::string_view& name)
|
|
||||||
{
|
|
||||||
std::unique_ptr<PostProcessingShader> shader = TryLoadingShader(name);
|
|
||||||
if (!shader)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_shaders.push_back(std::move(shader));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PostProcessingChain::GetConfigString() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
for (const auto& shader : m_shaders)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
ss << ':';
|
|
||||||
else
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
ss << shader->GetName();
|
|
||||||
std::string config_string = shader->GetConfigString();
|
|
||||||
if (!config_string.empty())
|
|
||||||
ss << ';' << config_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessingChain::CreateFromString(const std::string_view& chain_config)
|
|
||||||
{
|
|
||||||
std::vector<std::unique_ptr<PostProcessingShader>> shaders;
|
|
||||||
|
|
||||||
size_t last_sep = 0;
|
|
||||||
while (last_sep < chain_config.size())
|
|
||||||
{
|
|
||||||
size_t next_sep = chain_config.find(':', last_sep);
|
|
||||||
if (next_sep == std::string::npos)
|
|
||||||
next_sep = chain_config.size();
|
|
||||||
|
|
||||||
const std::string_view shader_config = chain_config.substr(last_sep, next_sep - last_sep);
|
|
||||||
size_t first_shader_sep = shader_config.find(';');
|
|
||||||
if (first_shader_sep == std::string::npos)
|
|
||||||
first_shader_sep = shader_config.size();
|
|
||||||
|
|
||||||
const std::string_view shader_name = shader_config.substr(0, first_shader_sep);
|
|
||||||
if (!shader_name.empty())
|
|
||||||
{
|
|
||||||
std::unique_ptr<PostProcessingShader> shader = TryLoadingShader(shader_name);
|
|
||||||
if (!shader)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (first_shader_sep < shader_config.size())
|
|
||||||
shader->SetConfigString(shader_config.substr(first_shader_sep + 1));
|
|
||||||
|
|
||||||
shaders.push_back(std::move(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
last_sep = next_sep + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shaders.empty())
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Postprocessing chain is empty!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_shaders = std::move(shaders);
|
|
||||||
Log_InfoPrintf("Loaded postprocessing chain of %zu shaders", m_shaders.size());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> PostProcessingChain::GetAvailableShaderNames()
|
|
||||||
{
|
|
||||||
std::vector<std::string> names;
|
|
||||||
|
|
||||||
FileSystem::FindResultsArray results;
|
|
||||||
FileSystem::FindFiles(Path::Combine(EmuFolders::Resources, "shaders").c_str(), "*.glsl",
|
|
||||||
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
|
|
||||||
FileSystem::FindFiles(EmuFolders::Shaders.c_str(), "*.glsl",
|
|
||||||
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_RELATIVE_PATHS |
|
|
||||||
FILESYSTEM_FIND_KEEP_ARRAY,
|
|
||||||
&results);
|
|
||||||
|
|
||||||
for (FILESYSTEM_FIND_DATA& fd : results)
|
|
||||||
{
|
|
||||||
size_t pos = fd.FileName.rfind('.');
|
|
||||||
if (pos != std::string::npos && pos > 0)
|
|
||||||
fd.FileName.erase(pos);
|
|
||||||
|
|
||||||
// swap any backslashes for forward slashes so the config is cross-platform
|
|
||||||
for (size_t i = 0; i < fd.FileName.size(); i++)
|
|
||||||
{
|
|
||||||
if (fd.FileName[i] == '\\')
|
|
||||||
fd.FileName[i] = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::none_of(names.begin(), names.end(), [&fd](const std::string& other) { return fd.FileName == other; }))
|
|
||||||
{
|
|
||||||
names.push_back(std::move(fd.FileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChain::RemoveStage(u32 index)
|
|
||||||
{
|
|
||||||
Assert(index < m_shaders.size());
|
|
||||||
m_shaders.erase(m_shaders.begin() + index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChain::MoveStageUp(u32 index)
|
|
||||||
{
|
|
||||||
Assert(index < m_shaders.size());
|
|
||||||
if (index == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto shader = std::move(m_shaders[index]);
|
|
||||||
m_shaders.erase(m_shaders.begin() + index);
|
|
||||||
m_shaders.insert(m_shaders.begin() + (index - 1u), std::move(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChain::MoveStageDown(u32 index)
|
|
||||||
{
|
|
||||||
Assert(index < m_shaders.size());
|
|
||||||
if (index == (m_shaders.size() - 1u))
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto shader = std::move(m_shaders[index]);
|
|
||||||
m_shaders.erase(m_shaders.begin() + index);
|
|
||||||
m_shaders.insert(m_shaders.begin() + (index + 1u), std::move(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingChain::ClearStages()
|
|
||||||
{
|
|
||||||
m_shaders.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessingChain::CheckTargets(GPUTexture::Format format, u32 target_width, u32 target_height)
|
|
||||||
{
|
|
||||||
if (m_target_format == format && m_target_width == target_width && m_target_height == target_height)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// In case any allocs fail.
|
|
||||||
m_target_format = GPUTexture::Format::Unknown;
|
|
||||||
m_target_width = 0;
|
|
||||||
m_target_height = 0;
|
|
||||||
m_output_framebuffer.reset();
|
|
||||||
m_output_texture.reset();
|
|
||||||
m_input_framebuffer.reset();
|
|
||||||
m_input_texture.reset();
|
|
||||||
|
|
||||||
if (!(m_input_texture =
|
|
||||||
g_gpu_device->CreateTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget, format)) ||
|
|
||||||
!(m_input_framebuffer = g_gpu_device->CreateFramebuffer(m_input_texture.get())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(m_output_texture =
|
|
||||||
g_gpu_device->CreateTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget, format)) ||
|
|
||||||
!(m_output_framebuffer = g_gpu_device->CreateFramebuffer(m_output_texture.get())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& shader : m_shaders)
|
|
||||||
{
|
|
||||||
if (!shader->CompilePipeline(format, target_width, target_height) ||
|
|
||||||
!shader->ResizeOutput(format, target_width, target_height))
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to compile one or more post-processing shaders, disabling.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_target_format = format;
|
|
||||||
m_target_width = target_width;
|
|
||||||
m_target_height = target_height;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessingChain::Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
|
|
||||||
s32 final_height, s32 orig_width, s32 orig_height)
|
|
||||||
{
|
|
||||||
GL_SCOPE("PostProcessingChain Apply");
|
|
||||||
|
|
||||||
const u32 target_width = final_target ? final_target->GetWidth() : g_gpu_device->GetWindowWidth();
|
|
||||||
const u32 target_height = final_target ? final_target->GetHeight() : g_gpu_device->GetWindowHeight();
|
|
||||||
const GPUTexture::Format target_format =
|
|
||||||
final_target ? final_target->GetRT()->GetFormat() : g_gpu_device->GetWindowFormat();
|
|
||||||
if (!CheckTargets(target_format, target_width, target_height))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
g_gpu_device->SetViewportAndScissor(final_left, final_top, final_width, final_height);
|
|
||||||
|
|
||||||
GPUTexture* input = m_input_texture.get();
|
|
||||||
GPUFramebuffer* input_fb = m_input_framebuffer.get();
|
|
||||||
GPUTexture* output = m_output_texture.get();
|
|
||||||
GPUFramebuffer* output_fb = m_output_framebuffer.get();
|
|
||||||
input->MakeReadyForSampling();
|
|
||||||
|
|
||||||
for (const std::unique_ptr<PostProcessingShader>& stage : m_shaders)
|
|
||||||
{
|
|
||||||
const bool is_final = (stage.get() == m_shaders.back().get());
|
|
||||||
|
|
||||||
if (!stage->Apply(input, is_final ? final_target : output_fb, final_left, final_top, final_width, final_height,
|
|
||||||
orig_width, orig_height, m_target_width, m_target_height))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_final)
|
|
||||||
{
|
|
||||||
output->MakeReadyForSampling();
|
|
||||||
std::swap(input, output);
|
|
||||||
std::swap(input_fb, output_fb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "gpu_device.h"
|
|
||||||
#include "postprocessing_shader.h"
|
|
||||||
|
|
||||||
#include "common/timer.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class GPUSampler;
|
|
||||||
class GPUFramebuffer;
|
|
||||||
class GPUTexture;
|
|
||||||
|
|
||||||
class PostProcessingChain
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PostProcessingChain();
|
|
||||||
~PostProcessingChain();
|
|
||||||
|
|
||||||
static std::vector<std::string> GetAvailableShaderNames();
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsEmpty() const { return m_shaders.empty(); }
|
|
||||||
ALWAYS_INLINE u32 GetStageCount() const { return static_cast<u32>(m_shaders.size()); }
|
|
||||||
ALWAYS_INLINE const PostProcessingShader* GetShaderStage(u32 i) const { return m_shaders[i].get(); }
|
|
||||||
ALWAYS_INLINE PostProcessingShader* GetShaderStage(u32 i) { return m_shaders[i].get(); }
|
|
||||||
ALWAYS_INLINE GPUTexture* GetInputTexture() const { return m_input_texture.get(); }
|
|
||||||
ALWAYS_INLINE GPUFramebuffer* GetInputFramebuffer() const { return m_input_framebuffer.get(); }
|
|
||||||
|
|
||||||
void AddShader(std::unique_ptr<PostProcessingShader> shader);
|
|
||||||
bool AddStage(const std::string_view& name);
|
|
||||||
void RemoveStage(u32 index);
|
|
||||||
void MoveStageUp(u32 index);
|
|
||||||
void MoveStageDown(u32 index);
|
|
||||||
void ClearStages();
|
|
||||||
|
|
||||||
std::string GetConfigString() const;
|
|
||||||
|
|
||||||
bool CreateFromString(const std::string_view& chain_config);
|
|
||||||
|
|
||||||
bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height);
|
|
||||||
|
|
||||||
bool Apply(GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width, s32 final_height,
|
|
||||||
s32 orig_width, s32 orig_height);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::unique_ptr<PostProcessingShader>> m_shaders;
|
|
||||||
|
|
||||||
GPUTexture::Format m_target_format = GPUTexture::Format::Unknown;
|
|
||||||
u32 m_target_width = 0;
|
|
||||||
u32 m_target_height = 0;
|
|
||||||
|
|
||||||
std::unique_ptr<GPUTexture> m_input_texture;
|
|
||||||
std::unique_ptr<GPUFramebuffer> m_input_framebuffer;
|
|
||||||
|
|
||||||
std::unique_ptr<GPUTexture> m_output_texture;
|
|
||||||
std::unique_ptr<GPUFramebuffer> m_output_framebuffer;
|
|
||||||
};
|
|
|
@ -11,9 +11,9 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
Log_SetChannel(PostProcessingShader);
|
Log_SetChannel(PostProcessing);
|
||||||
|
|
||||||
void PostProcessingShader::ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value)
|
void PostProcessing::Shader::ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value)
|
||||||
{
|
{
|
||||||
size_t key_start = 0;
|
size_t key_start = 0;
|
||||||
while (key_start < line.size() && std::isspace(line[key_start]))
|
while (key_start < line.size() && std::isspace(line[key_start]))
|
||||||
|
@ -48,168 +48,85 @@ void PostProcessingShader::ParseKeyValue(const std::string_view& line, std::stri
|
||||||
*value = line.substr(value_start, value_end - value_start);
|
*value = line.substr(value_start, value_end - value_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
PostProcessing::Shader::Shader() = default;
|
||||||
u32 PostProcessingShader::ParseVector(const std::string_view& line, PostProcessingShader::Option::ValueVector* values)
|
|
||||||
{
|
|
||||||
u32 index = 0;
|
|
||||||
size_t start = 0;
|
|
||||||
while (index < PostProcessingShader::Option::MAX_VECTOR_COMPONENTS)
|
|
||||||
{
|
|
||||||
while (start < line.size() && std::isspace(line[start]))
|
|
||||||
start++;
|
|
||||||
|
|
||||||
if (start >= line.size())
|
PostProcessing::Shader::Shader(std::string name) : m_name(std::move(name))
|
||||||
break;
|
|
||||||
|
|
||||||
size_t end = line.find(',', start);
|
|
||||||
if (end == std::string_view::npos)
|
|
||||||
end = line.size();
|
|
||||||
|
|
||||||
const std::string_view component = line.substr(start, end - start);
|
|
||||||
T value = StringUtil::FromChars<T>(component).value_or(static_cast<T>(0));
|
|
||||||
if constexpr (std::is_same_v<T, float>)
|
|
||||||
(*values)[index++].float_value = value;
|
|
||||||
else if constexpr (std::is_same_v<T, s32>)
|
|
||||||
(*values)[index++].int_value = value;
|
|
||||||
|
|
||||||
start = end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 size = index;
|
|
||||||
|
|
||||||
for (; index < PostProcessingShader::Option::MAX_VECTOR_COMPONENTS; index++)
|
|
||||||
{
|
|
||||||
if constexpr (std::is_same_v<T, float>)
|
|
||||||
(*values)[index++].float_value = 0.0f;
|
|
||||||
else if constexpr (std::is_same_v<T, s32>)
|
|
||||||
(*values)[index++].int_value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
template u32 PostProcessingShader::ParseVector<s32>(const std::string_view& line,
|
|
||||||
PostProcessingShader::Option::ValueVector* values);
|
|
||||||
template u32 PostProcessingShader::ParseVector<float>(const std::string_view& line,
|
|
||||||
PostProcessingShader::Option::ValueVector* values);
|
|
||||||
|
|
||||||
PostProcessingShader::PostProcessingShader() = default;
|
|
||||||
|
|
||||||
PostProcessingShader::PostProcessingShader(std::string name) : m_name(std::move(name))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingShader::~PostProcessingShader() = default;
|
PostProcessing::Shader::~Shader() = default;
|
||||||
|
|
||||||
bool PostProcessingShader::IsValid() const
|
bool PostProcessing::Shader::IsValid() const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const std::string_view& name) const
|
std::vector<PostProcessing::ShaderOption> PostProcessing::Shader::TakeOptions()
|
||||||
{
|
{
|
||||||
for (const Option& option : m_options)
|
return std::move(m_options);
|
||||||
{
|
|
||||||
if (option.name == name)
|
|
||||||
return &option;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const std::string_view& name)
|
void PostProcessing::Shader::LoadOptions(SettingsInterface& si, const char* section)
|
||||||
{
|
{
|
||||||
for (Option& option : m_options)
|
for (ShaderOption& option : m_options)
|
||||||
{
|
{
|
||||||
if (option.name == name)
|
if (option.type == ShaderOption::Type::Bool)
|
||||||
return &option;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PostProcessingShader::GetConfigString() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
bool first = true;
|
|
||||||
for (const Option& option : m_options)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
ss << ';';
|
|
||||||
else
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
ss << option.name;
|
|
||||||
ss << '=';
|
|
||||||
|
|
||||||
for (u32 i = 0; i < option.vector_size; i++)
|
|
||||||
{
|
{
|
||||||
if (i > 0)
|
const bool new_value = si.GetBoolValue(section, option.name.c_str(), option.default_value[0].int_value != 0);
|
||||||
ss << ",";
|
if ((option.value[0].int_value != 0) != new_value)
|
||||||
|
|
||||||
switch (option.type)
|
|
||||||
{
|
{
|
||||||
case Option::Type::Bool:
|
option.value[0].int_value = new_value ? 1 : 0;
|
||||||
ss << ((option.value[i].int_value != 0) ? "true" : "false");
|
OnOptionChanged(option);
|
||||||
break;
|
|
||||||
|
|
||||||
case Option::Type::Int:
|
|
||||||
ss << option.value[i].int_value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Option::Type::Float:
|
|
||||||
ss << option.value[i].float_value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessingShader::SetConfigString(const std::string_view& str)
|
|
||||||
{
|
|
||||||
for (Option& option : m_options)
|
|
||||||
option.value = option.default_value;
|
|
||||||
|
|
||||||
size_t last_sep = 0;
|
|
||||||
while (last_sep < str.size())
|
|
||||||
{
|
|
||||||
size_t next_sep = str.find(';', last_sep);
|
|
||||||
if (next_sep == std::string_view::npos)
|
|
||||||
next_sep = str.size();
|
|
||||||
|
|
||||||
const std::string_view kv = str.substr(last_sep, next_sep - last_sep);
|
|
||||||
std::string_view key, value;
|
|
||||||
ParseKeyValue(kv, &key, &value);
|
|
||||||
if (!key.empty() && !value.empty())
|
|
||||||
{
|
{
|
||||||
Option* option = GetOptionByName(key);
|
ShaderOption::ValueVector value = option.default_value;
|
||||||
if (option)
|
|
||||||
|
std::string config_value;
|
||||||
|
if (si.GetStringValue(section, option.name.c_str(), &config_value))
|
||||||
{
|
{
|
||||||
switch (option->type)
|
const u32 value_vector_size = (option.type == ShaderOption::Type::Int) ?
|
||||||
|
ShaderOption::ParseIntVector(config_value, &value) :
|
||||||
|
ShaderOption::ParseFloatVector(config_value, &value);
|
||||||
|
if (value_vector_size != option.vector_size)
|
||||||
{
|
{
|
||||||
case Option::Type::Bool:
|
Log_WarningPrintf("Only got %u of %u elements for '%s' in config section %s.", value_vector_size,
|
||||||
option->value[0].int_value = StringUtil::FromChars<bool>(value).value_or(false) ? 1 : 0;
|
option.vector_size, option.name.c_str(), section);
|
||||||
break;
|
|
||||||
|
|
||||||
case Option::Type::Int:
|
|
||||||
ParseVector<s32>(value, &option->value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Option::Type::Float:
|
|
||||||
ParseVector<float>(value, &option->value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
last_sep = next_sep + 1;
|
if (std::memcmp(&option.value, &value, sizeof(value)) != 0)
|
||||||
|
{
|
||||||
|
option.value = value;
|
||||||
|
OnOptionChanged(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PostProcessing::ShaderOption* PostProcessing::Shader::GetOptionByName(const std::string_view& name) const
|
||||||
|
{
|
||||||
|
for (const ShaderOption& option : m_options)
|
||||||
|
{
|
||||||
|
if (option.name == name)
|
||||||
|
return &option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessing::ShaderOption* PostProcessing::Shader::GetOptionByName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
for (ShaderOption& option : m_options)
|
||||||
|
{
|
||||||
|
if (option.name == name)
|
||||||
|
return &option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::Shader::OnOptionChanged(const ShaderOption& option)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "postprocessing.h"
|
||||||
|
|
||||||
|
#include "gpu_texture.h"
|
||||||
|
|
||||||
#include "common/rectangle.h"
|
#include "common/rectangle.h"
|
||||||
|
#include "common/settings_interface.h"
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "gpu_device.h"
|
#include "gpu_device.h"
|
||||||
|
@ -16,68 +21,27 @@
|
||||||
class GPUPipeline;
|
class GPUPipeline;
|
||||||
class GPUTexture;
|
class GPUTexture;
|
||||||
|
|
||||||
class PostProcessingChain;
|
namespace PostProcessing {
|
||||||
|
|
||||||
class PostProcessingShader
|
class Shader
|
||||||
{
|
{
|
||||||
friend PostProcessingChain;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Option
|
Shader();
|
||||||
{
|
Shader(std::string name);
|
||||||
enum : u32
|
virtual ~Shader();
|
||||||
{
|
|
||||||
MAX_VECTOR_COMPONENTS = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
Bool,
|
|
||||||
Int,
|
|
||||||
Float
|
|
||||||
};
|
|
||||||
|
|
||||||
union Value
|
|
||||||
{
|
|
||||||
s32 int_value;
|
|
||||||
float float_value;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Value) == sizeof(u32));
|
|
||||||
|
|
||||||
using ValueVector = std::array<Value, MAX_VECTOR_COMPONENTS>;
|
|
||||||
static_assert(sizeof(ValueVector) == sizeof(u32) * MAX_VECTOR_COMPONENTS);
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
std::string ui_name;
|
|
||||||
std::string dependent_option;
|
|
||||||
Type type;
|
|
||||||
u32 vector_size;
|
|
||||||
u32 buffer_size;
|
|
||||||
u32 buffer_offset;
|
|
||||||
ValueVector default_value;
|
|
||||||
ValueVector min_value;
|
|
||||||
ValueVector max_value;
|
|
||||||
ValueVector step_value;
|
|
||||||
ValueVector value;
|
|
||||||
};
|
|
||||||
|
|
||||||
PostProcessingShader();
|
|
||||||
PostProcessingShader(std::string name);
|
|
||||||
virtual ~PostProcessingShader();
|
|
||||||
|
|
||||||
ALWAYS_INLINE const std::string& GetName() const { return m_name; }
|
ALWAYS_INLINE const std::string& GetName() const { return m_name; }
|
||||||
ALWAYS_INLINE const std::vector<Option>& GetOptions() const { return m_options; }
|
ALWAYS_INLINE const std::vector<ShaderOption>& GetOptions() const { return m_options; }
|
||||||
ALWAYS_INLINE std::vector<Option>& GetOptions() { return m_options; }
|
ALWAYS_INLINE std::vector<ShaderOption>& GetOptions() { return m_options; }
|
||||||
ALWAYS_INLINE bool HasOptions() const { return !m_options.empty(); }
|
ALWAYS_INLINE bool HasOptions() const { return !m_options.empty(); }
|
||||||
|
|
||||||
virtual bool IsValid() const = 0;
|
virtual bool IsValid() const = 0;
|
||||||
|
|
||||||
const Option* GetOptionByName(const std::string_view& name) const;
|
std::vector<ShaderOption> TakeOptions();
|
||||||
Option* GetOptionByName(const std::string_view& name);
|
void LoadOptions(SettingsInterface& si, const char* section);
|
||||||
|
|
||||||
std::string GetConfigString() const;
|
const ShaderOption* GetOptionByName(const std::string_view& name) const;
|
||||||
void SetConfigString(const std::string_view& str);
|
ShaderOption* GetOptionByName(const std::string_view& name);
|
||||||
|
|
||||||
virtual bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) = 0;
|
virtual bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) = 0;
|
||||||
|
|
||||||
|
@ -89,11 +53,10 @@ public:
|
||||||
protected:
|
protected:
|
||||||
static void ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value);
|
static void ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value);
|
||||||
|
|
||||||
template<typename T>
|
virtual void OnOptionChanged(const ShaderOption& option);
|
||||||
static u32 ParseVector(const std::string_view& line, PostProcessingShader::Option::ValueVector* values);
|
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::vector<Option> m_options;
|
std::vector<ShaderOption> m_options;
|
||||||
|
|
||||||
Common::Timer m_timer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace PostProcessing
|
|
@ -12,7 +12,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
Log_SetChannel(PostProcessingShaderGLSL);
|
Log_SetChannel(PostProcessing);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class PostProcessingGLSLShaderGen : public ShaderGen
|
class PostProcessingGLSLShaderGen : public ShaderGen
|
||||||
|
@ -21,34 +21,34 @@ public:
|
||||||
PostProcessingGLSLShaderGen(RenderAPI render_api, bool supports_dual_source_blend);
|
PostProcessingGLSLShaderGen(RenderAPI render_api, bool supports_dual_source_blend);
|
||||||
~PostProcessingGLSLShaderGen();
|
~PostProcessingGLSLShaderGen();
|
||||||
|
|
||||||
std::string GeneratePostProcessingVertexShader(const PostProcessingShaderGLSL& shader);
|
std::string GeneratePostProcessingVertexShader(const PostProcessing::GLSLShader& shader);
|
||||||
std::string GeneratePostProcessingFragmentShader(const PostProcessingShaderGLSL& shader);
|
std::string GeneratePostProcessingFragmentShader(const PostProcessing::GLSLShader& shader);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteUniformBuffer(std::stringstream& ss, const PostProcessingShaderGLSL& shader, bool use_push_constants);
|
void WriteUniformBuffer(std::stringstream& ss, const PostProcessing::GLSLShader& shader, bool use_push_constants);
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PostProcessingShaderGLSL::PostProcessingShaderGLSL() = default;
|
PostProcessing::GLSLShader::GLSLShader() = default;
|
||||||
|
|
||||||
PostProcessingShaderGLSL::PostProcessingShaderGLSL(std::string name, std::string code) : m_code(code)
|
PostProcessing::GLSLShader::GLSLShader(std::string name, std::string code) : m_code(code)
|
||||||
{
|
{
|
||||||
m_name = std::move(name);
|
m_name = std::move(name);
|
||||||
LoadOptions();
|
LoadOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessingShaderGLSL::~PostProcessingShaderGLSL() = default;
|
PostProcessing::GLSLShader::~GLSLShader() = default;
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::LoadFromFile(std::string name, const char* filename)
|
bool PostProcessing::GLSLShader::LoadFromFile(std::string name, const char* filename, Error* error)
|
||||||
{
|
{
|
||||||
std::optional<std::string> code = FileSystem::ReadFileToString(filename);
|
std::optional<std::string> code = FileSystem::ReadFileToString(filename, error);
|
||||||
if (!code.has_value() || code->empty())
|
if (!code.has_value() || code->empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return LoadFromString(std::move(name), code.value());
|
return LoadFromString(std::move(name), code.value(), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::LoadFromString(std::string name, std::string code)
|
bool PostProcessing::GLSLShader::LoadFromString(std::string name, std::string code, Error* error)
|
||||||
{
|
{
|
||||||
m_name = std::move(name);
|
m_name = std::move(name);
|
||||||
m_code = std::move(code);
|
m_code = std::move(code);
|
||||||
|
@ -57,21 +57,21 @@ bool PostProcessingShaderGLSL::LoadFromString(std::string name, std::string code
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::IsValid() const
|
bool PostProcessing::GLSLShader::IsValid() const
|
||||||
{
|
{
|
||||||
return !m_name.empty() && !m_code.empty();
|
return !m_name.empty() && !m_code.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 PostProcessingShaderGLSL::GetUniformsSize() const
|
u32 PostProcessing::GLSLShader::GetUniformsSize() const
|
||||||
{
|
{
|
||||||
// lazy packing. todo improve.
|
// lazy packing. todo improve.
|
||||||
return sizeof(CommonUniforms) + (sizeof(Option::ValueVector) * static_cast<u32>(m_options.size()));
|
return sizeof(CommonUniforms) + (sizeof(ShaderOption::ValueVector) * static_cast<u32>(m_options.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingShaderGLSL::FillUniformBuffer(void* buffer, u32 texture_width, s32 texture_height,
|
void PostProcessing::GLSLShader::FillUniformBuffer(void* buffer, u32 texture_width, s32 texture_height,
|
||||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
||||||
s32 texture_view_height, u32 window_width, u32 window_height,
|
s32 texture_view_height, u32 window_width, u32 window_height,
|
||||||
s32 original_width, s32 original_height, float time) const
|
s32 original_width, s32 original_height, float time) const
|
||||||
{
|
{
|
||||||
CommonUniforms* common = static_cast<CommonUniforms*>(buffer);
|
CommonUniforms* common = static_cast<CommonUniforms*>(buffer);
|
||||||
|
|
||||||
|
@ -105,14 +105,14 @@ void PostProcessingShaderGLSL::FillUniformBuffer(void* buffer, u32 texture_width
|
||||||
common->time = time;
|
common->time = time;
|
||||||
|
|
||||||
u8* option_values = reinterpret_cast<u8*>(common + 1);
|
u8* option_values = reinterpret_cast<u8*>(common + 1);
|
||||||
for (const Option& option : m_options)
|
for (const ShaderOption& option : m_options)
|
||||||
{
|
{
|
||||||
std::memcpy(option_values, option.value.data(), sizeof(Option::ValueVector));
|
std::memcpy(option_values, option.value.data(), sizeof(ShaderOption::ValueVector));
|
||||||
option_values += sizeof(Option::ValueVector);
|
option_values += sizeof(ShaderOption::ValueVector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::CompilePipeline(GPUTexture::Format format, u32 width, u32 height)
|
bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
if (m_pipeline)
|
if (m_pipeline)
|
||||||
m_pipeline.reset();
|
m_pipeline.reset();
|
||||||
|
@ -155,11 +155,11 @@ bool PostProcessingShaderGLSL::CompilePipeline(GPUTexture::Format format, u32 wi
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::Apply(GPUTexture* input, GPUFramebuffer* final_target, s32 final_left, s32 final_top,
|
bool PostProcessing::GLSLShader::Apply(GPUTexture* input, GPUFramebuffer* final_target, s32 final_left, s32 final_top,
|
||||||
s32 final_width, s32 final_height, s32 orig_width, s32 orig_height,
|
s32 final_width, s32 final_height, s32 orig_width, s32 orig_height,
|
||||||
u32 target_width, u32 target_height)
|
u32 target_width, u32 target_height)
|
||||||
{
|
{
|
||||||
GL_SCOPE("PostProcessingShader %s", m_name.c_str());
|
GL_SCOPE("GLSL Shader %s", m_name.c_str());
|
||||||
|
|
||||||
// Assumes final stage has been cleared already.
|
// Assumes final stage has been cleared already.
|
||||||
if (!final_target)
|
if (!final_target)
|
||||||
|
@ -179,18 +179,19 @@ bool PostProcessingShaderGLSL::Apply(GPUTexture* input, GPUFramebuffer* final_ta
|
||||||
const u32 uniforms_size = GetUniformsSize();
|
const u32 uniforms_size = GetUniformsSize();
|
||||||
void* uniforms = g_gpu_device->MapUniformBuffer(uniforms_size);
|
void* uniforms = g_gpu_device->MapUniformBuffer(uniforms_size);
|
||||||
FillUniformBuffer(uniforms, input->GetWidth(), input->GetHeight(), final_left, final_top, final_width, final_height,
|
FillUniformBuffer(uniforms, input->GetWidth(), input->GetHeight(), final_left, final_top, final_width, final_height,
|
||||||
target_width, target_height, orig_width, orig_height, static_cast<float>(m_timer.GetTimeSeconds()));
|
target_width, target_height, orig_width, orig_height,
|
||||||
|
static_cast<float>(PostProcessing::GetTimer().GetTimeSeconds()));
|
||||||
g_gpu_device->UnmapUniformBuffer(uniforms_size);
|
g_gpu_device->UnmapUniformBuffer(uniforms_size);
|
||||||
g_gpu_device->Draw(3, 0);
|
g_gpu_device->Draw(3, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PostProcessingShaderGLSL::ResizeOutput(GPUTexture::Format format, u32 width, u32 height)
|
bool PostProcessing::GLSLShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingShaderGLSL::LoadOptions()
|
void PostProcessing::GLSLShader::LoadOptions()
|
||||||
{
|
{
|
||||||
// Adapted from Dolphin's PostProcessingConfiguration::LoadOptions().
|
// Adapted from Dolphin's PostProcessingConfiguration::LoadOptions().
|
||||||
constexpr char config_start_delimiter[] = "[configuration]";
|
constexpr char config_start_delimiter[] = "[configuration]";
|
||||||
|
@ -209,7 +210,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
|
|
||||||
std::istringstream in(configuration_string);
|
std::istringstream in(configuration_string);
|
||||||
|
|
||||||
Option current_option = {};
|
ShaderOption current_option = {};
|
||||||
while (!in.eof())
|
while (!in.eof())
|
||||||
{
|
{
|
||||||
std::string line_str;
|
std::string line_str;
|
||||||
|
@ -229,7 +230,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
size_t endpos = line_view.find("]");
|
size_t endpos = line_view.find("]");
|
||||||
if (endpos != std::string::npos)
|
if (endpos != std::string::npos)
|
||||||
{
|
{
|
||||||
if (current_option.type != Option::Type::Invalid)
|
if (current_option.type != ShaderOption::Type::Invalid)
|
||||||
{
|
{
|
||||||
current_option.value = current_option.default_value;
|
current_option.value = current_option.default_value;
|
||||||
if (current_option.ui_name.empty())
|
if (current_option.ui_name.empty())
|
||||||
|
@ -244,11 +245,11 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
// New section!
|
// New section!
|
||||||
std::string_view sub = line_view.substr(1, endpos - 1);
|
std::string_view sub = line_view.substr(1, endpos - 1);
|
||||||
if (sub == "OptionBool")
|
if (sub == "OptionBool")
|
||||||
current_option.type = Option::Type::Bool;
|
current_option.type = ShaderOption::Type::Bool;
|
||||||
else if (sub == "OptionRangeFloat")
|
else if (sub == "OptionRangeFloat")
|
||||||
current_option.type = Option::Type::Float;
|
current_option.type = ShaderOption::Type::Float;
|
||||||
else if (sub == "OptionRangeInteger")
|
else if (sub == "OptionRangeInteger")
|
||||||
current_option.type = Option::Type::Int;
|
current_option.type = ShaderOption::Type::Int;
|
||||||
else
|
else
|
||||||
Log_ErrorPrintf("Invalid option type: '%s'", line_str.c_str());
|
Log_ErrorPrintf("Invalid option type: '%s'", line_str.c_str());
|
||||||
|
|
||||||
|
@ -256,7 +257,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_option.type == Option::Type::Invalid)
|
if (current_option.type == ShaderOption::Type::Invalid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string_view key, value;
|
std::string_view key, value;
|
||||||
|
@ -277,7 +278,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
}
|
}
|
||||||
else if (key == "MinValue" || key == "MaxValue" || key == "DefaultValue" || key == "StepAmount")
|
else if (key == "MinValue" || key == "MaxValue" || key == "DefaultValue" || key == "StepAmount")
|
||||||
{
|
{
|
||||||
Option::ValueVector* dst_array;
|
ShaderOption::ValueVector* dst_array;
|
||||||
if (key == "MinValue")
|
if (key == "MinValue")
|
||||||
dst_array = ¤t_option.min_value;
|
dst_array = ¤t_option.min_value;
|
||||||
else if (key == "MaxValue")
|
else if (key == "MaxValue")
|
||||||
|
@ -288,12 +289,12 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
dst_array = ¤t_option.step_value;
|
dst_array = ¤t_option.step_value;
|
||||||
|
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
if (current_option.type == Option::Type::Bool)
|
if (current_option.type == ShaderOption::Type::Bool)
|
||||||
(*dst_array)[size++].int_value = StringUtil::FromChars<bool>(value).value_or(false) ? 1 : 0;
|
(*dst_array)[size++].int_value = StringUtil::FromChars<bool>(value).value_or(false) ? 1 : 0;
|
||||||
else if (current_option.type == Option::Type::Float)
|
else if (current_option.type == ShaderOption::Type::Float)
|
||||||
size = ParseVector<float>(value, dst_array);
|
size = PostProcessing::ShaderOption::ParseFloatVector(value, dst_array);
|
||||||
else if (current_option.type == Option::Type::Int)
|
else if (current_option.type == ShaderOption::Type::Int)
|
||||||
size = ParseVector<s32>(value, dst_array);
|
size = PostProcessing::ShaderOption::ParseIntVector(value, dst_array);
|
||||||
|
|
||||||
current_option.vector_size =
|
current_option.vector_size =
|
||||||
(current_option.vector_size == 0) ? size : std::min(current_option.vector_size, size);
|
(current_option.vector_size == 0) ? size : std::min(current_option.vector_size, size);
|
||||||
|
@ -306,7 +307,8 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_option.type != Option::Type::Invalid && !current_option.name.empty() && current_option.vector_size > 0)
|
if (current_option.type != ShaderOption::Type::Invalid && !current_option.name.empty() &&
|
||||||
|
current_option.vector_size > 0)
|
||||||
{
|
{
|
||||||
current_option.value = current_option.default_value;
|
current_option.value = current_option.default_value;
|
||||||
if (current_option.ui_name.empty())
|
if (current_option.ui_name.empty())
|
||||||
|
@ -323,7 +325,7 @@ PostProcessingGLSLShaderGen::PostProcessingGLSLShaderGen(RenderAPI render_api, b
|
||||||
|
|
||||||
PostProcessingGLSLShaderGen::~PostProcessingGLSLShaderGen() = default;
|
PostProcessingGLSLShaderGen::~PostProcessingGLSLShaderGen() = default;
|
||||||
|
|
||||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(const PostProcessingShaderGLSL& shader)
|
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(const PostProcessing::GLSLShader& shader)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
|
@ -346,7 +348,7 @@ std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(cons
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingFragmentShader(const PostProcessingShaderGLSL& shader)
|
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingFragmentShader(const PostProcessing::GLSLShader& shader)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
|
@ -459,7 +461,7 @@ void main(in float2 v_tex0_ : TEXCOORD0, in float4 v_pos_ : SV_Position, out flo
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, const PostProcessingShaderGLSL& shader,
|
void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, const PostProcessing::GLSLShader& shader,
|
||||||
bool use_push_constants)
|
bool use_push_constants)
|
||||||
{
|
{
|
||||||
u32 pad_counter = 0;
|
u32 pad_counter = 0;
|
||||||
|
@ -478,31 +480,31 @@ void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, cons
|
||||||
ss << " float ubo_pad" << (pad_counter++) << ";\n";
|
ss << " float ubo_pad" << (pad_counter++) << ";\n";
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
|
|
||||||
static constexpr std::array<const char*, PostProcessingShader::Option::MAX_VECTOR_COMPONENTS + 1> vector_size_suffix =
|
static constexpr std::array<const char*, PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS + 1> vector_size_suffix =
|
||||||
{{"", "", "2", "3", "4"}};
|
{{"", "", "2", "3", "4"}};
|
||||||
for (const PostProcessingShader::Option& option : shader.GetOptions())
|
for (const PostProcessing::ShaderOption& option : shader.GetOptions())
|
||||||
{
|
{
|
||||||
switch (option.type)
|
switch (option.type)
|
||||||
{
|
{
|
||||||
case PostProcessingShader::Option::Type::Bool:
|
case PostProcessing::ShaderOption::Type::Bool:
|
||||||
ss << " int " << option.name << ";\n";
|
ss << " int " << option.name << ";\n";
|
||||||
for (u32 i = option.vector_size; i < PostProcessingShader::Option::MAX_VECTOR_COMPONENTS; i++)
|
for (u32 i = option.vector_size; i < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS; i++)
|
||||||
ss << " int ubo_pad" << (pad_counter++) << ";\n";
|
ss << " int ubo_pad" << (pad_counter++) << ";\n";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PostProcessingShader::Option::Type::Int:
|
case PostProcessing::ShaderOption::Type::Int:
|
||||||
{
|
{
|
||||||
ss << " int" << vector_size_suffix[option.vector_size] << " " << option.name << ";\n";
|
ss << " int" << vector_size_suffix[option.vector_size] << " " << option.name << ";\n";
|
||||||
for (u32 i = option.vector_size; i < PostProcessingShader::Option::MAX_VECTOR_COMPONENTS; i++)
|
for (u32 i = option.vector_size; i < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS; i++)
|
||||||
ss << " int ubo_pad" << (pad_counter++) << ";\n";
|
ss << " int ubo_pad" << (pad_counter++) << ";\n";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PostProcessingShader::Option::Type::Float:
|
case PostProcessing::ShaderOption::Type::Float:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ss << " float" << vector_size_suffix[option.vector_size] << " " << option.name << ";\n";
|
ss << " float" << vector_size_suffix[option.vector_size] << " " << option.name << ";\n";
|
||||||
for (u32 i = option.vector_size; i < PostProcessingShader::Option::MAX_VECTOR_COMPONENTS; i++)
|
for (u32 i = option.vector_size; i < PostProcessing::ShaderOption::MAX_VECTOR_COMPONENTS; i++)
|
||||||
ss << " float ubo_pad" << (pad_counter++) << ";\n";
|
ss << " float ubo_pad" << (pad_counter++) << ";\n";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,19 +5,21 @@
|
||||||
|
|
||||||
#include "postprocessing_shader.h"
|
#include "postprocessing_shader.h"
|
||||||
|
|
||||||
class PostProcessingShaderGLSL final : public PostProcessingShader
|
namespace PostProcessing {
|
||||||
|
|
||||||
|
class GLSLShader final : public Shader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PostProcessingShaderGLSL();
|
GLSLShader();
|
||||||
PostProcessingShaderGLSL(std::string name, std::string code);
|
GLSLShader(std::string name, std::string code);
|
||||||
~PostProcessingShaderGLSL();
|
~GLSLShader();
|
||||||
|
|
||||||
ALWAYS_INLINE const std::string& GetCode() const { return m_code; }
|
ALWAYS_INLINE const std::string& GetCode() const { return m_code; }
|
||||||
|
|
||||||
bool IsValid() const override;
|
bool IsValid() const override;
|
||||||
|
|
||||||
bool LoadFromFile(std::string name, const char* filename);
|
bool LoadFromFile(std::string name, const char* filename, Error* error);
|
||||||
bool LoadFromString(std::string name, std::string code);
|
bool LoadFromString(std::string name, std::string code, Error* error);
|
||||||
|
|
||||||
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override;
|
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override;
|
||||||
bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height) override;
|
bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height) override;
|
||||||
|
@ -51,3 +53,5 @@ private:
|
||||||
std::unique_ptr<GPUPipeline> m_pipeline;
|
std::unique_ptr<GPUPipeline> m_pipeline;
|
||||||
std::unique_ptr<GPUSampler> m_sampler;
|
std::unique_ptr<GPUSampler> m_sampler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace PostProcessing
|
|
@ -62,7 +62,7 @@
|
||||||
<ClInclude Include="page_fault_handler.h" />
|
<ClInclude Include="page_fault_handler.h" />
|
||||||
<ClInclude Include="cd_subchannel_replacement.h" />
|
<ClInclude Include="cd_subchannel_replacement.h" />
|
||||||
<ClInclude Include="platform_misc.h" />
|
<ClInclude Include="platform_misc.h" />
|
||||||
<ClInclude Include="postprocessing_chain.h" />
|
<ClInclude Include="postprocessing.h" />
|
||||||
<ClInclude Include="postprocessing_shader.h" />
|
<ClInclude Include="postprocessing_shader.h" />
|
||||||
<ClInclude Include="postprocessing_shader_glsl.h" />
|
<ClInclude Include="postprocessing_shader_glsl.h" />
|
||||||
<ClInclude Include="sdl_input_source.h" />
|
<ClInclude Include="sdl_input_source.h" />
|
||||||
|
@ -162,7 +162,7 @@
|
||||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="platform_misc_win32.cpp" />
|
<ClCompile Include="platform_misc_win32.cpp" />
|
||||||
<ClCompile Include="postprocessing_chain.cpp" />
|
<ClCompile Include="postprocessing.cpp" />
|
||||||
<ClCompile Include="postprocessing_shader.cpp" />
|
<ClCompile Include="postprocessing_shader.cpp" />
|
||||||
<ClCompile Include="postprocessing_shader_glsl.cpp" />
|
<ClCompile Include="postprocessing_shader_glsl.cpp" />
|
||||||
<ClCompile Include="sdl_input_source.cpp" />
|
<ClCompile Include="sdl_input_source.cpp" />
|
||||||
|
@ -242,4 +242,4 @@
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<ClInclude Include="shadergen.h" />
|
<ClInclude Include="shadergen.h" />
|
||||||
<ClInclude Include="imgui_fullscreen.h" />
|
<ClInclude Include="imgui_fullscreen.h" />
|
||||||
<ClInclude Include="imgui_manager.h" />
|
<ClInclude Include="imgui_manager.h" />
|
||||||
<ClInclude Include="postprocessing_chain.h" />
|
<ClInclude Include="postprocessing.h" />
|
||||||
<ClInclude Include="postprocessing_shader.h" />
|
<ClInclude Include="postprocessing_shader.h" />
|
||||||
<ClInclude Include="input_source.h" />
|
<ClInclude Include="input_source.h" />
|
||||||
<ClInclude Include="platform_misc.h" />
|
<ClInclude Include="platform_misc.h" />
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
<ClCompile Include="shadergen.cpp" />
|
<ClCompile Include="shadergen.cpp" />
|
||||||
<ClCompile Include="imgui_fullscreen.cpp" />
|
<ClCompile Include="imgui_fullscreen.cpp" />
|
||||||
<ClCompile Include="imgui_manager.cpp" />
|
<ClCompile Include="imgui_manager.cpp" />
|
||||||
<ClCompile Include="postprocessing_chain.cpp" />
|
<ClCompile Include="postprocessing.cpp" />
|
||||||
<ClCompile Include="postprocessing_shader.cpp" />
|
<ClCompile Include="postprocessing_shader.cpp" />
|
||||||
<ClCompile Include="platform_misc_win32.cpp" />
|
<ClCompile Include="platform_misc_win32.cpp" />
|
||||||
<ClCompile Include="sdl_input_source.cpp" />
|
<ClCompile Include="sdl_input_source.cpp" />
|
||||||
|
@ -150,4 +150,4 @@
|
||||||
<UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier>
|
<UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in New Issue