blob: b25d0358b877c6ae7a61cdb6151b8d4582c38a7b [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/renderer/backend/vulkan/capabilities_vk.h"
#include <algorithm>
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/renderer/backend/vulkan/vk.h"
namespace impeller {
static constexpr const char* kInstanceLayer = "ImpellerInstance";
CapabilitiesVK::CapabilitiesVK(bool enable_validations,
bool fatal_missing_validations) {
auto extensions = vk::enumerateInstanceExtensionProperties();
auto layers = vk::enumerateInstanceLayerProperties();
if (extensions.result != vk::Result::eSuccess ||
layers.result != vk::Result::eSuccess) {
return;
}
for (const auto& ext : extensions.value) {
exts_[kInstanceLayer].insert(ext.extensionName);
}
for (const auto& layer : layers.value) {
const std::string layer_name = layer.layerName;
auto layer_exts = vk::enumerateInstanceExtensionProperties(layer_name);
if (layer_exts.result != vk::Result::eSuccess) {
return;
}
for (const auto& layer_ext : layer_exts.value) {
exts_[layer_name].insert(layer_ext.extensionName);
}
}
validations_enabled_ =
enable_validations && HasLayer("VK_LAYER_KHRONOS_validation");
if (enable_validations && !validations_enabled_) {
FML_LOG(ERROR)
<< "Requested Impeller context creation with validations but the "
"validation layers could not be found. Expect no Vulkan validation "
"checks!";
if (fatal_missing_validations) {
FML_LOG(FATAL) << "Validation missing. Exiting.";
}
}
if (validations_enabled_) {
FML_LOG(INFO) << "Vulkan validations are enabled.";
}
is_valid_ = true;
}
CapabilitiesVK::~CapabilitiesVK() = default;
bool CapabilitiesVK::IsValid() const {
return is_valid_;
}
bool CapabilitiesVK::AreValidationsEnabled() const {
return validations_enabled_;
}
std::optional<std::vector<std::string>> CapabilitiesVK::GetEnabledLayers()
const {
std::vector<std::string> required;
if (validations_enabled_) {
// The presence of this layer is already checked in the ctor.
required.push_back("VK_LAYER_KHRONOS_validation");
}
return required;
}
std::optional<std::vector<std::string>>
CapabilitiesVK::GetEnabledInstanceExtensions() const {
std::vector<std::string> required;
if (!HasExtension("VK_KHR_surface")) {
// Swapchain support is required and this is a dependency of
// VK_KHR_swapchain.
VALIDATION_LOG << "Could not find the surface extension.";
return std::nullopt;
}
required.push_back("VK_KHR_surface");
auto has_wsi = false;
if (HasExtension("VK_MVK_macos_surface")) {
required.push_back("VK_MVK_macos_surface");
has_wsi = true;
}
if (HasExtension("VK_EXT_metal_surface")) {
required.push_back("VK_EXT_metal_surface");
has_wsi = true;
}
if (HasExtension("VK_KHR_portability_enumeration")) {
required.push_back("VK_KHR_portability_enumeration");
has_wsi = true;
}
if (HasExtension("VK_KHR_win32_surface")) {
required.push_back("VK_KHR_win32_surface");
has_wsi = true;
}
if (HasExtension("VK_KHR_android_surface")) {
required.push_back("VK_KHR_android_surface");
has_wsi = true;
}
if (HasExtension("VK_KHR_xcb_surface")) {
required.push_back("VK_KHR_xcb_surface");
has_wsi = true;
}
if (HasExtension("VK_KHR_xlib_surface")) {
required.push_back("VK_KHR_xlib_surface");
has_wsi = true;
}
if (HasExtension("VK_KHR_wayland_surface")) {
required.push_back("VK_KHR_wayland_surface");
has_wsi = true;
}
if (!has_wsi) {
// Don't really care which WSI extension there is as long there is at least
// one.
VALIDATION_LOG << "Could not find a WSI extension.";
return std::nullopt;
}
if (validations_enabled_) {
if (!HasExtension("VK_EXT_debug_utils")) {
VALIDATION_LOG << "Requested validations but could not find the "
"VK_EXT_debug_utils extension.";
return std::nullopt;
}
required.push_back("VK_EXT_debug_utils");
if (HasExtension("VK_EXT_validation_features")) {
// It's valid to not have `VK_EXT_validation_features` available. That's
// the case when using AGI as a frame debugger.
required.push_back("VK_EXT_validation_features");
}
}
return required;
}
static const char* GetExtensionName(RequiredCommonDeviceExtensionVK ext) {
switch (ext) {
case RequiredCommonDeviceExtensionVK::kKHRSwapchain:
return VK_KHR_SWAPCHAIN_EXTENSION_NAME;
case RequiredCommonDeviceExtensionVK::kLast:
return "Unknown";
}
FML_UNREACHABLE();
}
static const char* GetExtensionName(RequiredAndroidDeviceExtensionVK ext) {
switch (ext) {
case RequiredAndroidDeviceExtensionVK::
kANDROIDExternalMemoryAndroidHardwareBuffer:
return "VK_ANDROID_external_memory_android_hardware_buffer";
case RequiredAndroidDeviceExtensionVK::kKHRSamplerYcbcrConversion:
return VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME;
case RequiredAndroidDeviceExtensionVK::kKHRExternalMemory:
return VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME;
case RequiredAndroidDeviceExtensionVK::kEXTQueueFamilyForeign:
return VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME;
case RequiredAndroidDeviceExtensionVK::kKHRDedicatedAllocation:
return VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME;
case RequiredAndroidDeviceExtensionVK::kLast:
return "Unknown";
}
FML_UNREACHABLE();
}
static const char* GetExtensionName(OptionalDeviceExtensionVK ext) {
switch (ext) {
case OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback:
return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME;
case OptionalDeviceExtensionVK::kVKKHRPortabilitySubset:
return "VK_KHR_portability_subset";
case OptionalDeviceExtensionVK::kLast:
return "Unknown";
}
FML_UNREACHABLE();
}
template <class T>
static bool IterateExtensions(const std::function<bool(T)>& it) {
if (!it) {
return false;
}
for (size_t i = 0; i < static_cast<uint32_t>(T::kLast); i++) {
if (!it(static_cast<T>(i))) {
return false;
}
}
return true;
}
static std::optional<std::set<std::string>> GetSupportedDeviceExtensions(
const vk::PhysicalDevice& physical_device) {
auto device_extensions = physical_device.enumerateDeviceExtensionProperties();
if (device_extensions.result != vk::Result::eSuccess) {
return std::nullopt;
}
std::set<std::string> exts;
for (const auto& device_extension : device_extensions.value) {
exts.insert(device_extension.extensionName);
};
return exts;
}
std::optional<std::vector<std::string>>
CapabilitiesVK::GetEnabledDeviceExtensions(
const vk::PhysicalDevice& physical_device) const {
auto exts = GetSupportedDeviceExtensions(physical_device);
if (!exts.has_value()) {
return std::nullopt;
}
std::vector<std::string> enabled;
auto for_each_common_extension = [&](RequiredCommonDeviceExtensionVK ext) {
auto name = GetExtensionName(ext);
if (exts->find(name) == exts->end()) {
VALIDATION_LOG << "Device does not support required extension: " << name;
return false;
}
enabled.push_back(name);
return true;
};
auto for_each_android_extension = [&](RequiredAndroidDeviceExtensionVK ext) {
#ifdef FML_OS_ANDROID
auto name = GetExtensionName(ext);
if (exts->find(name) == exts->end()) {
VALIDATION_LOG << "Device does not support required Android extension: "
<< name;
return false;
}
enabled.push_back(name);
#endif // FML_OS_ANDROID
return true;
};
auto for_each_optional_extension = [&](OptionalDeviceExtensionVK ext) {
auto name = GetExtensionName(ext);
if (exts->find(name) != exts->end()) {
enabled.push_back(name);
}
return true;
};
const auto iterate_extensions =
IterateExtensions<RequiredCommonDeviceExtensionVK>(
for_each_common_extension) &&
IterateExtensions<RequiredAndroidDeviceExtensionVK>(
for_each_android_extension) &&
IterateExtensions<OptionalDeviceExtensionVK>(for_each_optional_extension);
if (!iterate_extensions) {
VALIDATION_LOG << "Device not suitable since required extensions are not "
"supported.";
return std::nullopt;
}
return enabled;
}
static bool HasSuitableColorFormat(const vk::PhysicalDevice& device,
vk::Format format) {
const auto props = device.getFormatProperties(format);
// This needs to be more comprehensive.
return !!(props.optimalTilingFeatures &
vk::FormatFeatureFlagBits::eColorAttachment);
}
static bool HasSuitableDepthStencilFormat(const vk::PhysicalDevice& device,
vk::Format format) {
const auto props = device.getFormatProperties(format);
return !!(props.optimalTilingFeatures &
vk::FormatFeatureFlagBits::eDepthStencilAttachment);
}
static bool PhysicalDeviceSupportsRequiredFormats(
const vk::PhysicalDevice& device) {
const auto has_color_format =
HasSuitableColorFormat(device, vk::Format::eR8G8B8A8Unorm);
const auto has_stencil_format =
HasSuitableDepthStencilFormat(device, vk::Format::eD32SfloatS8Uint) ||
HasSuitableDepthStencilFormat(device, vk::Format::eD24UnormS8Uint);
return has_color_format && has_stencil_format;
}
static bool HasRequiredProperties(const vk::PhysicalDevice& physical_device) {
auto properties = physical_device.getProperties();
if (!(properties.limits.framebufferColorSampleCounts &
(vk::SampleCountFlagBits::e1 | vk::SampleCountFlagBits::e4))) {
return false;
}
return true;
}
static bool HasRequiredQueues(const vk::PhysicalDevice& physical_device) {
auto queue_flags = vk::QueueFlags{};
for (const auto& queue : physical_device.getQueueFamilyProperties()) {
if (queue.queueCount == 0) {
continue;
}
queue_flags |= queue.queueFlags;
}
return static_cast<VkQueueFlags>(queue_flags &
(vk::QueueFlagBits::eGraphics |
vk::QueueFlagBits::eCompute |
vk::QueueFlagBits::eTransfer));
}
template <class ExtensionEnum>
static bool IsExtensionInList(const std::vector<std::string>& list,
ExtensionEnum ext) {
const std::string name = GetExtensionName(ext);
return std::find(list.begin(), list.end(), name) != list.end();
}
std::optional<CapabilitiesVK::PhysicalDeviceFeatures>
CapabilitiesVK::GetEnabledDeviceFeatures(
const vk::PhysicalDevice& device) const {
if (!PhysicalDeviceSupportsRequiredFormats(device)) {
VALIDATION_LOG << "Device doesn't support the required formats.";
return std::nullopt;
}
if (!HasRequiredProperties(device)) {
VALIDATION_LOG << "Device doesn't support the required properties.";
return std::nullopt;
}
if (!HasRequiredQueues(device)) {
VALIDATION_LOG << "Device doesn't support the required queues.";
return std::nullopt;
}
const auto enabled_extensions = GetEnabledDeviceExtensions(device);
if (!enabled_extensions.has_value()) {
VALIDATION_LOG << "Device doesn't support the required queues.";
return std::nullopt;
}
PhysicalDeviceFeatures supported_chain;
device.getFeatures2(&supported_chain.get());
PhysicalDeviceFeatures required_chain;
// Base features.
{
auto& required = required_chain.get().features;
const auto& supported = supported_chain.get().features;
// We require this for enabling wireframes in the playground. But its not
// necessarily a big deal if we don't have this feature.
required.fillModeNonSolid = supported.fillModeNonSolid;
}
// VK_KHR_sampler_ycbcr_conversion features.
if (IsExtensionInList(
enabled_extensions.value(),
RequiredAndroidDeviceExtensionVK::kKHRSamplerYcbcrConversion)) {
auto& required =
required_chain
.get<vk::PhysicalDeviceSamplerYcbcrConversionFeaturesKHR>();
const auto& supported =
supported_chain
.get<vk::PhysicalDeviceSamplerYcbcrConversionFeaturesKHR>();
required.samplerYcbcrConversion = supported.samplerYcbcrConversion;
}
return required_chain;
}
bool CapabilitiesVK::HasLayer(const std::string& layer) const {
for (const auto& [found_layer, exts] : exts_) {
if (found_layer == layer) {
return true;
}
}
return false;
}
bool CapabilitiesVK::HasExtension(const std::string& ext) const {
for (const auto& [layer, exts] : exts_) {
if (exts.find(ext) != exts.end()) {
return true;
}
}
return false;
}
void CapabilitiesVK::SetOffscreenFormat(PixelFormat pixel_format) const {
default_color_format_ = pixel_format;
}
bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) {
if (HasSuitableColorFormat(device, vk::Format::eR8G8B8A8Unorm)) {
default_color_format_ = PixelFormat::kR8G8B8A8UNormInt;
} else {
default_color_format_ = PixelFormat::kUnknown;
}
if (HasSuitableDepthStencilFormat(device, vk::Format::eD32SfloatS8Uint)) {
default_depth_stencil_format_ = PixelFormat::kD32FloatS8UInt;
} else if (HasSuitableDepthStencilFormat(device,
vk::Format::eD24UnormS8Uint)) {
default_depth_stencil_format_ = PixelFormat::kD24UnormS8Uint;
} else {
default_depth_stencil_format_ = PixelFormat::kUnknown;
}
if (HasSuitableDepthStencilFormat(device, vk::Format::eS8Uint)) {
default_stencil_format_ = PixelFormat::kS8UInt;
} else if (default_depth_stencil_format_ != PixelFormat::kUnknown) {
default_stencil_format_ = default_depth_stencil_format_;
}
device_properties_ = device.getProperties();
auto physical_properties_2 =
device.getProperties2<vk::PhysicalDeviceProperties2,
vk::PhysicalDeviceSubgroupProperties>();
// Currently shaders only want access to arithmetic subgroup features.
// If that changes this needs to get updated, and so does Metal (which right
// now assumes it from compile time flags based on the MSL target version).
supports_compute_subgroups_ =
!!(physical_properties_2.get<vk::PhysicalDeviceSubgroupProperties>()
.supportedOperations &
vk::SubgroupFeatureFlagBits::eArithmetic);
{
// Query texture support.
// TODO(jonahwilliams):
// https://github.com/flutter/flutter/issues/129784
vk::PhysicalDeviceMemoryProperties memory_properties;
device.getMemoryProperties(&memory_properties);
for (auto i = 0u; i < memory_properties.memoryTypeCount; i++) {
if (memory_properties.memoryTypes[i].propertyFlags &
vk::MemoryPropertyFlagBits::eLazilyAllocated) {
supports_device_transient_textures_ = true;
}
}
}
// Determine the optional device extensions this physical device supports.
{
required_common_device_extensions_.clear();
required_android_device_extensions_.clear();
optional_device_extensions_.clear();
auto exts = GetSupportedDeviceExtensions(device);
if (!exts.has_value()) {
return false;
}
IterateExtensions<RequiredCommonDeviceExtensionVK>([&](auto ext) -> bool {
auto ext_name = GetExtensionName(ext);
if (exts->find(ext_name) != exts->end()) {
required_common_device_extensions_.insert(ext);
}
return true;
});
IterateExtensions<RequiredAndroidDeviceExtensionVK>([&](auto ext) -> bool {
auto ext_name = GetExtensionName(ext);
if (exts->find(ext_name) != exts->end()) {
required_android_device_extensions_.insert(ext);
}
return true;
});
IterateExtensions<OptionalDeviceExtensionVK>([&](auto ext) -> bool {
auto ext_name = GetExtensionName(ext);
if (exts->find(ext_name) != exts->end()) {
optional_device_extensions_.insert(ext);
}
return true;
});
}
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsOffscreenMSAA() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsImplicitResolvingMSAA() const {
return false;
}
// |Capabilities|
bool CapabilitiesVK::SupportsSSBO() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsBufferToTextureBlits() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsTextureToTextureBlits() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsFramebufferFetch() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsCompute() const {
// Vulkan 1.1 requires support for compute.
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsComputeSubgroups() const {
// Set by |SetPhysicalDevice|.
return supports_compute_subgroups_;
}
// |Capabilities|
bool CapabilitiesVK::SupportsReadFromResolve() const {
return false;
}
bool CapabilitiesVK::SupportsDecalSamplerAddressMode() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsDeviceTransientTextures() const {
return supports_device_transient_textures_;
}
// |Capabilities|
PixelFormat CapabilitiesVK::GetDefaultColorFormat() const {
return default_color_format_;
}
// |Capabilities|
PixelFormat CapabilitiesVK::GetDefaultStencilFormat() const {
return default_stencil_format_;
}
// |Capabilities|
PixelFormat CapabilitiesVK::GetDefaultDepthStencilFormat() const {
return default_depth_stencil_format_;
}
const vk::PhysicalDeviceProperties&
CapabilitiesVK::GetPhysicalDeviceProperties() const {
return device_properties_;
}
PixelFormat CapabilitiesVK::GetDefaultGlyphAtlasFormat() const {
return PixelFormat::kR8UNormInt;
}
bool CapabilitiesVK::HasExtension(RequiredCommonDeviceExtensionVK ext) const {
return required_common_device_extensions_.find(ext) !=
required_common_device_extensions_.end();
}
bool CapabilitiesVK::HasExtension(RequiredAndroidDeviceExtensionVK ext) const {
return required_android_device_extensions_.find(ext) !=
required_android_device_extensions_.end();
}
bool CapabilitiesVK::HasExtension(OptionalDeviceExtensionVK ext) const {
return optional_device_extensions_.find(ext) !=
optional_device_extensions_.end();
}
} // namespace impeller