PostProcessing: Refactor config to use separate sections
This commit is contained in:
@ -48,8 +48,8 @@ add_library(util
|
||||
page_fault_handler.cpp
|
||||
page_fault_handler.h
|
||||
platform_misc.h
|
||||
postprocessing_chain.cpp
|
||||
postprocessing_chain.h
|
||||
postprocessing.cpp
|
||||
postprocessing.h
|
||||
postprocessing_shader.cpp
|
||||
postprocessing_shader.h
|
||||
postprocessing_shader_glsl.cpp
|
||||
|
||||
677
src/util/postprocessing.cpp
Normal file
677
src/util/postprocessing.cpp
Normal file
@ -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;
|
||||
}
|
||||
134
src/util/postprocessing.h
Normal file
134
src/util/postprocessing.h
Normal file
@ -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 <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;
|
||||
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);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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++;
|
||||
PostProcessing::Shader::Shader() = default;
|
||||
|
||||
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 < 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))
|
||||
PostProcessing::Shader::Shader(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;
|
||||
}
|
||||
|
||||
const PostProcessingShader::Option* PostProcessingShader::GetOptionByName(const std::string_view& name) const
|
||||
std::vector<PostProcessing::ShaderOption> PostProcessing::Shader::TakeOptions()
|
||||
{
|
||||
for (const Option& option : m_options)
|
||||
{
|
||||
if (option.name == name)
|
||||
return &option;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return std::move(m_options);
|
||||
}
|
||||
|
||||
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)
|
||||
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 (option.type == ShaderOption::Type::Bool)
|
||||
{
|
||||
if (i > 0)
|
||||
ss << ",";
|
||||
|
||||
switch (option.type)
|
||||
const bool new_value = si.GetBoolValue(section, option.name.c_str(), option.default_value[0].int_value != 0);
|
||||
if ((option.value[0].int_value != 0) != new_value)
|
||||
{
|
||||
case Option::Type::Bool:
|
||||
ss << ((option.value[i].int_value != 0) ? "true" : "false");
|
||||
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;
|
||||
option.value[0].int_value = new_value ? 1 : 0;
|
||||
OnOptionChanged(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
else
|
||||
{
|
||||
Option* option = GetOptionByName(key);
|
||||
if (option)
|
||||
ShaderOption::ValueVector value = option.default_value;
|
||||
|
||||
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:
|
||||
option->value[0].int_value = StringUtil::FromChars<bool>(value).value_or(false) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case Option::Type::Int:
|
||||
ParseVector<s32>(value, &option->value);
|
||||
break;
|
||||
|
||||
case Option::Type::Float:
|
||||
ParseVector<float>(value, &option->value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
Log_WarningPrintf("Only got %u of %u elements for '%s' in config section %s.", value_vector_size,
|
||||
option.vector_size, option.name.c_str(), section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include "postprocessing.h"
|
||||
|
||||
#include "gpu_texture.h"
|
||||
|
||||
#include "common/rectangle.h"
|
||||
#include "common/settings_interface.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/types.h"
|
||||
#include "gpu_device.h"
|
||||
@ -16,68 +21,27 @@
|
||||
class GPUPipeline;
|
||||
class GPUTexture;
|
||||
|
||||
class PostProcessingChain;
|
||||
namespace PostProcessing {
|
||||
|
||||
class PostProcessingShader
|
||||
class Shader
|
||||
{
|
||||
friend PostProcessingChain;
|
||||
|
||||
public:
|
||||
struct Option
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
PostProcessingShader();
|
||||
PostProcessingShader(std::string name);
|
||||
virtual ~PostProcessingShader();
|
||||
Shader();
|
||||
Shader(std::string name);
|
||||
virtual ~Shader();
|
||||
|
||||
ALWAYS_INLINE const std::string& GetName() const { return m_name; }
|
||||
ALWAYS_INLINE const std::vector<Option>& GetOptions() const { return m_options; }
|
||||
ALWAYS_INLINE std::vector<Option>& GetOptions() { return m_options; }
|
||||
ALWAYS_INLINE const std::vector<ShaderOption>& GetOptions() const { return m_options; }
|
||||
ALWAYS_INLINE std::vector<ShaderOption>& GetOptions() { return m_options; }
|
||||
ALWAYS_INLINE bool HasOptions() const { return !m_options.empty(); }
|
||||
|
||||
virtual bool IsValid() const = 0;
|
||||
|
||||
const Option* GetOptionByName(const std::string_view& name) const;
|
||||
Option* GetOptionByName(const std::string_view& name);
|
||||
std::vector<ShaderOption> TakeOptions();
|
||||
void LoadOptions(SettingsInterface& si, const char* section);
|
||||
|
||||
std::string GetConfigString() const;
|
||||
void SetConfigString(const std::string_view& str);
|
||||
const ShaderOption* GetOptionByName(const std::string_view& name) const;
|
||||
ShaderOption* GetOptionByName(const std::string_view& name);
|
||||
|
||||
virtual bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) = 0;
|
||||
|
||||
@ -89,11 +53,10 @@ public:
|
||||
protected:
|
||||
static void ParseKeyValue(const std::string_view& line, std::string_view* key, std::string_view* value);
|
||||
|
||||
template<typename T>
|
||||
static u32 ParseVector(const std::string_view& line, PostProcessingShader::Option::ValueVector* values);
|
||||
virtual void OnOptionChanged(const ShaderOption& option);
|
||||
|
||||
std::string m_name;
|
||||
std::vector<Option> m_options;
|
||||
|
||||
Common::Timer m_timer;
|
||||
std::vector<ShaderOption> m_options;
|
||||
};
|
||||
|
||||
} // namespace PostProcessing
|
||||
@ -12,7 +12,7 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
Log_SetChannel(PostProcessingShaderGLSL);
|
||||
Log_SetChannel(PostProcessing);
|
||||
|
||||
namespace {
|
||||
class PostProcessingGLSLShaderGen : public ShaderGen
|
||||
@ -21,34 +21,34 @@ public:
|
||||
PostProcessingGLSLShaderGen(RenderAPI render_api, bool supports_dual_source_blend);
|
||||
~PostProcessingGLSLShaderGen();
|
||||
|
||||
std::string GeneratePostProcessingVertexShader(const PostProcessingShaderGLSL& shader);
|
||||
std::string GeneratePostProcessingFragmentShader(const PostProcessingShaderGLSL& shader);
|
||||
std::string GeneratePostProcessingVertexShader(const PostProcessing::GLSLShader& shader);
|
||||
std::string GeneratePostProcessingFragmentShader(const PostProcessing::GLSLShader& shader);
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
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())
|
||||
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_code = std::move(code);
|
||||
@ -57,21 +57,21 @@ bool PostProcessingShaderGLSL::LoadFromString(std::string name, std::string code
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostProcessingShaderGLSL::IsValid() const
|
||||
bool PostProcessing::GLSLShader::IsValid() const
|
||||
{
|
||||
return !m_name.empty() && !m_code.empty();
|
||||
}
|
||||
|
||||
u32 PostProcessingShaderGLSL::GetUniformsSize() const
|
||||
u32 PostProcessing::GLSLShader::GetUniformsSize() const
|
||||
{
|
||||
// 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,
|
||||
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
|
||||
s32 texture_view_height, u32 window_width, u32 window_height,
|
||||
s32 original_width, s32 original_height, float time) const
|
||||
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_height, u32 window_width, u32 window_height,
|
||||
s32 original_width, s32 original_height, float time) const
|
||||
{
|
||||
CommonUniforms* common = static_cast<CommonUniforms*>(buffer);
|
||||
|
||||
@ -105,14 +105,14 @@ void PostProcessingShaderGLSL::FillUniformBuffer(void* buffer, u32 texture_width
|
||||
common->time = time;
|
||||
|
||||
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));
|
||||
option_values += sizeof(Option::ValueVector);
|
||||
std::memcpy(option_values, option.value.data(), sizeof(ShaderOption::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)
|
||||
m_pipeline.reset();
|
||||
@ -155,11 +155,11 @@ bool PostProcessingShaderGLSL::CompilePipeline(GPUTexture::Format format, u32 wi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostProcessingShaderGLSL::Apply(GPUTexture* input, GPUFramebuffer* final_target, s32 final_left, s32 final_top,
|
||||
s32 final_width, s32 final_height, s32 orig_width, s32 orig_height,
|
||||
u32 target_width, u32 target_height)
|
||||
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,
|
||||
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.
|
||||
if (!final_target)
|
||||
@ -179,18 +179,19 @@ bool PostProcessingShaderGLSL::Apply(GPUTexture* input, GPUFramebuffer* final_ta
|
||||
const u32 uniforms_size = GetUniformsSize();
|
||||
void* uniforms = g_gpu_device->MapUniformBuffer(uniforms_size);
|
||||
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->Draw(3, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
void PostProcessingShaderGLSL::LoadOptions()
|
||||
void PostProcessing::GLSLShader::LoadOptions()
|
||||
{
|
||||
// Adapted from Dolphin's PostProcessingConfiguration::LoadOptions().
|
||||
constexpr char config_start_delimiter[] = "[configuration]";
|
||||
@ -209,7 +210,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||
|
||||
std::istringstream in(configuration_string);
|
||||
|
||||
Option current_option = {};
|
||||
ShaderOption current_option = {};
|
||||
while (!in.eof())
|
||||
{
|
||||
std::string line_str;
|
||||
@ -229,7 +230,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||
size_t endpos = line_view.find("]");
|
||||
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;
|
||||
if (current_option.ui_name.empty())
|
||||
@ -244,11 +245,11 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||
// New section!
|
||||
std::string_view sub = line_view.substr(1, endpos - 1);
|
||||
if (sub == "OptionBool")
|
||||
current_option.type = Option::Type::Bool;
|
||||
current_option.type = ShaderOption::Type::Bool;
|
||||
else if (sub == "OptionRangeFloat")
|
||||
current_option.type = Option::Type::Float;
|
||||
current_option.type = ShaderOption::Type::Float;
|
||||
else if (sub == "OptionRangeInteger")
|
||||
current_option.type = Option::Type::Int;
|
||||
current_option.type = ShaderOption::Type::Int;
|
||||
else
|
||||
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;
|
||||
|
||||
std::string_view key, value;
|
||||
@ -277,7 +278,7 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||
}
|
||||
else if (key == "MinValue" || key == "MaxValue" || key == "DefaultValue" || key == "StepAmount")
|
||||
{
|
||||
Option::ValueVector* dst_array;
|
||||
ShaderOption::ValueVector* dst_array;
|
||||
if (key == "MinValue")
|
||||
dst_array = ¤t_option.min_value;
|
||||
else if (key == "MaxValue")
|
||||
@ -288,12 +289,12 @@ void PostProcessingShaderGLSL::LoadOptions()
|
||||
dst_array = ¤t_option.step_value;
|
||||
|
||||
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;
|
||||
else if (current_option.type == Option::Type::Float)
|
||||
size = ParseVector<float>(value, dst_array);
|
||||
else if (current_option.type == Option::Type::Int)
|
||||
size = ParseVector<s32>(value, dst_array);
|
||||
else if (current_option.type == ShaderOption::Type::Float)
|
||||
size = PostProcessing::ShaderOption::ParseFloatVector(value, dst_array);
|
||||
else if (current_option.type == ShaderOption::Type::Int)
|
||||
size = PostProcessing::ShaderOption::ParseIntVector(value, dst_array);
|
||||
|
||||
current_option.vector_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;
|
||||
if (current_option.ui_name.empty())
|
||||
@ -323,7 +325,7 @@ PostProcessingGLSLShaderGen::PostProcessingGLSLShaderGen(RenderAPI render_api, b
|
||||
|
||||
PostProcessingGLSLShaderGen::~PostProcessingGLSLShaderGen() = default;
|
||||
|
||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(const PostProcessingShaderGLSL& shader)
|
||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(const PostProcessing::GLSLShader& shader)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
@ -346,7 +348,7 @@ std::string PostProcessingGLSLShaderGen::GeneratePostProcessingVertexShader(cons
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingFragmentShader(const PostProcessingShaderGLSL& shader)
|
||||
std::string PostProcessingGLSLShaderGen::GeneratePostProcessingFragmentShader(const PostProcessing::GLSLShader& shader)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, const PostProcessingShaderGLSL& shader,
|
||||
void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, const PostProcessing::GLSLShader& shader,
|
||||
bool use_push_constants)
|
||||
{
|
||||
u32 pad_counter = 0;
|
||||
@ -478,31 +480,31 @@ void PostProcessingGLSLShaderGen::WriteUniformBuffer(std::stringstream& ss, cons
|
||||
ss << " float ubo_pad" << (pad_counter++) << ";\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"}};
|
||||
for (const PostProcessingShader::Option& option : shader.GetOptions())
|
||||
for (const PostProcessing::ShaderOption& option : shader.GetOptions())
|
||||
{
|
||||
switch (option.type)
|
||||
{
|
||||
case PostProcessingShader::Option::Type::Bool:
|
||||
case PostProcessing::ShaderOption::Type::Bool:
|
||||
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";
|
||||
break;
|
||||
|
||||
case PostProcessingShader::Option::Type::Int:
|
||||
case PostProcessing::ShaderOption::Type::Int:
|
||||
{
|
||||
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";
|
||||
}
|
||||
break;
|
||||
|
||||
case PostProcessingShader::Option::Type::Float:
|
||||
case PostProcessing::ShaderOption::Type::Float:
|
||||
default:
|
||||
{
|
||||
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";
|
||||
}
|
||||
break;
|
||||
|
||||
@ -5,19 +5,21 @@
|
||||
|
||||
#include "postprocessing_shader.h"
|
||||
|
||||
class PostProcessingShaderGLSL final : public PostProcessingShader
|
||||
namespace PostProcessing {
|
||||
|
||||
class GLSLShader final : public Shader
|
||||
{
|
||||
public:
|
||||
PostProcessingShaderGLSL();
|
||||
PostProcessingShaderGLSL(std::string name, std::string code);
|
||||
~PostProcessingShaderGLSL();
|
||||
GLSLShader();
|
||||
GLSLShader(std::string name, std::string code);
|
||||
~GLSLShader();
|
||||
|
||||
ALWAYS_INLINE const std::string& GetCode() const { return m_code; }
|
||||
|
||||
bool IsValid() const override;
|
||||
|
||||
bool LoadFromFile(std::string name, const char* filename);
|
||||
bool LoadFromString(std::string name, std::string code);
|
||||
bool LoadFromFile(std::string name, const char* filename, Error* error);
|
||||
bool LoadFromString(std::string name, std::string code, Error* error);
|
||||
|
||||
bool ResizeOutput(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<GPUSampler> m_sampler;
|
||||
};
|
||||
|
||||
} // namespace PostProcessing
|
||||
@ -62,7 +62,7 @@
|
||||
<ClInclude Include="page_fault_handler.h" />
|
||||
<ClInclude Include="cd_subchannel_replacement.h" />
|
||||
<ClInclude Include="platform_misc.h" />
|
||||
<ClInclude Include="postprocessing_chain.h" />
|
||||
<ClInclude Include="postprocessing.h" />
|
||||
<ClInclude Include="postprocessing_shader.h" />
|
||||
<ClInclude Include="postprocessing_shader_glsl.h" />
|
||||
<ClInclude Include="sdl_input_source.h" />
|
||||
@ -162,7 +162,7 @@
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="platform_misc_win32.cpp" />
|
||||
<ClCompile Include="postprocessing_chain.cpp" />
|
||||
<ClCompile Include="postprocessing.cpp" />
|
||||
<ClCompile Include="postprocessing_shader.cpp" />
|
||||
<ClCompile Include="postprocessing_shader_glsl.cpp" />
|
||||
<ClCompile Include="sdl_input_source.cpp" />
|
||||
@ -242,4 +242,4 @@
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<ClInclude Include="shadergen.h" />
|
||||
<ClInclude Include="imgui_fullscreen.h" />
|
||||
<ClInclude Include="imgui_manager.h" />
|
||||
<ClInclude Include="postprocessing_chain.h" />
|
||||
<ClInclude Include="postprocessing.h" />
|
||||
<ClInclude Include="postprocessing_shader.h" />
|
||||
<ClInclude Include="input_source.h" />
|
||||
<ClInclude Include="platform_misc.h" />
|
||||
@ -98,7 +98,7 @@
|
||||
<ClCompile Include="shadergen.cpp" />
|
||||
<ClCompile Include="imgui_fullscreen.cpp" />
|
||||
<ClCompile Include="imgui_manager.cpp" />
|
||||
<ClCompile Include="postprocessing_chain.cpp" />
|
||||
<ClCompile Include="postprocessing.cpp" />
|
||||
<ClCompile Include="postprocessing_shader.cpp" />
|
||||
<ClCompile Include="platform_misc_win32.cpp" />
|
||||
<ClCompile Include="sdl_input_source.cpp" />
|
||||
@ -150,4 +150,4 @@
|
||||
<UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user