diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index cbd2c262f7..d34c1368e5 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -76,16 +76,11 @@ class Buffer size_t size, VkBufferUsageFlags usage); ~Buffer(); - size_t get_size() const { return size; } void *map(); - void unmap(); - - const VkBuffer &get_buffer() const { return buffer; } Buffer(Buffer&&) = delete; void operator=(Buffer&&) = delete; - private: VkDevice device; VkBuffer buffer; VkDeviceMemory memory; @@ -111,12 +106,6 @@ class StaticTexture StaticTexture(StaticTexture&&) = delete; void operator=(StaticTexture&&) = delete; - void release_staging_buffer() { buffer.reset(); } - void set_id(std::string name) { id = std::move(name); } - const std::string &get_id() const { return id; } - const Texture &get_texture() const { return texture; } - - private: VkDevice device; VkImage image; VkImageView view; @@ -139,16 +128,6 @@ class Framebuffer void set_size(DeferredDisposer &disposer, const Size2D &size, VkFormat format = VK_FORMAT_UNDEFINED); - const Size2D &get_size() const { return size; } - VkFormat get_format() const { return format; } - VkImage get_image() const { return image; } - VkImageView get_view() const { return view; } - VkFramebuffer get_framebuffer() const { return framebuffer; } - VkRenderPass get_render_pass() const { return render_pass; } - - unsigned get_levels() const { return levels; } - - private: Size2D size; VkFormat format; unsigned max_levels; @@ -217,9 +196,6 @@ class Pass Pass(Pass&&) = delete; void operator=(Pass&&) = delete; - const Framebuffer &get_framebuffer() const { return *framebuffer; } - Framebuffer *get_feedback_framebuffer() { return fb_feedback.get(); } - Size2D set_pass_info( const Size2D &max_original, const Size2D &max_source, @@ -231,7 +207,6 @@ class Pass size_t spirv_words); bool build(); - bool init_feedback(); void build_commands( DeferredDisposer &disposer, @@ -241,35 +216,8 @@ class Pass const VkViewport &vp, const float *mvp); - void notify_sync_index(unsigned index) { sync_index = index; } - void set_frame_count(uint64_t count) { frame_count = count; } - void set_frame_count_period(unsigned p) { frame_count_period = p; } - void set_frame_direction(int32_t dir) { frame_direction = dir; } - void set_name(const char *name) { pass_name = name; } - const std::string &get_name() const { return pass_name; } - glslang_filter_chain_filter get_source_filter() const { - return pass_info.source_filter; } - - glslang_filter_chain_filter get_mip_filter() const - { - return pass_info.mip_filter; - } - - glslang_filter_chain_address get_address_mode() const - { - return pass_info.address; - } - - void set_common_resources(CommonResources *c) { this->common = c; } - const slang_reflection &get_reflection() const { return reflection; } - void set_pass_number(unsigned pass) { pass_number = pass; } - void add_parameter(unsigned parameter_index, const std::string &id); - void end_frame(); - void allocate_buffers(); - - private: VkDevice device; const VkPhysicalDeviceMemoryProperties &memory_properties; VkPipelineCache cache; @@ -298,31 +246,12 @@ class Pass std::unique_ptr fb_feedback; VkRenderPass swapchain_render_pass; - void clear_vk(); - bool init_pipeline(); - bool init_pipeline_layout(); - - void set_semantic_texture(VkDescriptorSet set, - slang_texture_semantic semantic, - const Texture &texture); - void set_semantic_texture_array(VkDescriptorSet set, - slang_texture_semantic semantic, unsigned index, - const Texture &texture); - slang_reflection reflection; - void build_semantics(VkDescriptorSet set, uint8_t *buffer, - 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_int(uint8_t *data, slang_semantic semantic, int32_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); - void build_semantic_texture_array_vec4(uint8_t *data, - slang_texture_semantic semantic, unsigned index, - unsigned width, unsigned height); void build_semantic_texture(VkDescriptorSet set, uint8_t *buffer, slang_texture_semantic semantic, const Texture &texture); void build_semantic_texture_array(VkDescriptorSet set, uint8_t *buffer, @@ -361,44 +290,8 @@ struct vulkan_filter_chain vulkan_filter_chain(const vulkan_filter_chain_create_info &info); ~vulkan_filter_chain(); - inline void set_shader_preset(std::unique_ptr shader) - { - common.shader_preset = std::move(shader); - } - - inline video_shader *get_shader_preset() - { - return common.shader_preset.get(); - } - - void set_pass_info(unsigned pass, - const vulkan_filter_chain_pass_info &info); - void set_shader(unsigned pass, VkShaderStageFlags stage, - const uint32_t *spirv, size_t spirv_words); - bool init(); - bool update_swapchain_info( - const vulkan_filter_chain_swapchain_info &info); - void notify_sync_index(unsigned index); - void set_input_texture(const vulkan_filter_chain_texture &texture); - void build_offscreen_passes(VkCommandBuffer cmd, const VkViewport &vp); - void build_viewport_pass(VkCommandBuffer cmd, - const VkViewport &vp, const float *mvp); - void end_frame(VkCommandBuffer cmd); - - void set_frame_count(uint64_t count); - void set_frame_count_period(unsigned pass, unsigned period); - void set_frame_direction(int32_t direction); - void set_pass_name(unsigned pass, const char *name); - - void add_static_texture(std::unique_ptr texture); - void add_parameter(unsigned pass, unsigned parameter_index, const std::string &id); - void release_staging_buffers(); - - VkFormat get_pass_rt_format(unsigned pass); - - private: VkDevice device; VkPhysicalDevice gpu; const VkPhysicalDeviceMemoryProperties &memory_properties; @@ -415,19 +308,8 @@ struct vulkan_filter_chain vulkan_filter_chain_swapchain_info swapchain_info; unsigned current_sync_index; - void set_num_passes(unsigned passes); - void set_num_sync_indices(unsigned num_indices); - void set_swapchain_info(const vulkan_filter_chain_swapchain_info &info); - - bool init_ubo(); - bool init_history(); - bool init_feedback(); - bool init_alias(); - void update_history(DeferredDisposer &disposer, VkCommandBuffer cmd); std::vector> original_history; bool require_clear = false; - void update_feedback_info(); - void update_history_info(); }; static void vulkan_framebuffer_generate_mips( @@ -696,66 +578,72 @@ static uint32_t find_memory_type_fallback( return vulkan_find_memory_type(&mem_props, device_reqs, 0); } -static void build_identity_matrix(float *data) -{ - data[ 0] = 1.0f; - data[ 1] = 0.0f; - data[ 2] = 0.0f; - data[ 3] = 0.0f; - data[ 4] = 0.0f; - data[ 5] = 1.0f; - data[ 6] = 0.0f; - data[ 7] = 0.0f; - data[ 8] = 0.0f; - data[ 9] = 0.0f; - data[10] = 1.0f; - data[11] = 0.0f; - data[12] = 0.0f; - data[13] = 0.0f; - data[14] = 0.0f; - data[15] = 1.0f; -} - static VkFormat glslang_format_to_vk(glslang_format fmt) { -#undef FMT -#define FMT(x) case SLANG_FORMAT_##x: return VK_FORMAT_##x switch (fmt) { - FMT(R8_UNORM); - FMT(R8_SINT); - FMT(R8_UINT); - FMT(R8G8_UNORM); - FMT(R8G8_SINT); - FMT(R8G8_UINT); - FMT(R8G8B8A8_UNORM); - FMT(R8G8B8A8_SINT); - FMT(R8G8B8A8_UINT); - FMT(R8G8B8A8_SRGB); + case SLANG_FORMAT_R8_UNORM: + return VK_FORMAT_R8_UNORM; + case SLANG_FORMAT_R8_SINT: + return VK_FORMAT_R8_SINT; + case SLANG_FORMAT_R8_UINT: + return VK_FORMAT_R8_UINT; + case SLANG_FORMAT_R8G8_UNORM: + return VK_FORMAT_R8G8_UNORM; + case SLANG_FORMAT_R8G8_SINT: + return VK_FORMAT_R8G8_SINT; + case SLANG_FORMAT_R8G8_UINT: + return VK_FORMAT_R8G8_UINT; + case SLANG_FORMAT_R8G8B8A8_UNORM: + return VK_FORMAT_R8G8B8A8_UNORM; + case SLANG_FORMAT_R8G8B8A8_SINT: + return VK_FORMAT_R8G8B8A8_SINT; + case SLANG_FORMAT_R8G8B8A8_UINT: + return VK_FORMAT_R8G8B8A8_UINT; + case SLANG_FORMAT_R8G8B8A8_SRGB: + return VK_FORMAT_R8G8B8A8_SRGB; + case SLANG_FORMAT_A2B10G10R10_UNORM_PACK32: + return VK_FORMAT_A2B10G10R10_UNORM_PACK32; + case SLANG_FORMAT_A2B10G10R10_UINT_PACK32: + return VK_FORMAT_A2B10G10R10_UINT_PACK32; - FMT(A2B10G10R10_UNORM_PACK32); - FMT(A2B10G10R10_UINT_PACK32); - - FMT(R16_UINT); - FMT(R16_SINT); - FMT(R16_SFLOAT); - FMT(R16G16_UINT); - FMT(R16G16_SINT); - FMT(R16G16_SFLOAT); - FMT(R16G16B16A16_UINT); - FMT(R16G16B16A16_SINT); - FMT(R16G16B16A16_SFLOAT); - - FMT(R32_UINT); - FMT(R32_SINT); - FMT(R32_SFLOAT); - FMT(R32G32_UINT); - FMT(R32G32_SINT); - FMT(R32G32_SFLOAT); - FMT(R32G32B32A32_UINT); - FMT(R32G32B32A32_SINT); - FMT(R32G32B32A32_SFLOAT); + case SLANG_FORMAT_R16_UINT: + return VK_FORMAT_R16_UINT; + case SLANG_FORMAT_R16_SINT: + return VK_FORMAT_R16_SINT; + case SLANG_FORMAT_R16_SFLOAT: + return VK_FORMAT_R16_SFLOAT; + case SLANG_FORMAT_R16G16_UINT: + return VK_FORMAT_R16G16_UINT; + case SLANG_FORMAT_R16G16_SINT: + return VK_FORMAT_R16G16_SINT; + case SLANG_FORMAT_R16G16_SFLOAT: + return VK_FORMAT_R16G16_SFLOAT; + case SLANG_FORMAT_R16G16B16A16_UINT: + return VK_FORMAT_R16G16B16A16_UINT; + case SLANG_FORMAT_R16G16B16A16_SINT: + return VK_FORMAT_R16G16B16A16_SINT; + case SLANG_FORMAT_R16G16B16A16_SFLOAT: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case SLANG_FORMAT_R32_UINT: + return VK_FORMAT_R32_UINT; + case SLANG_FORMAT_R32_SINT: + return VK_FORMAT_R32_SINT; + case SLANG_FORMAT_R32_SFLOAT: + return VK_FORMAT_R32_SFLOAT; + case SLANG_FORMAT_R32G32_UINT: + return VK_FORMAT_R32G32_UINT; + case SLANG_FORMAT_R32G32_SINT: + return VK_FORMAT_R32G32_SINT; + case SLANG_FORMAT_R32G32_SFLOAT: + return VK_FORMAT_R32G32_SFLOAT; + case SLANG_FORMAT_R32G32B32A32_UINT: + return VK_FORMAT_R32G32B32A32_UINT; + case SLANG_FORMAT_R32G32B32A32_SINT: + return VK_FORMAT_R32G32B32A32_SINT; + case SLANG_FORMAT_R32G32B32A32_SFLOAT: + return VK_FORMAT_R32G32B32A32_SFLOAT; default: break; } @@ -835,7 +723,9 @@ static std::unique_ptr vulkan_filter_chain_load_lut( image.width * image.height * sizeof(uint32_t), VK_BUFFER_USAGE_TRANSFER_SRC_BIT)); ptr = buffer->map(); memcpy(ptr, image.pixels, image.width * image.height * sizeof(uint32_t)); - buffer->unmap(); + if (buffer->mapped) + vkUnmapMemory(buffer->device, buffer->memory); + buffer->mapped = nullptr; VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, tex, VK_REMAINING_MIP_LEVELS, @@ -859,7 +749,7 @@ static std::unique_ptr vulkan_filter_chain_load_lut( region.imageExtent.depth = 1; vkCmdCopyBufferToImage(cmd, - buffer->get_buffer(), + buffer->buffer, tex, shader->mipmap ? VK_IMAGE_LAYOUT_GENERAL @@ -984,7 +874,7 @@ static bool vulkan_filter_chain_load_luts( goto error; } - chain->add_static_texture(std::move(image)); + chain->common.luts.push_back(std::move(std::move(image))); } vkEndCommandBuffer(cmd); @@ -993,7 +883,8 @@ static bool vulkan_filter_chain_load_luts( vkQueueSubmit(info->queue, 1, &submit_info, VK_NULL_HANDLE); vkQueueWaitIdle(info->queue); vkFreeCommandBuffers(info->device, info->command_pool, 1, &cmd); - chain->release_staging_buffers(); + for (i = 0; i < chain->common.luts.size(); i++) + chain->common.luts[i]->buffer.reset(); return true; error: @@ -1013,9 +904,29 @@ vulkan_filter_chain::vulkan_filter_chain( common(info.device, *info.memory_properties), original_format(info.original_format) { + unsigned i; + unsigned num_indices, num_passes; max_input_size = { info.max_input_size.width, info.max_input_size.height }; - set_swapchain_info(info.swapchain); - set_num_passes(info.num_passes); + swapchain_info = info.swapchain; + num_indices = info.swapchain.num_indices; + for (auto &calls : deferred_calls) + { + for (auto &call : calls) + call(); + calls.clear(); + } + deferred_calls.resize(num_indices); + num_passes = info.num_passes; + pass_info.resize(num_passes); + passes.reserve(num_passes); + + for (i = 0; i < num_passes; i++) + { + passes.emplace_back(new Pass(device, memory_properties, + cache, deferred_calls.size(), i + 1 == num_passes)); + passes.back()->common = &common; + passes.back()->pass_number = i; + } } vulkan_filter_chain::~vulkan_filter_chain() @@ -1029,392 +940,21 @@ vulkan_filter_chain::~vulkan_filter_chain() } } -void vulkan_filter_chain::set_swapchain_info( - const vulkan_filter_chain_swapchain_info &info) -{ - swapchain_info = info; - set_num_sync_indices(info.num_indices); -} - -void vulkan_filter_chain::set_num_sync_indices(unsigned num_indices) -{ - for (auto &calls : deferred_calls) - { - for (auto &call : calls) - call(); - calls.clear(); - } - deferred_calls.resize(num_indices); -} - -void vulkan_filter_chain::notify_sync_index(unsigned index) -{ - unsigned i; - auto &calls = deferred_calls[index]; - for (auto &call : calls) - call(); - calls.clear(); - - current_sync_index = index; - - for (i = 0; i < passes.size(); i++) - passes[i]->notify_sync_index(index); -} - -bool vulkan_filter_chain::update_swapchain_info( - const vulkan_filter_chain_swapchain_info &info) -{ - vkDeviceWaitIdle(device); - for (auto &calls : deferred_calls) - { - for (auto &call : calls) - call(); - calls.clear(); - } - set_swapchain_info(info); - return init(); -} - -void vulkan_filter_chain::release_staging_buffers() -{ - unsigned i; - for (i = 0; i < common.luts.size(); i++) - common.luts[i]->release_staging_buffer(); -} - -void vulkan_filter_chain::update_history_info() -{ - unsigned i = 0; - - for (i = 0; i < original_history.size(); i++) - { - Texture *source = (Texture*)&common.original_history[i]; - - if (!source) - continue; - - source->texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - source->texture.view = original_history[i]->get_view(); - source->texture.image = original_history[i]->get_image(); - source->texture.width = original_history[i]->get_size().width; - source->texture.height = original_history[i]->get_size().height; - source->filter = passes.front()->get_source_filter(); - source->mip_filter = passes.front()->get_mip_filter(); - source->address = passes.front()->get_address_mode(); - } -} - -void vulkan_filter_chain::update_feedback_info() -{ - unsigned i; - - for (i = 0; i < passes.size() - 1; i++) - { - Framebuffer *fb = passes[i]->get_feedback_framebuffer(); - if (!fb) - continue; - - Texture *source = &common.fb_feedback[i]; - - if (!source) - continue; - - source->texture.image = fb->get_image(); - source->texture.view = fb->get_view(); - source->texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - source->texture.width = fb->get_size().width; - source->texture.height = fb->get_size().height; - source->filter = passes[i]->get_source_filter(); - source->mip_filter = passes[i]->get_mip_filter(); - source->address = passes[i]->get_address_mode(); - } -} - -void vulkan_filter_chain::build_offscreen_passes(VkCommandBuffer cmd, - const VkViewport &vp) -{ - unsigned i; - Texture source; - - /* First frame, make sure our history and feedback textures - * are in a clean state. */ - if (require_clear) - { - unsigned i; - for (i = 0; i < original_history.size(); i++) - vulkan_framebuffer_clear(original_history[i]->get_image(), cmd); - for (i = 0; i < passes.size(); i++) - { - Framebuffer *fb = passes[i]->get_feedback_framebuffer(); - if (fb) - vulkan_framebuffer_clear(fb->get_image(), cmd); - } - require_clear = false; - } - - update_history_info(); - if (!common.fb_feedback.empty()) - update_feedback_info(); - - DeferredDisposer disposer(deferred_calls[current_sync_index]); - const Texture original = { - input_texture, - passes.front()->get_source_filter(), - passes.front()->get_mip_filter(), - passes.front()->get_address_mode(), - }; - - source = original; - - for (i = 0; i < passes.size() - 1; i++) - { - passes[i]->build_commands(disposer, cmd, - original, source, vp, nullptr); - - const Framebuffer &fb = passes[i]->get_framebuffer(); - - source.texture.view = fb.get_view(); - source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - source.texture.width = fb.get_size().width; - source.texture.height = fb.get_size().height; - source.filter = passes[i + 1]->get_source_filter(); - source.mip_filter = passes[i + 1]->get_mip_filter(); - source.address = passes[i + 1]->get_address_mode(); - - common.pass_outputs[i] = source; - } -} - -void vulkan_filter_chain::update_history(DeferredDisposer &disposer, - VkCommandBuffer cmd) -{ - std::unique_ptr tmp; - VkImageLayout src_layout = input_texture.layout; - - /* Transition input texture to something appropriate. */ - if (input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) - { - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - input_texture.image,VK_REMAINING_MIP_LEVELS, - input_texture.layout, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - 0, - VK_ACCESS_TRANSFER_READ_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - src_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - } - - std::unique_ptr &back = original_history.back(); - swap(back, tmp); - - if (input_texture.width != tmp->get_size().width || - input_texture.height != tmp->get_size().height || - (input_texture.format != VK_FORMAT_UNDEFINED - && input_texture.format != tmp->get_format())) - tmp->set_size(disposer, { input_texture.width, input_texture.height }, input_texture.format); - - vulkan_framebuffer_copy(tmp->get_image(), tmp->get_size(), - cmd, input_texture.image, src_layout); - - /* Transition input texture back. */ - if (input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) - { - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - input_texture.image,VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - input_texture.layout, - 0, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - - /* Should ring buffer, but we don't have *that* many passes. */ - move_backward(begin(original_history), end(original_history) - 1, end(original_history)); - swap(original_history.front(), tmp); -} - -void vulkan_filter_chain::end_frame(VkCommandBuffer cmd) -{ - /* If we need to keep old frames, copy it after fragment is complete. - * TODO: We can improve pipelining by figuring out which - * pass is the last that reads from - * the history and dispatch the copy earlier. */ - if (!original_history.empty()) - { - DeferredDisposer disposer(deferred_calls[current_sync_index]); - update_history(disposer, cmd); - } -} - -void vulkan_filter_chain::build_viewport_pass( - VkCommandBuffer cmd, const VkViewport &vp, const float *mvp) -{ - unsigned i; - Texture source; - - /* First frame, make sure our history and - * feedback textures are in a clean state. */ - if (require_clear) - { - unsigned i; - for (i = 0; i < original_history.size(); i++) - vulkan_framebuffer_clear(original_history[i]->get_image(), cmd); - for (i = 0; i < passes.size(); i++) - { - Framebuffer *fb = passes[i]->get_feedback_framebuffer(); - if (fb) - vulkan_framebuffer_clear(fb->get_image(), cmd); - } - require_clear = false; - } - - DeferredDisposer disposer(deferred_calls[current_sync_index]); - const Texture original = { - input_texture, - passes.front()->get_source_filter(), - passes.front()->get_mip_filter(), - passes.front()->get_address_mode(), - }; - - if (passes.size() == 1) - { - source = { - input_texture, - passes.back()->get_source_filter(), - passes.back()->get_mip_filter(), - passes.back()->get_address_mode(), - }; - } - else - { - const Framebuffer &fb = passes[passes.size() - 2]->get_framebuffer(); - source.texture.view = fb.get_view(); - source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - source.texture.width = fb.get_size().width; - source.texture.height = fb.get_size().height; - source.filter = passes.back()->get_source_filter(); - source.mip_filter = passes.back()->get_mip_filter(); - source.address = passes.back()->get_address_mode(); - } - - passes.back()->build_commands(disposer, cmd, - original, source, vp, mvp); - - /* For feedback FBOs, swap current and previous. */ - for (i = 0; i < passes.size(); i++) - passes[i]->end_frame(); -} - -bool vulkan_filter_chain::init_history() -{ - unsigned i; - size_t required_images = 0; - - original_history.clear(); - common.original_history.clear(); - - for (i = 0; i < passes.size(); i++) - required_images = - std::max(required_images, - passes[i]->get_reflection().semantic_textures[ - SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY].size()); - - if (required_images < 2) - { -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan filter chain]: Not using frame history.\n"); -#endif - return true; - } - - /* We don't need to store array element #0, - * since it's aliased with the actual original. */ - required_images--; - original_history.reserve(required_images); - common.original_history.resize(required_images); - - for (i = 0; i < required_images; i++) - original_history.emplace_back( - new Framebuffer(device, memory_properties, - max_input_size, original_format, 1)); - -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan filter chain]: Using history of %u frames.\n", unsigned(required_images)); -#endif - - /* On first frame, we need to clear the textures to - * a known state, but we need - * a command buffer for that, so just defer to first frame. - */ - require_clear = true; - return true; -} - -bool vulkan_filter_chain::init_feedback() -{ - unsigned i; - bool use_feedbacks = false; - - common.fb_feedback.clear(); - - /* Final pass cannot have feedback. */ - for (i = 0; i < passes.size() - 1; i++) - { - bool use_feedback = false; - for (auto &pass : passes) - { - const slang_reflection &r = pass->get_reflection(); - auto &feedbacks = r.semantic_textures[ - SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK]; - - if (i < feedbacks.size() && feedbacks[i].texture) - { - use_feedback = true; - use_feedbacks = true; - break; - } - } - - if (use_feedback) - { - if (!passes[i]->init_feedback()) - return false; -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan filter chain]: Using framebuffer feedback for pass #%u.\n", i); -#endif - } - } - - if (!use_feedbacks) - { -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan filter chain]: Not using framebuffer feedback.\n"); -#endif - return true; - } - - common.fb_feedback.resize(passes.size() - 1); - require_clear = true; - return true; -} - -bool vulkan_filter_chain::init_alias() +bool vulkan_filter_chain::init() { unsigned i, j; + size_t required_images = 0; + bool use_feedbacks = false; + VkPhysicalDeviceProperties props; + Size2D source = max_input_size; + + /* Initialize alias */ common.texture_semantic_map.clear(); common.texture_semantic_uniform_map.clear(); for (i = 0; i < passes.size(); i++) { - const std::string name = passes[i]->get_name(); + const std::string name = passes[i]->pass_name; if (name.empty()) continue; @@ -1446,67 +986,47 @@ bool vulkan_filter_chain::init_alias() j = &common.luts[i] - common.luts.data(); if (!slang_set_unique_map( common.texture_semantic_map, - common.luts[i]->get_id(), + common.luts[i]->id, slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, j })) return false; if (!slang_set_unique_map( common.texture_semantic_uniform_map, - common.luts[i]->get_id() + "Size", + common.luts[i]->id + "Size", slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, j })) return false; } - return true; -} - -void vulkan_filter_chain::set_pass_info(unsigned pass, - const vulkan_filter_chain_pass_info &info) -{ - pass_info[pass] = info; -} - - VkFormat vulkan_filter_chain::get_pass_rt_format(unsigned pass) - { - return pass_info[pass].rt_format; - } - -void vulkan_filter_chain::set_num_passes(unsigned num_passes) -{ - unsigned i; - - pass_info.resize(num_passes); - passes.reserve(num_passes); - - for (i = 0; i < num_passes; i++) + for (i = 0; i < passes.size(); i++) { - 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); +#ifdef VULKAN_DEBUG + const char *name = passes[i]->pass_name.c_str(); + RARCH_LOG("[slang]: Building pass #%u (%s)\n", i, + string_is_empty(name) ? + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE) : + name); +#endif + if (passes[i]->pool != VK_NULL_HANDLE) + vkDestroyDescriptorPool(device, passes[i]->pool, nullptr); + if (passes[i]->pipeline != VK_NULL_HANDLE) + vkDestroyPipeline(device, passes[i]->pipeline, nullptr); + if (passes[i]->set_layout != VK_NULL_HANDLE) + vkDestroyDescriptorSetLayout(device, passes[i]->set_layout, nullptr); + if (passes[i]->pipeline_layout != VK_NULL_HANDLE) + vkDestroyPipelineLayout(device, passes[i]->pipeline_layout, nullptr); + + passes[i]->pool = VK_NULL_HANDLE; + passes[i]->pipeline = VK_NULL_HANDLE; + passes[i]->set_layout = VK_NULL_HANDLE; + source = passes[i]->set_pass_info(max_input_size, + source, swapchain_info, pass_info[i]); + if (!passes[i]->build()) + return false; } -} -void vulkan_filter_chain::set_shader( - unsigned pass, - VkShaderStageFlags stage, - const uint32_t *spirv, - size_t spirv_words) -{ - passes[pass]->set_shader(stage, spirv, spirv_words); -} - -void vulkan_filter_chain::add_parameter(unsigned pass, - unsigned index, const std::string &id) -{ - passes[pass]->add_parameter(index, id); -} - -bool vulkan_filter_chain::init_ubo() -{ - unsigned i; - VkPhysicalDeviceProperties props; + require_clear = false; + /* Initialize UBO (Uniform Buffer Object) */ common.ubo.reset(); common.ubo_offset = 0; @@ -1517,8 +1037,21 @@ bool vulkan_filter_chain::init_ubo() if (common.ubo_alignment == 0) common.ubo_alignment = 1; + /* Allocate pass buffers */ for (i = 0; i < passes.size(); i++) - passes[i]->allocate_buffers(); + { + if (passes[i]->reflection.ubo_stage_mask) + { + /* Align */ + passes[i]->common->ubo_offset = (passes[i]->common->ubo_offset + + passes[i]->common->ubo_alignment - 1) & + ~(passes[i]->common->ubo_alignment - 1); + passes[i]->ubo_offset = passes[i]->common->ubo_offset; + + /* Allocate */ + passes[i]->common->ubo_offset += passes[i]->reflection.ubo_size; + } + } common.ubo_offset = (common.ubo_offset + common.ubo_alignment - 1) & @@ -1531,80 +1064,87 @@ bool vulkan_filter_chain::init_ubo() VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)); common.ubo_mapped = static_cast(common.ubo->map()); - return true; -} -bool vulkan_filter_chain::init() -{ - unsigned i; - Size2D source = max_input_size; - - if (!init_alias()) - return false; + /* Initialize history */ + original_history.clear(); + common.original_history.clear(); for (i = 0; i < passes.size(); i++) + required_images = + std::max(required_images, + passes[i]->reflection.semantic_textures[ + SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY].size()); + + /* If required images are less than 2, not using frame history */ + if (required_images >= 2) { + /* We don't need to store array element #0, + * since it's aliased with the actual original. */ + required_images--; + original_history.reserve(required_images); + common.original_history.resize(required_images); + + for (i = 0; i < required_images; i++) + original_history.emplace_back( + new Framebuffer(device, memory_properties, + max_input_size, original_format, 1)); + #ifdef VULKAN_DEBUG - const char *name = passes[i]->get_name().c_str(); - RARCH_LOG("[slang]: Building pass #%u (%s)\n", i, - string_is_empty(name) ? - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE) : - name); + RARCH_LOG("[Vulkan filter chain]: Using history of %u frames.\n", unsigned(required_images)); #endif - source = passes[i]->set_pass_info(max_input_size, - source, swapchain_info, pass_info[i]); - if (!passes[i]->build()) - return false; + + /* On first frame, we need to clear the textures to + * a known state, but we need + * a command buffer for that, so just defer to first frame. + */ + require_clear = true; + } + + /* Initialize feedback */ + common.fb_feedback.clear(); + /* Final pass cannot have feedback. */ + for (i = 0; i < passes.size() - 1; i++) + { + bool use_feedback = false; + for (auto &pass : passes) + { + const slang_reflection &r = pass->reflection; + auto &feedbacks = r.semantic_textures[ + SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK]; + + if (i < feedbacks.size() && feedbacks[i].texture) + { + use_feedback = true; + use_feedbacks = true; + break; + } + } + + if (use_feedback) + { + if (!passes[i]->final_pass) + return false; + passes[i]->fb_feedback = std::unique_ptr( + new Framebuffer(device, memory_properties, + passes[i]->current_framebuffer_size, + passes[i]->pass_info.rt_format, + passes[i]->pass_info.max_levels)); +#ifdef VULKAN_DEBUG + RARCH_LOG("[Vulkan filter chain]: Using framebuffer feedback for pass #%u.\n", i); +#endif + } + } + + if (use_feedbacks) + { + common.fb_feedback.resize(passes.size() - 1); + require_clear = true; } - require_clear = false; - if (!init_ubo()) - return false; - if (!init_history()) - return false; - if (!init_feedback()) - return false; common.pass_outputs.resize(passes.size()); return true; } -void vulkan_filter_chain::set_input_texture( - const vulkan_filter_chain_texture &texture) -{ - input_texture = texture; -} - -void vulkan_filter_chain::add_static_texture( - std::unique_ptr texture) -{ - common.luts.push_back(std::move(texture)); -} - -void vulkan_filter_chain::set_frame_count(uint64_t count) -{ - unsigned i; - for (i = 0; i < passes.size(); i++) - passes[i]->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_frame_direction(int32_t direction) -{ - unsigned i; - for (i = 0; i < passes.size(); i++) - passes[i]->set_frame_direction(direction); -} - -void vulkan_filter_chain::set_pass_name(unsigned pass, const char *name) -{ - passes[pass]->set_name(name); -} - StaticTexture::StaticTexture(std::string id, VkDevice device, VkImage image, @@ -1675,25 +1215,18 @@ Buffer::Buffer(VkDevice device, void *Buffer::map() { - if (!mapped) - { - if (vkMapMemory(device, memory, 0, size, 0, &mapped) != VK_SUCCESS) - return nullptr; - } - return mapped; -} - -void Buffer::unmap() -{ - if (mapped) - vkUnmapMemory(device, memory); - mapped = nullptr; + if (!mapped && vkMapMemory(device, memory, 0, size, 0, &mapped) == VK_SUCCESS) + return mapped; + return nullptr; } Buffer::~Buffer() { if (mapped) - unmap(); + { + vkUnmapMemory(device, memory); + mapped = nullptr; + } if (memory != VK_NULL_HANDLE) vkFreeMemory(device, memory, nullptr); if (buffer != VK_NULL_HANDLE) @@ -1702,7 +1235,18 @@ Buffer::~Buffer() Pass::~Pass() { - clear_vk(); + if (pool != VK_NULL_HANDLE) + vkDestroyDescriptorPool(device, pool, nullptr); + if (pipeline != VK_NULL_HANDLE) + vkDestroyPipeline(device, pipeline, nullptr); + if (set_layout != VK_NULL_HANDLE) + vkDestroyDescriptorSetLayout(device, set_layout, nullptr); + if (pipeline_layout != VK_NULL_HANDLE) + vkDestroyPipelineLayout(device, pipeline_layout, nullptr); + + pool = VK_NULL_HANDLE; + pipeline = VK_NULL_HANDLE; + set_layout = VK_NULL_HANDLE; } void Pass::add_parameter(unsigned index, const std::string &id) @@ -1789,8 +1333,6 @@ Size2D Pass::set_pass_info( const vulkan_filter_chain_swapchain_info &swapchain, const vulkan_filter_chain_pass_info &info) { - clear_vk(); - current_viewport = swapchain.viewport; pass_info = info; @@ -1803,25 +1345,207 @@ Size2D Pass::set_pass_info( return current_framebuffer_size; } -void Pass::clear_vk() -{ - if (pool != VK_NULL_HANDLE) - vkDestroyDescriptorPool(device, pool, nullptr); - if (pipeline != VK_NULL_HANDLE) - vkDestroyPipeline(device, pipeline, nullptr); - if (set_layout != VK_NULL_HANDLE) - vkDestroyDescriptorSetLayout(device, set_layout, nullptr); - if (pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(device, pipeline_layout, nullptr); - - pool = VK_NULL_HANDLE; - pipeline = VK_NULL_HANDLE; - set_layout = VK_NULL_HANDLE; -} - -bool Pass::init_pipeline_layout() +CommonResources::CommonResources(VkDevice device, + const VkPhysicalDeviceMemoryProperties &memory_properties) + : device(device) { unsigned i; + VkSamplerCreateInfo info = { + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + /* The final pass uses an MVP designed for [0, 1] range VBO. + * For in-between passes, we just go with identity matrices, + * so keep it simple. + */ + const float vbo_data[] = { + /* Offscreen */ + -1.0f, -1.0f, 0.0f, 0.0f, + -1.0f, +1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, +1.0f, 1.0f, 1.0f, + + /* Final */ + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, +1.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, +1.0f, 1.0f, 1.0f, + }; + + vbo = + std::unique_ptr(new Buffer(device, + memory_properties, sizeof(vbo_data), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); + + void *ptr = vbo->map(); + memcpy(ptr, vbo_data, sizeof(vbo_data)); + if (vbo->mapped) + vkUnmapMemory(vbo->device, vbo->memory); + vbo->mapped = nullptr; + + info.mipLodBias = 0.0f; + info.maxAnisotropy = 1.0f; + info.compareEnable = false; + info.minLod = 0.0f; + info.maxLod = VK_LOD_CLAMP_NONE; + info.unnormalizedCoordinates = false; + info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + + for (i = 0; i < GLSLANG_FILTER_CHAIN_COUNT; i++) + { + unsigned j; + + switch (static_cast(i)) + { + case GLSLANG_FILTER_CHAIN_LINEAR: + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + break; + + case GLSLANG_FILTER_CHAIN_NEAREST: + info.magFilter = VK_FILTER_NEAREST; + info.minFilter = VK_FILTER_NEAREST; + break; + + default: + break; + } + + for (j = 0; j < GLSLANG_FILTER_CHAIN_COUNT; j++) + { + unsigned k; + + switch (static_cast(j)) + { + case GLSLANG_FILTER_CHAIN_LINEAR: + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + break; + + case GLSLANG_FILTER_CHAIN_NEAREST: + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + break; + + default: + break; + } + + for (k = 0; k < GLSLANG_FILTER_CHAIN_ADDRESS_COUNT; k++) + { + VkSamplerAddressMode mode = VK_SAMPLER_ADDRESS_MODE_MAX_ENUM; + + switch (static_cast(k)) + { + case GLSLANG_FILTER_CHAIN_ADDRESS_REPEAT: + mode = VK_SAMPLER_ADDRESS_MODE_REPEAT; + break; + + case GLSLANG_FILTER_CHAIN_ADDRESS_MIRRORED_REPEAT: + mode = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + break; + + case GLSLANG_FILTER_CHAIN_ADDRESS_CLAMP_TO_EDGE: + mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + break; + + case GLSLANG_FILTER_CHAIN_ADDRESS_CLAMP_TO_BORDER: + mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + break; + + case GLSLANG_FILTER_CHAIN_ADDRESS_MIRROR_CLAMP_TO_EDGE: + mode = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + break; + + default: + break; + } + + info.addressModeU = mode; + info.addressModeV = mode; + info.addressModeW = mode; + vkCreateSampler(device, &info, nullptr, &samplers[i][j][k]); + } + } + } +} + +CommonResources::~CommonResources() +{ + for (auto &i : samplers) + for (auto &j : i) + for (auto &k : j) + if (k != VK_NULL_HANDLE) + vkDestroySampler(device, k, nullptr); +} + +bool Pass::build() +{ + unsigned i; + unsigned j = 0; + std::unordered_map semantic_map; + + framebuffer.reset(); + fb_feedback.reset(); + + if (!final_pass) + framebuffer = std::unique_ptr( + new Framebuffer(device, memory_properties, + current_framebuffer_size, + pass_info.rt_format, pass_info.max_levels)); + + for (i = 0; i < parameters.size(); i++) + { + if (!slang_set_unique_map( + semantic_map, parameters[i].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 = &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 (i = 0; i < reflection.semantic_float_parameters.size(); i++) + { + if (reflection.semantic_float_parameters[i].uniform || + reflection.semantic_float_parameters[i].push_constant) + filtered_parameters.push_back(parameters[i]); + } + + VkPipelineInputAssemblyStateCreateInfo input_assembly = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + VkVertexInputAttributeDescription attributes[2] = {{0}}; + VkVertexInputBindingDescription binding = {0}; + VkPipelineVertexInputStateCreateInfo vertex_input = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + VkPipelineRasterizationStateCreateInfo raster = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + VkPipelineColorBlendAttachmentState blend_attachment = {0}; + VkPipelineColorBlendStateCreateInfo blend = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + VkPipelineViewportStateCreateInfo viewport = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + VkPipelineDepthStencilStateCreateInfo depth_stencil = { + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + VkPipelineMultisampleStateCreateInfo multisample = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + VkPipelineDynamicStateCreateInfo dynamic = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + static const VkDynamicState dynamics[] = { + VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineShaderStageCreateInfo shader_stages[2] = { + { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }, + { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }, + }; + VkShaderModuleCreateInfo module_info = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + VkGraphicsPipelineCreateInfo pipe = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; std::vector bindings; std::vector desc_counts; VkPushConstantRange push_range = {}; @@ -1921,44 +1645,6 @@ bool Pass::init_pipeline_layout() for (i = 0; i < num_sync_indices; i++) vkAllocateDescriptorSets(device, &alloc_info, &sets[i]); - return true; -} - -bool Pass::init_pipeline() -{ - VkPipelineInputAssemblyStateCreateInfo input_assembly = { - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - VkVertexInputAttributeDescription attributes[2] = {{0}}; - VkVertexInputBindingDescription binding = {0}; - VkPipelineVertexInputStateCreateInfo vertex_input = { - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - VkPipelineRasterizationStateCreateInfo raster = { - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - VkPipelineColorBlendAttachmentState blend_attachment = {0}; - VkPipelineColorBlendStateCreateInfo blend = { - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; - VkPipelineViewportStateCreateInfo viewport = { - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - VkPipelineDepthStencilStateCreateInfo depth_stencil = { - VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; - VkPipelineMultisampleStateCreateInfo multisample = { - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - VkPipelineDynamicStateCreateInfo dynamic = { - VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; - static const VkDynamicState dynamics[] = { - VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineShaderStageCreateInfo shader_stages[2] = { - { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }, - { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }, - }; - VkShaderModuleCreateInfo module_info = { - VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; - VkGraphicsPipelineCreateInfo pipe = { - VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - - if (!init_pipeline_layout()) - return false; - /* Input assembly */ input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; @@ -2040,7 +1726,7 @@ bool Pass::init_pipeline() pipe.pDepthStencilState = &depth_stencil; pipe.pDynamicState = &dynamic; pipe.renderPass = final_pass ? swapchain_render_pass : - framebuffer->get_render_pass(); + framebuffer->render_pass; pipe.layout = pipeline_layout; if (vkCreateGraphicsPipelines(device, @@ -2056,278 +1742,18 @@ bool Pass::init_pipeline() return true; } -CommonResources::CommonResources(VkDevice device, - const VkPhysicalDeviceMemoryProperties &memory_properties) - : device(device) -{ - unsigned i; - VkSamplerCreateInfo info = { - VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - /* The final pass uses an MVP designed for [0, 1] range VBO. - * For in-between passes, we just go with identity matrices, - * so keep it simple. - */ - const float vbo_data[] = { - /* Offscreen */ - -1.0f, -1.0f, 0.0f, 0.0f, - -1.0f, +1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, - 1.0f, +1.0f, 1.0f, 1.0f, - - /* Final */ - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, +1.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 1.0f, 0.0f, - 1.0f, +1.0f, 1.0f, 1.0f, - }; - - vbo = - std::unique_ptr(new Buffer(device, - memory_properties, sizeof(vbo_data), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); - - void *ptr = vbo->map(); - memcpy(ptr, vbo_data, sizeof(vbo_data)); - vbo->unmap(); - - info.mipLodBias = 0.0f; - info.maxAnisotropy = 1.0f; - info.compareEnable = false; - info.minLod = 0.0f; - info.maxLod = VK_LOD_CLAMP_NONE; - info.unnormalizedCoordinates = false; - info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; - - for (i = 0; i < GLSLANG_FILTER_CHAIN_COUNT; i++) - { - unsigned j; - - switch (static_cast(i)) - { - case GLSLANG_FILTER_CHAIN_LINEAR: - info.magFilter = VK_FILTER_LINEAR; - info.minFilter = VK_FILTER_LINEAR; - break; - - case GLSLANG_FILTER_CHAIN_NEAREST: - info.magFilter = VK_FILTER_NEAREST; - info.minFilter = VK_FILTER_NEAREST; - break; - - default: - break; - } - - for (j = 0; j < GLSLANG_FILTER_CHAIN_COUNT; j++) - { - unsigned k; - - switch (static_cast(j)) - { - case GLSLANG_FILTER_CHAIN_LINEAR: - info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - break; - - case GLSLANG_FILTER_CHAIN_NEAREST: - info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - break; - - default: - break; - } - - for (k = 0; k < GLSLANG_FILTER_CHAIN_ADDRESS_COUNT; k++) - { - VkSamplerAddressMode mode = VK_SAMPLER_ADDRESS_MODE_MAX_ENUM; - - switch (static_cast(k)) - { - case GLSLANG_FILTER_CHAIN_ADDRESS_REPEAT: - mode = VK_SAMPLER_ADDRESS_MODE_REPEAT; - break; - - case GLSLANG_FILTER_CHAIN_ADDRESS_MIRRORED_REPEAT: - mode = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; - break; - - case GLSLANG_FILTER_CHAIN_ADDRESS_CLAMP_TO_EDGE: - mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - break; - - case GLSLANG_FILTER_CHAIN_ADDRESS_CLAMP_TO_BORDER: - mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; - break; - - case GLSLANG_FILTER_CHAIN_ADDRESS_MIRROR_CLAMP_TO_EDGE: - mode = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; - break; - - default: - break; - } - - info.addressModeU = mode; - info.addressModeV = mode; - info.addressModeW = mode; - vkCreateSampler(device, &info, nullptr, &samplers[i][j][k]); - } - } - } -} - -CommonResources::~CommonResources() -{ - for (auto &i : samplers) - for (auto &j : i) - for (auto &k : j) - if (k != VK_NULL_HANDLE) - vkDestroySampler(device, k, nullptr); -} - -void Pass::allocate_buffers() -{ - if (reflection.ubo_stage_mask) - { - /* Align */ - common->ubo_offset = (common->ubo_offset + common->ubo_alignment - 1) & - ~(common->ubo_alignment - 1); - ubo_offset = common->ubo_offset; - - /* Allocate */ - common->ubo_offset += reflection.ubo_size; - } -} - -void Pass::end_frame() -{ - if (fb_feedback) - swap(framebuffer, fb_feedback); -} - -bool Pass::init_feedback() -{ - if (final_pass) - return false; - - fb_feedback = std::unique_ptr( - new Framebuffer(device, memory_properties, - current_framebuffer_size, - pass_info.rt_format, pass_info.max_levels)); - return true; -} - -bool Pass::build() -{ - unsigned i; - unsigned j = 0; - std::unordered_map semantic_map; - - framebuffer.reset(); - fb_feedback.reset(); - - if (!final_pass) - framebuffer = std::unique_ptr( - new Framebuffer(device, memory_properties, - current_framebuffer_size, - pass_info.rt_format, pass_info.max_levels)); - - for (i = 0; i < parameters.size(); i++) - { - if (!slang_set_unique_map( - semantic_map, parameters[i].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 = &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 (i = 0; i < reflection.semantic_float_parameters.size(); i++) - { - if (reflection.semantic_float_parameters[i].uniform || - reflection.semantic_float_parameters[i].push_constant) - filtered_parameters.push_back(parameters[i]); - } - - return init_pipeline(); -} - -void Pass::set_semantic_texture(VkDescriptorSet set, - slang_texture_semantic semantic, const Texture &texture) -{ - if (reflection.semantic_textures[semantic][0].texture) - { - VULKAN_PASS_SET_TEXTURE(device, set, common->samplers[texture.filter][texture.mip_filter][texture.address], reflection.semantic_textures[semantic][0].binding, texture.texture.view, texture.texture.layout); - } -} - -void Pass::set_semantic_texture_array(VkDescriptorSet set, - slang_texture_semantic semantic, unsigned index, - const Texture &texture) -{ - if (index < reflection.semantic_textures[semantic].size() && - reflection.semantic_textures[semantic][index].texture) - { - VULKAN_PASS_SET_TEXTURE(device, set, common->samplers[texture.filter][texture.mip_filter][texture.address], reflection.semantic_textures[semantic][index].binding, texture.texture.view, texture.texture.layout); - } -} - -void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic, - unsigned index, unsigned width, unsigned height) -{ - auto &refl = reflection.semantic_textures[semantic]; - - if (index >= refl.size()) - return; - - if (data && refl[index].uniform) - { - float *_data = reinterpret_cast(data + refl[index].ubo_offset); - _data[0] = (float)(width); - _data[1] = (float)(height); - _data[2] = 1.0f / (float)(width); - _data[3] = 1.0f / (float)(height); - } - - if (refl[index].push_constant) - { - float *_data = reinterpret_cast(push.buffer.data() + (refl[index].push_constant_offset >> 2)); - _data[0] = (float)(width); - _data[1] = (float)(height); - _data[2] = 1.0f / (float)(width); - _data[3] = 1.0f / (float)(height); - } -} - -void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic, - unsigned width, unsigned height) -{ - build_semantic_texture_array_vec4(data, semantic, 0, width, height); -} - -void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, +void Pass::build_semantic_vec4(uint8_t *u, slang_semantic semantic, unsigned width, unsigned height) { auto &refl = reflection.semantics[semantic]; - - if (data && refl.uniform) + if (u && refl.uniform) { - float *_data = reinterpret_cast(data + refl.ubo_offset); + float *_data = reinterpret_cast(u + refl.ubo_offset); _data[0] = (float)(width); _data[1] = (float)(height); _data[2] = 1.0f / (float)(width); _data[3] = 1.0f / (float)(height); } - if (refl.push_constant) { float *_data = reinterpret_cast @@ -2339,38 +1765,32 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic, } } -void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value) +void Pass::build_semantic_parameter(uint8_t *u, unsigned index, float value) { auto &refl = reflection.semantic_float_parameters[index]; - /* We will have filtered out stale parameters. */ - if (data && refl.uniform) - *reinterpret_cast(data + refl.ubo_offset) = value; - + if (u && refl.uniform) + *reinterpret_cast(u + refl.ubo_offset) = value; if (refl.push_constant) *reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)) = value; } -void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic, +void Pass::build_semantic_uint(uint8_t *u, slang_semantic semantic, uint32_t value) { auto &refl = reflection.semantics[semantic]; - - if (data && refl.uniform) - *reinterpret_cast(data + reflection.semantics[semantic].ubo_offset) = value; - + if (u && refl.uniform) + *reinterpret_cast(u + reflection.semantics[semantic].ubo_offset) = value; if (refl.push_constant) *reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)) = value; } -void Pass::build_semantic_int(uint8_t *data, slang_semantic semantic, +void Pass::build_semantic_int(uint8_t *u, slang_semantic semantic, int32_t value) { auto &refl = reflection.semantics[semantic]; - - if (data && refl.uniform) - *reinterpret_cast(data + reflection.semantics[semantic].ubo_offset) = value; - + if (u && refl.uniform) + *reinterpret_cast(u + reflection.semantics[semantic].ubo_offset) = value; if (refl.push_constant) *reinterpret_cast(push.buffer.data() + (refl.push_constant_offset >> 2)) = value; } @@ -2378,97 +1798,68 @@ void Pass::build_semantic_int(uint8_t *data, slang_semantic semantic, void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer, slang_texture_semantic semantic, const Texture &texture) { - build_semantic_texture_vec4(buffer, semantic, - texture.texture.width, texture.texture.height); - set_semantic_texture(set, semantic, texture); + unsigned width = texture.texture.width; + unsigned height = texture.texture.height; + unsigned index = 0; + auto &refl = reflection.semantic_textures[semantic]; + + if (index < refl.size()) + { + if (buffer && refl[index].uniform) + { + float *_data = reinterpret_cast(buffer + refl[index].ubo_offset); + _data[0] = (float)(width); + _data[1] = (float)(height); + _data[2] = 1.0f / (float)(width); + _data[3] = 1.0f / (float)(height); + } + if (refl[index].push_constant) + { + float *_data = reinterpret_cast(push.buffer.data() + (refl[index].push_constant_offset >> 2)); + _data[0] = (float)(width); + _data[1] = (float)(height); + _data[2] = 1.0f / (float)(width); + _data[3] = 1.0f / (float)(height); + } + } + if (reflection.semantic_textures[semantic][0].texture) + { + VULKAN_PASS_SET_TEXTURE(device, set, common->samplers[texture.filter][texture.mip_filter][texture.address], reflection.semantic_textures[semantic][0].binding, texture.texture.view, texture.texture.layout); + } } void Pass::build_semantic_texture_array(VkDescriptorSet set, uint8_t *buffer, slang_texture_semantic semantic, unsigned index, const Texture &texture) { - build_semantic_texture_array_vec4(buffer, semantic, index, - texture.texture.width, texture.texture.height); - set_semantic_texture_array(set, semantic, index, texture); -} + auto &refl = reflection.semantic_textures[semantic]; + unsigned width = texture.texture.width; + unsigned height = texture.texture.height; -void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer, - const float *mvp, const Texture &original, const Texture &source) -{ - unsigned i; - - /* MVP */ - if (buffer && reflection.semantics[SLANG_SEMANTIC_MVP].uniform) + if (index < refl.size()) { - size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].ubo_offset; - if (mvp) - memcpy(buffer + offset, mvp, sizeof(float) * 16); - else - build_identity_matrix(reinterpret_cast(buffer + offset)); + if (buffer && refl[index].uniform) + { + float *_data = reinterpret_cast(buffer + refl[index].ubo_offset); + _data[0] = (float)(width); + _data[1] = (float)(height); + _data[2] = 1.0f / (float)(width); + _data[3] = 1.0f / (float)(height); + } + if (refl[index].push_constant) + { + float *_data = reinterpret_cast(push.buffer.data() + (refl[index].push_constant_offset >> 2)); + _data[0] = (float)(width); + _data[1] = (float)(height); + _data[2] = 1.0f / (float)(width); + _data[3] = 1.0f / (float)(height); + } } - if (reflection.semantics[SLANG_SEMANTIC_MVP].push_constant) + if (index < reflection.semantic_textures[semantic].size() && + reflection.semantic_textures[semantic][index].texture) { - size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].push_constant_offset; - if (mvp) - memcpy(push.buffer.data() + (offset >> 2), mvp, sizeof(float) * 16); - else - build_identity_matrix(reinterpret_cast(push.buffer.data() + (offset >> 2))); + VULKAN_PASS_SET_TEXTURE(device, set, common->samplers[texture.filter][texture.mip_filter][texture.address], reflection.semantic_textures[semantic][index].binding, texture.texture.view, texture.texture.layout); } - - /* Output information */ - build_semantic_vec4(buffer, SLANG_SEMANTIC_OUTPUT, - 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)); - - build_semantic_int(buffer, SLANG_SEMANTIC_FRAME_DIRECTION, - frame_direction); - - /* Standard inputs */ - build_semantic_texture(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original); - build_semantic_texture(set, buffer, SLANG_TEXTURE_SEMANTIC_SOURCE, source); - - /* ORIGINAL_HISTORY[0] is an alias of ORIGINAL. */ - build_semantic_texture_array(set, buffer, - SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original); - - /* Parameters. */ - for (i = 0; i < filtered_parameters.size(); i++) - build_semantic_parameter(buffer, - filtered_parameters[i].semantic_index, - common->shader_preset->parameters[ - filtered_parameters[i].index].current); - - /* Previous inputs. */ - for (i = 0; i < common->original_history.size(); i++) - build_semantic_texture_array(set, buffer, - SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, i + 1, - common->original_history[i]); - - /* Previous passes. */ - for (i = 0; i < common->pass_outputs.size(); i++) - build_semantic_texture_array(set, buffer, - SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i, - common->pass_outputs[i]); - - /* Feedback FBOs. */ - for (i = 0; i < common->fb_feedback.size(); i++) - build_semantic_texture_array(set, buffer, - SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i, - common->fb_feedback[i]); - - /* LUTs. */ - for (i = 0; i < common->luts.size(); i++) - build_semantic_texture_array(set, buffer, - SLANG_TEXTURE_SEMANTIC_USER, i, - common->luts[i]->get_texture()); } void Pass::build_commands( @@ -2479,6 +1870,7 @@ void Pass::build_commands( const VkViewport &vp, const float *mvp) { + unsigned i; uint8_t *u = nullptr; current_viewport = vp; @@ -2487,8 +1879,8 @@ void Pass::build_commands( { source.texture.width, source.texture.height }); if (framebuffer && - (size.width != framebuffer->get_size().width || - size.height != framebuffer->get_size().height)) + (size.width != framebuffer->size.width || + size.height != framebuffer->size.height)) framebuffer->set_size(disposer, size); current_framebuffer_size = size; @@ -2497,13 +1889,124 @@ void Pass::build_commands( u = common->ubo_mapped + ubo_offset + sync_index * common->ubo_sync_index_stride; - build_semantics(sets[sync_index], u, mvp, original, source); + VkDescriptorSet set = sets[sync_index]; + + /* MVP */ + if (u && reflection.semantics[SLANG_SEMANTIC_MVP].uniform) + { + size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].ubo_offset; + if (mvp) + memcpy(u + offset, mvp, sizeof(float) * 16); + else /* Build identity matrix */ + { + float *data = reinterpret_cast(u + offset); + data[ 0] = 1.0f; + data[ 1] = 0.0f; + data[ 2] = 0.0f; + data[ 3] = 0.0f; + data[ 4] = 0.0f; + data[ 5] = 1.0f; + data[ 6] = 0.0f; + data[ 7] = 0.0f; + data[ 8] = 0.0f; + data[ 9] = 0.0f; + data[10] = 1.0f; + data[11] = 0.0f; + data[12] = 0.0f; + data[13] = 0.0f; + data[14] = 0.0f; + data[15] = 1.0f; + } + } + + if (reflection.semantics[SLANG_SEMANTIC_MVP].push_constant) + { + size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].push_constant_offset; + if (mvp) + memcpy(push.buffer.data() + (offset >> 2), mvp, sizeof(float) * 16); + else /* Build identity matrix */ + { + float *data = reinterpret_cast(push.buffer.data() + (offset >> + 2)); + data[ 0] = 1.0f; + data[ 1] = 0.0f; + data[ 2] = 0.0f; + data[ 3] = 0.0f; + data[ 4] = 0.0f; + data[ 5] = 1.0f; + data[ 6] = 0.0f; + data[ 7] = 0.0f; + data[ 8] = 0.0f; + data[ 9] = 0.0f; + data[10] = 1.0f; + data[11] = 0.0f; + data[12] = 0.0f; + data[13] = 0.0f; + data[14] = 0.0f; + data[15] = 1.0f; + } + } + + /* Output information */ + build_semantic_vec4(u, SLANG_SEMANTIC_OUTPUT, + current_framebuffer_size.width, + current_framebuffer_size.height); + build_semantic_vec4(u, SLANG_SEMANTIC_FINAL_VIEWPORT, + unsigned(current_viewport.width), + unsigned(current_viewport.height)); + + build_semantic_uint(u, SLANG_SEMANTIC_FRAME_COUNT, + frame_count_period + ? uint32_t(frame_count % frame_count_period) + : uint32_t(frame_count)); + + build_semantic_int(u, SLANG_SEMANTIC_FRAME_DIRECTION, + frame_direction); + + /* Standard inputs */ + build_semantic_texture(set, u, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original); + build_semantic_texture(set, u, SLANG_TEXTURE_SEMANTIC_SOURCE, source); + + /* ORIGINAL_HISTORY[0] is an alias of ORIGINAL. */ + build_semantic_texture_array(set, u, + SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original); + + /* Parameters. */ + for (i = 0; i < filtered_parameters.size(); i++) + build_semantic_parameter(u, + filtered_parameters[i].semantic_index, + common->shader_preset->parameters[ + filtered_parameters[i].index].current); + + /* Previous inputs. */ + for (i = 0; i < common->original_history.size(); i++) + build_semantic_texture_array(set, u, + SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, i + 1, + common->original_history[i]); + + /* Previous passes. */ + for (i = 0; i < common->pass_outputs.size(); i++) + build_semantic_texture_array(set, u, + SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i, + common->pass_outputs[i]); + + /* Feedback FBOs. */ + for (i = 0; i < common->fb_feedback.size(); i++) + build_semantic_texture_array(set, u, + SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i, + common->fb_feedback[i]); + + /* LUTs. */ + for (i = 0; i < common->luts.size(); i++) + build_semantic_texture_array(set, u, + SLANG_TEXTURE_SEMANTIC_USER, i, + common->luts[i]->texture); if (reflection.ubo_stage_mask) vulkan_set_uniform_buffer(device, sets[sync_index], reflection.ubo_binding, - common->ubo->get_buffer(), + common->ubo->buffer, ubo_offset + sync_index * common->ubo_sync_index_stride, reflection.ubo_size); @@ -2517,7 +2020,8 @@ void Pass::build_commands( /* Render. */ VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - framebuffer->get_image(), 1, + framebuffer->image, + 1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, @@ -2530,8 +2034,8 @@ void Pass::build_commands( rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_info.pNext = NULL; - rp_info.renderPass = framebuffer->get_render_pass(); - rp_info.framebuffer = framebuffer->get_framebuffer(); + rp_info.renderPass = framebuffer->render_pass; + rp_info.framebuffer = framebuffer->framebuffer; rp_info.renderArea.offset.x = 0; rp_info.renderArea.offset.y = 0; rp_info.renderArea.extent.width = current_framebuffer_size.width; @@ -2555,7 +2059,7 @@ void Pass::build_commands( { VkDeviceSize offset = final_pass ? 16 * sizeof(float) : 0; vkCmdBindVertexBuffers(cmd, 0, 1, - &common->vbo->get_buffer(), + &common->vbo->buffer, &offset); } @@ -2596,19 +2100,19 @@ void Pass::build_commands( vkCmdDraw(cmd, 4, 1, 0, 0); vkCmdEndRenderPass(cmd); - if (framebuffer->get_levels() > 1) + if (framebuffer->levels > 1) vulkan_framebuffer_generate_mips( - framebuffer->get_framebuffer(), - framebuffer->get_image(), - framebuffer->get_size(), + framebuffer->framebuffer, + framebuffer->image, + framebuffer->size, cmd, - framebuffer->get_levels()); + framebuffer->levels); else { /* Barrier to sync with next pass. */ VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS( cmd, - framebuffer->get_image(), + framebuffer->image, VK_REMAINING_MIP_LEVELS, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -2857,12 +2361,12 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_default( pass_info.address = GLSLANG_FILTER_CHAIN_ADDRESS_CLAMP_TO_EDGE; pass_info.max_levels = 0; - chain->set_pass_info(0, pass_info); + chain->pass_info[0] = pass_info; - chain->set_shader(0, VK_SHADER_STAGE_VERTEX_BIT, + chain->passes[0]->set_shader(VK_SHADER_STAGE_VERTEX_BIT, opaque_vert, sizeof(opaque_vert) / sizeof(uint32_t)); - chain->set_shader(0, VK_SHADER_STAGE_FRAGMENT_BIT, + chain->passes[0]->set_shader(VK_SHADER_STAGE_FRAGMENT_BIT, opaque_frag, sizeof(opaque_frag) / sizeof(uint32_t)); @@ -2891,10 +2395,10 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( std::unique_ptr chain{ new vulkan_filter_chain(tmpinfo) }; if (!chain) - goto error; + return nullptr; if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get())) - goto error; + return nullptr; shader->num_parameters = 0; @@ -2920,7 +2424,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( { RARCH_ERR("[Vulkan]: Failed to compile shader: \"%s\".\n", pass->source.path); - goto error; + return nullptr; } for (auto &meta_param : output.meta.parameters) @@ -2928,7 +2432,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( if (shader->num_parameters >= GFX_MAX_PARAMETERS) { RARCH_ERR("[Vulkan]: Exceeded maximum number of parameters (%u).\n", GFX_MAX_PARAMETERS); - goto error; + return nullptr; } auto itr = std::find_if(shader->parameters, shader->parameters + shader->num_parameters, @@ -2949,9 +2453,10 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( { RARCH_ERR("[Vulkan]: Duplicate parameters found for \"%s\", but arguments do not match.\n", itr->id); - goto error; + return nullptr; } - chain->add_parameter(i, itr - shader->parameters, meta_param.id); + chain->passes[i]->add_parameter(itr - shader->parameters, +meta_param.id); } else { @@ -2962,29 +2467,26 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( 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); + chain->passes[i]->add_parameter(shader->num_parameters, +meta_param.id); shader->num_parameters++; } } - chain->set_shader(i, - VK_SHADER_STAGE_VERTEX_BIT, + chain->passes[i]->set_shader(VK_SHADER_STAGE_VERTEX_BIT, output.vertex.data(), output.vertex.size()); - - chain->set_shader(i, - VK_SHADER_STAGE_FRAGMENT_BIT, + chain->passes[i]->set_shader(VK_SHADER_STAGE_FRAGMENT_BIT, output.fragment.data(), output.fragment.size()); - - chain->set_frame_count_period(i, pass->frame_count_mod); + chain->passes[i]->frame_count_period = pass->frame_count_mod; if (!output.meta.name.empty()) - chain->set_pass_name(i, output.meta.name.c_str()); + chain->passes[i]->pass_name = output.meta.name.c_str(); /* Preset overrides. */ if (*pass->alias) - chain->set_pass_name(i, pass->alias); + chain->passes[i]->pass_name = pass->alias; if (pass->filter == RARCH_FILTER_UNSPEC) pass_info.source_filter = filter; @@ -3110,7 +2612,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( } } - chain->set_pass_info(i, pass_info); + chain->pass_info[i] = pass_info; } if (last_pass_is_fbo) @@ -3130,34 +2632,30 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( pass_info.max_levels = 0; - chain->set_pass_info(shader->passes, pass_info); + chain->pass_info[shader->passes] = pass_info; - chain->set_shader(shader->passes, + chain->passes[shader->passes]->set_shader( VK_SHADER_STAGE_VERTEX_BIT, opaque_vert, sizeof(opaque_vert) / sizeof(uint32_t)); - - chain->set_shader(shader->passes, + chain->passes[shader->passes]->set_shader( VK_SHADER_STAGE_FRAGMENT_BIT, opaque_frag, sizeof(opaque_frag) / sizeof(uint32_t)); } - chain->set_shader_preset(std::move(shader)); + chain->common.shader_preset = std::move(std::move(shader)); if (!chain->init()) - goto error; + return nullptr; return chain.release(); - -error: - return nullptr; } struct video_shader *vulkan_filter_chain_get_preset( vulkan_filter_chain_t *chain) { - return chain->get_shader_preset(); + return chain->common.shader_preset.get(); } void vulkan_filter_chain_free( @@ -3173,7 +2671,7 @@ void vulkan_filter_chain_set_shader( const uint32_t *spirv, size_t spirv_words) { - chain->set_shader(pass, stage, spirv, spirv_words); + chain->passes[pass]->set_shader(stage, spirv, spirv_words); } void vulkan_filter_chain_set_pass_info( @@ -3181,28 +2679,54 @@ void vulkan_filter_chain_set_pass_info( unsigned pass, const struct vulkan_filter_chain_pass_info *info) { - chain->set_pass_info(pass, *info); + chain->pass_info[pass] = *info; } VkFormat vulkan_filter_chain_get_pass_rt_format( vulkan_filter_chain_t *chain, unsigned pass) { - return chain->get_pass_rt_format(pass); + return chain->pass_info[pass].rt_format; } bool vulkan_filter_chain_update_swapchain_info( vulkan_filter_chain_t *chain, const vulkan_filter_chain_swapchain_info *info) { - return chain->update_swapchain_info(*info); + unsigned num_indices; + vkDeviceWaitIdle(chain->device); + for (auto &calls : chain->deferred_calls) + { + for (auto &call : calls) + call(); + calls.clear(); + } + chain->swapchain_info = *info; + num_indices = info->num_indices; + for (auto &calls : chain->deferred_calls) + { + for (auto &call : calls) + call(); + calls.clear(); + } + chain->deferred_calls.resize(num_indices); + return chain->init(); } void vulkan_filter_chain_notify_sync_index( vulkan_filter_chain_t *chain, unsigned index) { - chain->notify_sync_index(index); + unsigned i; + auto &calls = chain->deferred_calls[index]; + for (auto &call : calls) + call(); + calls.clear(); + + chain->current_sync_index = index; + + for (i = 0; i < chain->passes.size(); i++) + chain->passes[i]->sync_index = index; } bool vulkan_filter_chain_init(vulkan_filter_chain_t *chain) @@ -3214,14 +2738,16 @@ void vulkan_filter_chain_set_input_texture( vulkan_filter_chain_t *chain, const struct vulkan_filter_chain_texture *texture) { - chain->set_input_texture(*texture); + chain->input_texture = *texture; } void vulkan_filter_chain_set_frame_count( vulkan_filter_chain_t *chain, uint64_t count) { - chain->set_frame_count(count); + unsigned i; + for (i = 0; i < chain->passes.size(); i++) + chain->passes[i]->frame_count = count; } void vulkan_filter_chain_set_frame_count_period( @@ -3229,14 +2755,16 @@ void vulkan_filter_chain_set_frame_count_period( unsigned pass, unsigned period) { - chain->set_frame_count_period(pass, period); + chain->passes[pass]->frame_count_period = period; } void vulkan_filter_chain_set_frame_direction( vulkan_filter_chain_t *chain, int32_t direction) { - chain->set_frame_direction(direction); + unsigned i; + for (i = 0; i < chain->passes.size(); i++) + chain->passes[i]->frame_direction = direction; } void vulkan_filter_chain_set_pass_name( @@ -3244,26 +2772,232 @@ void vulkan_filter_chain_set_pass_name( unsigned pass, const char *name) { - chain->set_pass_name(pass, name); + chain->passes[pass]->pass_name = name; } void vulkan_filter_chain_build_offscreen_passes( vulkan_filter_chain_t *chain, VkCommandBuffer cmd, const VkViewport *vp) { - chain->build_offscreen_passes(cmd, *vp); + unsigned i; + Texture source; + + /* First frame, make sure our history and feedback textures + * are in a clean state. */ + if (chain->require_clear) + { + unsigned i; + for (i = 0; i < chain->original_history.size(); i++) + vulkan_framebuffer_clear(chain->original_history[i]->image, cmd); + for (i = 0; i < chain->passes.size(); i++) + { + Framebuffer *fb = chain->passes[i]->fb_feedback.get(); + if (fb) + vulkan_framebuffer_clear(fb->image, cmd); + } + chain->require_clear = false; + } + + /* Update history info */ + for (i = 0; i < chain->original_history.size(); i++) + { + Texture *source = (Texture*)&chain->common.original_history[i]; + + if (!source) + continue; + + source->texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + source->texture.view = chain->original_history[i]->view; + source->texture.image = chain->original_history[i]->image; + source->texture.width = chain->original_history[i]->size.width; + source->texture.height = chain->original_history[i]->size.height; + source->filter = chain->passes.front()->pass_info.source_filter; + source->mip_filter = chain->passes.front()->pass_info.mip_filter; + source->address = chain->passes.front()->pass_info.address; + } + + /* Update feedback info? */ + if (!chain->common.fb_feedback.empty()) + { + for (i = 0; i < chain->passes.size() - 1; i++) + { + Framebuffer *fb = chain->passes[i]->fb_feedback.get(); + if (!fb) + continue; + + Texture *source = &chain->common.fb_feedback[i]; + + if (!source) + continue; + + source->texture.image = fb->image; + source->texture.view = fb->view; + source->texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + source->texture.width = fb->size.width; + source->texture.height = fb->size.height; + source->filter = chain->passes[i]->pass_info.source_filter; + source->mip_filter = chain->passes[i]->pass_info.mip_filter; + source->address = chain->passes[i]->pass_info.address; + } + } + + DeferredDisposer disposer(chain->deferred_calls[chain->current_sync_index]); + const Texture original = { + chain->input_texture, + chain->passes.front()->pass_info.source_filter, + chain->passes.front()->pass_info.mip_filter, + chain->passes.front()->pass_info.address, + }; + + source = original; + + for (i = 0; i < chain->passes.size() - 1; i++) + { + chain->passes[i]->build_commands(disposer, cmd, + original, source, *vp, nullptr); + + const Framebuffer &fb = *chain->passes[i]->framebuffer; + + source.texture.view = fb.view; + source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + source.texture.width = fb.size.width; + source.texture.height = fb.size.height; + source.filter = chain->passes[i + 1]->pass_info.source_filter; + source.mip_filter = chain->passes[i + 1]->pass_info.mip_filter; + source.address = chain->passes[i + 1]->pass_info.address; + + chain->common.pass_outputs[i] = source; + } } void vulkan_filter_chain_build_viewport_pass( vulkan_filter_chain_t *chain, VkCommandBuffer cmd, const VkViewport *vp, const float *mvp) { - chain->build_viewport_pass(cmd, *vp, mvp); + unsigned i; + Texture source; + + /* First frame, make sure our history and + * feedback textures are in a clean state. */ + if (chain->require_clear) + { + unsigned i; + for (i = 0; i < chain->original_history.size(); i++) + vulkan_framebuffer_clear(chain->original_history[i]->image, cmd); + for (i = 0; i < chain->passes.size(); i++) + { + Framebuffer *fb = chain->passes[i]->fb_feedback.get(); + if (fb) + vulkan_framebuffer_clear(fb->image, cmd); + } + chain->require_clear = false; + } + + DeferredDisposer disposer(chain->deferred_calls[chain->current_sync_index]); + const Texture original = { + chain->input_texture, + chain->passes.front()->pass_info.source_filter, + chain->passes.front()->pass_info.mip_filter, + chain->passes.front()->pass_info.address, + }; + + if (chain->passes.size() == 1) + { + source = { + chain->input_texture, + chain->passes.back()->pass_info.source_filter, + chain->passes.back()->pass_info.mip_filter, + chain->passes.back()->pass_info.address, + }; + } + else + { + const Framebuffer &fb = *chain->passes[chain->passes.size() - 2]->framebuffer; + source.texture.view = fb.view; + source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + source.texture.width = fb.size.width; + source.texture.height = fb.size.height; + source.filter = chain->passes.back()->pass_info.source_filter; + source.mip_filter = chain->passes.back()->pass_info.mip_filter; + source.address = chain->passes.back()->pass_info.address; + } + + chain->passes.back()->build_commands(disposer, cmd, + original, source, *vp, mvp); + + /* For feedback FBOs, swap current and previous. */ + for (i = 0; i < chain->passes.size(); i++) + { + if (chain->passes[i]->fb_feedback) + swap(chain->passes[i]->framebuffer, chain->passes[i]->fb_feedback); + } } void vulkan_filter_chain_end_frame( vulkan_filter_chain_t *chain, VkCommandBuffer cmd) { - chain->end_frame(cmd); + /* If we need to keep old frames, copy it after fragment is complete. + * TODO: We can improve pipelining by figuring out which + * pass is the last that reads from + * the history and dispatch the copy earlier. */ + if (!chain->original_history.empty()) + { + DeferredDisposer disposer(chain->deferred_calls[chain->current_sync_index]); + std::unique_ptr tmp; + VkImageLayout src_layout = chain->input_texture.layout; + + /* Transition input texture to something appropriate. */ + if (chain->input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) + { + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + chain->input_texture.image, + VK_REMAINING_MIP_LEVELS, + chain->input_texture.layout, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + 0, + VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + + src_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } + + std::unique_ptr &back = chain->original_history.back(); + swap(back, tmp); + + if (chain->input_texture.width != tmp->size.width || + chain->input_texture.height != tmp->size.height || + (chain->input_texture.format != VK_FORMAT_UNDEFINED + && chain->input_texture.format != tmp->format)) + tmp->set_size(disposer, { chain->input_texture.width, + chain->input_texture.height }, chain->input_texture.format); + + vulkan_framebuffer_copy(tmp->image, tmp->size, + cmd, chain->input_texture.image, src_layout); + + /* Transition input texture back. */ + if (chain->input_texture.layout != VK_IMAGE_LAYOUT_GENERAL) + { + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + chain->input_texture.image, + VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + chain->input_texture.layout, + 0, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + } + + /* Should ring buffer, but we don't have *that* many passes. */ + move_backward(begin(chain->original_history), end(chain->original_history) + - 1, end(chain->original_history)); + swap(chain->original_history.front(), tmp); + } }