Flutter Engine
The Flutter Engine
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);
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)
Definition: fatpathfill.cpp:24
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
static zx_koid_t GetCurrentProcessId()
static std::string GetCurrentProcessName()
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
def remove(*paths)
Definition: ref_ptr.h:256
Definition: SkSize.h:16
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
Definition: trace_event.h:145
#define TRACE_EVENT_INSTANT0(category_group, name)
Definition: trace_event.h:175
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:141