Skip to content
Snippets Groups Projects
Commit 2de7a961 authored by Alexander Gauggel's avatar Alexander Gauggel
Browse files

Merge branch '16-swapchain-class' into 18-first-triangle

parents a1df652c a48f433a
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"
This commit is part of merge request !5. Comments created here will be created in the context of that merge request.
......@@ -12,4 +12,7 @@ set(vkcv_sources
${vkcv_include}/vkcv/Window.hpp
${vkcv_source}/vkcv/Window.cpp
${vkcv_include}/vkcv/SwapChain.hpp
${vkcv_source}/vkcv/SwapChain.cpp
)
......@@ -6,6 +6,8 @@
#include <vulkan/vulkan.hpp>
#include "vkcv/Context.hpp"
#include "vkcv/SwapChain.hpp"
#include "vkcv/Window.hpp"
#include "vkcv/Handles.hpp"
namespace vkcv
......@@ -24,16 +26,20 @@ 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) noexcept;
// explicit destruction of default constructor
Core() = delete;
Context m_Context;
SwapChain m_swapchain;
std::vector<vk::ImageView> m_swapchainImageViews;
const Window& m_window;
public:
/**
* Destructor of #Core destroys the Vulkan objects contained in the core's context.
*/
~Core() noexcept = default;
~Core();
/**
* Copy-constructor of #Core is deleted!
......@@ -84,7 +90,8 @@ namespace vkcv
* @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 = {},
......
#pragma once
#include "vulkan/vulkan.hpp"
#include "Context.hpp"
#include "vkcv/Window.hpp"
#include <iostream>
// glfw is not initialized in this class because ist must be sure that there exists a context first
// glfw is already initialized by the context or the window class
namespace vkcv {
class SwapChain final {
private:
vk::SurfaceKHR m_surface;
const vkcv::Context& m_context;
vk::SwapchainKHR m_swapchain;
vk::SurfaceFormatKHR m_format;
SwapChain(vk::SurfaceKHR surface, const vkcv::Context &context, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format);
public:
// bin mir grade unsicher wegen der Mehrfachinstanziierung der Klasse
// es muessen ja oefter mal neue erstellt werden, aber diese existieren ja nicht gleichzeitig, oder?
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]]
vk::SwapchainKHR getSwapchain();
[[nodiscard]]
vk::SurfaceKHR getSurface();
[[nodiscard]]
vk::SurfaceFormatKHR getSurfaceFormat();
static SwapChain create(const Window &window, const Context &context);
virtual ~SwapChain();
};
}
......@@ -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
#include <iostream>
#include <vkcv/Core.hpp>
#include <vkcv/Window.hpp>
int main(int argc, const char** argv) {
const char* applicationName = "First Triangle";
vkcv::Window window = vkcv::Window::create(
vkcv::Window window = vkcv::Window::create(
applicationName,
800,
600,
false
);
800,
600,
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_KHR_swapchain"}
);
const auto &context = core.getContext();
......
#include <iostream>
#include "vkcv/Context.hpp"
namespace vkcv
......@@ -35,6 +36,7 @@ namespace vkcv
Context::~Context() noexcept
{
std::cout<< " Context " << std::endl;
m_Device.destroy();
m_Instance.destroy();
}
......
/**
* @authors Sebastian Gaida
* @file src/vkcv/CoreManager.cpp
* @authors Artur Wasmut
* @file src/vkcv/Core.cpp
* @brief Handling of global states regarding dependencies
*/
......@@ -146,14 +146,46 @@ 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;
}
/**
* @brief finds an queue family index that fits with the given queue flags to create a queue handle
* @param flag The given flag that specifies as which queue type the accessed queue should be treated
* @param createInfos The createInfos of the created queues depending on the logical device
* @param device The physical with which the queue families can be accessed
* @return a fitting queue family index
*/
int findQueueFamilyIndex(vk::QueueFlagBits flag, std::vector<vk::DeviceQueueCreateInfo> &createInfos, vk::PhysicalDevice &device){
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = device.getQueueFamilyProperties();
for (auto i = createInfos.begin(); i != createInfos.end(); ++i ) {
auto createInfo = *i;
int index = createInfo.queueFamilyIndex;
if(static_cast<uint32_t>(queueFamilyProperties[index].queueFlags & flag) != 0){
return index;
}
}
return -1;
}
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 +222,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,
......@@ -256,10 +288,56 @@ namespace vkcv
vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
// TODO: implement device.getQueue() to access the queues, if needed
uint32_t graphicsQueueFamilyIndex = findQueueFamilyIndex({vk::QueueFlagBits::eGraphics}, qCreateInfos, physicalDevice);
if(graphicsQueueFamilyIndex == -1){
throw std::runtime_error("It is not possible to access another queue as a graphics queue.");
}
uint32_t computeQueueFamilyIndex = findQueueFamilyIndex({vk::QueueFlagBits::eCompute}, qCreateInfos, physicalDevice);
if(computeQueueFamilyIndex == -1){
throw std::runtime_error("It is not possible to access another queue as a compute queue.");
}
uint32_t transferQueueFamilyIndex = findQueueFamilyIndex({vk::QueueFlagBits::eTransfer}, qCreateInfos, physicalDevice);
if(transferQueueFamilyIndex == -1){
throw std::runtime_error("It is not possible to access another queue as a transfer queue.");
}
vk::Queue graphicsQueue = device.getQueue( graphicsQueueFamilyIndex, 0 );
vk::Queue computeQueue = device.getQueue(computeQueueFamilyIndex,1);
vk::Queue transferQueue = device.getQueue(transferQueueFamilyIndex,2);
Context context(instance, physicalDevice, device);
return Core(std::move(context));
SwapChain swapChain = SwapChain::create(window, context);
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
// ToDo: we need the format from the surface object
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 ) );
}
return Core(std::move(context) , window, swapChain, imageViews);
}
const Context &Core::getContext() const
......@@ -267,7 +345,21 @@ 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) noexcept :
m_Context(std::move(context)),
m_window(window),
m_swapchain(swapChain),
m_swapchainImageViews(imageViews)
{}
Core::~Core() {
std::cout<< " Core " << std::endl;
for( auto image: m_swapchainImageViews ){
m_Context.getDevice().destroyImageView(image);
}
m_Context.getDevice().destroySwapchainKHR(m_swapchain.getSwapchain());
m_Context.getInstance().destroySurfaceKHR( m_swapchain.getSurface() );
}
}
#include <vkcv/SwapChain.hpp>
namespace vkcv {
SwapChain::SwapChain(vk::SurfaceKHR surface, const vkcv::Context &context, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format )
: m_surface(surface), m_context(context), m_swapchain(swapchain), m_format( format)
{}
vk::SwapchainKHR SwapChain::getSwapchain() {
return m_swapchain;
}
vk::SurfaceKHR SwapChain::getSurface() {
return m_surface;
}
vk::SurfaceFormatKHR SwapChain::getSurfaceFormat(){
return m_format;
}
vk::SurfaceKHR createSurface(GLFWwindow *window, const vk::Instance &instance, const vk::PhysicalDevice& physicalDevice) {
//create surface
VkSurfaceKHR surface;
// 0 means VK_SUCCESS
//std::cout << "FAIL: " << glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &newSurface) << std::endl;
if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create a window surface!");
}
vk::Bool32 surfaceSupport = false;
// ToDo: hierfuer brauchen wir jetzt den queuefamiliy Index -> siehe ToDo in Context.cpp
// frage: nimmt die Swapchain automatisch den 0'ten Index (Graphics Queue Family)?
if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) {
throw std::runtime_error("surface is not supported by the device!");
}
return vk::SurfaceKHR(surface);
}
vk::Extent2D chooseSwapExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
if(physicalDevice.getSurfaceCapabilitiesKHR(surface,&surfaceCapabilities) != vk::Result::eSuccess){
throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface.");
}
VkExtent2D extent2D = {
static_cast<uint32_t>(window.getWidth()),
static_cast<uint32_t>(window.getHeight())
};
extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width));
extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height));
if (extent2D.width > surfaceCapabilities.maxImageExtent.width ||
extent2D.width < surfaceCapabilities.minImageExtent.width ||
extent2D.height > surfaceCapabilities.maxImageExtent.height ||
extent2D.height < surfaceCapabilities.minImageExtent.height) {
std::printf("Surface size not matching. Resizing to allowed value.");
}
return extent2D;
}
vk::SurfaceFormatKHR chooseSwapSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
uint32_t formatCount;
physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, nullptr);
std::vector<vk::SurfaceFormatKHR> availableFormats(formatCount);
if (physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, &availableFormats[0]) != vk::Result::eSuccess) {
throw std::runtime_error("Failed to get surface formats");
}
for (const auto& availableFormat : availableFormats) {
if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
return availableFormat;
}
}
return availableFormats[0];
}
vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
uint32_t modeCount;
physicalDevice.getSurfacePresentModesKHR( surface, &modeCount, nullptr );
std::vector<vk::PresentModeKHR> availablePresentModes(modeCount);
if (physicalDevice.getSurfacePresentModesKHR(surface, &modeCount, &availablePresentModes[0]) != vk::Result::eSuccess) {
throw std::runtime_error("Failed to get presentation modes");
}
for (const auto& availablePresentMode : availablePresentModes) {
if (availablePresentMode == vk::PresentModeKHR::eMailbox) {
return availablePresentMode;
}
}
return vk::PresentModeKHR::eFifo;
}
uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) {
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess){
throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface.");
}
uint32_t imageCount = surfaceCapabilities.minImageCount + 1; // minImageCount should always be at least 2; set to 3 for triple buffering
// check if requested image count is supported
if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) {
imageCount = surfaceCapabilities.maxImageCount;
}
return imageCount;
}
SwapChain SwapChain::create(const Window &window, const Context &context) {
const vk::Instance& instance = context.getInstance();
const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
const vk::Device& device = context.getDevice();
vk::SurfaceKHR surface = createSurface(window.getWindow(),instance,physicalDevice);
vk::Extent2D extent2D = chooseSwapExtent(physicalDevice, surface, window);
vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(physicalDevice, surface);
vk::PresentModeKHR presentMode = choosePresentMode(physicalDevice, surface);
uint32_t imageCount = chooseImageCount(physicalDevice, surface);
vk::SwapchainCreateInfoKHR swapchainCreateInfo(
vk::SwapchainCreateFlagsKHR(), //flags
surface, // surface
imageCount, // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard)
surfaceFormat.format, // imageFormat
surfaceFormat.colorSpace, // imageColorSpace
extent2D, // imageExtent
1, // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers"
vk::ImageUsageFlagBits::eColorAttachment, // imageUsage TODO: what attachments? only color? depth?
vk::SharingMode::eExclusive, // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues"
0, // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT
nullptr, // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT
vk::SurfaceTransformFlagBitsKHR::eIdentity, // preTransform, transformations applied onto the image before display
vk::CompositeAlphaFlagBitsKHR::eOpaque, // compositeAlpha, TODO: how to handle transparent pixels? do we need transparency? If no -> opaque
presentMode, // presentMode
true, // clipped
nullptr // oldSwapchain
);
vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo);
return SwapChain(surface, context, swapchain, surfaceFormat);
}
SwapChain::~SwapChain() {
std::cout<< " Swap " << std::endl;
// m_context.getDevice().destroySwapchainKHR( m_swapchain );
// m_context.getInstance().destroySurfaceKHR( m_surface );
}
}
......@@ -6,6 +6,7 @@
#include "vkcv/Window.hpp"
namespace vkcv {
static uint32_t s_WindowCount = 0;
......@@ -18,14 +19,15 @@ namespace vkcv {
glfwDestroyWindow(m_window);
s_WindowCount--;
if(s_WindowCount == 0)
if(s_WindowCount == 0) {
glfwTerminate();
}
}
Window Window::create(const char *windowTitle, int width, int height, bool resizable) {
if(s_WindowCount == 0)
Window Window::create( const char *windowTitle, int width, int height, bool resizable) {
if(s_WindowCount == 0) {
glfwInit();
}
s_WindowCount++;
width = std::max(width, 1);
......@@ -35,9 +37,8 @@ namespace vkcv {
glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
GLFWwindow *window;
window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
return Window(window);
return Window(window);
}
bool Window::isWindowOpen() const {
......@@ -48,10 +49,6 @@ namespace vkcv {
glfwPollEvents();
}
GLFWwindow *Window::getWindow() const {
return m_window;
}
int Window::getWidth() const {
int width;
glfwGetWindowSize(m_window, &width, nullptr);
......@@ -63,4 +60,8 @@ namespace vkcv {
glfwGetWindowSize(m_window, nullptr, &height);
return height;
}
}
\ No newline at end of file
GLFWwindow *Window::getWindow() const {
return m_window;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment