Flutter Engine
 
Loading...
Searching...
No Matches
vulkan_surface_pool.cc
Go to the documentation of this file.
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
6
7#include <lib/fdio/directory.h>
8#include <lib/zx/process.h>
9
10#include <algorithm>
11#include <string>
12
14#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
15
16namespace flutter_runner {
17
18static std::string GetCurrentProcessName() {
19 char name[ZX_MAX_NAME_LEN];
20 zx_status_t status =
21 zx::process::self()->get_property(ZX_PROP_NAME, name, sizeof(name));
22 return status == ZX_OK ? std::string(name) : std::string();
23}
24
25static zx_koid_t GetCurrentProcessId() {
26 zx_info_handle_basic_t info;
27 zx_status_t status = zx::process::self()->get_info(
28 ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
29 return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
30}
31
33 sk_sp<GrDirectContext> context)
34 : vulkan_provider_(vulkan_provider), context_(std::move(context)) {
35 FML_CHECK(context_ != nullptr);
36
37 zx_status_t status = fdio_service_connect(
38 "/svc/fuchsia.sysmem2.Allocator",
39 sysmem_allocator_.NewRequest().TakeChannel().release());
40 sysmem_allocator_->SetDebugClientInfo(
41 std::move(fuchsia::sysmem2::AllocatorSetDebugClientInfoRequest{}
42 .set_name(GetCurrentProcessName())
43 .set_id(GetCurrentProcessId())));
44 FML_DCHECK(status == ZX_OK);
45
46 status = fdio_service_connect(
47 "/svc/fuchsia.ui.composition.Allocator",
48 flatland_allocator_.NewRequest().TakeChannel().release());
49 FML_DCHECK(status == ZX_OK);
50}
51
53
54std::unique_ptr<VulkanSurface> VulkanSurfacePool::AcquireSurface(
55 const SkISize& size) {
56 auto surface = GetCachedOrCreateSurface(size);
57
58 if (surface == nullptr) {
59 FML_LOG(ERROR) << "VulkanSurfaceProducer: Could not acquire surface";
60 return nullptr;
61 }
62
63 if (!surface->FlushSessionAcquireAndReleaseEvents()) {
64 FML_LOG(ERROR) << "VulkanSurfaceProducer: Could not flush acquire/release "
65 "events for buffer.";
66 return nullptr;
67 }
68
69 return surface;
70}
71
72std::unique_ptr<VulkanSurface> VulkanSurfacePool::GetCachedOrCreateSurface(
73 const SkISize& size) {
74 TRACE_EVENT2("flutter", "VulkanSurfacePool::GetCachedOrCreateSurface",
75 "width", size.width(), "height", size.height());
76 // First try to find a surface that exactly matches |size|.
77 {
78 auto exact_match_it =
79 std::find_if(available_surfaces_.begin(), available_surfaces_.end(),
80 [&size](const auto& surface) {
81 return surface->IsValid() && surface->GetSize() == size;
82 });
83 if (exact_match_it != available_surfaces_.end()) {
84 auto acquired_surface = std::move(*exact_match_it);
85 available_surfaces_.erase(exact_match_it);
86 TRACE_EVENT_INSTANT0("flutter", "Exact match found");
87 return acquired_surface;
88 }
89 }
90
91 return CreateSurface(size);
92}
93
95 std::unique_ptr<SurfaceProducerSurface> p_surface) {
96 TRACE_EVENT0("flutter", "VulkanSurfacePool::SubmitSurface");
97
98 // This cast is safe because |VulkanSurface| is the only implementation of
99 // |SurfaceProducerSurface| for Flutter on Fuchsia. Additionally, it is
100 // required, because we need to access |VulkanSurface| specific information
101 // of the surface (such as the amount of VkDeviceMemory it contains).
102 auto vulkan_surface = std::unique_ptr<VulkanSurface>(
103 static_cast<VulkanSurface*>(p_surface.release()));
104 if (!vulkan_surface) {
105 return;
106 }
107
108 uintptr_t surface_key = reinterpret_cast<uintptr_t>(vulkan_surface.get());
109 auto insert_iterator = pending_surfaces_.insert(std::make_pair(
110 surface_key, // key
111 std::move(vulkan_surface) // value
112 ));
113 if (insert_iterator.second) {
114 insert_iterator.first->second->SignalWritesFinished(std::bind(
115 &VulkanSurfacePool::RecyclePendingSurface, this, surface_key));
116 }
117}
118
119std::unique_ptr<VulkanSurface> VulkanSurfacePool::CreateSurface(
120 const SkISize& size) {
121 TRACE_EVENT2("flutter", "VulkanSurfacePool::CreateSurface", "width",
122 size.width(), "height", size.height());
123 auto surface = std::make_unique<VulkanSurface>(
124 vulkan_provider_, sysmem_allocator_, flatland_allocator_, context_, size);
125 if (!surface->IsValid()) {
126 FML_LOG(ERROR) << "VulkanSurfaceProducer: Created surface is invalid";
127 return nullptr;
128 }
129 trace_surfaces_created_++;
130 return surface;
131}
132
133void VulkanSurfacePool::RecyclePendingSurface(uintptr_t surface_key) {
134 // Before we do anything, we must clear the surface from the collection of
135 // pending surfaces.
136 auto found_in_pending = pending_surfaces_.find(surface_key);
137 if (found_in_pending == pending_surfaces_.end()) {
138 return;
139 }
140
141 // Grab a hold of the surface to recycle and clear the entry in the pending
142 // surfaces collection.
143 auto surface_to_recycle = std::move(found_in_pending->second);
144 pending_surfaces_.erase(found_in_pending);
145
146 RecycleSurface(std::move(surface_to_recycle));
147}
148
149void VulkanSurfacePool::RecycleSurface(std::unique_ptr<VulkanSurface> surface) {
150 // The surface may have become invalid (for example it the fences could
151 // not be reset).
152 if (!surface->IsValid()) {
153 return;
154 }
155
156 TRACE_EVENT0("flutter", "VulkanSurfacePool::RecycleSurface");
157 // Recycle the buffer by putting it in the list of available surfaces if we
158 // have not reached the maximum amount of cached surfaces.
159 if (available_surfaces_.size() < kMaxSurfaces) {
160 available_surfaces_.push_back(std::move(surface));
161 } else {
162 TRACE_EVENT_INSTANT0("flutter", "Too many surfaces in pool, dropping");
163 }
164 TraceStats();
165}
166
168 TRACE_EVENT0("flutter", "VulkanSurfacePool::AgeAndCollectOldBuffers");
169
170 // Remove all surfaces that are no longer valid or are too old.
171 size_t size_before = available_surfaces_.size();
172 available_surfaces_.erase(
173 std::remove_if(available_surfaces_.begin(), available_surfaces_.end(),
174 [&](auto& surface) {
175 return !surface->IsValid() ||
176 surface->AdvanceAndGetAge() >= kMaxSurfaceAge;
177 }),
178 available_surfaces_.end());
179 TRACE_EVENT1("flutter", "AgeAndCollect", "aged surfaces",
180 (size_before - available_surfaces_.size()));
181
182 // Look for a surface that has both a larger |VkDeviceMemory| allocation
183 // than is necessary for its |VkImage|, and has a stable size history.
184 auto surface_to_remove_it = std::find_if(
185 available_surfaces_.begin(), available_surfaces_.end(),
186 [](const auto& surface) {
187 return surface->IsOversized() && surface->HasStableSizeHistory();
188 });
189 // If we found such a surface, then destroy it and cache a new one that only
190 // uses a necessary amount of memory.
191 if (surface_to_remove_it != available_surfaces_.end()) {
192 TRACE_EVENT_INSTANT0("flutter", "replacing surface with smaller one");
193 auto size = (*surface_to_remove_it)->GetSize();
194 available_surfaces_.erase(surface_to_remove_it);
195 auto new_surface = CreateSurface(size);
196 if (new_surface != nullptr) {
197 available_surfaces_.push_back(std::move(new_surface));
198 } else {
199 FML_LOG(ERROR)
200 << "VulkanSurfaceProducer: Failed to create a new shrunk surface";
201 }
202 }
203
204 TraceStats();
205}
206
208 TRACE_EVENT0("flutter", "VulkanSurfacePool::ShrinkToFit");
209 // Reset all oversized surfaces in |available_surfaces_| so that the old
210 // surfaces and new surfaces don't exist at the same time at any point,
211 // reducing our peak memory footprint.
212 std::vector<SkISize> sizes_to_recreate;
213 for (auto& surface : available_surfaces_) {
214 if (surface->IsOversized()) {
215 sizes_to_recreate.push_back(surface->GetSize());
216 surface.reset();
217 }
218 }
219 available_surfaces_.erase(std::remove(available_surfaces_.begin(),
220 available_surfaces_.end(), nullptr),
221 available_surfaces_.end());
222 for (const auto& size : sizes_to_recreate) {
223 auto surface = CreateSurface(size);
224 if (surface != nullptr) {
225 available_surfaces_.push_back(std::move(surface));
226 } else {
227 FML_LOG(ERROR)
228 << "VulkanSurfaceProducer: Failed to create resized surface";
229 }
230 }
231
232 TraceStats();
233}
234
235void VulkanSurfacePool::TraceStats() {
236 // Resources held in cached buffers.
237 size_t cached_surfaces_bytes = 0;
238 for (const auto& surface : available_surfaces_) {
239 cached_surfaces_bytes += surface->GetAllocationSize();
240 }
241
242 // Resources held by Skia.
243 int skia_resources = 0;
244 size_t skia_bytes = 0;
245 context_->getResourceCacheUsage(&skia_resources, &skia_bytes);
246 const size_t skia_cache_purgeable =
247 context_->getResourceCachePurgeableBytes();
248
249 TRACE_COUNTER("flutter", "SurfacePoolCounts", 0u, "CachedCount",
250 available_surfaces_.size(), //
251 "Created", trace_surfaces_created_, //
252 "Reused", trace_surfaces_reused_, //
253 "PendingInCompositor", pending_surfaces_.size(), //
254 "Retained", 0, //
255 "SkiaCacheResources", skia_resources //
256 );
257
258 TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, //
259 "CachedBytes", cached_surfaces_bytes, //
260 "RetainedBytes", 0, //
261 "SkiaCacheBytes", skia_bytes, //
262 "SkiaCachePurgeable", skia_cache_purgeable //
263 );
264
265 // Reset per present/frame stats.
266 trace_surfaces_created_ = 0;
267 trace_surfaces_reused_ = 0;
268}
269
270} // namespace flutter_runner
std::unique_ptr< VulkanSurface > CreateSurface(const SkISize &size)
void SubmitSurface(std::unique_ptr< SurfaceProducerSurface > surface)
VulkanSurfacePool(vulkan::VulkanProvider &vulkan_provider, sk_sp< GrDirectContext > context)
std::unique_ptr< VulkanSurface > AcquireSurface(const SkISize &size)
VkSurfaceKHR surface
Definition main.cc:65
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
const char * name
Definition fuchsia.cc:49
static zx_koid_t GetCurrentProcessId()
static std::string GetCurrentProcessName()
Definition ref_ptr.h:261
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
#define TRACE_EVENT_INSTANT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)