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;
87 pool_create_info.minBlockCount = 1;
90 result = vk::Result{::vmaCreatePool(allocator, &pool_create_info, &pool)};
91 if (result != vk::Result::eSuccess) {
94 return {allocator, pool};
97 AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
98 uint32_t vulkan_api_version,
99 const vk::PhysicalDevice& physical_device,
100 const std::shared_ptr<DeviceHolderVK>& device_holder,
101 const vk::Instance& instance,
102 const CapabilitiesVK& capabilities)
103 : context_(
std::move(context)), device_holder_(device_holder) {
104 auto limits = physical_device.getProperties().limits;
105 max_texture_size_.width = max_texture_size_.height =
106 limits.maxImageDimension2D;
107 physical_device.getMemoryProperties(&memory_properties_);
109 VmaVulkanFunctions proc_table = {};
111 #define BIND_VMA_PROC(x) proc_table.x = VULKAN_HPP_DEFAULT_DISPATCHER.x;
112 #define BIND_VMA_PROC_KHR(x) \
113 proc_table.x##KHR = VULKAN_HPP_DEFAULT_DISPATCHER.x \
114 ? VULKAN_HPP_DEFAULT_DISPATCHER.x \
115 : VULKAN_HPP_DEFAULT_DISPATCHER.x##KHR;
140 #undef BIND_VMA_PROC_KHR
143 VmaAllocatorCreateInfo allocator_info = {};
144 allocator_info.vulkanApiVersion = vulkan_api_version;
145 allocator_info.physicalDevice = physical_device;
146 allocator_info.device = device_holder->GetDevice();
147 allocator_info.instance = instance;
149 allocator_info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
150 allocator_info.pVulkanFunctions = &proc_table;
152 VmaAllocator allocator = {};
153 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &allocator)};
154 if (result != vk::Result::eSuccess) {
159 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
160 allocator_.reset(allocator);
161 supports_memoryless_textures_ =
162 capabilities.SupportsDeviceTransientTextures();
166 AllocatorVK::~AllocatorVK() =
default;
169 bool AllocatorVK::IsValid()
const {
174 ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
175 return max_texture_size_;
178 int32_t AllocatorVK::FindMemoryTypeIndex(
179 uint32_t memory_type_bits_requirement,
180 vk::PhysicalDeviceMemoryProperties& memory_properties) {
181 int32_t type_index = -1;
182 vk::MemoryPropertyFlagBits required_properties =
183 vk::MemoryPropertyFlagBits::eDeviceLocal;
185 const uint32_t memory_count = memory_properties.memoryTypeCount;
186 for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
187 const uint32_t memory_type_bits = (1 << memory_index);
188 const bool is_required_memory_type =
189 memory_type_bits_requirement & memory_type_bits;
191 const auto properties =
192 memory_properties.memoryTypes[memory_index].propertyFlags;
193 const bool has_required_properties =
194 (properties & required_properties) == required_properties;
196 if (is_required_memory_type && has_required_properties) {
197 return static_cast<int32_t
>(memory_index);
204 vk::ImageUsageFlags AllocatorVK::ToVKImageUsageFlags(
208 bool supports_memoryless_textures) {
209 vk::ImageUsageFlags vk_usage;
212 case StorageMode::kHostVisible:
213 case StorageMode::kDevicePrivate:
215 case StorageMode::kDeviceTransient:
216 if (supports_memoryless_textures) {
217 vk_usage |= vk::ImageUsageFlagBits::eTransientAttachment;
222 if (usage & TextureUsage::kRenderTarget) {
224 vk_usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
226 vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
227 vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
231 if (usage & TextureUsage::kShaderRead) {
232 vk_usage |= vk::ImageUsageFlagBits::eSampled;
235 if (usage & TextureUsage::kShaderWrite) {
236 vk_usage |= vk::ImageUsageFlagBits::eStorage;
239 if (mode != StorageMode::kDeviceTransient) {
242 vk_usage |= vk::ImageUsageFlagBits::eTransferSrc |
243 vk::ImageUsageFlagBits::eTransferDst;
250 return VMA_MEMORY_USAGE_AUTO;
253 static constexpr vk::Flags<vk::MemoryPropertyFlagBits>
255 bool supports_memoryless_textures) {
257 case StorageMode::kHostVisible:
258 return vk::MemoryPropertyFlagBits::eHostVisible |
259 vk::MemoryPropertyFlagBits::eDeviceLocal;
260 case StorageMode::kDevicePrivate:
261 return vk::MemoryPropertyFlagBits::eDeviceLocal;
262 case StorageMode::kDeviceTransient:
263 if (supports_memoryless_textures) {
264 return vk::MemoryPropertyFlagBits::eLazilyAllocated |
265 vk::MemoryPropertyFlagBits::eDeviceLocal;
267 return vk::MemoryPropertyFlagBits::eDeviceLocal;
273 VmaAllocationCreateFlags flags = 0;
275 case StorageMode::kHostVisible:
277 case StorageMode::kDevicePrivate:
279 case StorageMode::kDeviceTransient:
289 VmaAllocator allocator,
291 bool supports_memoryless_textures)
293 FML_DCHECK(desc.
format != PixelFormat::kUnknown);
294 vk::StructureChain<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
296 auto& image_info = image_info_chain.get();
298 image_info.imageType = vk::ImageType::e2D;
300 image_info.extent = VkExtent3D{
308 image_info.tiling = vk::ImageTiling::eOptimal;
309 image_info.initialLayout = vk::ImageLayout::eUndefined;
310 image_info.usage = AllocatorVK::ToVKImageUsageFlags(
312 supports_memoryless_textures);
313 image_info.sharingMode = vk::SharingMode::eExclusive;
315 vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = {
316 vk::ImageCompressionFixedRateFlagBitsEXT::eNone};
318 const auto frc_rate =
322 if (frc_rate.has_value()) {
324 frc_rates[0] = frc_rate.value();
326 auto& compression_info =
327 image_info_chain.get<vk::ImageCompressionControlEXT>();
328 compression_info.pFixedRateFlags = frc_rates;
329 compression_info.compressionControlPlaneCount = 1u;
330 compression_info.flags =
331 vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit;
333 image_info_chain.unlink<vk::ImageCompressionControlEXT>();
336 VmaAllocationCreateInfo alloc_nfo = {};
339 alloc_nfo.preferredFlags =
344 auto create_info_native =
345 static_cast<vk::ImageCreateInfo::NativeType
>(image_info);
347 VkImage vk_image = VK_NULL_HANDLE;
348 VmaAllocation allocation = {};
349 VmaAllocationInfo allocation_info = {};
351 auto result = vk::Result{::vmaCreateImage(allocator,
358 if (result != vk::Result::eSuccess) {
360 << vk::to_string(result)
364 <<
" [VK]Flags: " << vk::to_string(image_info.flags)
365 <<
" [VK]Format: " << vk::to_string(image_info.format)
366 <<
" [VK]Usage: " << vk::to_string(image_info.usage)
367 <<
" [VK]Mem. Flags: "
368 << vk::to_string(vk::MemoryPropertyFlags(
369 alloc_nfo.preferredFlags));
374 auto image = vk::Image{vk_image};
376 vk::ImageViewCreateInfo view_info = {};
377 view_info.image = image;
379 view_info.format = image_info.format;
381 view_info.subresourceRange.levelCount = image_info.mipLevels;
388 if (desc.
format == PixelFormat::kA8UNormInt) {
389 view_info.components.a = vk::ComponentSwizzle::eR;
390 view_info.components.r = vk::ComponentSwizzle::eA;
393 auto [result, image_view] = device.createImageViewUnique(view_info);
394 if (result != vk::Result::eSuccess) {
395 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
396 << vk::to_string(result);
400 view_info.subresourceRange.levelCount = 1u;
401 auto [rt_result, rt_image_view] = device.createImageViewUnique(view_info);
402 if (rt_result != vk::Result::eSuccess) {
403 VALIDATION_LOG <<
"Unable to create an image view for allocation: "
404 << vk::to_string(rt_result);
408 resource_.Swap(ImageResource(
ImageVMA{allocator, allocation, image},
409 std::move(image_view),
410 std::move(rt_image_view)));
418 vk::Image
GetImage()
const override {
return resource_->image.get().image; }
421 return resource_->image_view.get();
425 return resource_->rt_image_view.get();
431 struct ImageResource {
433 vk::UniqueImageView image_view;
434 vk::UniqueImageView rt_image_view;
436 ImageResource() =
default;
439 vk::UniqueImageView p_image_view,
440 vk::UniqueImageView p_rt_image_view)
442 image_view(
std::move(p_image_view)),
443 rt_image_view(
std::move(p_rt_image_view)) {}
445 ImageResource(ImageResource&& o) =
default;
447 ImageResource(
const ImageResource&) =
delete;
449 ImageResource& operator=(
const ImageResource&) =
delete;
452 UniqueResourceVKT<ImageResource> resource_;
453 bool is_valid_ =
false;
455 AllocatedTextureSourceVK(
const AllocatedTextureSourceVK&) =
delete;
457 AllocatedTextureSourceVK& operator=(
const AllocatedTextureSourceVK&) =
delete;
461 std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
462 const TextureDescriptor& desc) {
466 auto device_holder = device_holder_.lock();
467 if (!device_holder) {
470 auto context = context_.lock();
474 auto source = std::make_shared<AllocatedTextureSourceVK>(
475 ContextVK::Cast(*context),
478 device_holder->GetDevice(),
479 supports_memoryless_textures_
481 if (!source->IsValid()) {
484 return std::make_shared<TextureVK>(context_, std::move(source));
488 std::shared_ptr<DeviceBuffer> AllocatorVK::OnCreateBuffer(
489 const DeviceBufferDescriptor& desc) {
490 vk::BufferCreateInfo buffer_info;
491 buffer_info.usage = vk::BufferUsageFlagBits::eVertexBuffer |
492 vk::BufferUsageFlagBits::eIndexBuffer |
493 vk::BufferUsageFlagBits::eUniformBuffer |
494 vk::BufferUsageFlagBits::eStorageBuffer |
495 vk::BufferUsageFlagBits::eTransferSrc |
496 vk::BufferUsageFlagBits::eTransferDst;
497 buffer_info.size = desc.size;
498 buffer_info.sharingMode = vk::SharingMode::eExclusive;
499 auto buffer_info_native =
500 static_cast<vk::BufferCreateInfo::NativeType
>(buffer_info);
502 VmaAllocationCreateInfo allocation_info = {};
504 allocation_info.preferredFlags =
static_cast<VkMemoryPropertyFlags
>(
506 allocation_info.flags =
508 if (created_buffer_pool_ && desc.storage_mode == StorageMode::kHostVisible &&
510 allocation_info.pool = staging_buffer_pool_.get().pool;
512 VkBuffer buffer = {};
513 VmaAllocation buffer_allocation = {};
514 VmaAllocationInfo buffer_allocation_info = {};
515 auto result = vk::Result{::vmaCreateBuffer(allocator_.get(),
520 &buffer_allocation_info
523 auto type = memory_properties_.memoryTypes[buffer_allocation_info.memoryType];
524 bool is_host_coherent =
525 !!(
type.propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent);
527 if (result != vk::Result::eSuccess) {
529 << vk::to_string(result);
533 return std::make_shared<DeviceBufferVK>(
538 vk::Buffer{buffer}}},
539 buffer_allocation_info,
543 Bytes AllocatorVK::DebugGetHeapUsage()
const {
544 auto count = memory_properties_.memoryHeapCount;
545 std::vector<VmaBudget> budgets(count);
546 vmaGetHeapBudgets(allocator_.get(), budgets.data());
547 size_t total_usage = 0;
548 for (
auto i = 0u; i < count; i++) {
549 const VmaBudget& budget = budgets[i];
550 total_usage += budget.usage;
552 return Bytes{
static_cast<double>(total_usage)};
555 void AllocatorVK::DebugTraceMemoryStatistics()
const {
556 #ifdef IMPELLER_DEBUG
557 FML_TRACE_COUNTER(
"flutter",
"AllocatorVK",
558 reinterpret_cast<int64_t
>(
this),
559 "MemoryBudgetUsageMB",
560 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.
const std::shared_ptr< const Capabilities > & GetCapabilities() const override
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
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