| // 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 <condition_variable> |
| #include <memory> |
| #include <mutex> |
| #include <thread> |
| #include <vector> |
| |
| #include "flutter/fml/macros.h" |
| |
| namespace impeller { |
| |
| //------------------------------------------------------------------------------ |
| /// @brief A resource that may be reclaimed by a |ResourceManagerVK|. |
| /// |
| /// To create a resource, use `UniqueResourceVKT` to create a unique handle: |
| /// |
| /// auto resource = UniqueResourceVKT<SomeResource>(resource_manager); |
| /// |
| /// @see |ResourceManagerVK::Reclaim|. |
| 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 final |
| : public std::enable_shared_from_this<ResourceManagerVK> { |
| public: |
| //---------------------------------------------------------------------------- |
| /// @brief Creates a shared resource manager (a dedicated thread). |
| /// |
| /// Upon creation, a thread is spawned which will collect resources as they |
| /// are reclaimed (passed to `Reclaim`). The thread will exit when the |
| /// resource manager is destroyed. |
| /// |
| /// @note Only one |ResourceManagerVK| should be created per Vulkan |
| /// context, but that contract is not enforced by this method. |
| /// |
| /// @return A resource manager if one could be created. |
| /// |
| static std::shared_ptr<ResourceManagerVK> Create(); |
| |
| //---------------------------------------------------------------------------- |
| /// @brief Mark a resource as being reclaimable. |
| /// |
| /// The resource will be reset at some point in the future. |
| /// |
| /// @param[in] resource The resource to reclaim. |
| /// |
| /// @note Despite being a public API, this method cannot be invoked |
| /// directly. Instead, use `UniqueResourceVKT` to create a unique |
| /// handle to a resource, which will call this method. |
| void Reclaim(std::unique_ptr<ResourceVK> resource); |
| |
| //---------------------------------------------------------------------------- |
| /// @brief Destroys the resource manager. |
| /// |
| /// The resource manager will stop collecting resources and will be destroyed |
| /// when all references to it are dropped. |
| ~ResourceManagerVK(); |
| |
| private: |
| using Reclaimables = std::vector<std::unique_ptr<ResourceVK>>; |
| |
| ResourceManagerVK(); |
| std::mutex reclaimables_mutex_; |
| std::condition_variable reclaimables_cv_; |
| Reclaimables reclaimables_; |
| bool should_exit_ = false; |
| // This should be initialized last since it references the other instance |
| // variables. |
| std::thread waiter_; |
| |
| //---------------------------------------------------------------------------- |
| /// @brief Starts the resource manager thread. |
| /// |
| /// This method is called implicitly by `Create`. |
| void Start(); |
| |
| //---------------------------------------------------------------------------- |
| /// @brief Terminates the resource manager thread. |
| /// |
| /// Any resources given to the resource manager post termination will be |
| /// collected when the resource manager is collected. |
| void Terminate(); |
| |
| ResourceManagerVK(const ResourceManagerVK&) = delete; |
| |
| ResourceManagerVK& operator=(const ResourceManagerVK&) = delete; |
| }; |
| |
| //------------------------------------------------------------------------------ |
| /// @brief An internal type that is used to move a resource reference. |
| /// |
| /// Do not use directly, use `UniqueResourceVKT` instead. |
| /// |
| /// @tparam ResourceType_ The type of the resource. |
| /// |
| /// @see |UniqueResourceVKT|. |
| template <class ResourceType_> |
| class ResourceVKT : public ResourceVK { |
| public: |
| using ResourceType = ResourceType_; |
| |
| /// @brief Construct a resource from a move-constructible resource. |
| /// |
| /// @param[in] resource The resource to move. |
| explicit ResourceVKT(ResourceType&& resource) |
| : resource_(std::move(resource)) {} |
| |
| /// @brief Returns a pointer to the resource. |
| const ResourceType* Get() const { return &resource_; } |
| |
| private: |
| // Prevents subclassing, use `UniqueResourceVKT`. |
| ResourceVKT() = default; |
| |
| ResourceType resource_; |
| |
| ResourceVKT(const ResourceVKT&) = delete; |
| |
| ResourceVKT& operator=(const ResourceVKT&) = delete; |
| }; |
| |
| //------------------------------------------------------------------------------ |
| /// @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 ResourceType_> |
| class UniqueResourceVKT final { |
| public: |
| using ResourceType = ResourceType_; |
| |
| /// @brief Construct a unique resource handle belonging to a manager. |
| /// |
| /// Initially the handle is empty, and can be populated by calling `Swap`. |
| /// |
| /// @param[in] resource_manager The resource manager. |
| explicit UniqueResourceVKT(std::weak_ptr<ResourceManagerVK> resource_manager) |
| : resource_manager_(std::move(resource_manager)) {} |
| |
| /// @brief Construct a unique resource handle belonging to a manager. |
| /// |
| /// Initially the handle is populated with the specified resource, but can |
| /// be replaced (reclaiming the old resource) by calling `Swap`. |
| /// |
| /// @param[in] resource_manager The resource manager. |
| /// @param[in] resource The resource to move. |
| explicit UniqueResourceVKT(std::weak_ptr<ResourceManagerVK> resource_manager, |
| ResourceType&& resource) |
| : resource_manager_(std::move(resource_manager)), |
| resource_( |
| std::make_unique<ResourceVKT<ResourceType>>(std::move(resource))) {} |
| |
| ~UniqueResourceVKT() { Reset(); } |
| |
| /// @brief Returns a pointer to the resource. |
| const ResourceType* operator->() const { |
| // If this would segfault, fail with a nicer error message. |
| FML_CHECK(resource_) << "UniqueResourceVKT was reclaimed."; |
| |
| return resource_.get()->Get(); |
| } |
| |
| /// @brief Reclaims the existing resource, if any, and replaces it. |
| /// |
| /// @param[in] other The (new) resource to move. |
| void Swap(ResourceType&& other) { |
| Reset(); |
| resource_ = std::make_unique<ResourceVKT<ResourceType>>(std::move(other)); |
| } |
| |
| /// @brief Reclaims the existing resource, if any. |
| void Reset() { |
| if (!resource_) { |
| return; |
| } |
| // If there is a manager, ask it to reclaim the resource. If there isn't a |
| // manager (because the manager has been destroyed), just drop it on the |
| // floor here. |
| if (auto manager = resource_manager_.lock()) { |
| manager->Reclaim(std::move(resource_)); |
| } |
| resource_.reset(); |
| } |
| |
| private: |
| std::weak_ptr<ResourceManagerVK> resource_manager_; |
| std::unique_ptr<ResourceVKT<ResourceType>> resource_; |
| |
| UniqueResourceVKT(const UniqueResourceVKT&) = delete; |
| |
| UniqueResourceVKT& operator=(const UniqueResourceVKT&) = delete; |
| }; |
| |
| } // namespace impeller |