Flutter Impeller
allocator_vk.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 <memory>
8 #include <mutex>
9 #include <utility>
10 
11 #include "flutter/fml/logging.h"
12 #include "flutter/fml/memory/ref_ptr.h"
13 #include "flutter/fml/trace_event.h"
15 #include "impeller/core/formats.h"
21 #include "vulkan/vulkan_enums.hpp"
22 
23 namespace impeller {
24 
25 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
27  switch (mode) {
29  return vk::MemoryPropertyFlagBits::eHostVisible;
31  return vk::MemoryPropertyFlagBits::eDeviceLocal;
33  return vk::MemoryPropertyFlagBits::eLazilyAllocated;
34  }
35  FML_UNREACHABLE();
36 }
37 
38 static VmaAllocationCreateFlags ToVmaAllocationBufferCreateFlags(
39  StorageMode mode,
40  bool readback) {
41  VmaAllocationCreateFlags flags = 0;
42  switch (mode) {
44  if (!readback) {
45  flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
46  } else {
47  flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
48  }
49  flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
50  return flags;
52  FML_DCHECK(!readback);
53  return flags;
55  FML_DCHECK(!readback);
56  return flags;
57  }
58  FML_UNREACHABLE();
59 }
60 
61 static PoolVMA CreateBufferPool(VmaAllocator allocator) {
62  vk::BufferCreateInfo buffer_info;
63  buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
64  vk::BufferUsageFlagBits::eIndexBuffer |
65  vk::BufferUsageFlagBits::eUniformBuffer |
66  vk::BufferUsageFlagBits::eStorageBuffer |
67  vk::BufferUsageFlagBits::eTransferSrc |
68  vk::BufferUsageFlagBits::eTransferDst;
69  buffer_info.size = 1u; // doesn't matter
70  buffer_info.sharingMode = vk::SharingMode::eExclusive;
71  auto buffer_info_native =
72  static_cast<vk::BufferCreateInfo::NativeType>(buffer_info);
73 
74  VmaAllocationCreateInfo allocation_info = {};
75  allocation_info.usage = VMA_MEMORY_USAGE_AUTO;
76  allocation_info.preferredFlags = static_cast<VkMemoryPropertyFlags>(
78  allocation_info.flags = ToVmaAllocationBufferCreateFlags(
79  StorageMode::kHostVisible, /*readback=*/false);
80 
81  uint32_t memTypeIndex;
82  auto result = vk::Result{vmaFindMemoryTypeIndexForBufferInfo(
83  allocator, &buffer_info_native, &allocation_info, &memTypeIndex)};
84  if (result != vk::Result::eSuccess) {
85  return {};
86  }
87 
88  VmaPoolCreateInfo pool_create_info = {};
89  pool_create_info.memoryTypeIndex = memTypeIndex;
90  pool_create_info.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;
91  pool_create_info.minBlockCount = 1;
92 
93  VmaPool pool = {};
94  result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
95  if (result != vk::Result::eSuccess) {
96  return {};
97  }
98  return {allocator, pool};
99 }
100 
101 AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
102  uint32_t vulkan_api_version,
103  const vk::PhysicalDevice& physical_device,
104  const std::shared_ptr<DeviceHolderVK>& device_holder,
105  const vk::Instance& instance,
106  const CapabilitiesVK& capabilities)
107  : context_(std::move(context)), device_holder_(device_holder) {
108  auto limits = physical_device.getProperties().limits;
109  max_texture_size_.width = max_texture_size_.height =
110  limits.maxImageDimension2D;
111  physical_device.getMemoryProperties(&memory_properties_);
112 
113  VmaVulkanFunctions proc_table = {};
114 
115 #define BIND_VMA_PROC(x) proc_table.x = VULKAN_HPP_DEFAULT_DISPATCHER.x;
116 #define BIND_VMA_PROC_KHR(x) \
117  proc_table.x##KHR = VULKAN_HPP_DEFAULT_DISPATCHER.x \
118  ? VULKAN_HPP_DEFAULT_DISPATCHER.x \
119  : VULKAN_HPP_DEFAULT_DISPATCHER.x##KHR;
120  BIND_VMA_PROC(vkGetInstanceProcAddr);
121  BIND_VMA_PROC(vkGetDeviceProcAddr);
122  BIND_VMA_PROC(vkGetPhysicalDeviceProperties);
123  BIND_VMA_PROC(vkGetPhysicalDeviceMemoryProperties);
124  BIND_VMA_PROC(vkAllocateMemory);
125  BIND_VMA_PROC(vkFreeMemory);
126  BIND_VMA_PROC(vkMapMemory);
127  BIND_VMA_PROC(vkUnmapMemory);
128  BIND_VMA_PROC(vkFlushMappedMemoryRanges);
129  BIND_VMA_PROC(vkInvalidateMappedMemoryRanges);
130  BIND_VMA_PROC(vkBindBufferMemory);
131  BIND_VMA_PROC(vkBindImageMemory);
132  BIND_VMA_PROC(vkGetBufferMemoryRequirements);
133  BIND_VMA_PROC(vkGetImageMemoryRequirements);
134  BIND_VMA_PROC(vkCreateBuffer);
135  BIND_VMA_PROC(vkDestroyBuffer);
136  BIND_VMA_PROC(vkCreateImage);
137  BIND_VMA_PROC(vkDestroyImage);
138  BIND_VMA_PROC(vkCmdCopyBuffer);
139  BIND_VMA_PROC_KHR(vkGetBufferMemoryRequirements2);
140  BIND_VMA_PROC_KHR(vkGetImageMemoryRequirements2);
141  BIND_VMA_PROC_KHR(vkBindBufferMemory2);
142  BIND_VMA_PROC_KHR(vkBindImageMemory2);
143  BIND_VMA_PROC_KHR(vkGetPhysicalDeviceMemoryProperties2);
144 #undef BIND_VMA_PROC_KHR
145 #undef BIND_VMA_PROC
146 
147  VmaAllocatorCreateInfo allocator_info = {};
148  allocator_info.vulkanApiVersion = vulkan_api_version;
149  allocator_info.physicalDevice = physical_device;
150  allocator_info.device = device_holder->GetDevice();
151  allocator_info.instance = instance;
152  // 4 MB, matching the default used by Skia Vulkan.
153  allocator_info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
154  allocator_info.pVulkanFunctions = &proc_table;
155 
156  VmaAllocator allocator = {};
157  auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
158  if (result != vk::Result::eSuccess) {
159  VALIDATION_LOG << "Could not create memory allocator";
160  return;
161  }
162  staging_buffer_pool_.reset(CreateBufferPool(allocator));
163  created_buffer_pool_ &= staging_buffer_pool_.is_valid();
164  allocator_.reset(allocator);
165  supports_memoryless_textures_ =
166  capabilities.SupportsDeviceTransientTextures();
167  is_valid_ = true;
168 }
169 
170 AllocatorVK::~AllocatorVK() = default;
171 
172 // |Allocator|
173 bool AllocatorVK::IsValid() const {
174  return is_valid_;
175 }
176 
177 // |Allocator|
178 ISize AllocatorVK::GetMaxTextureSizeSupported() const {
179  return max_texture_size_;
180 }
181 
182 int32_t AllocatorVK::FindMemoryTypeIndex(
183  uint32_t memory_type_bits_requirement,
184  vk::PhysicalDeviceMemoryProperties& memory_properties) {
185  int32_t type_index = -1;
186  vk::MemoryPropertyFlagBits required_properties =
187  vk::MemoryPropertyFlagBits::eDeviceLocal;
188 
189  const uint32_t memory_count = memory_properties.memoryTypeCount;
190  for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
191  const uint32_t memory_type_bits = (1 << memory_index);
192  const bool is_required_memory_type =
193  memory_type_bits_requirement & memory_type_bits;
194 
195  const auto properties =
196  memory_properties.memoryTypes[memory_index].propertyFlags;
197  const bool has_required_properties =
198  (properties & required_properties) == required_properties;
199 
200  if (is_required_memory_type && has_required_properties) {
201  return static_cast<int32_t>(memory_index);
202  }
203  }
204 
205  return type_index;
206 }
207 
208 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
209  PixelFormat format,
210  TextureUsageMask usage,
211  StorageMode mode,
212  bool supports_memoryless_textures) {
213  vk::ImageUsageFlags vk_usage;
214 
215  switch (mode) {
216  case StorageMode::kHostVisible:
217  case StorageMode::kDevicePrivate:
218  break;
219  case StorageMode::kDeviceTransient:
220  if (supports_memoryless_textures) {
221  vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
222  }
223  break;
224  }
225 
226  if (usage & TextureUsage::kRenderTarget) {
227  if (PixelFormatIsDepthStencil(format)) {
228  vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
229  } else {
230  vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
231  vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
232  }
233  }
234 
235  if (usage & TextureUsage::kShaderRead) {
236  vk_usage |= vk::ImageUsageFlagBits::eSampled;
237  }
238 
239  if (usage & TextureUsage::kShaderWrite) {
240  vk_usage |= vk::ImageUsageFlagBits::eStorage;
241  }
242 
243  if (mode != StorageMode::kDeviceTransient) {
244  // Add transfer usage flags to support blit passes only if image isn't
245  // device transient.
246  vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
247  vk::ImageUsageFlagBits::eTransferDst;
248  }
249 
250  return vk_usage;
251 }
252 
253 static constexpr VmaMemoryUsage ToVMAMemoryUsage() {
254  return VMA_MEMORY_USAGE_AUTO;
255 }
256 
257 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
259  bool supports_memoryless_textures) {
260  switch (mode) {
261  case StorageMode::kHostVisible:
262  return vk::MemoryPropertyFlagBits::eHostVisible |
263  vk::MemoryPropertyFlagBits::eDeviceLocal;
264  case StorageMode::kDevicePrivate:
265  return vk::MemoryPropertyFlagBits::eDeviceLocal;
266  case StorageMode::kDeviceTransient:
267  if (supports_memoryless_textures) {
268  return vk::MemoryPropertyFlagBits::eLazilyAllocated |
269  vk::MemoryPropertyFlagBits::eDeviceLocal;
270  }
271  return vk::MemoryPropertyFlagBits::eDeviceLocal;
272  }
273  FML_UNREACHABLE();
274 }
275 
276 static VmaAllocationCreateFlags ToVmaAllocationCreateFlags(StorageMode mode) {
277  VmaAllocationCreateFlags flags = 0;
278  switch (mode) {
279  case StorageMode::kHostVisible:
280  return flags;
281  case StorageMode::kDevicePrivate:
282  return flags;
283  case StorageMode::kDeviceTransient:
284  return flags;
285  }
286  FML_UNREACHABLE();
287 }
288 
290  public:
292  const TextureDescriptor& desc,
293  VmaAllocator allocator,
294  vk::Device device,
295  bool supports_memoryless_textures)
296  : TextureSourceVK(desc), resource_(context.GetResourceManager()) {
297  FML_DCHECK(desc.format != PixelFormat::kUnknown);
298  vk::StructureChain<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
299  image_info_chain;
300  auto& image_info = image_info_chain.get();
301  image_info.flags = ToVKImageCreateFlags(desc.type);
302  image_info.imageType = vk::ImageType::e2D;
303  image_info.format = ToVKImageFormat(desc.format);
304  image_info.extent = VkExtent3D{
305  static_cast<uint32_t>(desc.size.width), // width
306  static_cast<uint32_t>(desc.size.height), // height
307  1u // depth
308  };
309  image_info.samples = ToVKSampleCount(desc.sample_count);
310  image_info.mipLevels = desc.mip_count;
311  image_info.arrayLayers = ToArrayLayerCount(desc.type);
312  image_info.tiling = vk::ImageTiling::eOptimal;
313  image_info.initialLayout = vk::ImageLayout::eUndefined;
314  image_info.usage = AllocatorVK::ToVKImageUsageFlags(
315  desc.format, desc.usage, desc.storage_mode,
316  supports_memoryless_textures);
317  image_info.sharingMode = vk::SharingMode::eExclusive;
318 
319  vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = {
320  vk::ImageCompressionFixedRateFlagBitsEXT::eNone};
321 
322  const auto frc_rate =
323  CapabilitiesVK::Cast(*context.GetCapabilities())
324  .GetSupportedFRCRate(desc.compression_type,
325  FRCFormatDescriptor{image_info});
326  if (frc_rate.has_value()) {
327  // This array must not be in a temporary scope.
328  frc_rates[0] = frc_rate.value();
329 
330  auto& compression_info =
331  image_info_chain.get<vk::ImageCompressionControlEXT>();
332  compression_info.pFixedRateFlags = frc_rates;
333  compression_info.compressionControlPlaneCount = 1u;
334  compression_info.flags =
335  vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit;
336  } else {
337  image_info_chain.unlink<vk::ImageCompressionControlEXT>();
338  }
339 
340  VmaAllocationCreateInfo alloc_nfo = {};
341 
342  alloc_nfo.usage = ToVMAMemoryUsage();
343  alloc_nfo.preferredFlags =
344  static_cast<VkMemoryPropertyFlags>(ToVKTextureMemoryPropertyFlags(
345  desc.storage_mode, supports_memoryless_textures));
346  alloc_nfo.flags = ToVmaAllocationCreateFlags(desc.storage_mode);
347 
348  VkImage vk_image = VK_NULL_HANDLE;
349  VmaAllocation allocation = {};
350  VmaAllocationInfo allocation_info = {};
351 
352  // Performs the VMA image allocation using the current create-info chain.
353  // The native create-info is re-derived from the chain on each call, so an
354  // unlink of the compression-control struct between calls is reflected.
355  const auto try_create_image = [&]() -> vk::Result {
356  vk::ImageCreateInfo::NativeType create_info_native =
357  static_cast<vk::ImageCreateInfo::NativeType>(
358  image_info_chain.get<vk::ImageCreateInfo>());
359  return vk::Result{::vmaCreateImage(allocator, //
360  &create_info_native, //
361  &alloc_nfo, //
362  &vk_image, //
363  &allocation, //
364  &allocation_info //
365  )};
366  };
367 
368  // Fixed-rate compression was requested iff a rate was selected above.
369  const bool requested_compression = frc_rate.has_value();
370  vk::Result alloc_result = try_create_image();
371 
372  // Some drivers (e.g. PowerVR) can return VK_ERROR_COMPRESSION_EXHAUSTED_EXT
373  // when fixed-rate compression resources are depleted. Per the Vulkan spec
374  // this error is only returned for fixed-rate compression requests, so
375  // retrying without compression is a valid recovery. Without it the
376  // allocation fails, the texture is invalid, and the resulting null render
377  // target crashes the raster thread.
378  if (alloc_result == vk::Result::eErrorCompressionExhaustedEXT &&
379  requested_compression) {
380  static std::once_flag warn_once;
381  std::call_once(warn_once, [] {
382  FML_LOG(WARNING)
383  << "Fixed-rate image compression exhausted; falling back to "
384  "uncompressed image allocation. (This message is logged once.)";
385  });
386  // The compression-control struct is only present here because compression
387  // was requested above, so unlinking it once is safe (no double-unlink).
388  image_info_chain.unlink<vk::ImageCompressionControlEXT>();
389  alloc_result = try_create_image();
390  }
391 
392  if (alloc_result != vk::Result::eSuccess) {
393  VALIDATION_LOG << "Unable to allocate Vulkan Image: "
394  << vk::to_string(alloc_result)
395  << " Type: " << TextureTypeToString(desc.type)
396  << " Mode: " << StorageModeToString(desc.storage_mode)
397  << " Usage: " << TextureUsageMaskToString(desc.usage)
398  << " [VK]Flags: " << vk::to_string(image_info.flags)
399  << " [VK]Format: " << vk::to_string(image_info.format)
400  << " [VK]Usage: " << vk::to_string(image_info.usage)
401  << " [VK]Mem. Flags: "
402  << vk::to_string(
403  vk::MemoryPropertyFlags(alloc_nfo.preferredFlags));
404  return;
405  }
406 
407  auto image = vk::Image{vk_image};
408 
409  vk::ImageViewCreateInfo view_info = {};
410  view_info.image = image;
411  view_info.viewType = ToVKImageViewType(desc.type);
412  view_info.format = image_info.format;
413  view_info.subresourceRange.aspectMask = ToVKImageAspectFlags(desc.format);
414  view_info.subresourceRange.levelCount = image_info.mipLevels;
415  view_info.subresourceRange.layerCount = ToArrayLayerCount(desc.type);
416 
417  // Vulkan does not have an image format that is equivalent to
418  // `MTLPixelFormatA8Unorm`, so we use `R8Unorm` instead. Given that the
419  // shaders expect that alpha channel to be set in the cases, we swizzle.
420  // See: https://github.com/flutter/flutter/issues/115461 for more details.
421  if (desc.format == PixelFormat::kA8UNormInt) {
422  view_info.components.a = vk::ComponentSwizzle::eR;
423  view_info.components.r = vk::ComponentSwizzle::eA;
424  }
425 
426  auto [result, image_view] = device.createImageViewUnique(view_info);
427  if (result != vk::Result::eSuccess) {
428  VALIDATION_LOG << "Unable to create an image view for allocation: "
429  << vk::to_string(result);
430  return;
431  }
432  // Create one 2D attachment view per (mip level, array layer) so a
433  // specific subresource can be rendered into. Render targets are usually a
434  // single 2D mip (one view); cube and mipmapped render targets get the
435  // full set. Non-render-target textures only need the base view.
436  view_info.viewType = vk::ImageViewType::e2D;
437  view_info.subresourceRange.levelCount = 1u;
438  view_info.subresourceRange.layerCount = 1u;
439  const bool is_render_target = !!(desc.usage & TextureUsage::kRenderTarget);
440  const uint32_t rt_mip_count = is_render_target ? image_info.mipLevels : 1u;
441  const uint32_t rt_layer_count =
442  is_render_target ? ToArrayLayerCount(desc.type) : 1u;
443  std::vector<vk::UniqueImageView> rt_image_views;
444  rt_image_views.reserve(rt_mip_count * rt_layer_count);
445  for (uint32_t mip = 0; mip < rt_mip_count; mip++) {
446  for (uint32_t layer = 0; layer < rt_layer_count; layer++) {
447  view_info.subresourceRange.baseMipLevel = mip;
448  view_info.subresourceRange.baseArrayLayer = layer;
449  auto [rt_result, rt_image_view] =
450  device.createImageViewUnique(view_info);
451  if (rt_result != vk::Result::eSuccess) {
452  VALIDATION_LOG << "Unable to create a render target image view: "
453  << vk::to_string(rt_result);
454  return;
455  }
456  rt_image_views.push_back(std::move(rt_image_view));
457  }
458  }
459 
460  resource_.Swap(ImageResource(
461  ImageVMA{allocator, allocation, image}, std::move(image_view),
462  std::move(rt_image_views), context.GetResourceAllocator(),
463  context.GetDeviceHolder()));
464  is_valid_ = true;
465  }
466 
468 
469  bool IsValid() const { return is_valid_; }
470 
471  vk::Image GetImage() const override { return resource_->image.get().image; }
472 
473  vk::ImageView GetImageView() const override {
474  return resource_->image_view.get();
475  }
476 
477  vk::ImageView GetRenderTargetView(uint32_t mip_level,
478  uint32_t array_layer) const override {
479  const auto& views = resource_->rt_image_views;
480  if (views.empty()) {
481  return VK_NULL_HANDLE;
482  }
483  const uint32_t layer_count = ToArrayLayerCount(GetTextureDescriptor().type);
484  const size_t index =
485  static_cast<size_t>(mip_level) * layer_count + array_layer;
486  return index < views.size() ? views[index].get() : views[0].get();
487  }
488 
489  bool IsSwapchainImage() const override { return false; }
490 
491  private:
492  struct ImageResource {
493  std::shared_ptr<DeviceHolderVK> device_holder;
494  std::shared_ptr<Allocator> allocator;
495  UniqueImageVMA image;
496  vk::UniqueImageView image_view;
497  // One attachment view per (mip level, array layer), row-major by mip.
498  std::vector<vk::UniqueImageView> rt_image_views;
499 
500  ImageResource() = default;
501 
502  ImageResource(ImageVMA p_image,
503  vk::UniqueImageView p_image_view,
504  std::vector<vk::UniqueImageView> p_rt_image_views,
505  std::shared_ptr<Allocator> allocator,
506  std::shared_ptr<DeviceHolderVK> device_holder)
507  : device_holder(std::move(device_holder)),
508  allocator(std::move(allocator)),
509  image(p_image),
510  image_view(std::move(p_image_view)),
511  rt_image_views(std::move(p_rt_image_views)) {}
512 
513  ImageResource(ImageResource&& o) = default;
514 
515  ImageResource(const ImageResource&) = delete;
516 
517  ImageResource& operator=(const ImageResource&) = delete;
518  };
519 
520  UniqueResourceVKT<ImageResource> resource_;
521  bool is_valid_ = false;
522 
523  AllocatedTextureSourceVK(const AllocatedTextureSourceVK&) = delete;
524 
525  AllocatedTextureSourceVK& operator=(const AllocatedTextureSourceVK&) = delete;
526 };
527 
528 // |Allocator|
529 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
530  const TextureDescriptor& desc,
531  bool threadsafe) {
532  if (!IsValid()) {
533  return nullptr;
534  }
535  auto device_holder = device_holder_.lock();
536  if (!device_holder) {
537  return nullptr;
538  }
539  auto context = context_.lock();
540  if (!context) {
541  return nullptr;
542  }
543  auto source = std::make_shared<AllocatedTextureSourceVK>(
544  ContextVK::Cast(*context), //
545  desc, //
546  allocator_.get(), //
547  device_holder->GetDevice(), //
548  supports_memoryless_textures_ //
549  );
550  if (!source->IsValid()) {
551  return nullptr;
552  }
553  return std::make_shared<TextureVK>(context_, std::move(source));
554 }
555 
556 // |Allocator|
557 std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
558  const DeviceBufferDescriptor& desc) {
559  vk::BufferCreateInfo buffer_info;
560  buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
561  vk::BufferUsageFlagBits::eIndexBuffer |
562  vk::BufferUsageFlagBits::eUniformBuffer |
563  vk::BufferUsageFlagBits::eStorageBuffer |
564  vk::BufferUsageFlagBits::eTransferSrc |
565  vk::BufferUsageFlagBits::eTransferDst;
566  buffer_info.size = desc.size;
567  buffer_info.sharingMode = vk::SharingMode::eExclusive;
568  auto buffer_info_native =
569  static_cast<vk::BufferCreateInfo::NativeType>(buffer_info);
570 
571  VmaAllocationCreateInfo allocation_info = {};
572  allocation_info.usage = ToVMAMemoryUsage();
573  allocation_info.preferredFlags = static_cast<VkMemoryPropertyFlags>(
574  ToVKBufferMemoryPropertyFlags(desc.storage_mode));
575  allocation_info.flags =
576  ToVmaAllocationBufferCreateFlags(desc.storage_mode, desc.readback);
577  if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
578  !desc.readback) {
579  allocation_info.pool = staging_buffer_pool_.get().pool;
580  }
581  VkBuffer buffer = {};
582  VmaAllocation buffer_allocation = {};
583  VmaAllocationInfo buffer_allocation_info = {};
584  auto result = vk::Result{::vmaCreateBuffer(allocator_.get(), //
585  &buffer_info_native, //
586  &allocation_info, //
587  &buffer, //
588  &buffer_allocation, //
589  &buffer_allocation_info //
590  )};
591 
592  auto type = memory_properties_.memoryTypes[buffer_allocation_info.memoryType];
593  bool is_host_coherent =
594  !!(type.propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent);
595 
596  if (result != vk::Result::eSuccess) {
597  VALIDATION_LOG << "Unable to allocate a device buffer: "
598  << vk::to_string(result);
599  return {};
600  }
601 
602  return std::make_shared<DeviceBufferVK>(
603  desc, //
604  context_, //
605  UniqueBufferVMA{BufferVMA{allocator_.get(), //
606  buffer_allocation, //
607  vk::Buffer{buffer}}}, //
608  buffer_allocation_info, //
609  is_host_coherent);
610 }
611 
612 Bytes AllocatorVK::DebugGetHeapUsage() const {
613  auto count = memory_properties_.memoryHeapCount;
614  std::vector<VmaBudget> budgets(count);
615  vmaGetHeapBudgets(allocator_.get(), budgets.data());
616  size_t total_usage = 0;
617  for (auto i = 0u; i < count; i++) {
618  const VmaBudget& budget = budgets[i];
619  total_usage += budget.usage;
620  }
621  return Bytes{total_usage};
622 }
623 
624 void AllocatorVK::DebugTraceMemoryStatistics() const {
625 #ifdef IMPELLER_DEBUG
626  FML_TRACE_COUNTER("flutter", "AllocatorVK",
627  reinterpret_cast<int64_t>(this), // Trace Counter ID
628  "MemoryBudgetUsageMB",
629  DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
630 #endif // IMPELLER_DEBUG
631 }
632 
633 } // namespace impeller
#define BIND_VMA_PROC_KHR(x)
#define BIND_VMA_PROC(x)
vk::Image GetImage() const override
Get the image handle for this texture source.
vk::ImageView GetImageView() const override
Retrieve the image view used for sampling/blitting/compute with this texture source.
AllocatedTextureSourceVK(const ContextVK &context, const TextureDescriptor &desc, VmaAllocator allocator, vk::Device device, bool supports_memoryless_textures)
vk::ImageView GetRenderTargetView(uint32_t mip_level, uint32_t array_layer) const override
Retrieve the image view used to attach a specific subresource of this texture as a render target.
bool IsSwapchainImage() const override
Determines if swapchain image. That is, an image used as the root render target.
Abstract base class that represents a vkImage and an vkImageView.
constexpr uint32_t ToArrayLayerCount(TextureType type)
Definition: formats_vk.h:611
static VmaAllocationCreateFlags ToVmaAllocationBufferCreateFlags(StorageMode mode, bool readback)
Definition: allocator_vk.cc:38
std::string TextureUsageMaskToString(TextureUsageMask mask)
Definition: formats.cc:95
StorageMode
Specified where the allocation resides and how it is used.
Definition: formats.h:32
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
constexpr const char * TextureTypeToString(TextureType type)
Definition: formats.h:457
constexpr bool PixelFormatIsDepthStencil(PixelFormat format)
Definition: formats_vk.h:450
static constexpr VmaMemoryUsage ToVMAMemoryUsage()
constexpr vk::ImageViewType ToVKImageViewType(TextureType type)
Definition: formats_vk.h:625
constexpr vk::SampleCountFlagBits ToVKSampleCount(SampleCount sample_count)
Definition: formats_vk.h:250
constexpr vk::Format ToVKImageFormat(PixelFormat format)
Definition: formats_vk.h:146
static VmaAllocationCreateFlags ToVmaAllocationCreateFlags(StorageMode mode)
static PoolVMA CreateBufferPool(VmaAllocator allocator)
Definition: allocator_vk.cc:61
fml::UniqueObject< ImageVMA, ImageVMATraits > UniqueImageVMA
Definition: vma.h:127
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKTextureMemoryPropertyFlags(StorageMode mode, bool supports_memoryless_textures)
ISize64 ISize
Definition: size.h:162
constexpr const char * StorageModeToString(StorageMode mode)
Definition: formats.h:60
constexpr vk::ImageCreateFlags ToVKImageCreateFlags(TextureType type)
Definition: formats_vk.h:639
constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format)
Definition: formats_vk.h:567
fml::UniqueObject< BufferVMA, BufferVMATraits > UniqueBufferVMA
Definition: vma.h:92
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKBufferMemoryPropertyFlags(StorageMode mode)
Definition: allocator_vk.cc:26
Definition: comparable.h:93
std::shared_ptr< ContextGLES > context
A pixel format and usage that is sufficient to check if images of that format and usage are suitable ...
Type height
Definition: size.h:29
Type width
Definition: size.h:28
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define VALIDATION_LOG
Definition: validation.h:91