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