Shader_Preset_Simple_Save_Cleaned_2020_11_01

This commit is contained in:
HyperspaceMadness 2020-11-01 09:18:40 -05:00
parent a2a58147e7
commit 70bf90c5d8
14 changed files with 691 additions and 192 deletions

View File

@ -343,6 +343,10 @@
#define DEFAULT_SHADER_ENABLE false #define DEFAULT_SHADER_ENABLE false
#endif #endif
/* When presets are saved they will be saved using the #reference
* directive by default */
#define DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE true
#define DEFAULT_SHADER_DELAY 0 #define DEFAULT_SHADER_DELAY 0
/* Only scale in integer steps. /* Only scale in integer steps.

View File

@ -1455,6 +1455,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, DEFAULT_SHADER_ENABLE, false); SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, DEFAULT_SHADER_ENABLE, false);
SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, DEFAULT_VIDEO_SHADER_WATCH_FILES, false); SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, DEFAULT_VIDEO_SHADER_WATCH_FILES, false);
SETTING_BOOL("video_shader_remember_last_dir", &settings->bools.video_shader_remember_last_dir, true, DEFAULT_VIDEO_SHADER_REMEMBER_LAST_DIR, false); SETTING_BOOL("video_shader_remember_last_dir", &settings->bools.video_shader_remember_last_dir, true, DEFAULT_VIDEO_SHADER_REMEMBER_LAST_DIR, false);
SETTING_BOOL("video_shader_preset_save_reference_enable", &settings->bools.video_shader_preset_save_reference_enable, true, DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE, false);
/* Let implementation decide if automatic, or 1:1 PAR. */ /* Let implementation decide if automatic, or 1:1 PAR. */
SETTING_BOOL("video_aspect_ratio_auto", &settings->bools.video_aspect_ratio_auto, true, DEFAULT_ASPECT_RATIO_AUTO, false); SETTING_BOOL("video_aspect_ratio_auto", &settings->bools.video_aspect_ratio_auto, true, DEFAULT_ASPECT_RATIO_AUTO, false);

View File

@ -462,6 +462,7 @@ typedef struct settings
bool video_shader_enable; bool video_shader_enable;
bool video_shader_watch_files; bool video_shader_watch_files;
bool video_shader_remember_last_dir; bool video_shader_remember_last_dir;
bool video_shader_preset_save_reference_enable;
bool video_threaded; bool video_threaded;
bool video_font_enable; bool video_font_enable;
bool video_disable_composition; bool video_disable_composition;

View File

