| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2014 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Common/CommonPaths.h"
 | 
					
						
							|  |  |  | #include "Common/FileUtil.h"
 | 
					
						
							|  |  |  | #include "Common/IniFile.h"
 | 
					
						
							|  |  |  | #include "Common/StringUtil.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "VideoCommon/PostProcessing.h"
 | 
					
						
							|  |  |  | #include "VideoCommon/VideoConfig.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 23:08:49 +01:00
										 |  |  | static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-03 01:33:30 +01:00
										 |  |  | 	const std::string sub_dir = (g_Config.iStereoMode == STEREO_ANAGLYPH) ? ANAGLYPH_DIR DIR_SEP : ""; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 	// loading shader code
 | 
					
						
							|  |  |  | 	std::string code; | 
					
						
							| 
									
										
										
										
											2015-01-03 01:33:30 +01:00
										 |  |  | 	std::string path = File::GetUserPath(D_SHADERS_IDX) + sub_dir + shader + ".glsl"; | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 23:08:49 +01:00
										 |  |  | 	if (shader == "") | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-01-25 23:08:49 +01:00
										 |  |  | 		code = s_default_shader; | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-25 23:08:49 +01:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-01-25 23:08:49 +01:00
										 |  |  | 		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; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 == "")) | 
					
						
							| 
									
										
										
										
											2015-02-04 10:36:42 -05:00
										 |  |  | 							current_strings->m_options.emplace_back(key, value); | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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: | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2015-01-20 16:40:46 -06:00
										 |  |  | 			std::ostringstream value; | 
					
						
							|  |  |  | 			value.imbue(std::locale("C")); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 			for (size_t i = 0; i < it.second.m_float_values.size(); ++i) | 
					
						
							| 
									
										
										
										
											2015-01-20 16:40:46 -06:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				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()); | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PostProcessingShaderConfiguration::ReloadShader() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_current_shader = ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-28 20:28:48 -04:00
										 |  |  | void PostProcessingShaderConfiguration::SetOptionf(const std::string& option, int index, float value) | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	auto it = m_options.find(option); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it->second.m_float_values[index] = value; | 
					
						
							|  |  |  | 	it->second.m_dirty = true; | 
					
						
							|  |  |  | 	m_any_options_dirty = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-28 20:28:48 -04:00
										 |  |  | void PostProcessingShaderConfiguration::SetOptioni(const std::string& option, int index, s32 value) | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	auto it = m_options.find(option); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it->second.m_integer_values[index] = value; | 
					
						
							|  |  |  | 	it->second.m_dirty = true; | 
					
						
							|  |  |  | 	m_any_options_dirty = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-28 20:28:48 -04:00
										 |  |  | void PostProcessingShaderConfiguration::SetOptionb(const std::string& option, bool value) | 
					
						
							| 
									
										
										
										
											2014-07-29 11:47:56 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	auto it = m_options.find(option); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	it->second.m_bool_value = value; | 
					
						
							|  |  |  | 	it->second.m_dirty = true; | 
					
						
							|  |  |  | 	m_any_options_dirty = true; | 
					
						
							|  |  |  | } |