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