From 66213df612a09c5e272f24af640e73ad8769a574 Mon Sep 17 00:00:00 2001 From: Tobias Frisch <tfrisch@uni-koblenz.de> Date: Sun, 1 Aug 2021 19:53:30 +0200 Subject: [PATCH] [#101] Added feature manager to check active features and extensions Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de> --- config/Sources.cmake | 3 + include/vkcv/Context.hpp | 7 +- include/vkcv/FeatureManager.hpp | 133 +++ .../src/vkcv/upscaling/FSRUpscaling.cpp | 4 +- projects/mesh_shader/.gitignore | 2 +- src/vkcv/Context.cpp | 105 +- src/vkcv/FeatureManager.cpp | 1016 +++++++++++++++++ 7 files changed, 1205 insertions(+), 65 deletions(-) create mode 100644 include/vkcv/FeatureManager.hpp create mode 100644 src/vkcv/FeatureManager.cpp diff --git a/config/Sources.cmake b/config/Sources.cmake index 41cd0c20..bdcbc1c6 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -1,6 +1,9 @@ # adding all source files and header files of the framework: set(vkcv_sources + ${vkcv_include}/vkcv/FeatureManager.hpp + ${vkcv_source}/vkcv/FeatureManager.cpp + ${vkcv_include}/vkcv/Context.hpp ${vkcv_source}/vkcv/Context.cpp diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp index 824713fd..76d60525 100644 --- a/include/vkcv/Context.hpp +++ b/include/vkcv/Context.hpp @@ -5,6 +5,7 @@ #include "QueueManager.hpp" #include "DrawcallRecording.hpp" +#include "FeatureManager.hpp" namespace vkcv { @@ -32,6 +33,9 @@ namespace vkcv [[nodiscard]] const vk::Device &getDevice() const; + [[nodiscard]] + const FeatureManager& getFeatureManager() const; + [[nodiscard]] const QueueManager& getQueueManager() const; @@ -53,11 +57,12 @@ namespace vkcv * @param device Vulkan-Device */ Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, - QueueManager&& queueManager, vma::Allocator&& allocator) noexcept; + FeatureManager&& featureManager, QueueManager&& queueManager, vma::Allocator&& allocator) noexcept; vk::Instance m_Instance; vk::PhysicalDevice m_PhysicalDevice; vk::Device m_Device; + FeatureManager m_FeatureManager; QueueManager m_QueueManager; vma::Allocator m_Allocator; diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp new file mode 100644 index 00000000..742fd259 --- /dev/null +++ b/include/vkcv/FeatureManager.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include "Logger.hpp" + +#include <functional> +#include <unordered_set> +#include <vector> +#include <vulkan/vulkan.hpp> + +namespace vkcv { + + class FeatureManager { + private: + vk::PhysicalDevice& m_physicalDevice; + + std::vector<const char*> m_supportedExtensions; + std::vector<const char*> m_activeExtensions; + + vk::PhysicalDeviceFeatures2 m_featuresBase; + std::vector<vk::BaseOutStructure*> m_featuresExtensions; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDevice16BitStorageFeatures& features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDevice8BitStorageFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceBufferDeviceAddressFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceDescriptorIndexingFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceHostQueryResetFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceImagelessFramebufferFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceMultiviewFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceProtectedMemoryFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceSamplerYcbcrConversionFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceScalarBlockLayoutFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceShaderAtomicInt64Features &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceShaderFloat16Int8Features& features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceTimelineSemaphoreFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceUniformBufferStandardLayoutFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceVariablePointersFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceVulkanMemoryModelFeatures &features, bool required) const; + + [[nodiscard]] + bool checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV& features, bool required) const; + + public: + explicit FeatureManager(vk::PhysicalDevice& physicalDevice); + + FeatureManager(const FeatureManager& other) = delete; + FeatureManager(FeatureManager&& other) noexcept; + + ~FeatureManager(); + + FeatureManager& operator=(const FeatureManager& other) = delete; + FeatureManager& operator=(FeatureManager&& other) noexcept; + + [[nodiscard]] + bool isExtensionSupported(const char *extension) const; + + bool useExtension(const char *extension, bool required = true); + + [[nodiscard]] + bool isExtensionActive(const char *extension) const; + + [[nodiscard]] + const std::vector<const char*>& getActiveExtensions() const; + + bool useFeatures(const std::function<void(vk::PhysicalDeviceFeatures&)>& featureFunction, bool required = true); + + template<typename T> + bool useFeatures(const std::function<void(T&)>& featureFunction, bool required = true) { + T* features = new T(); + featureFunction(*features); + + if (!checkSupport(*features, required)) { + delete features; + return false; + } + + if (m_featuresExtensions.empty()) { + m_featuresBase.setPNext(features); + } else { + m_featuresExtensions.back()->setPNext( + reinterpret_cast<vk::BaseOutStructure*>(features) + ); + } + + m_featuresExtensions.push_back( + reinterpret_cast<vk::BaseOutStructure*>(features) + ); + + return true; + } + + [[nodiscard]] + const vk::PhysicalDeviceFeatures2& getFeatures() const; + + }; + +} diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp index 460a6d0b..85908a76 100644 --- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp +++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp @@ -177,7 +177,7 @@ namespace vkcv::upscaling { vkcv::shader::GLSLCompiler easuCompiler; vkcv::shader::GLSLCompiler rcasCompiler; - const auto& features = m_core.getContext().getPhysicalDevice().getFeatures2(); + const auto& features = m_core.getContext().getFeatureManager().getFeatures(); const bool float16Support = ( checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>( reinterpret_cast<const vk::BaseInStructure*>(&features), @@ -189,7 +189,7 @@ namespace vkcv::upscaling { vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR, check16Storage ) - ) || (true); // check doesn't work because chain is empty + ); if (!float16Support) { easuCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1"); diff --git a/projects/mesh_shader/.gitignore b/projects/mesh_shader/.gitignore index 7e24fd7b..fd009a62 100644 --- a/projects/mesh_shader/.gitignore +++ b/projects/mesh_shader/.gitignore @@ -1 +1 @@ -first_triangle \ No newline at end of file +mesh_shader \ No newline at end of file diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp index 2e30fb96..c83ec0b7 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -9,8 +9,9 @@ namespace vkcv m_Instance(other.m_Instance), m_PhysicalDevice(other.m_PhysicalDevice), m_Device(other.m_Device), - m_QueueManager(other.m_QueueManager), - m_Allocator(other.m_Allocator) + m_FeatureManager(std::move(other.m_FeatureManager)), + m_QueueManager(std::move(other.m_QueueManager)), + m_Allocator(other.m_Allocator) { other.m_Instance = nullptr; other.m_PhysicalDevice = nullptr; @@ -23,7 +24,8 @@ namespace vkcv m_Instance = other.m_Instance; m_PhysicalDevice = other.m_PhysicalDevice; m_Device = other.m_Device; - m_QueueManager = other.m_QueueManager; + m_FeatureManager = std::move(other.m_FeatureManager); + m_QueueManager = std::move(other.m_QueueManager); m_Allocator = other.m_Allocator; other.m_Instance = nullptr; @@ -37,12 +39,14 @@ namespace vkcv Context::Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, + FeatureManager&& featureManager, QueueManager&& queueManager, vma::Allocator&& allocator) noexcept : m_Instance(instance), m_PhysicalDevice(physicalDevice), m_Device(device), - m_QueueManager(queueManager), + m_FeatureManager(std::move(featureManager)), + m_QueueManager(std::move(queueManager)), m_Allocator(allocator) {} @@ -68,6 +72,10 @@ namespace vkcv return m_Device; } + const FeatureManager& Context::getFeatureManager() const { + return m_FeatureManager; + } + const QueueManager& Context::getQueueManager() const { return m_QueueManager; } @@ -167,7 +175,6 @@ namespace vkcv return true; } - std::vector<const char*> getRequiredExtensions() { uint32_t glfwExtensionCount = 0; const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); @@ -180,15 +187,6 @@ namespace vkcv return extensions; } - bool isPresentInCharPtrVector(const std::vector<const char*>& v, const char* term){ - for (const auto& entry : v) { - if (strcmp(entry, term) != 0) { - return true; - } - } - return false; - } - Context Context::create(const char *applicationName, uint32_t applicationVersion, const std::vector<vk::QueueFlagBits>& queueFlags, @@ -226,14 +224,14 @@ namespace vkcv supportedExtensions.push_back(elem.extensionName); } - if (!checkSupport(supportedExtensions, instanceExtensions)) { - throw std::runtime_error("The requested instance extensions are not supported!"); - } - // for GLFW: get all required extensions std::vector<const char*> requiredExtensions = getRequiredExtensions(); requiredExtensions.insert(requiredExtensions.end(), instanceExtensions.begin(), instanceExtensions.end()); + if (!checkSupport(supportedExtensions, requiredExtensions)) { + throw std::runtime_error("The requested instance extensions are not supported!"); + } + const vk::ApplicationInfo applicationInfo( applicationName, applicationVersion, @@ -261,15 +259,7 @@ namespace vkcv std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices(); vk::PhysicalDevice physicalDevice = pickPhysicalDevice(instance); - // check for physical device extension support - std::vector<vk::ExtensionProperties> deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties(); - supportedExtensions.clear(); - for (auto& elem : deviceExtensionProperties) { - supportedExtensions.push_back(elem.extensionName); - } - if (!checkSupport(supportedExtensions, deviceExtensions)) { - throw std::runtime_error("The requested device extensions are not supported by the physical device!"); - } + FeatureManager featureManager (physicalDevice); std::vector<vk::DeviceQueueCreateInfo> qCreateInfos; @@ -295,49 +285,41 @@ namespace vkcv deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); #endif - const bool shaderFloat16 = checkSupport(deviceExtensions, { "VK_KHR_shader_float16_int8" }); - const bool storage16bit = checkSupport(deviceExtensions, { "VK_KHR_16bit_storage" }); - - // FIXME: check if device feature is supported - vk::PhysicalDeviceShaderFloat16Int8Features deviceShaderFloat16Int8Features; - deviceShaderFloat16Int8Features.shaderFloat16 = shaderFloat16; - - vk::PhysicalDevice16BitStorageFeatures device16BitStorageFeatures; - device16BitStorageFeatures.storageBuffer16BitAccess = storage16bit; - - vk::PhysicalDeviceFeatures2 deviceFeatures2; - deviceFeatures2.features.fragmentStoresAndAtomics = true; - deviceFeatures2.features.geometryShader = true; - deviceFeatures2.features.depthClamp = true; - deviceFeatures2.features.shaderInt16 = true; - - const bool usingMeshShaders = isPresentInCharPtrVector(deviceExtensions, VK_NV_MESH_SHADER_EXTENSION_NAME); - vk::PhysicalDeviceMeshShaderFeaturesNV meshShadingFeatures; - if (usingMeshShaders) { - meshShadingFeatures.taskShader = true; - meshShadingFeatures.meshShader = true; - deviceFeatures2.setPNext(&meshShadingFeatures); + + if (featureManager.useExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDeviceShaderFloat16Int8Features>( + [](vk::PhysicalDeviceShaderFloat16Int8Features& features) { + features.setShaderFloat16(true); + }, false); } - if (shaderFloat16) { - deviceFeatures2.setPNext(&deviceShaderFloat16Int8Features); + if (featureManager.useExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDevice16BitStorageFeatures>( + [](vk::PhysicalDevice16BitStorageFeatures& features) { + features.setStorageBuffer16BitAccess(true); + }, false); } - if (storage16bit) { - deviceShaderFloat16Int8Features.setPNext(&device16BitStorageFeatures); + featureManager.useFeatures([](vk::PhysicalDeviceFeatures& features) { + features.setFragmentStoresAndAtomics(true); + features.setGeometryShader(true); + features.setDepthClamp(true); + features.setShaderInt16(true); + }); + + if (featureManager.useExtension(VK_NV_MESH_SHADER_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDeviceMeshShaderFeaturesNV>( + [](vk::PhysicalDeviceMeshShaderFeaturesNV& features) { + features.setTaskShader(true); + features.setMeshShader(true); + }, false); } - deviceCreateInfo.setPNext(&deviceFeatures2); - - // Ablauf - // qCreateInfos erstellen --> braucht das Device - // device erstellen - // jetzt koennen wir mit dem device die queues erstellen + deviceCreateInfo.setPNext(&(featureManager.getFeatures())); vk::Device device = physicalDevice.createDevice(deviceCreateInfo); - if (usingMeshShaders) - { + if (featureManager.isExtensionActive(VK_NV_MESH_SHADER_EXTENSION_NAME)) { InitMeshShaderDrawFunctions(device); } @@ -376,6 +358,7 @@ namespace vkcv instance, physicalDevice, device, + std::move(featureManager), std::move(queueManager), std::move(allocator) ); diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp new file mode 100644 index 00000000..4a96d85a --- /dev/null +++ b/src/vkcv/FeatureManager.cpp @@ -0,0 +1,1016 @@ + +#include "vkcv/FeatureManager.hpp" + +#include <string.h> + +namespace vkcv { + + bool FeatureManager::checkSupport(const vk::PhysicalDevice16BitStorageFeatures &features, bool required) const { + vk::PhysicalDevice16BitStorageFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.storageBuffer16BitAccess) && (!supported.storageBuffer16BitAccess)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'storageBuffer16BitAccess' is not supported"); + return false; + } + + if ((features.storageInputOutput16) && (!supported.storageInputOutput16)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'storageInputOutput16' is not supported"); + return false; + } + + if ((features.storagePushConstant16) && (!supported.storagePushConstant16)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'storagePushConstant16' is not supported"); + return false; + } + + if ((features.uniformAndStorageBuffer16BitAccess) && (!supported.uniformAndStorageBuffer16BitAccess)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'uniformAndStorageBuffer16BitAccess' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDevice8BitStorageFeatures &features, bool required) const { + vk::PhysicalDevice8BitStorageFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.storageBuffer8BitAccess) && (!supported.storageBuffer8BitAccess)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'storageBuffer8BitAccess' is not supported"); + return false; + } + + if ((features.storagePushConstant8) && (!supported.storagePushConstant8)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'storagePushConstant8' is not supported"); + return false; + } + + if ((features.uniformAndStorageBuffer8BitAccess) && (!supported.uniformAndStorageBuffer8BitAccess)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'uniformAndStorageBuffer8BitAccess' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceBufferDeviceAddressFeatures &features, + bool required) const { + vk::PhysicalDeviceBufferDeviceAddressFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.bufferDeviceAddress) && (!supported.bufferDeviceAddress)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'bufferDeviceAddress' is not supported"); + return false; + } + + if ((features.bufferDeviceAddressCaptureReplay) && (!supported.bufferDeviceAddressCaptureReplay)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'bufferDeviceAddressCaptureReplay' is not supported"); + return false; + } + + if ((features.bufferDeviceAddressMultiDevice) && (!supported.bufferDeviceAddressMultiDevice)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'bufferDeviceAddressMultiDevice' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceDescriptorIndexingFeatures &features, + bool required) const { + vk::PhysicalDeviceDescriptorIndexingFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.shaderInputAttachmentArrayDynamicIndexing) && + (!supported.shaderInputAttachmentArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderInputAttachmentArrayDynamicIndexing' is not supported"); + return false; + } + + if ((features.shaderInputAttachmentArrayNonUniformIndexing) && + (!supported.shaderInputAttachmentArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderInputAttachmentArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderSampledImageArrayNonUniformIndexing) && + (!supported.shaderSampledImageArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderSampledImageArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderStorageBufferArrayNonUniformIndexing) && + (!supported.shaderStorageBufferArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageBufferArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderStorageImageArrayNonUniformIndexing) && + (!supported.shaderStorageImageArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderStorageTexelBufferArrayDynamicIndexing) && + (!supported.shaderStorageTexelBufferArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageTexelBufferArrayDynamicIndexing' is not supported"); + return false; + } + + if ((features.shaderStorageTexelBufferArrayNonUniformIndexing) && + (!supported.shaderStorageTexelBufferArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageTexelBufferArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderUniformBufferArrayNonUniformIndexing) && + (!supported.shaderUniformBufferArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderUniformBufferArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.shaderUniformTexelBufferArrayDynamicIndexing) && + (!supported.shaderUniformTexelBufferArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderUniformTexelBufferArrayDynamicIndexing' is not supported"); + return false; + } + + if ((features.shaderUniformTexelBufferArrayNonUniformIndexing) && + (!supported.shaderUniformTexelBufferArrayNonUniformIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderUniformTexelBufferArrayNonUniformIndexing' is not supported"); + return false; + } + + if ((features.descriptorBindingPartiallyBound) && (!supported.descriptorBindingPartiallyBound)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingPartiallyBound' is not supported"); + return false; + } + + + if ((features.descriptorBindingSampledImageUpdateAfterBind) && + (!supported.descriptorBindingSampledImageUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingSampledImageUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingStorageBufferUpdateAfterBind) && + (!supported.descriptorBindingStorageBufferUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingStorageBufferUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingStorageImageUpdateAfterBind) && + (!supported.descriptorBindingStorageImageUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingStorageImageUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingStorageTexelBufferUpdateAfterBind) && + (!supported.descriptorBindingStorageTexelBufferUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingStorageTexelBufferUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingUniformBufferUpdateAfterBind) && + (!supported.descriptorBindingUniformBufferUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingUniformBufferUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingUniformTexelBufferUpdateAfterBind) && + (!supported.descriptorBindingUniformTexelBufferUpdateAfterBind)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingUniformTexelBufferUpdateAfterBind' is not supported"); + return false; + } + + if ((features.descriptorBindingUpdateUnusedWhilePending) && + (!supported.descriptorBindingUpdateUnusedWhilePending)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingUpdateUnusedWhilePending' is not supported"); + return false; + } + + if ((features.descriptorBindingVariableDescriptorCount) && + (!supported.descriptorBindingVariableDescriptorCount)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'descriptorBindingVariableDescriptorCount' is not supported"); + return false; + } + + if ((features.runtimeDescriptorArray) && (!supported.runtimeDescriptorArray)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'runtimeDescriptorArray' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceHostQueryResetFeatures &features, + bool required) const { + vk::PhysicalDeviceHostQueryResetFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.hostQueryReset) && (!supported.hostQueryReset)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'hostQueryReset' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceImagelessFramebufferFeatures &features, + bool required) const { + vk::PhysicalDeviceImagelessFramebufferFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.imagelessFramebuffer) && (!supported.imagelessFramebuffer)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'imagelessFramebuffer' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMultiviewFeatures &features, + bool required) const { + vk::PhysicalDeviceMultiviewFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.multiview) && (!supported.multiview)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'multiview' is not supported"); + return false; + } + + if ((features.multiviewGeometryShader) && (!supported.multiviewGeometryShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'multiviewGeometryShader' is not supported"); + return false; + } + + if ((features.multiviewTessellationShader) && (!supported.multiviewTessellationShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'multiviewTessellationShader' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceProtectedMemoryFeatures &features, + bool required) const { + vk::PhysicalDeviceProtectedMemoryFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.protectedMemory) && (!supported.protectedMemory)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'protectedMemory' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceSamplerYcbcrConversionFeatures &features, + bool required) const { + vk::PhysicalDeviceSamplerYcbcrConversionFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.samplerYcbcrConversion) && (!supported.samplerYcbcrConversion)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'samplerYcbcrConversion' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceScalarBlockLayoutFeatures &features, + bool required) const { + vk::PhysicalDeviceScalarBlockLayoutFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.scalarBlockLayout) && (!supported.scalarBlockLayout)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'scalarBlockLayout' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures &features, + bool required) const { + vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.separateDepthStencilLayouts) && (!supported.separateDepthStencilLayouts)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'separateDepthStencilLayouts' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderAtomicInt64Features &features, + bool required) const { + vk::PhysicalDeviceShaderAtomicInt64Features supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.shaderBufferInt64Atomics) && (!supported.shaderBufferInt64Atomics)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderBufferInt64Atomics' is not supported"); + return false; + } + + if ((features.shaderSharedInt64Atomics) && (!supported.shaderSharedInt64Atomics)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderSharedInt64Atomics' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderFloat16Int8Features &features, bool required) const { + vk::PhysicalDeviceShaderFloat16Int8Features supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.shaderFloat16) && (!supported.shaderFloat16)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderFloat16' is not supported"); + return false; + } + + if ((features.shaderInt8) && (!supported.shaderInt8)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderInt8' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures &features, + bool required) const { + vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.shaderSubgroupExtendedTypes) && (!supported.shaderSubgroupExtendedTypes)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderSubgroupExtendedTypes' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceTimelineSemaphoreFeatures &features, + bool required) const { + vk::PhysicalDeviceTimelineSemaphoreFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.timelineSemaphore) && (!supported.timelineSemaphore)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'timelineSemaphore' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceUniformBufferStandardLayoutFeatures &features, + bool required) const { + vk::PhysicalDeviceUniformBufferStandardLayoutFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.uniformBufferStandardLayout) && (!supported.uniformBufferStandardLayout)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'uniformBufferStandardLayout' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVariablePointersFeatures &features, + bool required) const { + vk::PhysicalDeviceVariablePointersFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.variablePointers) && (!supported.variablePointers)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'variablePointers' is not supported"); + return false; + } + + if ((features.variablePointersStorageBuffer) && (!supported.variablePointersStorageBuffer)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'variablePointersStorageBuffer' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkanMemoryModelFeatures &features, + bool required) const { + vk::PhysicalDeviceVulkanMemoryModelFeatures supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.vulkanMemoryModel) && (!supported.vulkanMemoryModel)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'vulkanMemoryModel' is not supported"); + return false; + } + + if ((features.vulkanMemoryModelDeviceScope) && (!supported.vulkanMemoryModelDeviceScope)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'vulkanMemoryModelDeviceScope' is not supported"); + return false; + } + + if ((features.vulkanMemoryModelAvailabilityVisibilityChains) && + (!supported.vulkanMemoryModelAvailabilityVisibilityChains)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'vulkanMemoryModelAvailabilityVisibilityChains' is not supported"); + return false; + } + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV &features, bool required) const { + vk::PhysicalDeviceMeshShaderFeaturesNV supported; + vk::PhysicalDeviceFeatures2 query; + query.setPNext(&supported); + + m_physicalDevice.getFeatures2(&query); + + if ((features.taskShader) && (!supported.taskShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'taskShader' is not supported"); + return false; + } + + if ((features.meshShader) && (!supported.meshShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'meshShader' is not supported"); + return false; + } + + return true; + } + + const char* strclone(const char* str) { + if (!str) { + return nullptr; + } + + const size_t length = strlen(str) + 1; + + if (length <= 1) { + return nullptr; + } + + char* clone = new char[length]; + strcpy(clone, str); + return clone; + } + + FeatureManager::FeatureManager(vk::PhysicalDevice &physicalDevice) : + m_physicalDevice(physicalDevice), + m_supportedExtensions(), + m_activeExtensions(), + m_featuresBase(), + m_featuresExtensions() { + for (const auto& extension : m_physicalDevice.enumerateDeviceExtensionProperties()) { + const char* clone = strclone(extension.extensionName); + + if (clone) { + m_supportedExtensions.push_back(clone); + } + } + } + + FeatureManager::FeatureManager(FeatureManager &&other) noexcept : + m_physicalDevice(other.m_physicalDevice), + m_supportedExtensions(std::move(other.m_supportedExtensions)), + m_activeExtensions(std::move(other.m_activeExtensions)), + m_featuresBase(other.m_featuresBase), + m_featuresExtensions(std::move(other.m_featuresExtensions)) { + other.m_featuresExtensions.clear(); + other.m_activeExtensions.clear(); + other.m_supportedExtensions.clear(); + } + + FeatureManager::~FeatureManager() { + for (auto& features : m_featuresExtensions) { + delete features; + } + + for (auto& extension : m_activeExtensions) { + delete[] extension; + } + + for (auto& extension : m_supportedExtensions) { + delete[] extension; + } + } + + FeatureManager &FeatureManager::operator=(FeatureManager &&other) noexcept { + m_physicalDevice = other.m_physicalDevice; + m_supportedExtensions = std::move(other.m_supportedExtensions); + m_activeExtensions = std::move(other.m_activeExtensions); + m_featuresBase = other.m_featuresBase; + m_featuresExtensions = std::move(other.m_featuresExtensions); + + other.m_featuresExtensions.clear(); + other.m_activeExtensions.clear(); + other.m_supportedExtensions.clear(); + + return *this; + } + + bool FeatureManager::isExtensionSupported(const char *extension) const { + for (const auto& supported : m_supportedExtensions) { + if (0 == strcmp(supported, extension)) { + return true; + } + } + + return false; + } + + bool FeatureManager::useExtension(const char *extension, bool required) { + const char* clone = strclone(extension); + + if (!clone) { + vkcv_log(LogLevel::WARNING, "Extension '%s' is not valid", extension); + return false; + } + + if (!isExtensionSupported(extension)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), "Extension '%s' is not supported", extension); + + delete[] clone; + return false; + } + + m_activeExtensions.push_back(clone); + return true; + } + + bool FeatureManager::isExtensionActive(const char *extension) const { + for (const auto& supported : m_activeExtensions) { + if (0 == strcmp(supported, extension)) { + return true; + } + } + + return false; + } + + const std::vector<const char*>& FeatureManager::getActiveExtensions() const { + return m_activeExtensions; + } + + bool FeatureManager::useFeatures(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction, + bool required) { + featureFunction(m_featuresBase.features); + + const auto& supported = m_physicalDevice.getFeatures(); + + if ((m_featuresBase.features.alphaToOne) && (!supported.alphaToOne)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'alphaToOne' is not supported"); + return false; + } + + if ((m_featuresBase.features.depthBiasClamp) && (!supported.depthBiasClamp)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'depthBiasClamp' is not supported"); + return false; + } + + if ((m_featuresBase.features.depthBounds) && (!supported.depthBounds)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'depthBounds' is not supported"); + return false; + } + + if ((m_featuresBase.features.depthClamp) && (!supported.depthClamp)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'depthClamp' is not supported"); + return false; + } + + if ((m_featuresBase.features.drawIndirectFirstInstance) && (!supported.drawIndirectFirstInstance)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'drawIndirectFirstInstance' is not supported"); + return false; + } + + if ((m_featuresBase.features.dualSrcBlend) && (!supported.dualSrcBlend)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'dualSrcBlend' is not supported"); + return false; + } + + if ((m_featuresBase.features.fillModeNonSolid) && (!supported.fillModeNonSolid)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'fillModeNonSolid' is not supported"); + return false; + } + + if ((m_featuresBase.features.fragmentStoresAndAtomics) && (!supported.fragmentStoresAndAtomics)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'fragmentStoresAndAtomics' is not supported"); + return false; + } + + if ((m_featuresBase.features.fullDrawIndexUint32) && (!supported.fullDrawIndexUint32)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'fullDrawIndexUint32' is not supported"); + return false; + } + + if ((m_featuresBase.features.geometryShader) && (!supported.geometryShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'geometryShader' is not supported"); + return false; + } + + if ((m_featuresBase.features.imageCubeArray) && (!supported.imageCubeArray)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'imageCubeArray' is not supported"); + return false; + } + + if ((m_featuresBase.features.independentBlend) && (!supported.independentBlend)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'independentBlend' is not supported"); + return false; + } + + if ((m_featuresBase.features.inheritedQueries) && (!supported.inheritedQueries)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'inheritedQueries' is not supported"); + return false; + } + + if ((m_featuresBase.features.largePoints) && (!supported.largePoints)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'largePoints' is not supported"); + return false; + } + + if ((m_featuresBase.features.logicOp) && (!supported.logicOp)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'logicOp' is not supported"); + return false; + } + + if ((m_featuresBase.features.multiDrawIndirect) && (!supported.multiDrawIndirect)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'multiDrawIndirect' is not supported"); + return false; + } + + if ((m_featuresBase.features.multiViewport) && (!supported.multiViewport)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'multiViewport' is not supported"); + return false; + } + + if ((m_featuresBase.features.occlusionQueryPrecise) && (!supported.occlusionQueryPrecise)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'occlusionQueryPrecise' is not supported"); + return false; + } + + if ((m_featuresBase.features.pipelineStatisticsQuery) && (!supported.pipelineStatisticsQuery)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'pipelineStatisticsQuery' is not supported"); + return false; + } + + if ((m_featuresBase.features.robustBufferAccess) && (!supported.robustBufferAccess)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'robustBufferAccess' is not supported"); + return false; + } + + if ((m_featuresBase.features.sampleRateShading) && (!supported.sampleRateShading)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sampleRateShading' is not supported"); + return false; + } + + if ((m_featuresBase.features.samplerAnisotropy) && (!supported.samplerAnisotropy)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'samplerAnisotropy' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderClipDistance) && (!supported.shaderClipDistance)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderClipDistance' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderCullDistance) && (!supported.shaderCullDistance)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderCullDistance' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderFloat64) && (!supported.shaderFloat64)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderFloat64' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderImageGatherExtended) && (!supported.shaderImageGatherExtended)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderImageGatherExtended' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderInt16) && (!supported.shaderInt16)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderInt16' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderInt64) && (!supported.shaderInt64)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderInt64' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderResourceMinLod) && (!supported.shaderResourceMinLod)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderResourceMinLod' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderResourceResidency) && (!supported.shaderResourceResidency)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderResourceResidency' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderSampledImageArrayDynamicIndexing) && + (!supported.shaderSampledImageArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderSampledImageArrayDynamicIndexing' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageBufferArrayDynamicIndexing) && + (!supported.shaderStorageBufferArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageBufferArrayDynamicIndexing' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageImageArrayDynamicIndexing) && + (!supported.shaderStorageImageArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageArrayDynamicIndexing' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageImageExtendedFormats) && + (!supported.shaderStorageImageExtendedFormats)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageExtendedFormats' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageImageMultisample) && + (!supported.shaderStorageImageMultisample)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageMultisample' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageImageReadWithoutFormat) && + (!supported.shaderStorageImageReadWithoutFormat)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageReadWithoutFormat' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderStorageImageWriteWithoutFormat) && + (!supported.shaderStorageImageWriteWithoutFormat)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderStorageImageWriteWithoutFormat' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderTessellationAndGeometryPointSize) && + (!supported.shaderTessellationAndGeometryPointSize)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderTessellationAndGeometryPointSize' is not supported"); + return false; + } + + if ((m_featuresBase.features.shaderUniformBufferArrayDynamicIndexing) && + (!supported.shaderUniformBufferArrayDynamicIndexing)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'shaderUniformBufferArrayDynamicIndexing' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseBinding) && (!supported.sparseBinding)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseBinding' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidency2Samples) && (!supported.sparseResidency2Samples)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidency2Samples' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidency4Samples) && (!supported.sparseResidency4Samples)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidency4Samples' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidency8Samples) && (!supported.sparseResidency8Samples)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidency8Samples' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidency16Samples) && (!supported.sparseResidency16Samples)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidency16Samples' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidencyAliased) && (!supported.sparseResidencyAliased)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidencyAliased' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidencyBuffer) && (!supported.sparseResidencyBuffer)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidencyBuffer' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidencyImage2D) && (!supported.sparseResidencyImage2D)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidencyImage2D' is not supported"); + return false; + } + + if ((m_featuresBase.features.sparseResidencyImage3D) && (!supported.sparseResidencyImage3D)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'sparseResidencyImage2D' is not supported"); + return false; + } + + if ((m_featuresBase.features.tessellationShader) && (!supported.tessellationShader)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'tessellationShader' is not supported"); + return false; + } + + if ((m_featuresBase.features.textureCompressionASTC_LDR) && (!supported.textureCompressionASTC_LDR)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'textureCompressionASTC_LDR' is not supported"); + return false; + } + + if ((m_featuresBase.features.textureCompressionBC) && (!supported.textureCompressionBC)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'textureCompressionBC' is not supported"); + return false; + } + + if ((m_featuresBase.features.textureCompressionETC2) && (!supported.textureCompressionETC2)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'textureCompressionETC2' is not supported"); + return false; + } + + if ((m_featuresBase.features.variableMultisampleRate) && (!supported.variableMultisampleRate)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'variableMultisampleRate' is not supported"); + return false; + } + + if ((m_featuresBase.features.vertexPipelineStoresAndAtomics) && (!supported.vertexPipelineStoresAndAtomics)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'vertexPipelineStoresAndAtomics' is not supported"); + return false; + } + + if ((m_featuresBase.features.wideLines) && (!supported.wideLines)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), + "Feature 'wideLines' is not supported"); + return false; + } + + return true; + } + + const vk::PhysicalDeviceFeatures2& FeatureManager::getFeatures() const { + return m_featuresBase; + } + +} -- GitLab