diff --git a/gfx/drivers_shader/shader_gl3.cpp b/gfx/drivers_shader/shader_gl3.cpp index 48f3724e9e..2e18355b49 100644 --- a/gfx/drivers_shader/shader_gl3.cpp +++ b/gfx/drivers_shader/shader_gl3.cpp @@ -37,6 +37,26 @@ #include "../../verbosity.h" #include "../../msg_hash.h" +static void gl3_build_default_matrix(float *data) +{ + data[0] = 2.0f; + data[1] = 0.0f; + data[2] = 0.0f; + data[3] = 0.0f; + data[4] = 0.0f; + data[5] = 2.0f; + data[6] = 0.0f; + data[7] = 0.0f; + data[8] = 0.0f; + data[9] = 0.0f; + data[10] = 2.0f; + data[11] = 0.0f; + data[12] = -1.0f; + data[13] = -1.0f; + data[14] = 0.0f; + data[15] = 1.0f; +} + GLuint gl3_cross_compile_program( const uint32_t *vertex, size_t vertex_size, const uint32_t *fragment, size_t fragment_size, @@ -1400,25 +1420,8 @@ void Pass::build_semantics(uint8_t *buffer, memcpy(buffer + offset, mvp, sizeof(float) * 16); else - { - float *mvp = reinterpret_cast(buffer + offset); - mvp[0] = 2.0f; - mvp[1] = 0.0f; - mvp[2] = 0.0f; - mvp[3] = 0.0f; - mvp[4] = 0.0f; - mvp[5] = 2.0f; - mvp[6] = 0.0f; - mvp[7] = 0.0f; - mvp[8] = 0.0f; - mvp[9] = 0.0f; - mvp[10] = 2.0f; - mvp[11] = 0.0f; - mvp[12] = -1.0f; - mvp[13] = -1.0f; - mvp[14] = 0.0f; - mvp[15] = 1.0f; - } + gl3_build_default_matrix(reinterpret_cast( + buffer + offset)); } if (reflection.semantics[SLANG_SEMANTIC_MVP].push_constant) @@ -1430,25 +1433,8 @@ void Pass::build_semantics(uint8_t *buffer, memcpy(push_constant_buffer.data() + offset, mvp, sizeof(float) * 16); else - { - float *mvp = reinterpret_cast(push_constant_buffer.data() + offset); - mvp[0] = 2.0f; - mvp[1] = 0.0f; - mvp[2] = 0.0f; - mvp[3] = 0.0f; - mvp[4] = 0.0f; - mvp[5] = 2.0f; - mvp[6] = 0.0f; - mvp[7] = 0.0f; - mvp[8] = 0.0f; - mvp[9] = 0.0f; - mvp[10] = 2.0f; - mvp[11] = 0.0f; - mvp[12] = -1.0f; - mvp[13] = -1.0f; - mvp[14] = 0.0f; - mvp[15] = 1.0f; - } + gl3_build_default_matrix(reinterpret_cast( + push_constant_buffer.data() + offset)); } /* Output information */ @@ -1681,6 +1667,7 @@ private: bool init_alias(); std::vector> original_history; bool require_clear = false; + void clear_history_and_feedback(); void update_feedback_info(); void update_history_info(); }; @@ -1740,18 +1727,7 @@ void gl3_filter_chain::build_offscreen_passes(const gl3_viewport &vp) * are in a clean state. */ if (require_clear) { - unsigned i; - for (i = 0; i < original_history.size(); i++) - { - if (original_history[i]->is_complete()) - gl3_framebuffer_clear(original_history[i]->get_framebuffer()); - } - for (i = 0; i < passes.size(); i++) - { - gl3_shader::Framebuffer *fb = passes[i]->get_feedback_framebuffer(); - if (fb && fb->is_complete()) - gl3_framebuffer_clear(fb->get_framebuffer()); - } + clear_history_and_feedback(); require_clear = false; } @@ -1826,18 +1802,7 @@ void gl3_filter_chain::build_viewport_pass( * feedback textures are in a clean state. */ if (require_clear) { - unsigned i; - for (i = 0; i < original_history.size(); i++) - { - if (original_history[i]->is_complete()) - gl3_framebuffer_clear(original_history[i]->get_framebuffer()); - } - for (i = 0; i < passes.size(); i++) - { - gl3_shader::Framebuffer *fb = passes[i]->get_feedback_framebuffer(); - if (fb && fb->is_complete()) - gl3_framebuffer_clear(fb->get_framebuffer()); - } + clear_history_and_feedback(); require_clear = false; } @@ -2074,6 +2039,22 @@ bool gl3_filter_chain::init() return true; } +void gl3_filter_chain::clear_history_and_feedback() +{ + unsigned i; + for (i = 0; i < original_history.size(); i++) + { + if (original_history[i]->is_complete()) + gl3_framebuffer_clear(original_history[i]->get_framebuffer()); + } + for (i = 0; i < passes.size(); i++) + { + gl3_shader::Framebuffer *fb = passes[i]->get_feedback_framebuffer(); + if (fb && fb->is_complete()) + gl3_framebuffer_clear(fb->get_framebuffer()); + } +} + void gl3_filter_chain::set_input_texture( const gl3_filter_chain_texture &texture) { diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index 05ba70c46c..409a42f4f9 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -46,6 +46,293 @@ static const uint32_t opaque_frag[] = #include "../drivers/vulkan_shaders/opaque.frag.inc" ; +static void vulkan_initialize_render_pass(VkDevice device, VkFormat format, + VkRenderPass *render_pass) +{ + VkAttachmentReference color_ref; + VkRenderPassCreateInfo rp_info; + VkAttachmentDescription attachment; + VkSubpassDescription subpass = {0}; + + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.flags = 0; + rp_info.attachmentCount = 1; + rp_info.pAttachments = &attachment; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 0; + rp_info.pDependencies = NULL; + + color_ref.attachment = 0; + color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + /* We will always write to the entire framebuffer, + * so we don't really need to clear. */ + attachment.flags = 0; + attachment.format = format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_ref; + + vkCreateRenderPass(device, &rp_info, NULL, render_pass); +} + +static void vulkan_framebuffer_clear(VkImage image, VkCommandBuffer cmd) +{ + VkClearColorValue color; + VkImageSubresourceRange range; + + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + image, + VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + + color.float32[0] = 0.0f; + color.float32[1] = 0.0f; + color.float32[2] = 0.0f; + color.float32[3] = 0.0f; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = 1; + range.baseArrayLayer = 0; + range.layerCount = 1; + + vkCmdClearColorImage(cmd, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &color, + 1, + &range); + + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + image, + VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); +} + +static void vulkan_framebuffer_generate_mips( + VkFramebuffer framebuffer, + VkImage image, + struct Size2D size, + VkCommandBuffer cmd, + unsigned levels + ) +{ + unsigned i; + /* This is run every frame, so make sure + * we aren't opting into the "lazy" way of doing this. :) */ + VkImageMemoryBarrier barriers[2] = { + { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }, + { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }, + }; + + /* First, transfer the input mip level to TRANSFER_SRC_OPTIMAL. + * This should allow the surface to stay compressed. + * All subsequent mip-layers are now transferred into DST_OPTIMAL from + * UNDEFINED at this point. + */ + + /* Input */ + barriers[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[0].image = image; + barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barriers[0].subresourceRange.baseMipLevel = 0; + barriers[0].subresourceRange.levelCount = 1; + barriers[0].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* The rest of the mip chain */ + barriers[1].srcAccessMask = 0; + barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barriers[1].image = image; + barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barriers[1].subresourceRange.baseMipLevel = 1; + barriers[1].subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + barriers[1].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + vkCmdPipelineBarrier(cmd, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + false, + 0, + NULL, + 0, + NULL, + 2, + barriers); + + for (i = 1; i < levels; i++) + { + unsigned src_width, src_height, target_width, target_height; + VkImageBlit blit_region = {{0}}; + + /* For subsequent passes, we have to transition + * from DST_OPTIMAL to SRC_OPTIMAL, + * but only do so one mip-level at a time. */ + if (i > 1) + { + barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barriers[0].subresourceRange.baseMipLevel = i - 1; + barriers[0].subresourceRange.levelCount = 1; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + vkCmdPipelineBarrier(cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + false, + 0, + NULL, + 0, + NULL, + 1, + barriers); + } + + src_width = MAX(size.width >> (i - 1), 1u); + src_height = MAX(size.height >> (i - 1), 1u); + target_width = MAX(size.width >> i, 1u); + target_height = MAX(size.height >> i, 1u); + + blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit_region.srcSubresource.mipLevel = i - 1; + blit_region.srcSubresource.baseArrayLayer = 0; + blit_region.srcSubresource.layerCount = 1; + blit_region.dstSubresource = blit_region.srcSubresource; + blit_region.dstSubresource.mipLevel = i; + blit_region.srcOffsets[1].x = src_width; + blit_region.srcOffsets[1].y = src_height; + blit_region.srcOffsets[1].z = 1; + blit_region.dstOffsets[1].x = target_width; + blit_region.dstOffsets[1].y = target_height; + blit_region.dstOffsets[1].z = 1; + + vkCmdBlitImage(cmd, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit_region, VK_FILTER_LINEAR); + } + + /* We are now done, and we have all mip-levels except + * the last in TRANSFER_SRC_OPTIMAL, + * and the last one still on TRANSFER_DST_OPTIMAL, + * so do a final barrier which + * moves everything to SHADER_READ_ONLY_OPTIMAL in + * one go along with the execution barrier to next pass. + * Read-to-read memory barrier, so only need execution + * barrier for first transition. + */ + barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[0].subresourceRange.baseMipLevel = 0; + barriers[0].subresourceRange.levelCount = levels - 1; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + /* This is read-after-write barrier. */ + barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[1].subresourceRange.baseMipLevel = levels - 1; + barriers[1].subresourceRange.levelCount = 1; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + vkCmdPipelineBarrier(cmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + false, + 0, + NULL, + 0, + NULL, + 2, barriers); + + /* Next pass will wait for ALL_GRAPHICS_BIT, and since + * we have dstStage as FRAGMENT_SHADER, + * the dependency chain will ensure we don't start + * next pass until the mipchain is complete. */ +} + +static void vulkan_framebuffer_copy(VkImage image, + struct Size2D size, + VkCommandBuffer cmd, + VkImage src_image, VkImageLayout src_layout) +{ + VkImageCopy region; + + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, image,VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.mipLevel = 0; + region.srcSubresource.baseArrayLayer = 0; + region.srcSubresource.layerCount = 1; + region.srcOffset.x = 0; + region.srcOffset.y = 0; + region.srcOffset.z = 0; + region.dstSubresource = region.srcSubresource; + region.dstOffset.x = 0; + region.dstOffset.y = 0; + region.dstOffset.z = 0; + region.extent.width = size.width; + region.extent.height = size.height; + region.extent.depth = 1; + + vkCmdCopyImage(cmd, + src_image, src_layout, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion); + + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + image, + VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); +} + struct Texture { vulkan_filter_chain_texture texture; @@ -58,6 +345,13 @@ class DeferredDisposer { public: DeferredDisposer(std::vector> &calls) : calls(calls) {} + + void defer(std::function func) + { + calls.push_back(std::move(func)); + } + + private: std::vector> &calls; }; @@ -69,9 +363,16 @@ 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; @@ -97,6 +398,12 @@ 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; @@ -117,6 +424,18 @@ class Framebuffer Framebuffer(Framebuffer&&) = delete; void operator=(Framebuffer&&) = delete; + 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; @@ -185,6 +504,59 @@ 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, + const vulkan_filter_chain_swapchain_info &swapchain, + const vulkan_filter_chain_pass_info &info); + + void set_shader(VkShaderStageFlags stage, + const uint32_t *spirv, + size_t spirv_words); + + bool build(); + bool init_feedback(); + + void build_commands( + DeferredDisposer &disposer, + VkCommandBuffer cmd, + const Texture &original, + const Texture &source, + 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; @@ -192,6 +564,9 @@ class Pass unsigned sync_index; bool final_pass; + Size2D get_output_size(const Size2D &original_size, + const Size2D &max_source) const; + VkPipeline pipeline = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; @@ -210,7 +585,35 @@ 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, + slang_texture_semantic semantic, unsigned index, const Texture &texture); uint64_t frame_count = 0; int32_t frame_direction = 1; @@ -245,6 +648,44 @@ 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; @@ -261,995 +702,25 @@ struct vulkan_filter_chain vulkan_filter_chain_swapchain_info swapchain_info; unsigned current_sync_index; + void flush(); + + void set_num_passes(unsigned passes); + void execute_deferred(); + 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 clear_history_and_feedback(VkCommandBuffer cmd); + void update_feedback_info(); + void update_history_info(); }; -static bool vulkan_pass_build(Pass *pass) -{ - unsigned i; - unsigned j = 0; - std::unordered_map semantic_map; - - pass->framebuffer.reset(); - pass->fb_feedback.reset(); - - if (!pass->final_pass) - pass->framebuffer = std::unique_ptr( - new Framebuffer(pass->device, - pass->memory_properties, - pass->current_framebuffer_size, - pass->pass_info.rt_format, - pass->pass_info.max_levels)); - - for (i = 0; i < pass->parameters.size(); i++) - { - if (!slang_set_unique_map( - semantic_map, pass->parameters[i].id, - slang_semantic_map{ SLANG_SEMANTIC_FLOAT_PARAMETER, j })) - return false; - j++; - } - - pass->reflection = slang_reflection{}; - pass->reflection.pass_number = pass->pass_number; - pass->reflection.texture_semantic_map = - &pass->common->texture_semantic_map; - pass->reflection.texture_semantic_uniform_map = - &pass->common->texture_semantic_uniform_map; - pass->reflection.semantic_map = &semantic_map; - - if (!slang_reflect_spirv( - pass->vertex_shader, - pass->fragment_shader, &pass->reflection)) - return false; - - /* Filter out parameters which we will never use anyways. */ - pass->filtered_parameters.clear(); - - for (i = 0; - i < pass->reflection.semantic_float_parameters.size(); i++) - { - if (pass->reflection.semantic_float_parameters[i].uniform || - pass->reflection.semantic_float_parameters[i].push_constant) - pass->filtered_parameters.push_back(pass->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 = {}; - VkDescriptorSetLayoutCreateInfo set_layout_info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - VkPipelineLayoutCreateInfo layout_info = { - VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - VkDescriptorPoolCreateInfo pool_info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - VkDescriptorSetAllocateInfo alloc_info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - - /* Main UBO. */ - VkShaderStageFlags ubo_mask = 0; - - if (pass->reflection.ubo_stage_mask & SLANG_STAGE_VERTEX_MASK) - ubo_mask |= VK_SHADER_STAGE_VERTEX_BIT; - if (pass->reflection.ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK) - ubo_mask |= VK_SHADER_STAGE_FRAGMENT_BIT; - - if (ubo_mask != 0) - { - bindings.push_back({ pass->reflection.ubo_binding, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, - ubo_mask, nullptr }); - desc_counts.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - pass->num_sync_indices }); - } - - /* Semantic textures. */ - for (auto &semantic : pass->reflection.semantic_textures) - { - for (auto &texture : semantic) - { - VkShaderStageFlags stages = 0; - - if (!texture.texture) - continue; - - if (texture.stage_mask & SLANG_STAGE_VERTEX_MASK) - stages |= VK_SHADER_STAGE_VERTEX_BIT; - if (texture.stage_mask & SLANG_STAGE_FRAGMENT_MASK) - stages |= VK_SHADER_STAGE_FRAGMENT_BIT; - - bindings.push_back({ texture.binding, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, - stages, nullptr }); - desc_counts.push_back({ - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - pass->num_sync_indices }); - } - } - - set_layout_info.bindingCount = bindings.size(); - set_layout_info.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(pass->device, - &set_layout_info, NULL, &pass->set_layout) != VK_SUCCESS) - return false; - - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = &pass->set_layout; - - /* Push constants */ - if ( pass->reflection.push_constant_stage_mask - && pass->reflection.push_constant_size) - { - if (pass->reflection.push_constant_stage_mask - & SLANG_STAGE_VERTEX_MASK) - push_range.stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; - if (pass->reflection.push_constant_stage_mask - & SLANG_STAGE_FRAGMENT_MASK) - push_range.stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; - -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan]: Push Constant Block: %u bytes.\n", - (unsigned int)pass->reflection.push_constant_size); -#endif - - layout_info.pushConstantRangeCount = 1; - layout_info.pPushConstantRanges = &push_range; - pass->push.buffer.resize((pass->reflection.push_constant_size - + sizeof(uint32_t) - 1) / sizeof(uint32_t)); - } - - pass->push.stages = push_range.stageFlags; - push_range.size = pass->reflection.push_constant_size; - - if (vkCreatePipelineLayout(pass->device, - &layout_info, NULL, &pass->pipeline_layout) != VK_SUCCESS) - return false; - - pool_info.maxSets = pass->num_sync_indices; - pool_info.poolSizeCount = desc_counts.size(); - pool_info.pPoolSizes = desc_counts.data(); - if (vkCreateDescriptorPool(pass->device, - &pool_info, nullptr, &pass->pool) != VK_SUCCESS) - return false; - - alloc_info.descriptorPool = pass->pool; - alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &pass->set_layout; - - pass->sets.resize(pass->num_sync_indices); - - for (i = 0; i < pass->num_sync_indices; i++) - vkAllocateDescriptorSets(pass->device, - &alloc_info, &pass->sets[i]); - - /* Input assembly */ - input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - - /* VAO state */ - attributes[0].location = 0; - attributes[0].binding = 0; - attributes[0].format = VK_FORMAT_R32G32_SFLOAT; - attributes[0].offset = 0; - attributes[1].location = 1; - attributes[1].binding = 0; - attributes[1].format = VK_FORMAT_R32G32_SFLOAT; - attributes[1].offset = 2 * sizeof(float); - - binding.binding = 0; - binding.stride = 4 * sizeof(float); - binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - vertex_input.vertexBindingDescriptionCount = 1; - vertex_input.pVertexBindingDescriptions = &binding; - vertex_input.vertexAttributeDescriptionCount = 2; - vertex_input.pVertexAttributeDescriptions = attributes; - - /* Raster state */ - raster.polygonMode = VK_POLYGON_MODE_FILL; - raster.cullMode = VK_CULL_MODE_NONE; - raster.frontFace = - VK_FRONT_FACE_COUNTER_CLOCKWISE; - raster.depthClampEnable = false; - raster.rasterizerDiscardEnable = false; - raster.depthBiasEnable = false; - raster.lineWidth = 1.0f; - - /* Blend state */ - blend_attachment.blendEnable = false; - blend_attachment.colorWriteMask = 0xf; - blend.attachmentCount = 1; - blend.pAttachments = &blend_attachment; - - /* Viewport state */ - viewport.viewportCount = 1; - viewport.scissorCount = 1; - - /* Depth-stencil state */ - depth_stencil.depthTestEnable = false; - depth_stencil.depthWriteEnable = false; - depth_stencil.depthBoundsTestEnable = false; - depth_stencil.stencilTestEnable = false; - depth_stencil.minDepthBounds = 0.0f; - depth_stencil.maxDepthBounds = 1.0f; - - /* Multisample state */ - multisample.rasterizationSamples = - VK_SAMPLE_COUNT_1_BIT; - - /* Dynamic state */ - dynamic.pDynamicStates = dynamics; - dynamic.dynamicStateCount = ARRAY_SIZE(dynamics); - - /* Shaders */ - module_info.codeSize = pass->vertex_shader.size() - * sizeof(uint32_t); - module_info.pCode = pass->vertex_shader.data(); - shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - shader_stages[0].pName = "main"; - vkCreateShaderModule(pass->device, &module_info, NULL, - &shader_stages[0].module); - - module_info.codeSize = pass->fragment_shader.size() - * sizeof(uint32_t); - module_info.pCode = pass->fragment_shader.data(); - shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shader_stages[1].pName = "main"; - vkCreateShaderModule(pass->device, &module_info, NULL, - &shader_stages[1].module); - - pipe.stageCount = 2; - pipe.pStages = shader_stages; - pipe.pVertexInputState = &vertex_input; - pipe.pInputAssemblyState = &input_assembly; - pipe.pRasterizationState = &raster; - pipe.pColorBlendState = &blend; - pipe.pMultisampleState = &multisample; - pipe.pViewportState = &viewport; - pipe.pDepthStencilState = &depth_stencil; - pipe.pDynamicState = &dynamic; - pipe.renderPass = pass->final_pass - ? pass->swapchain_render_pass - : pass->framebuffer->render_pass; - pipe.layout = pass->pipeline_layout; - - if (vkCreateGraphicsPipelines(pass->device, - pass->cache, 1, &pipe, - NULL, &pass->pipeline) != VK_SUCCESS) - { - vkDestroyShaderModule(pass->device, - shader_stages[0].module, NULL); - vkDestroyShaderModule(pass->device, - shader_stages[1].module, NULL); - return false; - } - - vkDestroyShaderModule(pass->device, shader_stages[0].module, NULL); - vkDestroyShaderModule(pass->device, shader_stages[1].module, NULL); - return true; -} - -static void vulkan_pass_build_semantic_texture(Pass *pass, - VkDescriptorSet set, uint8_t *buffer, - slang_texture_semantic semantic, const Texture &texture) -{ - unsigned width = texture.texture.width; - unsigned height = texture.texture.height; - unsigned index = 0; - auto &refl = pass->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(pass->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 (pass->reflection.semantic_textures[semantic][0].texture) - { - VULKAN_PASS_SET_TEXTURE( - pass->device, - set, - pass->common->samplers[texture.filter][texture.mip_filter][texture.address], - pass->reflection.semantic_textures[semantic][0].binding, - texture.texture.view, - texture.texture.layout); - } -} - -static void vulkan_pass_build_semantic_texture_array(Pass *pass, - VkDescriptorSet set, uint8_t *buffer, - slang_texture_semantic semantic, unsigned index, const Texture &texture) -{ - auto &refl = pass->reflection.semantic_textures[semantic]; - unsigned width = texture.texture.width; - unsigned height = texture.texture.height; - - 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(pass->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 (index < pass->reflection.semantic_textures[semantic].size() && - pass->reflection.semantic_textures[semantic][index].texture) - { - VULKAN_PASS_SET_TEXTURE( - pass->device, - set, - pass->common->samplers[texture.filter][texture.mip_filter][texture.address], - pass->reflection.semantic_textures[semantic][index].binding, - texture.texture.view, - texture.texture.layout); - } -} - -static void vulkan_framebuffer_generate_mips( - VkFramebuffer framebuffer, - VkImage image, - struct Size2D size, - VkCommandBuffer cmd, - unsigned levels - ) -{ - unsigned i; - /* This is run every frame, so make sure - * we aren't opting into the "lazy" way of doing this. :) */ - VkImageMemoryBarrier barriers[2]; - - /* First, transfer the input mip level to TRANSFER_SRC_OPTIMAL. - * This should allow the surface to stay compressed. - * All subsequent mip-layers are now transferred into DST_OPTIMAL from - * UNDEFINED at this point. - */ - - /* Input */ - barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barriers[0].pNext = NULL; - barriers[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barriers[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barriers[0].image = image; - barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barriers[0].subresourceRange.baseMipLevel = 0; - barriers[0].subresourceRange.levelCount = 1; - barriers[0].subresourceRange.baseArrayLayer = 0; - barriers[0].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; - - /* The rest of the mip chain */ - barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barriers[1].pNext = NULL; - barriers[1].srcAccessMask = 0; - barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barriers[1].image = image; - barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barriers[1].subresourceRange.baseMipLevel = 1; - barriers[1].subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; - barriers[0].subresourceRange.baseArrayLayer = 0; - barriers[1].subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; - - vkCmdPipelineBarrier(cmd, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - false, - 0, - NULL, - 0, - NULL, - 2, - barriers); - - /* First pass */ - { - VkImageBlit blit_region = {{0}}; - unsigned src_width = MAX(size.width, 1u); - unsigned src_height = MAX(size.height, 1u); - unsigned target_width = MAX(size.width >> 1, 1u); - unsigned target_height = MAX(size.height >> 1, 1u); - - blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit_region.srcSubresource.mipLevel = 0; - blit_region.srcSubresource.baseArrayLayer = 0; - blit_region.srcSubresource.layerCount = 1; - blit_region.dstSubresource = blit_region.srcSubresource; - blit_region.dstSubresource.mipLevel = 1; - blit_region.srcOffsets[1].x = src_width; - blit_region.srcOffsets[1].y = src_height; - blit_region.srcOffsets[1].z = 1; - blit_region.dstOffsets[1].x = target_width; - blit_region.dstOffsets[1].y = target_height; - blit_region.dstOffsets[1].z = 1; - - vkCmdBlitImage(cmd, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit_region, VK_FILTER_LINEAR); - } - - /* For subsequent passes, we have to transition - * from DST_OPTIMAL to SRC_OPTIMAL, - * but only do so one mip-level at a time. */ - - for (i = 2; i < levels; i++) - { - unsigned src_width, src_height, target_width, target_height; - VkImageBlit blit_region = {{0}}; - - barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barriers[0].subresourceRange.baseMipLevel = i - 1; - barriers[0].subresourceRange.levelCount = 1; - barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - vkCmdPipelineBarrier(cmd, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - false, - 0, - NULL, - 0, - NULL, - 1, - barriers); - - src_width = MAX(size.width >> (i - 1), 1u); - src_height = MAX(size.height >> (i - 1), 1u); - target_width = MAX(size.width >> i, 1u); - target_height = MAX(size.height >> i, 1u); - - blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit_region.srcSubresource.mipLevel = i - 1; - blit_region.srcSubresource.baseArrayLayer = 0; - blit_region.srcSubresource.layerCount = 1; - blit_region.dstSubresource = blit_region.srcSubresource; - blit_region.dstSubresource.mipLevel = i; - blit_region.srcOffsets[1].x = src_width; - blit_region.srcOffsets[1].y = src_height; - blit_region.srcOffsets[1].z = 1; - blit_region.dstOffsets[1].x = target_width; - blit_region.dstOffsets[1].y = target_height; - blit_region.dstOffsets[1].z = 1; - - vkCmdBlitImage(cmd, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit_region, VK_FILTER_LINEAR); - } - - /* We are now done, and we have all mip-levels except - * the last in TRANSFER_SRC_OPTIMAL, - * and the last one still on TRANSFER_DST_OPTIMAL, - * so do a final barrier which - * moves everything to SHADER_READ_ONLY_OPTIMAL in - * one go along with the execution barrier to next pass. - * Read-to-read memory barrier, so only need execution - * barrier for first transition. - */ - barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barriers[0].subresourceRange.baseMipLevel = 0; - barriers[0].subresourceRange.levelCount = levels - 1; - barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - /* This is read-after-write barrier. */ - barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barriers[1].subresourceRange.baseMipLevel = levels - 1; - barriers[1].subresourceRange.levelCount = 1; - barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - vkCmdPipelineBarrier(cmd, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - false, - 0, - NULL, - 0, - NULL, - 2, barriers); - - /* Next pass will wait for ALL_GRAPHICS_BIT, and since - * we have dstStage as FRAGMENT_SHADER, - * the dependency chain will ensure we don't start - * next pass until the mipchain is complete. */ -} - -static void vulkan_framebuffer_set_size(Framebuffer *_fb, - DeferredDisposer &disposer, const Size2D &size, VkFormat format) -{ - _fb->size = size; - if (format != VK_FORMAT_UNDEFINED) - _fb->format = format; - -#ifdef VULKAN_DEBUG - RARCH_LOG("[Vulkan filter chain]: Updating framebuffer size %ux%u (format: %u).\n", - size.width, size.height, (unsigned)fb->format); -#endif - - { - /* The current framebuffers, etc, might still be in use - * so defer deletion. - * We'll most likely be able to reuse the memory, - * so don't free it here. - * - * Fake lambda init captures for C++11. - */ - VkDevice d = _fb->device; - VkImage i = _fb->image; - VkImageView v = _fb->view; - VkImageView fbv = _fb->fb_view; - VkFramebuffer fb = _fb->framebuffer; - disposer.calls.push_back(std::move([=] - { - if (fb != VK_NULL_HANDLE) - vkDestroyFramebuffer(d, fb, nullptr); - if (v != VK_NULL_HANDLE) - vkDestroyImageView(d, v, nullptr); - if (fbv != VK_NULL_HANDLE) - vkDestroyImageView(d, fbv, nullptr); - if (i != VK_NULL_HANDLE) - vkDestroyImage(d, i, nullptr); - })); - } - - _fb->init(&disposer); -} - -static Size2D vulkan_pass_get_output_size(Pass *pass, - const Size2D &original, - const Size2D &source) -{ - float width = 0.0f; - float height = 0.0f; - switch (pass->pass_info.scale_type_x) - { - case GLSLANG_FILTER_CHAIN_SCALE_ORIGINAL: - width = float(original.width) * pass->pass_info.scale_x; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_SOURCE: - width = float(source.width) * pass->pass_info.scale_x; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_VIEWPORT: - width = (retroarch_get_rotation() % 2 - ? pass->current_viewport.height - : pass->current_viewport.width) - * pass->pass_info.scale_x; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_ABSOLUTE: - width = pass->pass_info.scale_x; - break; - - default: - break; - } - - switch (pass->pass_info.scale_type_y) - { - case GLSLANG_FILTER_CHAIN_SCALE_ORIGINAL: - height = float(original.height) * pass->pass_info.scale_y; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_SOURCE: - height = float(source.height) * pass->pass_info.scale_y; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_VIEWPORT: - height = (retroarch_get_rotation() % 2 - ? pass->current_viewport.width - : pass->current_viewport.height) - * pass->pass_info.scale_y; - break; - - case GLSLANG_FILTER_CHAIN_SCALE_ABSOLUTE: - height = pass->pass_info.scale_y; - break; - - default: - break; - } - - return { unsigned(roundf(width)), unsigned(roundf(height)) }; -} - -static void vulkan_pass_build_commands( - Pass *pass, - DeferredDisposer &disposer, - VkCommandBuffer cmd, - const Texture &original, - const Texture &source, - const VkViewport &vp, - const float *mvp) -{ - unsigned i; - Size2D size; - uint8_t *u = nullptr; - - pass->current_viewport = vp; - size = vulkan_pass_get_output_size(pass, - { original.texture.width, original.texture.height }, - { source.texture.width, source.texture.height }); - - if (pass->framebuffer && - (size.width != pass->framebuffer->size.width || - size.height != pass->framebuffer->size.height)) - vulkan_framebuffer_set_size(pass->framebuffer.get(), disposer, size, VK_FORMAT_UNDEFINED); - - pass->current_framebuffer_size = size; - - if (pass->reflection.ubo_stage_mask && pass->common->ubo_mapped) - u = pass->common->ubo_mapped - + pass->ubo_offset - + pass->sync_index - * pass->common->ubo_sync_index_stride; - - VkDescriptorSet set = pass->sets[pass->sync_index]; - - /* MVP */ - if (u && pass->reflection.semantics[SLANG_SEMANTIC_MVP].uniform) - { - size_t offset = pass->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 (pass->reflection.semantics[SLANG_SEMANTIC_MVP].push_constant) - { - size_t offset = pass->reflection.semantics[SLANG_SEMANTIC_MVP].push_constant_offset; - if (mvp) - memcpy(pass->push.buffer.data() + (offset >> 2), mvp, sizeof(float) * 16); - else /* Build identity matrix */ - { - float *data = reinterpret_cast(pass->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 */ - { - auto &refl = pass->reflection.semantics[SLANG_SEMANTIC_OUTPUT]; - unsigned width = pass->current_framebuffer_size.width; - unsigned height = pass->current_framebuffer_size.height; - - if (u && refl.uniform) - { - 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 - (pass->push.buffer.data() + (refl.push_constant_offset >> 2)); - _data[0] = (float)(width); - _data[1] = (float)(height); - _data[2] = 1.0f / (float)(width); - _data[3] = 1.0f / (float)(height); - } - } - { - auto &refl = pass->reflection.semantics[ - SLANG_SEMANTIC_FINAL_VIEWPORT]; - unsigned width = unsigned(pass->current_viewport.width); - unsigned height = unsigned(pass->current_viewport.height); - if (u && refl.uniform) - { - 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 - (pass->push.buffer.data() + (refl.push_constant_offset >> 2)); - _data[0] = (float)(width); - _data[1] = (float)(height); - _data[2] = 1.0f / (float)(width); - _data[3] = 1.0f / (float)(height); - } - } - - { - uint32_t value = pass->frame_count_period - ? uint32_t(pass->frame_count % pass->frame_count_period) - : uint32_t(pass->frame_count); - auto &refl = pass->reflection.semantics[SLANG_SEMANTIC_FRAME_COUNT]; - if (u && refl.uniform) - *reinterpret_cast(u + pass->reflection.semantics[SLANG_SEMANTIC_FRAME_COUNT].ubo_offset) = value; - if (refl.push_constant) - *reinterpret_cast(pass->push.buffer.data() + (refl.push_constant_offset >> 2)) = value; - } - { - auto &refl = pass->reflection.semantics[SLANG_SEMANTIC_FRAME_DIRECTION]; - if (u && refl.uniform) - *reinterpret_cast(u + pass->reflection.semantics[SLANG_SEMANTIC_FRAME_DIRECTION].ubo_offset) - = pass->frame_direction; - if (refl.push_constant) - *reinterpret_cast(pass->push.buffer.data() + - (refl.push_constant_offset >> 2)) = pass->frame_direction; - } - - /* Standard inputs */ - vulkan_pass_build_semantic_texture(pass, set, u, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original); - vulkan_pass_build_semantic_texture(pass, set, u, SLANG_TEXTURE_SEMANTIC_SOURCE, source); - - /* ORIGINAL_HISTORY[0] is an alias of ORIGINAL. */ - vulkan_pass_build_semantic_texture_array(pass, set, u, - SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original); - - /* Parameters. */ - for (i = 0; i < pass->filtered_parameters.size(); i++) - { - unsigned index = pass->filtered_parameters[i].semantic_index; - float value = pass->common->shader_preset->parameters[ - pass->filtered_parameters[i].index].current; - auto &refl = pass->reflection.semantic_float_parameters[index]; - /* We will have filtered out stale parameters. */ - if (u && refl.uniform) - *reinterpret_cast(u + refl.ubo_offset) = value; - if (refl.push_constant) - *reinterpret_cast(pass->push.buffer.data() + (refl.push_constant_offset >> 2)) = value; - } - - /* Previous inputs. */ - for (i = 0; i < pass->common->original_history.size(); i++) - vulkan_pass_build_semantic_texture_array(pass, set, u, - SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, i + 1, - pass->common->original_history[i]); - - /* Previous passes. */ - for (i = 0; i < pass->common->pass_outputs.size(); i++) - vulkan_pass_build_semantic_texture_array(pass, set, u, - SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, i, - pass->common->pass_outputs[i]); - - /* Feedback FBOs. */ - for (i = 0; i < pass->common->fb_feedback.size(); i++) - vulkan_pass_build_semantic_texture_array(pass, set, u, - SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, i, - pass->common->fb_feedback[i]); - - /* LUTs. */ - for (i = 0; i < pass->common->luts.size(); i++) - vulkan_pass_build_semantic_texture_array(pass, set, u, - SLANG_TEXTURE_SEMANTIC_USER, i, - pass->common->luts[i]->texture); - - if (pass->reflection.ubo_stage_mask) - vulkan_set_uniform_buffer( - pass->device, - pass->sets[pass->sync_index], - pass->reflection.ubo_binding, - pass->common->ubo->buffer, - pass->ubo_offset + pass->sync_index * pass->common->ubo_sync_index_stride, - pass->reflection.ubo_size); - - /* The final pass is always executed inside - * another render pass since the frontend will - * want to overlay various things on top for - * the passes that end up on-screen. */ - if (!pass->final_pass) - { - VkRenderPassBeginInfo rp_info; - - /* Render. */ - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - pass->framebuffer->image, - 1, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - 0, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - rp_info.pNext = NULL; - rp_info.renderPass = pass->framebuffer->render_pass; - rp_info.framebuffer = pass->framebuffer->framebuffer; - rp_info.renderArea.offset.x = 0; - rp_info.renderArea.offset.y = 0; - rp_info.renderArea.extent.width = pass->current_framebuffer_size.width; - rp_info.renderArea.extent.height = pass->current_framebuffer_size.height; - rp_info.clearValueCount = 0; - rp_info.pClearValues = nullptr; - - vkCmdBeginRenderPass(cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); - } - - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - pass->pipeline_layout, - 0, 1, &pass->sets[pass->sync_index], 0, nullptr); - - if (pass->push.stages != 0) - vkCmdPushConstants(cmd, pass->pipeline_layout, - pass->push.stages, 0, - pass->reflection.push_constant_size, - pass->push.buffer.data()); - - { - VkDeviceSize offset = pass->final_pass ? 16 * sizeof(float) : 0; - vkCmdBindVertexBuffers(cmd, 0, 1, - &pass->common->vbo->buffer, - &offset); - } - - if (pass->final_pass) - { - const VkRect2D sci = { - { - int32_t(pass->current_viewport.x), - int32_t(pass->current_viewport.y) - }, - { - uint32_t(pass->current_viewport.width), - uint32_t(pass->current_viewport.height) - }, - }; - vkCmdSetViewport(cmd, 0, 1, &pass->current_viewport); - vkCmdSetScissor(cmd, 0, 1, &sci); - vkCmdDraw(cmd, 4, 1, 0, 0); - } - else - { - const VkViewport _vp = { - 0.0f, 0.0f, - float(pass->current_framebuffer_size.width), - float(pass->current_framebuffer_size.height), - 0.0f, 1.0f - }; - const VkRect2D sci = { - { 0, 0 }, - { - pass->current_framebuffer_size.width, - pass->current_framebuffer_size.height - }, - }; - - vkCmdSetViewport(cmd, 0, 1, &_vp); - vkCmdSetScissor(cmd, 0, 1, &sci); - vkCmdDraw(cmd, 4, 1, 0, 0); - vkCmdEndRenderPass(cmd); - - if (pass->framebuffer->levels > 1) - vulkan_framebuffer_generate_mips( - pass->framebuffer->framebuffer, - pass->framebuffer->image, - pass->framebuffer->size, - cmd, - pass->framebuffer->levels); - else - { - /* Barrier to sync with next pass. */ - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS( - cmd, - pass->framebuffer->image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - } -} - static uint32_t find_memory_type_fallback( const VkPhysicalDeviceMemoryProperties &mem_props, uint32_t device_reqs, uint32_t host_reqs) @@ -1265,72 +736,66 @@ 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) { - 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(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_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; + 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_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; } @@ -1408,13 +873,9 @@ static std::unique_ptr vulkan_filter_chain_load_lut( buffer = std::unique_ptr(new Buffer(info->device, *info->memory_properties, image.width * image.height * sizeof(uint32_t), VK_BUFFER_USAGE_TRANSFER_SRC_BIT)); - if (!buffer->mapped && vkMapMemory(buffer->device, buffer->memory, 0, - buffer->size, 0, &buffer->mapped) == VK_SUCCESS) - ptr = buffer->mapped; + ptr = buffer->map(); memcpy(ptr, image.pixels, image.width * image.height * sizeof(uint32_t)); - if (buffer->mapped) - vkUnmapMemory(buffer->device, buffer->memory); - buffer->mapped = nullptr; + buffer->unmap(); VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, tex, VK_REMAINING_MIP_LEVELS, @@ -1438,7 +899,7 @@ static std::unique_ptr vulkan_filter_chain_load_lut( region.imageExtent.depth = 1; vkCmdCopyBufferToImage(cmd, - buffer->buffer, + buffer->get_buffer(), tex, shader->mipmap ? VK_IMAGE_LAYOUT_GENERAL @@ -1563,7 +1024,7 @@ static bool vulkan_filter_chain_load_luts( goto error; } - chain->common.luts.push_back(std::move(std::move(image))); + chain->add_static_texture(std::move(image)); } vkEndCommandBuffer(cmd); @@ -1572,8 +1033,7 @@ 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); - for (i = 0; i < chain->common.luts.size(); i++) - chain->common.luts[i]->buffer.reset(); + chain->release_staging_buffers(); return true; error: @@ -1593,19 +1053,448 @@ 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 }; - swapchain_info = info.swapchain; - num_indices = info.swapchain.num_indices; + set_swapchain_info(info.swapchain); + set_num_passes(info.num_passes); +} + +vulkan_filter_chain::~vulkan_filter_chain() +{ + flush(); +} + +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) +{ + execute_deferred(); + 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) +{ + flush(); + 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::execute_deferred() +{ for (auto &calls : deferred_calls) { for (auto &call : calls) call(); calls.clear(); } - deferred_calls.resize(num_indices); - num_passes = info.num_passes; +} + +void vulkan_filter_chain::flush() +{ + vkDeviceWaitIdle(device); + execute_deferred(); +} + +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; + if (common.fb_feedback.empty()) + return; + + 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) + { + clear_history_and_feedback(cmd); + require_clear = false; + } + + update_history_info(); + 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) + { + clear_history_and_feedback(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 && !passes[i]->init_feedback()) + return false; + + if (use_feedback) + RARCH_LOG("[Vulkan filter chain]: Using framebuffer feedback for pass #%u.\n", i); + } + + 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() +{ + unsigned i, j; + 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(); + if (name.empty()) + continue; + + j = &passes[i] - passes.data(); + + if (!slang_set_unique_map( + common.texture_semantic_map, name, + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, j })) + return false; + + if (!slang_set_unique_map( + common.texture_semantic_uniform_map, name + "Size", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, j })) + return false; + + if (!slang_set_unique_map( + common.texture_semantic_map, name + "Feedback", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, j })) + return false; + + if (!slang_set_unique_map( + common.texture_semantic_uniform_map, name + "FeedbackSize", + slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, j })) + return false; + } + + for (i = 0; i < common.luts.size(); i++) + { + j = &common.luts[i] - common.luts.data(); + if (!slang_set_unique_map( + common.texture_semantic_map, + common.luts[i]->get_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", + 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); @@ -1613,20 +1502,140 @@ vulkan_filter_chain::vulkan_filter_chain( { 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; + passes.back()->set_common_resources(&common); + passes.back()->set_pass_number(i); } } -vulkan_filter_chain::~vulkan_filter_chain() +void vulkan_filter_chain::set_shader( + unsigned pass, + VkShaderStageFlags stage, + const uint32_t *spirv, + size_t spirv_words) { - vkDeviceWaitIdle(device); - for (auto &calls : deferred_calls) + 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; + + common.ubo.reset(); + common.ubo_offset = 0; + + vkGetPhysicalDeviceProperties(gpu, &props); + common.ubo_alignment = props.limits.minUniformBufferOffsetAlignment; + + /* Who knows. :) */ + if (common.ubo_alignment == 0) + common.ubo_alignment = 1; + + for (i = 0; i < passes.size(); i++) + passes[i]->allocate_buffers(); + + common.ubo_offset = + (common.ubo_offset + common.ubo_alignment - 1) & + ~(common.ubo_alignment - 1); + common.ubo_sync_index_stride = common.ubo_offset; + + if (common.ubo_offset != 0) + common.ubo = std::unique_ptr(new Buffer(device, + memory_properties, common.ubo_offset * deferred_calls.size(), + 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; + + for (i = 0; i < passes.size(); i++) { - for (auto &call : calls) - call(); - calls.clear(); +#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); +#endif + source = passes[i]->set_pass_info(max_input_size, + source, swapchain_info, pass_info[i]); + if (!passes[i]->build()) + return false; } + + 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::clear_history_and_feedback(VkCommandBuffer cmd) +{ + 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); + } +} + +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, @@ -1697,13 +1706,27 @@ Buffer::Buffer(VkDevice device, vkBindBufferMemory(device, buffer, memory, 0); } +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; +} + Buffer::~Buffer() { if (mapped) - { - vkUnmapMemory(device, memory); - mapped = nullptr; - } + unmap(); if (memory != VK_NULL_HANDLE) vkFreeMemory(device, memory, nullptr); if (buffer != VK_NULL_HANDLE) @@ -1711,6 +1734,109 @@ Buffer::~Buffer() } 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) +{ + switch (stage) + { + case VK_SHADER_STAGE_VERTEX_BIT: + vertex_shader.clear(); + vertex_shader.insert(end(vertex_shader), + spirv, spirv + spirv_words); + break; + case VK_SHADER_STAGE_FRAGMENT_BIT: + fragment_shader.clear(); + fragment_shader.insert(end(fragment_shader), + spirv, spirv + spirv_words); + break; + default: + break; + } +} + +Size2D Pass::get_output_size(const Size2D &original, + const Size2D &source) const +{ + float width = 0.0f; + float height = 0.0f; + switch (pass_info.scale_type_x) + { + case GLSLANG_FILTER_CHAIN_SCALE_ORIGINAL: + width = float(original.width) * pass_info.scale_x; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_SOURCE: + width = float(source.width) * pass_info.scale_x; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_VIEWPORT: + width = (retroarch_get_rotation() % 2 ? current_viewport.height : current_viewport.width) * pass_info.scale_x; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_ABSOLUTE: + width = pass_info.scale_x; + break; + + default: + break; + } + + switch (pass_info.scale_type_y) + { + case GLSLANG_FILTER_CHAIN_SCALE_ORIGINAL: + height = float(original.height) * pass_info.scale_y; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_SOURCE: + height = float(source.height) * pass_info.scale_y; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_VIEWPORT: + height = (retroarch_get_rotation() % 2 ? current_viewport.width : current_viewport.height) * pass_info.scale_y; + break; + + case GLSLANG_FILTER_CHAIN_SCALE_ABSOLUTE: + height = pass_info.scale_y; + break; + + default: + break; + } + + return { unsigned(roundf(width)), unsigned(roundf(height)) }; +} + +Size2D Pass::set_pass_info( + const Size2D &max_original, + const Size2D &max_source, + const vulkan_filter_chain_swapchain_info &swapchain, + const vulkan_filter_chain_pass_info &info) +{ + clear_vk(); + + current_viewport = swapchain.viewport; + pass_info = info; + + num_sync_indices = swapchain.num_indices; + sync_index = 0; + + current_framebuffer_size = get_output_size(max_original, max_source); + swapchain_render_pass = swapchain.render_pass; + + return current_framebuffer_size; +} + +void Pass::clear_vk() { if (pool != VK_NULL_HANDLE) vkDestroyDescriptorPool(device, pool, nullptr); @@ -1726,12 +1852,248 @@ Pass::~Pass() set_layout = VK_NULL_HANDLE; } +bool Pass::init_pipeline_layout() +{ + unsigned i; + std::vector bindings; + std::vector desc_counts; + VkPushConstantRange push_range = {}; + VkDescriptorSetLayoutCreateInfo set_layout_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + VkPipelineLayoutCreateInfo layout_info = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + VkDescriptorPoolCreateInfo pool_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + VkDescriptorSetAllocateInfo alloc_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + + /* Main UBO. */ + VkShaderStageFlags ubo_mask = 0; + + if (reflection.ubo_stage_mask & SLANG_STAGE_VERTEX_MASK) + ubo_mask |= VK_SHADER_STAGE_VERTEX_BIT; + if (reflection.ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK) + ubo_mask |= VK_SHADER_STAGE_FRAGMENT_BIT; + + if (ubo_mask != 0) + { + bindings.push_back({ reflection.ubo_binding, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, + ubo_mask, nullptr }); + desc_counts.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, num_sync_indices }); + } + + /* Semantic textures. */ + for (auto &semantic : reflection.semantic_textures) + { + for (auto &texture : semantic) + { + VkShaderStageFlags stages = 0; + + if (!texture.texture) + continue; + + if (texture.stage_mask & SLANG_STAGE_VERTEX_MASK) + stages |= VK_SHADER_STAGE_VERTEX_BIT; + if (texture.stage_mask & SLANG_STAGE_FRAGMENT_MASK) + stages |= VK_SHADER_STAGE_FRAGMENT_BIT; + + bindings.push_back({ texture.binding, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, + stages, nullptr }); + desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sync_indices }); + } + } + + set_layout_info.bindingCount = bindings.size(); + set_layout_info.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(device, + &set_layout_info, NULL, &set_layout) != VK_SUCCESS) + return false; + + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = &set_layout; + + /* Push constants */ + if (reflection.push_constant_stage_mask && reflection.push_constant_size) + { + if (reflection.push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK) + push_range.stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; + if (reflection.push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK) + push_range.stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; + +#ifdef VULKAN_DEBUG + RARCH_LOG("[Vulkan]: Push Constant Block: %u bytes.\n", (unsigned int)reflection.push_constant_size); +#endif + + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = &push_range; + push.buffer.resize((reflection.push_constant_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)); + } + + push.stages = push_range.stageFlags; + push_range.size = reflection.push_constant_size; + + if (vkCreatePipelineLayout(device, + &layout_info, NULL, &pipeline_layout) != VK_SUCCESS) + return false; + + pool_info.maxSets = num_sync_indices; + pool_info.poolSizeCount = desc_counts.size(); + pool_info.pPoolSizes = desc_counts.data(); + if (vkCreateDescriptorPool(device, &pool_info, nullptr, &pool) != VK_SUCCESS) + return false; + + alloc_info.descriptorPool = pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &set_layout; + + sets.resize(num_sync_indices); + + 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; + + /* VAO state */ + attributes[0].location = 0; + attributes[0].binding = 0; + attributes[0].format = VK_FORMAT_R32G32_SFLOAT; + attributes[0].offset = 0; + attributes[1].location = 1; + attributes[1].binding = 0; + attributes[1].format = VK_FORMAT_R32G32_SFLOAT; + attributes[1].offset = 2 * sizeof(float); + + binding.binding = 0; + binding.stride = 4 * sizeof(float); + binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + vertex_input.vertexBindingDescriptionCount = 1; + vertex_input.pVertexBindingDescriptions = &binding; + vertex_input.vertexAttributeDescriptionCount = 2; + vertex_input.pVertexAttributeDescriptions = attributes; + + /* Raster state */ + raster.polygonMode = VK_POLYGON_MODE_FILL; + raster.cullMode = VK_CULL_MODE_NONE; + raster.frontFace = + VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster.depthClampEnable = false; + raster.rasterizerDiscardEnable = false; + raster.depthBiasEnable = false; + raster.lineWidth = 1.0f; + + /* Blend state */ + blend_attachment.blendEnable = false; + blend_attachment.colorWriteMask = 0xf; + blend.attachmentCount = 1; + blend.pAttachments = &blend_attachment; + + /* Viewport state */ + viewport.viewportCount = 1; + viewport.scissorCount = 1; + + /* Depth-stencil state */ + depth_stencil.depthTestEnable = false; + depth_stencil.depthWriteEnable = false; + depth_stencil.depthBoundsTestEnable = false; + depth_stencil.stencilTestEnable = false; + depth_stencil.minDepthBounds = 0.0f; + depth_stencil.maxDepthBounds = 1.0f; + + /* Multisample state */ + multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + /* Dynamic state */ + dynamic.pDynamicStates = dynamics; + dynamic.dynamicStateCount = sizeof(dynamics) / sizeof(dynamics[0]); + + /* Shaders */ + module_info.codeSize = vertex_shader.size() * sizeof(uint32_t); + module_info.pCode = vertex_shader.data(); + shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shader_stages[0].pName = "main"; + vkCreateShaderModule(device, &module_info, NULL, &shader_stages[0].module); + + module_info.codeSize = fragment_shader.size() * sizeof(uint32_t); + module_info.pCode = fragment_shader.data(); + shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shader_stages[1].pName = "main"; + vkCreateShaderModule(device, &module_info, NULL, &shader_stages[1].module); + + pipe.stageCount = 2; + pipe.pStages = shader_stages; + pipe.pVertexInputState = &vertex_input; + pipe.pInputAssemblyState = &input_assembly; + pipe.pRasterizationState = &raster; + pipe.pColorBlendState = &blend; + pipe.pMultisampleState = &multisample; + pipe.pViewportState = &viewport; + pipe.pDepthStencilState = &depth_stencil; + pipe.pDynamicState = &dynamic; + pipe.renderPass = final_pass ? swapchain_render_pass : + framebuffer->get_render_pass(); + pipe.layout = pipeline_layout; + + if (vkCreateGraphicsPipelines(device, + cache, 1, &pipe, NULL, &pipeline) != VK_SUCCESS) + { + vkDestroyShaderModule(device, shader_stages[0].module, NULL); + vkDestroyShaderModule(device, shader_stages[1].module, NULL); + return false; + } + + vkDestroyShaderModule(device, shader_stages[0].module, NULL); + vkDestroyShaderModule(device, shader_stages[1].module, NULL); + return true; +} + CommonResources::CommonResources(VkDevice device, const VkPhysicalDeviceMemoryProperties &memory_properties) : device(device) { unsigned i; - void *ptr = NULL; VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; /* The final pass uses an MVP designed for [0, 1] range VBO. @@ -1756,13 +2118,9 @@ CommonResources::CommonResources(VkDevice device, std::unique_ptr(new Buffer(device, memory_properties, sizeof(vbo_data), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); - if (!vbo->mapped && vkMapMemory(vbo->device, vbo->memory, 0, vbo->size, 0, - &vbo->mapped) == VK_SUCCESS) - ptr = vbo->mapped; + void *ptr = vbo->map(); memcpy(ptr, vbo_data, sizeof(vbo_data)); - if (vbo->mapped) - vkUnmapMemory(vbo->device, vbo->memory); - vbo->mapped = nullptr; + vbo->unmap(); info.mipLodBias = 0.0f; info.maxAnisotropy = 1.0f; @@ -1858,44 +2216,449 @@ CommonResources::~CommonResources() vkDestroySampler(device, k, nullptr); } -static void vulkan_initialize_render_pass(VkDevice device, VkFormat format, - VkRenderPass *render_pass) +void Pass::allocate_buffers() { - VkAttachmentReference color_ref; - VkRenderPassCreateInfo rp_info; - VkAttachmentDescription attachment; - VkSubpassDescription subpass = {0}; + 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; - rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - rp_info.pNext = NULL; - rp_info.flags = 0; - rp_info.attachmentCount = 1; - rp_info.pAttachments = &attachment; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = NULL; + /* Allocate */ + common->ubo_offset += reflection.ubo_size; + } +} - color_ref.attachment = 0; - color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; +void Pass::end_frame() +{ + if (fb_feedback) + swap(framebuffer, fb_feedback); +} - /* We will always write to the entire framebuffer, - * so we don't really need to clear. */ - attachment.flags = 0; - attachment.format = format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; +bool Pass::init_feedback() +{ + if (final_pass) + return false; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_ref; + fb_feedback = std::unique_ptr( + new Framebuffer(device, memory_properties, + current_framebuffer_size, + pass_info.rt_format, pass_info.max_levels)); + return true; +} - vkCreateRenderPass(device, &rp_info, NULL, render_pass); +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, + unsigned width, unsigned height) +{ + auto &refl = reflection.semantics[semantic]; + + if (data && refl.uniform) + { + float *_data = reinterpret_cast(data + 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 + (push.buffer.data() + (refl.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_parameter(uint8_t *data, 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 (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, + uint32_t value) +{ + auto &refl = reflection.semantics[semantic]; + + if (data && refl.uniform) + *reinterpret_cast(data + 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, + int32_t value) +{ + auto &refl = reflection.semantics[semantic]; + + if (data && refl.uniform) + *reinterpret_cast(data + 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_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); +} + +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); +} + +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) + { + 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 (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(reinterpret_cast(push.buffer.data() + (offset >> 2))); + } + + /* 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( + DeferredDisposer &disposer, + VkCommandBuffer cmd, + const Texture &original, + const Texture &source, + const VkViewport &vp, + const float *mvp) +{ + uint8_t *u = nullptr; + + current_viewport = vp; + Size2D size = get_output_size( + { original.texture.width, original.texture.height }, + { source.texture.width, source.texture.height }); + + if (framebuffer && + (size.width != framebuffer->get_size().width || + size.height != framebuffer->get_size().height)) + framebuffer->set_size(disposer, size); + + current_framebuffer_size = size; + + if (reflection.ubo_stage_mask && common->ubo_mapped) + u = common->ubo_mapped + ubo_offset + + sync_index * common->ubo_sync_index_stride; + + build_semantics(sets[sync_index], u, mvp, original, source); + + if (reflection.ubo_stage_mask) + vulkan_set_uniform_buffer(device, + sets[sync_index], + reflection.ubo_binding, + common->ubo->get_buffer(), + ubo_offset + sync_index * common->ubo_sync_index_stride, + reflection.ubo_size); + + /* The final pass is always executed inside + * another render pass since the frontend will + * want to overlay various things on top for + * the passes that end up on-screen. */ + if (!final_pass) + { + VkRenderPassBeginInfo rp_info; + + /* Render. */ + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, + framebuffer->get_image(), 1, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + 0, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + + 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.renderArea.offset.x = 0; + rp_info.renderArea.offset.y = 0; + rp_info.renderArea.extent.width = current_framebuffer_size.width; + rp_info.renderArea.extent.height = current_framebuffer_size.height; + rp_info.clearValueCount = 0; + rp_info.pClearValues = nullptr; + + vkCmdBeginRenderPass(cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); + } + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline_layout, + 0, 1, &sets[sync_index], 0, nullptr); + + if (push.stages != 0) + { + vkCmdPushConstants(cmd, pipeline_layout, + push.stages, 0, reflection.push_constant_size, + push.buffer.data()); + } + + { + VkDeviceSize offset = final_pass ? 16 * sizeof(float) : 0; + vkCmdBindVertexBuffers(cmd, 0, 1, + &common->vbo->get_buffer(), + &offset); + } + + if (final_pass) + { + const VkRect2D sci = { + { + int32_t(current_viewport.x), + int32_t(current_viewport.y) + }, + { + uint32_t(current_viewport.width), + uint32_t(current_viewport.height) + }, + }; + vkCmdSetViewport(cmd, 0, 1, ¤t_viewport); + vkCmdSetScissor(cmd, 0, 1, &sci); + } + else + { + const VkViewport _vp = { + 0.0f, 0.0f, + float(current_framebuffer_size.width), + float(current_framebuffer_size.height), + 0.0f, 1.0f + }; + const VkRect2D sci = { + { 0, 0 }, + { + current_framebuffer_size.width, + current_framebuffer_size.height + }, + }; + + vkCmdSetViewport(cmd, 0, 1, &_vp); + vkCmdSetScissor(cmd, 0, 1, &sci); + } + + vkCmdDraw(cmd, 4, 1, 0, 0); + + if (!final_pass) + { + vkCmdEndRenderPass(cmd); + + if (framebuffer->get_levels() > 1) + vulkan_framebuffer_generate_mips( + framebuffer->get_framebuffer(), + framebuffer->get_image(), + framebuffer->get_size(), + cmd, + framebuffer->get_levels()); + else + { + /* Barrier to sync with next pass. */ + VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS( + cmd, + framebuffer->get_image(), + VK_REMAINING_MIP_LEVELS, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED); + } + } } Framebuffer::Framebuffer( @@ -1909,10 +2672,8 @@ Framebuffer::Framebuffer( memory_properties(mem_props), device(device) { -#ifdef VULKAN_DEBUG RARCH_LOG("[Vulkan filter chain]: Creating framebuffer %ux%u (max %u level(s)).\n", max_size.width, max_size.height, max_levels); -#endif vulkan_initialize_render_pass(device, format, &render_pass); init(nullptr); } @@ -1963,7 +2724,7 @@ void Framebuffer::init(DeferredDisposer *disposer) { VkDevice d = device; VkDeviceMemory m = memory.memory; - disposer->calls.push_back(std::move([=] { vkFreeMemory(d, m, nullptr); })); + disposer->defer([=] { vkFreeMemory(d, m, nullptr); }); } memory.type = alloc.memoryTypeIndex; @@ -2006,6 +2767,44 @@ void Framebuffer::init(DeferredDisposer *disposer) } } +void Framebuffer::set_size(DeferredDisposer &disposer, const Size2D &size, VkFormat format) +{ + this->size = size; + if (format != VK_FORMAT_UNDEFINED) + this->format = format; + + RARCH_LOG("[Vulkan filter chain]: Updating framebuffer size %ux%u (format: %u).\n", + size.width, size.height, (unsigned)this->format); + + { + /* The current framebuffers, etc, might still be in use + * so defer deletion. + * We'll most likely be able to reuse the memory, + * so don't free it here. + * + * Fake lambda init captures for C++11. + */ + VkDevice d = device; + VkImage i = image; + VkImageView v = view; + VkImageView fbv = fb_view; + VkFramebuffer fb = framebuffer; + disposer.defer([=] + { + if (fb != VK_NULL_HANDLE) + vkDestroyFramebuffer(d, fb, nullptr); + if (v != VK_NULL_HANDLE) + vkDestroyImageView(d, v, nullptr); + if (fbv != VK_NULL_HANDLE) + vkDestroyImageView(d, fbv, nullptr); + if (i != VK_NULL_HANDLE) + vkDestroyImage(d, i, nullptr); + }); + } + + init(&disposer); +} + Framebuffer::~Framebuffer() { if (framebuffer != VK_NULL_HANDLE) @@ -2029,229 +2828,6 @@ vulkan_filter_chain_t *vulkan_filter_chain_new( return new vulkan_filter_chain(*info); } -static bool vulkan_filter_chain_initialize(vulkan_filter_chain_t *chain) -{ - unsigned i, j; - VkPhysicalDeviceProperties props; - size_t required_images = 0; - bool use_feedbacks = false; - Size2D source = chain->max_input_size; - - /* Initialize alias */ - chain->common.texture_semantic_map.clear(); - chain->common.texture_semantic_uniform_map.clear(); - - for (i = 0; i < chain->passes.size(); i++) - { - const std::string name = chain->passes[i]->pass_name; - if (name.empty()) - continue; - - j = &chain->passes[i] - chain->passes.data(); - - if (!slang_set_unique_map( - chain->common.texture_semantic_map, name, - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, j })) - return false; - - if (!slang_set_unique_map( - chain->common.texture_semantic_uniform_map, name + "Size", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT, j })) - return false; - - if (!slang_set_unique_map( - chain->common.texture_semantic_map, name + "Feedback", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, j })) - return false; - - if (!slang_set_unique_map( - chain->common.texture_semantic_uniform_map, name + "FeedbackSize", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK, j })) - return false; - } - - for (i = 0; i < chain->common.luts.size(); i++) - { - j = &chain->common.luts[i] - chain->common.luts.data(); - if (!slang_set_unique_map( - chain->common.texture_semantic_map, - chain->common.luts[i]->id, - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, j })) - return false; - - if (!slang_set_unique_map( - chain->common.texture_semantic_uniform_map, - chain->common.luts[i]->id + "Size", - slang_texture_semantic_map{ SLANG_TEXTURE_SEMANTIC_USER, j })) - return false; - } - - for (i = 0; i < chain->passes.size(); i++) - { -#ifdef VULKAN_DEBUG - const char *name = chain->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 (chain->passes[i]->pool != VK_NULL_HANDLE) - vkDestroyDescriptorPool(chain->device, chain->passes[i]->pool, nullptr); - if (chain->passes[i]->pipeline != VK_NULL_HANDLE) - vkDestroyPipeline(chain->device, chain->passes[i]->pipeline, nullptr); - if (chain->passes[i]->set_layout != VK_NULL_HANDLE) - vkDestroyDescriptorSetLayout(chain->device, chain->passes[i]->set_layout, nullptr); - if (chain->passes[i]->pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(chain->device, chain->passes[i]->pipeline_layout, nullptr); - - chain->passes[i]->pool = VK_NULL_HANDLE; - chain->passes[i]->pipeline = VK_NULL_HANDLE; - chain->passes[i]->set_layout = VK_NULL_HANDLE; - - chain->passes[i]->current_viewport = chain->swapchain_info.viewport; - chain->passes[i]->pass_info = chain->pass_info[i]; - - chain->passes[i]->num_sync_indices = chain->swapchain_info.num_indices; - chain->passes[i]->sync_index = 0; - - chain->passes[i]->current_framebuffer_size = - vulkan_pass_get_output_size(chain->passes[i].get(), - chain->max_input_size, source); - chain->passes[i]->swapchain_render_pass = chain->swapchain_info.render_pass; - - source = chain->passes[i]->current_framebuffer_size; - if (!vulkan_pass_build(chain->passes[i].get())) - return false; - } - - chain->require_clear = false; - - /* Initialize UBO (Uniform Buffer Object) */ - chain->common.ubo.reset(); - chain->common.ubo_offset = 0; - - vkGetPhysicalDeviceProperties(chain->gpu, &props); - chain->common.ubo_alignment = props.limits.minUniformBufferOffsetAlignment; - - /* Who knows. :) */ - if (chain->common.ubo_alignment == 0) - chain->common.ubo_alignment = 1; - - /* Allocate pass buffers */ - for (i = 0; i < chain->passes.size(); i++) - { - if (chain->passes[i]->reflection.ubo_stage_mask) - { - /* Align */ - chain->passes[i]->common->ubo_offset = (chain->passes[i]->common->ubo_offset + - chain->passes[i]->common->ubo_alignment - 1) & - ~(chain->passes[i]->common->ubo_alignment - 1); - chain->passes[i]->ubo_offset = chain->passes[i]->common->ubo_offset; - - /* Allocate */ - chain->passes[i]->common->ubo_offset += chain->passes[i]->reflection.ubo_size; - } - } - - chain->common.ubo_offset = - (chain->common.ubo_offset + chain->common.ubo_alignment - 1) & - ~(chain->common.ubo_alignment - 1); - chain->common.ubo_sync_index_stride = chain->common.ubo_offset; - - if (chain->common.ubo_offset != 0) - chain->common.ubo = std::unique_ptr(new Buffer(chain->device, - chain->memory_properties, - chain->common.ubo_offset * chain->deferred_calls.size(), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)); - - if (!chain->common.ubo->mapped && vkMapMemory(chain->common.ubo->device, - chain->common.ubo->memory, 0, chain->common.ubo->size, 0, &chain->common.ubo->mapped) == VK_SUCCESS) - chain->common.ubo_mapped = static_cast(chain->common.ubo->mapped); - - /* Initialize history */ - chain->original_history.clear(); - chain->common.original_history.clear(); - - for (i = 0; i < chain->passes.size(); i++) - required_images = - std::max(required_images, - chain->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--; - chain->original_history.reserve(required_images); - chain->common.original_history.resize(required_images); - - for (i = 0; i < required_images; i++) - chain->original_history.emplace_back( - new Framebuffer(chain->device, - chain->memory_properties, - chain->max_input_size, - chain->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. - */ - chain->require_clear = true; - } - - /* Initialize feedback */ - chain->common.fb_feedback.clear(); - /* Final pass cannot have feedback. */ - for (i = 0; i < chain->passes.size() - 1; i++) - { - bool use_feedback = false; - for (auto &pass : chain->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 (!chain->passes[i]->final_pass) - return false; - chain->passes[i]->fb_feedback = std::unique_ptr( - new Framebuffer( - chain->device, - chain->memory_properties, - chain->passes[i]->current_framebuffer_size, - chain->passes[i]->pass_info.rt_format, - chain->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) - { - chain->common.fb_feedback.resize(chain->passes.size() - 1); - chain->require_clear = true; - } - - chain->common.pass_outputs.resize(chain->passes.size()); - return true; -} - vulkan_filter_chain_t *vulkan_filter_chain_create_default( const struct vulkan_filter_chain_create_info *info, glslang_filter_chain_filter filter) @@ -2275,18 +2851,16 @@ 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->pass_info[0] = pass_info; + chain->set_pass_info(0, pass_info); - vulkan_filter_chain_set_shader(chain.get(), 0, - VK_SHADER_STAGE_VERTEX_BIT, + chain->set_shader(0, VK_SHADER_STAGE_VERTEX_BIT, opaque_vert, sizeof(opaque_vert) / sizeof(uint32_t)); - vulkan_filter_chain_set_shader(chain.get(), 0, - VK_SHADER_STAGE_FRAGMENT_BIT, + chain->set_shader(0, VK_SHADER_STAGE_FRAGMENT_BIT, opaque_frag, sizeof(opaque_frag) / sizeof(uint32_t)); - if (!vulkan_filter_chain_initialize(chain.get())) + if (!chain->init()) return nullptr; return chain.release(); @@ -2311,10 +2885,10 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( std::unique_ptr chain{ new vulkan_filter_chain(tmpinfo) }; if (!chain) - return nullptr; + goto error; if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get())) - return nullptr; + goto error; shader->num_parameters = 0; @@ -2340,7 +2914,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( { RARCH_ERR("[Vulkan]: Failed to compile shader: \"%s\".\n", pass->source.path); - return nullptr; + goto error; } for (auto &meta_param : output.meta.parameters) @@ -2348,7 +2922,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); - return nullptr; + goto error; } auto itr = std::find_if(shader->parameters, shader->parameters + shader->num_parameters, @@ -2369,9 +2943,9 @@ 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); - return nullptr; + goto error; } - chain->passes[i]->parameters.push_back({ meta_param.id, unsigned(itr - shader->parameters), unsigned(chain->passes[i]->parameters.size()) }); + chain->add_parameter(i, itr - shader->parameters, meta_param.id); } else { @@ -2382,27 +2956,29 @@ 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->passes[i]->parameters.push_back({ meta_param.id, shader->num_parameters, unsigned(chain->passes[i]->parameters.size()) }); + chain->add_parameter(i, shader->num_parameters, meta_param.id); shader->num_parameters++; } } - vulkan_filter_chain_set_shader(chain.get(), i, + chain->set_shader(i, VK_SHADER_STAGE_VERTEX_BIT, output.vertex.data(), output.vertex.size()); - vulkan_filter_chain_set_shader(chain.get(), i, + + chain->set_shader(i, VK_SHADER_STAGE_FRAGMENT_BIT, output.fragment.data(), output.fragment.size()); - chain->passes[i]->frame_count_period = pass->frame_count_mod; + + chain->set_frame_count_period(i, pass->frame_count_mod); if (!output.meta.name.empty()) - chain->passes[i]->pass_name = output.meta.name.c_str(); + chain->set_pass_name(i, output.meta.name.c_str()); /* Preset overrides. */ if (*pass->alias) - chain->passes[i]->pass_name = pass->alias; + chain->set_pass_name(i, pass->alias); if (pass->filter == RARCH_FILTER_UNSPEC) pass_info.source_filter = filter; @@ -2450,10 +3026,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( { pass_info.rt_format = glslang_format_to_vk( output.meta.rt_format); -#ifdef VULKAN_DEBUG RARCH_LOG("[slang]: Using render target format %s for pass output #%u.\n", glslang_format_to_string(output.meta.rt_format), i); -#endif } else #endif /* VULKAN_HDR_SWAPCHAIN */ @@ -2469,10 +3043,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( { pass_info.rt_format = glslang_format_to_vk( output.meta.rt_format); -#ifdef VULKAN_DEBUG RARCH_LOG("[slang]: Using render target format %s for pass output #%u.\n", glslang_format_to_string(output.meta.rt_format), i); -#endif } } else @@ -2486,10 +3058,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( pass_info.rt_format = glslang_format_to_vk(output.meta.rt_format); -#ifdef VULKAN_DEBUG RARCH_LOG("[slang]: Using render target format %s for pass output #%u.\n", glslang_format_to_string(output.meta.rt_format), i); -#endif switch (pass->fbo.type_x) { @@ -2528,7 +3098,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( } } - chain->pass_info[i] = pass_info; + chain->set_pass_info(i, pass_info); } if (last_pass_is_fbo) @@ -2548,32 +3118,34 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( pass_info.max_levels = 0; - chain->pass_info[shader->passes] = pass_info; + chain->set_pass_info(shader->passes, pass_info); - vulkan_filter_chain_set_shader(chain.get(), - shader->passes, + chain->set_shader(shader->passes, VK_SHADER_STAGE_VERTEX_BIT, opaque_vert, sizeof(opaque_vert) / sizeof(uint32_t)); - vulkan_filter_chain_set_shader(chain.get(), - shader->passes, + + chain->set_shader(shader->passes, VK_SHADER_STAGE_FRAGMENT_BIT, opaque_frag, sizeof(opaque_frag) / sizeof(uint32_t)); } - chain->common.shader_preset = std::move(std::move(shader)); + chain->set_shader_preset(std::move(shader)); - if (!vulkan_filter_chain_initialize(chain.get())) - return nullptr; + if (!chain->init()) + goto error; return chain.release(); + +error: + return nullptr; } struct video_shader *vulkan_filter_chain_get_preset( vulkan_filter_chain_t *chain) { - return chain->common.shader_preset.get(); + return chain->get_shader_preset(); } void vulkan_filter_chain_free( @@ -2589,91 +3161,55 @@ void vulkan_filter_chain_set_shader( const uint32_t *spirv, size_t spirv_words) { - switch (stage) - { - case VK_SHADER_STAGE_VERTEX_BIT: - chain->passes[pass]->vertex_shader.clear(); - chain->passes[pass]->vertex_shader.insert( - end(chain->passes[pass]->vertex_shader), - spirv, spirv + spirv_words); - break; - case VK_SHADER_STAGE_FRAGMENT_BIT: - chain->passes[pass]->fragment_shader.clear(); - chain->passes[pass]->fragment_shader.insert( - end(chain->passes[pass]->fragment_shader), - spirv, spirv + spirv_words); - break; - default: - break; - } + chain->set_shader(pass, stage, spirv, spirv_words); +} + +void vulkan_filter_chain_set_pass_info( + vulkan_filter_chain_t *chain, + unsigned pass, + const struct vulkan_filter_chain_pass_info *info) +{ + chain->set_pass_info(pass, *info); } VkFormat vulkan_filter_chain_get_pass_rt_format( vulkan_filter_chain_t *chain, unsigned pass) { - return chain->pass_info[pass].rt_format; + return chain->get_pass_rt_format(pass); } bool vulkan_filter_chain_update_swapchain_info( vulkan_filter_chain_t *chain, const vulkan_filter_chain_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 vulkan_filter_chain_initialize(chain); + return chain->update_swapchain_info(*info); } void vulkan_filter_chain_notify_sync_index( vulkan_filter_chain_t *chain, unsigned 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; + chain->notify_sync_index(index); } bool vulkan_filter_chain_init(vulkan_filter_chain_t *chain) { - return vulkan_filter_chain_initialize(chain); + return chain->init(); } void vulkan_filter_chain_set_input_texture( vulkan_filter_chain_t *chain, const struct vulkan_filter_chain_texture *texture) { - chain->input_texture = *texture; + chain->set_input_texture(*texture); } void vulkan_filter_chain_set_frame_count( vulkan_filter_chain_t *chain, uint64_t count) { - unsigned i; - for (i = 0; i < chain->passes.size(); i++) - chain->passes[i]->frame_count = count; + chain->set_frame_count(count); } void vulkan_filter_chain_set_frame_count_period( @@ -2681,475 +3217,41 @@ void vulkan_filter_chain_set_frame_count_period( unsigned pass, unsigned period) { - chain->passes[pass]->frame_count_period = period; + chain->set_frame_count_period(pass, period); } void vulkan_filter_chain_set_frame_direction( vulkan_filter_chain_t *chain, int32_t direction) { - unsigned i; - for (i = 0; i < chain->passes.size(); i++) - chain->passes[i]->frame_direction = direction; + chain->set_frame_direction(direction); +} + +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) { - unsigned i; - Texture source; - - /* First frame, make sure our history and feedback textures - * are in a clean state. - * Clear framebuffers. - */ - if (chain->require_clear) - { - unsigned i; - for (i = 0; i < chain->original_history.size(); i++) - { - VkClearColorValue color; - VkImageSubresourceRange range; - VkImage image = chain->original_history[i]->image; - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - color.float32[0] = 0.0f; - color.float32[1] = 0.0f; - color.float32[2] = 0.0f; - color.float32[3] = 0.0f; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = 1; - range.baseArrayLayer = 0; - range.layerCount = 1; - - vkCmdClearColorImage(cmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &color, - 1, - &range); - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - for (i = 0; i < chain->passes.size(); i++) - { - Framebuffer *fb = chain->passes[i]->fb_feedback.get(); - if (fb) - { - VkClearColorValue color; - VkImageSubresourceRange range; - VkImage image = fb->image; - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - color.float32[0] = 0.0f; - color.float32[1] = 0.0f; - color.float32[2] = 0.0f; - color.float32[3] = 0.0f; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = 1; - range.baseArrayLayer = 0; - range.layerCount = 1; - - vkCmdClearColorImage(cmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &color, - 1, - &range); - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - } - 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++) - { - vulkan_pass_build_commands(chain->passes[i].get(), - 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; - } + chain->build_offscreen_passes(cmd, *vp); } void vulkan_filter_chain_build_viewport_pass( vulkan_filter_chain_t *chain, 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. - * Clear framebuffers. - */ - if (chain->require_clear) - { - unsigned i; - for (i = 0; i < chain->original_history.size(); i++) - { - VkClearColorValue color; - VkImageSubresourceRange range; - VkImage image = chain->original_history[i]->image; - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - color.float32[0] = 0.0f; - color.float32[1] = 0.0f; - color.float32[2] = 0.0f; - color.float32[3] = 0.0f; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = 1; - range.baseArrayLayer = 0; - range.layerCount = 1; - - vkCmdClearColorImage(cmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &color, - 1, - &range); - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - for (i = 0; i < chain->passes.size(); i++) - { - Framebuffer *fb = chain->passes[i]->fb_feedback.get(); - if (fb) - { - VkClearColorValue color; - VkImageSubresourceRange range; - VkImage image = fb->image; - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - color.float32[0] = 0.0f; - color.float32[1] = 0.0f; - color.float32[2] = 0.0f; - color.float32[3] = 0.0f; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = 1; - range.baseArrayLayer = 0; - range.layerCount = 1; - - vkCmdClearColorImage(cmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &color, - 1, - &range); - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - } - 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; - } - - vulkan_pass_build_commands(chain->passes.back().get(), - 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); - } + chain->build_viewport_pass(cmd, *vp, mvp); } void vulkan_filter_chain_end_frame( vulkan_filter_chain_t *chain, 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 (!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)) - vulkan_framebuffer_set_size( - tmp.get(), disposer, - { chain->input_texture.width, - chain->input_texture.height }, - chain->input_texture.format); - - /* Copy framebuffer */ - { - VkImageCopy region; - VkImage image = tmp->image; - VkImage src_image = chain->input_texture.image; - struct Size2D size = tmp->size; - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, image,VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - - region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.srcSubresource.mipLevel = 0; - region.srcSubresource.baseArrayLayer = 0; - region.srcSubresource.layerCount = 1; - region.srcOffset.x = 0; - region.srcOffset.y = 0; - region.srcOffset.z = 0; - region.dstSubresource = region.srcSubresource; - region.dstOffset.x = 0; - region.dstOffset.y = 0; - region.dstOffset.z = 0; - region.extent.width = size.width; - region.extent.height = size.height; - region.extent.depth = 1; - - vkCmdCopyImage(cmd, - src_image, src_layout, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion); - - VULKAN_IMAGE_LAYOUT_TRANSITION_LEVELS(cmd, - image, - VK_REMAINING_MIP_LEVELS, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED); - } - - /* 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); - } + chain->end_frame(cmd); }