diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 3bf140e625..4f7afd9544 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -1481,6 +1481,7 @@ static bool vulkan_frame(void *data, const void *frame, /* Notify filter chain about the new sync index. */ vulkan_filter_chain_notify_sync_index(vk->filter_chain, frame_index); + vulkan_filter_chain_set_frame_count(vk->filter_chain, frame_count); retro_perf_start(&build_cmd); /* Render offscreen filter chain passes. */ diff --git a/gfx/drivers_shader/glslang_util.cpp b/gfx/drivers_shader/glslang_util.cpp index 191d075fd9..e112267216 100644 --- a/gfx/drivers_shader/glslang_util.cpp +++ b/gfx/drivers_shader/glslang_util.cpp @@ -101,8 +101,11 @@ static string build_stage_source(const vector &lines, const char *stage) { if (itr->find("#pragma stage ") != string::npos) { - auto expected = string("#pragma stage ") + stage; - active = itr->find(expected) != string::npos; + if (stage) + { + auto expected = string("#pragma stage ") + stage; + active = itr->find(expected) != string::npos; + } // Improve debuggability. if (active) @@ -112,6 +115,10 @@ static string build_stage_source(const vector &lines, const char *stage) str << '\n'; } } + else if (itr->find("#pragma name ") != string::npos) + { + // Ignore + } else if (active) str << *itr; str << '\n'; @@ -120,14 +127,39 @@ static string build_stage_source(const vector &lines, const char *stage) return str.str(); } +static bool glslang_parse_meta(const vector &lines, glslang_meta *meta) +{ + *meta = glslang_meta{}; + for (auto &line : lines) + { + if (line.find("#pragma name ") != string::npos) + { + if (!meta->name.empty()) + { + RARCH_ERR("[slang]: Trying to declare multiple names for file.\n"); + return false; + } + + const char *str = line.c_str() + strlen("#pragma name "); + while (*str == ' ') + str++; + meta->name = str; + } + } + return true; +} + bool glslang_compile_shader(const char *shader_path, glslang_output *output) { vector lines; - RARCH_LOG("Compiling shader \"%s\".\n", shader_path); + RARCH_LOG("[slang]: Compiling shader \"%s\".\n", shader_path); if (!read_shader_file(shader_path, &lines)) return false; + if (!glslang_parse_meta(lines, &output->meta)) + return false; + auto &header = lines.front(); if (header.find_first_of("#version ") == string::npos) { diff --git a/gfx/drivers_shader/glslang_util.hpp b/gfx/drivers_shader/glslang_util.hpp index 7602e6ff8e..5e8037cd2e 100644 --- a/gfx/drivers_shader/glslang_util.hpp +++ b/gfx/drivers_shader/glslang_util.hpp @@ -18,11 +18,18 @@ #include #include +#include + +struct glslang_meta +{ + std::string name; +}; struct glslang_output { std::vector vertex; std::vector fragment; + glslang_meta meta; }; bool glslang_compile_shader(const char *shader_path, glslang_output *output); diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index 3ce7a21523..7931ca28b9 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -219,6 +219,10 @@ struct CommonResources vector original_history; vector framebuffer_feedback; + unordered_map texture_semantic_map; + unordered_map texture_semantic_uniform_map; + unordered_map semantic_map; + VkDevice device; }; @@ -276,6 +280,26 @@ class Pass sync_index = index; } + void set_frame_count(uint64_t count) + { + frame_count = count; + } + + void set_frame_count_period(unsigned period) + { + frame_count_period = period; + } + + void set_name(const char *name) + { + pass_name = name; + } + + const string &get_name() const + { + return pass_name; + } + vulkan_filter_chain_filter get_source_filter() const { return pass_info.source_filter; @@ -291,6 +315,11 @@ class Pass return reflection; } + void set_pass_number(unsigned pass) + { + pass_number = pass; + } + void end_frame(); private: @@ -347,6 +376,7 @@ class Pass const float *mvp, const Texture &original, const Texture &source); 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_texture_vec4(uint8_t *data, slang_texture_semantic semantic, unsigned width, unsigned height); @@ -357,6 +387,12 @@ class Pass slang_texture_semantic semantic, const Texture &texture); void build_semantic_texture_array(VkDescriptorSet set, uint8_t *buffer, slang_texture_semantic semantic, unsigned index, const Texture &texture); + + uint64_t frame_count = 0; + unsigned frame_count_period = 0; + unsigned pass_number = 0; + + string pass_name; }; // struct here since we're implementing the opaque typedef from C. @@ -391,6 +427,10 @@ struct vulkan_filter_chain void build_viewport_pass(VkCommandBuffer cmd, const VkViewport &vp, const float *mvp); + void set_frame_count(uint64_t count); + void set_frame_count_period(unsigned pass, unsigned period); + void set_pass_name(unsigned pass, const char *name); + private: VkDevice device; const VkPhysicalDeviceMemoryProperties &memory_properties; @@ -418,6 +458,7 @@ struct vulkan_filter_chain bool init_history(); bool init_feedback(); + bool init_alias(); void update_history(DeferredDisposer &disposer, VkCommandBuffer cmd); vector> original_history; bool require_clear = false; @@ -479,6 +520,7 @@ void vulkan_filter_chain::set_num_passes(unsigned num_passes) passes.emplace_back(new Pass(device, memory_properties, cache, deferred_calls.size(), i + 1 == num_passes)); passes.back()->set_common_resources(common); + passes.back()->set_pass_number(i); } } @@ -511,6 +553,22 @@ void vulkan_filter_chain::set_input_texture( input_texture = texture; } +void vulkan_filter_chain::set_frame_count(uint64_t count) +{ + for (auto &pass : passes) + pass->set_frame_count(count); +} + +void vulkan_filter_chain::set_frame_count_period(unsigned pass, unsigned period) +{ + passes[pass]->set_frame_count_period(period); +} + +void vulkan_filter_chain::set_pass_name(unsigned pass, const char *name) +{ + passes[pass]->set_name(name); +} + void vulkan_filter_chain::execute_deferred() { for (auto &calls : deferred_calls) @@ -642,13 +700,66 @@ bool vulkan_filter_chain::init_feedback() return true; } +template +static bool set_unique_map(M &m, const string &name, const P &p) +{ + auto itr = m.find(name); + if (itr != end(m)) + { + RARCH_ERR("[slang]: Alias \"%s\" already exists.\n", + name.c_str()); + return false; + } + + m[name] = p; + return true; +} + +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) + { + auto &name = pass->get_name(); + if (name.empty()) + continue; + + if (!set_unique_map(common.texture_semantic_map, name, + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) + return false; + + if (!set_unique_map(common.texture_semantic_uniform_map, name + "Size", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i })) + return false; + + if (!set_unique_map(common.texture_semantic_map, name + "Feedback", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) + return false; + + if (!set_unique_map(common.texture_semantic_uniform_map, name + "FeedbackSize", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i })) + return false; + + i++; + } + return true; +} + bool vulkan_filter_chain::init() { Size2D source = max_input_size; + if (!init_alias()) + return false; for (unsigned i = 0; i < passes.size(); i++) { auto &pass = passes[i]; + RARCH_LOG("[slang]: Building pass #%u (%s)\n", i, pass->get_name().empty() ? "N/A" : pass->get_name().c_str()); + source = pass->set_pass_info(max_input_size, source, swapchain_info, pass_info[i]); if (!pass->build()) @@ -1273,6 +1384,11 @@ bool Pass::build() } 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; + if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection)) return false; @@ -1378,6 +1494,13 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, } } +void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic, + uint32_t value) +{ + if (data && reflection.semantics[semantic].uniform) + *reinterpret_cast(data + reflection.semantics[semantic].ubo_offset) = value; +} + void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer, slang_texture_semantic semantic, const Texture &texture) { @@ -1412,6 +1535,8 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer, current_framebuffer_size.width, current_framebuffer_size.height); build_semantic_vec4(buffer, SLANG_SEMANTIC_FINAL_VIEWPORT, unsigned(current_viewport.width), unsigned(current_viewport.height)); + build_semantic_uint(buffer, SLANG_SEMANTIC_FRAME_COUNT, + frame_count_period ? uint32_t(frame_count % frame_count_period) : uint32_t(frame_count)); // Standard inputs build_semantic_texture(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original); @@ -1908,6 +2033,15 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( output.fragment.data(), output.fragment.size()); + chain->set_frame_count_period(i, pass->frame_count_mod); + + if (!output.meta.name.empty()) + chain->set_pass_name(i, output.meta.name.c_str()); + + // Preset overrides. + if (*pass->alias) + chain->set_pass_name(i, pass->alias); + if (pass->filter == RARCH_FILTER_UNSPEC) pass_info.source_filter = filter; else @@ -2067,6 +2201,29 @@ void vulkan_filter_chain_set_input_texture( chain->set_input_texture(*texture); } +void vulkan_filter_chain_set_frame_count( + vulkan_filter_chain_t *chain, + uint64_t count) +{ + chain->set_frame_count(count); +} + +void vulkan_filter_chain_set_frame_count_period( + vulkan_filter_chain_t *chain, + unsigned pass, + unsigned period) +{ + chain->set_frame_count_period(pass, period); +} + +void vulkan_filter_chain_set_pass_name( + vulkan_filter_chain_t *chain, + unsigned pass, + const char *name) +{ + chain->set_pass_name(pass, name); +} + void vulkan_filter_chain_build_offscreen_passes( vulkan_filter_chain_t *chain, VkCommandBuffer cmd, const VkViewport *vp) diff --git a/gfx/drivers_shader/shader_vulkan.h b/gfx/drivers_shader/shader_vulkan.h index bf05c305f8..d1a9ca3b71 100644 --- a/gfx/drivers_shader/shader_vulkan.h +++ b/gfx/drivers_shader/shader_vulkan.h @@ -116,6 +116,17 @@ bool vulkan_filter_chain_init(vulkan_filter_chain_t *chain); void vulkan_filter_chain_set_input_texture(vulkan_filter_chain_t *chain, const struct vulkan_filter_chain_texture *texture); +void vulkan_filter_chain_set_frame_count(vulkan_filter_chain_t *chain, + uint64_t count); + +void vulkan_filter_chain_set_frame_count_period(vulkan_filter_chain_t *chain, + unsigned pass, + unsigned period); + +void vulkan_filter_chain_set_pass_name(vulkan_filter_chain_t *chain, + unsigned pass, + const char *name); + void vulkan_filter_chain_build_offscreen_passes(vulkan_filter_chain_t *chain, VkCommandBuffer cmd, const VkViewport *vp); void vulkan_filter_chain_build_viewport_pass(vulkan_filter_chain_t *chain, diff --git a/gfx/drivers_shader/slang_reflection.cpp b/gfx/drivers_shader/slang_reflection.cpp index ff27c81540..0715850fb7 100644 --- a/gfx/drivers_shader/slang_reflection.cpp +++ b/gfx/drivers_shader/slang_reflection.cpp @@ -68,6 +68,7 @@ static const char *semantic_uniform_names[] = { "MVP", "OutputSize", "FinalViewportSize", + "FrameCount", }; static slang_texture_semantic slang_name_to_texture_semantic_array(const string &name, const char **names, @@ -101,18 +102,42 @@ static slang_texture_semantic slang_name_to_texture_semantic_array(const string return SLANG_INVALID_TEXTURE_SEMANTIC; } -static slang_texture_semantic slang_name_to_texture_semantic(const string &name, unsigned *index) +static slang_texture_semantic slang_name_to_texture_semantic( + const unordered_map &semantic_map, + const string &name, unsigned *index) { + auto itr = semantic_map.find(name); + if (itr != end(semantic_map)) + { + *index = itr->second.index; + return itr->second.semantic; + } + return slang_name_to_texture_semantic_array(name, texture_semantic_names, index); } -static slang_texture_semantic slang_uniform_name_to_texture_semantic(const string &name, unsigned *index) +static slang_texture_semantic slang_uniform_name_to_texture_semantic( + const unordered_map &semantic_map, + const string &name, unsigned *index) { + auto itr = semantic_map.find(name); + if (itr != end(semantic_map)) + { + *index = itr->second.index; + return itr->second.semantic; + } + return slang_name_to_texture_semantic_array(name, texture_semantic_uniform_names, index); } -static slang_semantic slang_uniform_name_to_semantic(const string &name) +static slang_semantic slang_uniform_name_to_semantic( + const unordered_map &semantic_map, + const string &name) { + auto itr = semantic_map.find(name); + if (itr != end(semantic_map)) + return itr->second; + unsigned i = 0; for (auto n : semantic_uniform_names) { @@ -198,6 +223,10 @@ static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem) // mat4 return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 4; + case SLANG_SEMANTIC_FRAME_COUNT: + // uint + return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1; + default: // vec4 return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1; @@ -222,8 +251,16 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r auto &type = compiler.get_type(compiler.get_type(resource.type_id).member_types[range.index]); unsigned tex_sem_index = 0; - auto sem = slang_uniform_name_to_semantic(name); - auto tex_sem = slang_uniform_name_to_texture_semantic(name, &tex_sem_index); + auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name); + auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map, + name, &tex_sem_index); + + if (tex_sem == SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT && tex_sem_index >= reflection->pass_number) + { + RARCH_ERR("[slang]: Non causal filter chain detected. Shader is trying to use output from pass #%u, but this shader is pass #%u.\n", + tex_sem_index, reflection->pass_number); + return false; + } if (sem != SLANG_INVALID_SEMANTIC) { @@ -250,6 +287,8 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r else { // TODO: Handle invalid semantics as user defined. + RARCH_ERR("[slang]: Unknown semantic found.\n"); + return false; } } return true; @@ -412,7 +451,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm binding_mask |= 1 << binding; unsigned array_index = 0; - slang_texture_semantic index = slang_name_to_texture_semantic(texture.name, &array_index); + slang_texture_semantic index = slang_name_to_texture_semantic(*reflection->texture_semantic_map, + texture.name, &array_index); if (index == SLANG_INVALID_TEXTURE_SEMANTIC) { diff --git a/gfx/drivers_shader/slang_reflection.hpp b/gfx/drivers_shader/slang_reflection.hpp index 94228b2757..9394b8a3b4 100644 --- a/gfx/drivers_shader/slang_reflection.hpp +++ b/gfx/drivers_shader/slang_reflection.hpp @@ -17,6 +17,7 @@ #define SLANG_REFLECTION_HPP #include +#include #include // Textures with built-in meaning. @@ -56,6 +57,7 @@ enum slang_semantic SLANG_SEMANTIC_MVP = 0, SLANG_SEMANTIC_OUTPUT = 1, SLANG_SEMANTIC_FINAL_VIEWPORT = 2, + SLANG_SEMANTIC_FRAME_COUNT = 3, SLANG_NUM_SEMANTICS, SLANG_INVALID_SEMANTIC = -1 @@ -87,6 +89,12 @@ struct slang_semantic_meta bool uniform = false; }; +struct slang_texture_semantic_map +{ + slang_texture_semantic semantic; + unsigned index; +}; + struct slang_reflection { slang_reflection(); @@ -97,6 +105,11 @@ struct slang_reflection std::vector semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS]; slang_semantic_meta semantics[SLANG_NUM_SEMANTICS]; + + const std::unordered_map *texture_semantic_map = nullptr; + const std::unordered_map *texture_semantic_uniform_map = nullptr; + const std::unordered_map *semantic_map = nullptr; + unsigned pass_number = 0; }; bool slang_reflect_spirv(const std::vector &vertex,