1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http:www.gnu.org/licenses/>.
15  */
16 
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "../config.h"
22 #endif
23 
24 #include <libretro.h>
25 #include <compat/posix_string.h>
26 #include <compat/msvc.h>
27 #include <compat/strl.h>
28 #include <file/file_path.h>
29 #include <lrc_hash.h>
30 #include <string/stdstring.h>
31 #include <streams/file_stream.h>
32 #include <lists/string_list.h>
33 
34 #include "../configuration.h"
35 #include "../verbosity.h"
36 #include "../frontend/frontend_driver.h"
37 #include "../command.h"
38 #include "../file_path_special.h"
39 #include "../retroarch.h"
40 #include "video_shader_parse.h"
41 
42 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
43 #include "drivers_shader/slang_process.h"
44 #endif
45 
46 /* Maximum depth of chain of referenced shader presets.
47  * 16 seems to be a very large number of references at the moment. */
48 #define SHADER_MAX_REFERENCE_DEPTH 16
49 
50 /* TODO/FIXME - global state - perhaps move outside this file */
51 static path_change_data_t *file_change_data = NULL;
52 
53 /**
54  * fill_pathname_expanded_and_absolute:
55  * @out_path              : string to write into
56  * @in_refpath            : used to get the base path if in_path is relative
57  * @in_path               : path to turn into an absolute path
58  *
59  * Takes a path and returns an absolute path,
60  * It will expand it if the path was using the root path format
61  * e.g. :\shaders
62  * If the path was relative it will take this path and get the
63  * absolute path using in_refpath
64  * as the path to extract a base path
65  *
66  * out_path is filled with the absolute path
67  **/
fill_pathname_expanded_and_absolute(char * out_path,const char * in_refpath,const char * in_path)68 static void fill_pathname_expanded_and_absolute(char *out_path,
69       const char *in_refpath, const char *in_path)
70 {
71    char expanded_path[PATH_MAX_LENGTH];
72 
73    expanded_path[0] = '\0';
74 
75    /* Expand paths which start with :\ to an absolute path */
76    fill_pathname_expand_special(expanded_path,
77          in_path, sizeof(expanded_path));
78 
79    /* Resolve the reference path relative to the config */
80    if (path_is_absolute(expanded_path))
81       strlcpy(out_path, expanded_path, PATH_MAX_LENGTH);
82    else
83       fill_pathname_resolve_relative(out_path, in_refpath,
84             in_path, PATH_MAX_LENGTH);
85 
86    pathname_conform_slashes_to_os(out_path);
87 }
88 
89 /**
90  * wrap_mode_to_str:
91  * @type              : Wrap type.
92  *
93  * Translates wrap mode to human-readable string identifier.
94  *
95  * Returns: human-readable string identifier of wrap mode.
96  **/
wrap_mode_to_str(enum gfx_wrap_type type)97 static const char *wrap_mode_to_str(enum gfx_wrap_type type)
98 {
99    switch (type)
100    {
101       case RARCH_WRAP_BORDER:
102          return "clamp_to_border";
103       case RARCH_WRAP_EDGE:
104          return "clamp_to_edge";
105       case RARCH_WRAP_REPEAT:
106          return "repeat";
107       case RARCH_WRAP_MIRRORED_REPEAT:
108          return "mirrored_repeat";
109       default:
110          break;
111    }
112 
113    return "???";
114 }
115 
116 /**
117  * wrap_str_to_mode:
118  * @type              : Wrap type in human-readable string format.
119  *
120  * Translates wrap mode from human-readable string to enum mode value.
121  *
122  * Returns: enum mode value of wrap type.
123  **/
wrap_str_to_mode(const char * wrap_mode)124 static enum gfx_wrap_type wrap_str_to_mode(const char *wrap_mode)
125 {
126    if (string_is_equal(wrap_mode,      "clamp_to_border"))
127       return RARCH_WRAP_BORDER;
128    else if (string_is_equal(wrap_mode, "clamp_to_edge"))
129       return RARCH_WRAP_EDGE;
130    else if (string_is_equal(wrap_mode, "repeat"))
131       return RARCH_WRAP_REPEAT;
132    else if (string_is_equal(wrap_mode, "mirrored_repeat"))
133       return RARCH_WRAP_MIRRORED_REPEAT;
134 
135    RARCH_WARN("[Shaders]:  Invalid wrapping type %s. Valid ones are: clamp_to_border"
136          " (default), clamp_to_edge, repeat and mirrored_repeat. Falling back to default.\n",
137          wrap_mode);
138    return RARCH_WRAP_DEFAULT;
139 }
140 
141 /**
142  * video_shader_parse_pass:
143  * @conf              : Preset file to read from.
144  * @pass              : Shader passes handle.
145  * @i                 : Index of shader pass.
146  *
147  * Parses shader pass from preset file.
148  *
149  * Returns: true (1) if successful, otherwise false (0).
150  **/
video_shader_parse_pass(config_file_t * conf,struct video_shader_pass * pass,unsigned i)151 static bool video_shader_parse_pass(config_file_t *conf,
152       struct video_shader_pass *pass, unsigned i)
153 {
154    char shader_name[64];
155    char filter_name_buf[64];
156    char wrap_name_buf[64];
157    char frame_count_mod_buf[64];
158    char srgb_output_buf[64];
159    char fp_fbo_buf[64];
160    char mipmap_buf[64];
161    char alias_buf[64];
162    char scale_name_buf[64];
163    char attr_name_buf[64];
164    char scale_type[64];
165    char scale_type_x[64];
166    char scale_type_y[64];
167    char tmp_path[PATH_MAX_LENGTH];
168    struct gfx_fbo_scale *scale      = NULL;
169    bool tmp_bool                    = false;
170    float fattr                      = 0.0f;
171    int iattr                        = 0;
172    struct config_entry_list *entry  = NULL;
173 
174    fp_fbo_buf[0]      = mipmap_buf[0]          = alias_buf[0]       =
175    scale_name_buf[0]  = attr_name_buf[0]       = scale_type[0]      =
176    scale_type_x[0]    = scale_type_y[0]        =
177    shader_name[0]     = filter_name_buf[0]     = wrap_name_buf[0]   =
178                         frame_count_mod_buf[0] = srgb_output_buf[0] = '\0';
179 
180    /* Source */
181    snprintf(shader_name, sizeof(shader_name), "shader%u", i);
182    if (!config_get_path(conf, shader_name, tmp_path, sizeof(tmp_path)))
183    {
184       RARCH_ERR("[Shaders]:  Couldn't parse shader source (%s).\n", shader_name);
185       return false;
186    }
187 
188    /* Get the absolute path */
189    fill_pathname_expanded_and_absolute(pass->source.path,
190          conf->path, tmp_path);
191 
192    /* Smooth */
193    snprintf(filter_name_buf, sizeof(filter_name_buf), "filter_linear%u", i);
194 
195    if (config_get_bool(conf, filter_name_buf, &tmp_bool))
196    {
197       bool smooth  = tmp_bool;
198       pass->filter = smooth ? RARCH_FILTER_LINEAR : RARCH_FILTER_NEAREST;
199    }
200    else
201       pass->filter = RARCH_FILTER_UNSPEC;
202 
203    /* Wrapping mode */
204    snprintf(wrap_name_buf, sizeof(wrap_name_buf), "wrap_mode%u", i);
205    if ((entry = config_get_entry(conf, wrap_name_buf))
206          && !string_is_empty(entry->value))
207       pass->wrap = wrap_str_to_mode(entry->value);
208    entry = NULL;
209 
210    /* Frame count mod */
211    snprintf(frame_count_mod_buf, sizeof(frame_count_mod_buf),
212          "frame_count_mod%u", i);
213    if ((entry = config_get_entry(conf, frame_count_mod_buf))
214          && !string_is_empty(entry->value))
215       pass->frame_count_mod = (unsigned)strtoul(entry->value, NULL, 0);
216    entry = NULL;
217 
218    /* FBO types and mipmapping */
219    snprintf(srgb_output_buf, sizeof(srgb_output_buf), "srgb_framebuffer%u", i);
220    if (config_get_bool(conf, srgb_output_buf, &tmp_bool))
221       pass->fbo.srgb_fbo = tmp_bool;
222 
223    snprintf(fp_fbo_buf, sizeof(fp_fbo_buf), "float_framebuffer%u", i);
224    if (config_get_bool(conf, fp_fbo_buf, &tmp_bool))
225       pass->fbo.fp_fbo = tmp_bool;
226 
227    snprintf(mipmap_buf, sizeof(mipmap_buf), "mipmap_input%u", i);
228    if (config_get_bool(conf, mipmap_buf, &tmp_bool))
229       pass->mipmap = tmp_bool;
230 
231    snprintf(alias_buf, sizeof(alias_buf), "alias%u", i);
232    if (!config_get_array(conf, alias_buf, pass->alias, sizeof(pass->alias)))
233       *pass->alias = '\0';
234 
235    /* Scale */
236    scale = &pass->fbo;
237    snprintf(scale_name_buf, sizeof(scale_name_buf), "scale_type%u", i);
238    config_get_array(conf, scale_name_buf, scale_type, sizeof(scale_type));
239 
240    snprintf(scale_name_buf, sizeof(scale_name_buf), "scale_type_x%u", i);
241    config_get_array(conf, scale_name_buf, scale_type_x, sizeof(scale_type_x));
242 
243    snprintf(scale_name_buf, sizeof(scale_name_buf), "scale_type_y%u", i);
244    config_get_array(conf, scale_name_buf, scale_type_y, sizeof(scale_type_y));
245 
246    if (!*scale_type && !*scale_type_x && !*scale_type_y)
247       return true;
248 
249    if (*scale_type)
250    {
251       strlcpy(scale_type_x, scale_type, sizeof(scale_type_x));
252       strlcpy(scale_type_y, scale_type, sizeof(scale_type_y));
253    }
254 
255    scale->valid   = true;
256    scale->type_x  = RARCH_SCALE_INPUT;
257    scale->type_y  = RARCH_SCALE_INPUT;
258    scale->scale_x = 1.0;
259    scale->scale_y = 1.0;
260 
261    if (*scale_type_x)
262    {
263       if (string_is_equal(scale_type_x, "source"))
264          scale->type_x = RARCH_SCALE_INPUT;
265       else if (string_is_equal(scale_type_x, "viewport"))
266          scale->type_x = RARCH_SCALE_VIEWPORT;
267       else if (string_is_equal(scale_type_x, "absolute"))
268          scale->type_x = RARCH_SCALE_ABSOLUTE;
269       else
270       {
271          RARCH_ERR("[Shaders]:  Invalid attribute.\n");
272          return false;
273       }
274    }
275 
276    if (*scale_type_y)
277    {
278       if (string_is_equal(scale_type_y, "source"))
279          scale->type_y = RARCH_SCALE_INPUT;
280       else if (string_is_equal(scale_type_y, "viewport"))
281          scale->type_y = RARCH_SCALE_VIEWPORT;
282       else if (string_is_equal(scale_type_y, "absolute"))
283          scale->type_y = RARCH_SCALE_ABSOLUTE;
284       else
285       {
286          RARCH_ERR("[Shaders]:  Invalid attribute.\n");
287          return false;
288       }
289    }
290 
291    snprintf(attr_name_buf, sizeof(attr_name_buf), "scale%u", i);
292 
293    if (scale->type_x == RARCH_SCALE_ABSOLUTE)
294    {
295       if (config_get_int(conf, attr_name_buf, &iattr))
296          scale->abs_x = iattr;
297       else
298       {
299          snprintf(attr_name_buf, sizeof(attr_name_buf), "scale_x%u", i);
300          if (config_get_int(conf, attr_name_buf, &iattr))
301             scale->abs_x = iattr;
302       }
303    }
304    else
305    {
306       if (config_get_float(conf, attr_name_buf, &fattr))
307          scale->scale_x = fattr;
308       else
309       {
310          snprintf(attr_name_buf, sizeof(attr_name_buf), "scale_x%u", i);
311          if (config_get_float(conf, attr_name_buf, &fattr))
312             scale->scale_x = fattr;
313       }
314    }
315 
316    snprintf(attr_name_buf, sizeof(attr_name_buf), "scale%u", i);
317 
318    if (scale->type_y == RARCH_SCALE_ABSOLUTE)
319    {
320       if (config_get_int(conf, attr_name_buf, &iattr))
321          scale->abs_y = iattr;
322       else
323       {
324          snprintf(attr_name_buf, sizeof(attr_name_buf), "scale_y%u", i);
325          if (config_get_int(conf, attr_name_buf, &iattr))
326             scale->abs_y = iattr;
327       }
328    }
329    else
330    {
331       if (config_get_float(conf, attr_name_buf, &fattr))
332          scale->scale_y = fattr;
333       else
334       {
335          snprintf(attr_name_buf, sizeof(attr_name_buf), "scale_y%u", i);
336          if (config_get_float(conf, attr_name_buf, &fattr))
337             scale->scale_y = fattr;
338       }
339    }
340 
341    return true;
342 }
343 
344 /**
345  * video_shader_parse_textures:
346  * @conf              : Preset file to read from.
347  * @shader            : Shader pass handle.
348  *
349  * Parses shader textures.
350  *
351  * Returns: true (1) if successful, otherwise false (0).
352  **/
video_shader_parse_textures(config_file_t * conf,struct video_shader * shader)353 static bool video_shader_parse_textures(config_file_t *conf,
354       struct video_shader *shader)
355 {
356    size_t path_size     = PATH_MAX_LENGTH;
357    const char *id       = NULL;
358    char *save           = NULL;
359    char *textures       = (char*)malloc(1024 + path_size);
360    char texture_path[PATH_MAX_LENGTH];
361 
362    if (!textures)
363       return false;
364 
365    textures[0] = '\0';
366    texture_path[0] = '\0';
367 
368    if (!config_get_array(conf, "textures", textures, 1024))
369    {
370       free(textures);
371       return true;
372    }
373 
374    for (id = strtok_r(textures, ";", &save);
375          id && shader->luts < GFX_MAX_TEXTURES;
376          shader->luts++, id = strtok_r(NULL, ";", &save))
377    {
378       char id_filter[64];
379       char id_wrap[64];
380       char id_mipmap[64];
381       bool mipmap         = false;
382       bool smooth         = false;
383       struct config_entry_list
384          *entry           = NULL;
385 
386       id_filter[0] = id_wrap[0] = id_mipmap[0] = '\0';
387 
388       if (!(entry = config_get_entry(conf, id)) ||
389             string_is_empty(entry->value))
390       {
391          RARCH_ERR("[Shaders]:  Cannot find path to texture \"%s\" ...\n",
392                id);
393          free(textures);
394          return false;
395       }
396 
397       config_get_path(conf, id, texture_path, sizeof(texture_path));
398 
399       /* Get the absolute path */
400       fill_pathname_expanded_and_absolute(
401             shader->lut[shader->luts].path, conf->path, texture_path);
402 
403       entry = NULL;
404 
405       strlcpy(shader->lut[shader->luts].id, id,
406             sizeof(shader->lut[shader->luts].id));
407 
408       strlcpy(id_filter, id, sizeof(id_filter));
409       strlcat(id_filter, "_linear", sizeof(id_filter));
410       if (config_get_bool(conf, id_filter, &smooth))
411          shader->lut[shader->luts].filter = smooth
412             ? RARCH_FILTER_LINEAR
413             : RARCH_FILTER_NEAREST;
414       else
415          shader->lut[shader->luts].filter = RARCH_FILTER_UNSPEC;
416 
417       strlcpy(id_wrap, id, sizeof(id_wrap));
418       strlcat(id_wrap, "_wrap_mode", sizeof(id_wrap));
419       if ((entry = config_get_entry(conf, id_wrap))
420             && !string_is_empty(entry->value))
421          shader->lut[shader->luts].wrap = wrap_str_to_mode(entry->value);
422       entry = NULL;
423 
424       strlcpy(id_mipmap, id, sizeof(id_mipmap));
425       strlcat(id_mipmap, "_mipmap", sizeof(id_mipmap));
426       if (config_get_bool(conf, id_mipmap, &mipmap))
427          shader->lut[shader->luts].mipmap = mipmap;
428       else
429          shader->lut[shader->luts].mipmap = false;
430    }
431 
432    free(textures);
433    return true;
434 }
435 
436 /**
437  * video_shader_parse_find_parameter:
438  * @params            : Shader parameter handle.
439  * @num_params        : Number of shader params in @params.
440  * @id                : Identifier to search for.
441  *
442  * Finds a shader parameter with identifier @id in @params..
443  *
444  * Returns: handle to shader parameter if successful, otherwise NULL.
445  **/
video_shader_parse_find_parameter(struct video_shader_parameter * params,unsigned num_params,const char * id)446 static struct video_shader_parameter *video_shader_parse_find_parameter(
447       struct video_shader_parameter *params,
448       unsigned num_params, const char *id)
449 {
450    unsigned i;
451 
452    for (i = 0; i < num_params; i++)
453    {
454       if (string_is_equal(params[i].id, id))
455          return &params[i];
456    }
457 
458    return NULL;
459 }
460 
461 /**
462  * video_shader_resolve_parameters:
463  * @conf              : Preset file to read from.
464  * @shader            : Shader passes handle.
465  *
466  * Resolves all shader parameters belonging to shaders
467  * from the #pragma parameter lines in the shader for each pass.
468  *
469  * Returns: true (1) if successful, otherwise false (0).
470  **/
video_shader_resolve_parameters(struct video_shader * shader)471 bool video_shader_resolve_parameters(struct video_shader *shader)
472 {
473    unsigned i;
474    struct video_shader_parameter *param = &shader->parameters[0];
475 
476    shader->num_parameters = 0;
477 
478    /* Find all parameters in our shaders. */
479 
480    RARCH_DBG("[Shaders]:  Finding Parameters in Shader Passes (#pragma parameter)\n");
481 
482    for (i = 0; i < shader->passes; i++)
483    {
484       const char *path          = shader->pass[i].source.path;
485       uint8_t *buf              = NULL;
486       int64_t buf_len           = 0;
487 
488       if (string_is_empty(path))
489          continue;
490 
491       if (!path_is_valid(path))
492          continue;
493 
494       /* First try to use the more robust slang implementation
495        * to support #includes. */
496 
497 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
498       /* FIXME: The check for slang can be removed
499        * if it's sufficiently tested for GLSL/Cg as well,
500        * it should be the same implementation.
501        * The problem with switching currently is that it looks
502        * for a #version string in the first line of the file
503        * which glsl doesn't have */
504 
505       if (     string_is_equal(path_get_extension(path), "slang")
506             && slang_preprocess_parse_parameters(path, shader))
507          continue;
508 #endif
509 
510       /* Read file contents */
511       if (filestream_read_file(path, (void**)&buf, &buf_len))
512       {
513          size_t line_index         = 0;
514          struct string_list lines  = {0};
515          bool lines_inited         = false;
516 
517          /* Split into lines */
518          if (buf_len > 0)
519          {
520             string_list_initialize(&lines);
521             lines_inited = string_split_noalloc(&lines, (const char*)buf, "\n");
522          }
523 
524          /* Buffer is no longer required - clean up */
525          if ((void*)buf)
526             free((void*)buf);
527 
528          if (!lines_inited)
529             continue;
530 
531          /* Even though the pass is set in the loop too,
532           * not all passes have parameters */
533          param->pass = i;
534 
535          while ((shader->num_parameters < ARRAY_SIZE(shader->parameters))
536                && (line_index < lines.size))
537          {
538             int ret;
539             const char *line = lines.elems[line_index].data;
540             line_index++;
541 
542             /* Check if this is a '#pragma parameter' line */
543             if (strncmp("#pragma parameter", line,
544                      STRLEN_CONST("#pragma parameter")))
545                continue;
546 
547             /* Parse line */
548             ret = sscanf(line, "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f",
549                   param->id,        param->desc,    &param->initial,
550                   &param->minimum, &param->maximum, &param->step);
551 
552             if (ret < 5)
553                continue;
554 
555             param->id[63]   = '\0';
556             param->desc[63] = '\0';
557 
558             if (ret == 5)
559                param->step  = 0.1f * (param->maximum - param->minimum);
560 
561             param->pass     = i;
562 
563             RARCH_DBG("[Shaders]:     Found #pragma parameter %s (%s) %f %f %f %f in pass %d\n",
564                   param->desc,    param->id,      param->initial,
565                   param->minimum, param->maximum, param->step, param->pass);
566             param->current  = param->initial;
567 
568             shader->num_parameters++;
569             param++;
570          }
571 
572          string_list_deinitialize(&lines);
573       }
574    }
575 
576    return true;
577 }
578 
579 
580 /**
581  * video_shader_load_current_parameter_values:
582  * @conf              : Preset file to read from.
583  * @shader            : Shader passes handle.
584  *
585  * For each parameter in the shader, if a value is set in the config file
586  * load this value to the parameter's current value.
587  *
588  * Returns: true (1) if successful, otherwise false (0).
589  **/
video_shader_load_current_parameter_values(config_file_t * conf,struct video_shader * shader)590 bool video_shader_load_current_parameter_values(
591       config_file_t *conf, struct video_shader *shader)
592 {
593    unsigned i;
594    bool load_parameter_message_shown      = false;
595    const struct config_entry_list *entry  = NULL;
596 
597    if (!conf)
598    {
599       RARCH_ERR("[Shaders]: Load Parameter Values - Config is Null.\n");
600       return false;
601    }
602 
603    /* For all parameters in the shader see if there is any config value set */
604    for (i = 0; i < shader->num_parameters; i++)
605    {
606       entry = config_get_entry(conf, shader->parameters[i].id);
607 
608       /* Only try to load the parameter value if an entry exists in the config */
609       if (entry)
610       {
611          struct video_shader_parameter *parameter = (struct video_shader_parameter*)
612                                                    video_shader_parse_find_parameter(shader->parameters,
613                                                                                     shader->num_parameters,
614                                                                                     shader->parameters[i].id);
615          /* Log the message for loading parameter values only once*/
616          if (!load_parameter_message_shown)
617          {
618             RARCH_DBG("[Shaders]:    Loading base parameter values\n");
619             load_parameter_message_shown = true;
620          }
621 
622          /* Log each parameter read */
623          if (config_get_float(conf, shader->parameters[i].id, &parameter->current))
624             RARCH_DBG("[Shaders]:      Load parameter value:   %s = %f.\n", shader->parameters[i].id, parameter->current);
625          else
626             RARCH_WARN("[Shaders]:      Load parameter value: name %s is set in preset but couldn't load its value.\n",
627                         shader->parameters[i].id);
628       }
629    }
630 
631    return true;
632 }
633 
scale_type_to_str(enum gfx_scale_type type)634 static const char *scale_type_to_str(enum gfx_scale_type type)
635 {
636    switch (type)
637    {
638       case RARCH_SCALE_INPUT:
639          return "source";
640       case RARCH_SCALE_VIEWPORT:
641          return "viewport";
642       case RARCH_SCALE_ABSOLUTE:
643          return "absolute";
644       default:
645          break;
646    }
647 
648    return "?";
649 }
650 
shader_write_scale_dim(config_file_t * conf,const char * dim,enum gfx_scale_type type,float scale,unsigned absolute,unsigned i)651 static void shader_write_scale_dim(config_file_t *conf,
652       const char *dim,
653       enum gfx_scale_type type,
654       float scale,
655       unsigned absolute,
656       unsigned i)
657 {
658    char key[64];
659 
660    key[0] = '\0';
661 
662    snprintf(key, sizeof(key), "scale_type_%s%u", dim, i);
663    config_set_string(conf, key, scale_type_to_str(type));
664 
665    snprintf(key, sizeof(key), "scale_%s%u", dim, i);
666    if (type == RARCH_SCALE_ABSOLUTE)
667       config_set_int(conf, key, absolute);
668    else
669       config_set_float(conf, key, scale);
670 }
671 
shader_write_fbo(config_file_t * conf,const struct gfx_fbo_scale * fbo,unsigned i)672 static void shader_write_fbo(config_file_t *conf,
673       const struct gfx_fbo_scale *fbo, unsigned i)
674 {
675    char key[64];
676 
677    key[0] = '\0';
678 
679    snprintf(key, sizeof(key), "float_framebuffer%u", i);
680    config_set_bool(conf, key, fbo->fp_fbo);
681    snprintf(key, sizeof(key), "srgb_framebuffer%u", i);
682    config_set_bool(conf, key, fbo->srgb_fbo);
683 
684    if (!fbo->valid)
685       return;
686 
687    shader_write_scale_dim(conf, "x", fbo->type_x, fbo->scale_x, fbo->abs_x, i);
688    shader_write_scale_dim(conf, "y", fbo->type_y, fbo->scale_y, fbo->abs_y, i);
689 }
690 
691 /**
692  * video_shader_write_root_preset:
693  * @conf              : Preset file to write to.
694  * @shader            : Shader passes handle.
695  * @preset_path       : Optional path to where the preset will be written.
696  *
697  * Writes preset and all associated state (passes, textures, imports,
698  * etc) into @conf.
699  * If @preset_path is not NULL, shader paths are saved relative to it.
700  **/
video_shader_write_root_preset(const struct video_shader * shader,const char * path)701 static bool video_shader_write_root_preset(const struct video_shader *shader,
702       const char *path)
703 {
704    bool ret = true;
705    unsigned i;
706    char key[64];
707    size_t tmp_size      = PATH_MAX_LENGTH;
708    char *tmp            = (char*)malloc(3*tmp_size);
709    char *tmp_rel        = tmp +   tmp_size;
710    char *tmp_base       = tmp + 2*tmp_size;
711    config_file_t *conf  = NULL;
712 
713    if (!(conf = config_file_new_alloc()))
714    {
715       ret = false;
716       goto end;
717    }
718 
719    if (!tmp)
720    {
721       ret = false;
722       goto end;
723    }
724 
725    RARCH_DBG("[Shaders]:  Saving FULL PRESET to: %s\n", path);
726 
727    config_set_int(conf, "shaders", shader->passes);
728    if (shader->feedback_pass >= 0)
729       config_set_int(conf, "feedback_pass", shader->feedback_pass);
730 
731    strlcpy(tmp_base, path, tmp_size);
732 
733    /* ensure we use a clean base like the shader passes and texture paths do */
734    path_resolve_realpath(tmp_base, tmp_size, false);
735    path_basedir(tmp_base);
736 
737    for (i = 0; i < shader->passes; i++)
738    {
739       const struct video_shader_pass *pass = &shader->pass[i];
740 
741       snprintf(key, sizeof(key), "shader%u", i);
742 
743       strlcpy(tmp, pass->source.path, tmp_size);
744       path_relative_to(tmp_rel, tmp, tmp_base, tmp_size);
745 
746       pathname_make_slashes_portable(tmp_rel);
747 
748       config_set_path(conf, key, tmp_rel);
749 
750 
751       if (pass->filter != RARCH_FILTER_UNSPEC)
752       {
753          snprintf(key, sizeof(key), "filter_linear%u", i);
754          config_set_bool(conf, key, pass->filter == RARCH_FILTER_LINEAR);
755       }
756 
757       snprintf(key, sizeof(key), "wrap_mode%u", i);
758       config_set_string(conf, key, wrap_mode_to_str(pass->wrap));
759 
760       if (pass->frame_count_mod)
761       {
762          snprintf(key, sizeof(key), "frame_count_mod%u", i);
763          config_set_int(conf, key, pass->frame_count_mod);
764       }
765 
766       snprintf(key, sizeof(key), "mipmap_input%u", i);
767       config_set_bool(conf, key, pass->mipmap);
768 
769       snprintf(key, sizeof(key), "alias%u", i);
770       config_set_string(conf, key, pass->alias);
771 
772       shader_write_fbo(conf, &pass->fbo, i);
773    }
774 
775    /* Write shader parameters which are different than the default shader values */
776    if (shader->num_parameters)
777       for (i = 0; i < shader->num_parameters; i++)
778          if (shader->parameters[i].current != shader->parameters[i].initial)
779             config_set_float(conf, shader->parameters[i].id, shader->parameters[i].current);
780 
781    if (shader->luts)
782    {
783       char textures[4096];
784 
785       textures[0] = '\0';
786 
787       /* Names of the textures */
788       strlcpy(textures, shader->lut[0].id, sizeof(textures));
789 
790       for (i = 1; i < shader->luts; i++)
791       {
792          /* O(n^2), but number of textures is very limited. */
793          strlcat(textures, ";", sizeof(textures));
794          strlcat(textures, shader->lut[i].id, sizeof(textures));
795       }
796 
797       config_set_string(conf, "textures", textures);
798 
799       /* Step through the textures in the shader */
800       for (i = 0; i < shader->luts; i++)
801       {
802          fill_pathname_abbreviated_or_relative(tmp_rel,
803                tmp_base, shader->lut[i].path, PATH_MAX_LENGTH);
804          pathname_make_slashes_portable(tmp_rel);
805          config_set_string(conf, shader->lut[i].id, tmp_rel);
806 
807          /* Linear filter ON or OFF */
808          if (shader->lut[i].filter != RARCH_FILTER_UNSPEC)
809          {
810             char key[128];
811             key[0]  = '\0';
812             strlcpy(key, shader->lut[i].id, sizeof(key));
813             strlcat(key, "_linear", sizeof(key));
814             config_set_bool(conf, key,
815                   shader->lut[i].filter == RARCH_FILTER_LINEAR);
816          }
817 
818          /* Wrap Mode */
819          {
820             char key[128];
821             key[0]  = '\0';
822             strlcpy(key, shader->lut[i].id, sizeof(key));
823             strlcat(key, "_wrap_mode", sizeof(key));
824             config_set_string(conf, key,
825                   wrap_mode_to_str(shader->lut[i].wrap));
826          }
827 
828          /* Mipmap On or Off */
829          {
830             char key[128];
831             key[0]  = '\0';
832             strlcpy(key, shader->lut[i].id, sizeof(key));
833             strlcat(key, "_mipmap", sizeof(key));
834             config_set_bool(conf, key, shader->lut[i].mipmap);
835          }
836       }
837    }
838 
839    /* Write the File! */
840    ret = config_file_write(conf, path, false);
841 
842 end:
843    config_file_free(conf);
844    free(tmp);
845 
846    return ret;
847 }
848 
video_shader_get_root_preset_config(const char * path)849 static config_file_t *video_shader_get_root_preset_config(const char *path)
850 {
851    int reference_depth           = 1;
852    config_file_t *conf           = config_file_new_from_path_to_string(path);
853    char* nested_reference_path   = (char*)malloc(PATH_MAX_LENGTH);
854 
855    if (!conf)
856       goto end;
857 
858    while (conf->reference)
859    {
860       /* If we have reached the max depth of nested references,
861        * stop attempting to read the next reference,
862        * because we are likely in a self referential loop.
863        *
864        * SHADER_MAX_REFERENCE_DEPTH references deep seems
865        * like more than enough depth for expected usage */
866       if (reference_depth > SHADER_MAX_REFERENCE_DEPTH)
867       {
868          RARCH_ERR("[Shaders] - Get Root Preset - Exceeded maximum reference depth(%u) without finding a full preset. "
869                "This chain of referenced presets is likely cyclical.\n", SHADER_MAX_REFERENCE_DEPTH);
870          config_file_free(conf);
871          conf = NULL;
872          goto end;
873       }
874 
875       /* Get the absolute path for the reference */
876       fill_pathname_expanded_and_absolute(nested_reference_path, conf->path, conf->reference);
877 
878       /* Create a new config from the referenced path */
879       config_file_free(conf);
880       conf = config_file_new_from_path_to_string(nested_reference_path);
881 
882       /* If we can't read the reference preset */
883       if (!conf)
884       {
885          RARCH_WARN("[Shaders]:  Could not read shader preset in #reference line: %s\n", nested_reference_path);
886          goto end;
887       }
888 
889       reference_depth += 1;
890    }
891 
892 end:
893    free(nested_reference_path);
894    return conf;
895 }
896 
897 /**
898  * video_shader_check_reference_chain:
899  * @path_to_save              : Path of the preset we want to validate is safe to save
900  *                              as a simple preset
901  * @reference_path            : Path of the reference which we would want to write into
902  *                              the new preset
903  *
904  * Checks to see if we can save a valid simple preset (preset with a #reference in it)
905  * to this path
906  *
907  * This takes into account reference links which can't be loaded and if saving
908  * this file would create a creating circular reference chain because some link in
909  * the chain references the file path we want to save to
910  *
911  * Checks each preset in the chain of presets with #reference
912  * Starts with reference_path, If it has no reference then our check is valid
913  * If it has a #reference then check that the reference path is not the same as path_to_save
914  * If it is not the same path then go the the next nested reference
915  *
916  * Continues this until it finds a preset without #reference in it,
917  * or it hits the maximum recursion depth (at that point
918  * it is probably in a self referential cycle)
919  *
920  * Returns: true (1) if it was able to load all presets and found a full preset
921  *          otherwise false (0).
922  **/
video_shader_check_reference_chain_for_save(const char * path_to_save,const char * reference_path)923 static bool video_shader_check_reference_chain_for_save(
924       const char *path_to_save, const char *reference_path)
925 {
926    config_file_t *conf           = config_file_new_from_path_to_string(
927          reference_path);
928    char* nested_reference_path   = (char*)malloc(PATH_MAX_LENGTH);
929    char* path_to_save_conformed  = (char*)malloc(PATH_MAX_LENGTH);
930    bool return_val               = true;
931 
932    strlcpy(path_to_save_conformed, path_to_save, PATH_MAX_LENGTH);
933    pathname_conform_slashes_to_os(path_to_save_conformed);
934 
935    if (!conf)
936    {
937       RARCH_ERR("[Shaders]:  Could not read the #reference preset: %s\n", reference_path);
938       return_val = false;
939    }
940    else
941    {
942       int reference_depth = 1;
943 
944       while (conf->reference)
945       {
946          /* If we have reached the max depth of nested references stop attempting to read
947           * the next reference because we are likely in a self referential loop. */
948          if (reference_depth > SHADER_MAX_REFERENCE_DEPTH)
949          {
950             RARCH_ERR("[Shaders] - Check Reference Chain for Save - Exceeded maximum reference depth(%u) without "
951                       "finding a full preset. This chain of referenced presets is likely cyclical.\n", SHADER_MAX_REFERENCE_DEPTH);
952             return_val = false;
953             break;
954          }
955 
956          /* Get the absolute path for the reference */
957          fill_pathname_expanded_and_absolute(nested_reference_path, conf->path, conf->reference);
958 
959          /* If one of the reference paths is the same as the file we want to save then this reference chain would be
960           * self-referential / cyclical and we can't save this as a simple preset*/
961          if (string_is_equal(nested_reference_path, path_to_save_conformed))
962          {
963             RARCH_WARN("[Shaders]:  Saving preset:\n"
964                        "                                              %s\n"
965                        "                                          With a #reference of:\n"
966                        "                                              %s\n"
967                        "                                          Would create a cyclical reference in preset:\n"
968                        "                                              %s\n"
969                        "                                          Which already references preset:\n"
970                        "                                              %s\n\n",
971                        path_to_save_conformed, reference_path, conf->path, nested_reference_path);
972             return_val = false;
973             break;
974          }
975 
976          /* Create a new config from the referenced path */
977          config_file_free(conf);
978          conf = config_file_new_from_path_to_string(nested_reference_path);
979 
980          /* If we can't read the reference preset */
981          if (!conf)
982          {
983             RARCH_WARN("[Shaders]:  Could not read shader preset"
984                   " in #reference line: %s\n", nested_reference_path);
985             return_val = false;
986             break;
987          }
988 
989          reference_depth += 1;
990       }
991    }
992 
993 
994    free(path_to_save_conformed);
995    free(nested_reference_path);
996    config_file_free(conf);
997 
998    return return_val;
999 }
1000 
1001 /**
1002  * video_shader_write_referenced_preset:
1003  * @path              : File to write to
1004  * @shader            : Shader preset to write
1005  *
1006  * Writes a referenced preset to disk
1007  *    A referenced preset is a preset which includes the #reference directive
1008  *    as it's first line to specify a root preset and can also
1009  *    include parameter and texture values to override the values
1010  *    of the root preset
1011  *
1012  * Returns false if a referenced preset cannot be saved
1013  **/
video_shader_write_referenced_preset(const char * path_to_save,const char * shader_dir,const struct video_shader * shader)1014 static bool video_shader_write_referenced_preset(
1015       const char *path_to_save,
1016       const char *shader_dir,
1017       const struct video_shader *shader)
1018 {
1019    unsigned i;
1020    config_file_t *conf                    = NULL;
1021    config_file_t *reference_conf          = NULL;
1022    struct video_shader *referenced_shader = (struct video_shader*)
1023       calloc(1, sizeof(*referenced_shader));
1024    bool ret                               = false;
1025    bool continue_saving_reference         = true;
1026    char *new_preset_basedir               = strdup(path_to_save);
1027    char *config_dir                       = (char*)malloc(PATH_MAX_LENGTH);
1028    char *relative_temp_reference_path     = (char*)malloc(PATH_MAX_LENGTH);
1029    char *abs_temp_reference_path          = (char*)malloc(PATH_MAX_LENGTH);
1030    char *path_to_reference                = (char*)malloc(PATH_MAX_LENGTH);
1031    char* path_to_save_conformed           = (char*)malloc(PATH_MAX_LENGTH);
1032 
1033    strlcpy(path_to_save_conformed, path_to_save, PATH_MAX_LENGTH);
1034    pathname_conform_slashes_to_os(path_to_save_conformed);
1035 
1036    config_dir[0]                          = '\0';
1037    relative_temp_reference_path[0]        = '\0';
1038    abs_temp_reference_path[0]             = '\0';
1039    path_to_reference[0]                   = '\0';
1040 
1041    path_basedir(new_preset_basedir);
1042 
1043    /* Get the retroarch config dir where the automatically
1044     * loaded presets are located
1045     * and where Save Game Preset, Save Core Preset,
1046     * Save Global Preset save to */
1047    fill_pathname_application_special(config_dir, PATH_MAX_LENGTH,
1048          APPLICATION_SPECIAL_DIRECTORY_CONFIG);
1049 
1050    /* If there is no initial preset path loaded */
1051    if (string_is_empty(shader->loaded_preset_path))
1052    {
1053       RARCH_WARN("[Shaders]: Saving Full Preset because the loaded Shader"
1054             "does not have "
1055             "a path to a previously loaded preset file on disk.\n");
1056       goto end;
1057    }
1058 
1059    /* If the initial preset loaded is the ever-changing retroarch
1060     * preset don't save a reference
1061     * TODO/FIXME - remove once we don't write this preset anymore */
1062    if (!strncmp(path_basename_nocompression(shader->loaded_preset_path),
1063             "retroarch",
1064             STRLEN_CONST("retroarch")))
1065    {
1066       RARCH_WARN("[Shaders]: Saving Full Preset because we can't save"
1067             " a reference to the "
1068             "ever-changing retroarch preset.\n");
1069       goto end;
1070    }
1071 
1072    strlcpy(path_to_reference, shader->loaded_preset_path, PATH_MAX_LENGTH);
1073    pathname_conform_slashes_to_os(path_to_reference);
1074 
1075    /* Get a config from the file we want to make a reference to */
1076    reference_conf = config_file_new_from_path_to_string(path_to_reference);
1077 
1078    /* If the original preset can't be loaded, probably because
1079     * it isn't there anymore */
1080    if (!reference_conf)
1081    {
1082       RARCH_WARN("[Shaders]: Saving Full Preset because the initially"
1083             " loaded preset can't be loaded. "
1084             "It was likely renamed or deleted.\n");
1085       goto end;
1086    }
1087 
1088    /* If we are trying to save on top the path referenced in the
1089     * initially loaded preset.
1090     *
1091     * E.G. Preset_B references Preset_A, I load Preset_B do some
1092     * parameter adjustments,
1093     * then I save on top of Preset_A, we want to get a preset
1094     * just like the original Preset_A with the new parameter
1095     * adjustments.
1096     *
1097     * If there is a reference in the initially loaded preset,
1098     * we should check it against the preset path we are currently
1099     * trying to save */
1100    if (reference_conf->reference)
1101    {
1102       /* Get the absolute path for the reference */
1103       fill_pathname_expanded_and_absolute(abs_temp_reference_path,
1104             reference_conf->path, reference_conf->reference);
1105 
1106       pathname_conform_slashes_to_os(abs_temp_reference_path);
1107 
1108       /* If the reference is the same as the path we are trying to save to
1109          then this should be used as the reference to save */
1110       if (string_is_equal(abs_temp_reference_path, path_to_save_conformed))
1111       {
1112          strlcpy(path_to_reference, abs_temp_reference_path,
1113                PATH_MAX_LENGTH);
1114          config_file_free(reference_conf);
1115          reference_conf = config_file_new_from_path_to_string(
1116                path_to_reference);
1117       }
1118    }
1119 
1120    /*
1121     * If
1122     *    The new preset file we are trying to save is the
1123     *    same as the initially loaded preset
1124     * or
1125     *    The initially loaded preset was located under the
1126     *    retroarch config folder
1127     *    this means that it was likely saved from inside the retroarch UI
1128     * Then
1129     *    We should not save a preset with a reference to the initially loaded
1130     *    preset file itself, instead we need to save a new preset with
1131     *    the same reference as was in the initially loaded preset.
1132     */
1133 
1134    /* If the reference path is the same as the path we want to save
1135     * or the reference path is in the config (auto shader) folder */
1136    if (      string_is_equal(path_to_reference, path_to_save_conformed)
1137          || !strncmp(config_dir, path_to_reference, strlen(config_dir)))
1138    {
1139       /* If the config from the reference path has a reference in it,
1140        * we will use this same nested reference for the new preset */
1141       if (reference_conf->reference)
1142       {
1143          /* Get the absolute path for the reference */
1144          fill_pathname_expanded_and_absolute(path_to_reference,
1145                reference_conf->path, reference_conf->reference);
1146 
1147          /* If the reference path is also the same as what
1148           * we are trying to save
1149             This can easily happen
1150             E.G.
1151             - Save Preset As
1152             - Save Game Preset
1153             - Save Preset As (use same name as first time)
1154          */
1155          if (string_is_equal(path_to_reference, path_to_save_conformed))
1156          {
1157             config_file_free(reference_conf);
1158             reference_conf = config_file_new_from_path_to_string(
1159                   path_to_reference);
1160 
1161             /* If the reference also has a reference inside it */
1162             if (reference_conf->reference)
1163             {
1164                /* Get the absolute path for the reference */
1165                fill_pathname_expanded_and_absolute(path_to_reference,
1166                      reference_conf->path, reference_conf->reference);
1167             }
1168             /* If the config referenced is a full preset */
1169             else
1170             {
1171                RARCH_WARN("[Shaders]: Saving Full Preset because we can't"
1172                      " save a preset which "
1173                      "would reference itself.\n");
1174                goto end;
1175             }
1176          }
1177       }
1178       /* If there is no reference in the initial preset we need to
1179        * save a full preset */
1180       else
1181       {
1182          /* We can't save a reference to ourselves */
1183          RARCH_WARN("[Shaders]: Saving Full Preset because we can't save"
1184                " a preset which "
1185                "would reference itself.\n");
1186          goto end;
1187       }
1188    }
1189 
1190    /* Check the reference chain that we would be saving to make sure it
1191     * is valid */
1192    if (!video_shader_check_reference_chain_for_save(
1193             path_to_save_conformed, path_to_reference))
1194    {
1195       RARCH_WARN("[Shaders]: Saving Full Preset because saving a"
1196             " Simple Preset would result "
1197             "in a cyclical reference, or a preset in the reference"
1198             " chain could not be read.\n");
1199       goto end;
1200    }
1201 
1202    RARCH_DBG("[Shaders]:  Reading Preset to Compare with"
1203          " Current Values: %s\n", path_to_save_conformed);
1204 
1205    /* Load the preset referenced in the preset into the shader */
1206    if (!video_shader_load_preset_into_shader(path_to_reference,
1207             referenced_shader))
1208    {
1209       RARCH_WARN("[Shaders]:  Saving Full Preset because we could"
1210             " not load the preset from the #reference line: %s.\n",
1211             path_to_reference);
1212       goto end;
1213    }
1214 
1215    /* Create a new EMPTY config */
1216    conf = config_file_new_alloc();
1217 
1218    if (!(conf))
1219       goto end;
1220 
1221    conf->path = strdup(path_to_save_conformed);
1222 
1223    pathname_make_slashes_portable(relative_temp_reference_path);
1224 
1225    /* Add the reference path to the config */
1226    config_file_set_reference_path(conf, path_to_reference);
1227 
1228    /* Set modified to true so when you run config_file_write
1229     * it will save a file */
1230    conf->modified = true;
1231 
1232    /*
1233       Compare the shader to a shader created from the referenced
1234       config to see if we can save a referenced preset and what
1235       parameters and textures of the root_config are overridden
1236    */
1237 
1238    /* Check number of passes match */
1239    if (shader->passes != referenced_shader->passes)
1240    {
1241       RARCH_WARN("[Shaders]: passes (Number of Passes) "
1242                   "Current Value doesn't match Referenced Value"
1243                   " - Full Preset will be Saved instead of Simple Preset\n");
1244       continue_saving_reference = false;
1245    }
1246 
1247    /* Compare all passes from the shader, if anything is different
1248     * then we should not save a reference and instead save a
1249     * full preset instead.
1250    */
1251    if (continue_saving_reference)
1252    {
1253       /* Step through each pass comparing all the properties to
1254        * make sure they match */
1255       for (i = 0; (i < shader->passes && continue_saving_reference == true);
1256             i++)
1257       {
1258          const struct video_shader_pass *pass      = &shader->pass[i];
1259          const struct video_shader_pass *root_pass = &referenced_shader->pass[i];
1260          const struct gfx_fbo_scale *fbo           = &pass->fbo;
1261          const struct gfx_fbo_scale *root_fbo      = &root_pass->fbo;
1262 
1263          if (!string_is_equal(pass->source.path, root_pass->source.path))
1264          {
1265 #ifdef DEBUG
1266             RARCH_WARN("[Shaders]: Pass %u path", i);
1267 #endif
1268             continue_saving_reference = false;
1269          }
1270 
1271          if (continue_saving_reference && pass->filter != root_pass->filter)
1272          {
1273 #ifdef DEBUG
1274             RARCH_WARN("[Shaders]: Pass %u filter", i);
1275 #endif
1276             continue_saving_reference = false;
1277          }
1278 
1279          if (continue_saving_reference && pass->wrap != root_pass->wrap)
1280          {
1281 #ifdef DEBUG
1282             RARCH_WARN("[Shaders]: Pass %u wrap", i);
1283 #endif
1284             continue_saving_reference = false;
1285          }
1286 
1287          if (continue_saving_reference && pass->frame_count_mod != root_pass->frame_count_mod)
1288          {
1289 #ifdef DEBUG
1290             RARCH_WARN("[Shaders]: Pass %u frame_count", i);
1291 #endif
1292             continue_saving_reference = false;
1293          }
1294 
1295          if (continue_saving_reference && pass->mipmap != root_pass->mipmap)
1296          {
1297 #ifdef DEBUG
1298             RARCH_WARN("[Shaders]: Pass %u mipmap", i);
1299 #endif
1300             continue_saving_reference = false;
1301          }
1302 
1303          if (continue_saving_reference && !string_is_equal(pass->alias, root_pass->alias))
1304          {
1305 #ifdef DEBUG
1306             RARCH_WARN("[Shaders]: Pass %u alias", i);
1307 #endif
1308             continue_saving_reference = false;
1309          }
1310 
1311          if (continue_saving_reference && fbo->type_x != root_fbo->type_x)
1312          {
1313 #ifdef DEBUG
1314             RARCH_WARN("[Shaders]: Pass %u type_x", i);
1315 #endif
1316             continue_saving_reference = false;
1317          }
1318 
1319          if (continue_saving_reference && fbo->type_y != root_fbo->type_y)
1320          {
1321 #ifdef DEBUG
1322             RARCH_WARN("[Shaders]: Pass %u type_y", i);
1323 #endif
1324             continue_saving_reference = false;
1325          }
1326 
1327          if (continue_saving_reference && fbo->scale_x != root_fbo->scale_x)
1328          {
1329 #ifdef DEBUG
1330             RARCH_WARN("[Shaders]: Pass %u scale_x", i);
1331 #endif
1332             continue_saving_reference = false;
1333          }
1334 
1335          if (continue_saving_reference && fbo->scale_y != root_fbo->scale_y)
1336          {
1337 #ifdef DEBUG
1338             RARCH_WARN("[Shaders]: Pass %u scale_y", i);
1339 #endif
1340             continue_saving_reference = false;
1341          }
1342 
1343          if (continue_saving_reference && fbo->fp_fbo != root_fbo->fp_fbo)
1344          {
1345 #ifdef DEBUG
1346             RARCH_WARN("[Shaders]: Pass %u fp_fbo", i);
1347 #endif
1348             continue_saving_reference = false;
1349          }
1350 
1351          if (continue_saving_reference && fbo->srgb_fbo != root_fbo->srgb_fbo)
1352          {
1353 #ifdef DEBUG
1354             RARCH_WARN("[Shaders]: Pass %u srgb_fbo", i);
1355 #endif
1356             continue_saving_reference = false;
1357          }
1358 
1359          if (continue_saving_reference && fbo->valid != root_fbo->valid)
1360          {
1361 #ifdef DEBUG
1362             RARCH_WARN("[Shaders]: Pass %u valid", i);
1363 #endif
1364             continue_saving_reference = false;
1365          }
1366 
1367          if (continue_saving_reference && fbo->abs_x != root_fbo->abs_x)
1368          {
1369 #ifdef DEBUG
1370             RARCH_WARN("[Shaders]: Pass %u abs_x", i);
1371 #endif
1372             continue_saving_reference = false;
1373          }
1374 
1375          if (continue_saving_reference && fbo->abs_y != root_fbo->abs_y)
1376          {
1377 #ifdef DEBUG
1378             RARCH_WARN("[Shaders]: Pass %u abs_y", i);
1379 #endif
1380             continue_saving_reference = false;
1381          }
1382 
1383          if (!continue_saving_reference)
1384          {
1385 #ifdef DEBUG
1386             RARCH_WARN(" Current Value doesn't match Referenced Value -"
1387                   " Full Preset Will be Saved instead of Simple Preset\n");
1388 #endif
1389             goto end;
1390          }
1391       }
1392    }
1393 
1394    /* If the shader has parameters */
1395    if (shader->num_parameters)
1396    {
1397       for (i = 0; i < shader->num_parameters; i++)
1398       {
1399          /* If the parameter's current value is different
1400           * than the referenced shader then write the value
1401           * into the new preset */
1402          if (     shader->parameters[i].current
1403                != referenced_shader->parameters[i].current)
1404             config_set_float(conf, shader->parameters[i].id,
1405                   shader->parameters[i].current);
1406       }
1407    }
1408 
1409    /* If the shader has textures */
1410    if (shader->luts)
1411    {
1412       for (i = 0; i < shader->luts; i++)
1413       {
1414          /* If the current shader texture path is different
1415           * than the referenced shader texture then write the
1416           * current path into the new preset */
1417          if (!string_is_equal(referenced_shader->lut[i].path,
1418                   shader->lut[i].path))
1419          {
1420             char *path_for_save  = (char*)malloc(PATH_MAX_LENGTH);
1421 
1422             fill_pathname_abbreviated_or_relative(path_for_save,
1423                   conf->path, shader->lut[i].path, PATH_MAX_LENGTH);
1424             pathname_make_slashes_portable(path_for_save);
1425             config_set_string(conf, shader->lut[i].id, path_for_save);
1426 #ifdef DEBUG
1427             RARCH_DBG("[Shaders]:  Texture override %s = %s.\n",
1428                   shader->lut[i].id, path_for_save);
1429 #endif
1430 
1431             free(path_for_save);
1432          }
1433       }
1434    }
1435 
1436    /* Write the file, return will be true if successful */
1437    RARCH_DBG("[Shaders]:  Saving simple preset to: %s\n",
1438          path_to_save_conformed);
1439    ret = config_file_write(conf, path_to_save_conformed, false);
1440 
1441 end:
1442 
1443    config_file_free(conf);
1444    config_file_free(reference_conf);
1445    free(referenced_shader);
1446    free(abs_temp_reference_path);
1447    free(relative_temp_reference_path);
1448    free(new_preset_basedir);
1449    free(config_dir);
1450    free(path_to_reference);
1451    free(path_to_save_conformed);
1452 
1453    return ret;
1454 }
1455 
1456 /**
1457  * video_shader_load_root_config_into_shader:
1458  * @conf              : Preset file to read from.
1459  * @shader            : Shader handle.
1460  *
1461  * Loads preset file and all associated state (passes, textures, imports, etc).
1462  *
1463  * Returns: true (1) if successful, otherwise false (0).
1464  **/
video_shader_load_root_config_into_shader(config_file_t * conf,settings_t * settings,struct video_shader * shader)1465 static bool video_shader_load_root_config_into_shader(
1466       config_file_t *conf,
1467       settings_t *settings,
1468       struct video_shader *shader)
1469 {
1470    unsigned i;
1471    unsigned num_passes              = 0;
1472    bool watch_files                 = settings->bools.video_shader_watch_files;
1473 
1474    /* This sets the shader to empty */
1475    memset(shader, 0, sizeof(*shader));
1476 
1477    if (!config_get_uint(conf, "shaders", &num_passes))
1478       return false;
1479    if (!num_passes)
1480       return false;
1481 
1482    if (!config_get_int(conf, "feedback_pass", &shader->feedback_pass))
1483       shader->feedback_pass = -1;
1484 
1485    shader->passes = MIN(num_passes, GFX_MAX_SHADERS);
1486 
1487    /* Set the path of the root preset for this shader */
1488    strlcpy(shader->path, conf->path, sizeof(shader->path));
1489 
1490    /* Set the path of the original preset which was loaded, for
1491     * a full preset config this is the same as the root config
1492     * For simple presets (using #reference) this different than
1493     * the root preset and it is the path to the
1494     * simple preset originally loaded, but that is set inside
1495     * video_shader_load_preset_into_shader*/
1496    strlcpy( shader->loaded_preset_path,
1497             conf->path,
1498             sizeof(shader->loaded_preset_path));
1499 
1500    if (watch_files)
1501    {
1502       union string_list_elem_attr attr;
1503       int flags                        =
1504          PATH_CHANGE_TYPE_MODIFIED                   |
1505          PATH_CHANGE_TYPE_WRITE_FILE_CLOSED          |
1506          PATH_CHANGE_TYPE_FILE_MOVED                 |
1507          PATH_CHANGE_TYPE_FILE_DELETED;
1508       struct string_list file_list     = {0};
1509 
1510       attr.i         = 0;
1511 
1512       if (file_change_data)
1513          frontend_driver_watch_path_for_changes(NULL, 0, &file_change_data);
1514 
1515       file_change_data = NULL;
1516       string_list_initialize(&file_list);
1517       string_list_append(&file_list, conf->path, attr);
1518 
1519       /* TODO We aren't currently watching the originally loaded preset
1520        * We should probably watch it for changes too */
1521 
1522       for (i = 0; i < shader->passes; i++)
1523       {
1524          if (!video_shader_parse_pass(conf, &shader->pass[i], i))
1525          {
1526             string_list_deinitialize(&file_list);
1527             return false;
1528          }
1529 
1530          string_list_append(&file_list, shader->pass[i].source.path, attr);
1531       }
1532 
1533       frontend_driver_watch_path_for_changes(&file_list, flags,
1534             &file_change_data);
1535       string_list_deinitialize(&file_list);
1536    }
1537    else
1538    {
1539       for (i = 0; i < shader->passes; i++)
1540       {
1541          if (!video_shader_parse_pass(conf, &shader->pass[i], i))
1542             return false;
1543       }
1544    }
1545 
1546    if (!video_shader_parse_textures(conf, shader))
1547       return false;
1548 
1549    /* Load the parameter values */
1550    video_shader_resolve_parameters(shader);
1551 
1552    /* Load the parameter values */
1553    video_shader_load_current_parameter_values(conf, shader);
1554 
1555 #ifdef DEBUG
1556    RARCH_DBG("[Shaders]:      Number of Passes:  %u\n", shader->passes);
1557    RARCH_DBG("[Shaders]:      Number of Textures:  %u\n", shader->luts);
1558 
1559    /* Log Texture Names & Paths */
1560    for (i = 0; i < shader->luts; i++)
1561       RARCH_DBG("[Shaders]:        %s = %s.\n", shader->lut[i].id,
1562             shader->lut[i].path);
1563 #endif
1564 
1565    return true;
1566 }
1567 
1568 /**
1569  * override_shader_values:
1570  * @override_conf     : Config file who's values will be copied on top of conf
1571  * @shader            : Shader to be affected
1572  *
1573  * Takes values from override_config and overrides values of the shader
1574  *
1575  * Returns 0 if nothing is overridden
1576  * Returns 1 if something is overridden
1577  **/
override_shader_values(config_file_t * override_conf,struct video_shader * shader)1578 static bool override_shader_values(config_file_t *override_conf,
1579       struct video_shader *shader)
1580 {
1581    unsigned i;
1582    bool return_val                     = false;
1583    struct config_entry_list *entry     = NULL;
1584 
1585    if (!shader || !override_conf)
1586       return 0;
1587 
1588    /* If the shader has parameters */
1589    if (shader->num_parameters)
1590    {
1591       /* Step through the parameters in the shader and
1592        * see if there is an entry for each in the override config */
1593       for (i = 0; i < shader->num_parameters; i++)
1594       {
1595          entry = config_get_entry(override_conf, shader->parameters[i].id);
1596 
1597          /* If the parameter is in the reference config */
1598          if (entry)
1599          {
1600             struct video_shader_parameter *parameter =
1601                (struct video_shader_parameter*)
1602                video_shader_parse_find_parameter(
1603                      shader->parameters,
1604                      shader->num_parameters,
1605                      shader->parameters[i].id);
1606 
1607             /* Set the shader's parameter value */
1608             config_get_float(override_conf, shader->parameters[i].id,
1609                   &parameter->current);
1610 
1611 #ifdef DEBUG
1612             RARCH_DBG("[Shaders]:      Parameter:  %s = %f.\n",
1613                   shader->parameters[i].id,
1614                   shader->parameters[i].current);
1615 #endif
1616 
1617             return_val = true;
1618          }
1619       }
1620    }
1621 
1622    /* ---------------------------------------------------------------------------------
1623     * ------------- Resolve Override texture paths to absolute paths-------------------
1624     * --------------------------------------------------------------------------------- */
1625 
1626    /* If the shader has textures */
1627    if (shader->luts)
1628    {
1629       char *override_tex_path             = (char*)malloc(PATH_MAX_LENGTH);
1630 
1631       override_tex_path[0]                = '\0';
1632 
1633       /* Step through the textures in the shader and see if there is an entry
1634        * for each in the override config */
1635       for (i = 0; i < shader->luts; i++)
1636       {
1637          entry = config_get_entry(override_conf, shader->lut[i].id);
1638 
1639          /* If the texture is defined in the reference config */
1640          if (entry)
1641          {
1642             /* Texture path from shader the config */
1643             config_get_path(override_conf, shader->lut[i].id,
1644                   override_tex_path, PATH_MAX_LENGTH);
1645 
1646             /* Get the absolute path */
1647             fill_pathname_expanded_and_absolute(shader->lut[i].path,
1648                   override_conf->path, override_tex_path);
1649 
1650 #ifdef DEBUG
1651             RARCH_DBG("[Shaders]:      Texture:    %s = %s.\n",
1652                         shader->lut[i].id,
1653                         shader->lut[i].path);
1654 #endif
1655 
1656             return_val = true;
1657          }
1658       }
1659 
1660       free(override_tex_path);
1661    }
1662 
1663    return return_val;
1664 }
1665 
1666 /**
1667  * video_shader_write_preset:
1668  * @path              : File to write to
1669  * @shader            : Shader to write
1670  * @reference         : Whether a simple preset should be written
1671  * with the #reference to another preset in it
1672  *
1673  * Writes a preset to disk. Can be written as a simple preset
1674  * (With the #reference directive in it) or a full preset.
1675  **/
video_shader_write_preset(const char * path,const char * shader_dir,const struct video_shader * shader,bool reference)1676 bool video_shader_write_preset(const char *path,
1677       const char *shader_dir,
1678       const struct video_shader *shader,
1679       bool reference)
1680 {
1681    /* We need to clean up paths to be able to properly process them
1682     * path and shader->loaded_preset_path can use '/' on
1683     * Windows due to Qt being Qt */
1684    char preset_dir[PATH_MAX_LENGTH];
1685 
1686    if (!shader || string_is_empty(path))
1687       return false;
1688 
1689    fill_pathname_join(preset_dir, shader_dir, "presets", sizeof(preset_dir));
1690 
1691    /* If we should still save a referenced preset do it now */
1692    if (reference)
1693    {
1694       if (video_shader_write_referenced_preset(path, shader_dir, shader))
1695          return true;
1696 
1697       RARCH_WARN("[Shaders]:  Failed writing Simple Preset to %s - "
1698             "Full Preset Will be Saved instead.\n", path);
1699    }
1700 
1701    /* If we aren't saving a referenced preset or weren't able to save one
1702     * then save a full preset */
1703    if (path)
1704       return video_shader_write_root_preset(shader, path);
1705    return false;
1706 }
1707 
1708 
1709 /**
1710  * video_shader_load_preset_into_shader:
1711  * @path              : Path to preset file, could be a
1712  * Simple Preset (including a #reference) or Full Preset
1713  * @shader            : Shader
1714  *
1715  * Loads preset file to a shader including passes, textures
1716  * and parameters
1717  *
1718  * Returns: true (1) if successful, otherwise false (0).
1719  **/
video_shader_load_preset_into_shader(const char * path,struct video_shader * shader)1720 bool video_shader_load_preset_into_shader(const char *path,
1721       struct video_shader *shader)
1722 {
1723    unsigned i                                        = 0;
1724    bool ret                                          = true;
1725    char override_conf_paths[SHADER_MAX_REFERENCE_DEPTH][PATH_MAX_LENGTH];
1726    config_file_t *conf                               = NULL;
1727    /* Get the root config, If we were able to get a root_config
1728     * that means the reference chain is valid */
1729    config_file_t *root_conf                          =
1730       video_shader_get_root_preset_config(path);
1731 
1732    if (!root_conf)
1733    {
1734 #ifdef DEBUG
1735       RARCH_LOG("\n");
1736       RARCH_WARN("[Shaders]:  Could not read root preset: %s \n", path);
1737 #endif
1738       ret = false;
1739       goto end;
1740    }
1741 
1742    /* If we were able to get a root_config that means that the
1743     * whole reference chain is valid */
1744 #ifdef DEBUG
1745    RARCH_DBG("\n");
1746 #endif
1747 
1748    video_shader_load_root_config_into_shader(root_conf,
1749          config_get_ptr(), shader);
1750    /* If the root_conf path matches the original path then
1751     * there are no references  so we just load it and go to the end */
1752    if (string_is_equal(root_conf->path, path))
1753       goto end;
1754 
1755    /* Get the config from the initial preset file
1756     * We don't need to check it's validity because it must
1757     * have been valid to get the root preset */
1758    conf = config_file_new_from_path_to_string(path);
1759 
1760    /* Set all override_conf_paths to empty so we know which
1761     * ones have been filled */
1762    for (i = 0; i < SHADER_MAX_REFERENCE_DEPTH; i++)
1763       override_conf_paths[i][0] = '\0';
1764 
1765    i = 0;
1766 
1767 #ifdef DEBUG
1768    RARCH_DBG("\n");
1769    RARCH_DBG("[Shaders]:  Crawl Preset Reference Chain\n");
1770 #endif
1771 
1772    /* If the config has a reference then we need gather all presets from the
1773     * chain of references to apply their values later */
1774    while (conf->reference)
1775    {
1776       char* reference_preset_path = (char*)malloc(PATH_MAX_LENGTH);
1777       i++;
1778 
1779 #ifdef DEBUG
1780       RARCH_DBG("[Shaders]:    Preset (Depth %u):  %s \n", i, conf->path);
1781 #endif
1782 
1783       /* Add the reference to the list */
1784       strlcpy(override_conf_paths[i], conf->path, PATH_MAX_LENGTH);
1785 
1786       /* Get the absolute path for the reference */
1787       fill_pathname_expanded_and_absolute(reference_preset_path,
1788             conf->path, conf->reference);
1789 
1790 #ifdef DEBUG
1791       RARCH_DBG("[Shaders]:      #reference     = %s \n",
1792             reference_preset_path);
1793 #endif
1794 
1795       /* Create a new config from this reference level */
1796       config_file_free(conf);
1797       conf = config_file_new_from_path_to_string(reference_preset_path);
1798 
1799       free(reference_preset_path);
1800    }
1801 
1802    /* Step back through the references starting with the one
1803     * referencing the root config and apply overrides for each one */
1804 #ifdef DEBUG
1805    RARCH_DBG("\n");
1806    RARCH_DBG("[Shaders]:  Start Applying Simple Preset Overrides\n");
1807 #endif
1808 
1809    while (i)
1810    {
1811       config_file_t *override_conf = config_file_new_from_path_to_string(
1812             override_conf_paths[i]);
1813 
1814 #ifdef DEBUG
1815       RARCH_DBG("[Shaders]:    Depth %u Apply Overrides\n", i);
1816       RARCH_DBG("[Shaders]:      Apply values from:   %s\n",
1817             override_conf->path);
1818 #endif
1819       override_shader_values(override_conf, shader);
1820 
1821       config_file_free(override_conf);
1822       i--;
1823    }
1824 
1825 #ifdef DEBUG
1826    RARCH_DBG("[Shaders]:  End Apply Overrides\n");
1827    RARCH_DBG("\n");
1828 #endif
1829 
1830    /* Set Path for originally loaded preset because it is
1831     * different than the root preset path */
1832    strlcpy( shader->loaded_preset_path, path,
1833          sizeof(shader->loaded_preset_path));
1834 end:
1835 
1836    config_file_free(conf);
1837    config_file_free(root_conf);
1838 
1839    return ret;
1840 }
1841 
video_shader_type_to_str(enum rarch_shader_type type)1842 const char *video_shader_type_to_str(enum rarch_shader_type type)
1843 {
1844    switch (type)
1845    {
1846       case RARCH_SHADER_CG:
1847          return "Cg";
1848       case RARCH_SHADER_HLSL:
1849          return "HLSL";
1850       case RARCH_SHADER_GLSL:
1851          return "GLSL";
1852       case RARCH_SHADER_SLANG:
1853          return "Slang";
1854       case RARCH_SHADER_METAL:
1855          return "Metal";
1856       case RARCH_SHADER_NONE:
1857          return "none";
1858       default:
1859          break;
1860    }
1861 
1862    return "???";
1863 }
1864 
1865 /**
1866  * video_shader_is_supported:
1867  * Tests if a shader type is supported.
1868  * This is only accurate once the context driver was initialized.
1869  **/
video_shader_is_supported(enum rarch_shader_type type)1870 bool video_shader_is_supported(enum rarch_shader_type type)
1871 {
1872    gfx_ctx_flags_t flags;
1873    enum display_flags testflag = GFX_CTX_FLAGS_NONE;
1874 
1875    flags.flags     = 0;
1876 
1877    switch (type)
1878    {
1879       case RARCH_SHADER_SLANG:
1880          testflag = GFX_CTX_FLAGS_SHADERS_SLANG;
1881          break;
1882       case RARCH_SHADER_GLSL:
1883          testflag = GFX_CTX_FLAGS_SHADERS_GLSL;
1884          break;
1885       case RARCH_SHADER_CG:
1886          testflag = GFX_CTX_FLAGS_SHADERS_CG;
1887          break;
1888       case RARCH_SHADER_HLSL:
1889          testflag = GFX_CTX_FLAGS_SHADERS_HLSL;
1890          break;
1891       case RARCH_SHADER_NONE:
1892       default:
1893          return false;
1894    }
1895    video_context_driver_get_flags(&flags);
1896 
1897    return BIT32_GET(flags.flags, testflag);
1898 }
1899 
video_shader_get_preset_extension(enum rarch_shader_type type)1900 const char *video_shader_get_preset_extension(enum rarch_shader_type type)
1901 {
1902    switch (type)
1903    {
1904       case RARCH_SHADER_GLSL:
1905          return ".glslp";
1906       case RARCH_SHADER_SLANG:
1907          return ".slangp";
1908       case RARCH_SHADER_HLSL:
1909       case RARCH_SHADER_CG:
1910          return ".cgp";
1911       default:
1912          break;
1913    }
1914 
1915    return NULL;
1916 }
1917 
video_shader_any_supported(void)1918 bool video_shader_any_supported(void)
1919 {
1920    gfx_ctx_flags_t flags;
1921    flags.flags     = 0;
1922    video_context_driver_get_flags(&flags);
1923 
1924    return
1925       BIT32_GET(flags.flags, GFX_CTX_FLAGS_SHADERS_SLANG) ||
1926       BIT32_GET(flags.flags, GFX_CTX_FLAGS_SHADERS_GLSL)  ||
1927       BIT32_GET(flags.flags, GFX_CTX_FLAGS_SHADERS_CG)    ||
1928       BIT32_GET(flags.flags, GFX_CTX_FLAGS_SHADERS_HLSL);
1929 }
1930 
video_shader_get_type_from_ext(const char * ext,bool * is_preset)1931 enum rarch_shader_type video_shader_get_type_from_ext(
1932       const char *ext, bool *is_preset)
1933 {
1934    if (string_is_empty(ext))
1935       return RARCH_SHADER_NONE;
1936 
1937    if (strlen(ext) > 1 && ext[0] == '.')
1938       ext++;
1939 
1940    if (is_preset)
1941       *is_preset =
1942          string_is_equal_case_insensitive(ext, "cgp")   ||
1943          string_is_equal_case_insensitive(ext, "glslp") ||
1944          string_is_equal_case_insensitive(ext, "slangp");
1945 
1946    if (string_is_equal_case_insensitive(ext, "cgp") ||
1947        string_is_equal_case_insensitive(ext, "cg")
1948       )
1949       return RARCH_SHADER_CG;
1950 
1951    if (string_is_equal_case_insensitive(ext, "glslp") ||
1952        string_is_equal_case_insensitive(ext, "glsl")
1953       )
1954       return RARCH_SHADER_GLSL;
1955 
1956    if (string_is_equal_case_insensitive(ext, "slangp") ||
1957        string_is_equal_case_insensitive(ext, "slang")
1958       )
1959       return RARCH_SHADER_SLANG;
1960 
1961    return RARCH_SHADER_NONE;
1962 }
1963 
video_shader_check_for_changes(void)1964 bool video_shader_check_for_changes(void)
1965 {
1966    if (!file_change_data)
1967       return false;
1968 
1969    return frontend_driver_check_for_path_changes(file_change_data);
1970 }
1971