9 #include "flutter/fml/memory/ref_ptr.h"
10 #include "flutter/fml/trace_event.h"
15 #include "vulkan/vulkan_enums.hpp"
19 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
23 return vk::MemoryPropertyFlagBits::eHostVisible;
25 return vk::MemoryPropertyFlagBits::eDeviceLocal;
27 return vk::MemoryPropertyFlagBits::eLazilyAllocated;
35 VmaAllocationCreateFlags flags = 0;
39 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
41 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
43 flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
46 FML_DCHECK(!readback);
49 FML_DCHECK(!readback);
56 vk::BufferCreateInfo buffer_info;
57 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
58 vk::BufferUsageFlagBits::eIndexBuffer |
59 vk::BufferUsageFlagBits::eUniformBuffer |
60 vk::BufferUsageFlagBits::eStorageBuffer |
61 vk::BufferUsageFlagBits::eTransferSrc |
62 vk::BufferUsageFlagBits::eTransferDst;
63 buffer_info.size = 1u;
64 buffer_info.sharingMode = vk::SharingMode::eExclusive;
65 auto buffer_info_native =
66 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
68 VmaAllocationCreateInfo allocation_info = {};
69 allocation_info.usage = VMA_MEMORY_USAGE_AUTO;
70 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
75 uint32_t memTypeIndex;
76 auto result = vk::Result{vmaFindMemoryTypeIndexForBufferInfo(
77 allocator, &buffer_info_native, &allocation_info, &memTypeIndex)};
78 if (result != vk::Result::eSuccess) {
82 VmaPoolCreateInfo pool_create_info = {};
83 pool_create_info.memoryTypeIndex = memTypeIndex;
84 pool_create_info.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;
87 result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
88 if (result != vk::Result::eSuccess) {
91 return {allocator, pool};
94 AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
95 uint32_t vulkan_api_version,
96 const vk::PhysicalDevice& physical_device,
97 const std::shared_ptr<DeviceHolderVK>& device_holder,
98 const vk::Instance& instance,
99 const CapabilitiesVK& capabilities)
100 : context_(
std::move(context)), device_holder_(device_holder) {
101 auto limits = physical_device.getProperties().limits;
102 max_texture_size_.width = max_texture_size_.height =
103 limits.maxImageDimension2D;
104 physical_device.getMemoryProperties(&memory_properties_);
106 VmaVulkanFunctions proc_table = {};
108 #define BIND_VMA_PROC(x) proc_table.x = VULKAN_HPP_DEFAULT_DISPATCHER.x;
109 #define BIND_VMA_PROC_KHR(x) \
110 proc_table.x##KHR = VULKAN_HPP_DEFAULT_DISPATCHER.x \
111 ? VULKAN_HPP_DEFAULT_DISPATCHER.x \
112 : VULKAN_HPP_DEFAULT_DISPATCHER.x##KHR;
137 #undef BIND_VMA_PROC_KHR
140 VmaAllocatorCreateInfo allocator_info = {};
141 allocator_info.vulkanApiVersion = vulkan_api_version;
142 allocator_info.physicalDevice = physical_device;
143 allocator_info.device = device_holder->GetDevice();
144 allocator_info.instance = instance;
145 allocator_info.pVulkanFunctions = &proc_table;
147 VmaAllocator allocator = {};
148 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
149 if (result != vk::Result::eSuccess) {
154 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
155 allocator_.reset(allocator);
156 supports_memoryless_textures_ =
157 capabilities.SupportsDeviceTransientTextures();
161 AllocatorVK::~AllocatorVK() =
default;
164 bool AllocatorVK::IsValid()
const {
169 ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
170 return max_texture_size_;
173 int32_t AllocatorVK::FindMemoryTypeIndex(
174 uint32_t memory_type_bits_requirement,
175 vk::PhysicalDeviceMemoryProperties& memory_properties) {
176 int32_t type_index = -1;
177 vk::MemoryPropertyFlagBits required_properties =
178 vk::MemoryPropertyFlagBits::eDeviceLocal;
180 const uint32_t memory_count = memory_properties.memoryTypeCount;
181 for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
182 const uint32_t memory_type_bits = (1 << memory_index);
183 const bool is_required_memory_type =
184 memory_type_bits_requirement & memory_type_bits;
186 const auto properties =
187 memory_properties.memoryTypes[memory_index].propertyFlags;
188 const bool has_required_properties =
189 (properties & required_properties) == required_properties;
191 if (is_required_memory_type && has_required_properties) {
192 return static_cast<int32_t
>(memory_index);
199 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
203 bool supports_memoryless_textures) {
204 vk::ImageUsageFlags vk_usage;
207 case StorageMode::kHostVisible:
208 case StorageMode::kDevicePrivate:
210 case StorageMode::kDeviceTransient:
211 if (supports_memoryless_textures) {
212 vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
217 if (usage & TextureUsage::kRenderTarget) {
219 vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
221 vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
222 vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
226 if (usage & TextureUsage::kShaderRead) {
227 vk_usage |= vk::ImageUsageFlagBits::eSampled;
230 if (usage & TextureUsage::kShaderWrite) {
231 vk_usage |= vk::ImageUsageFlagBits::eStorage;
234 if (mode != StorageMode::kDeviceTransient) {
237 vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
238 vk::ImageUsageFlagBits::eTransferDst;
245 return VMA_MEMORY_USAGE_AUTO;
248 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
250 bool supports_memoryless_textures) {
252 case StorageMode::kHostVisible:
253 return vk::MemoryPropertyFlagBits::eHostVisible |
254 vk::MemoryPropertyFlagBits::eDeviceLocal;
255 case StorageMode::kDevicePrivate:
256 return vk::MemoryPropertyFlagBits::eDeviceLocal;
257 case StorageMode::kDeviceTransient:
258 if (supports_memoryless_textures) {
259 return vk::MemoryPropertyFlagBits::eLazilyAllocated |
260 vk::MemoryPropertyFlagBits::eDeviceLocal;
262 return vk::MemoryPropertyFlagBits::eDeviceLocal;
268 VmaAllocationCreateFlags flags = 0;
270 case StorageMode::kHostVisible:
272 case StorageMode::kDevicePrivate:
274 case StorageMode::kDeviceTransient:
284 VmaAllocator allocator,
286 bool supports_memoryless_textures)
288 FML_DCHECK(desc.
format != PixelFormat::kUnknown);
289 vk::ImageCreateInfo image_info;
291 image_info.imageType = vk::ImageType::e2D;
293 image_info.extent = VkExtent3D{
301 image_info.tiling = vk::ImageTiling::eOptimal;
302 image_info.initialLayout = vk::ImageLayout::eUndefined;
303 image_info.usage = AllocatorVK::ToVKImageUsageFlags(
305 supports_memoryless_textures);
306 image_info.sharingMode = vk::SharingMode::eExclusive;
308 VmaAllocationCreateInfo alloc_nfo = {};
311 alloc_nfo.preferredFlags =
316 auto create_info_native =
317 static_cast<vk::ImageCreateInfo::NativeType
>(image_info);
319 VkImage vk_image = VK_NULL_HANDLE;
320 VmaAllocation allocation = {};
321 VmaAllocationInfo allocation_info = {};
323 auto result = vk::Result{::vmaCreateImage(allocator,
330 if (result != vk::Result::eSuccess) {
332 << vk::to_string(result)
336 <<
" [VK]Flags: " << vk::to_string(image_info.flags)
337 <<
" [VK]Format: " << vk::to_string(image_info.format)
338 <<
" [VK]Usage: " << vk::to_string(image_info.usage)
339 <<
" [VK]Mem. Flags: "
340 << vk::to_string(vk::MemoryPropertyFlags(
341 alloc_nfo.preferredFlags));
346 auto image = vk::Image{vk_image};
348 vk::ImageViewCreateInfo view_info = {};
349 view_info.image = image;
351 view_info.format = image_info.format;
353 view_info.subresourceRange.levelCount = image_info.mipLevels;
360 if (desc.
format == PixelFormat::kA8UNormInt) {
361 view_info.components.a = vk::ComponentSwizzle::eR;
362 view_info.components.r = vk::ComponentSwizzle::eA;
365 auto [result, image_view] = device.createImageViewUnique(view_info);
366 if (result != vk::Result::eSuccess) {
367 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
368 << vk::to_string(result);
372 view_info.subresourceRange.levelCount = 1u;
373 auto [rt_result, rt_image_view] = device.createImageViewUnique(view_info);
374 if (rt_result != vk::Result::eSuccess) {
375 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
376 << vk::to_string(rt_result);
380 resource_.Swap(ImageResource(
ImageVMA{allocator, allocation, image},
381 std::move(image_view),
382 std::move(rt_image_view)));
390 vk::Image
GetImage()
const override {
return resource_->image.get().image; }
393 return resource_->image_view.get();
397 return resource_->rt_image_view.get();
403 struct ImageResource {
405 vk::UniqueImageView image_view;
406 vk::UniqueImageView rt_image_view;
408 ImageResource() =
default;
411 vk::UniqueImageView p_image_view,
412 vk::UniqueImageView p_rt_image_view)
414 image_view(
std::move(p_image_view)),
415 rt_image_view(
std::move(p_rt_image_view)) {}
417 ImageResource(ImageResource&& o) =
default;
419 ImageResource(
const ImageResource&) =
delete;
421 ImageResource& operator=(
const ImageResource&) =
delete;
424 UniqueResourceVKT<ImageResource> resource_;
425 bool is_valid_ =
false;
427 AllocatedTextureSourceVK(
const AllocatedTextureSourceVK&) =
delete;
429 AllocatedTextureSourceVK& operator=(
const AllocatedTextureSourceVK&) =
delete;
433 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
434 const TextureDescriptor& desc) {
438 auto device_holder = device_holder_.lock();
439 if (!device_holder) {
442 auto context = context_.lock();
446 auto source = std::make_shared<AllocatedTextureSourceVK>(
447 ContextVK::Cast(*context).GetResourceManager(),
450 device_holder->GetDevice(),
451 supports_memoryless_textures_
453 if (!source->IsValid()) {
456 return std::make_shared<TextureVK>(context_, std::move(source));
460 std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
461 const DeviceBufferDescriptor& desc) {
462 vk::BufferCreateInfo buffer_info;
463 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
464 vk::BufferUsageFlagBits::eIndexBuffer |
465 vk::BufferUsageFlagBits::eUniformBuffer |
466 vk::BufferUsageFlagBits::eStorageBuffer |
467 vk::BufferUsageFlagBits::eTransferSrc |
468 vk::BufferUsageFlagBits::eTransferDst;
469 buffer_info.size = desc.size;
470 buffer_info.sharingMode = vk::SharingMode::eExclusive;
471 auto buffer_info_native =
472 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
474 VmaAllocationCreateInfo allocation_info = {};
476 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
478 allocation_info.flags =
480 if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
482 allocation_info.pool = staging_buffer_pool_.get().pool;
485 VkBuffer buffer = {};
486 VmaAllocation buffer_allocation = {};
487 VmaAllocationInfo buffer_allocation_info = {};
488 auto result = vk::Result{::vmaCreateBuffer(allocator_.get(),
493 &buffer_allocation_info
496 if (result != vk::Result::eSuccess) {
498 << vk::to_string(result);
502 return std::make_shared<DeviceBufferVK>(
507 vk::Buffer{buffer}}},
508 buffer_allocation_info
512 size_t AllocatorVK::DebugGetHeapUsage()
const {
513 auto count = memory_properties_.memoryHeapCount;
514 std::vector<VmaBudget> budgets(count);
515 vmaGetHeapBudgets(allocator_.get(), budgets.data());
516 size_t total_usage = 0;
517 for (
auto i = 0u; i < count; i++) {
518 const VmaBudget& budget = budgets[i];
519 total_usage += budget.usage;
526 void AllocatorVK::DebugTraceMemoryStatistics()
const {
527 #ifdef IMPELLER_DEBUG
528 FML_TRACE_COUNTER(
"flutter",
"AllocatorVK",
529 reinterpret_cast<int64_t
>(
this),
530 "MemoryBudgetUsageMB", DebugGetHeapUsage());
531 #endif // IMPELLER_DEBUG