@ -417,6 +417,7 @@ static struct video_shader_parameter *video_shader_parse_find_parameter(
* @shader : Shader passes handle. * @shader : Shader passes handle.
* *
* Reads the current value for all parameters from config file. * Reads the current value for all parameters from config file.
* Checks for parameters in the parameter list which don't have a value set
* *
* Returns: true (1) if successful, otherwise false (0). * Returns: true (1) if successful, otherwise false (0).
**/ **/
@ -440,6 +441,7 @@ bool video_shader_resolve_current_parameters(config_file_t *conf,
for ( id = strtok_r(parameters, ";", &save); id; for ( id = strtok_r(parameters, ";", &save); id;
id = strtok_r(NULL, ";", &save)) id = strtok_r(NULL, ";", &save))
{ {
/* Get the parameter in the shader matching this name */
struct video_shader_parameter *parameter = struct video_shader_parameter *parameter =
(struct video_shader_parameter*) (struct video_shader_parameter*)
video_shader_parse_find_parameter( video_shader_parse_find_parameter(
@ -465,6 +467,8 @@ bool video_shader_resolve_current_parameters(config_file_t *conf,
* @shader : Shader passes handle. * @shader : Shader passes handle.
* *
* Resolves all shader parameters belonging to shaders. * Resolves all shader parameters belonging to shaders.
* Fills the parameter definition list of the shader
* Does not read any of the config parameter values
* *
* Returns: true (1) if successful, otherwise false (0). * Returns: true (1) if successful, otherwise false (0).
**/ **/
@ -571,6 +575,375 @@ bool video_shader_resolve_parameters(config_file_t *conf,
return video_shader_resolve_current_parameters(conf, shader); return video_shader_resolve_current_parameters(conf, shader);
} }
#ifdef _WIN32
static void make_relative_path_portable(char* path)
{
/* use '/' instead of '\' for maximum portability */
char* p;
for (p = path; *p; p++)
if (*p == '\\')
*p = '/';
}
#endif
/**
* video_shader_write_referenced_preset:
* @path : File to write to
* @shader : Shader preset to write
*
* Writes a referenced preset to disk
* A referenced preset is a preset which includes the #reference directive
* as it's first line to specify a root preset and can also include parameter
* and texture values to override the values of the root preset
* Returns false if a referenced preset cannot be saved
* See: video_shader_read_preset
**/
bool video_shader_write_referenced_preset(const char *path,
const char *shader_dir,
const struct video_shader *shader)
{
unsigned i;
bool ret = false;
bool continue_saving_reference = true;
char preset_dir[PATH_MAX_LENGTH];
config_file_t *conf;
char *absolute_root_preset_path = (char*)malloc(PATH_MAX_LENGTH);
char *absolute_new_preset_basedir = (char*)malloc(PATH_MAX_LENGTH);
char *relative_root_preset_path = (char*)malloc(PATH_MAX_LENGTH);
preset_dir[0] = '\0';
absolute_new_preset_basedir = strdup(path);
path_basedir(absolute_new_preset_basedir);
absolute_root_preset_path[0] = '\0';
relative_root_preset_path[0] = '\0';
/* Get the absolute path to the root preset, this is the one which is used in the #reference directive */
strlcpy(absolute_root_preset_path, shader->path, PATH_MAX_LENGTH);
if (string_is_empty(shader->path))
{
RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because the loaded Shader "
"does not have a path to a previously loaded preset file on disk.\n");
continue_saving_reference = false;
}
/* We can't save a reference to ourselves */
if (string_is_equal(shader->path, path))
{
RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a preset "
"which would reference itself.\n");
continue_saving_reference = false;
}
/* Auto-shaders can be written as copies or references.
* If we write a reference to a copy, we could then overwrite the copy
* with any reference, thus creating a reference to a reference.
* To prevent this, we disallow saving references to auto-shaders. */
fill_pathname_join(preset_dir,
shader_dir,
"presets",
sizeof(preset_dir));
if (continue_saving_reference && !strncmp(preset_dir, absolute_root_preset_path, strlen(preset_dir)))
{
RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a "
"reference to an auto-loaded shader (E.G. Game Preset, Core Preset).\n");
continue_saving_reference = false;
}
/* Don't ever create a reference to the ever-changing retroarch preset
* TODO remove once we don't write this preset anymore */
if (continue_saving_reference && !strncmp(path_basename(absolute_root_preset_path), "retroarch", STRLEN_CONST("retroarch")))
{
continue_saving_reference = false;
RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a reference "
"to the ever-changing retroarch preset.\n");
}
if (continue_saving_reference)
{
config_file_t *root_conf;
path_relative_to(relative_root_preset_path,
absolute_root_preset_path,
absolute_new_preset_basedir,
PATH_MAX_LENGTH);
#ifdef _WIN32
if (!path_is_absolute(relative_root_preset_path))
make_relative_path_portable(relative_root_preset_path);
#endif
/* Create a new EMPTY config */
conf = config_file_new_alloc();
if (!(conf))
return false;
conf->path = strdup(path);
/* Add the reference path to the config */
config_file_set_reference_path(conf, relative_root_preset_path);
/* Set modified to true so when you run config_file_write it will save a file */
conf->modified = true;
/* Get a config from root preset which was defined by the #reference directive
* to compare the current shader against */
root_conf = config_file_new_from_path_to_string(absolute_root_preset_path);
/* root_conf could be NULL if the file was not found */
if (root_conf == NULL)
RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we could not "
"load the Root Preset: %s.\n",
absolute_root_preset_path);
else
{
/*
Compare the shader to a shader created from the root config to see if
we can save a referenced preset and what parameters and textures of the
root_config are overridden
*/
struct video_shader *root_shader = NULL;
root_shader = (struct video_shader*) calloc(1, sizeof(*root_shader));
video_shader_read_conf_preset(root_conf, root_shader);
/* Check number of passes match */
if (shader->passes != root_shader->passes)
{
RARCH_WARN("[Shaders-Save Reference]: passes (Number of Passes)");
continue_saving_reference = false;
}
/*
Compare all passes from the shader
if anything is different then we should not save a reference
and save instead safe a full preset instead
*/
if (continue_saving_reference)
{
/* Step through each pass comparing all the properties to make sure they match */
for (i = 0; (i < shader->passes && continue_saving_reference == true); i++)
{
const struct video_shader_pass *pass = &shader->pass[i];
const struct video_shader_pass *root_pass = &root_shader->pass[i];
const struct gfx_fbo_scale *fbo = &pass->fbo;
const struct gfx_fbo_scale *root_fbo = &root_pass->fbo;
if (!string_is_equal(pass->source.path, root_pass->source.path))
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u path", i);
continue_saving_reference = false;
}
if (continue_saving_reference && pass->filter != root_pass->filter)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u filter", i);
continue_saving_reference = false;
}
if (continue_saving_reference && pass->wrap != root_pass->wrap)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u wrap", i);
continue_saving_reference = false;
}
if (continue_saving_reference && pass->frame_count_mod != root_pass->frame_count_mod)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u frame_count", i);
continue_saving_reference = false;
}
if (continue_saving_reference && pass->mipmap != root_pass->mipmap)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u mipmap", i);
continue_saving_reference = false;
}
if (continue_saving_reference && !string_is_equal(pass->alias, root_pass->alias))
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u alias", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->type_x != root_fbo->type_x)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u type_x", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->type_y != root_fbo->type_y)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u type_y", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->scale_x != root_fbo->scale_x)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u scale_x", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->scale_y != root_fbo->scale_y)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u scale_y", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->fp_fbo != root_fbo->fp_fbo)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u fp_fbo", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->srgb_fbo != root_fbo->srgb_fbo)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u srgb_fbo", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->valid != root_fbo->valid)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u valid", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->abs_x != root_fbo->abs_x)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u abs_x", i);
continue_saving_reference = false;
}
if (continue_saving_reference && fbo->abs_y != root_fbo->abs_y)
{
RARCH_WARN("[Shaders-Save Reference]: Pass %u abs_y", i);
continue_saving_reference = false;
}
if (!continue_saving_reference)
RARCH_WARN(" Current Value doesn't match Referenced Value - "
"Full Preset Will be Saved instead of Simple Preset\n");
}
}
if (continue_saving_reference)
{
const struct config_entry_list *entry = NULL;
/* If the shader has parameters */
if (shader->num_parameters)
{
size_t param_size = 4096 * sizeof(char);
char *override_parameters = (char*)malloc(param_size);
float parameter_value_reference;
param_size = 4096 * sizeof(char);
override_parameters[0] = '\0';
parameter_value_reference = 0.0f;
if (override_parameters)
{
unsigned i;
for (i = 0; i < shader->num_parameters; i++)
{
bool add_param_to_override = false;
entry = config_get_entry(root_conf, shader->parameters[i].id);
/* If the parameter is in the reference config */
if (entry)
{
/* If the current param value is different than the referenced preset's value */
config_get_float(root_conf, shader->parameters[i].id, &parameter_value_reference);
if (shader->parameters[i].current != parameter_value_reference)
add_param_to_override = true;
}
/* If it's not in the reference config, but it's different than the
initial value of the shader */
else if (shader->parameters[i].current != shader->parameters[i].initial)
add_param_to_override = true;
/* Add the parameter name to the parameters list */
if (add_param_to_override)
{
config_set_float(conf, shader->parameters[i].id, shader->parameters[i].current);
strlcat(override_parameters, ";", param_size);
strlcat(override_parameters, shader->parameters[i].id, param_size);
}
}
/* Write the list of override parameter names if there are any*/
if (!string_is_empty(override_parameters))
config_set_string(conf, "parameters", override_parameters);
}
free(override_parameters);
}
/* If the shader has textures */
if (shader->luts)
{
char *shader_tex_path = (char*)malloc(3*PATH_MAX_LENGTH);
char *shader_tex_relative_path = shader_tex_path + PATH_MAX_LENGTH;
char *shader_tex_base_path = shader_tex_path + 2*PATH_MAX_LENGTH;
char *referenced_tex_absolute_path = (char*)malloc(PATH_MAX_LENGTH);
char *referenced_tex_path = (char*)malloc(PATH_MAX_LENGTH);
size_t tex_size = 4096 * sizeof(char);
char *textures = (char*)malloc(tex_size);
unsigned i;
shader_tex_path[0] = '\0';
shader_tex_relative_path[0] = '\0';
shader_tex_base_path[0] = '\0';
textures[0] = '\0';
referenced_tex_absolute_path[0] = '\0';
referenced_tex_path[0] = '\0';
for (i = 0; i < shader->luts; i++)
{
/* If the texture is defined in the reference config */
entry = config_get_entry(root_conf, shader->lut[i].id);
if (entry)
{
/* Texture path from shader is already absolute */
strlcpy(shader_tex_path, shader->lut[i].path, PATH_MAX_LENGTH);
strlcpy(referenced_tex_path, entry->value, PATH_MAX_LENGTH);
/* Resolve the texture's path relative to the override config */
if (!path_is_absolute(referenced_tex_path))
fill_pathname_resolve_relative(referenced_tex_absolute_path,
root_conf->path,
entry->value,
PATH_MAX_LENGTH);
else
strlcpy(referenced_tex_absolute_path, referenced_tex_path, PATH_MAX_LENGTH);
/* If the current shader texture path is different than the referenced paths then
* write the current path into the new preset */
if (!string_is_equal(referenced_tex_absolute_path, shader->lut[i].path))
{
/* Get the texture path relative to the new preset */
path_relative_to(shader_tex_relative_path, shader_tex_path, path_basename(path), PATH_MAX_LENGTH);
RARCH_LOG("[Shaders-Save Reference]: Texture override %s = %s.\n",
shader->lut[i].id,
shader->lut[i].path);
config_set_path(conf, shader->lut[i].id, shader->lut[i].path);
}
}
}
}
/* Write the file, return will be true if successful */
ret = config_file_write(conf, path, false);
if (!ret)
RARCH_WARN("[Shaders-Save Reference]: Failed writing Referenced Preset to %s - "
"Full Preset Will be Saved instead of Simple Preset\n", path);
}
config_file_free(root_conf);
}
config_file_free(conf);
}
free(relative_root_preset_path);
return ret;
}
/** /**
* video_shader_write_preset: * video_shader_write_preset:
* @path : File to write to * @path : File to write to
@ -586,9 +959,9 @@ bool video_shader_write_preset(const char *path,
{ {
/* We need to clean up paths to be able to properly process them /* We need to clean up paths to be able to properly process them
* path and shader->path can use '/' on Windows due to Qt being Qt */ * path and shader->path can use '/' on Windows due to Qt being Qt */
char clean_path[PATH_MAX_LENGTH];
char clean_shader_path[PATH_MAX_LENGTH];
char preset_dir[PATH_MAX_LENGTH]; char preset_dir[PATH_MAX_LENGTH];
config_file_t *conf;
bool ret;
if (!shader || string_is_empty(path)) if (!shader || string_is_empty(path))
return false; return false;
@ -599,46 +972,13 @@ bool video_shader_write_preset(const char *path,
"presets", "presets",
sizeof(preset_dir)); sizeof(preset_dir));
strlcpy(clean_shader_path, shader->path, sizeof(clean_shader_path)); /* If we should still save a referenced preset do it now */
path_resolve_realpath(clean_shader_path, sizeof(clean_shader_path), false);
if (string_is_empty(shader->path))
reference = false;
/* Auto-shaders can be written as copies or references.
* If we write a reference to a copy, we could then overwrite the copy
* with any reference, thus creating a reference to a reference.
* To prevent this, we disallow saving references to auto-shaders. */
if (reference && !strncmp(preset_dir, clean_shader_path, strlen(preset_dir)))
reference = false;
/* Don't ever create a reference to the ever-changing retroarch preset
* TODO remove once we don't write this preset anymore */
if (reference && !strncmp(path_basename(clean_shader_path), "retroarch", STRLEN_CONST("retroarch")))
reference = false;
if (reference) if (reference)
{ if (video_shader_write_referenced_preset(path, shader_dir, shader))
/* write a reference preset */ return true;
char buf[STRLEN_CONST("#reference \"") + PATH_MAX_LENGTH + 1] = "#reference \"";
size_t len = STRLEN_CONST("#reference \"");
char *preset_ref = buf + len;
strlcpy(clean_path, path, sizeof(clean_path)); /* If we aren't saving a referenced preset or weren't able to save one
path_resolve_realpath(clean_path, sizeof(clean_path), false); * then save a full preset */
path_relative_to(preset_ref, clean_shader_path, clean_path, PATH_MAX_LENGTH);
len += strlen(preset_ref);
buf[len++] = '\"';
return filestream_write_file(clean_path, (void *)buf, len);
}
else
{
/* regular saving function */
config_file_t *conf;
bool ret;
/* Note: We always create a new/blank config /* Note: We always create a new/blank config
* file here. Loading and updating an existing * file here. Loading and updating an existing
@ -655,122 +995,137 @@ bool video_shader_write_preset(const char *path,
return ret; return ret;
} }
}
/** /**
* video_shader_read_reference_path: * override_config_values:
* @path : File to read * @conf : Config file to be affected
* @override_conf : Config file who's values will be copied on top of conf
* *
* Returns: the reference path of a preset if it exists, * Takes values from override_config and overrides values of conf
* otherwise returns NULL. * The 'parameters' value will be the combined parameter list from both configs
* *
* The returned string needs to be freed. * Returns 0 if nothing is overridden
*/ * Returns 1 if something is overridden
static char *video_shader_read_reference_path(const char *path) **/
bool override_config_values(config_file_t *conf, config_file_t *override_conf)
{ {
/* We want shader presets that point to other presets. int return_val = 0;
* size_t param_size = 4096 * sizeof(char);
* While config_file_new_from_path_to_string() does support the const char *id = NULL;
* #include directive, it will not rebase relative paths on char *save = NULL;
* the include path. size_t path_size = PATH_MAX_LENGTH;
* char *override_texture_path = (char*)malloc(path_size);
* There's a plethora of reasons why a general solution is hard: char *resolved_path = (char*)malloc(path_size);
* - it's impossible to distinguish a generic string from a (relative) path char *textures_in_conf = (char*)malloc(param_size);
* - config_file_new_from_path_to_string() doesn't return the include path, size_t tmp_size = PATH_MAX_LENGTH;
* so we cannot rebase afterwards char *tmp = (char*)malloc(3*tmp_size);
* - #include is recursive, so we'd actually need to track the include path char *tmp_rel = tmp + tmp_size;
* for every setting char *tmp_base = tmp + 2*tmp_size;
* struct config_entry_list *override_entry = NULL;
* So instead, we use a custom #reference directive, which is just a char *override_parameters = (char*)malloc(param_size);
* one-time/non-recursive redirection, e.g.
*
* #reference "<path to config>"
* or
* #reference <path to config>
*
* which we will load as config_file_new_from_path_to_string(<path to config>).
*/
char *reference = NULL;
RFILE *file = NULL;
char *line = NULL;
if (string_is_empty(path) || !path_is_valid(path)) override_parameters[0] = '\0';
return NULL; textures_in_conf[0] = '\0';
file = filestream_open(path, strlcpy(tmp_base, conf->path, tmp_size);
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
return NULL;
line = filestream_getline(file); if (conf == NULL || override_conf == NULL) return 0;
filestream_close(file);
if (line && !strncmp("#reference", line, STRLEN_CONST("#reference"))) /* ---------------------------------------------------------------------------------
* ------------- Resolve Override texture paths to absolute paths-------------------
* --------------------------------------------------------------------------------- */
/* ensure we use a clean base like the shader passes and texture paths do */
path_resolve_realpath(tmp_base, tmp_size, false);
path_basedir(tmp_base);
/* If there are textures in the referenced config */
if (config_get_array(conf, "textures", textures_in_conf, param_size))
{ {
char *ref_path = line + STRLEN_CONST("#reference"); for ( id = strtok_r(textures_in_conf, ";", &save);
id;
/* have at least 1 whitespace */ id = strtok_r(NULL, ";", &save))
if (!isspace((unsigned char)*ref_path))
{ {
free(line); /* Get the texture path from the override config */
return NULL; if (config_get_path(override_conf, id, override_texture_path, path_size))
}
ref_path++;
while (isspace((unsigned char)*ref_path))
ref_path++;
if (*ref_path == '\"')
{ {
/* remove "" */ /* Resolve the texture's path relative to the override config */
char *p; if (!path_is_absolute(override_texture_path))
ref_path++; fill_pathname_resolve_relative(resolved_path,
override_conf->path,
p = ref_path; override_texture_path,
while (*p != '\0' && *p != '\"') PATH_MAX_LENGTH);
p++;
if (*p == '\"')
*p = '\0';
else else
strlcpy(resolved_path, override_texture_path, path_size);
path_relative_to(tmp_rel, resolved_path, tmp_base, tmp_size);
config_set_path(override_conf, id, tmp_rel);
return_val = 1;
}
}
}
/* ---------------------------------------------------------------------------------
* -------------Update Parameter List to include Override Parameters----------------
* --------------------------------------------------------------------------------- */
/* If there is a 'parameters' entry in the override config we want to add these parameters
* to the referenced config if they are not already there */
if (config_get_array(override_conf, "parameters", override_parameters, param_size))
{ {
/* if there's no second ", remove whitespace at the end */ /* Get the string for the parameters from the root config */
p--; char *parameters = NULL;
while (isspace((unsigned char)*p)) parameters = (char*)malloc(param_size);
*p-- = '\0'; parameters[0] = '\0';
}
} /* If there are is no parameters entry in the root config, add one */
else if (!config_get_array(conf, "parameters", parameters, param_size))
{ {
/* remove whitespace at the end (e.g. carriage return) */ config_set_string(conf, "parameters", "");
char *end = ref_path + strlen(ref_path) - 1; config_get_array(conf, "parameters", parameters, param_size);
while (isspace((unsigned char)*end))
*end-- = '\0';
} }
if (string_is_empty(ref_path)) /* Step through each parameter in override config */
for ( id = strtok_r(override_parameters, ";", &save);
id;
id = strtok_r(NULL, ";", &save))
{ {
free(line); /* If the parameter is not in the root config's parameter list add it */
return NULL; if (!strstr(parameters, id))
}
reference = (char *)malloc(PATH_MAX_LENGTH);
if (!reference)
{ {
free(line); strlcat(parameters, ";", param_size);
return NULL; strlcat(parameters, id, param_size);
return_val = 1;
}
}
config_set_string(conf, "parameters", strdup(parameters));
free(parameters);
} }
/* rebase relative reference path */ /* ---------------------------------------------------------------------------------
fill_pathname_resolve_relative(reference, * ------------- Update entries to match the override entries ----------------------
path, ref_path, PATH_MAX_LENGTH); * --------------------------------------------------------------------------------- */
for (override_entry = override_conf->entries; override_entry; override_entry = override_entry->next)
{
/* Only override an entry if the it's key is not "parameters", and not in list of textures */
if (!string_is_empty(override_entry->key) && !string_is_equal(override_entry->key, "parameters") && !string_is_equal(override_entry->key, "textures"))
{
RARCH_LOG("[Shaders-Load Reference]: Entry overridden %s = %s.\n",
override_entry->key, override_entry->value);
config_set_string(conf, override_entry->key, strdup(override_entry->value));
return_val = 1;
}
} }
if (line) free(tmp);
free(line); free(resolved_path);
free(override_texture_path);
free(override_parameters);
free(textures_in_conf);
return reference; return return_val;
} }
/** /**
@ -787,15 +1142,52 @@ static char *video_shader_read_reference_path(const char *path)
**/ **/
config_file_t *video_shader_read_preset(const char *path) config_file_t *video_shader_read_preset(const char *path)
{ {
char *reference = video_shader_read_reference_path(path); config_file_t *conf;
if (reference) conf = config_file_new_from_path_to_string(path);
if (conf != NULL)
{ {
config_file_t *conf = /* If the original config had a reference then it was really
config_file_new_from_path_to_string(reference); * the override config. We now load a new config from the reference
free(reference); * then override it's values with the override config */
return conf; if (conf->reference)
{
char* root_preset_path = (char*)malloc(PATH_MAX_LENGTH);
/* Set override_conf to refer to the original config */
config_file_t *override_conf = conf;
/* Resolve the reference path relative to the config */
if (path_is_absolute(conf->reference))
strlcpy(root_preset_path, conf->reference, PATH_MAX_LENGTH);
else
fill_pathname_resolve_relative(root_preset_path,
conf->path,
conf->reference,
PATH_MAX_LENGTH);
/* Create a new config from the root preset */
conf = config_file_new_from_path_to_string(root_preset_path);
/* Only try to override values if the config is not NULL
* If it is NULL there is no shader*/
if (conf != NULL)
/* override_conf is from the initial file we loaded which
* has the #reference directive*/
override_config_values(conf, override_conf);
else
RARCH_WARN("[ SHADER LOAD ] Could not read root shader preset in '#reference' line: %s\n", root_preset_path);
free(root_preset_path);
config_file_free(override_conf);
} }
return config_file_new_from_path_to_string(path);
/* Set Path for originally loaded preset */
config_set_path(conf, "loaded_preset_path", path);
}
else
RARCH_WARN("[ SHADER LOAD ] Could not read preset: %s", path);
return conf;
} }
/** /**
@ -815,6 +1207,7 @@ bool video_shader_read_conf_preset(config_file_t *conf,
unsigned shaders = 0; unsigned shaders = 0;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
bool watch_files = settings->bools.video_shader_watch_files; bool watch_files = settings->bools.video_shader_watch_files;
char loaded_preset_path[PATH_MAX_LENGTH];
memset(shader, 0, sizeof(*shader)); memset(shader, 0, sizeof(*shader));
@ -826,7 +1219,7 @@ bool video_shader_read_conf_preset(config_file_t *conf,
if (!shaders) if (!shaders)
{ {
RARCH_ERR("[Shaders]: Need to define at least 1 shader.\n"); RARCH_ERR("[Shaders]: Need to define at least 1 shader pass.\n");
return false; return false;
} }
@ -836,8 +1229,17 @@ bool video_shader_read_conf_preset(config_file_t *conf,
shader->passes = MIN(shaders, GFX_MAX_SHADERS); shader->passes = MIN(shaders, GFX_MAX_SHADERS);
/* Set the path of the root preset for this shader */
strlcpy(shader->path, conf->path, sizeof(shader->path)); strlcpy(shader->path, conf->path, sizeof(shader->path));
/* Set the path of the original preset which was loaded, this would be
* different than the root preset in the case preset of use of the #reference directive
* in the original preset loaded */
config_get_path(conf, "loaded_preset_path", loaded_preset_path, PATH_MAX_LENGTH);
strlcpy( shader->loaded_preset_path,
loaded_preset_path,
sizeof(shader->loaded_preset_path));
if (watch_files) if (watch_files)
{ {
union string_list_elem_attr attr; union string_list_elem_attr attr;
@ -858,6 +1260,9 @@ bool video_shader_read_conf_preset(config_file_t *conf,
string_list_initialize(&file_list); string_list_initialize(&file_list);
string_list_append(&file_list, conf->path, attr); string_list_append(&file_list, conf->path, attr);
/* TODO We aren't currently watching the originally loaded preset
* We should probably watch it for changes too */
for (i = 0; i < shader->passes; i++) for (i = 0; i < shader->passes; i++)
{ {
if (!video_shader_parse_pass(conf, &shader->pass[i], i)) if (!video_shader_parse_pass(conf, &shader->pass[i], i))
@ -944,17 +1349,6 @@ static void shader_write_fbo(config_file_t *conf,
fbo->scale_y, fbo->abs_y, i); fbo->scale_y, fbo->abs_y, i);
} }
#ifdef _WIN32
static void make_relative_path_portable(char *path)
{
/* use '/' instead of '\' for maximum portability */
char *p;
for (p = path; *p; p++)
if (*p == '\\')
*p = '/';
}
#endif
/** /**
* video_shader_write_conf_preset: * video_shader_write_conf_preset:
* @conf : Preset file to write to. * @conf : Preset file to write to.

View File

@ -153,8 +153,14 @@ struct video_shader
unsigned variables; unsigned variables;
char prefix[64]; char prefix[64];
/* Path to the root preset */
char path[PATH_MAX_LENGTH]; char path[PATH_MAX_LENGTH];
/* Path to the original preset loaded, if this is a preset with the #reference
* directive then this will be different than the path*/
char loaded_preset_path[PATH_MAX_LENGTH];
bool modern; /* Only used for XML shaders. */ bool modern; /* Only used for XML shaders. */
/* indicative of whether shader was modified - /* indicative of whether shader was modified -
* for instance from the menus */ * for instance from the menus */

View File

@ -3230,6 +3230,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE, MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE,
"video_shader_preset_save" "video_shader_preset_save"
) )
MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
"video_shader_preset_save_reference"
)
MSG_HASH( MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS, MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS,
"video_shader_preset_save_as" "video_shader_preset_save_as"

View File

@ -6674,6 +6674,16 @@ MSG_HASH(
/* Quick Menu > Shaders > Save */ /* Quick Menu > Shaders > Save */
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
"Save Simple Preset"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
"Saves a Shader Preset which has a link to the original preset loaded and includes only the parameter changes you made."
)
MSG_HASH( MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS, MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS,
"Save Shader Preset As" "Save Shader Preset As"

View File

@ -466,14 +466,22 @@ static bool config_file_parse_line(config_file_t *conf,
char real_path[PATH_MAX_LENGTH]; char real_path[PATH_MAX_LENGTH];
char *path = NULL; char *path = NULL;
char *include_line = NULL; char *include_line = NULL;
char *reference_line = NULL;
/* Starting a line with an 'include' directive /* Starting a line with an 'include' directive
* appends a sub-config file * appends a sub-config file
* > All other comments are ignored */ * > All other comments are ignored */
if (!string_starts_with_size(comment, "include ", if (!string_starts_with_size(comment, "include ",
STRLEN_CONST("include "))) STRLEN_CONST("include ")) &&
!string_starts_with_size(comment, "reference ",
STRLEN_CONST("reference ")))
return false; return false;
/* Starting a line with an 'include' directive
* appends a sub-config file */
if (string_starts_with_size(comment, "include ",
STRLEN_CONST("include ")))
{
include_line = comment + STRLEN_CONST("include "); include_line = comment + STRLEN_CONST("include ");
if (string_is_empty(include_line)) if (string_is_empty(include_line))
@ -511,6 +519,27 @@ static bool config_file_parse_line(config_file_t *conf,
default: default:
break; break;
} }
}
/* If it's a 'reference' directive */
if (string_starts_with_size(comment, "reference ",
STRLEN_CONST("reference ")))
{
reference_line = comment + STRLEN_CONST("reference ");
if (string_is_empty(reference_line))
return false;
path = config_file_extract_value(reference_line, false);
if (!path)
return false;
config_file_set_reference_path(conf, path);
if (!path)
return false;
}
free(path); free(path);
return true; return true;
@ -617,6 +646,16 @@ static int config_file_from_string_internal(
return 0; return 0;
} }
void config_file_set_reference_path(config_file_t *conf, char *path)
{
if (conf)
{
/* Assumes if you wanted a relative path the input path is
* already relative to the config
*/
conf->reference = strdup(path);
}
}
bool config_file_deinitialize(config_file_t *conf) bool config_file_deinitialize(config_file_t *conf)
{ {
@ -774,6 +813,7 @@ void config_file_initialize(struct config_file *conf)
conf->entries = NULL; conf->entries = NULL;
conf->tail = NULL; conf->tail = NULL;
conf->last = NULL; conf->last = NULL;
conf->reference = NULL;
conf->includes = NULL; conf->includes = NULL;
conf->include_depth = 0; conf->include_depth = 0;
conf->guaranteed_no_duplicates = false; conf->guaranteed_no_duplicates = false;
@ -1245,6 +1285,10 @@ void config_file_dump_orbis(config_file_t *conf, int fd)
{ {
struct config_entry_list *list = NULL; struct config_entry_list *list = NULL;
struct config_include_list *includes = conf->includes; struct config_include_list *includes = conf->includes;
if (conf->reference)
fprintf(file, "#reference \"%s\"\n", conf->reference);
while (includes) while (includes)
{ {
char cad[256]; char cad[256];
@ -1278,6 +1322,9 @@ void config_file_dump(config_file_t *conf, FILE *file, bool sort)
struct config_entry_list *list = NULL; struct config_entry_list *list = NULL;
struct config_include_list *includes = conf->includes; struct config_include_list *includes = conf->includes;
if (conf->reference)
fprintf(file, "#reference \"%s\"\n", conf->reference);
while (includes) while (includes)
{ {
fprintf(file, "#include \"%s\"\n", includes->path); fprintf(file, "#include \"%s\"\n", includes->path);

View File

@ -54,6 +54,7 @@ RETRO_BEGIN_DECLS
struct config_file struct config_file
{ {
char *path; char *path;
char *reference;
struct config_entry_list *entries; struct config_entry_list *entries;
struct config_entry_list *tail; struct config_entry_list *tail;
struct config_entry_list *last; struct config_entry_list *last;
@ -107,6 +108,8 @@ config_file_t *config_file_new_from_path_to_string(const char *path);
/* Frees config file. */ /* Frees config file. */
void config_file_free(config_file_t *conf); void config_file_free(config_file_t *conf);
void config_file_set_reference_path(config_file_t *conf, char *path);
bool config_file_deinitialize(config_file_t *conf); bool config_file_deinitialize(config_file_t *conf);
/* Loads a new config, and appends its data to conf. /* Loads a new config, and appends its data to conf.

View File

@ -2893,6 +2893,11 @@ static int generic_action_ok_shader_preset_save(const char *path,
return 0; return 0;
} }
/* Save Auto Preset and have it immediately reapply the preset
* TODO: This seems necessary so that the loaded shader gains a link to the file saved
* But this is slow and seems like a redundant way to do this
* It seems like it would be better to just set the path and shader_preset_loaded
* on the current shader */
if (menu_shader_manager_save_auto_preset(menu_shader_get(), preset_type, if (menu_shader_manager_save_auto_preset(menu_shader_get(), preset_type,
dir_video_shader, dir_menu_config, dir_video_shader, dir_menu_config,
true)) true))

View File

@ -5311,6 +5311,10 @@ unsigned menu_displaylist_build_list(
case DISPLAYLIST_SHADER_PRESET_SAVE: case DISPLAYLIST_SHADER_PRESET_SAVE:
{ {
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
PARSE_ONLY_BOOL, false) == 0)
count++;
if (menu_entries_append_enum(list, if (menu_entries_append_enum(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS),
msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS), msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS),

View File

@ -9170,6 +9170,21 @@ static bool setting_append_list(
general_read_handler, general_read_handler,
SD_FLAG_NONE); SD_FLAG_NONE);
CONFIG_BOOL(
list, list_info,
&settings->bools.video_shader_preset_save_reference_enable,
MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_REFERENCE,
DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_NONE);
END_SUB_GROUP(list, list_info, parent_group); END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group);
} }

View File

@ -2325,6 +2325,7 @@ enum msg_hash_enums
MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_PARENT), MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_PARENT),
MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_GAME), MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_GAME),
MENU_LABEL(VIDEO_SHADER_PRESET_SAVE), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE),
MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_REFERENCE),
MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_AS), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_AS),
MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_GLOBAL), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_GLOBAL),
MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_CORE), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_CORE),

