10 #include "flutter/fml/memory/ref_ptr.h"
11 #include "flutter/fml/trace_event.h"
19 #include "vulkan/vulkan_enums.hpp"
23 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
27 return vk::MemoryPropertyFlagBits::eHostVisible;
29 return vk::MemoryPropertyFlagBits::eDeviceLocal;
31 return vk::MemoryPropertyFlagBits::eLazilyAllocated;
39 VmaAllocationCreateFlags flags = 0;
43 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
45 flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
47 flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
50 FML_DCHECK(!readback);
53 FML_DCHECK(!readback);
60 vk::BufferCreateInfo buffer_info;
61 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
62 vk::BufferUsageFlagBits::eIndexBuffer |
63 vk::BufferUsageFlagBits::eUniformBuffer |
64 vk::BufferUsageFlagBits::eStorageBuffer |
65 vk::BufferUsageFlagBits::eTransferSrc |
66 vk::BufferUsageFlagBits::eTransferDst;
67 buffer_info.size = 1u;
68 buffer_info.sharingMode = vk::SharingMode::eExclusive;
69 auto buffer_info_native =
70 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
72 VmaAllocationCreateInfo allocation_info = {};
73 allocation_info.usage = VMA_MEMORY_USAGE_AUTO;
74 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
79 uint32_t memTypeIndex;
80 auto result = vk::Result{vmaFindMemoryTypeIndexForBufferInfo(
81 allocator, &buffer_info_native, &allocation_info, &memTypeIndex)};
82 if (result != vk::Result::eSuccess) {
86 VmaPoolCreateInfo pool_create_info = {};
87 pool_create_info.memoryTypeIndex = memTypeIndex;
88 pool_create_info.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;
89 pool_create_info.minBlockCount = 1;
92 result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
93 if (result != vk::Result::eSuccess) {
96 return {allocator, pool};
99 AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
100 uint32_t vulkan_api_version,
101 const vk::PhysicalDevice& physical_device,
102 const std::shared_ptr<DeviceHolderVK>& device_holder,
103 const vk::Instance& instance,
104 const CapabilitiesVK& capabilities)
105 : context_(
std::move(context)), device_holder_(device_holder) {
106 auto limits = physical_device.getProperties().limits;
107 max_texture_size_.width = max_texture_size_.height =
108 limits.maxImageDimension2D;
109 physical_device.getMemoryProperties(&memory_properties_);
111 VmaVulkanFunctions proc_table = {};
113 #define BIND_VMA_PROC(x) proc_table.x = VULKAN_HPP_DEFAULT_DISPATCHER.x;
114 #define BIND_VMA_PROC_KHR(x) \
115 proc_table.x##KHR = VULKAN_HPP_DEFAULT_DISPATCHER.x \
116 ? VULKAN_HPP_DEFAULT_DISPATCHER.x \
117 : VULKAN_HPP_DEFAULT_DISPATCHER.x##KHR;
142 #undef BIND_VMA_PROC_KHR
145 VmaAllocatorCreateInfo allocator_info = {};
146 allocator_info.vulkanApiVersion = vulkan_api_version;
147 allocator_info.physicalDevice = physical_device;
148 allocator_info.device = device_holder->GetDevice();
149 allocator_info.instance = instance;
151 allocator_info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
152 allocator_info.pVulkanFunctions = &proc_table;
154 VmaAllocator allocator = {};
155 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
156 if (result != vk::Result::eSuccess) {
161 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
162 allocator_.reset(allocator);
163 supports_memoryless_textures_ =
164 capabilities.SupportsDeviceTransientTextures();
168 AllocatorVK::~AllocatorVK() =
default;
171 bool AllocatorVK::IsValid()
const {
176 ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
177 return max_texture_size_;
180 int32_t AllocatorVK::FindMemoryTypeIndex(
181 uint32_t memory_type_bits_requirement,
182 vk::PhysicalDeviceMemoryProperties& memory_properties) {
183 int32_t type_index = -1;
184 vk::MemoryPropertyFlagBits required_properties =
185 vk::MemoryPropertyFlagBits::eDeviceLocal;
187 const uint32_t memory_count = memory_properties.memoryTypeCount;
188 for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
189 const uint32_t memory_type_bits = (1 << memory_index);
190 const bool is_required_memory_type =
191 memory_type_bits_requirement & memory_type_bits;
193 const auto properties =
194 memory_properties.memoryTypes[memory_index].propertyFlags;
195 const bool has_required_properties =
196 (properties & required_properties) == required_properties;
198 if (is_required_memory_type && has_required_properties) {
199 return static_cast<int32_t
>(memory_index);
206 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
210 bool supports_memoryless_textures) {
211 vk::ImageUsageFlags vk_usage;
214 case StorageMode::kHostVisible:
215 case StorageMode::kDevicePrivate:
217 case StorageMode::kDeviceTransient:
218 if (supports_memoryless_textures) {
219 vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
224 if (usage & TextureUsage::kRenderTarget) {
226 vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
228 vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
229 vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
233 if (usage & TextureUsage::kShaderRead) {
234 vk_usage |= vk::ImageUsageFlagBits::eSampled;
237 if (usage & TextureUsage::kShaderWrite) {
238 vk_usage |= vk::ImageUsageFlagBits::eStorage;
241 if (mode != StorageMode::kDeviceTransient) {
244 vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
245 vk::ImageUsageFlagBits::eTransferDst;
252 return VMA_MEMORY_USAGE_AUTO;
255 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
257 bool supports_memoryless_textures) {
259 case StorageMode::kHostVisible:
260 return vk::MemoryPropertyFlagBits::eHostVisible |
261 vk::MemoryPropertyFlagBits::eDeviceLocal;
262 case StorageMode::kDevicePrivate:
263 return vk::MemoryPropertyFlagBits::eDeviceLocal;
264 case StorageMode::kDeviceTransient:
265 if (supports_memoryless_textures) {
266 return vk::MemoryPropertyFlagBits::eLazilyAllocated |
267 vk::MemoryPropertyFlagBits::eDeviceLocal;
269 return vk::MemoryPropertyFlagBits::eDeviceLocal;
275 VmaAllocationCreateFlags flags = 0;
277 case StorageMode::kHostVisible:
279 case StorageMode::kDevicePrivate:
281 case StorageMode::kDeviceTransient:
291 VmaAllocator allocator,
293 bool supports_memoryless_textures)
295 FML_DCHECK(desc.
format != PixelFormat::kUnknown);
296 vk::StructureChain<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
298 auto& image_info = image_info_chain.get();
300 image_info.imageType = vk::ImageType::e2D;
302 image_info.extent = VkExtent3D{
310 image_info.tiling = vk::ImageTiling::eOptimal;
311 image_info.initialLayout = vk::ImageLayout::eUndefined;
312 image_info.usage = AllocatorVK::ToVKImageUsageFlags(
314 supports_memoryless_textures);
315 image_info.sharingMode = vk::SharingMode::eExclusive;
317 vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = {
318 vk::ImageCompressionFixedRateFlagBitsEXT::eNone};
320 const auto frc_rate =
324 if (frc_rate.has_value()) {
326 frc_rates[0] = frc_rate.value();
328 auto& compression_info =
329 image_info_chain.get<vk::ImageCompressionControlEXT>();
330 compression_info.pFixedRateFlags = frc_rates;
331 compression_info.compressionControlPlaneCount = 1u;
332 compression_info.flags =
333 vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit;
335 image_info_chain.unlink<vk::ImageCompressionControlEXT>();
338 VmaAllocationCreateInfo alloc_nfo = {};
341 alloc_nfo.preferredFlags =
346 auto create_info_native =
347 static_cast<vk::ImageCreateInfo::NativeType
>(image_info);
349 VkImage vk_image = VK_NULL_HANDLE;
350 VmaAllocation allocation = {};
351 VmaAllocationInfo allocation_info = {};
353 auto result = vk::Result{::vmaCreateImage(allocator,
360 if (result != vk::Result::eSuccess) {
362 << vk::to_string(result)
366 <<
" [VK]Flags: " << vk::to_string(image_info.flags)
367 <<
" [VK]Format: " << vk::to_string(image_info.format)
368 <<
" [VK]Usage: " << vk::to_string(image_info.usage)
369 <<
" [VK]Mem. Flags: "
370 << vk::to_string(vk::MemoryPropertyFlags(
371 alloc_nfo.preferredFlags));
376 auto image = vk::Image{vk_image};
378 vk::ImageViewCreateInfo view_info = {};
379 view_info.image = image;
381 view_info.format = image_info.format;
383 view_info.subresourceRange.levelCount = image_info.mipLevels;
390 if (desc.
format == PixelFormat::kA8UNormInt) {
391 view_info.components.a = vk::ComponentSwizzle::eR;
392 view_info.components.r = vk::ComponentSwizzle::eA;
395 auto [result, image_view] = device.createImageViewUnique(view_info);
396 if (result != vk::Result::eSuccess) {
397 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
398 << vk::to_string(result);
402 view_info.subresourceRange.levelCount = 1u;
403 auto [rt_result, rt_image_view] = device.createImageViewUnique(view_info);
404 if (rt_result != vk::Result::eSuccess) {
405 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
406 << vk::to_string(rt_result);
410 resource_.Swap(ImageResource(
411 ImageVMA{allocator, allocation, image}, std::move(image_view),
421 vk::Image
GetImage()
const override {
return resource_->image.get().image; }
424 return resource_->image_view.get();
428 return resource_->rt_image_view.get();
434 struct ImageResource {
435 std::shared_ptr<DeviceHolderVK> device_holder;
436 std::shared_ptr<Allocator> allocator;
438 vk::UniqueImageView image_view;
439 vk::UniqueImageView rt_image_view;
441 ImageResource() =
default;
444 vk::UniqueImageView p_image_view,
445 vk::UniqueImageView p_rt_image_view,
446 std::shared_ptr<Allocator> allocator,
447 std::shared_ptr<DeviceHolderVK> device_holder)
448 : device_holder(
std::move(device_holder)),
449 allocator(
std::move(allocator)),
451 image_view(
std::move(p_image_view)),
452 rt_image_view(
std::move(p_rt_image_view)) {}
454 ImageResource(ImageResource&& o) =
default;
456 ImageResource(
const ImageResource&) =
delete;
458 ImageResource& operator=(
const ImageResource&) =
delete;
461 UniqueResourceVKT<ImageResource> resource_;
462 bool is_valid_ =
false;
464 AllocatedTextureSourceVK(
const AllocatedTextureSourceVK&) =
delete;
466 AllocatedTextureSourceVK& operator=(
const AllocatedTextureSourceVK&) =
delete;
470 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
471 const TextureDescriptor& desc) {
475 auto device_holder = device_holder_.lock();
476 if (!device_holder) {
479 auto context = context_.lock();
483 auto source = std::make_shared<AllocatedTextureSourceVK>(
484 ContextVK::Cast(*context),
487 device_holder->GetDevice(),
488 supports_memoryless_textures_
490 if (!source->IsValid()) {
493 return std::make_shared<TextureVK>(context_, std::move(source));
497 std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
498 const DeviceBufferDescriptor& desc) {
499 vk::BufferCreateInfo buffer_info;
500 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
501 vk::BufferUsageFlagBits::eIndexBuffer |
502 vk::BufferUsageFlagBits::eUniformBuffer |
503 vk::BufferUsageFlagBits::eStorageBuffer |
504 vk::BufferUsageFlagBits::eTransferSrc |
505 vk::BufferUsageFlagBits::eTransferDst;
506 buffer_info.size = desc.size;
507 buffer_info.sharingMode = vk::SharingMode::eExclusive;
508 auto buffer_info_native =
509 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
511 VmaAllocationCreateInfo allocation_info = {};
513 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
515 allocation_info.flags =
517 if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
519 allocation_info.pool = staging_buffer_pool_.get().pool;
521 VkBuffer buffer = {};
522 VmaAllocation buffer_allocation = {};
523 VmaAllocationInfo buffer_allocation_info = {};
524 auto result = vk::Result{::vmaCreateBuffer(allocator_.get(),
529 &buffer_allocation_info
532 auto type = memory_properties_.memoryTypes[buffer_allocation_info.memoryType];
533 bool is_host_coherent =
534 !!(
type.propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent);
536 if (result != vk::Result::eSuccess) {
538 << vk::to_string(result);
542 return std::make_shared<DeviceBufferVK>(
547 vk::Buffer{buffer}}},
548 buffer_allocation_info,
552 Bytes AllocatorVK::DebugGetHeapUsage()
const {
553 auto count = memory_properties_.memoryHeapCount;
554 std::vector<VmaBudget> budgets(count);
555 vmaGetHeapBudgets(allocator_.get(), budgets.data());
556 size_t total_usage = 0;
557 for (
auto i = 0u; i < count; i++) {
558 const VmaBudget& budget = budgets[i];
559 total_usage += budget.usage;
561 return Bytes{
static_cast<double>(total_usage)};
564 void AllocatorVK::DebugTraceMemoryStatistics()
const {
565 #ifdef IMPELLER_DEBUG
566 FML_TRACE_COUNTER(
"flutter",
"AllocatorVK",
567 reinterpret_cast<int64_t
>(
this),
568 "MemoryBudgetUsageMB",
569 DebugGetHeapUsage().ConvertTo<MebiBytes>().GetSize());
#define BIND_VMA_PROC_KHR(x)
vk::ImageView GetRenderTargetView() const override
Retrieve the image view used for render target attachments with this texture source.
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
bool IsSwapchainImage() const override
Determines if swapchain image. That is, an image used as the root render target.
std::shared_ptr< Allocator > GetResourceAllocator() const override
Returns the allocator used to create textures and buffers on the device.
const std::shared_ptr< const Capabilities > & GetCapabilities() const override
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
std::shared_ptr< DeviceHolderVK > GetDeviceHolder() const
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)
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
CompressionType compression_type