21#include "vulkan/vulkan_enums.hpp"
25static 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;
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) {
101AllocatorVK::AllocatorVK(std::weak_ptr<Context>
context,
102 uint32_t vulkan_api_version,
104 const std::shared_ptr<DeviceHolderVK>& device_holder,
106 const CapabilitiesVK& capabilities)
107 : context_(
std::move(
context)), device_holder_(device_holder) {
109 max_texture_size_.width = max_texture_size_.height =
110 limits.maxImageDimension2D;
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;
150 allocator_info.device = device_holder->GetDevice();
153 allocator_info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
154 allocator_info.pVulkanFunctions = &proc_table;
157 auto result = vk::Result{::vmaCreateAllocator(&allocator_info, &
allocator)};
158 if (result != vk::Result::eSuccess) {
163 created_buffer_pool_ &= staging_buffer_pool_.is_valid();
165 supports_memoryless_textures_ =
166 capabilities.SupportsDeviceTransientTextures();
170AllocatorVK::~AllocatorVK() =
default;
173bool AllocatorVK::IsValid()
const {
178ISize AllocatorVK::GetMaxTextureSizeSupported()
const {
179 return max_texture_size_;
182int32_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);
208vk::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;
257static 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:
295 bool supports_memoryless_textures)
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 = {};
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(
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;
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,
506 std::shared_ptr<DeviceHolderVK> device_holder)
507 : device_holder(
std::move(device_holder)),
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;
529std::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));
557std::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;
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>(
605 UniqueBufferVMA{BufferVMA{allocator_.get(),
608 buffer_allocation_info,
612Bytes 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{
static_cast<double>(total_usage)};
624void AllocatorVK::DebugTraceMemoryStatistics()
const {
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.
FlutterVulkanImage * image
VkPhysicalDevice physical_device
uint32_t uint32_t * format
#define FML_LOG(severity)
#define FML_UNREACHABLE()
#define FML_DCHECK(condition)
std::shared_ptr< ImpellerAllocator > allocator
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKBufferMemoryPropertyFlags(StorageMode mode)
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.
static constexpr vk::Flags< vk::MemoryPropertyFlagBits > ToVKTextureMemoryPropertyFlags(StorageMode mode, bool supports_memoryless_textures)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
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)
constexpr const char * TextureTypeToString(TextureType type)
constexpr const char * StorageModeToString(StorageMode mode)
constexpr vk::ImageCreateFlags ToVKImageCreateFlags(TextureType type)
constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format)
std::shared_ptr< ContextGLES > context
impeller::ShaderType type
FlutterVulkanImageHandle image
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
CompressionType compression_type
#define FML_TRACE_COUNTER(category_group, name, counter_id, arg1,...)