From 0fee6a9ef4914d228118b689fe755ef663ce2d4d Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 8 Aug 2019 04:55:22 +0200 Subject: [PATCH] Refactor code for glslang shader presets - buffer entire file into memory before passing it on --- gfx/drivers_shader/glslang_util.cpp | 109 +++++++++++++++++++++++- gfx/drivers_shader/glslang_util.h | 3 +- gfx/drivers_shader/slang_preprocess.cpp | 7 +- gfx/drivers_shader/slang_preprocess.h | 2 +- gfx/video_shader_parse.c | 30 +++---- 5 files changed, 127 insertions(+), 24 deletions(-) diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index 40c0281cdc..915b6cf80e 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -38,7 +38,7 @@ using namespace std; -bool glslang_read_shader_file(const char *path, vector *output, bool root_file) +static bool glslang_read_shader_file(const char *path, vector *output, bool root_file) { vector lines; char include_path[PATH_MAX_LENGTH]; @@ -283,6 +283,113 @@ static glslang_format glslang_find_format(const char *fmt) return SLANG_FORMAT_UNKNOWN; } +bool glslang_parse_meta(void *data, glslang_meta *meta) +{ + char id[64]; + char desc[64]; + size_t line_index = 0; + struct string_list *lines = (struct string_list*)data; + + id[0] = desc[0] = '\0'; + + *meta = glslang_meta{}; + + while (line_index < lines->size) + { + const char *line_c = lines->elems[line_index].data; + line_index++; + + if (!strncmp("#pragma name", line_c, STRLEN_CONST("#pragma name"))) + { + const char *str = NULL; + + if (!meta->name.empty()) + { + RARCH_ERR("[slang]: Trying to declare multiple names for file.\n"); + string_list_free(lines); + return false; + } + + str = line_c + STRLEN_CONST("#pragma name "); + + while (*str == ' ') + str++; + meta->name = str; + } + if (!strncmp("#pragma parameter", line_c, STRLEN_CONST("#pragma parameter"))) + { + float initial, minimum, maximum, step; + int ret = sscanf(line_c, "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f", + id, desc, &initial, &minimum, &maximum, &step); + + if (ret == 5) + { + step = 0.1f * (maximum - minimum); + ret = 6; + } + + if (ret == 6) + { + auto itr = find_if(begin(meta->parameters), end(meta->parameters), [&](const glslang_parameter ¶m) { + return param.id == id; + }); + + /* Allow duplicate #pragma parameter, but only + * if they are exactly the same. */ + if (itr != end(meta->parameters)) + { + if ( itr->desc != desc || + itr->initial != initial || + itr->minimum != minimum || + itr->maximum != maximum || + itr->step != step + ) + { + RARCH_ERR("[slang]: Duplicate parameters found for \"%s\", but arguments do not match.\n", id); + string_list_free(lines); + return false; + } + } + else + meta->parameters.push_back({ id, desc, initial, minimum, maximum, step }); + } + else + { + RARCH_ERR("[slang]: Invalid #pragma parameter line: \"%s\".\n", line_c); + string_list_free(lines); + return false; + } + } + else if (!strncmp("#pragma format", line_c, STRLEN_CONST("#pragma format"))) + { + const char *str = NULL; + + if (meta->rt_format != SLANG_FORMAT_UNKNOWN) + { + RARCH_ERR("[slang]: Trying to declare format multiple times for file.\n"); + string_list_free(lines); + return false; + } + + str = line_c + STRLEN_CONST("#pragma format "); + + while (*str == ' ') + str++; + + meta->rt_format = glslang_find_format(str); + if (meta->rt_format == SLANG_FORMAT_UNKNOWN) + { + RARCH_ERR("[slang]: Failed to find format \"%s\".\n", str); + string_list_free(lines); + return false; + } + } + } + + string_list_free(lines); + return true; +} + bool glslang_parse_meta(const vector &lines, glslang_meta *meta) { char id[64]; diff --git a/gfx/drivers_shader/glslang_util.h b/gfx/drivers_shader/glslang_util.h index 24f86f190e..3586685572 100644 --- a/gfx/drivers_shader/glslang_util.h +++ b/gfx/drivers_shader/glslang_util.h @@ -105,9 +105,8 @@ struct glslang_output bool glslang_compile_shader(const char *shader_path, glslang_output *output); -/* Helpers for internal use. */ -bool glslang_read_shader_file(const char *path, std::vector *output, bool root_file); bool glslang_parse_meta(const std::vector &lines, glslang_meta *meta); +bool glslang_parse_meta(void *data, glslang_meta *meta); #endif void *config_file_new_wrapper(const char *path); diff --git a/gfx/drivers_shader/slang_preprocess.cpp b/gfx/drivers_shader/slang_preprocess.cpp index a814359e06..c9127ad95e 100644 --- a/gfx/drivers_shader/slang_preprocess.cpp +++ b/gfx/drivers_shader/slang_preprocess.cpp @@ -89,14 +89,11 @@ bool slang_preprocess_parse_parameters(glslang_meta& meta, return true; } -bool slang_preprocess_parse_parameters(const char *shader_path, - struct video_shader *shader) +bool slang_preprocess_parse_parameters(void *data, struct video_shader *shader) { glslang_meta meta; - vector lines; + struct string_list *lines = (struct string_list*)data; - if (!glslang_read_shader_file(shader_path, &lines, true)) - return false; if (!glslang_parse_meta(lines, &meta)) return false; return slang_preprocess_parse_parameters(meta, shader); diff --git a/gfx/drivers_shader/slang_preprocess.h b/gfx/drivers_shader/slang_preprocess.h index b730582ffe..b542c24860 100644 --- a/gfx/drivers_shader/slang_preprocess.h +++ b/gfx/drivers_shader/slang_preprocess.h @@ -26,7 +26,7 @@ RETRO_BEGIN_DECLS /* Utility function to implement the same parameter reflection * which happens in the slang backend. * This does preprocess over the input file to handle #includes and so on. */ -bool slang_preprocess_parse_parameters(const char *shader_path, +bool slang_preprocess_parse_parameters(void *data, struct video_shader *shader); RETRO_END_DECLS diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 63ab2af7a2..c788f3d5d9 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -496,20 +496,6 @@ bool video_shader_resolve_parameters(config_file_t *conf, if (!path_is_valid(path)) continue; -#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) - /* First try to use the more robust slang - * implementation to support #includes. */ - /* FIXME: The check for slang can be removed - * if it's sufficiently tested for - * GLSL/Cg as well, it should be the same implementation. */ - if (string_is_equal(path_get_extension(path), "slang") && - slang_preprocess_parse_parameters(path, shader)) - continue; - - /* If that doesn't work, fallback to the old path. - * Ideally, we'd get rid of this path sooner or later. */ -#endif - /* Read file contents */ if (!filestream_read_file(path, (void**)&buf, &buf_len)) continue; @@ -525,7 +511,21 @@ bool video_shader_resolve_parameters(config_file_t *conf, if (!lines) continue; - /* even though the pass is set in the loop too, not all passes have parameters */ +#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) + /* First try to use the more robust slang + * implementation to support #includes. */ + /* FIXME: The check for slang can be removed + * if it's sufficiently tested for + * GLSL/Cg as well, it should be the same implementation. */ + if (string_is_equal(path_get_extension(path), "slang") && + slang_preprocess_parse_parameters(lines, shader)) + continue; + + /* If that doesn't work, fallback to the old path. + * Ideally, we'd get rid of this path sooner or later. */ +#endif + /* even though the pass is set in the loop too, not all + * passes have parameters */ param->pass = i; while ((shader->num_parameters < ARRAY_SIZE(shader->parameters)) &&