Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8d07c29

Browse files
authored
[Impeller] Document and slightly refactor ResourceManagerVK & friends. (#45474)
@chinmaygarde specifically, let me know if I violated any correctness you were looking for, or if you would prefer I _don't_ make 1 or more of the changes I made here. @gaaclarke would love your input on C++ style specifically, or anything that stands out in general. --- ## Summary I plan to use the resource strategy in flutter/flutter#133198, but wanted to understand the code better first, so this is my contribution to make it a bit easier to understand (hopefully! push back if not!) and contribute tests. 1. Renamed `Reset(ResourceType)` to `Swap()` for consistency with the std smart pointers. 2. Moved non-trivial work out of the constructor into `::Create` ([style guide](https://google.github.io/styleguide/cppguide.html#Doing_Work_in_Constructors)). 3. Added some `FML_DCHECK`s to private APIs to enforce correctness. 4. Made classes final and methods private where they were effectively that ([style guide](https://google.github.io/styleguide/cppguide.html#Inheritance)). 5. Added tests to make sure I understood the contracts.
1 parent 633ba42 commit 8d07c29

File tree

6 files changed

+159
-31
lines changed

6 files changed

+159
-31
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc
154154
../../../flutter/impeller/renderer/backend/vulkan/context_vk_unittests.cc
155155
../../../flutter/impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc
156+
../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc
156157
../../../flutter/impeller/renderer/backend/vulkan/test
157158
../../../flutter/impeller/renderer/capabilities_unittests.cc
158159
../../../flutter/impeller/renderer/compute_subgroup_unittests.cc

impeller/renderer/backend/vulkan/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ impeller_component("vulkan_unittests") {
1111
"blit_command_vk_unittests.cc",
1212
"context_vk_unittests.cc",
1313
"pass_bindings_cache_unittests.cc",
14+
"resource_manager_vk_unittests.cc",
1415
"test/mock_vulkan.cc",
1516
"test/mock_vulkan.h",
1617
]

impeller/renderer/backend/vulkan/allocator_vk.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,8 @@ class AllocatedTextureSourceVK final : public TextureSourceVK {
365365
<< vk::to_string(result);
366366
return;
367367
}
368-
resource_.Reset(ImageResource(ImageVMA{allocator, allocation, image},
369-
std::move(image_view)));
368+
resource_.Swap(ImageResource(ImageVMA{allocator, allocation, image},
369+
std::move(image_view)));
370370
is_valid_ = true;
371371
}
372372

impeller/renderer/backend/vulkan/resource_manager_vk.cc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,28 @@
66

77
#include "flutter/fml/thread.h"
88
#include "flutter/fml/trace_event.h"
9+
#include "fml/logging.h"
910

