From ccd8aa7e6de5e97c44f98ea378f5daff4a1ee445 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Sun, 29 Aug 2021 17:18:54 +0200
Subject: [PATCH] [#101] Added class to store feature and extension
 requirements before core creation

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 config/Sources.cmake                      |  3 +
 include/vkcv/Context.hpp                  |  6 +-
 include/vkcv/Core.hpp                     | 20 +++---
 include/vkcv/FeatureManager.hpp           |  6 +-
 include/vkcv/Features.hpp                 | 80 +++++++++++++++++++++++
 projects/first_mesh/src/main.cpp          |  6 +-
 projects/first_scene/src/main.cpp         |  6 +-
 projects/first_triangle/src/main.cpp      |  6 +-
 projects/mesh_shader/src/main.cpp         | 11 +++-
 projects/particle_simulation/src/main.cpp |  6 +-
 projects/voxelization/src/main.cpp        | 10 ++-
 src/vkcv/Context.cpp                      | 18 ++---
 src/vkcv/Core.cpp                         |  8 +--
 src/vkcv/FeatureManager.cpp               | 17 ++---
 src/vkcv/Features.cpp                     | 56 ++++++++++++++++
 15 files changed, 204 insertions(+), 55 deletions(-)
 create mode 100644 include/vkcv/Features.hpp
 create mode 100644 src/vkcv/Features.cpp

diff --git a/config/Sources.cmake b/config/Sources.cmake
index bdcbc1c6..ea95d152 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/Features.hpp
+		${vkcv_source}/vkcv/Features.cpp
+		
 		${vkcv_include}/vkcv/FeatureManager.hpp
 		${vkcv_source}/vkcv/FeatureManager.cpp
 		
diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp
index 76d60525..1160857c 100644
--- a/include/vkcv/Context.hpp
+++ b/include/vkcv/Context.hpp
@@ -5,7 +5,7 @@
 
 #include "QueueManager.hpp"
 #include "DrawcallRecording.hpp"
-#include "FeatureManager.hpp"
+#include "Features.hpp"
 
 namespace vkcv
 {
@@ -45,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:
         /**
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 7f23c384..e7f6688b 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.
diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp
index 9e472a38..cf945d74 100644
--- a/include/vkcv/FeatureManager.hpp
+++ b/include/vkcv/FeatureManager.hpp
@@ -93,12 +93,12 @@ namespace vkcv {
 		FeatureManager& operator=(FeatureManager&& other) noexcept;
 		
 		[[nodiscard]]
-		bool isExtensionSupported(const char *extension) const;
+		bool isExtensionSupported(const std::string& extension) const;
 		
-		bool useExtension(const char *extension, bool required = true);
+		bool useExtension(const std::string& extension, bool required = true);
 		
 		[[nodiscard]]
-		bool isExtensionActive(const char *extension) const;
+		bool isExtensionActive(const std::string& extension) const;
 		
 		[[nodiscard]]
 		const std::vector<const char*>& getActiveExtensions() const;
diff --git a/include/vkcv/Features.hpp b/include/vkcv/Features.hpp
new file mode 100644
index 00000000..f1329106
--- /dev/null
+++ b/include/vkcv/Features.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <functional>
+#include <vector>
+
+#include "FeatureManager.hpp"
+
+namespace vkcv {
+	
+	typedef std::function<bool(FeatureManager&)> Feature;
+	
+	class Features {
+	private:
+		std::vector<Feature> m_features;
+		
+	public:
+		Features() = default;
+		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/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 731d3e56..f784c130 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -18,14 +18,16 @@ int main(int argc, const char** argv) {
 		windowHeight,
 		true
 	);
+	
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
 
 	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" }
+		features
 	);
 
 	vkcv::asset::Scene mesh;
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 527eba8c..6ac367e4 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -29,13 +29,15 @@ int main(int argc, const char** argv) {
 	
 	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
 
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+	
 	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" }
+		features
 	);
 	
 	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 253efad4..de5d345f 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -16,14 +16,16 @@ int main(int argc, const char** argv) {
 		windowHeight,
 		false
 	);
+	
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_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" }
+		features
 	);
 
 	const auto& context = core.getContext();
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 3a94de58..18d705ec 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 07ba6b19..1aafa57f 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -24,13 +24,15 @@ int main(int argc, const char **argv) {
 
     vkcv::camera::CameraManager cameraManager(window);
 
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_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"}
+			features
     );
 
     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 e7f9caa4..3c23c7ea 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 75d409d7..f48fad1a 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -190,8 +190,8 @@ namespace vkcv
 	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();
@@ -282,8 +282,8 @@ namespace vkcv
 			features.setShaderInt16(true);
 		});
 		
-		for (const auto& extension : deviceExtensions) {
-			featureManager.useExtension(extension);
+		for (const auto& feature : features.getList()) {
+			feature(featureManager);
 		}
 		
 		const auto& extensions = featureManager.getActiveExtensions();
@@ -313,16 +313,6 @@ namespace vkcv
 		deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
 #endif
 		
-		for (const auto& extension : deviceExtensions) {
-			if (0 == strcmp(extension, VK_NV_MESH_SHADER_EXTENSION_NAME)) {
-				featureManager.useFeatures<vk::PhysicalDeviceMeshShaderFeaturesNV>(
-						[](vk::PhysicalDeviceMeshShaderFeaturesNV& features) {
-					features.setTaskShader(true);
-					features.setMeshShader(true);
-				});
-			}
-		}
-		
 		deviceCreateInfo.setPNext(&(featureManager.getFeatures()));
 		
 		vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 69da1106..1ae1c316 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);
diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp
index 416858c8..18307999 100644
--- a/src/vkcv/FeatureManager.cpp
+++ b/src/vkcv/FeatureManager.cpp
@@ -372,9 +372,9 @@ m_physicalDevice.getFeatures2(&query)
 		return *this;
 	}
 	
-	bool FeatureManager::isExtensionSupported(const char *extension) const {
+	bool FeatureManager::isExtensionSupported(const std::string& extension) const {
 		for (const auto& supported : m_supportedExtensions) {
-			if (0 == strcmp(supported, extension)) {
+			if (0 == strcmp(supported, extension.c_str())) {
 				return true;
 			}
 		}
@@ -382,16 +382,17 @@ m_physicalDevice.getFeatures2(&query)
 		return false;
 	}
 	
-	bool FeatureManager::useExtension(const char *extension, bool required) {
-		const char* clone = strclone(extension);
+	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);
+			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);
+			vkcv_log((required? LogLevel::ERROR : LogLevel::WARNING), "Extension '%s' is not supported",
+					 extension.c_str());
 			
 			delete[] clone;
 			return false;
@@ -401,9 +402,9 @@ m_physicalDevice.getFeatures2(&query)
 		return true;
 	}
 	
-	bool FeatureManager::isExtensionActive(const char *extension) const {
+	bool FeatureManager::isExtensionActive(const std::string& extension) const {
 		for (const auto& supported : m_activeExtensions) {
-			if (0 == strcmp(supported, extension)) {
+			if (0 == strcmp(supported, extension.c_str())) {
 				return true;
 			}
 		}
diff --git a/src/vkcv/Features.cpp b/src/vkcv/Features.cpp
new file mode 100644
index 00000000..cdfbcabb
--- /dev/null
+++ b/src/vkcv/Features.cpp
@@ -0,0 +1,56 @@
+
+#include "vkcv/Features.hpp"
+
+namespace vkcv {
+	
+	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;
+	}
+
+}
-- 
GitLab