Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
Show changes
Commits on Source (13)
Showing
with 606 additions and 475 deletions
build_ubuntu_gcc:
variables:
GIT_SUBMODULE_STRATEGY: recursive
stage: build
tags:
- ubuntu-gcc
script:
- mkdir debug
- cd debug
- cmake -DCMAKE_BUILD_TYPE=Debug ..
- cmake --build .
build_win10_msvc:
variables:
GIT_SUBMODULE_STRATEGY: recursive
stage: build
tags:
- win10-msvc
script:
- cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\'
- .\Launch-VsDevShell.ps1
- cd $CI_PROJECT_DIR
- mkdir debug
- cd debug
# - cmake -DCMAKE_BUILD_TYPE=Debug .. # Debug Modus derzeit deaktiviert, da das Buildlog die maximale Größe von Gitlab überschreitet, wegen Warnings in Vulkan.hpp
- cmake ..
- cmake --build .
\ No newline at end of file
cmake_minimum_required(VERSION 3.16)
project(vkcv)
# settings c++ standard for the framework
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# checking build type and setting up a variable
if (CMAKE_BUILD_TYPE)
string(TOLOWER "${CMAKE_BUILD_TYPE}" _vkcv_build_type)
set(vkcv_build_${_vkcv_build_type} 1)
......@@ -11,15 +13,19 @@ endif()
message("-- Language: [ C++ " ${CMAKE_CXX_STANDARD} " ]")
# setting up different paths
set(vkcv_config ${PROJECT_SOURCE_DIR}/config)
set(vkcv_config_ext ${vkcv_config}/ext)
set(vkcv_lib lib)
set(vkcv_source ${PROJECT_SOURCE_DIR}/src)
set(vkcv_include ${PROJECT_SOURCE_DIR}/include)
# initializes compiler flags with defaults
set(vkcv_flags ${CMAKE_CXX_FLAGS})
# enabling warnings in the debug build
if (vkcv_build_debug)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(vkcv_flags ${vkcv_flags} " -Weverything")
......@@ -30,19 +36,21 @@ if (vkcv_build_debug)
endif()
endif()
# add source files for compilation
include(${vkcv_config}/Sources.cmake)
# configure everything to use the required dependencies
include(${vkcv_config}/Libraries.cmake)
# set the compiler flags for the framework
set(CMAKE_CXX_FLAGS ${vkcv_flags})
message("-- Flags: [ ${CMAKE_CXX_FLAGS} ]")
# set the compile definitions aka preprocessor variables
add_compile_definitions(${vkcv_definitions})
if (vkcv_directories)
include_directories(${vkcv_directories})
endif()
# create VkCV framework as static library using all source files
add_library(vkcv STATIC ${vkcv_sources})
if(MSVC)
......@@ -53,9 +61,17 @@ if(MSVC)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${vkcv_sources})
endif()
# add include directories from dependencies as system includes
target_include_directories(vkcv SYSTEM BEFORE PRIVATE ${vkcv_includes})
# add the own include directory for public headers
target_include_directories(vkcv BEFORE PUBLIC ${vkcv_include})
# link the framework using all required libraries
target_link_libraries(vkcv ${vkcv_libraries})
# add sub-projects/examples as targets
add_subdirectory(projects)
# add doxygen as target if installed
include(${vkcv_config}/ext/Doxygen.cmake)
......@@ -864,7 +864,8 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = src
INPUT = src \
include
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
......
set(vkcv_config_lib ${vkcv_config}/lib)
set(vkcv_config_lib ${vkcv_config}/lib)
set(vkcv_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_lib})
if(NOT WIN32)
......@@ -10,21 +10,27 @@ if(NOT WIN32)
list(APPEND vkcv_flags -fopenmp)
endif()
# some formatted printing
set(vkcv_config_msg " - Library: ")
# load dependencies via separate cmake file
include(${vkcv_config_lib}/GLFW.cmake) # glfw-x11 / glfw-wayland # libglfw3-dev
include(${vkcv_config_lib}/Vulkan.cmake) # vulkan-intel / vulkan-radeon / nvidia # libvulkan-dev
# cleanup of compiler flags
if (vkcv_flags)
list(REMOVE_DUPLICATES vkcv_flags)
endif()
# cleanup of include directories from dependencies
if (vkcv_includes)
list(REMOVE_DUPLICATES vkcv_includes)
endif ()
# fix dependencies for different Linux distros (looking at you Ubuntu)
include(${vkcv_config_ext}/CheckLibraries.cmake)
# cleanup of compiler definitions aka preprocessor variables
if (vkcv_definitions)
list(REMOVE_DUPLICATES vkcv_definitions)
endif ()
......
# adding all source files and header files of the framework:
set(vkcv_sources
${vkcv_source}/vkcv/Context.hpp
${vkcv_include}/vkcv/Context.hpp
${vkcv_source}/vkcv/Context.cpp
${vkcv_source}/vkcv/Window.hpp
${vkcv_include}/vkcv/Core.hpp
${vkcv_source}/vkcv/Core.cpp
${vkcv_include}/vkcv/Handles.hpp
${vkcv_source}/vkcv/Handles.cpp
${vkcv_include}/vkcv/Window.hpp
${vkcv_source}/vkcv/Window.cpp
${vkcv_source}/vkcv/CoreManager.hpp
${vkcv_source}/vkcv/CoreManager.cpp
)
# cleanup of dependency libraries
list(REMOVE_DUPLICATES vkcv_libraries)
# fixing paths of libraries to work on most Linux distros
foreach (a_lib IN LISTS vkcv_libraries)
if (NOT EXISTS "${a_lib}")
string(REGEX MATCH ^/usr/lib/x86_64-linux-gnu/.*$ vkcv_usr_lib_u_match ${a_lib})
......
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv
{
class Context
{
friend class Core;
public:
// 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
[[nodiscard]]
const vk::Instance &getInstance() const;
[[nodiscard]]
const vk::PhysicalDevice &getPhysicalDevice() const;
[[nodiscard]]
const vk::Device &getDevice() const;
private:
/**
* 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;
vk::Instance m_Instance;
vk::PhysicalDevice m_PhysicalDevice;
vk::Device m_Device;
};
}
#pragma once
/**
* @file src/vkcv/Core.hpp
* @brief Handling of global states regarding dependencies
*/
#include <vulkan/vulkan.hpp>
#include "vkcv/Context.hpp"
#include "vkcv/Handles.hpp"
namespace vkcv
{
// TODO:
class Buffer;
class Renderpass;
class Pipeline;
class Core final
{
private:
/**
* 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;
Context m_Context;
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);
};
}
#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;};
}
......@@ -8,6 +8,9 @@
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#define NOMINMAX
#include <algorithm>
namespace vkcv {
class Window final {
......
# Add new projects/examples here:
add_subdirectory(first_triangle)
cmake_minimum_required(VERSION 3.16)
project(first_triangle)
# setting c++ standard for the project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# adding source files to the project
add_executable(first_triangle src/main.cpp)
target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_source} ${vkcv_includes})
# including headers of dependencies and the VkCV framework
target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(first_triangle vkcv ${vkcv_libraries})
#include <iostream>
#include <vkcv/Context.hpp>
#include <vkcv/Core.hpp>
#include <vkcv/Window.hpp>
int main(int argc, const char** argv) {
......@@ -10,13 +10,14 @@ int main(int argc, const char** argv) {
600,
false
);
vkcv::Context context = vkcv::Context::create(
vkcv::Core core = vkcv::Core::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();
......@@ -32,7 +33,30 @@ 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;
}
while (window.isWindowOpen()) {
/*
* 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
window.pollEvents();
}
return 0;
......
/**
* @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 "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;
}
#include "vkcv/Context.hpp"
namespace vkcv
{
Context::Context(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;
}
Context & Context::operator=(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;
}
Context::Context(vk::Instance instance,
vk::PhysicalDevice physicalDevice,
vk::Device device) noexcept :
m_Instance{instance},
m_PhysicalDevice{physicalDevice},
m_Device{device}
{}
Context::~Context() noexcept
{
m_Device.destroy();
m_Instance.destroy();
}
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;
}
}
#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);
};
}
/**
* @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 Context &Core::getContext() const
{
return m_Context;
}
Core::Core(Context &&context) noexcept :
m_Context(std::move(context))
{}
}
/**
* @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--;
}
}
#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();
}
#include "vkcv/Handles.hpp"
......@@ -4,22 +4,30 @@
* @brief Window class to handle a basic rendering surface and input
*/
#include "Window.hpp"
#include "CoreManager.hpp"
#include "vkcv/Window.hpp"
namespace vkcv {
static uint32_t s_WindowCount = 0;
Window::Window(GLFWwindow *window)
: m_window(window) {
}
Window::~Window() {
glfwDestroyWindow(m_window);
vkcv::terminateGLFW();
s_WindowCount--;
if(s_WindowCount == 0)
glfwTerminate();
}
Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
vkcv::initGLFW();
if(s_WindowCount == 0)
glfwInit();
s_WindowCount++;
width = std::max(width, 1);
height = std::max(height, 1);
......@@ -28,6 +36,8 @@ namespace vkcv {
GLFWwindow *window;
window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
return Window(window);
}
bool Window::isWindowOpen() const {
......