[Impeller] Enable fixed-rate compression support in Vulkan. (flutter/engine#53292)

Fixes https://github.com/flutter/flutter/issues/129501
This commit is contained in:
Chinmay Garde 2024-07-11 14:41:28 -07:00 committed by GitHub
parent 0fa80539c5
commit 412e42ae83
4 changed files with 187 additions and 12 deletions

View File

@ -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<ResourceManagerVK> 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<vk::ImageCreateInfo, vk::ImageCompressionControlEXT>
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<vk::ImageCompressionControlEXT>();
compression_info.pFixedRateFlags = frc_rates;
compression_info.compressionControlPlaneCount = 1u;
compression_info.flags =
vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit;
} else {
image_info_chain.unlink<vk::ImageCompressionControlEXT>();
}
VmaAllocationCreateInfo alloc_nfo = {};
alloc_nfo.usage = ToVMAMemoryUsage();
@ -445,11 +469,11 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
return nullptr;
}
auto source = std::make_shared<AllocatedTextureSourceVK>(
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;

View File

@ -5,6 +5,7 @@
#include "impeller/renderer/backend/vulkan/capabilities_vk.h"
#include <algorithm>
#include <array>
#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<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>();
}
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<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>();
const auto& supported =
supported_chain
.get<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>();
required.imageCompressionControl = supported.imageCompressionControl;
} else {
required_chain
.unlink<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>();
}
// 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<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>() &&
enabled_features
.get<vk::PhysicalDeviceImageCompressionControlFeaturesEXT>()
.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<vk::ImageCompressionFixedRateFlagBitsEXT>
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<vk::PhysicalDeviceImageFormatInfo2,
vk::ImageCompressionControlEXT>
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<vk::ImageCompressionFixedRateFlagsEXT, 1u> rates = {kIdealFRCRate};
auto& compression = format_chain.get<vk::ImageCompressionControlEXT>();
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<vk::ImageCompressionPropertiesEXT>()) {
return std::nullopt;
}
const auto& compression_props =
supported.get<vk::ImageCompressionPropertiesEXT>();
if ((compression_props.imageCompressionFlags &
vk::ImageCompressionFlagBitsEXT::eFixedRateExplicit) &&
(compression_props.imageCompressionFixedRateFlags & kIdealFRCRate)) {
return kIdealFRCRate;
}
return std::nullopt;
}
} // namespace impeller

View File

@ -7,11 +7,13 @@
#include <cstdint>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>
#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::PhysicalDeviceFeatures2,
vk::PhysicalDeviceSamplerYcbcrConversionFeaturesKHR,
vk::PhysicalDevice16BitStorageFeatures>;
vk::PhysicalDevice16BitStorageFeatures,
vk::PhysicalDeviceImageCompressionControlFeaturesEXT>;
std::optional<PhysicalDeviceFeatures> 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<vk::ImageCompressionFixedRateFlagBitsEXT> GetSupportedFRCRate(
CompressionType compression_type,
const FRCFormatDescriptor& desc) const;
private:
bool validations_enabled_ = false;
std::map<std::string, std::set<std::string>> 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;

View File

@ -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;
}