mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 13:57:57 -07:00
f9c20571ab
Also ensure that all members of the class are initialized on construction as well. Previously the bool indicating if options are dirty wouldn't be initialized, which could be read uninitialized if an instance was constructed and then IsDirty() is called.
366 lines
11 KiB
C++
366 lines
11 KiB
C++
// Copyright 2014 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include "Common/CommonPaths.h"
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/FileSearch.h"
|
|
#include "Common/FileUtil.h"
|
|
#include "Common/IniFile.h"
|
|
#include "Common/Logging/Log.h"
|
|
#include "Common/StringUtil.h"
|
|
|
|
#include "VideoCommon/PostProcessing.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n";
|
|
|
|
PostProcessingShaderImplementation::PostProcessingShaderImplementation()
|
|
{
|
|
m_timer.Start();
|
|
}
|
|
|
|
PostProcessingShaderImplementation::~PostProcessingShaderImplementation()
|
|
{
|
|
m_timer.Stop();
|
|
}
|
|
|
|
static std::vector<std::string> GetShaders(const std::string& sub_dir = "")
|
|
{
|
|
std::vector<std::string> paths =
|
|
Common::DoFileSearch({File::GetUserPath(D_SHADERS_IDX) + sub_dir,
|
|
File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir},
|
|
{".glsl"});
|
|
std::vector<std::string> result;
|
|
for (std::string path : paths)
|
|
{
|
|
std::string name;
|
|
SplitPath(path, nullptr, &name, nullptr);
|
|
result.push_back(name);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::string> PostProcessingShaderImplementation::GetShaderList(APIType api_type)
|
|
{
|
|
// Currently there is no differentiation between API types and shader languages.
|
|
// This could change in the future, hence the api_type parameter, but ideally,
|
|
// shaders should be compatible across backends.
|
|
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
|
|
return GetShaders();
|
|
|
|
return {};
|
|
}
|
|
|
|
std::vector<std::string> PostProcessingShaderImplementation::GetAnaglyphShaderList(APIType api_type)
|
|
{
|
|
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
|
|
return GetShaders(ANAGLYPH_DIR DIR_SEP);
|
|
|
|
return {};
|
|
}
|
|
|
|
PostProcessingShaderConfiguration::PostProcessingShaderConfiguration() = default;
|
|
|
|
PostProcessingShaderConfiguration::~PostProcessingShaderConfiguration() = default;
|
|
|
|
std::string PostProcessingShaderConfiguration::LoadShader(std::string shader)
|
|
{
|
|
// Load the shader from the configuration if there isn't one sent to us.
|
|
if (shader == "")
|
|
shader = g_ActiveConfig.sPostProcessingShader;
|
|
m_current_shader = shader;
|
|
|
|
const std::string sub_dir =
|
|
(g_Config.stereo_mode == StereoMode::Anaglyph) ? ANAGLYPH_DIR DIR_SEP : "";
|
|
|
|
// loading shader code
|
|
std::string code;
|
|
std::string path = File::GetUserPath(D_SHADERS_IDX) + sub_dir + shader + ".glsl";
|
|
|
|
if (shader == "")
|
|
{
|
|
code = s_default_shader;
|
|
}
|
|
else
|
|
{
|
|
if (!File::Exists(path))
|
|
{
|
|
// Fallback to shared user dir
|
|
path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir + shader + ".glsl";
|
|
}
|
|
|
|
if (!File::ReadFileToString(path, code))
|
|
{
|
|
ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str());
|
|
code = s_default_shader;
|
|
}
|
|
}
|
|
|
|
LoadOptions(code);
|
|
LoadOptionsConfiguration();
|
|
|
|
return code;
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::LoadOptions(const std::string& code)
|
|
{
|
|
const std::string config_start_delimiter = "[configuration]";
|
|
const std::string config_end_delimiter = "[/configuration]";
|
|
size_t configuration_start = code.find(config_start_delimiter);
|
|
size_t configuration_end = code.find(config_end_delimiter);
|
|
|
|
m_options.clear();
|
|
m_any_options_dirty = true;
|
|
|
|
if (configuration_start == std::string::npos || configuration_end == std::string::npos)
|
|
{
|
|
// Issue loading configuration or there isn't one.
|
|
return;
|
|
}
|
|
|
|
std::string configuration_string =
|
|
code.substr(configuration_start + config_start_delimiter.size(),
|
|
configuration_end - configuration_start - config_start_delimiter.size());
|
|
|
|
std::istringstream in(configuration_string);
|
|
|
|
struct GLSLStringOption
|
|
{
|
|
std::string m_type;
|
|
std::vector<std::pair<std::string, std::string>> m_options;
|
|
};
|
|
|
|
std::vector<GLSLStringOption> option_strings;
|
|
GLSLStringOption* current_strings = nullptr;
|
|
while (!in.eof())
|
|
{
|
|
std::string line;
|
|
|
|
if (std::getline(in, line))
|
|
{
|
|
#ifndef _WIN32
|
|
// Check for CRLF eol and convert it to LF
|
|
if (!line.empty() && line.at(line.size() - 1) == '\r')
|
|
{
|
|
line.erase(line.size() - 1);
|
|
}
|
|
#endif
|
|
|
|
if (line.size() > 0)
|
|
{
|
|
if (line[0] == '[')
|
|
{
|
|
size_t endpos = line.find("]");
|
|
|
|
if (endpos != std::string::npos)
|
|
{
|
|
// New section!
|
|
std::string sub = line.substr(1, endpos - 1);
|
|
option_strings.push_back({sub});
|
|
current_strings = &option_strings.back();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (current_strings)
|
|
{
|
|
std::string key, value;
|
|
IniFile::ParseLine(line, &key, &value);
|
|
|
|
if (!(key == "" && value == ""))
|
|
current_strings->m_options.emplace_back(key, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto& it : option_strings)
|
|
{
|
|
ConfigurationOption option;
|
|
option.m_dirty = true;
|
|
|
|
if (it.m_type == "OptionBool")
|
|
option.m_type = ConfigurationOption::OptionType::OPTION_BOOL;
|
|
else if (it.m_type == "OptionRangeFloat")
|
|
option.m_type = ConfigurationOption::OptionType::OPTION_FLOAT;
|
|
else if (it.m_type == "OptionRangeInteger")
|
|
option.m_type = ConfigurationOption::OptionType::OPTION_INTEGER;
|
|
|
|
for (const auto& string_option : it.m_options)
|
|
{
|
|
if (string_option.first == "GUIName")
|
|
{
|
|
option.m_gui_name = string_option.second;
|
|
}
|
|
else if (string_option.first == "OptionName")
|
|
{
|
|
option.m_option_name = string_option.second;
|
|
}
|
|
else if (string_option.first == "DependentOption")
|
|
{
|
|
option.m_dependent_option = string_option.second;
|
|
}
|
|
else if (string_option.first == "MinValue" || string_option.first == "MaxValue" ||
|
|
string_option.first == "DefaultValue" || string_option.first == "StepAmount")
|
|
{
|
|
std::vector<s32>* output_integer = nullptr;
|
|
std::vector<float>* output_float = nullptr;
|
|
|
|
if (string_option.first == "MinValue")
|
|
{
|
|
output_integer = &option.m_integer_min_values;
|
|
output_float = &option.m_float_min_values;
|
|
}
|
|
else if (string_option.first == "MaxValue")
|
|
{
|
|
output_integer = &option.m_integer_max_values;
|
|
output_float = &option.m_float_max_values;
|
|
}
|
|
else if (string_option.first == "DefaultValue")
|
|
{
|
|
output_integer = &option.m_integer_values;
|
|
output_float = &option.m_float_values;
|
|
}
|
|
else if (string_option.first == "StepAmount")
|
|
{
|
|
output_integer = &option.m_integer_step_values;
|
|
output_float = &option.m_float_step_values;
|
|
}
|
|
|
|
if (option.m_type == ConfigurationOption::OptionType::OPTION_BOOL)
|
|
{
|
|
TryParse(string_option.second, &option.m_bool_value);
|
|
}
|
|
else if (option.m_type == ConfigurationOption::OptionType::OPTION_INTEGER)
|
|
{
|
|
TryParseVector(string_option.second, output_integer);
|
|
if (output_integer->size() > 4)
|
|
output_integer->erase(output_integer->begin() + 4, output_integer->end());
|
|
}
|
|
else if (option.m_type == ConfigurationOption::OptionType::OPTION_FLOAT)
|
|
{
|
|
TryParseVector(string_option.second, output_float);
|
|
if (output_float->size() > 4)
|
|
output_float->erase(output_float->begin() + 4, output_float->end());
|
|
}
|
|
}
|
|
}
|
|
m_options[option.m_option_name] = option;
|
|
}
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::LoadOptionsConfiguration()
|
|
{
|
|
IniFile ini;
|
|
ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
|
std::string section = m_current_shader + "-options";
|
|
|
|
for (auto& it : m_options)
|
|
{
|
|
switch (it.second.m_type)
|
|
{
|
|
case ConfigurationOption::OptionType::OPTION_BOOL:
|
|
ini.GetOrCreateSection(section)->Get(it.second.m_option_name, &it.second.m_bool_value,
|
|
it.second.m_bool_value);
|
|
break;
|
|
case ConfigurationOption::OptionType::OPTION_INTEGER:
|
|
{
|
|
std::string value;
|
|
ini.GetOrCreateSection(section)->Get(it.second.m_option_name, &value);
|
|
if (value != "")
|
|
TryParseVector(value, &it.second.m_integer_values);
|
|
}
|
|
break;
|
|
case ConfigurationOption::OptionType::OPTION_FLOAT:
|
|
{
|
|
std::string value;
|
|
ini.GetOrCreateSection(section)->Get(it.second.m_option_name, &value);
|
|
if (value != "")
|
|
TryParseVector(value, &it.second.m_float_values);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::SaveOptionsConfiguration()
|
|
{
|
|
IniFile ini;
|
|
ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
|
std::string section = m_current_shader + "-options";
|
|
|
|
for (auto& it : m_options)
|
|
{
|
|
switch (it.second.m_type)
|
|
{
|
|
case ConfigurationOption::OptionType::OPTION_BOOL:
|
|
{
|
|
ini.GetOrCreateSection(section)->Set(it.second.m_option_name, it.second.m_bool_value);
|
|
}
|
|
break;
|
|
case ConfigurationOption::OptionType::OPTION_INTEGER:
|
|
{
|
|
std::string value = "";
|
|
for (size_t i = 0; i < it.second.m_integer_values.size(); ++i)
|
|
value += StringFromFormat("%d%s", it.second.m_integer_values[i],
|
|
i == (it.second.m_integer_values.size() - 1) ? "" : ", ");
|
|
ini.GetOrCreateSection(section)->Set(it.second.m_option_name, value);
|
|
}
|
|
break;
|
|
case ConfigurationOption::OptionType::OPTION_FLOAT:
|
|
{
|
|
std::ostringstream value;
|
|
value.imbue(std::locale("C"));
|
|
|
|
for (size_t i = 0; i < it.second.m_float_values.size(); ++i)
|
|
{
|
|
value << it.second.m_float_values[i];
|
|
if (i != (it.second.m_float_values.size() - 1))
|
|
value << ", ";
|
|
}
|
|
ini.GetOrCreateSection(section)->Set(it.second.m_option_name, value.str());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::ReloadShader()
|
|
{
|
|
m_current_shader = "";
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::SetOptionf(const std::string& option, int index,
|
|
float value)
|
|
{
|
|
auto it = m_options.find(option);
|
|
|
|
it->second.m_float_values[index] = value;
|
|
it->second.m_dirty = true;
|
|
m_any_options_dirty = true;
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::SetOptioni(const std::string& option, int index, s32 value)
|
|
{
|
|
auto it = m_options.find(option);
|
|
|
|
it->second.m_integer_values[index] = value;
|
|
it->second.m_dirty = true;
|
|
m_any_options_dirty = true;
|
|
}
|
|
|
|
void PostProcessingShaderConfiguration::SetOptionb(const std::string& option, bool value)
|
|
{
|
|
auto it = m_options.find(option);
|
|
|
|
it->second.m_bool_value = value;
|
|
it->second.m_dirty = true;
|
|
m_any_options_dirty = true;
|
|
}
|