11 #include "flutter/fml/logging.h"
12 #include "flutter/fml/memory/ref_ptr.h"
13 #include "flutter/fml/trace_event.h"
21 #include "vulkan/vulkan_enums.hpp"
25 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
29 return vk::MemoryPropertyFlagBits::eHostVisible;
31 return vk::MemoryPropertyFlagBits::eDeviceLocal;
33 return vk::MemoryPropertyFlagBits::eLazilyAllocated;
41 VmaAllocationCreateFlags flags = 0;
45 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
47 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
49 flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
52 FML_DCHECK(!readback);
55 FML_DCHECK(!readback);
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;
70 buffer_info.sharingMode = vk::SharingMode::eExclusive;
71 auto buffer_info_native =
72 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
74 VmaAllocationCreateInfo allocation_info = {};
75 allocation_info.usage = VMA_MEMORY_USAGE_AUTO;
76 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
81 uint32_t memTypeIndex;
82 auto result = vk::Result{vmaFindMemoryTypeIndexForBufferInfo(
83 allocator, &buffer_info_native, &allocation_info, &memTypeIndex)};
84 if (result != vk::Result::eSuccess) {
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;
94 result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
95 if (result != vk::Result::eSuccess) {
98 return {allocator, pool};
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_);
113 VmaVulkanFunctions proc_table = {};
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;
144 #undef BIND_VMA_PROC_KHR
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;
153 allocator_info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
154 allocator_info.pVulkanFunctions = &proc_table;
156 VmaAllocator allocator = {};
157 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
158 if (result != vk::Result::eSuccess) {
163 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
164 allocator_.reset(allocator);
165 supports_memoryless_textures_ =
166 capabilities.SupportsDeviceTransientTextures();
170 AllocatorVK::~AllocatorVK() =
default;
173 bool AllocatorVK::IsValid()
const {
178 ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
179 return max_texture_size_;
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;
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;
195 const auto properties =
196 memory_properties.memoryTypes[memory_index].propertyFlags;
197 const bool has_required_properties =
198 (properties & required_properties) == required_properties;
200 if (is_required_memory_type && has_required_properties) {
201 return static_cast<int32_t
>(memory_index);
208 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
212 bool supports_memoryless_textures) {
213 vk::ImageUsageFlags vk_usage;
216 case StorageMode::kHostVisible:
217 case StorageMode::kDevicePrivate:
219 case StorageMode::kDeviceTransient:
220 if (supports_memoryless_textures) {
221 vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
226 if (usage & TextureUsage::kRenderTarget) {
228 vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
230 vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
231 vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
235 if (usage & TextureUsage::kShaderRead) {
236 vk_usage |= vk::ImageUsageFlagBits::eSampled;
239 if (usage & TextureUsage::kShaderWrite) {
240 vk_usage |= vk::ImageUsageFlagBits::eStorage;
243 if (mode != StorageMode::kDeviceTransient) {
246 vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
247 vk::ImageUsageFlagBits::eTransferDst;
254 return VMA_MEMORY_USAGE_AUTO;
257 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
259 bool supports_memoryless_textures) {
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;
271 return vk::MemoryPropertyFlagBits::eDeviceLocal;
277 VmaAllocationCreateFlags flags = 0;
279 case StorageMode::kHostVisible:
281 case StorageMode::kDevicePrivate:
283 case StorageMode::kDeviceTransient:
293 VmaAllocator allocator,
295 bool supports_memoryless_textures)
297 FML_DCHECK(desc.
format != PixelFormat::kUnknown);
298 vk::StructureChain<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
300 auto& image_info = image_info_chain.get();
302 image_info.imageType = vk::ImageType::e2D;
304 image_info.extent = VkExtent3D{
312 image_info.tiling = vk::ImageTiling::eOptimal;
313 image_info.initialLayout = vk::ImageLayout::eUndefined;
314 image_info.usage = AllocatorVK::ToVKImageUsageFlags(
316 supports_memoryless_textures);
317 image_info.sharingMode = vk::SharingMode::eExclusive;
319 vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = {
320 vk::ImageCompressionFixedRateFlagBitsEXT::eNone};
322 const auto frc_rate =
323 CapabilitiesVK::Cast(*
context.GetCapabilities())
326 if (frc_rate.has_value()) {
328 frc_rates[0] = frc_rate.value();
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;
337 image_info_chain.unlink<vk::ImageCompressionControlEXT>();
340 VmaAllocationCreateInfo alloc_nfo = {};
343 alloc_nfo.preferredFlags =
348 VkImage vk_image = VK_NULL_HANDLE;
349 VmaAllocation allocation = {};
350 VmaAllocationInfo allocation_info = {};
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,
369 const bool requested_compression = frc_rate.has_value();
370 vk::Result alloc_result = try_create_image();
378 if (alloc_result == vk::Result::eErrorCompressionExhaustedEXT &&
379 requested_compression) {
380 static std::once_flag warn_once;
381 std::call_once(warn_once, [] {
383 <<
"Fixed-rate image compression exhausted; falling back to "
384 "uncompressed image allocation. (This message is logged once.)";
388 image_info_chain.unlink<vk::ImageCompressionControlEXT>();
389 alloc_result = try_create_image();
392 if (alloc_result != vk::Result::eSuccess) {
394 << vk::to_string(alloc_result)
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: "
403 vk::MemoryPropertyFlags(alloc_nfo.preferredFlags));
407 auto image = vk::Image{vk_image};
409 vk::ImageViewCreateInfo view_info = {};
410 view_info.image = image;
412 view_info.format = image_info.format;
414 view_info.subresourceRange.levelCount = image_info.mipLevels;
421 if (desc.
format == PixelFormat::kA8UNormInt) {
422 view_info.components.a = vk::ComponentSwizzle::eR;
423 view_info.components.r = vk::ComponentSwizzle::eA;
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);
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 =
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) {
453 << vk::to_string(rt_result);
456 rt_image_views.push_back(std::move(rt_image_view));
460 resource_.Swap(ImageResource(
461 ImageVMA{allocator, allocation, image}, std::move(image_view),
462 std::move(rt_image_views),
context.GetResourceAllocator(),
471 vk::Image
GetImage()
const override {
return resource_->image.get().image; }
474 return resource_->image_view.get();
478 uint32_t array_layer)
const override {
479 const auto& views = resource_->rt_image_views;
481 return VK_NULL_HANDLE;
485 static_cast<size_t>(mip_level) * layer_count + array_layer;
486 return index < views.size() ? views[index].get() : views[0].get();
492 struct ImageResource {
493 std::shared_ptr<DeviceHolderVK> device_holder;
494 std::shared_ptr<Allocator> allocator;
496 vk::UniqueImageView image_view;
498 std::vector<vk::UniqueImageView> rt_image_views;
500 ImageResource() =
default;
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)),
510 image_view(
std::move(p_image_view)),
511 rt_image_views(
std::move(p_rt_image_views)) {}
513 ImageResource(ImageResource&& o) =
default;
515 ImageResource(
const ImageResource&) =
delete;
517 ImageResource& operator=(
const ImageResource&) =
delete;
520 UniqueResourceVKT<ImageResource> resource_;
521 bool is_valid_ =
false;
523 AllocatedTextureSourceVK(
const AllocatedTextureSourceVK&) =
delete;
525 AllocatedTextureSourceVK& operator=(
const AllocatedTextureSourceVK&) =
delete;
529 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
530 const TextureDescriptor& desc,
535 auto device_holder = device_holder_.lock();
536 if (!device_holder) {
539 auto context = context_.lock();
543 auto source = std::make_shared<AllocatedTextureSourceVK>(
547 device_holder->GetDevice(),
548 supports_memoryless_textures_
550 if (!source->IsValid()) {
553 return std::make_shared<TextureVK>(context_, std::move(source));
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);
571 VmaAllocationCreateInfo allocation_info = {};
573 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
575 allocation_info.flags =
577 if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
579 allocation_info.pool = staging_buffer_pool_.get().pool;
581 VkBuffer buffer = {};
582 VmaAllocation buffer_allocation = {};
583 VmaAllocationInfo buffer_allocation_info = {};
584 auto result = vk::Result{::vmaCreateBuffer(allocator_.get(),
589 &buffer_allocation_info
592 auto type = memory_properties_.memoryTypes[buffer_allocation_info.memoryType];
593 bool is_host_coherent =
594 !!(type.propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent);
596 if (result != vk::Result::eSuccess) {
598 << vk::to_string(result);
602 return std::make_shared<DeviceBufferVK>(
607 vk::Buffer{buffer}}},
608 buffer_allocation_info,
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;
621 return Bytes{total_usage};
624 void AllocatorVK::DebugTraceMemoryStatistics()
const {
625 #ifdef IMPELLER_DEBUG
626 FML_TRACE_COUNTER(
"flutter",
"AllocatorVK",
627 reinterpret_cast<int64_t
>(
this),
628 "MemoryBudgetUsageMB",
629 DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
#define BIND_VMA_PROC_KHR(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)
~AllocatedTextureSourceVK()=default
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)
static VmaAllocationCreateFlags ToVmaAllocationBufferCreateFlags(StorageMode mode, bool readback)
std::string TextureUsageMaskToString(TextureUsageMask mask)
StorageMode
Specified where the allocation resides and how it is used.
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
constexpr const char * TextureTypeToString(TextureType type)
constexpr bool PixelFormatIsDepthStencil(PixelFormat format)
static constexpr VmaMemoryUsage ToVMAMemoryUsage()
constexpr vk::ImageViewType ToVKImageViewType(TextureType type)
constexpr vk::SampleCountFlagBits ToVKSampleCount(SampleCount sample_count)
constexpr vk::Format ToVKImageFormat(PixelFormat format)
static VmaAllocationCreateFlags ToVmaAllocationCreateFlags(StorageMode mode)
static PoolVMA CreateBufferPool(VmaAllocator allocator)
fml::UniqueObject< ImageVMA, ImageVMATraits > UniqueImageVMA
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKTextureMemoryPropertyFlags(StorageMode mode, bool supports_memoryless_textures)
constexpr const char * StorageModeToString(StorageMode mode)
constexpr vk::ImageCreateFlags ToVKImageCreateFlags(TextureType type)
constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format)
fml::UniqueObject< BufferVMA, BufferVMATraits > UniqueBufferVMA
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKBufferMemoryPropertyFlags(StorageMode mode)
std::shared_ptr< ContextGLES > context
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
CompressionType compression_type