diff --git a/CMakeLists.txt b/CMakeLists.txt index dfafe1cd084d4b324c233d502e301c24a5ee95e1..da150fcbeafec3be555d4bbefdab37dbdedf277f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,7 @@ add_library(vkcv STATIC ${vkcv_sources}) if(MSVC) #enable multicore compilation on visual studio - target_compile_options(vkcv PRIVATE "/MP" "/openmp") + target_compile_options(vkcv PRIVATE "/MP" "/openmp" "/Zc:offsetof-") #set source groups to create proper filters in visual studio source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${vkcv_sources}) diff --git a/config/Sources.cmake b/config/Sources.cmake index 41cd0c20f2106dc02700d9b23227f3e6c34a057a..ea95d152280fde1dcfdd93e7e48d1f8b58d3bb9e 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -1,6 +1,12 @@ # adding all source files and header files of the framework: set(vkcv_sources + ${vkcv_include}/vkcv/Features.hpp + ${vkcv_source}/vkcv/Features.cpp + + ${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 824713fd1e29cbb8b7e60b22768c0019daaa9938..1160857cb2cdbbb0815390ff1f8405dda0f83796 100644 --- a/include/vkcv/Context.hpp +++ b/include/vkcv/Context.hpp @@ -5,6 +5,7 @@ #include "QueueManager.hpp" #include "DrawcallRecording.hpp" +#include "Features.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; @@ -41,8 +45,8 @@ namespace vkcv static Context create(const char *applicationName, uint32_t applicationVersion, const std::vector<vk::QueueFlagBits>& queueFlags, - const std::vector<const char *>& instanceExtensions, - const std::vector<const char *>& deviceExtensions); + const Features& features, + const std::vector<const char*>& instanceExtensions = {}); private: /** @@ -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/Core.hpp b/include/vkcv/Core.hpp index 7b5c1d94a6519e626249d55c65da19b1e8f95044..2d5dcd0f5e00e0870ea3b9cb4ea8d20f2f623c3d 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -7,14 +7,14 @@ #include <memory> #include <vulkan/vulkan.hpp> -#include "vkcv/Context.hpp" -#include "vkcv/Swapchain.hpp" -#include "vkcv/Window.hpp" -#include "vkcv/PassConfig.hpp" -#include "vkcv/Handles.hpp" -#include "vkcv/Buffer.hpp" -#include "vkcv/Image.hpp" -#include "vkcv/PipelineConfig.hpp" +#include "Context.hpp" +#include "Swapchain.hpp" +#include "Window.hpp" +#include "PassConfig.hpp" +#include "Handles.hpp" +#include "Buffer.hpp" +#include "Image.hpp" +#include "PipelineConfig.hpp" #include "CommandResources.hpp" #include "SyncResources.hpp" #include "Result.hpp" @@ -139,8 +139,8 @@ namespace vkcv const char *applicationName, uint32_t applicationVersion, const std::vector<vk::QueueFlagBits>& queueFlags = {}, - const std::vector<const char*>& instanceExtensions = {}, - const std::vector<const char*>& deviceExtensions = {}); + const Features& features = {}, + const std::vector<const char *>& instanceExtensions = {}); /** * Creates a basic vulkan graphics pipeline using @p config from the pipeline config class and returns it using the @p handle. @@ -249,16 +249,16 @@ namespace vkcv bool beginFrame(uint32_t& width, uint32_t& height); void recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, + const CommandStreamHandle& cmdStreamHandle, + const PassHandle& renderpassHandle, const PipelineHandle pipelineHandle, const PushConstants &pushConstants, const std::vector<DrawcallInfo> &drawcalls, const std::vector<ImageHandle> &renderTargets); void recordMeshShaderDrawcalls( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, + const CommandStreamHandle& cmdStreamHandle, + const PassHandle& renderpassHandle, const PipelineHandle pipelineHandle, const PushConstants& pushConstantData, const std::vector<MeshShaderDrawcall>& drawcalls, diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cf945d7498fec83f0b128294caa3ba267b69a6b3 --- /dev/null +++ b/include/vkcv/FeatureManager.hpp @@ -0,0 +1,150 @@ +#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::PhysicalDeviceFeatures& features, bool required) const; + + [[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; + + vk::BaseOutStructure* findFeatureStructure(vk::StructureType type) 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 std::string& extension) const; + + bool useExtension(const std::string& extension, bool required = true); + + [[nodiscard]] + bool isExtensionActive(const std::string& 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; + T* features_ptr = reinterpret_cast<T*>(findFeatureStructure(features.sType)); + + if (features_ptr) { + features = *features_ptr; + } + + featureFunction(features); + + if (!checkSupport(features, required)) { + return false; + } + + if (features_ptr) { + *features_ptr = features; + return true; + } + + features_ptr = new T(features); + + if (m_featuresExtensions.empty()) { + m_featuresBase.setPNext(features_ptr); + } else { + m_featuresExtensions.back()->setPNext( + reinterpret_cast<vk::BaseOutStructure*>(features_ptr) + ); + } + + m_featuresExtensions.push_back( + reinterpret_cast<vk::BaseOutStructure*>(features_ptr) + ); + + return true; + } + + [[nodiscard]] + const vk::PhysicalDeviceFeatures2& getFeatures() const; + + }; + +} diff --git a/include/vkcv/Features.hpp b/include/vkcv/Features.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6ef3fa28be912627b4495c66427336dfaa51beff --- /dev/null +++ b/include/vkcv/Features.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include <functional> +#include <vector> +#include <initializer_list> + +#include "FeatureManager.hpp" + +namespace vkcv { + + typedef std::function<bool(FeatureManager&)> Feature; + + class Features { + private: + std::vector<Feature> m_features; + + public: + Features() = default; + + Features(const std::initializer_list<std::string>& list); + + Features(const Features& other) = default; + Features(Features&& other) = default; + + ~Features() = default; + + Features& operator=(const Features& other) = default; + Features& operator=(Features&& other) = default; + + void requireExtension(const std::string& extension); + + void requireExtensionFeature(const std::string& extension, + const std::function<void(vk::PhysicalDeviceFeatures&)>& featureFunction); + + template<typename T> + void requireExtensionFeature(const std::string& extension, const std::function<void(T&)>& featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager& featureManager) { + if (featureManager.useExtension(extension, true)) { + return featureManager.template useFeatures<T>(featureFunction, true); + } else { + return false; + } + }); + } + + void requireFeature(const std::function<void(vk::PhysicalDeviceFeatures&)>& featureFunction); + + template<typename T> + void requireFeature(const std::function<void(T&)>& featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager& featureManager) { + return featureManager.template useFeatures<T>(featureFunction, true); + }); + } + + void tryExtension(const std::string& extension); + + void tryExtensionFeature(const std::string& extension, + const std::function<void(vk::PhysicalDeviceFeatures&)>& featureFunction); + + template<typename T> + void tryExtensionFeature(const std::string& extension, const std::function<void(T&)>& featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager& featureManager) { + if (featureManager.useExtension(extension, false)) { + return featureManager.template useFeatures<T>(featureFunction, false); + } else { + return false; + } + }); + } + + void tryFeature(const std::function<void(vk::PhysicalDeviceFeatures&)>& featureFunction); + + template<typename T> + void tryFeature(const std::function<void(T&)>& featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager& featureManager) { + return featureManager.template useFeatures<T>(featureFunction, false); + }); + } + + [[nodiscard]] + const std::vector<Feature>& getList() const; + + }; + +} diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp index 460a6d0b459fe7d1d2a917a62138fea2e5a40908..85908a76d51082b09cf5e6b09ac01fbbd4ef9266 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/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index fc682ae1f8b3d1a174ff230c274b89093bc3325c..2ad76e8f78c5870f6b582a1970caac306026166f 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -18,14 +18,13 @@ int main(int argc, const char** argv) { windowHeight, true ); - + vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, - {}, - { "VK_KHR_swapchain" } + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); vkcv::asset::Scene mesh; diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp index 527eba8c3a1e020e14d92f5d305e2ddced936333..21f8deacdb3f81fcd29c5f14cbe74485f36d13cd 100644 --- a/projects/first_scene/src/main.cpp +++ b/projects/first_scene/src/main.cpp @@ -28,14 +28,13 @@ int main(int argc, const char** argv) { cameraManager.getCamera(camIndex0).setNearFar(0.1f, 30.0f); cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f); - + vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, - {}, - { "VK_KHR_swapchain" } + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); vkcv::scene::Scene scene = vkcv::scene::Scene::load(core, std::filesystem::path( diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 3598da5f579b608d2c29f1f6fea0b0e25a560336..76e860dea27126c943fa6b6a7d5a01f174203920 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -16,14 +16,13 @@ int main(int argc, const char** argv) { windowHeight, false ); - + vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain" } + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); auto triangleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, vkcv::BufferMemoryType::DEVICE_LOCAL); diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp index 92d548acde9c5a27e69c6daf4d92ca1da9d50a2c..5927970333d63d7e0c3bbbda4b7ccbf321c48a48 100644 --- a/projects/indirect_dispatch/src/App.cpp +++ b/projects/indirect_dispatch/src/App.cpp @@ -18,8 +18,7 @@ App::App() : m_applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, - {}, - { "VK_KHR_swapchain" })), + { VK_KHR_SWAPCHAIN_EXTENSION_NAME })), m_cameraManager(m_window){} bool App::initialize() { diff --git a/projects/mesh_shader/.gitignore b/projects/mesh_shader/.gitignore index 54601c357bf3fb97b914a6e657c042a5c6a985d7..fd009a6281f4b2b6716e193d23829907f4bb5f33 100644 --- a/projects/mesh_shader/.gitignore +++ b/projects/mesh_shader/.gitignore @@ -1 +1 @@ -mesh_shader +mesh_shader \ No newline at end of file diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp index 611a324f875f5726ebd674e3ee51d27ad2d8e849..15aaf527619b4fb06d89bccc78cffe843cc723b7 100644 --- a/projects/mesh_shader/src/main.cpp +++ b/projects/mesh_shader/src/main.cpp @@ -86,14 +86,21 @@ int main(int argc, const char** argv) { windowHeight, false ); + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireExtensionFeature<vk::PhysicalDeviceMeshShaderFeaturesNV>( + VK_NV_MESH_SHADER_EXTENSION_NAME, [](vk::PhysicalDeviceMeshShaderFeaturesNV& features) { + features.setTaskShader(true); + features.setMeshShader(true); + }); vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain", VK_NV_MESH_SHADER_EXTENSION_NAME } + features ); vkcv::gui::GUI gui (core, window); diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp index 07ba6b194ce72dbad15a921ca13a4814c6d4f5df..4de016aefd345a4231ea2dae0818b0839b163729 100644 --- a/projects/particle_simulation/src/main.cpp +++ b/projects/particle_simulation/src/main.cpp @@ -23,14 +23,13 @@ int main(int argc, const char **argv) { ); vkcv::camera::CameraManager cameraManager(window); - + vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, - {}, - {"VK_KHR_swapchain"} + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); auto particleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index e7f9caa493714d30f13f64c292f1b6e51e5170b1..3c23c7eabbbdc5d83d37bde79b43a2730b1f28b2 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -80,14 +80,18 @@ int main(int argc, const char** argv) { cameraManager.getCamera(camIndex).setFov(glm::radians(37.8)); // fov of a 35mm lens cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); - + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); + features.requireExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); + vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain", "VK_KHR_shader_float16_int8", "VK_KHR_16bit_storage" } + features ); vkcv::asset::Scene mesh; diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp index 2e30fb961d0b0931e4ff8796dd92b2cbd0b5f734..f48fad1a9022cbf99f6452c319d629060befbd98 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,20 +187,11 @@ 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, - const std::vector<const char *>& instanceExtensions, - const std::vector<const char *>& deviceExtensions) { + const Features& features, + const std::vector<const char*>& instanceExtensions) { // check for layer support const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties(); @@ -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,16 +259,35 @@ 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); + FeatureManager featureManager (physicalDevice); + + if (featureManager.useExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDeviceShaderFloat16Int8Features>( + [](vk::PhysicalDeviceShaderFloat16Int8Features& features) { + features.setShaderFloat16(true); + }, false); } - if (!checkSupport(supportedExtensions, deviceExtensions)) { - throw std::runtime_error("The requested device extensions are not supported by the physical device!"); + + if (featureManager.useExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false)) { + featureManager.useFeatures<vk::PhysicalDevice16BitStorageFeatures>( + [](vk::PhysicalDevice16BitStorageFeatures& features) { + features.setStorageBuffer16BitAccess(true); + }, false); } + featureManager.useFeatures([](vk::PhysicalDeviceFeatures& features) { + features.setFragmentStoresAndAtomics(true); + features.setGeometryShader(true); + features.setDepthClamp(true); + features.setShaderInt16(true); + }); + + for (const auto& feature : features.getList()) { + feature(featureManager); + } + + const auto& extensions = featureManager.getActiveExtensions(); + std::vector<vk::DeviceQueueCreateInfo> qCreateInfos; // create required queues @@ -286,58 +303,21 @@ namespace vkcv qCreateInfos.data(), 0, nullptr, - deviceExtensions.size(), - deviceExtensions.data(), - nullptr // Should our device use some features??? If yes: TODO + extensions.size(), + extensions.data(), + nullptr ); #ifndef NDEBUG 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 (shaderFloat16) { - deviceFeatures2.setPNext(&deviceShaderFloat16Int8Features); - } - if (storage16bit) { - deviceShaderFloat16Int8Features.setPNext(&device16BitStorageFeatures); - } - - 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 +356,7 @@ namespace vkcv instance, physicalDevice, device, + std::move(featureManager), std::move(queueManager), std::move(allocator) ); diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 92e2df4f18f59355868e9dcce7a78c4e1a9c5cb7..851f08a4c0894b6f6753270ba068e9b2dd544574 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -54,14 +54,14 @@ namespace vkcv const char *applicationName, uint32_t applicationVersion, const std::vector<vk::QueueFlagBits>& queueFlags, - const std::vector<const char *>& instanceExtensions, - const std::vector<const char *>& deviceExtensions) + const Features& features, + const std::vector<const char *>& instanceExtensions) { Context context = Context::create( applicationName, applicationVersion, queueFlags, - instanceExtensions, - deviceExtensions + features, + instanceExtensions ); Swapchain swapChain = Swapchain::create(window, context); @@ -331,8 +331,8 @@ namespace vkcv } void Core::recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, + const CommandStreamHandle& cmdStreamHandle, + const PassHandle& renderpassHandle, const PipelineHandle pipelineHandle, const PushConstants &pushConstantData, const std::vector<DrawcallInfo> &drawcalls, @@ -368,7 +368,6 @@ namespace vkcv submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { - const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments); const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); @@ -377,16 +376,14 @@ namespace vkcv cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); const PipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); - if(pipeConfig.m_UseDynamicViewport) - { + if (pipeConfig.m_UseDynamicViewport) { recordDynamicViewport(cmdBuffer, width, height); } - for (int i = 0; i < drawcalls.size(); i++) { + for (size_t i = 0; i < drawcalls.size(); i++) { recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i); } - vk::Rect2D dynamicScissor({0, 0}, {width, height}); cmdBuffer.endRenderPass(); }; @@ -399,8 +396,8 @@ namespace vkcv } void Core::recordMeshShaderDrawcalls( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, + const CommandStreamHandle& cmdStreamHandle, + const PassHandle& renderpassHandle, const PipelineHandle pipelineHandle, const PushConstants& pushConstantData, const std::vector<MeshShaderDrawcall>& drawcalls, @@ -436,7 +433,6 @@ namespace vkcv submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { - const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments); const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); @@ -445,12 +441,11 @@ namespace vkcv cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); const PipelineConfig& pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); - if (pipeConfig.m_UseDynamicViewport) - { + if (pipeConfig.m_UseDynamicViewport) { recordDynamicViewport(cmdBuffer, width, height); } - for (int i = 0; i < drawcalls.size(); i++) { + for (size_t i = 0; i < drawcalls.size(); i++) { const uint32_t pushConstantOffset = i * pushConstantData.getSizePerDrawcall(); recordMeshShaderDrawcall( cmdBuffer, @@ -458,14 +453,14 @@ namespace vkcv pushConstantData, pushConstantOffset, drawcalls[i], - 0); + 0 + ); } cmdBuffer.endRenderPass(); }; - auto finishFunction = [framebuffer, this]() - { + auto finishFunction = [framebuffer, this]() { m_Context.m_Device.destroy(framebuffer); }; diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18307999bb28ed60791a801b952619eeeba51a53 --- /dev/null +++ b/src/vkcv/FeatureManager.cpp @@ -0,0 +1,437 @@ + +#include "vkcv/FeatureManager.hpp" + +#include <stddef.h> +#include <string.h> +#include <type_traits> + +namespace vkcv { + +#ifdef _MSVC_LANG +#define typeof(var) std::decay<decltype((var))>::type +#endif + +#define vkcv_check_init_features2(type)\ +type supported; \ +vk::PhysicalDeviceFeatures2 query; \ +query.setPNext(&supported); \ +m_physicalDevice.getFeatures2(&query) + +#define vkcv_check_feature(attribute) { \ + const char *f = reinterpret_cast<const char*>(&(features)); \ + const char *s = reinterpret_cast<const char*>(&(supported)); \ + const vk::Bool32* fb = reinterpret_cast<const vk::Bool32*>(f + offsetof(typeof((features)), attribute)); \ + const vk::Bool32* sb = reinterpret_cast<const vk::Bool32*>(s + offsetof(typeof((features)), attribute)); \ + if ((*fb) && (!*sb)) { \ + vkcv_log(((required)? LogLevel::ERROR : LogLevel::WARNING), \ + "Feature '" #attribute "' is not supported"); \ + return false; \ + } \ +} + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceFeatures &features, bool required) const { + const auto& supported = m_physicalDevice.getFeatures(); + + vkcv_check_feature(alphaToOne); + vkcv_check_feature(depthBiasClamp); + vkcv_check_feature(depthBounds); + vkcv_check_feature(depthClamp); + vkcv_check_feature(drawIndirectFirstInstance); + vkcv_check_feature(dualSrcBlend); + vkcv_check_feature(fillModeNonSolid); + vkcv_check_feature(fragmentStoresAndAtomics); + vkcv_check_feature(fullDrawIndexUint32); + vkcv_check_feature(geometryShader); + vkcv_check_feature(imageCubeArray); + vkcv_check_feature(independentBlend); + vkcv_check_feature(inheritedQueries); + vkcv_check_feature(largePoints); + vkcv_check_feature(logicOp); + vkcv_check_feature(multiDrawIndirect); + vkcv_check_feature(multiViewport); + vkcv_check_feature(occlusionQueryPrecise); + vkcv_check_feature(pipelineStatisticsQuery); + vkcv_check_feature(robustBufferAccess); + vkcv_check_feature(sampleRateShading); + vkcv_check_feature(samplerAnisotropy); + vkcv_check_feature(shaderClipDistance); + vkcv_check_feature(shaderCullDistance); + vkcv_check_feature(shaderFloat64); + vkcv_check_feature(shaderImageGatherExtended); + vkcv_check_feature(shaderInt16); + vkcv_check_feature(shaderInt64); + vkcv_check_feature(shaderResourceMinLod); + vkcv_check_feature(shaderResourceResidency); + vkcv_check_feature(shaderSampledImageArrayDynamicIndexing); + vkcv_check_feature(shaderStorageBufferArrayDynamicIndexing); + vkcv_check_feature(shaderStorageImageArrayDynamicIndexing); + vkcv_check_feature(shaderStorageImageExtendedFormats); + vkcv_check_feature(shaderStorageImageMultisample); + vkcv_check_feature(shaderStorageImageReadWithoutFormat); + vkcv_check_feature(shaderStorageImageWriteWithoutFormat); + vkcv_check_feature(shaderTessellationAndGeometryPointSize); + vkcv_check_feature(shaderUniformBufferArrayDynamicIndexing); + vkcv_check_feature(sparseBinding); + vkcv_check_feature(sparseResidency2Samples); + vkcv_check_feature(sparseResidency4Samples); + vkcv_check_feature(sparseResidency8Samples); + vkcv_check_feature(sparseResidency16Samples); + vkcv_check_feature(sparseResidencyAliased); + vkcv_check_feature(sparseResidencyBuffer); + vkcv_check_feature(sparseResidencyImage2D); + vkcv_check_feature(sparseResidencyImage3D); + vkcv_check_feature(tessellationShader); + vkcv_check_feature(textureCompressionASTC_LDR); + vkcv_check_feature(textureCompressionBC); + vkcv_check_feature(textureCompressionETC2); + vkcv_check_feature(variableMultisampleRate); + vkcv_check_feature(vertexPipelineStoresAndAtomics); + vkcv_check_feature(wideLines); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDevice16BitStorageFeatures &features, bool required) const { + vkcv_check_init_features2(vk::PhysicalDevice16BitStorageFeatures); + + vkcv_check_feature(storageBuffer16BitAccess); + vkcv_check_feature(storageInputOutput16); + vkcv_check_feature(storagePushConstant16); + vkcv_check_feature(uniformAndStorageBuffer16BitAccess); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDevice8BitStorageFeatures &features, bool required) const { + vkcv_check_init_features2(vk::PhysicalDevice8BitStorageFeatures); + + vkcv_check_feature(storageBuffer8BitAccess); + vkcv_check_feature(storagePushConstant8); + vkcv_check_feature(uniformAndStorageBuffer8BitAccess); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceBufferDeviceAddressFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceBufferDeviceAddressFeatures); + + vkcv_check_feature(bufferDeviceAddress); + vkcv_check_feature(bufferDeviceAddressCaptureReplay); + vkcv_check_feature(bufferDeviceAddressMultiDevice); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceDescriptorIndexingFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceDescriptorIndexingFeatures); + + vkcv_check_feature(shaderInputAttachmentArrayDynamicIndexing); + vkcv_check_feature(shaderInputAttachmentArrayNonUniformIndexing); + vkcv_check_feature(shaderSampledImageArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageImageArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderUniformBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(descriptorBindingPartiallyBound); + vkcv_check_feature(descriptorBindingSampledImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUniformBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUniformTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUpdateUnusedWhilePending); + vkcv_check_feature(descriptorBindingVariableDescriptorCount); + vkcv_check_feature(runtimeDescriptorArray); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceHostQueryResetFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceHostQueryResetFeatures); + + vkcv_check_feature(hostQueryReset); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceImagelessFramebufferFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceImagelessFramebufferFeatures); + + vkcv_check_feature(imagelessFramebuffer); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMultiviewFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceMultiviewFeatures); + + vkcv_check_feature(multiview); + vkcv_check_feature(multiviewGeometryShader); + vkcv_check_feature(multiviewTessellationShader); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceProtectedMemoryFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceProtectedMemoryFeatures); + + vkcv_check_feature(protectedMemory); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceSamplerYcbcrConversionFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceSamplerYcbcrConversionFeatures); + + vkcv_check_feature(samplerYcbcrConversion); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceScalarBlockLayoutFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceScalarBlockLayoutFeatures); + + vkcv_check_feature(scalarBlockLayout); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures); + + vkcv_check_feature(separateDepthStencilLayouts); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderAtomicInt64Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderAtomicInt64Features); + + vkcv_check_feature(shaderBufferInt64Atomics); + vkcv_check_feature(shaderSharedInt64Atomics); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderFloat16Int8Features &features, bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderFloat16Int8Features); + + vkcv_check_feature(shaderFloat16); + vkcv_check_feature(shaderInt8); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures); + + vkcv_check_feature(shaderSubgroupExtendedTypes); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceTimelineSemaphoreFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceTimelineSemaphoreFeatures); + + vkcv_check_feature(timelineSemaphore); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceUniformBufferStandardLayoutFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceUniformBufferStandardLayoutFeatures); + + vkcv_check_feature(uniformBufferStandardLayout); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVariablePointersFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVariablePointersFeatures); + + vkcv_check_feature(variablePointers); + vkcv_check_feature(variablePointersStorageBuffer); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkanMemoryModelFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVulkanMemoryModelFeatures); + + vkcv_check_feature(vulkanMemoryModel); + vkcv_check_feature(vulkanMemoryModelDeviceScope); + vkcv_check_feature(vulkanMemoryModelAvailabilityVisibilityChains); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV &features, bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceMeshShaderFeaturesNV); + + vkcv_check_feature(taskShader); + vkcv_check_feature(meshShader); + + return true; + } + + vk::BaseOutStructure* FeatureManager::findFeatureStructure(vk::StructureType type) const { + for (auto& base : m_featuresExtensions) { + if (base->sType == type) { + return base; + } + } + + return nullptr; + } + + 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 std::string& extension) const { + for (const auto& supported : m_supportedExtensions) { + if (0 == strcmp(supported, extension.c_str())) { + return true; + } + } + + return false; + } + + bool FeatureManager::useExtension(const std::string& extension, bool required) { + const char* clone = strclone(extension.c_str()); + + if (!clone) { + vkcv_log(LogLevel::WARNING, "Extension '%s' is not valid", extension.c_str()); + return false; + } + + if (!isExtensionSupported(extension)) { + vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), "Extension '%s' is not supported", + extension.c_str()); + + delete[] clone; + return false; + } + + m_activeExtensions.push_back(clone); + return true; + } + + bool FeatureManager::isExtensionActive(const std::string& extension) const { + for (const auto& supported : m_activeExtensions) { + if (0 == strcmp(supported, extension.c_str())) { + 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) { + vk::PhysicalDeviceFeatures features = m_featuresBase.features; + + featureFunction(features); + + if (!checkSupport(features, required)) { + return false; + } + + m_featuresBase.features = features; + return true; + } + + const vk::PhysicalDeviceFeatures2& FeatureManager::getFeatures() const { + return m_featuresBase; + } + +} diff --git a/src/vkcv/Features.cpp b/src/vkcv/Features.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60616685f67084b6875ce14bf09f524a9127688f --- /dev/null +++ b/src/vkcv/Features.cpp @@ -0,0 +1,62 @@ + +#include "vkcv/Features.hpp" + +namespace vkcv { + + Features::Features(const std::initializer_list<std::string>& list) : m_features() { + for (const auto& extension : list) { + requireExtension(extension); + } + } + + void Features::requireExtension(const std::string& extension) { + m_features.emplace_back([extension](FeatureManager& featureManager) { + return featureManager.useExtension(extension, true); + }); + } + + void Features::requireExtensionFeature(const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager& featureManager) { + if (featureManager.useExtension(extension, true)) { + return featureManager.useFeatures(featureFunction, true); + } else { + return false; + } + }); + } + + void Features::requireFeature(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager& featureManager) { + return featureManager.useFeatures(featureFunction, true); + }); + } + + void Features::tryExtension(const std::string& extension) { + m_features.emplace_back([extension](FeatureManager& featureManager) { + return featureManager.useExtension(extension, false); + }); + } + + void Features::tryExtensionFeature(const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager& featureManager) { + if (featureManager.useExtension(extension, false)) { + return featureManager.useFeatures(featureFunction, false); + } else { + return false; + } + }); + } + + void Features::tryFeature(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager& featureManager) { + return featureManager.useFeatures(featureFunction, false); + }); + } + + const std::vector<Feature>& Features::getList() const { + return m_features; + } + +}