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
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results
Show changes
Showing
with 810 additions and 263 deletions
#include "vkcv/testing/Test.hpp"
namespace vkcv::testing {
void Test::test(int test_value) {
// TODO: this is an example
}
}
first_triangle
\ No newline at end of file
......@@ -5,11 +5,20 @@ project(first_triangle)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# this should fix the execution path to load local files from the project
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# adding source files to the project
add_executable(first_triangle src/main.cpp)
# this should fix the execution path to load local files from the project (for MSVC)
if(MSVC)
set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# including headers of dependencies and the VkCV framework
target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes})
target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(first_triangle vkcv ${vkcv_libraries})
target_link_libraries(first_triangle vkcv ${vkcv_libraries} vkcv_testing)
%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv
%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv
pause
\ No newline at end of file
File added
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
\ No newline at end of file
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) out vec3 fragColor;
void main() {
vec3 positions[3] = {
vec3(-0.5, 0.5, 0),
vec3( 0.5, 0.5, 0),
vec3(0, -0.5, 0)
};
vec3 colors[3] = {
vec3(1, 0, 0),
vec3(0, 1, 0),
vec3(0, 0, 1)
};
gl_Position = vec4(positions[gl_VertexIndex], 1.0);
fragColor = colors[gl_VertexIndex];
}
\ No newline at end of file
File added
#include <iostream>
#include <vkcv/Core.hpp>
#include <vkcv/Window.hpp>
#include <vkcv/ShaderProgram.hpp>
int main(int argc, const char** argv) {
const char* applicationName = "First Triangle";
vkcv::Window window = vkcv::Window::create(
applicationName,
800,
600,
const int windowWidth = 800;
const int windowHeight = 600;
vkcv::Window window = vkcv::Window::create(
applicationName,
windowWidth,
windowHeight,
false
);
);
vkcv::Core core = vkcv::Core::create(
window,
applicationName,
VK_MAKE_VERSION(0, 0, 1),
20,
{vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer}
{vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
{},
{"VK_KHR_swapchain"}
);
const auto &context = core.getContext();
......@@ -33,6 +40,36 @@ 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;
}
// an example attachment for passes that output to the window
const vkcv::AttachmentDescription present_color_attachment(
vkcv::AttachmentLayout::UNDEFINED,
vkcv::AttachmentLayout::COLOR_ATTACHMENT,
vkcv::AttachmentLayout::PRESENTATION,
vkcv::AttachmentOperation::STORE,
vkcv::AttachmentOperation::CLEAR,
core.getSwapchainImageFormat());
vkcv::PassConfig trianglePassDefinition({present_color_attachment});
vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition);
if (trianglePass.id == 0)
{
std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv::ShaderProgram triangleShaderProgram{};
triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv"));
triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv"));
const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass);
vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition);
if (trianglePipeline.id == 0)
{
std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
return EXIT_FAILURE;
}
/*
* BufferHandle triangleVertices = core.createBuffer(vertices);
* BufferHandle triangleIndices = core.createBuffer(indices);
......@@ -51,11 +88,9 @@ int main(int argc, const char** argv) {
while (window.isWindowOpen())
{
// core.beginFrame(); or something like that
// core.execute(trianglePass, trianglePipeline, triangleModel);
// core.endFrame(); or something like that
// TBD: synchronization
core.beginFrame();
core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight);
core.endFrame();
window.pollEvents();
}
......
#include "vkcv/CommandResources.hpp"
namespace vkcv {
CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex) {
CommandResources resources;
vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
vk::CommandPoolCreateInfo poolCreateInfo(flags, graphicFamilyIndex);
resources.commandPool = device.createCommandPool(poolCreateInfo, nullptr, {});
const int commandPoolCount = 1;
vk::CommandBufferAllocateInfo allocateInfo(resources.commandPool, vk::CommandBufferLevel::ePrimary, commandPoolCount);
const std::vector<vk::CommandBuffer> createdBuffers = device.allocateCommandBuffers(allocateInfo, {});
assert(createdBuffers.size() == 1);
resources.commandBuffer = createdBuffers[0];
return resources;
}
void destroyCommandResources(const vk::Device& device, const CommandResources& resources) {
device.freeCommandBuffers(resources.commandPool, resources.commandBuffer, {});
device.destroyCommandPool(resources.commandPool, {});
}
}
\ No newline at end of file
#include <GLFW/glfw3.h>
#include "vkcv/Context.hpp"
namespace vkcv
......@@ -5,7 +8,8 @@ namespace vkcv
Context::Context(Context &&other) noexcept:
m_Instance(other.m_Instance),
m_PhysicalDevice(other.m_PhysicalDevice),
m_Device(other.m_Device)
m_Device(other.m_Device),
m_QueueManager(other.m_QueueManager)
{
other.m_Instance = nullptr;
other.m_PhysicalDevice = nullptr;
......@@ -17,6 +21,7 @@ namespace vkcv
m_Instance = other.m_Instance;
m_PhysicalDevice = other.m_PhysicalDevice;
m_Device = other.m_Device;
m_QueueManager = other.m_QueueManager;
other.m_Instance = nullptr;
other.m_PhysicalDevice = nullptr;
......@@ -27,10 +32,12 @@ namespace vkcv
Context::Context(vk::Instance instance,
vk::PhysicalDevice physicalDevice,
vk::Device device) noexcept :
vk::Device device,
QueueManager&& queueManager) noexcept :
m_Instance{instance},
m_PhysicalDevice{physicalDevice},
m_Device{device}
m_Device{device},
m_QueueManager{queueManager}
{}
Context::~Context() noexcept
......@@ -53,4 +60,232 @@ namespace vkcv
{
return m_Device;
}
const QueueManager& Context::getQueueManager() const {
return m_QueueManager;
}
/**
* @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 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;
}
std::vector<const char*> 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;
}
Context Context::create(const char *applicationName,
uint32_t applicationVersion,
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!");
}
// for GLFW: get all required extensions
std::vector<const char*> requiredExtensions = 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!");
}
std::vector<vk::DeviceQueueCreateInfo> qCreateInfos;
// create required queues
std::vector<float> qPriorities;
qPriorities.resize(queueFlags.size(), 1.f);
std::vector<std::pair<int, int>> queuePairsGraphics, queuePairsCompute, queuePairsTransfer;
QueueManager::queueCreateInfosQueueHandles(physicalDevice, qPriorities, queueFlags, qCreateInfos, queuePairsGraphics, queuePairsCompute, queuePairsTransfer);
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
// Ablauf
// qCreateInfos erstellen --> braucht das Device
// device erstellen
// jetzt koennen wir mit dem device die queues erstellen
vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
QueueManager queueManager = QueueManager::create(device, queuePairsGraphics, queuePairsCompute, queuePairsTransfer);
return Context(instance, physicalDevice, device, std::move(queueManager));
}
}
/**
* @authors Sebastian Gaida
* @file src/vkcv/CoreManager.cpp
* @authors Artur Wasmut
* @file src/vkcv/Core.cpp
* @brief Handling of global states regarding dependencies
*/
#include <GLFW/glfw3.h>
#include "vkcv/Core.hpp"
#include "PassManager.hpp"
#include "PipelineManager.hpp"
#include "Surface.hpp"
#include "ImageLayoutTransitions.hpp"
#include "Framebuffer.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)
Core Core::create(const Window &window,
const char *applicationName,
uint32_t applicationVersion,
std::vector<vk::QueueFlagBits> queueFlags,
std::vector<const char *> instanceExtensions,
std::vector<const char *> deviceExtensions)
{
vk::PhysicalDevice phyDevice;
std::vector<vk::PhysicalDevice> devices = instance.enumeratePhysicalDevices();
Context context = Context::create(
applicationName, applicationVersion,
queueFlags,
instanceExtensions,
deviceExtensions
);
const vk::SurfaceKHR surface = createSurface(
window.getWindow(),
context.getInstance(),
context.getPhysicalDevice()
);
SwapChain swapChain = SwapChain::create(window, context, surface);
std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain());
std::vector<vk::ImageView> imageViews;
imageViews.reserve( swapChainImages.size() );
//here can be swizzled with vk::ComponentSwizzle if needed
vk::ComponentMapping componentMapping(
vk::ComponentSwizzle::eR,
vk::ComponentSwizzle::eG,
vk::ComponentSwizzle::eB,
vk::ComponentSwizzle::eA );
vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 );
for ( auto image : swapChainImages )
{
vk::ImageViewCreateInfo imageViewCreateInfo(
vk::ImageViewCreateFlags(),
image,
vk::ImageViewType::e2D,
swapChain.getSurfaceFormat().format,
componentMapping,
subResourceRange
);
if (devices.empty()) {
throw std::runtime_error("failed to find GPUs with Vulkan support!");
imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo));
}
int max_score = -1;
for (const auto& device : devices) {
int score = deviceScore(device);
if (score > max_score) {
max_score = score;
phyDevice = device;
}
}
const auto& queueManager = context.getQueueManager();
const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex;
const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex);
const auto defaultSyncResources = createDefaultSyncResources(context.getDevice());
if (max_score == -1) {
throw std::runtime_error("failed to find a suitable GPU!");
}
return phyDevice;
return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources);
}
/**
* @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)
const Context &Core::getContext() const
{
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;
return m_Context;
}
/**
* @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)
Core::Core(Context &&context, const Window &window , SwapChain swapChain, std::vector<vk::ImageView> imageViews,
const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
m_Context(std::move(context)),
m_window(window),
m_swapchain(swapChain),
m_swapchainImageViews(imageViews),
m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)},
m_CommandResources(commandResources),
m_SyncResources(syncResources)
{}
Core::~Core() noexcept {
m_Context.getDevice().waitIdle();
for (auto image : m_swapchainImageViews) {
m_Context.m_Device.destroyImageView(image);
}
destroyCommandResources(m_Context.getDevice(), m_CommandResources);
destroySyncResources(m_Context.getDevice(), m_SyncResources);
destroyTemporaryFramebuffers();
m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain());
m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface());
}
PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config)
{
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;
const vk::RenderPass &pass = m_PassManager->getVkPass(config.m_PassHandle);
return m_PipelineManager->createPipeline(config, pass);
}
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
PassHandle Core::createPass(const PassConfig &config)
{
return m_Context;
return m_PassManager->createPass(config);
}
Core::Core(Context &&context) noexcept :
m_Context(std::move(context))
{}
Result Core::acquireSwapchainImage() {
uint32_t imageIndex;
const auto& acquireResult = m_Context.getDevice().acquireNextImageKHR(
m_swapchain.getSwapchain(), std::numeric_limits<uint64_t>::max(), nullptr,
m_SyncResources.swapchainImageAcquired, &imageIndex, {}
);
if (acquireResult != vk::Result::eSuccess) {
return Result::ERROR;
}
const auto& result = m_Context.getDevice().waitForFences(
m_SyncResources.swapchainImageAcquired, true,
std::numeric_limits<uint64_t>::max()
);
m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired);
if (result != vk::Result::eSuccess) {
return Result::ERROR;
}
m_currentSwapchainImageIndex = imageIndex;
return Result::SUCCESS;
}
void Core::destroyTemporaryFramebuffers() {
for (const vk::Framebuffer f : m_TemporaryFramebuffers) {
m_Context.getDevice().destroyFramebuffer(f);
}
m_TemporaryFramebuffers.clear();
}
void Core::beginFrame() {
if (acquireSwapchainImage() != Result::SUCCESS) {
return;
}
m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex
destroyTemporaryFramebuffers();
const vk::CommandBufferUsageFlags beginFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
const vk::CommandBufferBeginInfo beginInfos(beginFlags);
m_CommandResources.commandBuffer.begin(beginInfos);
}
void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle,
const int width, const int height) {
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f };
const vk::ClearValue clearValues(clearColor);
const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex];
const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView);
m_TemporaryFramebuffers.push_back(framebuffer);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues);
const vk::SubpassContents subpassContents = {};
m_CommandResources.commandBuffer.beginRenderPass(beginInfo, subpassContents, {});
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
m_CommandResources.commandBuffer.draw(3, 1, 0, 0, {});
m_CommandResources.commandBuffer.endRenderPass();
}
void Core::endFrame() {
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain());
const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex];
m_CommandResources.commandBuffer.end();
const auto& queueManager = m_Context.getQueueManager();
const vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished);
queueManager.getGraphicsQueues()[0].handle.submit(submitInfo);
vk::Result presentResult;
const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain();
const vk::PresentInfoKHR presentInfo(1, &m_SyncResources.renderFinished, 1, &swapchain,
&m_currentSwapchainImageIndex, &presentResult);
queueManager.getPresentQueue().handle.presentKHR(presentInfo);
if (presentResult != vk::Result::eSuccess) {
std::cout << "Error: swapchain present failed" << std::endl;
}
}
vk::Format Core::getSwapchainImageFormat() {
return m_swapchain.getSurfaceFormat().format;
}
}
#include "Framebuffer.hpp"
namespace vkcv {
vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass,
const int width, const int height, const vk::ImageView imageView) {
const vk::FramebufferCreateFlags flags = {};
const uint32_t attachmentCount = 1; // TODO: proper value
const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachmentCount, &imageView, width, height, 1);
return device.createFramebuffer(createInfo, nullptr, {});
}
}
\ No newline at end of file
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv{
vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass,
const int width, const int height, const vk::ImageView imageView);
}
\ No newline at end of file
#include "ImageLayoutTransitions.hpp"
namespace vkcv {
void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image,
const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout) {
// TODO: proper src and dst masks
const vk::PipelineStageFlags srcStageMask = vk::PipelineStageFlagBits::eAllCommands;
const vk::PipelineStageFlags dstStageMask = vk::PipelineStageFlagBits::eAllCommands;
const vk::DependencyFlags dependecyFlags = {};
// TODO: proper src and dst masks
const vk::AccessFlags srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
const vk::AccessFlags dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
// TODO: proper aspect flags
const vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor;
const vk::ImageSubresourceRange subresourceRange(aspectFlags, 0, 1, 0, 1);
vk::ImageMemoryBarrier imageBarrier(srcAccessMask, dstAccessMask, oldLayout, newLayout, 0, 0, image, subresourceRange);
cmdBuffer.pipelineBarrier(srcStageMask, dstStageMask, dependecyFlags, 0, nullptr, 0, nullptr, 1, &imageBarrier, {});
}
}
\ No newline at end of file
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv {
void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image,
const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout);
}
\ No newline at end of file
#include "vkcv/PassConfig.hpp"
#include <utility>
namespace vkcv
{
AttachmentDescription::AttachmentDescription(
AttachmentLayout initial,
AttachmentLayout in_pass,
AttachmentLayout final,
AttachmentOperation store_op,
AttachmentOperation load_op,
vk::Format format) noexcept :
layout_initial{initial},
layout_in_pass{in_pass},
layout_final{final},
store_operation{store_op},
load_operation{load_op},
format(format)
{};
PassConfig::PassConfig(std::vector<AttachmentDescription> attachments) noexcept :
attachments{std::move(attachments)}
{}
}
\ No newline at end of file
#include "PassManager.hpp"
namespace vkcv
{
static vk::ImageLayout getVkLayoutFromAttachLayout(AttachmentLayout layout)
{
switch(layout)
{
case AttachmentLayout::GENERAL:
return vk::ImageLayout::eGeneral;
case AttachmentLayout::COLOR_ATTACHMENT:
return vk::ImageLayout::eColorAttachmentOptimal;
case AttachmentLayout::SHADER_READ_ONLY:
return vk::ImageLayout::eShaderReadOnlyOptimal;
case AttachmentLayout::DEPTH_STENCIL_ATTACHMENT:
return vk::ImageLayout::eDepthStencilAttachmentOptimal;
case AttachmentLayout::DEPTH_STENCIL_READ_ONLY:
return vk::ImageLayout::eDepthStencilReadOnlyOptimal;
case AttachmentLayout::PRESENTATION:
return vk::ImageLayout::ePresentSrcKHR;
default:
return vk::ImageLayout::eUndefined;
}
}
static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op)
{
switch(op)
{
case AttachmentOperation::STORE:
return vk::AttachmentStoreOp::eStore;
default:
return vk::AttachmentStoreOp::eDontCare;
}
}
static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op)
{
switch(op)
{
case AttachmentOperation::LOAD:
return vk::AttachmentLoadOp::eLoad;
case AttachmentOperation::CLEAR:
return vk::AttachmentLoadOp::eClear;
default:
return vk::AttachmentLoadOp::eDontCare;
}
}
PassManager::PassManager(vk::Device device) noexcept :
m_Device{device},
m_RenderPasses{},
m_NextPassId{1}
{}
PassManager::~PassManager() noexcept
{
for(const auto &pass : m_RenderPasses)
m_Device.destroy(pass);
m_RenderPasses.clear();
m_NextPassId = 1;
}
PassHandle PassManager::createPass(const PassConfig &config)
{
// description of all {color, input, depth/stencil} attachments of the render pass
std::vector<vk::AttachmentDescription> attachmentDescriptions{};
// individual references to color attachments (of a subpass)
std::vector<vk::AttachmentReference> colorAttachmentReferences{};
// individual reference to depth attachment (of a subpass)
vk::AttachmentReference depthAttachmentReference{};
vk::AttachmentReference *pDepthAttachment = nullptr; //stays nullptr if no depth attachment used
for (uint32_t i = 0; i < config.attachments.size(); i++)
{
// TODO: Renderpass struct should hold proper format information
vk::Format format = config.attachments[i].format;
if (config.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT)
{
depthAttachmentReference.attachment = i;
depthAttachmentReference.layout = getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass);
pDepthAttachment = &depthAttachmentReference;
}
else
{
vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass));
colorAttachmentReferences.push_back(attachmentRef);
}
vk::AttachmentDescription attachmentDesc({},
format,
vk::SampleCountFlagBits::e1,
getVKLoadOpFromAttachOp(config.attachments[i].load_operation),
getVkStoreOpFromAttachOp(config.attachments[i].store_operation),
vk::AttachmentLoadOp::eDontCare,
vk::AttachmentStoreOp::eDontCare,
getVkLayoutFromAttachLayout(config.attachments[i].layout_initial),
getVkLayoutFromAttachLayout(config.attachments[i].layout_final));
attachmentDescriptions.push_back(attachmentDesc);
}
vk::SubpassDescription subpassDescription({},
vk::PipelineBindPoint::eGraphics,
0,
{},
static_cast<uint32_t>(colorAttachmentReferences.size()),
colorAttachmentReferences.data(),
{},
pDepthAttachment,
0,
{});
vk::RenderPassCreateInfo passInfo({},
static_cast<uint32_t>(attachmentDescriptions.size()),
attachmentDescriptions.data(),
1,
&subpassDescription,
0,
{});
vk::RenderPass vkObject{nullptr};
if(m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess)
return PassHandle{0};
m_RenderPasses.push_back(vkObject);
return PassHandle{m_NextPassId++};
}
vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const
{
return m_RenderPasses[handle.id - 1];
}
}
#pragma once
#include <vulkan/vulkan.hpp>
#include <vector>
#include "vkcv/Handles.hpp"
#include "vkcv/PassConfig.hpp"
namespace vkcv
{
class PassManager
{
private:
vk::Device m_Device;
std::vector<vk::RenderPass> m_RenderPasses;
uint64_t m_NextPassId;
public:
PassManager() = delete; // no default ctor
explicit PassManager(vk::Device device) noexcept; // ctor
~PassManager() noexcept; // dtor
PassManager(const PassManager &other) = delete; // copy-ctor
PassManager(PassManager &&other) = delete; // move-ctor;
PassManager & operator=(const PassManager &other) = delete; // copy-assign op
PassManager & operator=(PassManager &&other) = delete; // move-assign op
PassHandle createPass(const PassConfig &config);
[[nodiscard]]
vk::RenderPass getVkPass(const PassHandle &handle) const;
};
}
/**
* @authors Mara Vogt, Mark Mints
* @file src/vkcv/Pipeline.cpp
* @brief Pipeline class to handle shader stages
*/
#include "vkcv/PipelineConfig.hpp"
namespace vkcv {
PipelineConfig::PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle):
m_ShaderProgram(shaderProgram),
m_Height(height),
m_Width(width),
m_PassHandle(passHandle)
{}
}