1011
namespace impeller {
1112

1213
std::shared_ptr<ResourceManagerVK> ResourceManagerVK::Create() {
13-
return std::shared_ptr<ResourceManagerVK>(new ResourceManagerVK());
14+
auto manager = std::shared_ptr<ResourceManagerVK>(new ResourceManagerVK());
15+
manager->waiter_ = std::thread([manager]() { manager->Start(); });
16+
manager->waiter_.detach();
17+
return manager;
1418
}
1519

16-
ResourceManagerVK::ResourceManagerVK() : waiter_([&]() { Main(); }) {}
20+
ResourceManagerVK::ResourceManagerVK() {}
1721

1822
ResourceManagerVK::~ResourceManagerVK() {
1923
Terminate();
2024
waiter_.join();
2125
}
2226

23-
void ResourceManagerVK::Main() {
27+
void ResourceManagerVK::Start() {
28+
// This thread should not be started more than once.
29+
FML_DCHECK(!should_exit_);
30+
2431
fml::Thread::SetCurrentThreadName(
2532
fml::Thread::ThreadConfig{"io.flutter.impeller.resource_manager"});
2633

@@ -65,6 +72,9 @@ void ResourceManagerVK::Reclaim(std::unique_ptr<ResourceVK> resource) {
6572
}
6673

6774
void ResourceManagerVK::Terminate() {
75+
// The thread should not be terminated more than once.
76+
FML_DCHECK(!should_exit_);
77+
6878
{
6979
std::scoped_lock lock(reclaimables_mutex_);
7080
should_exit_ = true;

impeller/renderer/backend/vulkan/resource_manager_vk.h

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
#include <memory>
99
#include <mutex>
1010
#include <thread>
11-
#include <type_traits>
1211
#include <vector>
1312

1413
#include "flutter/fml/macros.h"
1514

1615
namespace impeller {
1716

1817
//------------------------------------------------------------------------------
19-
/// @brief A resource that will be reclaimed by the resource manager. Do
20-
/// not subclass this yourself. Instead, use the `UniqueResourceVKT`
21-
/// template
18+
/// @brief A resource that may be reclaimed by a |ResourceManagerVK|.
2219
///
20+
/// To create a resource, use `UniqueResourceVKT` to create a unique handle:
21+
///
22+
/// auto resource = UniqueResourceVKT<SomeResource>(resource_manager);
23+
///
24+
/// @see |ResourceManagerVK::Reclaim|.
2325
class ResourceVK {
2426
public:
2527
virtual ~ResourceVK() = default;
@@ -33,66 +35,95 @@ class ResourceVK {
3335
/// thread. In the future, the resource manager may allow resource
3436
/// pooling/reuse, delaying reclamation past frame workloads, etc...
3537
///
36-
class ResourceManagerVK
38+
class ResourceManagerVK final
3739
: public std::enable_shared_from_this<ResourceManagerVK> {
3840
public:
3941
//----------------------------------------------------------------------------
40-
/// @brief Create a shared resource manager. This creates a dedicated
41-
/// thread for resource management. Only one is created per Vulkan
42-
/// context.
42+
/// @brief Creates a shared resource manager (a dedicated thread).
43+
///
44+
/// Upon creation, a thread is spawned which will collect resources as they
45+
/// are reclaimed (passed to `Reclaim`). The thread will exit when the
46+
/// resource manager is destroyed.
47+
///
48+
/// @note Only one |ResourceManagerVK| should be created per Vulkan
49+
/// context, but that contract is not enforced by this method.
4350
///
4451
/// @return A resource manager if one could be created.
4552
///
4653
static std::shared_ptr<ResourceManagerVK> Create();
4754

4855
//----------------------------------------------------------------------------
49-
/// @brief Destroy the resource manager and join all thread. An
50-
/// unreclaimed resources will be destroyed now.
56+
/// @brief Mark a resource as being reclaimable.
5157
///
52-
~ResourceManagerVK();
53-
54-
//----------------------------------------------------------------------------
55-
/// @brief Mark a resource as being reclaimable by giving ownership of
56-
/// the resource to the resource manager.
58+
/// The resource will be reset at some point in the future.
5759
///
5860
/// @param[in] resource The resource to reclaim.
5961
///
62+
/// @note Despite being a public API, this method cannot be invoked
63+
/// directly. Instead, use `UniqueResourceVKT` to create a unique
64+
/// handle to a resource, which will call this method.
6065
void Reclaim(std::unique_ptr<ResourceVK> resource);
6166

6267
//----------------------------------------------------------------------------
63-
/// @brief Terminate the resource manager. Any resources given to the
64-
/// resource manager post termination will be collected when the
65-
/// resource manager is collected.
68+
/// @brief Destroys the resource manager.
6669
///
67-
void Terminate();
70+
/// The resource manager will stop collecting resources and will be destroyed
71+
/// when all references to it are dropped.
72+
~ResourceManagerVK();
6873

6974
private:
70-
ResourceManagerVK();
71-
7275
using Reclaimables = std::vector<std::unique_ptr<ResourceVK>>;
7376

77+
ResourceManagerVK();
78+
7479
std::thread waiter_;
7580
std::mutex reclaimables_mutex_;
7681
std::condition_variable reclaimables_cv_;
7782
Reclaimables reclaimables_;
7883
bool should_exit_ = false;
7984

80-
void Main();
85+
//----------------------------------------------------------------------------
86+
/// @brief Starts the resource manager thread.
87+
///
88+
/// This method is called implicitly by `Create`.
89+
void Start();
90+
91+
//----------------------------------------------------------------------------
92+
/// @brief Terminates the resource manager thread.
93+
///
94+
/// Any resources given to the resource manager post termination will be
95+
/// collected when the resource manager is collected.
96+
void Terminate();
8197

8298
FML_DISALLOW_COPY_AND_ASSIGN(ResourceManagerVK);
8399
};
84100

101+
//------------------------------------------------------------------------------
102+
/// @brief An internal type that is used to move a resource reference.
103+
///
104+
/// Do not use directly, use `UniqueResourceVKT` instead.
105+
///
106+
/// @tparam ResourceType_ The type of the resource.
107+
///
108+
/// @see |UniqueResourceVKT|.
85109
template <class ResourceType_>
86110
class ResourceVKT : public ResourceVK {
87111
public:
88112
using ResourceType = ResourceType_;
89113

114+
/// @brief Construct a resource from a move-constructible resource.
115+
///
116+
/// @param[in] resource The resource to move.
90117
explicit ResourceVKT(ResourceType&& resource)
91118
: resource_(std::move(resource)) {}
92119

120+
/// @brief Returns a pointer to the resource.
93121
const ResourceType* Get() const { return &resource_; }
94122

95123
private:
124+
// Prevents subclassing, use `UniqueResourceVKT`.
125+
ResourceVKT() = default;
126+
96127
ResourceType resource_;
97128

98129
FML_DISALLOW_COPY_AND_ASSIGN(ResourceVKT);
@@ -105,13 +136,25 @@ class ResourceVKT : public ResourceVK {
105136
/// @tparam ResourceType_ A move-constructible resource type.
106137
///
107138
template <class ResourceType_>
108-
class UniqueResourceVKT {
139+
class UniqueResourceVKT final {
109140
public:
110141
using ResourceType = ResourceType_;
111142

143+
/// @brief Construct a unique resource handle belonging to a manager.
144+
///
145+
/// Initially the handle is empty, and can be populated by calling `Swap`.
146+
///
147+
/// @param[in] resource_manager The resource manager.
112148
explicit UniqueResourceVKT(std::weak_ptr<ResourceManagerVK> resource_manager)
113149
: resource_manager_(std::move(resource_manager)) {}
114150

151+
/// @brief Construct a unique resource handle belonging to a manager.
152+
///
153+
/// Initially the handle is populated with the specified resource, but can
154+
/// be replaced (reclaiming the old resource) by calling `Swap`.
155+
///
156+
/// @param[in] resource_manager The resource manager.
157+
/// @param[in] resource The resource to move.
115158
explicit UniqueResourceVKT(std::weak_ptr<ResourceManagerVK> resource_manager,
116159
ResourceType&& resource)
117160
: resource_manager_(std::move(resource_manager)),
@@ -120,19 +163,30 @@ class UniqueResourceVKT {
120163

121164
~UniqueResourceVKT() { Reset(); }
122165

123-
const ResourceType* operator->() const { return resource_.get()->Get(); }
166+
/// @brief Returns a pointer to the resource.
167+
const ResourceType* operator->() const {
168+
// If this would segfault, fail with a nicer error message.
169+
FML_CHECK(resource_) << "UniqueResourceVKT was reclaimed.";
170+
171+
return resource_.get()->Get();
172+
}
124173

125-
void Reset(ResourceType&& other) {
174+
/// @brief Reclaims the existing resource, if any, and replaces it.
175+
///
176+
/// @param[in] other The (new) resource to move.
177+
void Swap(ResourceType&& other) {
126178
Reset();
127179
resource_ = std::make_unique<ResourceVKT<ResourceType>>(std::move(other));
128180
}
129181

182+
/// @brief Reclaims the existing resource, if any.
130183
void Reset() {
131184
if (!resource_) {
132185
return;
133186
}
134187
// If there is a manager, ask it to reclaim the resource. If there isn't a
135-
// manager, just drop it on the floor here.
188+
// manager (because the manager has been destroyed), just drop it on the
189+
// floor here.
136190
if (auto manager = resource_manager_.lock()) {
137191
manager->Reclaim(std::move(resource_));
138192
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <sys/types.h>
6+
#include <functional>
7+
#include <memory>
8+
#include <utility>
9+
#include "fml/synchronization/waitable_event.h"
10+
#include "gtest/gtest.h"
11+
#include "impeller/renderer/backend/vulkan/resource_manager_vk.h"
12+
13+
namespace impeller {
14+
namespace testing {
15+
16+
// While expected to be a singleton per context, the class does not enforce it.
17+
TEST(ResourceManagerVKTest, CreatesANewInstance) {
18+
auto const a = ResourceManagerVK::Create();
19+
auto const b = ResourceManagerVK::Create();
20+
EXPECT_NE(a, b);
21+
}
22+
23+
namespace {
24+
25+
// Invokes the provided callback when the destructor is called.
26+
//
27+
// Can be moved, but not copied.
28+
class DeathRattle final {
29+
public:
30+
explicit DeathRattle(std::function<void()> callback)
31+
: callback_(std::move(callback)) {}
32+
33+
DeathRattle(DeathRattle&&) = default;
34+
DeathRattle& operator=(DeathRattle&&) = default;
35+
36+
~DeathRattle() { callback_(); }
37+
38+
private:
39+
std::function<void()> callback_;
40+
};
41+
42+
} // namespace
43+
44+
TEST(ResourceManagerVKTest, ReclaimMovesAResourceAndDestroysIt) {
45+
auto const manager = ResourceManagerVK::Create();
46+
47+
auto waiter = fml::AutoResetWaitableEvent();
48+
auto dead = false;
49+
auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
50+
51+
// Not killed immediately.
52+
EXPECT_FALSE(waiter.IsSignaledForTest());
53+
54+
{
55+
auto resource = UniqueResourceVKT<DeathRattle>(manager, std::move(rattle));
56+
}
57+
58+
waiter.Wait();
59+
}
60+
61+
} // namespace testing
62+
} // namespace impeller

0 commit comments

Comments
 (0)