blob: 4d8ef36a1c1bdb9e6c69589577cf3ba0896a63b5 [file] [log] [blame]
// 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.
#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RESOURCE_MANAGER_VK_H_
#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RESOURCE_MANAGER_VK_H_
#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
#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_RESOURCE_MANAGER_VK_H_