From 75b5854d22e708f9e2fd0a216689e4701da6cc79 Mon Sep 17 00:00:00 2001 From: "r.kuznetsov" Date: Thu, 28 Feb 2019 14:38:08 +0300 Subject: [PATCH] [vulkan] Added inflight frames --- .../vulkan/android_vulkan_context_factory.cpp | 1 - drape/vulkan/vulkan_base_context.cpp | 200 +++++++++++------- drape/vulkan/vulkan_base_context.hpp | 30 +-- drape/vulkan/vulkan_memory_manager.cpp | 2 +- drape/vulkan/vulkan_mesh_object_impl.cpp | 3 +- drape/vulkan/vulkan_object_manager.cpp | 61 ++++-- drape/vulkan/vulkan_object_manager.hpp | 23 +- drape/vulkan/vulkan_param_descriptor.cpp | 47 ++-- drape/vulkan/vulkan_param_descriptor.hpp | 18 +- drape/vulkan/vulkan_utils.hpp | 2 + .../vulkan_vertex_array_buffer_impl.cpp | 2 +- drape_frontend/frontend_renderer.cpp | 1 - shaders/vulkan_program_params.cpp | 77 ++++--- shaders/vulkan_program_params.hpp | 8 +- 14 files changed, 300 insertions(+), 175 deletions(-) diff --git a/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp b/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp index df8c0df595..73146bd160 100644 --- a/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp +++ b/android/jni/com/mapswithme/vulkan/android_vulkan_context_factory.cpp @@ -35,7 +35,6 @@ public: vkGetDeviceQueue(device, renderingQueueFamilyIndex, 0, &queue); SetRenderingQueue(queue); CreateCommandPool(); - CreateSyncPrimitives(); } void MakeCurrent() override diff --git a/drape/vulkan/vulkan_base_context.cpp b/drape/vulkan/vulkan_base_context.cpp index 334b079013..ce60938904 100644 --- a/drape/vulkan/vulkan_base_context.cpp +++ b/drape/vulkan/vulkan_base_context.cpp @@ -1,9 +1,5 @@ #include "drape/vulkan/vulkan_base_context.hpp" -#include "drape/vulkan/vulkan_staging_buffer.hpp" -#include "drape/vulkan/vulkan_texture.hpp" -#include "drape/vulkan/vulkan_utils.hpp" - #include "drape/framebuffer.hpp" #include "base/assert.hpp" @@ -18,7 +14,7 @@ namespace vulkan { namespace { -uint32_t constexpr kDefaultStagingBufferSizeInBytes = 10 * 1024 * 1024; +uint32_t constexpr kDefaultStagingBufferSizeInBytes = 3 * 1024 * 1024; VkImageMemoryBarrier PostRenderBarrier(VkImage image) { @@ -78,7 +74,8 @@ VulkanBaseContext::~VulkanBaseContext() m_pipeline.reset(); } - m_defaultStagingBuffer.reset(); + for (auto & b : m_defaultStagingBuffers) + b.reset(); DestroyRenderPassAndFramebuffers(); DestroySwapchain(); @@ -107,8 +104,8 @@ std::string VulkanBaseContext::GetRendererVersion() const void VulkanBaseContext::Init(ApiVersion apiVersion) { UNUSED_VALUE(apiVersion); - m_defaultStagingBuffer = make_unique_dp(m_objectManager, - kDefaultStagingBufferSizeInBytes); + for (auto & b : m_defaultStagingBuffers) + b = make_unique_dp(m_objectManager, kDefaultStagingBufferSizeInBytes); } void VulkanBaseContext::SetPresentAvailable(bool available) @@ -122,12 +119,15 @@ void VulkanBaseContext::SetSurface(VkSurfaceKHR surface, VkSurfaceFormatKHR surf m_surface = surface; m_surfaceFormat = surfaceFormat; m_surfaceCapabilities = surfaceCapabilities; + CreateSyncPrimitives(); RecreateSwapchainAndDependencies(); } void VulkanBaseContext::ResetSurface(bool allowPipelineDump) { vkDeviceWaitIdle(m_device); + DestroySyncPrimitives(); + ResetSwapchainAndDependencies(); m_surface.reset(); @@ -177,56 +177,87 @@ bool VulkanBaseContext::BeginRendering() if (!m_presentAvailable) return false; + auto res = vkWaitForFences(m_device, 1, &m_fences[m_inflightFrameIndex], VK_TRUE, UINT64_MAX); + if (res != VK_SUCCESS && res != VK_ERROR_DEVICE_LOST) + CHECK_RESULT_VK_CALL(vkWaitForFences, res); + + CHECK_VK_CALL(vkResetFences(m_device, 1, &m_fences[m_inflightFrameIndex])); + + // Clear resources for the finished inflight frame. + { + for (auto const & h : m_handlers[static_cast(HandlerType::PostPresent)]) + h.second(m_inflightFrameIndex); + + // Resetting of the default staging buffer is only after the finishing of current + // inflight frame rendering. It prevents data collisions. + m_defaultStagingBuffers[m_inflightFrameIndex]->Reset(); + + // Descriptors can be used only on the thread which renders. + m_objectManager->CollectDescriptorSetGroups(m_inflightFrameIndex); + + CollectMemory(); + } + m_frameCounter++; - auto const res = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_acquireComplete, - VK_NULL_HANDLE, &m_imageIndex); + res = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_acquireSemaphores[m_inflightFrameIndex], + VK_NULL_HANDLE, &m_imageIndex); if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) + { RecreateSwapchainAndDependencies(); + return false; + } else + { CHECK_RESULT_VK_CALL(vkAcquireNextImageKHR, res); + } + + m_objectManager->SetCurrentInflightFrameIndex(m_inflightFrameIndex); + for (auto const & h : m_handlers[static_cast(HandlerType::UpdateInflightFrame)]) + h.second(m_inflightFrameIndex); VkCommandBufferBeginInfo commandBufferBeginInfo = {}; commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - CHECK_VK_CALL(vkBeginCommandBuffer(m_memoryCommandBuffer, &commandBufferBeginInfo)); - CHECK_VK_CALL(vkBeginCommandBuffer(m_renderingCommandBuffer, &commandBufferBeginInfo)); + CHECK_VK_CALL(vkBeginCommandBuffer(m_memoryCommandBuffers[m_inflightFrameIndex], + &commandBufferBeginInfo)); + CHECK_VK_CALL(vkBeginCommandBuffer(m_renderingCommandBuffers[m_inflightFrameIndex], + &commandBufferBeginInfo)); return true; } void VulkanBaseContext::EndRendering() { - CHECK(m_presentAvailable, ()); - // Flushing of the default staging buffer must be before submitting the queue. // It guarantees the graphics data coherence. - m_defaultStagingBuffer->Flush(); + m_defaultStagingBuffers[m_inflightFrameIndex]->Flush(); for (auto const & h : m_handlers[static_cast(HandlerType::PrePresent)]) - h.second(make_ref(this)); + h.second(m_inflightFrameIndex); CHECK(m_isActiveRenderPass, ()); m_isActiveRenderPass = false; - vkCmdEndRenderPass(m_renderingCommandBuffer); + vkCmdEndRenderPass(m_renderingCommandBuffers[m_inflightFrameIndex]); - CHECK_VK_CALL(vkEndCommandBuffer(m_memoryCommandBuffer)); - CHECK_VK_CALL(vkEndCommandBuffer(m_renderingCommandBuffer)); + CHECK_VK_CALL(vkEndCommandBuffer(m_memoryCommandBuffers[m_inflightFrameIndex])); + CHECK_VK_CALL(vkEndCommandBuffer(m_renderingCommandBuffers[m_inflightFrameIndex])); VkSubmitInfo submitInfo = {}; VkPipelineStageFlags const waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkCommandBuffer commandBuffers[] = {m_memoryCommandBuffer, m_renderingCommandBuffer}; + VkCommandBuffer commandBuffers[] = {m_memoryCommandBuffers[m_inflightFrameIndex], + m_renderingCommandBuffers[m_inflightFrameIndex]}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pWaitDstStageMask = &waitStageMask; - submitInfo.pWaitSemaphores = &m_acquireComplete; + submitInfo.pWaitSemaphores = &m_acquireSemaphores[m_inflightFrameIndex]; submitInfo.waitSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_renderComplete; + submitInfo.pSignalSemaphores = &m_renderSemaphores[m_inflightFrameIndex]; submitInfo.signalSemaphoreCount = 1; submitInfo.commandBufferCount = 2; submitInfo.pCommandBuffers = commandBuffers; - auto const res = vkQueueSubmit(m_queue, 1, &submitInfo, m_fence); + auto const res = vkQueueSubmit(m_queue, 1, &submitInfo, m_fences[m_inflightFrameIndex]); if (res != VK_ERROR_DEVICE_LOST) { m_needPresent = true; @@ -247,12 +278,13 @@ void VulkanBaseContext::SetFramebuffer(ref_ptr framebuffer) ref_ptr fb = m_currentFramebuffer; ref_ptr tex = fb->GetTexture()->GetHardwareTexture(); VkImageMemoryBarrier imageBarrier = PostRenderBarrier(tex->GetImage()); - vkCmdPipelineBarrier(m_renderingCommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, - &imageBarrier); + vkCmdPipelineBarrier(m_renderingCommandBuffers[m_inflightFrameIndex], + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &imageBarrier); } - vkCmdEndRenderPass(m_renderingCommandBuffer); + vkCmdEndRenderPass(m_renderingCommandBuffers[m_inflightFrameIndex]); m_isActiveRenderPass = false; } @@ -261,7 +293,7 @@ void VulkanBaseContext::SetFramebuffer(ref_ptr framebuffer) void VulkanBaseContext::ApplyFramebuffer(std::string const & framebufferLabel) { - vkCmdSetStencilReference(m_renderingCommandBuffer, VK_STENCIL_FRONT_AND_BACK, + vkCmdSetStencilReference(m_renderingCommandBuffers[m_inflightFrameIndex], VK_STENCIL_FRONT_AND_BACK, m_stencilReferenceValue); // Calculate attachment operations. @@ -401,7 +433,8 @@ void VulkanBaseContext::ApplyFramebuffer(std::string const & framebufferLabel) renderPassBeginInfo.framebuffer = fbData.m_framebuffers[m_currentFramebuffer == nullptr ? m_imageIndex : 0]; m_isActiveRenderPass = true; - vkCmdBeginRenderPass(m_renderingCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(m_renderingCommandBuffers[m_inflightFrameIndex], &renderPassBeginInfo, + VK_SUBPASS_CONTENTS_INLINE); } void VulkanBaseContext::Present() @@ -414,7 +447,7 @@ void VulkanBaseContext::Present() presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &m_swapchain; presentInfo.pImageIndices = &m_imageIndex; - presentInfo.pWaitSemaphores = &m_renderComplete; + presentInfo.pWaitSemaphores = &m_renderSemaphores[m_inflightFrameIndex]; presentInfo.waitSemaphoreCount = 1; auto const res = vkQueuePresentKHR(m_queue, &presentInfo); @@ -422,21 +455,7 @@ void VulkanBaseContext::Present() CHECK_RESULT_VK_CALL(vkQueuePresentKHR, res); } - auto const res = vkWaitForFences(m_device, 1, &m_fence, VK_TRUE, UINT64_MAX); - if (res != VK_SUCCESS && res != VK_ERROR_DEVICE_LOST) - CHECK_RESULT_VK_CALL(vkWaitForFences, res); - - CHECK_VK_CALL(vkResetFences(m_device, 1, &m_fence)); - - for (auto const & h : m_handlers[static_cast(HandlerType::PostPresent)]) - h.second(make_ref(this)); - - // Resetting of the default staging buffer only after the finishing of rendering. - // It prevents data collisions. - m_defaultStagingBuffer->Reset(); - - // Descriptors can be used only on the thread which renders. - m_objectManager->CollectDescriptorSetGroups(); + m_inflightFrameIndex = (m_inflightFrameIndex + 1) % kMaxInflightFrames; m_pipelineKey = {}; m_stencilReferenceValue = 1; @@ -445,7 +464,7 @@ void VulkanBaseContext::Present() void VulkanBaseContext::CollectMemory() { - m_objectManager->CollectObjects(); + m_objectManager->CollectObjects(m_inflightFrameIndex); } uint32_t VulkanBaseContext::RegisterHandler(HandlerType handlerType, ContextHandler && handler) @@ -513,8 +532,8 @@ void VulkanBaseContext::Clear(uint32_t clearBits, uint32_t storeBits) } } - vkCmdClearAttachments(m_renderingCommandBuffer, attachmentsCount, attachments.data(), - 1 /* rectCount */, &clearRect); + vkCmdClearAttachments(m_renderingCommandBuffers[m_inflightFrameIndex], attachmentsCount, + attachments.data(), 1 /* rectCount */, &clearRect); } else { @@ -527,8 +546,8 @@ VulkanBaseContext::AttachmentsOperations VulkanBaseContext::GetAttachmensOperati { AttachmentsOperations operations; - // Here, if we do not clear attachments, we load data ONLY if we store it afterwards, otherwise we use 'DontCare' option - // to improve performance. + // Here, if we do not clear attachments, we load data ONLY if we store it afterwards, + // otherwise we use 'DontCare' option to improve performance. if (m_clearBits & ClearBits::ColorBit) operations.m_color.m_loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; else @@ -578,13 +597,13 @@ void VulkanBaseContext::SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t viewport.height = h; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; - vkCmdSetViewport(m_renderingCommandBuffer, 0, 1, &viewport); + vkCmdSetViewport(m_renderingCommandBuffers[m_inflightFrameIndex], 0, 1, &viewport); VkRect2D scissor = {}; scissor.extent = {w, h}; scissor.offset.x = x; scissor.offset.y = y; - vkCmdSetScissor(m_renderingCommandBuffer, 0, 1, &scissor); + vkCmdSetScissor(m_renderingCommandBuffers[m_inflightFrameIndex], 0, 1, &scissor); } void VulkanBaseContext::SetDepthTestEnabled(bool enabled) @@ -695,9 +714,19 @@ VkSampler VulkanBaseContext::GetSampler(SamplerKey const & key) return m_objectManager->GetSampler(key); } +VkCommandBuffer VulkanBaseContext::GetCurrentMemoryCommandBuffer() const +{ + return m_memoryCommandBuffers[m_inflightFrameIndex]; +} + +VkCommandBuffer VulkanBaseContext::GetCurrentRenderingCommandBuffer() const +{ + return m_renderingCommandBuffers[m_inflightFrameIndex]; +} + ref_ptr VulkanBaseContext::GetDefaultStagingBuffer() const { - return make_ref(m_defaultStagingBuffer); + return make_ref(m_defaultStagingBuffers[m_inflightFrameIndex]); } void VulkanBaseContext::RecreateSwapchain() @@ -837,57 +866,80 @@ void VulkanBaseContext::CreateCommandBuffers() cmdBufAllocateInfo.commandPool = m_commandPool; cmdBufAllocateInfo.commandBufferCount = 1; cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - CHECK_VK_CALL(vkAllocateCommandBuffers(m_device, &cmdBufAllocateInfo, &m_memoryCommandBuffer)); - CHECK_VK_CALL(vkAllocateCommandBuffers(m_device, &cmdBufAllocateInfo, &m_renderingCommandBuffer)); + + for (auto & cb : m_memoryCommandBuffers) + CHECK_VK_CALL(vkAllocateCommandBuffers(m_device, &cmdBufAllocateInfo, &cb)); + + for (auto & cb : m_renderingCommandBuffers) + CHECK_VK_CALL(vkAllocateCommandBuffers(m_device, &cmdBufAllocateInfo, &cb)); } void VulkanBaseContext::DestroyCommandBuffers() { - if (m_memoryCommandBuffer != VK_NULL_HANDLE) + for (auto & cb : m_memoryCommandBuffers) { - vkFreeCommandBuffers(m_device, m_commandPool, 1, &m_memoryCommandBuffer); - m_memoryCommandBuffer = VK_NULL_HANDLE; + if (cb == VK_NULL_HANDLE) + continue; + + vkFreeCommandBuffers(m_device, m_commandPool, 1, &cb); + cb = VK_NULL_HANDLE; } - if (m_renderingCommandBuffer != VK_NULL_HANDLE) + for (auto & cb : m_renderingCommandBuffers) { - vkFreeCommandBuffers(m_device, m_commandPool, 1, &m_renderingCommandBuffer); - m_renderingCommandBuffer = VK_NULL_HANDLE; + if (cb == VK_NULL_HANDLE) + continue; + + vkFreeCommandBuffers(m_device, m_commandPool, 1, &cb); + cb = VK_NULL_HANDLE; } } void VulkanBaseContext::CreateSyncPrimitives() { - // A fence is need to check for command buffer completion before we can recreate it. VkFenceCreateInfo fenceCI = {}; fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - CHECK_VK_CALL(vkCreateFence(m_device, &fenceCI, nullptr, &m_fence)); + fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT; + for (auto & f : m_fences) + CHECK_VK_CALL(vkCreateFence(m_device, &fenceCI, nullptr, &f)); VkSemaphoreCreateInfo semaphoreCI = {}; semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - CHECK_VK_CALL(vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_acquireComplete)); - CHECK_VK_CALL(vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &m_renderComplete)); + for (auto & s : m_acquireSemaphores) + CHECK_VK_CALL(vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &s)); + + for (auto & s : m_renderSemaphores) + CHECK_VK_CALL(vkCreateSemaphore(m_device, &semaphoreCI, nullptr, &s)); } void VulkanBaseContext::DestroySyncPrimitives() { - if (m_fence != VK_NULL_HANDLE) + for (auto & f : m_fences) { - vkDestroyFence(m_device, m_fence, nullptr); - m_fence = VK_NULL_HANDLE; + if (f == VK_NULL_HANDLE) + continue; + + vkDestroyFence(m_device, f, nullptr); + f = VK_NULL_HANDLE; } - if (m_acquireComplete != VK_NULL_HANDLE) + for (auto & s : m_acquireSemaphores) { - vkDestroySemaphore(m_device, m_acquireComplete, nullptr); - m_acquireComplete = VK_NULL_HANDLE; + if (s == VK_NULL_HANDLE) + continue; + + vkDestroySemaphore(m_device, s, nullptr); + s = VK_NULL_HANDLE; } - if (m_renderComplete != VK_NULL_HANDLE) + for (auto & s : m_renderSemaphores) { - vkDestroySemaphore(m_device, m_renderComplete, nullptr); - m_renderComplete = VK_NULL_HANDLE; + if (s == VK_NULL_HANDLE) + continue; + + vkDestroySemaphore(m_device, s, nullptr); + s = VK_NULL_HANDLE; } } diff --git a/drape/vulkan/vulkan_base_context.hpp b/drape/vulkan/vulkan_base_context.hpp index 62dc2048db..0f61054355 100644 --- a/drape/vulkan/vulkan_base_context.hpp +++ b/drape/vulkan/vulkan_base_context.hpp @@ -6,6 +6,7 @@ #include "drape/vulkan/vulkan_object_manager.hpp" #include "drape/vulkan/vulkan_param_descriptor.hpp" #include "drape/vulkan/vulkan_pipeline.hpp" +#include "drape/vulkan/vulkan_staging_buffer.hpp" #include "drape/vulkan/vulkan_texture.hpp" #include "drape/vulkan/vulkan_utils.hpp" @@ -37,7 +38,7 @@ public: drape_ptr && pipeline); ~VulkanBaseContext() override; - using ContextHandler = std::function)>; + using ContextHandler = std::function; bool BeginRendering() override; void EndRendering() override; @@ -90,11 +91,13 @@ public: ref_ptr GetObjectManager() const { return m_objectManager; } - VkCommandBuffer GetCurrentMemoryCommandBuffer() const { return m_memoryCommandBuffer; } - VkCommandBuffer GetCurrentRenderingCommandBuffer() const { return m_renderingCommandBuffer; } - + // The following methods return short-live objects. Typically they must not be stored. + VkCommandBuffer GetCurrentMemoryCommandBuffer() const; + VkCommandBuffer GetCurrentRenderingCommandBuffer() const; ref_ptr GetDefaultStagingBuffer() const; + uint32_t GetCurrentInflightFrameIndex() const { return m_inflightFrameIndex; } + VkPipeline GetCurrentPipeline(); VkPipelineLayout GetCurrentPipelineLayout() const; uint32_t GetCurrentDynamicBufferOffset() const; @@ -108,6 +111,7 @@ public: { PrePresent = 0, PostPresent, + UpdateInflightFrame, Count }; @@ -164,16 +168,17 @@ protected: VkQueue m_queue = {}; VkCommandPool m_commandPool = {}; - VkCommandBuffer m_renderingCommandBuffer = {}; - VkCommandBuffer m_memoryCommandBuffer = {}; bool m_isActiveRenderPass = false; - // Swap chain image acquiring. - VkSemaphore m_acquireComplete = {}; - // Command buffer submission and execution. - VkSemaphore m_renderComplete = {}; + std::array m_renderingCommandBuffers = {}; + std::array m_memoryCommandBuffers = {}; - VkFence m_fence = {}; + // Swap chain image acquiring. + std::array m_acquireSemaphores = {}; + // Command buffers submission and execution. + std::array m_renderSemaphores = {}; + // All rendering tasks completion. + std::array m_fences = {}; ref_ptr m_objectManager; drape_ptr m_pipeline; @@ -210,10 +215,11 @@ protected: VulkanPipeline::PipelineKey m_pipelineKey; std::vector m_paramDescriptors; - drape_ptr m_defaultStagingBuffer; + std::array, kMaxInflightFrames> m_defaultStagingBuffers = {}; std::atomic m_presentAvailable; uint32_t m_frameCounter = 0; bool m_needPresent = true; + uint32_t m_inflightFrameIndex = 0; }; } // namespace vulkan } // namespace dp diff --git a/drape/vulkan/vulkan_memory_manager.cpp b/drape/vulkan/vulkan_memory_manager.cpp index 6954773b80..84e1925ee7 100644 --- a/drape/vulkan/vulkan_memory_manager.cpp +++ b/drape/vulkan/vulkan_memory_manager.cpp @@ -17,7 +17,7 @@ namespace std::array const kMinBlockSizeInBytes = {{ 1024 * 1024, // Geometry - 64 * 1024, // Uniform (no minimal size) + 128 * 1024, // Uniform 0, // Staging (no minimal size) 0, // Image (no minimal size) }}; diff --git a/drape/vulkan/vulkan_mesh_object_impl.cpp b/drape/vulkan/vulkan_mesh_object_impl.cpp index 1e2630d4cc..c628198eff 100644 --- a/drape/vulkan/vulkan_mesh_object_impl.cpp +++ b/drape/vulkan/vulkan_mesh_object_impl.cpp @@ -42,7 +42,6 @@ public: void Build(ref_ptr context, ref_ptr program) override { - m_descriptorUpdater.Reset(); m_geometryBuffers.resize(m_mesh->m_buffers.size()); m_bindingInfoCount = static_cast(m_mesh->m_buffers.size()); CHECK_LESS_OR_EQUAL(m_bindingInfoCount, kMaxBindingInfo, ()); @@ -74,7 +73,7 @@ public: void Reset() override { - m_descriptorUpdater.Reset(); + m_descriptorUpdater.Destroy(); for (auto const & b : m_geometryBuffers) m_objectManager->DestroyObject(b); m_geometryBuffers.clear(); diff --git a/drape/vulkan/vulkan_object_manager.cpp b/drape/vulkan/vulkan_object_manager.cpp index 13b3fdd4ee..9919cb471a 100644 --- a/drape/vulkan/vulkan_object_manager.cpp +++ b/drape/vulkan/vulkan_object_manager.cpp @@ -13,6 +13,9 @@ namespace vulkan { namespace { +size_t constexpr kBackendQueueIndex = 0; +size_t constexpr kOtherQueueIndex = 0; + VkSamplerAddressMode GetVulkanSamplerAddressMode(TextureWrapping wrapping) { switch (wrapping) @@ -41,18 +44,25 @@ VulkanObjectManager::VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits , m_queueFamilyIndex(queueFamilyIndex) , m_memoryManager(device, deviceLimits, memoryProperties) { - for (size_t i = 0; i < ThreadType::Count; ++i) - m_queuesToDestroy[i].reserve(50); - m_descriptorsToDestroy.reserve(50); + for (auto & q : m_queuesToDestroy[ThreadType::Frontend]) + q.reserve(50); + + for (auto & descriptorsToDestroy : m_descriptorsToDestroy) + descriptorsToDestroy.reserve(50); + CreateDescriptorPool(); } VulkanObjectManager::~VulkanObjectManager() { - CollectDescriptorSetGroupsUnsafe(); + for (auto & descriptorsToDestroy : m_descriptorsToDestroy) + CollectDescriptorSetGroupsUnsafe(descriptorsToDestroy); for (size_t i = 0; i < ThreadType::Count; ++i) - CollectObjectsImpl(m_queuesToDestroy[i]); + { + for (auto & q : m_queuesToDestroy[i]) + CollectObjectsImpl(q); + } for (auto const & s : m_samplers) vkDestroySampler(m_device, s.second, nullptr); @@ -66,6 +76,13 @@ void VulkanObjectManager::RegisterThread(ThreadType type) m_renderers[type] = std::this_thread::get_id(); } +void VulkanObjectManager::SetCurrentInflightFrameIndex(uint32_t index) +{ + CHECK(std::this_thread::get_id() == m_renderers[ThreadType::Frontend], ()); + CHECK_LESS(m_currentInflightFrameIndex, kMaxInflightFrames, ()); + m_currentInflightFrameIndex = index; +} + VulkanObject VulkanObjectManager::CreateBuffer(VulkanMemoryManager::ResourceType resourceType, uint32_t sizeInBytes, uint64_t batcherHash) { @@ -207,71 +224,71 @@ void VulkanObjectManager::DestroyObject(VulkanObject object) auto const currentThreadId = std::this_thread::get_id(); if (currentThreadId == m_renderers[ThreadType::Frontend]) { - m_queuesToDestroy[ThreadType::Frontend].push_back(std::move(object)); + m_queuesToDestroy[ThreadType::Frontend][m_currentInflightFrameIndex].push_back(std::move(object)); } else if (currentThreadId == m_renderers[ThreadType::Backend]) { - m_queuesToDestroy[ThreadType::Backend].push_back(std::move(object)); + m_queuesToDestroy[ThreadType::Backend][kBackendQueueIndex].push_back(std::move(object)); } else { std::lock_guard lock(m_destroyMutex); - m_queuesToDestroy[ThreadType::Other].push_back(std::move(object)); + m_queuesToDestroy[ThreadType::Other][kOtherQueueIndex].push_back(std::move(object)); } } void VulkanObjectManager::DestroyDescriptorSetGroup(DescriptorSetGroup group) { CHECK(std::this_thread::get_id() == m_renderers[ThreadType::Frontend], ()); - m_descriptorsToDestroy.push_back(std::move(group)); + m_descriptorsToDestroy[m_currentInflightFrameIndex].push_back(std::move(group)); } -void VulkanObjectManager::CollectDescriptorSetGroups() +void VulkanObjectManager::CollectDescriptorSetGroups(uint32_t inflightFrameIndex) { CHECK(std::this_thread::get_id() == m_renderers[ThreadType::Frontend], ()); - CollectDescriptorSetGroupsUnsafe(); + CollectDescriptorSetGroupsUnsafe(m_descriptorsToDestroy[inflightFrameIndex]); } -void VulkanObjectManager::CollectDescriptorSetGroupsUnsafe() +void VulkanObjectManager::CollectDescriptorSetGroupsUnsafe(DescriptorSetGroupArray & descriptors) { - for (auto const & d : m_descriptorsToDestroy) + for (auto const & d : descriptors) { CHECK_VK_CALL(vkFreeDescriptorSets(m_device, d.m_descriptorPool, 1 /* count */, &d.m_descriptorSet)); } - m_descriptorsToDestroy.clear(); + descriptors.clear(); } -void VulkanObjectManager::CollectObjects() +void VulkanObjectManager::CollectObjects(uint32_t inflightFrameIndex) { auto const currentThreadId = std::this_thread::get_id(); if (currentThreadId == m_renderers[ThreadType::Frontend]) { - CollectObjectsForThread(ThreadType::Frontend); + CollectObjectsForThread(m_queuesToDestroy[ThreadType::Frontend][inflightFrameIndex]); } else if (currentThreadId == m_renderers[ThreadType::Backend]) { - CollectObjectsForThread(ThreadType::Backend); + CollectObjectsForThread(m_queuesToDestroy[ThreadType::Backend][kBackendQueueIndex]); std::lock_guard lock(m_destroyMutex); - CollectObjectsForThread(ThreadType::Other); + CollectObjectsForThread(m_queuesToDestroy[ThreadType::Other][kOtherQueueIndex]); } } -void VulkanObjectManager::CollectObjectsForThread(ThreadType type) +void VulkanObjectManager::CollectObjectsForThread(VulkanObjectArray & objects) { - if (m_queuesToDestroy[type].empty()) + if (objects.empty()) return; std::vector queueToDestroy; - std::swap(m_queuesToDestroy[type], queueToDestroy); + std::swap(objects, queueToDestroy); DrapeRoutine::Run([this, queueToDestroy = std::move(queueToDestroy)]() { CollectObjectsImpl(queueToDestroy); }); } -void VulkanObjectManager::CollectObjectsImpl(std::vector const & objects) +void VulkanObjectManager::CollectObjectsImpl(VulkanObjectArray const & objects) { for (auto const & obj : objects) { diff --git a/drape/vulkan/vulkan_object_manager.hpp b/drape/vulkan/vulkan_object_manager.hpp index 444157a44c..aca03d9fd5 100644 --- a/drape/vulkan/vulkan_object_manager.hpp +++ b/drape/vulkan/vulkan_object_manager.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,8 @@ public: }; void RegisterThread(ThreadType type); + void SetCurrentInflightFrameIndex(uint32_t index); + VulkanObject CreateBuffer(VulkanMemoryManager::ResourceType resourceType, uint32_t sizeInBytes, uint64_t batcherHash); VulkanObject CreateImage(VkImageUsageFlags usageFlags, VkFormat format, @@ -83,32 +86,38 @@ public: void DestroyObject(VulkanObject object); void DestroyDescriptorSetGroup(DescriptorSetGroup group); - void CollectDescriptorSetGroups(); - void CollectObjects(); + void CollectDescriptorSetGroups(uint32_t inflightFrameIndex); + void CollectObjects(uint32_t inflightFrameIndex); VkDevice GetDevice() const { return m_device; } VulkanMemoryManager const & GetMemoryManager() const { return m_memoryManager; }; VkSampler GetSampler(SamplerKey const & key); private: + using DescriptorSetGroupArray = std::vector; + using VulkanObjectArray = std::vector; + void CreateDescriptorPool(); void DestroyDescriptorPools(); - void CollectObjectsForThread(ThreadType type); - void CollectObjectsImpl(std::vector const & objects); - void CollectDescriptorSetGroupsUnsafe(); + void CollectObjectsForThread(VulkanObjectArray & objects); + void CollectObjectsImpl(VulkanObjectArray const & objects); + void CollectDescriptorSetGroupsUnsafe(DescriptorSetGroupArray & descriptors); VkDevice const m_device; uint32_t const m_queueFamilyIndex; VulkanMemoryManager m_memoryManager; std::array m_renderers = {}; - std::array, ThreadType::Count> m_queuesToDestroy = {}; + std::array, ThreadType::Count> m_queuesToDestroy = {}; std::vector m_descriptorPools; - std::vector m_descriptorsToDestroy; + + std::array m_descriptorsToDestroy; std::map m_samplers; + uint32_t m_currentInflightFrameIndex = 0; + std::mutex m_mutex; std::mutex m_samplerMutex; std::mutex m_destroyMutex; diff --git a/drape/vulkan/vulkan_param_descriptor.cpp b/drape/vulkan/vulkan_param_descriptor.cpp index ed1f0e0328..fb59924af3 100644 --- a/drape/vulkan/vulkan_param_descriptor.cpp +++ b/drape/vulkan/vulkan_param_descriptor.cpp @@ -56,47 +56,58 @@ ParamDescriptorUpdater::ParamDescriptorUpdater(ref_ptr obje : m_objectManager(std::move(objectManager)) {} -void ParamDescriptorUpdater::Reset() +void ParamDescriptorUpdater::Reset(uint32_t inflightFrameIndex) { - for (auto const & g : m_descriptorSetGroups) + auto & ud = m_updateData[inflightFrameIndex]; + for (auto const & g : ud.m_descriptorSetGroups) m_objectManager->DestroyDescriptorSetGroup(g); - m_descriptorSetGroups.clear(); - m_descriptorSetIndex = 0; - m_updateDescriptorFrame = 0; + ud.m_descriptorSetGroups.clear(); + ud.m_descriptorSetIndex = 0; + ud.m_updateDescriptorFrame = 0; +} + +void ParamDescriptorUpdater::Destroy() +{ + for (size_t i = 0; i < m_updateData.size(); ++i) + Reset(static_cast(i)); } void ParamDescriptorUpdater::Update(ref_ptr context) { ref_ptr vulkanContext = context; - if (m_program != vulkanContext->GetCurrentProgram()) + m_currentInflightFrameIndex = vulkanContext->GetCurrentInflightFrameIndex(); + auto & ud = m_updateData[m_currentInflightFrameIndex]; + + if (ud.m_program != vulkanContext->GetCurrentProgram()) { - Reset(); - m_program = vulkanContext->GetCurrentProgram(); + Reset(m_currentInflightFrameIndex); + ud.m_program = vulkanContext->GetCurrentProgram(); } // We can update descriptors only once per frame. So if we need to render // object several times per frame, we must allocate new descriptors. - if (m_updateDescriptorFrame != vulkanContext->GetCurrentFrameIndex()) + if (ud.m_updateDescriptorFrame != vulkanContext->GetCurrentFrameIndex()) { - m_updateDescriptorFrame = vulkanContext->GetCurrentFrameIndex(); - m_descriptorSetIndex = 0; + ud.m_updateDescriptorFrame = vulkanContext->GetCurrentFrameIndex(); + ud.m_descriptorSetIndex = 0; } else { - m_descriptorSetIndex++; + ud.m_descriptorSetIndex++; } - CHECK_LESS_OR_EQUAL(m_descriptorSetIndex, m_descriptorSetGroups.size(), ()); - if (m_descriptorSetIndex == m_descriptorSetGroups.size()) - m_descriptorSetGroups.emplace_back(m_objectManager->CreateDescriptorSetGroup(m_program)); + CHECK_LESS_OR_EQUAL(ud.m_descriptorSetIndex, ud.m_descriptorSetGroups.size(), ()); + if (ud.m_descriptorSetIndex == ud.m_descriptorSetGroups.size()) + ud.m_descriptorSetGroups.emplace_back(m_objectManager->CreateDescriptorSetGroup(ud.m_program)); - m_descriptorSetGroups[m_descriptorSetIndex].Update(vulkanContext->GetDevice(), - vulkanContext->GetCurrentParamDescriptors()); + ud.m_descriptorSetGroups[ud.m_descriptorSetIndex].Update(vulkanContext->GetDevice(), + vulkanContext->GetCurrentParamDescriptors()); } VkDescriptorSet ParamDescriptorUpdater::GetDescriptorSet() const { - return m_descriptorSetGroups[m_descriptorSetIndex].m_descriptorSet; + auto const & ud = m_updateData[m_currentInflightFrameIndex]; + return ud.m_descriptorSetGroups[ud.m_descriptorSetIndex].m_descriptorSet; } } // namespace vulkan } // namespace dp diff --git a/drape/vulkan/vulkan_param_descriptor.hpp b/drape/vulkan/vulkan_param_descriptor.hpp index 8eb6b69342..bf1f34f234 100644 --- a/drape/vulkan/vulkan_param_descriptor.hpp +++ b/drape/vulkan/vulkan_param_descriptor.hpp @@ -2,6 +2,7 @@ #include "drape/graphics_context.hpp" #include "drape/vulkan/vulkan_gpu_program.hpp" +#include "drape/vulkan/vulkan_utils.hpp" #include #include @@ -61,15 +62,22 @@ public: explicit ParamDescriptorUpdater(ref_ptr objectManager); void Update(ref_ptr context); - void Reset(); + void Destroy(); VkDescriptorSet GetDescriptorSet() const; private: + void Reset(uint32_t inflightFrameIndex); + ref_ptr m_objectManager; - std::vector m_descriptorSetGroups; - ref_ptr m_program; - uint32_t m_updateDescriptorFrame = 0; - uint32_t m_descriptorSetIndex = 0; + struct UpdateData + { + std::vector m_descriptorSetGroups; + ref_ptr m_program; + uint32_t m_updateDescriptorFrame = 0; + uint32_t m_descriptorSetIndex = 0; + }; + std::array m_updateData; + uint32_t m_currentInflightFrameIndex = 0; }; } // namespace vulkan diff --git a/drape/vulkan/vulkan_utils.hpp b/drape/vulkan/vulkan_utils.hpp index 09c18b9911..712f5fdd64 100644 --- a/drape/vulkan/vulkan_utils.hpp +++ b/drape/vulkan/vulkan_utils.hpp @@ -17,6 +17,8 @@ namespace vulkan { extern std::string GetVulkanResultString(VkResult result); +uint32_t constexpr kMaxInflightFrames = 2; + class VulkanFormatUnpacker { public: diff --git a/drape/vulkan/vulkan_vertex_array_buffer_impl.cpp b/drape/vulkan/vulkan_vertex_array_buffer_impl.cpp index 780446801f..6bf5d451e4 100644 --- a/drape/vulkan/vulkan_vertex_array_buffer_impl.cpp +++ b/drape/vulkan/vulkan_vertex_array_buffer_impl.cpp @@ -37,7 +37,7 @@ public: ~VulkanVertexArrayBufferImpl() override { - m_descriptorUpdater.Reset(); + m_descriptorUpdater.Destroy(); } bool Build(ref_ptr program) override diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index b6a8450c31..97d300a941 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -1716,7 +1716,6 @@ void FrontendRenderer::RenderFrame() #ifndef DISABLE_SCREEN_PRESENTATION m_context->Present(); - m_context->CollectMemory(); #endif // Limit fps in following mode. diff --git a/shaders/vulkan_program_params.cpp b/shaders/vulkan_program_params.cpp index 65eeac4da5..0934928cc3 100644 --- a/shaders/vulkan_program_params.cpp +++ b/shaders/vulkan_program_params.cpp @@ -35,56 +35,74 @@ VulkanProgramParamsSetter::VulkanProgramParamsSetter(ref_ptrGetObjectManager(); - m_uniformBuffers.emplace_back(CreateUniformBuffer(context->GetDevice(), m_objectManager, - m_sizeAlignment, m_offsetAlignment)); + for (auto & ub : m_uniformBuffers) + { + ub.emplace_back(CreateUniformBuffer(context->GetDevice(), m_objectManager, + m_sizeAlignment, m_offsetAlignment)); + } m_flushHandlerId = context->RegisterHandler(VulkanBaseContext::HandlerType::PrePresent, - [this](ref_ptr) + [this](uint32_t) { Flush(); }); + m_finishHandlerId = context->RegisterHandler(VulkanBaseContext::HandlerType::PostPresent, - [this](ref_ptr) + [this](uint32_t inflightFrameIndex) { - Finish(); + CHECK_LESS(inflightFrameIndex, dp::vulkan::kMaxInflightFrames, ()); + Finish(inflightFrameIndex); + }); + + m_updateInflightFrameId = context->RegisterHandler(VulkanBaseContext::HandlerType::UpdateInflightFrame, + [this](uint32_t inflightFrameIndex) + { + CHECK_LESS(inflightFrameIndex, dp::vulkan::kMaxInflightFrames, ()); + m_currentInflightFrameIndex = inflightFrameIndex; }); } VulkanProgramParamsSetter::~VulkanProgramParamsSetter() { - ASSERT_EQUAL(m_uniformBuffers.size(), 0, ()); + for (auto & ub : m_uniformBuffers) + CHECK_EQUAL(ub.size(), 0, ()); } void VulkanProgramParamsSetter::Destroy(ref_ptr context) { CHECK_THREAD_CHECKER(m_threadChecker, ()); - for (auto & b : m_uniformBuffers) + for (auto & ub : m_uniformBuffers) { - m_objectManager->UnmapUnsafe(b.m_object); - m_objectManager->DestroyObject(b.m_object); + for (auto & b : ub) + { + m_objectManager->UnmapUnsafe(b.m_object); + m_objectManager->DestroyObject(b.m_object); + } + ub.clear(); } - m_uniformBuffers.clear(); + context->UnregisterHandler(m_finishHandlerId); context->UnregisterHandler(m_flushHandlerId); + context->UnregisterHandler(m_updateInflightFrameId); } void VulkanProgramParamsSetter::Flush() { CHECK_THREAD_CHECKER(m_threadChecker, ()); - for (auto & ub : m_uniformBuffers) + for (auto & b : m_uniformBuffers[m_currentInflightFrameIndex]) { - if (ub.m_freeOffset == 0) + if (b.m_freeOffset == 0) continue; - auto const size = ub.m_freeOffset; - m_objectManager->FlushUnsafe(ub.m_object, 0 /* offset */, size); + auto const size = b.m_freeOffset; + m_objectManager->FlushUnsafe(b.m_object, 0 /* offset */, size); } } -void VulkanProgramParamsSetter::Finish() +void VulkanProgramParamsSetter::Finish(uint32_t inflightFrameIndex) { CHECK_THREAD_CHECKER(m_threadChecker, ()); - for (auto & b : m_uniformBuffers) + for (auto & b : m_uniformBuffers[inflightFrameIndex]) b.m_freeOffset = 0; } @@ -95,10 +113,12 @@ void VulkanProgramParamsSetter::ApplyBytes(ref_ptrGetMemoryManager(); auto const alignedSize = mm.GetAligned(sizeInBytes, m_sizeAlignment); + auto & ub = m_uniformBuffers[m_currentInflightFrameIndex]; + int index = -1; - for (int i = 0; i < static_cast(m_uniformBuffers.size()); ++i) + for (int i = 0; i < static_cast(ub.size()); ++i) { - if (m_uniformBuffers[i].m_freeOffset + alignedSize <= kUniformBufferSizeInBytes) + if (ub[i].m_freeOffset + alignedSize <= kUniformBufferSizeInBytes) { index = i; break; @@ -107,28 +127,27 @@ void VulkanProgramParamsSetter::ApplyBytes(ref_ptrGetDevice(), m_objectManager, - sizeAlignment, offsetAlignment)); + ub.emplace_back(CreateUniformBuffer(context->GetDevice(), m_objectManager, + sizeAlignment, offsetAlignment)); CHECK_EQUAL(m_sizeAlignment, sizeAlignment, ()); CHECK_EQUAL(m_offsetAlignment, offsetAlignment, ()); - index = static_cast(m_uniformBuffers.size()) - 1; + index = static_cast(ub.size()) - 1; } - CHECK_LESS_OR_EQUAL(m_uniformBuffers[index].m_freeOffset + alignedSize, kUniformBufferSizeInBytes, ()); + CHECK_LESS_OR_EQUAL(ub[index].m_freeOffset + alignedSize, kUniformBufferSizeInBytes, ()); - auto const alignedOffset = m_uniformBuffers[index].m_freeOffset; - uint8_t * ptr = m_uniformBuffers[index].m_pointer + alignedOffset; + auto const alignedOffset = ub[index].m_freeOffset; + uint8_t * ptr = ub[index].m_pointer + alignedOffset; // Update offset and align it. - m_uniformBuffers[index].m_freeOffset += alignedSize; - m_uniformBuffers[index].m_freeOffset = - std::min(mm.GetAligned(m_uniformBuffers[index].m_freeOffset, m_offsetAlignment), - m_uniformBuffers[index].m_object.GetAlignedSize()); + ub[index].m_freeOffset += alignedSize; + ub[index].m_freeOffset = std::min(mm.GetAligned(ub[index].m_freeOffset, m_offsetAlignment), + ub[index].m_object.GetAlignedSize()); memcpy(ptr, data, sizeInBytes); dp::vulkan::ParamDescriptor descriptor; descriptor.m_type = dp::vulkan::ParamDescriptor::Type::DynamicUniformBuffer; - descriptor.m_bufferDescriptor.buffer = m_uniformBuffers[index].m_object.m_buffer; + descriptor.m_bufferDescriptor.buffer = ub[index].m_object.m_buffer; descriptor.m_bufferDescriptor.range = VK_WHOLE_SIZE; descriptor.m_bufferDynamicOffset = alignedOffset; descriptor.m_id = static_cast(index); diff --git a/shaders/vulkan_program_params.hpp b/shaders/vulkan_program_params.hpp index a4e4ffb2c7..a6ed127866 100644 --- a/shaders/vulkan_program_params.hpp +++ b/shaders/vulkan_program_params.hpp @@ -5,6 +5,7 @@ #include "drape/vulkan/vulkan_base_context.hpp" #include "drape/vulkan/vulkan_gpu_program.hpp" #include "drape/vulkan/vulkan_object_manager.hpp" +#include "drape/vulkan/vulkan_utils.hpp" #include "base/thread_checker.hpp" @@ -33,7 +34,7 @@ public: void Destroy(ref_ptr context); void Flush(); - void Finish(); + void Finish(uint32_t inflightFrameIndex); void Apply(ref_ptr context, ref_ptr program, MapProgramParams const & params) override; @@ -70,12 +71,15 @@ private: void const * data, uint32_t sizeInBytes); ref_ptr m_objectManager; - std::vector m_uniformBuffers; + std::array, dp::vulkan::kMaxInflightFrames> m_uniformBuffers; uint32_t m_offsetAlignment = 0; uint32_t m_sizeAlignment = 0; uint32_t m_flushHandlerId = 0; uint32_t m_finishHandlerId = 0; + uint32_t m_updateInflightFrameId = 0; ThreadChecker m_threadChecker; + + uint32_t m_currentInflightFrameIndex = 0; }; } // namespace vulkan } // namespace gpu