diff --git a/engine/flutter/vulkan/vulkan_backbuffer.h b/engine/flutter/vulkan/vulkan_backbuffer.h index 2877846f64fdc1f04effac4b837f59ba16a1c1cb..22037b5c01f3b02e63fdbcb1b7c0c30ed9e9d32b 100644 --- a/engine/flutter/vulkan/vulkan_backbuffer.h +++ b/engine/flutter/vulkan/vulkan_backbuffer.h @@ -50,6 +50,14 @@ class VulkanBackbuffer { VulkanCommandBuffer& GetRenderCommandBuffer(); +#ifdef RS_ENABLE_VK + void SetMultiThreading() { multi_threading_ = true; } + + void UnsetMultiThreading() { multi_threading_ = false; } + + bool IsMultiThreading() { return multi_threading_; } +#endif + private: const VulkanProcTable& vk; const VulkanHandle& device_; @@ -63,6 +71,10 @@ class VulkanBackbuffer { bool CreateFences(); +#ifdef RS_ENABLE_VK + bool multi_threading_ = false; +#endif + #ifndef RS_ENABLE_VK FML_DISALLOW_COPY_AND_ASSIGN(VulkanBackbuffer); #endif diff --git a/engine/flutter/vulkan/vulkan_swapchain.cc b/engine/flutter/vulkan/vulkan_swapchain.cc index 91cf928a183b6636f68999dfb3cdf164f2c84219..2c4d1c9c98a7a3535528ecb9aea4ccd50a65a3bc 100644 --- a/engine/flutter/vulkan/vulkan_swapchain.cc +++ b/engine/flutter/vulkan/vulkan_swapchain.cc @@ -39,6 +39,10 @@ static std::vector DesiredFormatInfos() { {VK_FORMAT_B8G8R8A8_UNORM, kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB()}}; } + +std::mutex VulkanSwapchain::map_mutex_; +std::unordered_map VulkanSwapchain::to_be_present_; + #else static std::vector DesiredFormatInfos() { return {{VK_FORMAT_R8G8B8A8_SRGB, kRGBA_8888_SkColorType, @@ -407,31 +411,42 @@ VulkanSwapchain::AcquireResult VulkanSwapchain::AcquireSurface() { return error; } - // --------------------------------------------------------------------------- - // Step 1: - // Wait for use readiness. - // --------------------------------------------------------------------------- - if (!backbuffer->WaitFences()) { #ifdef RS_ENABLE_VK - LOGE("Failed waiting on fences."); + // ----------------------------------------------------------------------------- + // when back buffer is used in multi threading mode it need to wait shared fence + // instead of its private fence + // ----------------------------------------------------------------------------- + + if (!backbuffer->IsMultiThreading()) { +#endif + // --------------------------------------------------------------------------- + // Step 1: + // Wait for use readiness. + // --------------------------------------------------------------------------- + if (!backbuffer->WaitFences()) { +#ifdef RS_ENABLE_VK + LOGE("Failed waiting on fences."); #else - FML_DLOG(INFO) << "Failed waiting on fences."; + FML_DLOG(INFO) << "Failed waiting on fences."; #endif - return error; - } + return error; + } // --------------------------------------------------------------------------- // Step 2: // Put semaphores in unsignaled state. // --------------------------------------------------------------------------- - if (!backbuffer->ResetFences()) { + if (!backbuffer->ResetFences()) { #ifdef RS_ENABLE_VK - LOGE("Could not reset fences."); + LOGE("Could not reset fences."); #else - FML_DLOG(INFO) << "Could not reset fences."; + FML_DLOG(INFO) << "Could not reset fences."; +#endif + return error; + } +#ifdef RS_ENABLE_VK + } // !backbuffer->IsMultiThreading() #endif - return error; - } // --------------------------------------------------------------------------- // Step 3: @@ -560,6 +575,11 @@ VulkanSwapchain::AcquireResult VulkanSwapchain::AcquireSurface() { return error; } +#ifdef RS_ENABLE_VK + // reset to not under multi-threading by default + // the reality will be judged later in flush stage + backbuffer->UnsetMultiThreading(); +#endif // --------------------------------------------------------------------------- // Step 8: // Tell Skia about the updated image layout. @@ -592,6 +612,139 @@ VulkanSwapchain::AcquireResult VulkanSwapchain::AcquireSurface() { return {AcquireStatus::Success, surface}; } +#ifdef RS_ENABLE_VK +bool VulkanSwapchain::FlushCommands() { + if (!IsValid()) { + LOGE("Swapchain was invalid."); + return false; + } + + sk_sp surface = surfaces_[current_image_index_]; + const std::unique_ptr& image = images_[current_image_index_]; + auto backbuffer = backbuffers_[current_backbuffer_index_].get(); + + // --------------------------------------------------------------------------- + // Step 0: + // Make sure Skia has flushed all work for the surface to the gpu. + // --------------------------------------------------------------------------- + surface->flush(); + + // --------------------------------------------------------------------------- + // Step 1: + // Start recording to the command buffer. + // --------------------------------------------------------------------------- + if (!backbuffer->GetRenderCommandBuffer().Begin()) { + LOGE("Could not start recording to the command buffer."); + return false; + } + + // --------------------------------------------------------------------------- + // Step 2: + // Set image layout to present mode. + // --------------------------------------------------------------------------- + VkPipelineStageFlagBits destination_pipeline_stage = + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + VkImageLayout destination_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + if (!image->InsertImageMemoryBarrier( + backbuffer->GetRenderCommandBuffer(), // command buffer + current_pipeline_stage_, // src_pipeline_bits + destination_pipeline_stage, // dest_pipeline_bits + VK_ACCESS_MEMORY_READ_BIT, // dest_access_flags + destination_image_layout // dest_layout + )) { + LOGE("Could not insert memory barrier."); + return false; + } else { + current_pipeline_stage_ = destination_pipeline_stage; + } + + // --------------------------------------------------------------------------- + // Step 3: + // End recording to the command buffer + // --------------------------------------------------------------------------- + if (!backbuffer->GetRenderCommandBuffer().End()) { + LOGE("Could not end recording to the command buffer."); + return false; + } +} + +void VulkanSwapchain::AddToPresent() { + std::lock_guard lock(map_mutex_); + to_be_present_[std::this_thread::get_id()] = this; +} + +void VulkanSwapchain::PresentAll(VulkanHandle& shared_fence) { + if (to_be_present_.empty()) { + LOGE("nothing to be presented"); + return; + } + + std::lock_guard lock(map_mutex_); + // --------------------------------------------------------------------------- + // Submit all the command buffer to the device queue. Tell it to signal the render + // semaphore. + // --------------------------------------------------------------------------- + std::vector wait_semaphores = {}; + std::vector queue_signal_semaphores; + std::vector command_buffers; + std::vector swapchains; + std::vector present_image_index; + queue_signal_semaphores.reserve(to_be_present_.size()); + command_buffers.reserve(to_be_present_.size()); + swapchains.reserve(to_be_present_.size()); + present_image_indices_.reserve(to_be_present_.size()); + VulkanSwapchain* tmpSwapChain = nullptr; + for (const auto& entry : to_be_present_) { + auto swapchain = entry.second; + if (!tmpSwapChain) tmpSwapChain = swapchain; + auto backbuffer = swapchain->backbuffers_[swapchain->current_backbuffer_index_].get(); + backbuffer->SetMultiThreading(); + queue_signal_semaphores.push_back(backbuffer->GetRenderSemaphore()); + command_buffers.push_back(backbuffer->GetRenderCommandBuffer().Handle()); + swapchains.push_back(swapchain->swapchain_); + present_image_indices_.push_back(static_cast(swapchain->current_image_index_)); + } + + const VulkanProcTable& vk = tmpSwapChain->vk; + const VulkanDevice& device = tmpSwapChain->device_; + + if (!device.QueueSubmit( + {/*Empty, No wait Semaphores. */}, + wait_semaphores, + queue_signal_semaphores, + command_buffers, + shared_fence + )) { + LOGE("Could not submit to the device queue"); + return; + } + + // ---------------------------------------- + // present multiple swapchain all at once + // ---------------------------------------- + const VkPresentInfoKHR present_info = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = + static_cast (queue_signal_semaphores.size()), + .pWaitSemaphores = queue_signal_semaphores.data(), + .swapchainCount = static_cast(swapchains.size()), + .pSwapchains = swapchains.data(), + .pImageIndices = present_image_indices_.data(), + .pResults = nullptr, + }; + + if (VK_CALL_LOG_ERROR(vk.QueuePresentKHR(device.GetQueueHandle(), + &present_info)) != VK_SUCCESS) { + LOGE("Could not submit the present operation"); + return; + } + + to_be_present_.clear(); +} +#endif + bool VulkanSwapchain::Submit() { if (!IsValid()) { #ifdef RS_ENABLE_VK @@ -689,6 +842,9 @@ bool VulkanSwapchain::Submit() { return false; } +#ifdef RS_ENABLE_VK + backbuffer->UnsetMultiThreading(); +#endif // --------------------------------------------------------------------------- // Step 5: // Submit the present operation and wait on the render semaphore. diff --git a/engine/flutter/vulkan/vulkan_swapchain.h b/engine/flutter/vulkan/vulkan_swapchain.h index 2cb8922d2fafc8cd34606a5a5c722dd22edc56ad..4cea015777eb10f2122a648bcf4914811169b401 100644 --- a/engine/flutter/vulkan/vulkan_swapchain.h +++ b/engine/flutter/vulkan/vulkan_swapchain.h @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #ifndef RS_ENABLE_VK #include "flutter/fml/compiler_specific.h" @@ -66,6 +69,12 @@ class VulkanSwapchain { SkISize GetSize() const; #ifdef RS_ENABLE_VK + bool FlushCommands(); + + void AddToPresent(); + + static void PresentAll(VulkanHandle& shared_fence); + private: const VulkanProcTable& vk; const VulkanDevice& device_; @@ -79,6 +88,8 @@ class VulkanSwapchain { size_t current_backbuffer_index_; size_t current_image_index_; bool valid_; + static std::mutex map_mutex_; + static std::unordered_map to_be_present_; std::vector GetImages() const; diff --git a/engine/flutter/vulkan/vulkan_window.cc b/engine/flutter/vulkan/vulkan_window.cc index fd339c41c2f2be99affe62c904d675fa79d005a6..047749f2581ed4c12b88f7766ec4b9cf66eb8c1c 100644 --- a/engine/flutter/vulkan/vulkan_window.cc +++ b/engine/flutter/vulkan/vulkan_window.cc @@ -23,8 +23,12 @@ namespace vulkan { VulkanProcTable* VulkanWindow::vk; std::unique_ptr VulkanWindow::application_; std::unique_ptr VulkanWindow::logical_device_; +std::thread::id VulkanWindow::device_thread_; +std::vector> VulkanWindow::shared_fences_; +uint32_t VulkanWindow::shared_fence_index_; +bool VulkanWindow::presenting_ = false; -void VulkanWindow::InitializeVulkan() +void VulkanWindow::InitializeVulkan(size_t thread_num) { if (logical_device_ != nullptr) { LOGI("Vulkan Already Initialized"); @@ -59,6 +63,13 @@ void VulkanWindow::InitializeVulkan() LOGE("Device proc addresses have not been setup."); return; } + + device_thread_ = std::this_thread::get_id(); + + if (shared_fences_.empty() && thread_num > 0) { + shared_fences_.resize(thread_num); + shared_fence_index_ = 0; + } } VulkanWindow::VulkanWindow(std::unique_ptr native_surface, bool is_offscreen) @@ -352,16 +363,77 @@ bool VulkanWindow::SwapBuffers() { LOGE("Window was invalid or offscreen."); return false; } + if (device_thread_ != std::this_thread::get_id()) { + LOGI("MT mode in VulkanWindow::SwapBuffers()"); + swapchain_->AddToPresent(); + return swapchain_->FlushCommands(); + } #else if (!IsValid()) { FML_DLOG(INFO) << "Window was invalid."; return false; } #endif - + LOGI("ST mode in VulkanWindow::SwapBuffers()"); return swapchain_->Submit(); } +#ifdef RS_ENABLE_VK +void VulkanWindow::PresentAll() { + //----------------------------------------- + // create shared fences if not already + //----------------------------------------- + if (!shared_fences_[shared_fence_index_]) { + const VkFenceCreateInfo create_info = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + + auto fence_collect = [](VkFence fence) { + VulkanWindow::vk->DestroyFence(VulkanWindow::logical_device_->GetHandle(), fence, nullptr); + }; + + VkFence fence = VK_NULL_HANDLE; + + if (VK_CALL_LOG_ERROR(vk->CreateFence(logical_device_->GetHandle(), &create_info, nullptr, &fence)) != VK_SUCCESS) { + return; + } + shared_fences_[shared_fence_index_] = {fence, fence_collect}; + } + VulkanSwapchain::PresentAll(shared_fences_[shared_fence_index_]); + shared_fence_index_++; + if (shared_fence_index_ >= shared_fences_.size()) { + shared_fence_index_ = 0; + } + presenting_ = true; +} + +bool VulkanWindow::WaitForSharedFence() { + if (presenting_) { + if (shared_fences_[shared_fence_index_]) { + VkFence fence = shared_fences_[shared_fence_index_]; + return VK_CALL_LOG_ERROR(vk->WaitForFences( + logical_device_->GetHandle(), 1, &fence, true, + std::numeric_limits::max())) == VK_SUCCESS; + } + } + return false; +} + +bool VulkanWindow::ResetSharedFence() { + if (presenting_) { + presenting_ = false; + if (shared_fences_[shared_fence_index_]) { + VkFence fence = shared_fences_[shared_fence_index_]; + return VK_CALL_LOG_ERROR(vk->ResetFences( + logical_device_->GetHandle(), 1, &fence)) == VK_SUCCESS; + } + } + return false; +} +#endif + bool VulkanWindow::RecreateSwapchain() { #ifdef RS_ENABLE_VK if (is_offscreen_) { diff --git a/engine/flutter/vulkan/vulkan_window.h b/engine/flutter/vulkan/vulkan_window.h index 8ec9de0b38fdee28ecd71c49ccbacb0f9f83556a..1fde3c79f185b034af728666b6da2f59a945b7c0 100644 --- a/engine/flutter/vulkan/vulkan_window.h +++ b/engine/flutter/vulkan/vulkan_window.h @@ -34,6 +34,7 @@ class VulkanBackbuffer; class VulkanWindow { public: #ifdef RS_ENABLE_VK + typedef std::shared_ptr Ptr; VulkanWindow(std::unique_ptr native_surface, bool is_offscreen = false); #else @@ -51,6 +52,14 @@ class VulkanWindow { bool SwapBuffers(); +#ifdef RS_ENABLE_VK + bool FlushCommands(); + static void PresentAll(); + static void InitializeVulkan(size_t thread_num = 0); + static void WaitForSharedFence(); + static bool ResetSharedFence(); +#endif + private: bool valid_; #ifdef RS_ENABLE_VK @@ -58,6 +67,10 @@ class VulkanWindow { static std::unique_ptr application_; static std::unique_ptr logical_device_; bool is_offscreen_ = false; + static std::thread::id device_thread_; + static std::vector> shared_fences_; + static uint32_t shared_fence_index_; + static bool presenting_; #else fml::RefPtr vk; std::unique_ptr application_; @@ -67,9 +80,6 @@ class VulkanWindow { std::unique_ptr swapchain_; sk_sp skia_gr_context_; -#ifdef RS_ENABLE_VK - static void InitializeVulkan(); -#endif bool CreateSkiaGrContext(); bool CreateSkiaBackendContext(GrVkBackendContext* context);