diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index a99167eeae..54f5f8f407 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -252,6 +253,9 @@ static glslang_format glslang_find_format(const char *fmt) static bool glslang_parse_meta(const vector &lines, glslang_meta *meta) { + char id[64] = {}; + char desc[64] = {}; + *meta = glslang_meta{}; for (auto &line : lines) { @@ -268,6 +272,46 @@ static bool glslang_parse_meta(const vector &lines, glslang_meta *meta) str++; meta->name = str; } + else if (line.find("#pragma parameter ") == 0) + { + float initial, minimum, maximum, step; + int ret = sscanf(line.c_str(), "#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); + 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_str()); + return false; + } + } else if (line.find("#pragma format ") == 0) { if (meta->rt_format != SLANG_FORMAT_UNKNOWN) diff --git a/gfx/drivers_shader/glslang_util.hpp b/gfx/drivers_shader/glslang_util.hpp index a28d7fbd19..0ababea8b1 100644 --- a/gfx/drivers_shader/glslang_util.hpp +++ b/gfx/drivers_shader/glslang_util.hpp @@ -63,8 +63,19 @@ enum glslang_format SLANG_FORMAT_UNKNOWN }; +struct glslang_parameter +{ + std::string id; + std::string desc; + float initial; + float minimum; + float maximum; + float step; +}; + struct glslang_meta { + std::vector parameters; std::string name; glslang_format rt_format = SLANG_FORMAT_UNKNOWN; }; diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index 10d67acf3a..a5073e2269 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -323,7 +324,7 @@ struct CommonResources unordered_map texture_semantic_map; unordered_map texture_semantic_uniform_map; - unordered_map semantic_map; + unique_ptr shader_preset; VkDevice device; }; @@ -432,6 +433,8 @@ class Pass pass_number = pass; } + void add_parameter(unsigned parameter_index, const std::string &id); + void end_frame(); void allocate_buffers(); @@ -488,6 +491,7 @@ class Pass void build_semantic_vec4(uint8_t *data, slang_semantic semantic, unsigned width, unsigned height); void build_semantic_uint(uint8_t *data, slang_semantic semantic, uint32_t value); + void build_semantic_parameter(uint8_t *data, unsigned index, float value); void build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic, unsigned width, unsigned height); @@ -505,6 +509,16 @@ class Pass size_t ubo_offset = 0; string pass_name; + + struct Parameter + { + string id; + unsigned index; + unsigned semantic_index; + }; + + vector parameters; + vector filtered_parameters; }; // struct here since we're implementing the opaque typedef from C. @@ -516,12 +530,12 @@ struct vulkan_filter_chain inline void set_shader_preset(unique_ptr shader) { - shader_preset = move(shader); + common.shader_preset = move(shader); } inline video_shader *get_shader_preset() { - return shader_preset.get(); + return common.shader_preset.get(); } void set_pass_info(unsigned pass, @@ -544,6 +558,7 @@ struct vulkan_filter_chain void set_pass_name(unsigned pass, const char *name); void add_static_texture(unique_ptr texture); + void add_parameter(unsigned pass, unsigned parameter_index, const std::string &id); void release_staging_buffers(); private: @@ -563,8 +578,6 @@ struct vulkan_filter_chain vulkan_filter_chain_swapchain_info swapchain_info; unsigned current_sync_index; - unique_ptr shader_preset; - void flush(); void set_num_passes(unsigned passes); @@ -610,6 +623,11 @@ void vulkan_filter_chain::set_swapchain_info( set_num_sync_indices(info.num_indices); } +void vulkan_filter_chain::add_parameter(unsigned pass, unsigned index, const std::string &id) +{ + passes[pass]->add_parameter(index, id); +} + void vulkan_filter_chain::set_num_sync_indices(unsigned num_indices) { execute_deferred(); @@ -852,7 +870,6 @@ bool vulkan_filter_chain::init_alias() { common.texture_semantic_map.clear(); common.texture_semantic_uniform_map.clear(); - common.semantic_map.clear(); unsigned i = 0; for (auto &pass : passes) @@ -1213,6 +1230,11 @@ Pass::~Pass() clear_vk(); } +void Pass::add_parameter(unsigned index, const std::string &id) +{ + parameters.push_back({ id, index, unsigned(parameters.size()) }); +} + void Pass::set_shader(VkShaderStageFlags stage, const uint32_t *spirv, size_t spirv_words) @@ -1693,15 +1715,33 @@ bool Pass::build() pass_info.rt_format, pass_info.max_levels)); } + unordered_map semantic_map; + unsigned j = 0; + for (auto ¶m : parameters) + { + if (!set_unique_map(semantic_map, param.id, + slang_semantic_map{ SLANG_SEMANTIC_FLOAT_PARAMETER, j })) + return false; + j++; + } + reflection = slang_reflection{}; reflection.pass_number = pass_number; reflection.texture_semantic_map = &common->texture_semantic_map; reflection.texture_semantic_uniform_map = &common->texture_semantic_uniform_map; - reflection.semantic_map = &common->semantic_map; + reflection.semantic_map = &semantic_map; if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection)) return false; + // Filter out parameters which we will never use anyways. + filtered_parameters.clear(); + for (unsigned i = 0; i < reflection.semantic_float_parameters.size(); i++) + { + if (reflection.semantic_float_parameters[i].uniform) + filtered_parameters.push_back(parameters[i]); + } + if (!init_pipeline()) return false; @@ -1801,6 +1841,13 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, } } +void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value) +{ + // We will have filtered out stale parameters. + if (data) + *reinterpret_cast(data + reflection.semantic_float_parameters[index].ubo_offset) = value; +} + void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic, uint32_t value) { @@ -1853,6 +1900,13 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer, // ORIGINAL_HISTORY[0] is an alias of ORIGINAL. build_semantic_texture_array(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original); + // Parameters. + for (auto ¶m : filtered_parameters) + { + float value = common->shader_preset->parameters[param.index].current; + build_semantic_parameter(buffer, param.semantic_index, value); + } + // Previous inputs. unsigned i = 0; for (auto &texture : common->original_history) @@ -2736,7 +2790,6 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( return nullptr; video_shader_resolve_relative(shader.get(), path); - video_shader_resolve_parameters(conf.get(), shader.get()); bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid; auto tmpinfo = *info; @@ -2749,6 +2802,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get())) return nullptr; + shader->num_parameters = 0; + for (unsigned i = 0; i < shader->passes; i++) { const video_shader_pass *pass = &shader->pass[i]; @@ -2765,6 +2820,49 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( return nullptr; } + for (auto &meta_param : output.meta.parameters) + { + if (shader->num_parameters >= GFX_MAX_PARAMETERS) + { + RARCH_ERR("[Vulkan]: Exceeded maximum number of parameters.\n"); + return nullptr; + } + + auto itr = find_if(shader->parameters, shader->parameters + shader->num_parameters, + [&](const video_shader_parameter ¶m) { + return meta_param.id == param.id; + }); + + if (itr != shader->parameters + shader->num_parameters) + { + // Allow duplicate #pragma parameter, but only if they are exactly the same. + if (meta_param.desc != itr->desc || + meta_param.initial != itr->initial || + meta_param.minimum != itr->minimum || + meta_param.maximum != itr->maximum || + meta_param.step != itr->step) + { + RARCH_ERR("[Vulkan]: Duplicate parameters found for \"%s\", but arguments do not match.\n", + itr->id); + return nullptr; + } + chain->add_parameter(i, itr - shader->parameters, meta_param.id); + } + else + { + auto ¶m = shader->parameters[shader->num_parameters]; + strlcpy(param.id, meta_param.id.c_str(), sizeof(param.id)); + strlcpy(param.desc, meta_param.desc.c_str(), sizeof(param.desc)); + param.current = meta_param.initial; + param.initial = meta_param.initial; + param.minimum = meta_param.minimum; + param.maximum = meta_param.maximum; + param.step = meta_param.step; + chain->add_parameter(i, shader->num_parameters, meta_param.id); + shader->num_parameters++; + } + } + chain->set_shader(i, VK_SHADER_STAGE_VERTEX_BIT, output.vertex.data(), @@ -2917,6 +3015,9 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( sizeof(opaque_frag) / sizeof(uint32_t)); } + if (!video_shader_resolve_current_parameters(conf.get(), shader.get())) + return nullptr; + chain->set_shader_preset(move(shader)); if (!chain->init()) diff --git a/gfx/drivers_shader/slang_reflection.cpp b/gfx/drivers_shader/slang_reflection.cpp index bbe7060909..45c02581a1 100644 --- a/gfx/drivers_shader/slang_reflection.cpp +++ b/gfx/drivers_shader/slang_reflection.cpp @@ -134,13 +134,18 @@ static slang_texture_semantic slang_uniform_name_to_texture_semantic( } static slang_semantic slang_uniform_name_to_semantic( - const unordered_map &semantic_map, - const string &name) + const unordered_map &semantic_map, + const string &name, unsigned *index) { auto itr = semantic_map.find(name); if (itr != end(semantic_map)) - return itr->second; + { + *index = itr->second.index; + return itr->second.semantic; + } + // No builtin semantics are arrayed. + *index = 0; unsigned i = 0; for (auto n : semantic_uniform_names) { @@ -183,6 +188,29 @@ static bool set_ubo_texture_offset(slang_reflection *reflection, return true; } +static bool set_ubo_float_parameter_offset(slang_reflection *reflection, + unsigned index, size_t offset, unsigned num_components) +{ + resize_minimum(reflection->semantic_float_parameters, index + 1); + auto &sem = reflection->semantic_float_parameters[index]; + + if (sem.uniform) + { + if (sem.ubo_offset != offset) + { + RARCH_ERR("[slang]: Vertex and fragment have different offsets for same parameter #%u (%u vs. %u).\n", + index, + unsigned(sem.ubo_offset), + unsigned(offset)); + return false; + } + } + sem.uniform = true; + sem.ubo_offset = offset; + sem.num_components = num_components; + return true; +} + static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic, size_t offset, unsigned num_components) { @@ -230,6 +258,10 @@ static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem) // uint return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1; + case SLANG_SEMANTIC_FLOAT_PARAMETER: + // float + return type.basetype == SPIRType::Float && type.vecsize == 1 && type.columns == 1; + default: // vec4 return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1; @@ -253,8 +285,9 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r auto &name = compiler.get_member_name(resource.base_type_id, range.index); auto &type = compiler.get_type(compiler.get_type(resource.base_type_id).member_types[range.index]); + unsigned sem_index = 0; unsigned tex_sem_index = 0; - auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name); + auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name, &sem_index); auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map, name, &tex_sem_index); @@ -273,8 +306,18 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r return false; } - if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize)) - return false; + switch (sem) + { + case SLANG_SEMANTIC_FLOAT_PARAMETER: + if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize)) + return false; + break; + + default: + if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize)) + return false; + break; + } } else if (tex_sem != SLANG_INVALID_TEXTURE_SEMANTIC) { @@ -510,6 +553,16 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm } } + RARCH_LOG("[slang]:\n"); + RARCH_LOG("[slang]: Parameters:\n"); + unsigned i = 0; + for (auto ¶m : reflection->semantic_float_parameters) + { + if (param.uniform) + RARCH_LOG("[slang]: #%u (Offset: %u)\n", i, param.ubo_offset); + i++; + } + return true; } diff --git a/gfx/drivers_shader/slang_reflection.hpp b/gfx/drivers_shader/slang_reflection.hpp index cfb7cb0801..8fd2df5e58 100644 --- a/gfx/drivers_shader/slang_reflection.hpp +++ b/gfx/drivers_shader/slang_reflection.hpp @@ -59,10 +59,16 @@ enum slang_texture_semantic enum slang_semantic { + // mat4, MVP SLANG_SEMANTIC_MVP = 0, + // vec4, viewport size of current pass SLANG_SEMANTIC_OUTPUT = 1, + // vec4, viewport size of final pass SLANG_SEMANTIC_FINAL_VIEWPORT = 2, + // uint, frame count with modulo SLANG_SEMANTIC_FRAME_COUNT = 3, + // float, user defined parameter, arrayed + SLANG_SEMANTIC_FLOAT_PARAMETER = 4, SLANG_NUM_SEMANTICS, SLANG_INVALID_SEMANTIC = -1 @@ -100,6 +106,12 @@ struct slang_texture_semantic_map unsigned index; }; +struct slang_semantic_map +{ + slang_semantic semantic; + unsigned index; +}; + struct slang_reflection { slang_reflection(); @@ -110,10 +122,11 @@ struct slang_reflection std::vector semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS]; slang_semantic_meta semantics[SLANG_NUM_SEMANTICS]; + std::vector semantic_float_parameters; const std::unordered_map *texture_semantic_map = nullptr; const std::unordered_map *texture_semantic_uniform_map = nullptr; - const std::unordered_map *semantic_map = nullptr; + const std::unordered_map *semantic_map = nullptr; unsigned pass_number = 0; }; diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 398583694d..980d789d42 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -391,6 +391,48 @@ static struct video_shader_parameter *video_shader_parse_find_parameter( return NULL; } +/** + * video_shader_set_current_parameters: + * @conf : Preset file to read from. + * @shader : Shader passes handle. + * + * Reads the current value for all parameters from config file. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool video_shader_resolve_current_parameters(config_file_t *conf, + struct video_shader *shader) +{ + if (!conf) + return false; + + /* Read in parameters which override the defaults. */ + char parameters[4096] = {0}; + const char *id = NULL; + char *save = NULL; + + if (!config_get_array(conf, "parameters", + parameters, sizeof(parameters))) + return true; + + for (id = strtok_r(parameters, ";", &save); id; + id = strtok_r(NULL, ";", &save)) + { + struct video_shader_parameter *parameter = (struct video_shader_parameter*) + video_shader_parse_find_parameter(shader->parameters, shader->num_parameters, id); + + if (!parameter) + { + RARCH_WARN("[CGP/GLSLP]: Parameter %s is set in the preset, but no shader uses this parameter, ignoring.\n", id); + continue; + } + + if (!config_get_float(conf, id, ¶meter->current)) + RARCH_WARN("[CGP/GLSLP]: Parameter %s is not set in preset.\n", id); + } + return true; +} + /** * video_shader_resolve_parameters: * @conf : Preset file to read from. @@ -447,33 +489,8 @@ bool video_shader_resolve_parameters(config_file_t *conf, filestream_close(file); } - if (conf) - { - /* Read in parameters which override the defaults. */ - char parameters[4096] = {0}; - const char *id = NULL; - char *save = NULL; - - if (!config_get_array(conf, "parameters", - parameters, sizeof(parameters))) - return true; - - for (id = strtok_r(parameters, ";", &save); id; - id = strtok_r(NULL, ";", &save)) - { - struct video_shader_parameter *parameter = (struct video_shader_parameter*) - video_shader_parse_find_parameter(shader->parameters, shader->num_parameters, id); - - if (!parameter) - { - RARCH_WARN("[CGP/GLSLP]: Parameter %s is set in the preset, but no shader uses this parameter, ignoring.\n", id); - continue; - } - - if (!config_get_float(conf, id, ¶meter->current)) - RARCH_WARN("[CGP/GLSLP]: Parameter %s is not set in preset.\n", id); - } - } + if (conf && !video_shader_resolve_current_parameters(conf, shader)) + return false; return true; } diff --git a/gfx/video_shader_parse.h b/gfx/video_shader_parse.h index 23b78d7731..116a1d415c 100644 --- a/gfx/video_shader_parse.h +++ b/gfx/video_shader_parse.h @@ -192,6 +192,18 @@ void video_shader_write_conf_cgp(config_file_t *conf, void video_shader_resolve_relative(struct video_shader *shader, const char *ref_path); +/** + * video_shader_resolve_parameters: + * @conf : Preset file to read from. + * @shader : Shader passes handle. + * + * Reads the current value for all parameters from config file. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool video_shader_resolve_current_parameters(config_file_t *conf, + struct video_shader *shader); + /** * video_shader_resolve_parameters: * @conf : Preset file to read from.