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 (32)
.project
.cproject
.vs/
.vscode/
.idea/
.editorconfig
......
......@@ -4,6 +4,11 @@ project(vkcv)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CMAKE_BUILD_TYPE)
string(TOLOWER "${CMAKE_BUILD_TYPE}" _vkcv_build_type)
set(vkcv_build_${_vkcv_build_type} 1)
endif()
message("-- Language: [ C++ " ${CMAKE_CXX_STANDARD} " ]")
set(vkcv_config ${PROJECT_SOURCE_DIR}/config)
......@@ -15,6 +20,16 @@ set(vkcv_source ${PROJECT_SOURCE_DIR}/src)
set(vkcv_flags ${CMAKE_CXX_FLAGS})
if (vkcv_build_debug)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(vkcv_flags ${vkcv_flags} " -Weverything")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(vkcv_flags ${vkcv_flags} " -Wextra -Wall -pedantic")
else()
set(vkcv_flags ${vkcv_flags} " -Wall")
endif()
endif()
include(${vkcv_config}/Sources.cmake)
include(${vkcv_config}/Libraries.cmake)
......@@ -42,3 +57,5 @@ target_include_directories(vkcv SYSTEM BEFORE PRIVATE ${vkcv_includes})
target_link_libraries(vkcv ${vkcv_libraries})
add_subdirectory(projects)
include(${vkcv_config}/ext/Doxygen.cmake)
This diff is collapsed.
......@@ -2,4 +2,8 @@
set(vkcv_sources
${vkcv_source}/vkcv/Context.hpp
${vkcv_source}/vkcv/Context.cpp
${vkcv_source}/vkcv/Window.hpp
${vkcv_source}/vkcv/Window.cpp
${vkcv_source}/vkcv/CoreManager.hpp
${vkcv_source}/vkcv/CoreManager.cpp
)
# check if Doxygen is installed
find_package(Doxygen)
if (DOXYGEN_FOUND)
# note the option ALL which allows to build the docs together with the application
add_custom_target( doc_doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_SOURCE_DIR}/Doxyfile
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM )
else (DOXYGEN_FOUND)
message(WARNING "Doxygen need to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)
html/
latex/
#include <iostream>
#include <vkcv/Context.hpp>
#include <vkcv/Window.hpp>
int main(int argc, const char** argv) {
const char* applicationName = "First Triangle";
vkcv::Window window = vkcv::Window::create(
applicationName,
800,
600,
false
);
vkcv::Context context = vkcv::Context::create(
"First Triangle",
VK_MAKE_VERSION(0, 0, 1)
applicationName,
VK_MAKE_VERSION(0, 0, 1),
20,
{vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer}
);
const vk::Instance& instance = context.getInstance();
......@@ -23,5 +32,8 @@ 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()) {
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)
: 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) {
const vk::ApplicationInfo applicationInfo (
applicationName,
applicationVersion,
"vkCV",
VK_MAKE_VERSION(0, 0, 1),
VK_HEADER_VERSION_COMPLETE
);
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
// TODO: enable validation layers in debug build and add required extensions
const vk::InstanceCreateInfo instanceCreateInfo (
vk::InstanceCreateFlags(),
&applicationInfo,
0,
nullptr,
0,
nullptr
);
const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties();
vk::Instance instance = vk::createInstance(instanceCreateInfo);
std::vector<const char*> supportedLayers;
supportedLayers.reserve(layerProperties.size());
// TODO: search for the best physical device (discrete GPU)
vk::PhysicalDevice physicalDevice = instance.enumeratePhysicalDevices()[0];
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"
};
// TODO: create required queues, add validation layers and required extensions
const vk::DeviceCreateInfo deviceCreateInfo (
vk::DeviceCreateFlags(),
0,
nullptr,
0,
nullptr,
0,
nullptr,
nullptr
);
if (!Context::checkSupport(supportedLayers, validationLayers)) {
throw std::runtime_error("Validation layers requested but not available!");
}
#endif
vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
// 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);
}
......@@ -64,4 +140,117 @@ namespace vkcv {
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;
}
}
#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>
......@@ -9,29 +14,136 @@ namespace vkcv {
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;
Context& operator=(Context &&other) = default;
static Context create(const char* applicationName, uint32_t applicationVersion);
/**
* 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 "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();
}
/**
* @authors Sebastian Gaida
* @file src/vkcv/Window.cpp
* @brief Window class to handle a basic rendering surface and input
*/
#include "Window.hpp"
#include "CoreManager.hpp"
namespace vkcv {
Window::Window(GLFWwindow *window)
: m_window(window) {
}
Window::~Window() {
glfwDestroyWindow(m_window);
vkcv::terminateGLFW();
}
Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
vkcv::initGLFW();
width = std::max(width, 1);
height = std::max(height, 1);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
GLFWwindow *window;
window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
return Window(window);
}
bool Window::isWindowOpen() const {
return !glfwWindowShouldClose(m_window);
}
void Window::pollEvents() {
glfwPollEvents();
}
GLFWwindow *Window::getWindow() const {
return m_window;
}
int Window::getWidth() const {
int width;
glfwGetWindowSize(m_window, &width, nullptr);
return width;
}
int Window::getHeight() const {
int height;
glfwGetWindowSize(m_window, nullptr, &height);
return height;
}
}
\ No newline at end of file
#pragma once
/**
* @authors Sebastian Gaida
* @file src/vkcv/Window.hpp
* @brief Window class to handle a basic rendering surface and input
*/
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
namespace vkcv {
class Window final {
private:
GLFWwindow *m_window;
/**
*
* @param GLFWwindow of the class
*/
explicit Window(GLFWwindow *window);
public:
/**
* creates a GLFWwindow with the parameters in the function
* @param[in] windowTitle of the window
* @param[in] width of the window (optional)
* @param[in] height of the window (optional)
* @param[in] resizable resize ability of the window (optional)
* @return Window class
*/
static Window create(const char *windowTitle, int width = -1, int height = -1, bool resizable = false);
/**
* checks if the window is still open, or the close event was called
* This function should be changed/removed later on
* @return bool if the window is still open
*/
[[nodiscard]]
bool isWindowOpen() const;
/**
* polls all events on the GLFWwindow
*/
static void pollEvents();
/**
* returns the current window
* @return window handle
*/
[[nodiscard]]
GLFWwindow *getWindow() const;
/**
* gets the current window width
* @return int with window width
*/
[[nodiscard]]
int getWidth() const;
/**
* gets the current window height
* @return int with window height
*/
[[nodiscard]]
int getHeight() const;
/**
* Copy-operator of #Window is deleted!
*
* @param other Other instance of #Window
* @return Reference to itself
*/
Window &operator=(const Window &other) = delete;
/**
* Move-operator of #Window uses default behavior!
*
* @param other Other instance of #Window
* @return Reference to itself
*/
Window &operator=(Window &&other) = default;
/**
* Destructor of #Window, terminates GLFW
*/
virtual ~Window();
};
}
\ No newline at end of file