diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/allocator_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/allocator_vk.cc index ddbedc2df9..18ef93d8e6 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/allocator_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/allocator_vk.cc @@ -10,6 +10,7 @@ #include "flutter/fml/trace_event.h" #include "impeller/base/allocation_size.h" #include "impeller/core/formats.h" +#include "impeller/renderer/backend/vulkan/capabilities_vk.h" #include "impeller/renderer/backend/vulkan/device_buffer_vk.h" #include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" @@ -280,14 +281,16 @@ static VmaAllocationCreateFlags ToVmaAllocationCreateFlags(StorageMode mode) { class AllocatedTextureSourceVK final : public TextureSourceVK { public: - AllocatedTextureSourceVK(std::weak_ptr resource_manager, + AllocatedTextureSourceVK(const ContextVK& context, const TextureDescriptor& desc, VmaAllocator allocator, vk::Device device, bool supports_memoryless_textures) - : TextureSourceVK(desc), resource_(std::move(resource_manager)) { + : TextureSourceVK(desc), resource_(context.GetResourceManager()) { FML_DCHECK(desc.format != PixelFormat::kUnknown); - vk::ImageCreateInfo image_info; + vk::StructureChain + image_info_chain; + auto& image_info = image_info_chain.get(); image_info.flags = ToVKImageCreateFlags(desc.type); image_info.imageType = vk::ImageType::e2D; image_info.format = ToVKImageFormat(desc.format); @@ -306,6 +309,27 @@ class AllocatedTextureSourceVK final : public TextureSourceVK { supports_memoryless_textures); image_info.sharingMode = vk::SharingMode::eExclusive; + vk::ImageCompressionFixedRateFlagsEXT frc_rates[1] = { + vk::ImageCompressionFixedRateFlagBitsEXT::eNone}; + + const auto frc_rate = + CapabilitiesVK::Cast(*context.GetCapabilities()) + .GetSupportedFRCRate(desc.compression_type, + FRCFormatDescriptor{image_info}); + if (frc_rate.has_value()) { + // This array must not be in a temporary scope. + frc_rates[0] = frc_rate.value(); + + auto& compression_info = + image_info_chain.get(); + compression_info.pFixedRateFlags = frc_rates; + compression_info.compressionControlPlaneCount = 1u; + compression_info.flags = + vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit; + } else { + image_info_chain.unlink(); + } + VmaAllocationCreateInfo alloc_nfo = {}; alloc_nfo.usage = ToVMAMemoryUsage(); @@ -445,11 +469,11 @@ std::shared_ptr AllocatorVK::OnCreateTexture( return nullptr; } auto source = std::make_shared( - ContextVK::Cast(*context).GetResourceManager(), // - desc, // - allocator_.get(), // - device_holder->GetDevice(), // - supports_memoryless_textures_ // + ContextVK::Cast(*context), // + desc, // + allocator_.get(), // + device_holder->GetDevice(), // + supports_memoryless_textures_ // ); if (!source->IsValid()) { return nullptr; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc index 7b465b7040..7099982696 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/vulkan/capabilities_vk.h" #include +#include #include "impeller/base/validation.h" #include "impeller/core/formats.h" @@ -199,6 +200,8 @@ static const char* GetExtensionName(OptionalDeviceExtensionVK ext) { return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME; case OptionalDeviceExtensionVK::kVKKHRPortabilitySubset: return "VK_KHR_portability_subset"; + case OptionalDeviceExtensionVK::kEXTImageCompressionControl: + return VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME; case OptionalDeviceExtensionVK::kLast: return "Unknown"; } @@ -371,6 +374,17 @@ CapabilitiesVK::GetEnabledDeviceFeatures( } PhysicalDeviceFeatures supported_chain; + + // Swiftshader seems to be fussy about just this structure even being in the + // chain. Just unlink it if its not supported. We already perform an + // extensions check on the other side when reading. + if (!IsExtensionInList( + enabled_extensions.value(), + OptionalDeviceExtensionVK::kEXTImageCompressionControl)) { + supported_chain + .unlink(); + } + device.getFeatures2(&supported_chain.get()); PhysicalDeviceFeatures required_chain; @@ -398,6 +412,23 @@ CapabilitiesVK::GetEnabledDeviceFeatures( required.samplerYcbcrConversion = supported.samplerYcbcrConversion; } + // VK_EXT_image_compression_control + if (IsExtensionInList( + enabled_extensions.value(), + OptionalDeviceExtensionVK::kEXTImageCompressionControl)) { + auto& required = + required_chain + .get(); + const auto& supported = + supported_chain + .get(); + + required.imageCompressionControl = supported.imageCompressionControl; + } else { + required_chain + .unlink(); + } + // Vulkan 1.1 { auto& required = @@ -434,7 +465,9 @@ void CapabilitiesVK::SetOffscreenFormat(PixelFormat pixel_format) const { default_color_format_ = pixel_format; } -bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { +bool CapabilitiesVK::SetPhysicalDevice( + const vk::PhysicalDevice& device, + const PhysicalDeviceFeatures& enabled_features) { if (HasSuitableColorFormat(device, vk::Format::eR8G8B8A8Unorm)) { default_color_format_ = PixelFormat::kR8G8B8A8UNormInt; } else { @@ -456,6 +489,7 @@ bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { default_stencil_format_ = default_depth_stencil_format_; } + physical_device_ = device; device_properties_ = device.getProperties(); auto physical_properties_2 = @@ -518,6 +552,13 @@ bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) { }); } + supports_texture_fixed_rate_compression_ = + enabled_features + .isLinked() && + enabled_features + .get() + .imageCompressionControl; + return true; } @@ -611,4 +652,61 @@ bool CapabilitiesVK::HasExtension(OptionalDeviceExtensionVK ext) const { optional_device_extensions_.end(); } +bool CapabilitiesVK::SupportsTextureFixedRateCompression() const { + return supports_texture_fixed_rate_compression_; +} + +std::optional +CapabilitiesVK::GetSupportedFRCRate(CompressionType compression_type, + const FRCFormatDescriptor& desc) const { + if (compression_type != CompressionType::kLossy) { + return std::nullopt; + } + if (!supports_texture_fixed_rate_compression_) { + return std::nullopt; + } + // There are opportunities to hash and cache the FRCFormatDescriptor if + // needed. + vk::StructureChain + format_chain; + + auto& format_info = format_chain.get(); + + format_info.format = desc.format; + format_info.type = desc.type; + format_info.tiling = desc.tiling; + format_info.usage = desc.usage; + format_info.flags = desc.flags; + + const auto kIdealFRCRate = vk::ImageCompressionFixedRateFlagBitsEXT::e4Bpc; + + std::array rates = {kIdealFRCRate}; + + auto& compression = format_chain.get(); + compression.flags = vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit; + compression.compressionControlPlaneCount = rates.size(); + compression.pFixedRateFlags = rates.data(); + + const auto [result, supported] = physical_device_.getImageFormatProperties2< + vk::ImageFormatProperties2, vk::ImageCompressionPropertiesEXT>( + format_chain.get()); + + if (result != vk::Result::eSuccess || + !supported.isLinked()) { + return std::nullopt; + } + + const auto& compression_props = + supported.get(); + + if ((compression_props.imageCompressionFlags & + vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit) && + (compression_props.imageCompressionFixedRateFlags & kIdealFRCRate)) { + return kIdealFRCRate; + } + + return std::nullopt; +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h index 632a96a4be..1024f2f822 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -7,11 +7,13 @@ #include #include +#include #include #include #include #include "impeller/base/backend_cast.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/capabilities.h" @@ -131,9 +133,36 @@ enum class OptionalDeviceExtensionVK : uint32_t { /// kVKKHRPortabilitySubset, + //---------------------------------------------------------------------------- + /// For fixed-rate compression of images. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_image_compression_control.html + /// + kEXTImageCompressionControl, + kLast, }; +//------------------------------------------------------------------------------ +/// @brief A pixel format and usage that is sufficient to check if images +/// of that format and usage are suitable for use with fixed-rate +/// compression. +/// +struct FRCFormatDescriptor { + vk::Format format = vk::Format::eUndefined; + vk::ImageType type = {}; + vk::ImageTiling tiling = {}; + vk::ImageUsageFlags usage = {}; + vk::ImageCreateFlags flags = {}; + + explicit FRCFormatDescriptor(const vk::ImageCreateInfo& image_info) + : format(image_info.format), + type(image_info.imageType), + tiling(image_info.tiling), + usage(image_info.usage), + flags(image_info.flags) {} +}; + //------------------------------------------------------------------------------ /// @brief The Vulkan layers and extensions wrangler. /// @@ -165,13 +194,15 @@ class CapabilitiesVK final : public Capabilities, using PhysicalDeviceFeatures = vk::StructureChain; + vk::PhysicalDevice16BitStorageFeatures, + vk::PhysicalDeviceImageCompressionControlFeaturesEXT>; std::optional GetEnabledDeviceFeatures( const vk::PhysicalDevice& physical_device) const; [[nodiscard]] bool SetPhysicalDevice( - const vk::PhysicalDevice& physical_device); + const vk::PhysicalDevice& physical_device, + const PhysicalDeviceFeatures& enabled_features); const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const; @@ -219,6 +250,25 @@ class CapabilitiesVK final : public Capabilities, // |Capabilities| PixelFormat GetDefaultGlyphAtlasFormat() const override; + //---------------------------------------------------------------------------- + /// @return If fixed-rate compression for non-onscreen surfaces is + /// supported. + /// + bool SupportsTextureFixedRateCompression() const; + + //---------------------------------------------------------------------------- + /// @brief Get the fixed compression rate supported by the context for + /// the given format and usage. + /// + /// @param[in] compression_type The compression type. + /// @param[in] desc The format and usage of the image. + /// + /// @return The supported fixed compression rate. + /// + std::optional GetSupportedFRCRate( + CompressionType compression_type, + const FRCFormatDescriptor& desc) const; + private: bool validations_enabled_ = false; std::map> exts_; @@ -229,9 +279,11 @@ class CapabilitiesVK final : public Capabilities, mutable PixelFormat default_color_format_ = PixelFormat::kUnknown; PixelFormat default_stencil_format_ = PixelFormat::kUnknown; PixelFormat default_depth_stencil_format_ = PixelFormat::kUnknown; + vk::PhysicalDevice physical_device_; vk::PhysicalDeviceProperties device_properties_; bool supports_compute_subgroups_ = false; bool supports_device_transient_textures_ = false; + bool supports_texture_fixed_rate_compression_ = false; bool is_valid_ = false; bool HasExtension(const std::string& ext) const; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index b9c9c02e26..d73975acaf 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -338,7 +338,8 @@ void ContextVK::Setup(Settings settings) { device_holder->device = std::move(device_result.value); } - if (!caps->SetPhysicalDevice(device_holder->physical_device)) { + if (!caps->SetPhysicalDevice(device_holder->physical_device, + *enabled_features)) { VALIDATION_LOG << "Capabilities could not be updated."; return; }