From 89d543c07ba80430baebcba19f9a353b25cd8ab4 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2022 11:46:11 +0300 Subject: [PATCH] Implement multisample resolve for Vulkan --- source/backends/vulkan/commands_backend.cpp | 5 +- .../backends/vulkan/framebuffer_backend.cpp | 16 +++- source/backends/vulkan/pipelinecache.cpp | 4 +- source/backends/vulkan/renderpass.cpp | 79 +++++++++++++------ source/backends/vulkan/vulkan.cpp | 1 + source/backends/vulkan/vulkan.h | 4 + 6 files changed, 80 insertions(+), 29 deletions(-) diff --git a/source/backends/vulkan/commands_backend.cpp b/source/backends/vulkan/commands_backend.cpp index 2273d919..9f53f1ce 100644 --- a/source/backends/vulkan/commands_backend.cpp +++ b/source/backends/vulkan/commands_backend.cpp @@ -252,7 +252,10 @@ void VulkanCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count void VulkanCommands::resolve_multisample() { - throw logic_error("VulkanCommands::resolve_multisample is unimplemented"); + if(!framebuffer || !framebuffer->has_resolve_attachments()) + throw invalid_operation("VulkanCommands::resolve_multisample"); + + end_render_pass(); } void VulkanCommands::begin_query(const QueryPool &, unsigned) diff --git a/source/backends/vulkan/framebuffer_backend.cpp b/source/backends/vulkan/framebuffer_backend.cpp index 5db8c356..9af8a2f7 100644 --- a/source/backends/vulkan/framebuffer_backend.cpp +++ b/source/backends/vulkan/framebuffer_backend.cpp @@ -64,8 +64,9 @@ void VulkanFramebuffer::update(unsigned mask) const if(handle) device.get_destroy_queue().destroy(handle); - VkImageView vk_attachments[FrameFormat::MAX_ATTACHMENTS] = { }; + VkImageView vk_attachments[FrameFormat::MAX_ATTACHMENTS*2] = { }; unsigned i = 0; + bool any_resolve = false; for(const Framebuffer::Attachment &a: self.attachments) { bool use_tex_view = (a.tex->view_type==VK_IMAGE_VIEW_TYPE_2D || (a.tex->view_type==VK_IMAGE_VIEW_TYPE_2D_ARRAY && a.layer<0)); @@ -109,6 +110,13 @@ void VulkanFramebuffer::update(unsigned mask) const else throw logic_error("unexpected framebuffer configuration"); + if(a.resolve) + { + a.resolve->refresh_mip_views(); + vk_attachments[self.format.size()+i] = a.resolve->mip_view_handles[0]; + any_resolve = true; + } + ++i; } @@ -119,7 +127,7 @@ void VulkanFramebuffer::update(unsigned mask) const VkFramebufferCreateInfo framebuffer_info = { }; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.renderPass = handle_cast<::VkRenderPass>(render_pass.handle); - framebuffer_info.attachmentCount = self.format.size(); + framebuffer_info.attachmentCount = self.format.size()*(1+any_resolve); framebuffer_info.pAttachments = handle_cast<::VkImageView *>(vk_attachments); framebuffer_info.width = self.width; framebuffer_info.height = self.height; @@ -134,7 +142,11 @@ void VulkanFramebuffer::update(unsigned mask) const void VulkanFramebuffer::prepare_image_layouts(bool discard) const { for(const Framebuffer::Attachment &a: static_cast(this)->attachments) + { a.tex->change_layout(a.level, get_vulkan_attachment_layout(get_components(a.tex->get_format())), (discard && a.layer<0)); + if(a.resolve) + a.resolve->change_layout(a.level, get_vulkan_attachment_layout(get_components(a.resolve->get_format())), discard); + } } void VulkanFramebuffer::set_debug_name(const string &name) diff --git a/source/backends/vulkan/pipelinecache.cpp b/source/backends/vulkan/pipelinecache.cpp index 0ec5fcc7..618ddc93 100644 --- a/source/backends/vulkan/pipelinecache.cpp +++ b/source/backends/vulkan/pipelinecache.cpp @@ -34,10 +34,10 @@ VkRenderPass PipelineCache::get_render_pass(const RenderPass &rp) vector buffer; rp.fill_creation_info(buffer); - const VkRenderPassCreateInfo *creation_info = reinterpret_cast(buffer.data()); + const VkRenderPassCreateInfo2 *creation_info = reinterpret_cast(buffer.data()); VkRenderPass render_pass; - vk.CreateRenderPass(*creation_info, render_pass); + vk.CreateRenderPass2(*creation_info, render_pass); render_passes.insert(make_pair(key, render_pass)); diff --git a/source/backends/vulkan/renderpass.cpp b/source/backends/vulkan/renderpass.cpp index fb2389e5..8459b3a9 100644 --- a/source/backends/vulkan/renderpass.cpp +++ b/source/backends/vulkan/renderpass.cpp @@ -20,8 +20,11 @@ void RenderPass::update(Device &device) uint64_t RenderPass::compute_hash() const { - bool discard = (!clear_values && discard_fb_contents); - uint64_t result = hash<64>(static_cast(clear | (discard*2) | (to_present*4))); + const FrameFormat &format = framebuffer->get_format(); + bool discard = (clear && !clear_values && discard_fb_contents); + bool resolve = framebuffer->has_resolve_attachments(); + uint64_t result = hash<64>(static_cast(clear | (discard<<1) | (resolve<<2) | (to_present<<3))); + result = hash_round<64>(result, format.get_samples()); for(FrameAttachment a: framebuffer->get_format()) result = hash_update<64>(result, a); @@ -32,68 +35,96 @@ void RenderPass::fill_creation_info(vector &buffer) const { const FrameFormat &format = framebuffer->get_format(); - bool discard = (!clear_values && discard_fb_contents); bool has_depth = any_of(format.begin(), format.end(), [](FrameAttachment a){ return get_components(get_attachment_pixelformat(a))==DEPTH_COMPONENT; }); unsigned color_count = format.size()-has_depth; - - StructureBuilder sb(buffer, 6); - VkRenderPassCreateInfo *const &render_pass_info = sb.add(); - VkSubpassDescription *const &subpass = sb.add(); - VkAttachmentDescription *const &attachments = sb.add(format.size()); - VkAttachmentReference *const &color_refs = sb.add(color_count); - VkAttachmentReference *const &depth_stencil_ref = sb.add(has_depth); - VkSubpassDependency *const &dependency = sb.add(to_present); - - VkSampleCountFlagBits vk_samples = static_cast(get_vulkan_samples(format.get_samples())); - - VkAttachmentReference *color_ptr = color_refs; + bool resolve = framebuffer->has_resolve_attachments(); + + StructureBuilder sb(buffer, 7); + VkRenderPassCreateInfo2 *const &render_pass_info = sb.add(); + VkSubpassDescription2 *const &subpass = sb.add(); + VkAttachmentDescription2 *const &attachments = sb.add(format.size()*(1+resolve)); + VkAttachmentReference2 *const &color_refs = sb.add(color_count*(1+resolve)); + VkAttachmentReference2 *const &depth_stencil_ref = sb.add(has_depth*(1+resolve)); + VkSubpassDescriptionDepthStencilResolve *const &depth_stencil_resolve = sb.add(has_depth && resolve); + VkSubpassDependency2 *const &dependency = sb.add(to_present); + + VkAttachmentReference2 *color_ptr = color_refs; + VkAttachmentReference2 *ds_ptr = depth_stencil_ref; unsigned i = 0; - for(FrameAttachment a: format) - { + auto fill_attachment = [=, &color_ptr, &ds_ptr, &i](FrameAttachment a, VkSampleCountFlagBits samples, bool discard){ VkImageLayout subpass_layout = static_cast(get_vulkan_attachment_layout(get_components(get_attachment_pixelformat(a)))); VkImageLayout external_layout = (to_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : subpass_layout); + attachments[i].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; attachments[i].format = static_cast(get_vulkan_pixelformat(get_attachment_pixelformat(a))); - attachments[i].samples = vk_samples; - attachments[i].loadOp = (clear ? discard ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD); + attachments[i].samples = samples; + attachments[i].loadOp = (discard ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD); attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[i].initialLayout = ((clear && discard) ? VK_IMAGE_LAYOUT_UNDEFINED : external_layout); + attachments[i].initialLayout = (discard ? VK_IMAGE_LAYOUT_UNDEFINED : external_layout); attachments[i].finalLayout = external_layout; if(subpass_layout==VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + color_ptr->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; color_ptr->attachment = i; color_ptr->layout = subpass_layout; ++color_ptr; } else { - depth_stencil_ref->attachment = i; - depth_stencil_ref->layout = subpass_layout; + ds_ptr->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + ds_ptr->attachment = i; + ds_ptr->layout = subpass_layout; } ++i; + }; + + bool discard = (clear && !clear_values && discard_fb_contents); + VkSampleCountFlagBits vk_samples = static_cast(get_vulkan_samples(format.get_samples())); + for(FrameAttachment a: format) + fill_attachment(a, vk_samples, discard); + + if(resolve) + { + ++ds_ptr; + for(FrameAttachment a: format) + fill_attachment(a, VK_SAMPLE_COUNT_1_BIT, true); } + subpass->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2; subpass->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass->colorAttachmentCount = color_count; if(color_count) subpass->pColorAttachments = color_refs; if(has_depth) subpass->pDepthStencilAttachment = depth_stencil_ref; + if(resolve) + { + subpass->pResolveAttachments = color_refs+color_count; + + if(has_depth) + { + depth_stencil_resolve->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE; + depth_stencil_resolve->depthResolveMode = VK_RESOLVE_MODE_MIN_BIT; + depth_stencil_resolve->pDepthStencilResolveAttachment = depth_stencil_ref+1; + subpass->pNext = depth_stencil_resolve; + } + } - render_pass_info->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - render_pass_info->attachmentCount = format.size(); + render_pass_info->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2; + render_pass_info->attachmentCount = format.size()*(1+resolve); render_pass_info->pAttachments = attachments; render_pass_info->subpassCount = 1; render_pass_info->pSubpasses = subpass; if(to_present) { + dependency->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; dependency->srcSubpass = 0; dependency->dstSubpass = VK_SUBPASS_EXTERNAL; dependency->srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; diff --git a/source/backends/vulkan/vulkan.cpp b/source/backends/vulkan/vulkan.cpp index 31349e8f..c7823ae5 100644 --- a/source/backends/vulkan/vulkan.cpp +++ b/source/backends/vulkan/vulkan.cpp @@ -32,6 +32,7 @@ VulkanFunctions::VulkanFunctions(const Graphics::VulkanContext &c): vkQueueWaitIdle(context.get_function("vkQueueWaitIdle")), // 8 vkCreateRenderPass(context.get_function("vkCreateRenderPass")), + vkCreateRenderPass2(context.get_function("vkCreateRenderPass2")), vkDestroyRenderPass(context.get_function("vkDestroyRenderPass")), vkCreateFramebuffer(context.get_function("vkCreateFramebuffer")), vkDestroyFramebuffer(context.get_function("vkDestroyFramebuffer")), diff --git a/source/backends/vulkan/vulkan.h b/source/backends/vulkan/vulkan.h index 2b42550c..6c3726f6 100644 --- a/source/backends/vulkan/vulkan.h +++ b/source/backends/vulkan/vulkan.h @@ -117,6 +117,7 @@ private: PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier = 0; // 7.6 PFN_vkQueueWaitIdle vkQueueWaitIdle = 0; // 7.8 PFN_vkCreateRenderPass vkCreateRenderPass = 0; // 8.1 + PFN_vkCreateRenderPass2 vkCreateRenderPass2 = 0; // 8.1 PFN_vkDestroyRenderPass vkDestroyRenderPass = 0; // 8.1 PFN_vkCreateFramebuffer vkCreateFramebuffer = 0; // 8.3 PFN_vkDestroyFramebuffer vkDestroyFramebuffer = 0; // 8.3 @@ -239,6 +240,9 @@ public: Result CreateRenderPass(const VkRenderPassCreateInfo &rCreateInfo, VkRenderPass &rRenderPass) const { return { vkCreateRenderPass(device, &rCreateInfo, 0, handle_cast<::VkRenderPass *>(&rRenderPass)), "vkCreateRenderPass" }; } + Result CreateRenderPass2(const VkRenderPassCreateInfo2 &rCreateInfo, VkRenderPass &rRenderPass) const + { return { vkCreateRenderPass2(device, &rCreateInfo, 0, handle_cast<::VkRenderPass *>(&rRenderPass)), "vkCreateRenderPass2" }; } + void DestroyRenderPass(VkRenderPass renderPass) const { vkDestroyRenderPass(device, handle_cast<::VkRenderPass>(renderPass), 0); } -- 2.43.0