diff --git a/config/Sources.cmake b/config/Sources.cmake index e390e997071d86f5dcd1f4744a1f8bfd348d0c99..7c1f5990789951163eccb96a7ab32eacc6e05ca8 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 0000000000000000000000000000000000000000..1d11f82743ada5d1493df48ece54e558c234df47 --- /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 a347380ece98514049bdac3b7f7d8fee008d15bb..0000000000000000000000000000000000000000 --- 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 4ec2bc058409e9119695700b2b727be9426c2bcd..0000000000000000000000000000000000000000 --- 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 080e55350ba82ae58da8afcc8758da3fda77f19f..62440d90143d5c4cd9a31fbdf46819fdeda2d101 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 0c981a25dc52db4363d2bc7835df3c3c9d7c49fa..cc592f468e0c3e95d64a1558404873c4ca19f8b9 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 0000000000000000000000000000000000000000..4500a135fc5c9d247e58a323b8d6f26972a0480e --- /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 962f55606d492501a95bc3e31c736e1869f61d53..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..471075e3b48eb6aba8251d2924c00462f506fbc3 --- /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 0000000000000000000000000000000000000000..a4104ae4a8dca0b01825f023bcb3ec2a2808c876 --- /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 1337a132ae0b8721bd2776e24212f73dfb94ae46..0000000000000000000000000000000000000000 --- 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 8814a5abcf7977386490d9783d5b121b7f986651..e0e0fb16ec78d65dbd58b1a362c6bf72f1cca1e4 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 {