mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 17:39:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			324 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
	
		
			9.3 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/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();
 | |
| }
 | |
| 
 | |
| 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.iStereoMode == STEREO_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;
 | |
| }
 |