9 #include "flutter/fml/memory/ref_ptr.h"
10 #include "flutter/fml/trace_event.h"
17 #include "vulkan/vulkan_enums.hpp"
21 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
25 return vk::MemoryPropertyFlagBits::eHostVisible;
27 return vk::MemoryPropertyFlagBits::eDeviceLocal;
29 return vk::MemoryPropertyFlagBits::eLazilyAllocated;
37 VmaAllocationCreateFlags flags = 0;
41 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
43 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
45 flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
48 FML_DCHECK(!readback);
51 FML_DCHECK(!readback);
58 vk::BufferCreateInfo buffer_info;
59 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
60 vk::BufferUsageFlagBits::eIndexBuffer |
61 vk::BufferUsageFlagBits::eUniformBuffer |
62 vk::BufferUsageFlagBits::eStorageBuffer |
63 vk::BufferUsageFlagBits::eTransferSrc |
64 vk::BufferUsageFlagBits::eTransferDst;
65 buffer_info.size = 1u;
66 buffer_info.sharingMode = vk::SharingMode::eExclusive;
67 auto buffer_info_native =
68 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
70 VmaAllocationCreateInfo allocation_info = {};
71 allocation_info.usage = VMA_MEMORY_USAGE_AUTO;
72 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
77 uint32_t memTypeIndex;
78 auto result = vk::Result{vmaFindMemoryTypeIndexForBufferInfo(
79 allocator, &buffer_info_native, &allocation_info, &memTypeIndex)};
80 if (result != vk::Result::eSuccess) {
84 VmaPoolCreateInfo pool_create_info = {};
85 pool_create_info.memoryTypeIndex = memTypeIndex;
86 pool_create_info.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;
89 result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
90 if (result != vk::Result::eSuccess) {
93 return {allocator, pool};
96 AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
97 uint32_t vulkan_api_version,
98 const vk::PhysicalDevice& physical_device,
99 const std::shared_ptr<DeviceHolderVK>& device_holder,
100 const vk::Instance& instance,
101 const CapabilitiesVK& capabilities)
102 : context_(
std::move(context)), device_holder_(device_holder) {
103 auto limits = physical_device.getProperties().limits;
104 max_texture_size_.width = max_texture_size_.height =
105 limits.maxImageDimension2D;
106 physical_device.getMemoryProperties(&memory_properties_);
108 VmaVulkanFunctions proc_table = {};
110 #define BIND_VMA_PROC(x) proc_table.x = VULKAN_HPP_DEFAULT_DISPATCHER.x;
111 #define BIND_VMA_PROC_KHR(x) \
112 proc_table.x##KHR = VULKAN_HPP_DEFAULT_DISPATCHER.x \
113 ? VULKAN_HPP_DEFAULT_DISPATCHER.x \
114 : VULKAN_HPP_DEFAULT_DISPATCHER.x##KHR;
139 #undef BIND_VMA_PROC_KHR
142 VmaAllocatorCreateInfo allocator_info = {};
143 allocator_info.vulkanApiVersion = vulkan_api_version;
144 allocator_info.physicalDevice = physical_device;
145 allocator_info.device = device_holder->GetDevice();
146 allocator_info.instance = instance;
147 allocator_info.pVulkanFunctions = &proc_table;
149 VmaAllocator allocator = {};
150 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
151 if (result != vk::Result::eSuccess) {
156 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
157 allocator_.reset(allocator);
158 supports_memoryless_textures_ =
159 capabilities.SupportsDeviceTransientTextures();
163 AllocatorVK::~AllocatorVK() =
default;
166 bool AllocatorVK::IsValid()
const {
171 ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
172 return max_texture_size_;
175 int32_t AllocatorVK::FindMemoryTypeIndex(
176 uint32_t memory_type_bits_requirement,
177 vk::PhysicalDeviceMemoryProperties& memory_properties) {
178 int32_t type_index = -1;
179 vk::MemoryPropertyFlagBits required_properties =
180 vk::MemoryPropertyFlagBits::eDeviceLocal;
182 const uint32_t memory_count = memory_properties.memoryTypeCount;
183 for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
184 const uint32_t memory_type_bits = (1 << memory_index);
185 const bool is_required_memory_type =
186 memory_type_bits_requirement & memory_type_bits;
188 const auto properties =
189 memory_properties.memoryTypes[memory_index].propertyFlags;
190 const bool has_required_properties =
191 (properties & required_properties) == required_properties;
193 if (is_required_memory_type && has_required_properties) {
194 return static_cast<int32_t
>(memory_index);
201 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
205 bool supports_memoryless_textures) {
206 vk::ImageUsageFlags vk_usage;
209 case StorageMode::kHostVisible:
210 case StorageMode::kDevicePrivate:
212 case StorageMode::kDeviceTransient:
213 if (supports_memoryless_textures) {
214 vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
219 if (usage & TextureUsage::kRenderTarget) {
221 vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
223 vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
224 vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
228 if (usage & TextureUsage::kShaderRead) {
229 vk_usage |= vk::ImageUsageFlagBits::eSampled;
232 if (usage & TextureUsage::kShaderWrite) {
233 vk_usage |= vk::ImageUsageFlagBits::eStorage;
236 if (mode != StorageMode::kDeviceTransient) {
239 vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
240 vk::ImageUsageFlagBits::eTransferDst;
247 return VMA_MEMORY_USAGE_AUTO;
250 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
252 bool supports_memoryless_textures) {
254 case StorageMode::kHostVisible:
255 return vk::MemoryPropertyFlagBits::eHostVisible |
256 vk::MemoryPropertyFlagBits::eDeviceLocal;
257 case StorageMode::kDevicePrivate:
258 return vk::MemoryPropertyFlagBits::eDeviceLocal;
259 case StorageMode::kDeviceTransient:
260 if (supports_memoryless_textures) {
261 return vk::MemoryPropertyFlagBits::eLazilyAllocated |
262 vk::MemoryPropertyFlagBits::eDeviceLocal;
264 return vk::MemoryPropertyFlagBits::eDeviceLocal;
270 VmaAllocationCreateFlags flags = 0;
272 case StorageMode::kHostVisible:
274 case StorageMode::kDevicePrivate:
276 case StorageMode::kDeviceTransient:
286 VmaAllocator allocator,
288 bool supports_memoryless_textures)
290 FML_DCHECK(desc.
format != PixelFormat::kUnknown);
291 vk::StructureChain<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
293 auto& image_info = image_info_chain.get();
295 image_info.imageType = vk::ImageType::e2D;
297 image_info.extent = VkExtent3D{
305 image_info.tiling = vk::ImageTiling::eOptimal;
306 image_info.initialLayout = vk::ImageLayout::eUndefined;
307 image_info.usage = AllocatorVK::ToVKImageUsageFlags(
309 supports_memoryless_textures);
310 image_info.sharingMode = vk::SharingMode::eExclusive;
312 vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = {
313 vk::ImageCompressionFixedRateFlagBitsEXT::eNone};
315 const auto frc_rate =
319 if (frc_rate.has_value()) {
321 frc_rates[0] = frc_rate.value();
323 auto& compression_info =
324 image_info_chain.get<vk::ImageCompressionControlEXT>();
325 compression_info.pFixedRateFlags = frc_rates;
326 compression_info.compressionControlPlaneCount = 1u;
327 compression_info.flags =
328 vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit;
330 image_info_chain.unlink<vk::ImageCompressionControlEXT>();
333 VmaAllocationCreateInfo alloc_nfo = {};
336 alloc_nfo.preferredFlags =
341 auto create_info_native =
342 static_cast<vk::ImageCreateInfo::NativeType
>(image_info);
344 VkImage vk_image = VK_NULL_HANDLE;
345 VmaAllocation allocation = {};
346 VmaAllocationInfo allocation_info = {};
348 auto result = vk::Result{::vmaCreateImage(allocator,
355 if (result != vk::Result::eSuccess) {
357 << vk::to_string(result)
361 <<
" [VK]Flags: " << vk::to_string(image_info.flags)
362 <<
" [VK]Format: " << vk::to_string(image_info.format)
363 <<
" [VK]Usage: " << vk::to_string(image_info.usage)
364 <<
" [VK]Mem. Flags: "
365 << vk::to_string(vk::MemoryPropertyFlags(
366 alloc_nfo.preferredFlags));
371 auto image = vk::Image{vk_image};
373 vk::ImageViewCreateInfo view_info = {};
374 view_info.image = image;
376 view_info.format = image_info.format;
378 view_info.subresourceRange.levelCount = image_info.mipLevels;
385 if (desc.
format == PixelFormat::kA8UNormInt) {
386 view_info.components.a = vk::ComponentSwizzle::eR;
387 view_info.components.r = vk::ComponentSwizzle::eA;
390 auto [result, image_view] = device.createImageViewUnique(view_info);
391 if (result != vk::Result::eSuccess) {
392 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
393 << vk::to_string(result);
397 view_info.subresourceRange.levelCount = 1u;
398 auto [rt_result, rt_image_view] = device.createImageViewUnique(view_info);
399 if (rt_result != vk::Result::eSuccess) {
400 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
401 << vk::to_string(rt_result);
405 resource_.Swap(ImageResource(
ImageVMA{allocator, allocation, image},
406 std::move(image_view),
407 std::move(rt_image_view)));
415 vk::Image
GetImage()
const override {
return resource_->image.get().image; }
418 return resource_->image_view.get();
422 return resource_->rt_image_view.get();
428 struct ImageResource {
430 vk::UniqueImageView image_view;
431 vk::UniqueImageView rt_image_view;
433 ImageResource() =
default;
436 vk::UniqueImageView p_image_view,
437 vk::UniqueImageView p_rt_image_view)
439 image_view(
std::move(p_image_view)),
440 rt_image_view(
std::move(p_rt_image_view)) {}
442 ImageResource(ImageResource&& o) =
default;
444 ImageResource(
const ImageResource&) =
delete;
446 ImageResource& operator=(
const ImageResource&) =
delete;
449 UniqueResourceVKT<ImageResource> resource_;
450 bool is_valid_ =
false;
452 AllocatedTextureSourceVK(
const AllocatedTextureSourceVK&) =
delete;
454 AllocatedTextureSourceVK& operator=(
const AllocatedTextureSourceVK&) =
delete;
458 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
459 const TextureDescriptor& desc) {
463 auto device_holder = device_holder_.lock();
464 if (!device_holder) {
467 auto context = context_.lock();
471 auto source = std::make_shared<AllocatedTextureSourceVK>(
472 ContextVK::Cast(*context),
475 device_holder->GetDevice(),
476 supports_memoryless_textures_
478 if (!source->IsValid()) {
481 return std::make_shared<TextureVK>(context_, std::move(source));
485 std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
486 const DeviceBufferDescriptor& desc) {
487 vk::BufferCreateInfo buffer_info;
488 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
489 vk::BufferUsageFlagBits::eIndexBuffer |
490 vk::BufferUsageFlagBits::eUniformBuffer |
491 vk::BufferUsageFlagBits::eStorageBuffer |
492 vk::BufferUsageFlagBits::eTransferSrc |
493 vk::BufferUsageFlagBits::eTransferDst;
494 buffer_info.size = desc.size;
495 buffer_info.sharingMode = vk::SharingMode::eExclusive;
496 auto buffer_info_native =
497 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
499 VmaAllocationCreateInfo allocation_info = {};
501 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
503 allocation_info.flags =
505 if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
507 allocation_info.pool = staging_buffer_pool_.get().pool;
510 VkBuffer buffer = {};
511 VmaAllocation buffer_allocation = {};
512 VmaAllocationInfo buffer_allocation_info = {};
513 auto result = vk::Result{::vmaCreateBuffer(allocator_.get(),
518 &buffer_allocation_info
521 if (result != vk::Result::eSuccess) {
523 << vk::to_string(result);
527 return std::make_shared<DeviceBufferVK>(
532 vk::Buffer{buffer}}},
533 buffer_allocation_info
537 Bytes AllocatorVK::DebugGetHeapUsage()
const {
538 auto count = memory_properties_.memoryHeapCount;
539 std::vector<VmaBudget> budgets(count);
540 vmaGetHeapBudgets(allocator_.get(), budgets.data());
541 size_t total_usage = 0;
542 for (
auto i = 0u; i < count; i++) {
543 const VmaBudget& budget = budgets[i];
544 total_usage += budget.usage;
546 return Bytes{
static_cast<double>(total_usage)};
549 void AllocatorVK::DebugTraceMemoryStatistics()
const {
550 #ifdef IMPELLER_DEBUG
551 FML_TRACE_COUNTER(
"flutter",
"AllocatorVK",
552 reinterpret_cast<int64_t
>(
this),
553 "MemoryBudgetUsageMB",
554 DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
555 #endif // IMPELLER_DEBUG