Skip to content
Snippets Groups Projects
Commit dfc1a650 authored by Tobias Frisch's avatar Tobias Frisch
Browse files

Merge branch '16-swapchain-class' into 'develop'

Resolve "Swapchain Class"

Closes #16

See merge request !12
parents 6d202adf 96561f3e
No related branches found
No related tags found
4 merge requests!12Resolve "Swapchain Class",!7Resolve "Shader Program Class",!5Resolve "Pipeline State Object",!4Resolve "Renderpass Class"
Pipeline #24863 passed
Showing
with 745 additions and 117 deletions
......@@ -7,9 +7,45 @@ set(vkcv_sources
${vkcv_include}/vkcv/Core.hpp
${vkcv_source}/vkcv/Core.cpp
${vkcv_include}/vkcv/PassConfig.hpp
${vkcv_source}/vkcv/PassConfig.cpp
${vkcv_source}/vkcv/PassManager.hpp
${vkcv_source}/vkcv/PassManager.cpp
${vkcv_include}/vkcv/Handles.hpp
${vkcv_source}/vkcv/Handles.cpp
${vkcv_include}/vkcv/Window.hpp
${vkcv_source}/vkcv/Window.cpp
${vkcv_include}/vkcv/SwapChain.hpp
${vkcv_source}/vkcv/SwapChain.cpp
${vkcv_include}/vkcv/ShaderProgram.hpp
${vkcv_source}/vkcv/ShaderProgram.cpp
${vkcv_include}/vkcv/PipelineConfig.hpp
${vkcv_source}/vkcv/PipelineConfig.cpp
${vkcv_source}/vkcv/PipelineManager.hpp
${vkcv_source}/vkcv/PipelineManager.cpp
${vkcv_include}/vkcv/CommandResources.hpp
${vkcv_source}/vkcv/CommandResources.cpp
${vkcv_include}/vkcv/SyncResources.hpp
${vkcv_source}/vkcv/SyncResources.cpp
${vkcv_include}/vkcv/QueueManager.hpp
${vkcv_source}/vkcv/QueueManager.cpp
${vkcv_source}/vkcv/Surface.hpp
${vkcv_source}/vkcv/Surface.cpp
${vkcv_source}/vkcv/ImageLayoutTransitions.hpp
${vkcv_source}/vkcv/ImageLayoutTransitions.cpp
${vkcv_source}/vkcv/Framebuffer.hpp
${vkcv_source}/vkcv/Framebuffer.cpp
)
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv {
struct CommandResources {
vk::CommandPool commandPool;
vk::CommandBuffer commandBuffer;
};
CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex);
void destroyCommandResources(const vk::Device& device, const CommandResources& resources);
}
\ No newline at end of file
......@@ -4,16 +4,27 @@
* @brief Handling of global states regarding dependencies
*/
#include <memory>
#include <vulkan/vulkan.hpp>
#include "vkcv/Context.hpp"
#include "vkcv/SwapChain.hpp"
#include "vkcv/Window.hpp"
#include "vkcv/PassConfig.hpp"
#include "vkcv/Handles.hpp"
#include "vkcv/PipelineConfig.hpp"
#include "CommandResources.hpp"
#include "SyncResources.hpp"
#include "vkcv/QueueManager.hpp"
namespace vkcv
{
// TODO:
class Buffer;
class Renderpass;
class Pipeline;
// forward declarations
class PassManager;
class PipelineManager;
class Core final
{
......@@ -24,16 +35,32 @@ namespace vkcv
*
* @param context encapsulates various Vulkan objects
*/
explicit Core(Context &&context) noexcept;
Core(Context &&context, const Window &window, SwapChain swapChain, std::vector<vk::ImageView> imageViews,
const CommandResources& commandResources, const SyncResources& syncResources, const QueueManager &queues) noexcept;
// explicit destruction of default constructor
Core() = delete;
uint32_t acquireSwapchainImage();
void destroyTemporaryFramebuffers();
Context m_Context;
SwapChain m_swapchain;
std::vector<vk::ImageView> m_swapchainImageViews;
const Window& m_window;
std::unique_ptr<PassManager> m_PassManager;
std::unique_ptr<PipelineManager> m_PipelineManager;
CommandResources m_CommandResources;
SyncResources m_SyncResources;
QueueManager m_QueueManager;
uint32_t m_currentSwapchainImageIndex;
std::vector<vk::Framebuffer> m_TemporaryFramebuffers;
public:
/**
* Destructor of #Core destroys the Vulkan objects contained in the core's context.
*/
~Core() noexcept = default;
~Core() noexcept;
/**
* Copy-constructor of #Core is deleted!
......@@ -78,23 +105,59 @@ namespace vkcv
*
* @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,
static Core create(const Window &window,
const char *applicationName,
uint32_t applicationVersion,
uint32_t queueCount,
std::vector<vk::QueueFlagBits> queueFlags = {},
std::vector<const char*> instanceExtensions = {},
std::vector<const char*> deviceExtensions = {});
/**
* Creates a basic vulkan graphics pipeline using @p config from the pipeline config class and returns it using the @p handle.
* Fixed Functions for pipeline are set with standard values.
*
* @param config a pipeline config object from the pipeline config class
* @param handle a handle to return the created vulkan handle
* @return True if pipeline creation was successful, False if not
*/
[[nodiscard]]
PipelineHandle createGraphicsPipeline(const PipelineConfig &config);
/**
* Creates a basic vulkan render pass using @p config from the render pass config class and returns it using the @p handle.
* Fixed Functions for pipeline are set with standard values.
*
* @param config a render pass config object from the render pass config class
* @param handle a handle to return the created vulkan handle
* @return True if render pass creation was successful, False if not
*/
[[nodiscard]]
PassHandle createPass(const PassConfig &config);
// TODO:
BufferHandle createBuffer(const Buffer &buf);
PassHandle createRenderPass(const Renderpass &pass) ;
PipelineHandle createPipeline(const Pipeline &pipeline);
/**
* @brief start recording command buffers and increment frame index
*/
void beginFrame();
/**
* @brief render a beautiful triangle
*/
void renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle,
const int width, const int height);
/**
* @brief end recording and present image
*/
void endFrame();
vk::Format getSwapchainImageFormat();
};
}
#pragma once
#include <vector>
#include <vulkan/vulkan.hpp>
namespace vkcv
{
enum class AttachmentLayout
{
UNDEFINED,
GENERAL,
COLOR_ATTACHMENT,
SHADER_READ_ONLY,
DEPTH_STENCIL_ATTACHMENT,
DEPTH_STENCIL_READ_ONLY,
TRANSFER_SRC,
TRANSFER_DST,
PRESENTATION
};
enum class AttachmentOperation
{
LOAD,
CLEAR,
STORE,
DONT_CARE
};
struct AttachmentDescription
{
AttachmentDescription() = delete;
AttachmentDescription(
AttachmentLayout initial,
AttachmentLayout in_pass,
AttachmentLayout final,
AttachmentOperation store_op,
AttachmentOperation load_op,
vk::Format format) noexcept;
AttachmentLayout layout_initial;
AttachmentLayout layout_in_pass;
AttachmentLayout layout_final;
AttachmentOperation store_operation;
AttachmentOperation load_operation;
vk::Format format;
};
struct PassConfig
{
PassConfig() = delete;
explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept;
std::vector<AttachmentDescription> attachments{};
};
}
\ No newline at end of file
/**
* @authors Mara Vogt, Mark Mints
* @file src/vkcv/Pipeline.hpp
* @brief Pipeline class to handle shader stages
*/
#ifndef VKCV_PIPELINECONFIG_HPP
#define VKCV_PIPELINECONFIG_HPP
#include <vector>
#include <cstdint>
#include "vkcv/Handles.hpp"
#include "ShaderProgram.hpp"
namespace vkcv {
class PipelineConfig {
public:
/**
* Default constructer is deleted!
*/
PipelineConfig() = delete;
/**
* Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the
* dimensions of the application window @p width and @p height. A handle for the Render Pass is also needed, @p passHandle.
*
* @param shaderProgram shaders of the pipeline
* @param height height of the application window
* @param width width of the application window
* @param passHandle handle for Render Pass
*/
PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle);
ShaderProgram m_ShaderProgram;
uint32_t m_Height;
uint32_t m_Width;
PassHandle m_PassHandle;
};
}
#endif //VKCV_PIPELINECONFIG_HPP
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv {
class QueueManager {
public:
static QueueManager create(vk::Device device,
std::vector<std::pair<int, int>> &queuePairsGraphics,
std::vector<std::pair<int, int>> &queuePairsCompute,
std::vector<std::pair<int, int>> &queuePairsTransfer);
const vk::Queue &getPresentQueue() const;
const std::vector<vk::Queue> &getGraphicsQueues() const;
const std::vector<vk::Queue> &getComputeQueues() const;
const std::vector<vk::Queue> &getTransferQueues() const;
static void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice,
std::vector<float> &queuePriorities,
std::vector<vk::QueueFlagBits> &queueFlags,
std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos,
std::vector<std::pair<int, int>> &queuePairsGraphics,
std::vector<std::pair<int, int>> &queuePairsCompute,
std::vector<std::pair<int, int>> &queuePairsTransfer);
private:
vk::Queue m_presentQueue;
std::vector<vk::Queue> m_graphicsQueues;
std::vector<vk::Queue> m_computeQueues;
std::vector<vk::Queue> m_transferQueues;
QueueManager(std::vector<vk::Queue> graphicsQueues, std::vector<vk::Queue> computeQueues, std::vector<vk::Queue> transferQueues, vk::Queue presentQueue);
};
}
#pragma once
/**
* @authors Simeon Hermann, Leonie Franken
* @file src/vkcv/ShaderProgram.hpp
* @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline
*/
#include <unordered_map>
#include <fstream>
#include <iostream>
#include <filesystem>
#include <vulkan/vulkan.hpp>
namespace vkcv {
enum class ShaderStage
{
VERTEX,
TESS_CONTROL,
TESS_EVAL,
GEOMETRY,
FRAGMENT,
COMPUTE
};
struct Shader
{
std::vector<char> shaderCode;
ShaderStage shaderStage;
};
class ShaderProgram
{
public:
ShaderProgram() noexcept; // ctor
~ShaderProgram() = default; // dtor
/**
* Adds a shader into the shader program.
* The shader is only added if the shader program does not contain the particular shader stage already.
* Contains: (1) reading of the code, (2) creation of a shader module, (3) creation of a shader stage, (4) adding to the shader stage list, (5) destroying of the shader module
* @param[in] flag that signals the respective shaderStage (e.g. VK_SHADER_STAGE_VERTEX_BIT)
* @param[in] relative path to the shader code (e.g. "../../../../../shaders/vert.spv")
*/
bool addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath);
/**
* Returns the shader program's shader of the specified shader.
* Needed for the transfer to the pipeline.
* @return Shader object consisting of buffer with shader code and shader stage enum
*/
const Shader &getShader(ShaderStage shaderStage) const;
bool existsShader(ShaderStage shaderStage) const;
private:
std::unordered_map<ShaderStage, Shader> m_Shaders;
};
}
#pragma once
#include "vulkan/vulkan.hpp"
#include "Context.hpp"
#include "vkcv/Window.hpp"
namespace vkcv {
class SwapChain final {
private:
vk::SurfaceKHR m_surface;
vk::SwapchainKHR m_swapchain;
vk::SurfaceFormatKHR m_format;
uint32_t m_ImageCount;
/**
* Constructor of a SwapChain object
* glfw is not initialized in this class because ist must be sure that there exists a context first
* glfw is already initialized by the window class
* @param surface used by the swapchain
* @param swapchain to show images in the window
* @param format
*/
SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount);
public:
SwapChain(const SwapChain &other) = default;
SwapChain(SwapChain &&other) = default;
/**
* @return The swapchain linked with the #SwapChain class
* @note The reference to our Swapchain variable is needed for the recreation step
*/
[[nodiscard]]
const vk::SwapchainKHR& getSwapchain() const;
/**
* gets the current surface object
* @return current surface
*/
[[nodiscard]]
vk::SurfaceKHR getSurface();
/**
* gets the current surface format
* @return gets the surface format
*/
[[nodiscard]]
vk::SurfaceFormatKHR getSurfaceFormat();
/**
* creates a swap chain object out of the given window and the given context
* @param window a wrapper that represents a glfw window
* @param context of the application
* @return returns an object of swapChain
*/
static SwapChain create(const Window &window, const Context &context, const vk::SurfaceKHR surface);
/**
* Destructor of SwapChain
*/
virtual ~SwapChain();
/**
* @return number of images in swapchain
*/
uint32_t getImageCount();
};
}
#pragma once
#include <vulkan/vulkan.hpp>
namespace vkcv {
struct SyncResources {
vk::Semaphore renderFinished;
vk::Fence swapchainImageAcquired;
vk::Fence presentFinished;
};
SyncResources createDefaultSyncResources(const vk::Device& device);
void destroySyncResources(const vk::Device& device, const SyncResources& resources);
}
\ No newline at end of file
......@@ -4,19 +4,17 @@
* @file src/vkcv/Window.hpp
* @brief Window class to handle a basic rendering surface and input
*/
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#define NOMINMAX
#include <algorithm>
namespace vkcv {
class Window final {
private:
GLFWwindow *m_window;
/**
*
* @param GLFWwindow of the class
......@@ -32,8 +30,7 @@ namespace vkcv {
* @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);
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
......@@ -54,20 +51,6 @@ namespace vkcv {
[[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!
*
......@@ -84,10 +67,25 @@ namespace vkcv {
*/
Window &operator=(Window &&other) = default;
/**
* gets the window width
* @param window glfwWindow
* @return int with window width
*/
[[nodiscard]]
int getWidth() const;
/**
* gets the window height
* @param window glfwWindow
* @return int with window height
*/
[[nodiscard]]
int getHeight() const;
/**
* Destructor of #Window, terminates GLFW
*/
virtual ~Window();
};
}
\ No newline at end of file
......@@ -5,6 +5,9 @@ project(first_triangle)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders/vert.spv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/shaders)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders/frag.spv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/shaders)
# adding source files to the project
add_executable(first_triangle src/main.cpp)
......
%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
/**
* @authors Sebastian Gaida
* @file src/vkcv/CoreManager.cpp
* @authors Artur Wasmut
* @file src/vkcv/Core.cpp
* @brief Handling of global states regarding dependencies
*/
#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.
......@@ -76,53 +82,6 @@ namespace vkcv
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.
......@@ -146,14 +105,26 @@ namespace vkcv
return true;
}
Core Core::create(const char *applicationName,
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;
}
Core Core::create(const Window &window,
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();
......@@ -190,9 +161,9 @@ namespace vkcv
throw std::runtime_error("The requested instance extensions are not supported!");
}
#ifndef NDEBUG
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
// 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,
......@@ -231,35 +202,75 @@ namespace vkcv
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
const vk::SurfaceKHR surface = createSurface(window.getWindow(), instance, physicalDevice);
std::vector<vk::DeviceQueueCreateInfo> qCreateInfos;
// 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
);
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);
// TODO: implement device.getQueue() to access the queues, if needed
Context context(instance, physicalDevice, device);
return Core(std::move(context));
QueueManager queueManager = QueueManager::create(device, queuePairsGraphics, queuePairsCompute, queuePairsTransfer);
Context context (instance, physicalDevice, device);
SwapChain swapChain = SwapChain::create(window, context, surface);
std::vector<vk::Image> swapChainImages = device.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
);
imageViews.push_back( device.createImageView( imageViewCreateInfo ));
}
const int graphicQueueFamilyIndex = queuePairsGraphics[0].first;
const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex);
const auto defaultSyncResources = createDefaultSyncResources(context.getDevice());
return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources, queueManager);
}
const Context &Core::getContext() const
......@@ -267,7 +278,129 @@ namespace vkcv
return m_Context;
}
Core::Core(Context &&context) noexcept :
m_Context(std::move(context))
{}
Core::Core(Context &&context, const Window &window , SwapChain swapChain, std::vector<vk::ImageView> imageViews,
const CommandResources& commandResources, const SyncResources& syncResources, const QueueManager& queueManager) 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),
m_QueueManager(queueManager)
{}
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)
{
const vk::RenderPass &pass = m_PassManager->getVkPass(config.m_PassHandle);
return m_PipelineManager->createPipeline(config, pass);
}
PassHandle Core::createPass(const PassConfig &config)
{
return m_PassManager->createPass(config);
}
uint32_t Core::acquireSwapchainImage() {
uint32_t index;
m_Context.getDevice().acquireNextImageKHR(m_swapchain.getSwapchain(), 0, nullptr,
m_SyncResources.swapchainImageAcquired, &index, {});
const uint64_t timeoutPeriodNs = 1000; // TODO: think if is adequate
const auto& result = m_Context.getDevice().waitForFences(m_SyncResources.swapchainImageAcquired, true, timeoutPeriodNs);
m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired);
if (result == vk::Result::eTimeout) {
index = std::numeric_limits<uint32_t>::max();
}
return index;
}
void Core::destroyTemporaryFramebuffers() {
for (const vk::Framebuffer f : m_TemporaryFramebuffers) {
m_Context.getDevice().destroyFramebuffer(f);
}
m_TemporaryFramebuffers.clear();
}
void Core::beginFrame() {
m_currentSwapchainImageIndex = acquireSwapchainImage();
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
std::cerr << "Drop frame!" << std::endl;
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 vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished);
m_QueueManager.getGraphicsQueues()[0].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);
m_QueueManager.getPresentQueue().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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment