From 42d09d6b74ee6f75d1f2fad929f48e723e83dfac Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Sun, 9 May 2021 13:57:05 +0200
Subject: [PATCH] Revert "Initial implementation of a core class object."

This reverts commit 7df91724e73ca1347a2edc0ce26f23c211d2d705.
---
 config/Sources.cmake                 |  10 +-
 include/vkcv/Context.hpp             | 149 ++++++++++++
 include/vkcv/Core.hpp                | 129 -----------
 include/vkcv/Handles.hpp             |  16 --
 include/vkcv/Window.hpp              |   3 -
 projects/first_triangle/src/main.cpp |  30 +--
 src/vkcv/Context.cpp                 | 256 +++++++++++++++++++++
 src/vkcv/Core.cpp                    | 325 ---------------------------
 src/vkcv/CoreManager.cpp             |  31 +++
 src/vkcv/CoreManager.hpp             |  22 ++
 src/vkcv/Handles.cpp                 |   1 -
 src/vkcv/Window.cpp                  |  16 +-
 12 files changed, 468 insertions(+), 520 deletions(-)
 create mode 100644 include/vkcv/Context.hpp
 delete mode 100644 include/vkcv/Core.hpp
 delete mode 100644 include/vkcv/Handles.hpp
 create mode 100644 src/vkcv/Context.cpp
 delete mode 100644 src/vkcv/Core.cpp
 create mode 100644 src/vkcv/CoreManager.cpp
 create mode 100644 src/vkcv/CoreManager.hpp
 delete mode 100644 src/vkcv/Handles.cpp

diff --git a/config/Sources.cmake b/config/Sources.cmake
index e390e997..7c1f5990 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -1,12 +1,10 @@
 
 # adding all source files and header files of the framework:
 set(vkcv_sources
-		${vkcv_include}/vkcv/Core.hpp
-		${vkcv_source}/vkcv/Core.cpp
-
-		${vkcv_include}/vkcv/Handles.hpp
-		${vkcv_source}/vkcv/Handles.cpp
-
+		${vkcv_include}/vkcv/Context.hpp
+		${vkcv_source}/vkcv/Context.cpp
 		${vkcv_include}/vkcv/Window.hpp
 		${vkcv_source}/vkcv/Window.cpp
+		${vkcv_source}/vkcv/CoreManager.hpp
+		${vkcv_source}/vkcv/CoreManager.cpp
 )
diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp
new file mode 100644
index 00000000..1d11f827
--- /dev/null
+++ b/include/vkcv/Context.hpp
@@ -0,0 +1,149 @@
+#pragma once
+/**
+ * @authors Tobias Frisch, Vanessa Karolek, Katharina Krämer, Sebastian Gaida
+ * @file src/vkcv/Context.hpp
+ * @brief Context class to handle instance, physical-device and device
+ */
+
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+
+	class Context final {
+	private:
+		vk::Instance m_instance;
+		vk::PhysicalDevice m_physicalDevice;
+		vk::Device m_device;
+
+		/**
+		 * Constructor of #Context requires an @p instance, a @p physicalDevice and a @p device.
+		 *
+		 * @param instance Vulkan-Instance
+		 * @param physicalDevice Vulkan-PhysicalDevice
+		 * @param device Vulkan-Device
+		 */
+		Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device);
+
+	public:
+		/**
+		 * Copy-constructor of #Context is deleted!
+		 *
+		 * @param other Other instance of #Context
+		 */
+		Context(const Context &other) = delete;
+		
+		/**
+		 * Move-constructor of #Context uses default behavior!
+		 *
+		 * @param other Other instance of #Context
+		 */
+		Context(Context &&other) = default;
+
+		/**
+		 * Get the Vulkan handle for the instance.
+		 *
+		 * @return Vulkan-Instance
+		 */
+		[[nodiscard]]
+		const vk::Instance& getInstance() const;
+		
+		/**
+		 * Get the Vulkan handle for the physical-device.
+		 *
+		 * @return Vulkan-PhysicalDevice
+		 */
+		[[nodiscard]]
+		const vk::PhysicalDevice& getPhysicalDevice() const;
+		
+		/**
+		 * Get the Vulkan handle for the device.
+		 *
+		 * @return Vulkan-Device
+		 */
+		[[nodiscard]]
+		const vk::Device& getDevice() const;
+
+		/**
+		 * Destructor of #Context
+		 */
+		virtual ~Context();
+
+		/**
+		 * Copy-operator of #Context is deleted!
+		 *
+		 * @param other Other instance of #Context
+		 * @return Reference to itself
+		 */
+		Context& operator=(const Context &other) = delete;
+		
+		/**
+		 * Move-operator of #Context uses default behavior!
+		 *
+		 * @param other Other instance of #Context
+		 * @return Reference to itself
+		 */
+		Context& operator=(Context &&other) = default;
+
+		/**
+		 * Creates a #Context with given @p applicationName and @p applicationVersion for your application.
+		 *
+		 * It is also possible to require a specific amount of queues, ask for specific queue-flags or
+		 * extensions. This function will take care of the required arguments as best as possible.
+		 *
+		 * To pass a valid version for your application, you should use #VK_MAKE_VERSION().
+		 *
+		 * @param[in] applicationName Name of the application
+		 * @param[in] applicationVersion Version of the application
+		 * @param[in] queueCount (optional) Amount of queues which is requested
+		 * @param[in] queueFlags (optional) Requested flags of queues
+		 * @param[in] instanceExtensions (optional) Requested instance extensions
+		 * @param[in] deviceExtensions (optional) Requested device extensions
+		 * @return New instance of #Context
+		 */
+		static Context create(const char* applicationName, uint32_t applicationVersion, uint32_t queueCount = 1, std::vector<vk::QueueFlagBits> queueFlags = {}, std::vector<const char*> instanceExtensions = {}, std::vector<const char*> deviceExtensions = {});
+
+		/**
+		 * @brief With the help of the reference "supported" all elements in "check" checked,
+		 * if they are supported by the physical device.
+		 * @param supported The reference that can be used to check "check"
+		 * @param check The elements to be checked
+		 * @return True, if all elements in "check" are supported
+		*/
+		static bool checkSupport(std::vector<const char*> &supported, std::vector<const char*> &check);
+
+		/**
+		 * @brief Gets all extensions required, i.e. GLFW and advanced debug extensions.
+		 * @return The required extensions
+		*/
+		static std::vector<const char*> getRequiredExtensions();
+
+		/**
+		 * @brief All existing physical devices will be evaluated by deviceScore.
+		 * @param instance The instance
+		 * @return The optimal physical device
+		 * @see Context.deviceScore
+		*/
+		static vk::PhysicalDevice pickPhysicalDevice(vk::Instance& instance);
+
+		/**
+		 * @brief The physical device is evaluated by three categories:
+		 * discrete GPU vs. integrated GPU, amount of queues and its abilities, and VRAM.physicalDevice.
+		 * @param physicalDevice The physical device
+		 * @return Device score as integer
+		*/
+		static int deviceScore(const vk::PhysicalDevice &physicalDevice);
+
+		/**
+		 * @brief Creates a candidate list of queues that all meet the desired flags and then creates the maximum possible number
+		 * of queues. If the number of desired queues is not sufficient, the remaining queues are created from the next
+		 * candidate from the list.
+		 * @param physicalDevice The physical device
+		 * @param queueCount The amount of queues to be created
+		 * @param qPriorities 
+		 * @param queueFlags The abilities which have to be supported by any created queue
+		 * @return 
+		*/
+		static std::vector<vk::DeviceQueueCreateInfo> getQueueCreateInfos(vk::PhysicalDevice& physicalDevice, uint32_t queueCount, std::vector<float>& qPriorities, std::vector<vk::QueueFlagBits> &queueFlags);
+	};
+
+}
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
deleted file mode 100644
index a347380e..00000000
--- a/include/vkcv/Core.hpp
+++ /dev/null
@@ -1,129 +0,0 @@
-#pragma once
-/**
- * @file src/vkcv/Core.hpp
- * @brief Handling of global states regarding dependencies
- */
-
-#include <vulkan/vulkan.hpp>
-#include "vkcv/Handles.hpp"
-
-namespace vkcv
-{
-    // TODO:
-    class Buffer;
-    class Renderpass;
-    class Pipeline;
-
-    class Core final
-    {
-    private:
-        class Context
-        {
-        public:
-            /**
-             * Constructor of #Context requires an @p instance, a @p physicalDevice and a @p device.
-             *
-             * @param instance Vulkan-Instance
-             * @param physicalDevice Vulkan-PhysicalDevice
-             * @param device Vulkan-Device
-             */
-            Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device) noexcept;
-            // explicit destruction of default constructor
-            Context() = delete;
-            // is never called directly
-            ~Context() noexcept;
-
-            Context(const Context &other) = delete; // copy-ctor
-            Context(Context &&other) noexcept; // move-ctor
-
-            Context & operator=(const Context &other) = delete; // copy assignment
-            Context & operator=(Context &&other) noexcept; // move assignment
-
-            const vk::Instance &getInstance() const;
-            const vk::PhysicalDevice &getPhysicalDevice() const;
-            const vk::Device &getDevice() const;
-
-        private:
-            vk::Instance        m_Instance;
-            vk::PhysicalDevice  m_PhysicalDevice;
-            vk::Device          m_Device;
-        } m_Context;
-
-        /**
-         * Constructor of #Core requires an @p context.
-         *
-         * @param context encapsulates various Vulkan objects
-         */
-        explicit Core(Context &&context) noexcept;
-        // explicit destruction of default constructor
-        Core() = delete;
-
-    public:
-        /**
-         * Destructor of #Core destroys the Vulkan objects contained in the core's context.
-         */
-        ~Core() noexcept = default;
-
-        /**
-         * Copy-constructor of #Core is deleted!
-         *
-         * @param other Other instance of #Context
-         */
-        Core(const Core& other) = delete;
-
-        /**
-         * Move-constructor of #Core uses default behavior!
-         *
-         * @param other Other instance of #Context
-         */
-        Core(Core &&other) = delete; // move-ctor
-
-        /**
-         * Copy assignment operator of #Core is deleted!
-         *
-         * @param other Other instance of #Context
-         * @return Reference to itself
-         */
-        Core & operator=(const Core &other) = delete;
-
-        /**
-         * Move assignment operator of #Core uses default behavior!
-         *
-         * @param other Other instance of #Context
-         * @return Reference to itself
-         */
-        Core & operator=(Core &&other) = delete;
-
-        [[nodiscard]]
-        const Context &getContext() const;
-
-        /**
-             * Creates a #Core with given @p applicationName and @p applicationVersion for your application.
-             *
-             * It is also possible to require a specific amount of queues, ask for specific queue-flags or
-             * extensions. This function will take care of the required arguments as best as possible.
-             *
-             * To pass a valid version for your application, you should use #VK_MAKE_VERSION().
-             *
-             * @param[in] applicationName Name of the application
-             * @param[in] applicationVersion Version of the application
-             * @param[in] queueCount (optional) Amount of queues which is requested
-             * @param[in] queueFlags (optional) Requested flags of queues
-             * @param[in] instanceExtensions (optional) Requested instance extensions
-             * @param[in] deviceExtensions (optional) Requested device extensions
-             * @return New instance of #Context
-             */
-        static Core create(const char *applicationName,
-                           uint32_t applicationVersion,
-                           uint32_t queueCount,
-                           std::vector<vk::QueueFlagBits> queueFlags    = {},
-                           std::vector<const char*> instanceExtensions  = {},
-                           std::vector<const char*> deviceExtensions    = {});
-
-        // TODO:
-        BufferHandle createBuffer(const Buffer &buf);
-        PassHandle createRenderPass(const Renderpass &pass) ;
-        PipelineHandle createPipeline(const Pipeline &pipeline);
-
-    };
-}
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
deleted file mode 100644
index 4ec2bc05..00000000
--- a/include/vkcv/Handles.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-/**
- * @authors Artur Wasmut
- * @file src/vkcv/Handles.cpp
- * @brief Central header file for all possible handles that the framework will hand out.
- */
-
-#include <cstdint>
-
-namespace vkcv
-{
-    // Handle returned for any buffer created with the core/context objects
-    struct BufferHandle     {uint64_t id;};
-    struct PassHandle       {uint64_t id;};
-    struct PipelineHandle   {uint64_t id;};
-}
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index 080e5535..62440d90 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -8,9 +8,6 @@
 #define GLFW_INCLUDE_VULKAN
 #include <GLFW/glfw3.h>
 
-#define NOMINMAX
-#include <algorithm>
-
 namespace vkcv {
 
     class Window final {
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 0c981a25..cc592f46 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -1,5 +1,5 @@
 #include <iostream>
-#include <vkcv/Core.hpp>
+#include <vkcv/Context.hpp>
 #include <vkcv/Window.hpp>
 
 int main(int argc, const char** argv) {
@@ -10,14 +10,13 @@ int main(int argc, const char** argv) {
         600,
 		false
 	);
-	vkcv::Core core = vkcv::Core::create(
+	vkcv::Context context = vkcv::Context::create(
             applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		20,
 		{vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer}
 	);
 
-	const auto &context = core.getContext();
 	const vk::Instance& instance = context.getInstance();
 	const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
 	const vk::Device& device = context.getDevice();
@@ -33,30 +32,7 @@ int main(int argc, const char** argv) {
 		default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl;
 	}
 
-	/*
-	 * BufferHandle triangleVertices = core.createBuffer(vertices);
-	 * BufferHandle triangleIndices = core.createBuffer(indices);
-	 *
-	 * // triangle Model creation goes here
-	 *
-	 *
-	 * // attachment creation goes here
-	 * PassHandle trianglePass = core.CreatePass(presentationPass);
-	 *
-	 * // shader creation goes here
-	 * // material creation goes here
-	 *
-	 * PipelineHandle trianglePipeline = core.CreatePipeline(trianglePipeline);
-	 */
-
-	while (window.isWindowOpen())
-	{
-        // core.beginFrame(); or something like that
-	    // core.execute(trianglePass, trianglePipeline, triangleModel);
-	    // core.endFrame(); or something like that
-
-	    // TBD: synchronization
-
+	while (window.isWindowOpen()) {
 		window.pollEvents();
 	}
 	return 0;
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
new file mode 100644
index 00000000..4500a135
--- /dev/null
+++ b/src/vkcv/Context.cpp
@@ -0,0 +1,256 @@
+/**
+ * @authors Tobias Frisch, Vanessa Karolek, Katharina Krämer, Sebastian Gaida
+ * @file src/vkcv/Context.cpp
+ * @brief Context class to handle instance, physical-device and device
+ */
+
+#include "vkcv/Context.hpp"
+#include "CoreManager.hpp"
+
+namespace vkcv {
+
+	Context::Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device)
+		: m_instance(instance), m_physicalDevice(physicalDevice), m_device(device)
+	{}
+
+	Context::~Context() {
+		m_device.destroy();
+		m_instance.destroy();
+		vkcv::terminateGLFW();
+	}
+
+	Context Context::create(const char* applicationName, uint32_t applicationVersion, uint32_t queueCount, std::vector<vk::QueueFlagBits> queueFlags, std::vector<const char*> instanceExtensions, std::vector<const char*> deviceExtensions) {
+		vkcv::initGLFW();
+
+		// check for layer support
+		
+		const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties();
+		
+		std::vector<const char*> supportedLayers;
+		supportedLayers.reserve(layerProperties.size());
+		
+		for (auto& elem : layerProperties) {
+			supportedLayers.push_back(elem.layerName);
+		}
+
+// if in debug mode, check if validation layers are supported. Enable them if supported
+#ifndef NDEBUG
+		std::vector<const char*> validationLayers = {
+			"VK_LAYER_KHRONOS_validation"
+		};
+		
+		if (!Context::checkSupport(supportedLayers, validationLayers)) {
+			throw std::runtime_error("Validation layers requested but not available!");
+		}
+#endif
+		
+		// check for extension support
+		std::vector<vk::ExtensionProperties> instanceExtensionProperties = vk::enumerateInstanceExtensionProperties();
+		
+		std::vector<const char*> supportedExtensions;
+		supportedExtensions.reserve(instanceExtensionProperties.size());
+		
+		for (auto& elem : instanceExtensionProperties) {
+			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 = Context::getRequiredExtensions();
+		instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end());
+
+		const vk::ApplicationInfo applicationInfo(
+			applicationName,
+			applicationVersion,
+			"vkCV",
+			VK_MAKE_VERSION(0, 0, 1),
+			VK_HEADER_VERSION_COMPLETE
+		);
+
+		vk::InstanceCreateInfo instanceCreateInfo(
+			vk::InstanceCreateFlags(),
+			&applicationInfo,
+			0,
+			nullptr,
+			static_cast<uint32_t>(instanceExtensions.size()),
+			instanceExtensions.data()
+		);
+
+#ifndef NDEBUG
+		instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
+		instanceCreateInfo.ppEnabledLayerNames = validationLayers.data();
+#endif
+
+		vk::Instance instance = vk::createInstance(instanceCreateInfo);
+
+		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!");
+		}
+
+		//vector to define the queue priorities
+		std::vector<float> qPriorities;
+		qPriorities.resize(queueCount, 1.f); // all queues have the same priorities
+
+		// create required queues
+		std::vector<vk::DeviceQueueCreateInfo> qCreateInfos = getQueueCreateInfos(physicalDevice, queueCount, qPriorities,queueFlags);
+
+		vk::DeviceCreateInfo deviceCreateInfo(
+			vk::DeviceCreateFlags(),
+			qCreateInfos.size(),
+			qCreateInfos.data(),
+			0,
+			nullptr,
+			deviceExtensions.size(),
+			deviceExtensions.data(),
+			nullptr		// Should our device use some features??? If yes: TODO
+		);
+
+#ifndef NDEBUG
+		deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
+		deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
+#endif
+
+
+		vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
+		// TODO: implement device.getQueue() to access the queues, if needed
+
+		return Context(instance, physicalDevice, device);
+	}
+
+	const vk::Instance& Context::getInstance() const {
+		return m_instance;
+	}
+
+	const vk::PhysicalDevice& Context::getPhysicalDevice() const {
+		return m_physicalDevice;
+	}
+
+	const vk::Device& Context::getDevice() const {
+		return m_device;
+	}
+
+	vk::PhysicalDevice Context::pickPhysicalDevice(vk::Instance& instance) {
+		vk::PhysicalDevice phyDevice;
+		std::vector<vk::PhysicalDevice> devices = instance.enumeratePhysicalDevices();
+
+		if (devices.empty()) {
+			throw std::runtime_error("failed to find GPUs with Vulkan support!");
+		}
+
+		int max_score = -1;
+		for (const auto& device : devices) {
+			int score = deviceScore(device);
+			if (score > max_score) {
+				max_score = score;
+				phyDevice = device;
+			}
+		}
+
+		if (max_score == -1) {
+			throw std::runtime_error("failed to find a suitable GPU!");
+		}
+
+		return phyDevice;
+	}
+
+	int Context::deviceScore(const vk::PhysicalDevice& physicalDevice) {
+		int score = 0;
+		vk::PhysicalDeviceProperties properties = physicalDevice.getProperties();
+		std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+
+		// for every queue family compute queue flag bits and the amount of queues
+		for (const auto& qFamily : qFamilyProperties) {
+			uint32_t qCount = qFamily.queueCount;
+			uint32_t bitCount = (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eSparseBinding) != 0);
+			score += static_cast<int>(qCount * bitCount);
+		}
+
+		// compute the VRAM of the physical device
+		vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties();
+		int vram = static_cast<int>(memoryProperties.memoryHeaps[0].size / 1E9);
+		score *= vram;
+
+		if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
+			score *= 2;
+		}
+		else if (properties.deviceType != vk::PhysicalDeviceType::eIntegratedGpu) {
+			score = -1;
+		}
+
+		return score;
+	}
+
+	std::vector<vk::DeviceQueueCreateInfo> Context::getQueueCreateInfos(vk::PhysicalDevice& physicalDevice, uint32_t queueCount,std::vector<float> &qPriorities, std::vector<vk::QueueFlagBits>& queueFlags) {
+		std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
+		std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+		std::vector<vk::QueueFamilyProperties> qFamilyCandidates;
+
+		// search for queue families which support the desired queue flag bits
+		for (auto& qFamily : qFamilyProperties) {
+			bool supported = true;
+			for (auto qFlag : queueFlags) {
+				supported = supported && (static_cast<uint32_t>(qFlag & qFamily.queueFlags) != 0);
+			}
+			if (supported) {
+				qFamilyCandidates.push_back(qFamily);
+			}
+		}
+
+		uint32_t create = queueCount;
+		for (uint32_t i = 0; i < qFamilyCandidates.size() && create > 0; i++) {
+			const int maxCreatableQueues = std::min(create, qFamilyCandidates[i].queueCount);		
+			vk::DeviceQueueCreateInfo qCreateInfo(
+				vk::DeviceQueueCreateFlags(),
+				i,
+				maxCreatableQueues,
+				qPriorities.data()
+			);
+			queueCreateInfos.push_back(qCreateInfo);
+			create -= maxCreatableQueues;
+		}
+
+		return queueCreateInfos;
+	}
+
+	bool Context::checkSupport(std::vector<const char*>& supported, std::vector<const char*>& check) {
+		for (auto checkElem : check) {
+			bool found = false;
+			for (auto supportedElem : supported) {
+				if (strcmp(supportedElem, checkElem) == 0) {
+					found = true;
+					break;
+				}
+			}
+			if (!found)
+				return false;
+		}
+		return true;
+	}
+
+	std::vector<const char*> Context::getRequiredExtensions() {
+		uint32_t glfwExtensionCount = 0;
+		const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
+		std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
+
+#ifndef NDEBUG
+	extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+#endif
+
+		return extensions;
+	}
+}
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
deleted file mode 100644
index 962f5560..00000000
--- a/src/vkcv/Core.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/**
- * @authors Sebastian Gaida
- * @file src/vkcv/CoreManager.cpp
- * @brief Handling of global states regarding dependencies
- */
-
-#include "vkcv/Core.hpp"
-
-namespace vkcv
-{
-    /**
-     * @brief The physical device is evaluated by three categories:
-     * discrete GPU vs. integrated GPU, amount of queues and its abilities, and VRAM.physicalDevice.
-     * @param physicalDevice The physical device
-     * @return Device score as integer
-    */
-    int deviceScore(const vk::PhysicalDevice& physicalDevice)
-    {
-        int score = 0;
-        vk::PhysicalDeviceProperties properties = physicalDevice.getProperties();
-        std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
-
-        // for every queue family compute queue flag bits and the amount of queues
-        for (const auto& qFamily : qFamilyProperties) {
-            uint32_t qCount = qFamily.queueCount;
-            uint32_t bitCount = (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0)
-                                + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0)
-                                + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0)
-                                + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eSparseBinding) != 0);
-            score += static_cast<int>(qCount * bitCount);
-        }
-
-        // compute the VRAM of the physical device
-        vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties();
-        auto vram = static_cast<int>(memoryProperties.memoryHeaps[0].size / static_cast<uint32_t>(1E9));
-        score *= vram;
-
-        if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
-            score *= 2;
-        }
-        else if (properties.deviceType != vk::PhysicalDeviceType::eIntegratedGpu) {
-            score = -1;
-        }
-
-        return score;
-    }
-
-    /**
-     * @brief All existing physical devices will be evaluated by deviceScore.
-     * @param instance The instance
-     * @return The optimal physical device
-     * @see Context.deviceScore
-    */
-    vk::PhysicalDevice pickPhysicalDevice(vk::Instance& instance)
-    {
-        vk::PhysicalDevice phyDevice;
-        std::vector<vk::PhysicalDevice> devices = instance.enumeratePhysicalDevices();
-
-        if (devices.empty()) {
-            throw std::runtime_error("failed to find GPUs with Vulkan support!");
-        }
-
-        int max_score = -1;
-        for (const auto& device : devices) {
-            int score = deviceScore(device);
-            if (score > max_score) {
-                max_score = score;
-                phyDevice = device;
-            }
-        }
-
-        if (max_score == -1) {
-            throw std::runtime_error("failed to find a suitable GPU!");
-        }
-
-        return phyDevice;
-    }
-
-
-    /**
-     * @brief Creates a candidate list of queues that all meet the desired flags and then creates the maximum possible number
-     * of queues. If the number of desired queues is not sufficient, the remaining queues are created from the next
-     * candidate from the list.
-     * @param physicalDevice The physical device
-     * @param queueCount The amount of queues to be created
-     * @param qPriorities
-     * @param queueFlags The abilities which have to be supported by any created queue
-     * @return
-    */
-    std::vector<vk::DeviceQueueCreateInfo> getQueueCreateInfos(vk::PhysicalDevice& physicalDevice,
-                                                               uint32_t queueCount,
-                                                               std::vector<float> &qPriorities,
-                                                               std::vector<vk::QueueFlagBits>& queueFlags)
-    {
-        std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
-        std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
-        std::vector<vk::QueueFamilyProperties> qFamilyCandidates;
-
-        // search for queue families which support the desired queue flag bits
-        for (auto& qFamily : qFamilyProperties) {
-            bool supported = true;
-            for (auto qFlag : queueFlags) {
-                supported = supported && (static_cast<uint32_t>(qFlag & qFamily.queueFlags) != 0);
-            }
-            if (supported) {
-                qFamilyCandidates.push_back(qFamily);
-            }
-        }
-
-        uint32_t create = queueCount;
-        for (uint32_t i = 0; i < qFamilyCandidates.size() && create > 0; i++) {
-            const uint32_t maxCreatableQueues = std::min(create, qFamilyCandidates[i].queueCount);
-            vk::DeviceQueueCreateInfo qCreateInfo(
-                    vk::DeviceQueueCreateFlags(),
-                    i,
-                    maxCreatableQueues,
-                    qPriorities.data()
-            );
-            queueCreateInfos.push_back(qCreateInfo);
-            create -= maxCreatableQueues;
-        }
-
-        return queueCreateInfos;
-    }
-
-    /**
-     * @brief With the help of the reference "supported" all elements in "check" checked,
-     * if they are supported by the physical device.
-     * @param supported The reference that can be used to check "check"
-     * @param check The elements to be checked
-     * @return True, if all elements in "check" are supported
-    */
-    bool checkSupport(std::vector<const char*>& supported, std::vector<const char*>& check)
-    {
-        for (auto checkElem : check) {
-            bool found = false;
-            for (auto supportedElem : supported) {
-                if (strcmp(supportedElem, checkElem) == 0) {
-                    found = true;
-                    break;
-                }
-            }
-            if (!found)
-                return false;
-        }
-        return true;
-    }
-
-    Core Core::create(const char *applicationName,
-                      uint32_t applicationVersion,
-                      uint32_t queueCount,
-                      std::vector<vk::QueueFlagBits> queueFlags,
-                      std::vector<const char *> instanceExtensions,
-                      std::vector<const char *> deviceExtensions)
-    {
-
-        // check for layer support
-
-        const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties();
-
-        std::vector<const char*> supportedLayers;
-        supportedLayers.reserve(layerProperties.size());
-
-        for (auto& elem : layerProperties) {
-            supportedLayers.push_back(elem.layerName);
-        }
-
-// if in debug mode, check if validation layers are supported. Enable them if supported
-#ifndef NDEBUG
-        std::vector<const char*> validationLayers = {
-                "VK_LAYER_KHRONOS_validation"
-        };
-
-        if (!checkSupport(supportedLayers, validationLayers)) {
-            throw std::runtime_error("Validation layers requested but not available!");
-        }
-#endif
-
-        // check for extension support
-        std::vector<vk::ExtensionProperties> instanceExtensionProperties = vk::enumerateInstanceExtensionProperties();
-
-        std::vector<const char*> supportedExtensions;
-        supportedExtensions.reserve(instanceExtensionProperties.size());
-
-        for (auto& elem : instanceExtensionProperties) {
-            supportedExtensions.push_back(elem.extensionName);
-        }
-
-        if (!checkSupport(supportedExtensions, instanceExtensions)) {
-            throw std::runtime_error("The requested instance extensions are not supported!");
-        }
-
-#ifndef NDEBUG
-        instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
-#endif
-
-        const vk::ApplicationInfo applicationInfo(
-                applicationName,
-                applicationVersion,
-                "vkCV",
-                VK_MAKE_VERSION(0, 0, 1),
-                VK_HEADER_VERSION_COMPLETE
-        );
-
-        vk::InstanceCreateInfo instanceCreateInfo(
-                vk::InstanceCreateFlags(),
-                &applicationInfo,
-                0,
-                nullptr,
-                static_cast<uint32_t>(instanceExtensions.size()),
-                instanceExtensions.data()
-        );
-
-#ifndef NDEBUG
-        instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
-        instanceCreateInfo.ppEnabledLayerNames = validationLayers.data();
-#endif
-
-        vk::Instance instance = vk::createInstance(instanceCreateInfo);
-
-        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!");
-        }
-
-        //vector to define the queue priorities
-        std::vector<float> qPriorities;
-        qPriorities.resize(queueCount, 1.f); // all queues have the same priorities
-
-        // create required queues
-        std::vector<vk::DeviceQueueCreateInfo> qCreateInfos = getQueueCreateInfos(physicalDevice, queueCount, qPriorities,queueFlags);
-
-        vk::DeviceCreateInfo deviceCreateInfo(
-                vk::DeviceCreateFlags(),
-                qCreateInfos.size(),
-                qCreateInfos.data(),
-                0,
-                nullptr,
-                deviceExtensions.size(),
-                deviceExtensions.data(),
-                nullptr		// Should our device use some features??? If yes: TODO
-        );
-
-#ifndef NDEBUG
-        deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
-        deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
-#endif
-
-
-        vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
-        // TODO: implement device.getQueue() to access the queues, if needed
-        Context context(instance, physicalDevice, device);
-
-        return Core(std::move(context));
-    }
-
-    const Core::Context &Core::getContext() const
-    {
-        return m_Context;
-    }
-
-    Core::Core(Core::Context &&context) noexcept :
-            m_Context(std::move(context))
-    {}
-
-    Core::Context::Context(vk::Instance instance,
-                           vk::PhysicalDevice physicalDevice,
-                           vk::Device device) noexcept :
-    m_Instance{instance},
-    m_PhysicalDevice{physicalDevice},
-    m_Device{device}
-    {}
-
-    Core::Context::~Context() noexcept
-    {
-        m_Device.destroy();
-        m_Instance.destroy();
-    }
-
-    const vk::Instance &Core::Context::getInstance() const
-    {
-        return m_Instance;
-    }
-
-    const vk::PhysicalDevice &Core::Context::getPhysicalDevice() const
-    {
-        return m_PhysicalDevice;
-    }
-
-    const vk::Device &Core::Context::getDevice() const
-    {
-        return m_Device;
-    }
-
-    Core::Context::Context(Core::Context &&other) noexcept:
-    m_Instance(other.m_Instance),
-    m_PhysicalDevice(other.m_PhysicalDevice),
-    m_Device(other.m_Device)
-    {
-        other.m_Instance        = nullptr;
-        other.m_PhysicalDevice  = nullptr;
-        other.m_Device          = nullptr;
-    }
-
-    Core::Context &Core::Context::operator=(Core::Context &&other) noexcept
-    {
-        m_Instance          = other.m_Instance;
-        m_PhysicalDevice    = other.m_PhysicalDevice;
-        m_Device            = other.m_Device;
-
-        other.m_Instance        = nullptr;
-        other.m_PhysicalDevice  = nullptr;
-        other.m_Device          = nullptr;
-
-        return *this;
-    }
-}
diff --git a/src/vkcv/CoreManager.cpp b/src/vkcv/CoreManager.cpp
new file mode 100644
index 00000000..471075e3
--- /dev/null
+++ b/src/vkcv/CoreManager.cpp
@@ -0,0 +1,31 @@
+/**
+ * @authors Sebastian Gaida
+ * @file src/vkcv/CoreManager.cpp
+ * @brief Handling of global states regarding dependencies
+ */
+
+#include "CoreManager.hpp"
+
+namespace vkcv {
+
+    int glfwCounter = 0;
+
+    void initGLFW() {
+
+        if (glfwCounter == 0) {
+            int glfwSuccess = glfwInit();
+
+            if (glfwSuccess == GLFW_FALSE) {
+                throw std::runtime_error("Could not initialize GLFW");
+            }
+        }
+        glfwCounter++;
+    }
+
+    void terminateGLFW() {
+        if (glfwCounter == 1) {
+            glfwTerminate();
+        }
+        glfwCounter--;
+    }
+}
diff --git a/src/vkcv/CoreManager.hpp b/src/vkcv/CoreManager.hpp
new file mode 100644
index 00000000..a4104ae4
--- /dev/null
+++ b/src/vkcv/CoreManager.hpp
@@ -0,0 +1,22 @@
+#pragma once
+/**
+ * @authors Sebastian Gaida
+ * @file src/vkcv/CoreManager.hpp
+ * @brief Handling of global states regarding dependencies
+ */
+
+#include <GLFW/glfw3.h>
+#include <stdexcept>
+
+namespace vkcv {
+
+    /**
+     * initializes glfw once and increases the counter
+     */
+    void initGLFW();
+
+    /**
+     * terminates glfw once, if it was initialized or decreases the counter
+     */
+    void terminateGLFW();
+}
diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp
deleted file mode 100644
index 1337a132..00000000
--- a/src/vkcv/Handles.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "vkcv/Handles.hpp"
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 8814a5ab..e0e0fb16 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -5,29 +5,21 @@
  */
 
 #include "vkcv/Window.hpp"
+#include "CoreManager.hpp"
 
 namespace vkcv {
 
-    static uint32_t s_WindowCount = 0;
-
     Window::Window(GLFWwindow *window)
             : m_window(window) {
     }
 
     Window::~Window() {
         glfwDestroyWindow(m_window);
-        s_WindowCount--;
-
-        if(s_WindowCount == 0)
-            glfwTerminate();
+        vkcv::terminateGLFW();
     }
 
     Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
-        if(s_WindowCount == 0)
-            glfwInit();
-
-        s_WindowCount++;
-
+        vkcv::initGLFW();
         width = std::max(width, 1);
         height = std::max(height, 1);
 
@@ -36,8 +28,6 @@ namespace vkcv {
         GLFWwindow *window;
         window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
         return Window(window);
-
-
     }
 
     bool Window::isWindowOpen() const {
-- 
GitLab