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 ¶ms[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, ¶m->initial,
550 ¶m->minimum, ¶m->maximum, ¶m->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, ¶meter->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 ¶meter->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