View File

@ -8063,7 +8063,9 @@ bool menu_shader_manager_init(void)
video_shader_driver_get_current_shader(&shader_info); video_shader_driver_get_current_shader(&shader_info);
if (shader_info.data) if (shader_info.data)
path_shader = shader_info.data->path; /* Use the path of the originally loaded preset because it could
* have been a preset with a #reference in it to another preset */
path_shader = shader_info.data->loaded_preset_path;
else else
path_shader = retroarch_get_shader_preset(); path_shader = retroarch_get_shader_preset();
@ -8195,9 +8197,10 @@ clear:
} }
static bool menu_shader_manager_save_preset_internal( static bool menu_shader_manager_save_preset_internal(
const struct video_shader *shader, const char *basename, const struct video_shader *shader,
const char *basename,
const char *dir_video_shader, const char *dir_video_shader,
bool apply, bool save_reference, bool apply,
const char **target_dirs, const char **target_dirs,
size_t num_target_dirs) size_t num_target_dirs)
{ {
@ -8208,6 +8211,10 @@ static bool menu_shader_manager_save_preset_internal(
char fullname[PATH_MAX_LENGTH]; char fullname[PATH_MAX_LENGTH];
char buffer[PATH_MAX_LENGTH]; char buffer[PATH_MAX_LENGTH];
struct rarch_state *p_rarch = &rarch_st;
settings_t *settings = p_rarch->configuration_settings;
bool save_reference = settings->bools.video_shader_preset_save_reference_enable;
fullname[0] = buffer[0] = '\0'; fullname[0] = buffer[0] = '\0';
if (!shader || !shader->passes) if (!shader || !shader->passes)
@ -8218,9 +8225,6 @@ static bool menu_shader_manager_save_preset_internal(
if (type == RARCH_SHADER_NONE) if (type == RARCH_SHADER_NONE)
return false; return false;
if (shader->modified)
save_reference = false;
if (!string_is_empty(basename)) if (!string_is_empty(basename))
{ {
/* We are comparing against a fixed list of file /* We are comparing against a fixed list of file
@ -8396,7 +8400,7 @@ static bool menu_shader_manager_operate_auto_preset(
return menu_shader_manager_save_preset_internal( return menu_shader_manager_save_preset_internal(
shader, file, shader, file,
dir_video_shader, dir_video_shader,
apply, true, apply,
auto_preset_dirs, auto_preset_dirs,
ARRAY_SIZE(auto_preset_dirs)); ARRAY_SIZE(auto_preset_dirs));
case AUTO_SHADER_OP_REMOVE: case AUTO_SHADER_OP_REMOVE:
@ -8546,7 +8550,7 @@ bool menu_shader_manager_save_preset(const struct video_shader *shader,
return menu_shader_manager_save_preset_internal( return menu_shader_manager_save_preset_internal(
shader, basename, shader, basename,
dir_video_shader, dir_video_shader,
apply, false, apply,
preset_dirs, preset_dirs,
ARRAY_SIZE(preset_dirs)); ARRAY_SIZE(preset_dirs));
} }