diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 0c0cbc5d51f0a..49f5af8529f2a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1537,6 +1537,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.cc + ../../.. ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/sampler_library_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/sampler_library_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/sampler_vk.cc + ../../../flutter/LICENSE @@ -4223,6 +4225,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/queue_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/render_pass_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/sampler_library_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/sampler_library_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/sampler_vk.cc diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index eb2953985ec8b..bb75156ab63db 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -67,6 +67,8 @@ impeller_component("vulkan") { "queue_vk.h", "render_pass_vk.cc", "render_pass_vk.h", + "resource_manager_vk.cc", + "resource_manager_vk.h", "sampler_library_vk.cc", "sampler_library_vk.h", "sampler_vk.cc", diff --git a/impeller/renderer/backend/vulkan/allocator_vk.cc b/impeller/renderer/backend/vulkan/allocator_vk.cc index 6ce8ea7e6812f..d0af9e4afff2d 100644 --- a/impeller/renderer/backend/vulkan/allocator_vk.cc +++ b/impeller/renderer/backend/vulkan/allocator_vk.cc @@ -241,11 +241,12 @@ static VmaAllocationCreateFlags ToVmaAllocationCreateFlags(StorageMode mode, class AllocatedTextureSourceVK final : public TextureSourceVK { public: - AllocatedTextureSourceVK(const TextureDescriptor& desc, + AllocatedTextureSourceVK(std::weak_ptr resource_manager, + const TextureDescriptor& desc, VmaAllocator allocator, vk::Device device, bool supports_memoryless_textures) - : TextureSourceVK(desc) { + : TextureSourceVK(desc), resource_(std::move(resource_manager)) { TRACE_EVENT0("impeller", "CreateDeviceTexture"); vk::ImageCreateInfo image_info; image_info.flags = ToVKImageCreateFlags(desc.type); @@ -305,12 +306,10 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { } } - image_ = vk::Image{vk_image}; - allocator_ = allocator; - allocation_ = allocation; + auto image = vk::Image{vk_image}; vk::ImageViewCreateInfo view_info = {}; - view_info.image = image_; + view_info.image = image; view_info.viewType = ToVKImageViewType(desc.type); view_info.format = image_info.format; view_info.subresourceRange.aspectMask = ToVKImageAspectFlags(desc.format); @@ -332,34 +331,65 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { << vk::to_string(result); return; } - image_view_ = std::move(image_view); - + resource_.Reset( + ImageResource(image, allocator, allocation, std::move(image_view))); is_valid_ = true; } - ~AllocatedTextureSourceVK() { - TRACE_EVENT0("impeller", "DestroyDeviceTexture"); - image_view_.reset(); - if (image_) { - ::vmaDestroyImage( - allocator_, // - static_cast(image_), // - allocation_ // - ); - } - } + ~AllocatedTextureSourceVK() = default; bool IsValid() const { return is_valid_; } - vk::Image GetImage() const override { return image_; } + vk::Image GetImage() const override { return resource_->image; } - vk::ImageView GetImageView() const override { return image_view_.get(); } + vk::ImageView GetImageView() const override { + return resource_->image_view.get(); + } private: - vk::Image image_ = {}; - VmaAllocator allocator_ = {}; - VmaAllocation allocation_ = {}; - vk::UniqueImageView image_view_; + struct ImageResource { + vk::Image image = {}; + VmaAllocator allocator = {}; + VmaAllocation allocation = {}; + vk::UniqueImageView image_view; + + ImageResource() = default; + + ImageResource(vk::Image p_image, + VmaAllocator p_allocator, + VmaAllocation p_allocation, + vk::UniqueImageView p_image_view) + : image(p_image), + allocator(p_allocator), + allocation(p_allocation), + image_view(std::move(p_image_view)) {} + + ImageResource(ImageResource&& o) { + std::swap(image, o.image); + std::swap(allocator, o.allocator); + std::swap(allocation, o.allocation); + std::swap(image_view, o.image_view); + } + + ~ImageResource() { + if (!image) { + return; + } + TRACE_EVENT0("impeller", "DestroyDeviceTexture"); + image_view.reset(); + if (image) { + ::vmaDestroyImage( + allocator, // + static_cast(image), // + allocation // + ); + } + } + + FML_DISALLOW_COPY_AND_ASSIGN(ImageResource); + }; + + UniqueResourceVKT resource_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(AllocatedTextureSourceVK); @@ -376,11 +406,16 @@ std::shared_ptr AllocatorVK::OnCreateTexture( if (!device_holder) { return nullptr; } + auto context = context_.lock(); + if (!context) { + return nullptr; + } auto source = std::make_shared( - desc, // - allocator_, // - device_holder->GetDevice(), // - supports_memoryless_textures_ // + ContextVK::Cast(*context).GetResourceManager(), // + desc, // + allocator_, // + device_holder->GetDevice(), // + supports_memoryless_textures_ // ); if (!source->IsValid()) { return nullptr; diff --git a/impeller/renderer/backend/vulkan/context_vk.cc b/impeller/renderer/backend/vulkan/context_vk.cc index 0b1fc69001e33..116aab140aa5e 100644 --- a/impeller/renderer/backend/vulkan/context_vk.cc +++ b/impeller/renderer/backend/vulkan/context_vk.cc @@ -29,6 +29,7 @@ #include "impeller/renderer/backend/vulkan/debug_report_vk.h" #include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/resource_manager_vk.h" #include "impeller/renderer/backend/vulkan/surface_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/capabilities.h" @@ -379,6 +380,15 @@ void ContextVK::Setup(Settings settings) { return; } + //---------------------------------------------------------------------------- + /// Create the resource manager. + /// + auto resource_manager = ResourceManagerVK::Create(); + if (!resource_manager) { + VALIDATION_LOG << "Could not create resource manager."; + return; + } + //---------------------------------------------------------------------------- /// Fetch the queues. /// @@ -408,6 +418,7 @@ void ContextVK::Setup(Settings settings) { queues_ = std::move(queues); device_capabilities_ = std::move(caps); fence_waiter_ = std::move(fence_waiter); + resource_manager_ = std::move(resource_manager); device_name_ = std::string(physical_device_properties.deviceName); is_valid_ = true; @@ -533,6 +544,10 @@ std::shared_ptr ContextVK::GetFenceWaiter() const { return fence_waiter_; } +std::shared_ptr ContextVK::GetResourceManager() const { + return resource_manager_; +} + std::unique_ptr ContextVK::CreateGraphicsCommandEncoder() const { auto tls_pool = CommandPoolVK::GetThreadLocal(this); diff --git a/impeller/renderer/backend/vulkan/context_vk.h b/impeller/renderer/backend/vulkan/context_vk.h index 25f1ff6890d17..9f9b9af9b1999 100644 --- a/impeller/renderer/backend/vulkan/context_vk.h +++ b/impeller/renderer/backend/vulkan/context_vk.h @@ -30,6 +30,7 @@ bool HasValidationLayers(); class CommandEncoderVK; class DebugReportVK; class FenceWaiterVK; +class ResourceManagerVK; class ContextVK final : public Context, public BackendCast, @@ -136,6 +137,8 @@ class ContextVK final : public Context, std::shared_ptr GetFenceWaiter() const; + std::shared_ptr GetResourceManager() const; + private: struct DeviceHolderImpl : public DeviceHolder { // |DeviceHolder| @@ -160,6 +163,7 @@ class ContextVK final : public Context, std::shared_ptr swapchain_; std::shared_ptr device_capabilities_; std::shared_ptr fence_waiter_; + std::shared_ptr resource_manager_; std::string device_name_; std::shared_ptr raster_message_loop_; const uint64_t hash_; diff --git a/impeller/renderer/backend/vulkan/device_buffer_vk.cc b/impeller/renderer/backend/vulkan/device_buffer_vk.cc index 8f1911b10d4b7..d113e853bb194 100644 --- a/impeller/renderer/backend/vulkan/device_buffer_vk.cc +++ b/impeller/renderer/backend/vulkan/device_buffer_vk.cc @@ -18,22 +18,18 @@ DeviceBufferVK::DeviceBufferVK(DeviceBufferDescriptor desc, vk::Buffer buffer) : DeviceBuffer(desc), context_(std::move(context)), - allocator_(allocator), - allocation_(allocation), - info_(info), - buffer_(buffer) {} - -DeviceBufferVK::~DeviceBufferVK() { - TRACE_EVENT0("impeller", "DestroyDeviceBuffer"); - if (buffer_) { - ::vmaDestroyBuffer(allocator_, - static_cast(buffer_), - allocation_); - } -} + resource_(ContextVK::Cast(*context_.lock().get()).GetResourceManager(), + BufferResource{ + allocator, // + allocation, // + info, // + buffer // + }) {} + +DeviceBufferVK::~DeviceBufferVK() {} uint8_t* DeviceBufferVK::OnGetContents() const { - return static_cast(info_.pMappedData); + return static_cast(resource_->info.pMappedData); } bool DeviceBufferVK::OnCopyHostBuffer(const uint8_t* source, @@ -55,14 +51,17 @@ bool DeviceBufferVK::OnCopyHostBuffer(const uint8_t* source, bool DeviceBufferVK::SetLabel(const std::string& label) { auto context = context_.lock(); - if (!context || !buffer_) { + if (!context || !resource_->buffer) { // The context could have died at this point. return false; } - ::vmaSetAllocationName(allocator_, allocation_, label.c_str()); + ::vmaSetAllocationName(resource_->allocator, // + resource_->allocation, // + label.c_str() // + ); - return ContextVK::Cast(*context).SetDebugName(buffer_, label); + return ContextVK::Cast(*context).SetDebugName(resource_->buffer, label); } bool DeviceBufferVK::SetLabel(const std::string& label, Range range) { @@ -71,7 +70,7 @@ bool DeviceBufferVK::SetLabel(const std::string& label, Range range) { } vk::Buffer DeviceBufferVK::GetBuffer() const { - return buffer_; + return resource_->buffer; } } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/device_buffer_vk.h b/impeller/renderer/backend/vulkan/device_buffer_vk.h index 229a1b6b04325..bf5c033b482d8 100644 --- a/impeller/renderer/backend/vulkan/device_buffer_vk.h +++ b/impeller/renderer/backend/vulkan/device_buffer_vk.h @@ -7,9 +7,11 @@ #include #include "flutter/fml/macros.h" +#include "flutter/fml/trace_event.h" #include "impeller/base/backend_cast.h" #include "impeller/core/device_buffer.h" #include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/resource_manager_vk.h" namespace impeller { @@ -31,11 +33,45 @@ class DeviceBufferVK final : public DeviceBuffer, private: friend class AllocatorVK; + struct BufferResource { + VmaAllocator allocator = {}; + VmaAllocation allocation = {}; + VmaAllocationInfo info = {}; + vk::Buffer buffer = {}; + + BufferResource() = default; + + BufferResource(VmaAllocator p_allocator, + VmaAllocation p_allocation, + VmaAllocationInfo p_info, + vk::Buffer p_buffer) + : allocator(p_allocator), + allocation(p_allocation), + info(p_info), + buffer(p_buffer) {} + + BufferResource(BufferResource&& o) { + std::swap(o.allocator, allocator); + std::swap(o.allocation, allocation); + std::swap(o.info, info); + std::swap(o.buffer, buffer); + } + + ~BufferResource() { + if (!buffer) { + return; + } + TRACE_EVENT0("impeller", "DestroyDeviceBuffer"); + ::vmaDestroyBuffer(allocator, + static_cast(buffer), + allocation); + } + + FML_DISALLOW_COPY_AND_ASSIGN(BufferResource); + }; + std::weak_ptr context_; - VmaAllocator allocator_ = {}; - VmaAllocation allocation_ = {}; - VmaAllocationInfo info_ = {}; - vk::Buffer buffer_ = {}; + UniqueResourceVKT resource_; // |DeviceBuffer| uint8_t* OnGetContents() const override; diff --git a/impeller/renderer/backend/vulkan/resource_manager_vk.cc b/impeller/renderer/backend/vulkan/resource_manager_vk.cc new file mode 100644 index 0000000000000..1be606bfdba17 --- /dev/null +++ b/impeller/renderer/backend/vulkan/resource_manager_vk.cc @@ -0,0 +1,75 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/resource_manager_vk.h" + +#include "flutter/fml/thread.h" +#include "flutter/fml/trace_event.h" + +namespace impeller { + +std::shared_ptr ResourceManagerVK::Create() { + return std::shared_ptr(new ResourceManagerVK()); +} + +ResourceManagerVK::ResourceManagerVK() : waiter_([&]() { Main(); }) {} + +ResourceManagerVK::~ResourceManagerVK() { + Terminate(); + waiter_.join(); +} + +void ResourceManagerVK::Main() { + fml::Thread::SetCurrentThreadName( + fml::Thread::ThreadConfig{"io.flutter.impeller.resource_manager"}); + + bool should_exit = false; + while (!should_exit) { + std::unique_lock lock(reclaimables_mutex_); + + // Wait until there are reclaimable resource or if the manager should be + // torn down. + reclaimables_cv_.wait( + lock, [&]() { return !reclaimables_.empty() || should_exit_; }); + + // Don't reclaim resources when the lock is being held as this may gate + // further reclaimables from being registered. + Reclaimables resources_to_collect; + std::swap(resources_to_collect, reclaimables_); + + // We can't read the ivar outside the lock. Read it here instead. + should_exit = should_exit_; + + // We know what to collect. Unlock before doing anything else. + lock.unlock(); + + // Claim all resources while tracing. + { + TRACE_EVENT0("Impeller", "ReclaimResources"); + resources_to_collect.clear(); // Redundant because of scope but here so + // we can add a trace around it. + } + } +} + +void ResourceManagerVK::Reclaim(std::unique_ptr resource) { + if (!resource) { + return; + } + { + std::scoped_lock lock(reclaimables_mutex_); + reclaimables_.emplace_back(std::move(resource)); + } + reclaimables_cv_.notify_one(); +} + +void ResourceManagerVK::Terminate() { + { + std::scoped_lock lock(reclaimables_mutex_); + should_exit_ = true; + } + reclaimables_cv_.notify_one(); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/resource_manager_vk.h b/impeller/renderer/backend/vulkan/resource_manager_vk.h new file mode 100644 index 0000000000000..1acc90f60e3b8 --- /dev/null +++ b/impeller/renderer/backend/vulkan/resource_manager_vk.h @@ -0,0 +1,149 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A resource that will be reclaimed by the resource manager. Do +/// not subclass this yourself. Instead, use the `UniqueResourceVKT` +/// template +/// +class ResourceVK { + public: + virtual ~ResourceVK() = default; +}; + +//------------------------------------------------------------------------------ +/// @brief A resource manager controls how resources are allocated and +/// reclaimed. +/// +/// Reclaimed resources are collected in a batch on a separate +/// thread. In the future, the resource manager may allow resource +/// pooling/reuse, delaying reclamation past frame workloads, etc... +/// +class ResourceManagerVK + : public std::enable_shared_from_this { + public: + //---------------------------------------------------------------------------- + /// @brief Create a shared resource manager. This creates a dedicated + /// thread for resource management. Only one is created per Vulkan + /// context. + /// + /// @return A resource manager if one could be created. + /// + static std::shared_ptr Create(); + + //---------------------------------------------------------------------------- + /// @brief Destroy the resource manager and join all thread. An + /// unreclaimed resources will be destroyed now. + /// + ~ResourceManagerVK(); + + //---------------------------------------------------------------------------- + /// @brief Mark a resource as being reclaimable by giving ownership of + /// the resource to the resource manager. + /// + /// @param[in] resource The resource to reclaim. + /// + void Reclaim(std::unique_ptr resource); + + //---------------------------------------------------------------------------- + /// @brief Terminate the resource manager. Any resources given to the + /// resource manager post termination will be collected when the + /// resource manager is collected. + /// + void Terminate(); + + private: + ResourceManagerVK(); + + using Reclaimables = std::vector>; + + std::thread waiter_; + std::mutex reclaimables_mutex_; + std::condition_variable reclaimables_cv_; + Reclaimables reclaimables_; + bool should_exit_ = false; + + void Main(); + + FML_DISALLOW_COPY_AND_ASSIGN(ResourceManagerVK); +}; + +template +class ResourceVKT : public ResourceVK { + public: + using ResourceType = ResourceType_; + + explicit ResourceVKT(ResourceType&& resource) + : resource_(std::move(resource)) {} + + const ResourceType* Get() const { return &resource_; } + + private: + ResourceType resource_; + + FML_DISALLOW_COPY_AND_ASSIGN(ResourceVKT); +}; + +//------------------------------------------------------------------------------ +/// @brief A unique handle to a resource which will be reclaimed by the +/// specified resource manager. +/// +/// @tparam ResourceType_ A move-constructible resource type. +/// +template +class UniqueResourceVKT { + public: + using ResourceType = ResourceType_; + + explicit UniqueResourceVKT(std::weak_ptr resource_manager) + : resource_manager_(std::move(resource_manager)) {} + + explicit UniqueResourceVKT(std::weak_ptr resource_manager, + ResourceType&& resource) + : resource_manager_(std::move(resource_manager)), + resource_( + std::make_unique>(std::move(resource))) {} + + ~UniqueResourceVKT() { Reset(); } + + const ResourceType* operator->() const { return resource_.get()->Get(); } + + void Reset(ResourceType&& other) { + Reset(); + resource_ = std::make_unique>(std::move(other)); + } + + void Reset() { + if (!resource_) { + return; + } + // If there is a manager, ask it to reclaim the resource. If there isn't a + // manager, just drop it on the floor here. + if (auto manager = resource_manager_.lock()) { + manager->Reclaim(std::move(resource_)); + } + resource_.reset(); + } + + private: + std::weak_ptr resource_manager_; + std::unique_ptr> resource_; + + FML_DISALLOW_COPY_AND_ASSIGN(UniqueResourceVKT); +}; + +} // namespace impeller