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
#include "PipelineManager.hpp"
namespace vkcv
{
PipelineManager::PipelineManager(vk::Device device) noexcept :
m_Device{device},
m_Pipelines{},
m_PipelineLayouts{},
m_NextPipelineId{1}
{}
PipelineManager::~PipelineManager() noexcept
{
for(const auto &pipeline: m_Pipelines)
m_Device.destroy(pipeline);
for(const auto &layout : m_PipelineLayouts)
m_Device.destroy(layout);
m_Pipelines.clear();
m_PipelineLayouts.clear();
m_NextPipelineId = 1;
}
PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, const vk::RenderPass &pass)
{
const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX);
const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT);
if (!(existsVertexShader && existsFragmentShader))
{
std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl;
return PipelineHandle{0};
}
// vertex shader stage
std::vector<char> vertexCode = config.m_ShaderProgram.getShader(ShaderStage::VERTEX).shaderCode;
vk::ShaderModuleCreateInfo vertexModuleInfo({}, vertexCode.size(), reinterpret_cast<uint32_t*>(vertexCode.data()));
vk::ShaderModule vertexModule{};
if (m_Device.createShaderModule(&vertexModuleInfo, nullptr, &vertexModule) != vk::Result::eSuccess)
return PipelineHandle{0};
vk::PipelineShaderStageCreateInfo pipelineVertexShaderStageInfo(
{},
vk::ShaderStageFlagBits::eVertex,
vertexModule,
"main",
nullptr
);
// fragment shader stage
std::vector<char> fragCode = config.m_ShaderProgram.getShader(ShaderStage::FRAGMENT).shaderCode;
vk::ShaderModuleCreateInfo fragmentModuleInfo({}, fragCode.size(), reinterpret_cast<uint32_t*>(fragCode.data()));
vk::ShaderModule fragmentModule{};
if (m_Device.createShaderModule(&fragmentModuleInfo, nullptr, &fragmentModule) != vk::Result::eSuccess)
{
m_Device.destroy(vertexModule);
return PipelineHandle{0};
}
vk::PipelineShaderStageCreateInfo pipelineFragmentShaderStageInfo(
{},
vk::ShaderStageFlagBits::eFragment,
fragmentModule,
"main",
nullptr
);
// vertex input state
vk::VertexInputBindingDescription vertexInputBindingDescription(0, 12, vk::VertexInputRate::eVertex);
vk::VertexInputAttributeDescription vertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0);
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo(
{}, // no vertex input until vertex buffer is implemented
0, // 1,
nullptr, // &vertexInputBindingDescription,
0, // 1,
nullptr // &vertexInputAttributeDescription
);
// input assembly state
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
{},
vk::PrimitiveTopology::eTriangleList,
false
);
// viewport state
vk::Viewport viewport(0.f, 0.f, static_cast<float>(config.m_Width), static_cast<float>(config.m_Height), 0.f, 1.f);
vk::Rect2D scissor({ 0,0 }, { config.m_Width, config.m_Height });
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo({}, 1, &viewport, 1, &scissor);
// rasterization state
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
{},
false,
false,
vk::PolygonMode::eFill,
vk::CullModeFlagBits::eNone,
vk::FrontFace::eCounterClockwise,
false,
0.f,
0.f,
0.f,
1.f
);
// multisample state
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
{},
vk::SampleCountFlagBits::e1,
false,
0.f,
nullptr,
false,
false
);
// color blend state
vk::ColorComponentFlags colorWriteMask(VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT);
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState(
false,
vk::BlendFactor::eOne,
vk::BlendFactor::eOne,
vk::BlendOp::eAdd,
vk::BlendFactor::eOne,
vk::BlendFactor::eOne,
vk::BlendOp::eAdd,
colorWriteMask
);
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
{},
false,
vk::LogicOp::eClear,
1, //TODO: hardcoded to one
&colorBlendAttachmentState,
{ 1.f,1.f,1.f,1.f }
);
// pipeline layout
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo(
{},
0,
{},
0,
{}
);
vk::PipelineLayout vkPipelineLayout{};
if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess)
{
m_Device.destroy(vertexModule);
m_Device.destroy(fragmentModule);
return PipelineHandle{0};
}
// graphics pipeline create
std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo };
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo(
{},
static_cast<uint32_t>(shaderStages.size()),
shaderStages.data(),
&pipelineVertexInputStateCreateInfo,
&pipelineInputAssemblyStateCreateInfo,
nullptr,
&pipelineViewportStateCreateInfo,
&pipelineRasterizationStateCreateInfo,
&pipelineMultisampleStateCreateInfo,
nullptr,
&pipelineColorBlendStateCreateInfo,
nullptr,
vkPipelineLayout,
pass,
0,
{},
0
);
vk::Pipeline vkPipeline{};
if (m_Device.createGraphicsPipelines(nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) != vk::Result::eSuccess)
{
m_Device.destroy(vertexModule);
m_Device.destroy(fragmentModule);
return PipelineHandle{0};
}
m_Device.destroy(vertexModule);
m_Device.destroy(fragmentModule);
m_Pipelines.push_back(vkPipeline);
m_PipelineLayouts.push_back(vkPipelineLayout);
return PipelineHandle{m_NextPipelineId++};
}
vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const
{
return m_Pipelines.at(handle.id -1);
}
vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const
{
return m_PipelineLayouts.at(handle.id - 1);
}
}
\ No newline at end of file
#pragma once
#include <vulkan/vulkan.hpp>
#include <vector>
#include "vkcv/Handles.hpp"
#include "vkcv/PipelineConfig.hpp"
namespace vkcv
{
class PipelineManager
{
private:
vk::Device m_Device;
std::vector<vk::Pipeline> m_Pipelines;
std::vector<vk::PipelineLayout> m_PipelineLayouts;
uint64_t m_NextPipelineId;
public:
PipelineManager() = delete; // no default ctor
explicit PipelineManager(vk::Device device) noexcept; // ctor
~PipelineManager() noexcept; // dtor
PipelineManager(const PipelineManager &other) = delete; // copy-ctor
PipelineManager(PipelineManager &&other) = delete; // move-ctor;
PipelineManager & operator=(const PipelineManager &other) = delete; // copy-assign op
PipelineManager & operator=(PipelineManager &&other) = delete; // move-assign op
PipelineHandle createPipeline(const PipelineConfig &config, const vk::RenderPass &pass);
[[nodiscard]]
vk::Pipeline getVkPipeline(const PipelineHandle &handle) const;
[[nodiscard]]
vk::PipelineLayout getVkPipelineLayout(const PipelineHandle &handle) const;
};
}
#include <limits>
#include <unordered_set>
#include "vkcv/QueueManager.hpp"
#include "vkcv/QueueManager.hpp"
namespace vkcv {
/**
* Given the @p physicalDevice and the @p queuePriorities, the @p queueCreateInfos are computed. First, the requested
* queues are sorted by priority depending on the availability of queues in the queue families of the given
* @p physicalDevice. Then check, if all requested queues are creatable. If so, the @p queueCreateInfos will be computed.
* Furthermore, lists of index pairs (queueFamilyIndex, queueIndex) for later referencing of the separate queues will
* be computed.
* @param[in] physicalDevice The physical device
* @param[in] queuePriorities The queue priorities used for the computation of @p queueCreateInfos
* @param[in] queueFlags The queue flags requesting the queues
* @param[in,out] queueCreateInfos The queue create info structures to be created
* @param[in,out] queuePairsGraphics The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
* vk::QueueFlagBits::eGraphics
* @param[in,out] queuePairsCompute The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
* vk::QueueFlagBits::eCompute
* @param[in,out] queuePairsTransfer The list of index pairs (queueFamilyIndex, queueIndex) of queues of type
* vk::QueueFlagBits::eTransfer
* @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due to insufficient availability.
*/
void QueueManager::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)
{
queueCreateInfos = {};
queuePairsGraphics = {};
queuePairsCompute = {};
queuePairsTransfer = {};
std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
//check priorities of flags -> the lower prioCount the higher the priority
std::vector<int> prios;
for(auto flag: queueFlags) {
int prioCount = 0;
for (int i = 0; i < qFamilyProperties.size(); i++) {
prioCount += (static_cast<uint32_t>(flag & qFamilyProperties[i].queueFlags) != 0) * qFamilyProperties[i].queueCount;
}
prios.push_back(prioCount);
}
//resort flags with heighest priority before allocating the queues
std::vector<vk::QueueFlagBits> newFlags;
for(int i = 0; i < prios.size(); i++) {
auto minElem = std::min_element(prios.begin(), prios.end());
int index = minElem - prios.begin();
newFlags.push_back(queueFlags[index]);
prios[index] = std::numeric_limits<int>::max();
}
// create requested queues and check if more requested queues are supported
// herefore: create vector that updates available queues in each queue family
// structure: [qFamily_0, ..., qFamily_n] where
// - qFamily_i = [GraphicsCount, ComputeCount, TransferCount], 0 <= i <= n
std::vector<std::vector<int>> queueFamilyStatus, initialQueueFamilyStatus;
for (auto qFamily : qFamilyProperties) {
int graphicsCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0) * qFamily.queueCount;
int computeCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0) * qFamily.queueCount;
int transferCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0) * qFamily.queueCount;
queueFamilyStatus.push_back({graphicsCount, computeCount, transferCount});
}
initialQueueFamilyStatus = queueFamilyStatus;
// check if every queue with the specified queue flag can be created
// this automatically checks for queue flag support!
for (auto qFlag : newFlags) {
bool found;
switch (qFlag) {
case vk::QueueFlagBits::eGraphics:
found = false;
for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
if (queueFamilyStatus[i][0] > 0) {
queuePairsGraphics.push_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]));
queueFamilyStatus[i][0]--;
queueFamilyStatus[i][1]--;
queueFamilyStatus[i][2]--;
found = true;
}
}
if (!found) {
throw std::runtime_error("Too many graphics queues were requested than being available!");
}
break;
case vk::QueueFlagBits::eCompute:
found = false;
for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
if (queueFamilyStatus[i][1] > 0) {
queuePairsCompute.push_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1]));
queueFamilyStatus[i][0]--;
queueFamilyStatus[i][1]--;
queueFamilyStatus[i][2]--;
found = true;
}
}
if (!found) {
throw std::runtime_error("Too many compute queues were requested than being available!");
}
break;
case vk::QueueFlagBits::eTransfer:
found = false;
for (int i = 0; i < queueFamilyStatus.size() && !found; i++) {
if (queueFamilyStatus[i][2] > 0) {
queuePairsTransfer.push_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2]));
queueFamilyStatus[i][0]--;
queueFamilyStatus[i][1]--;
queueFamilyStatus[i][2]--;
found = true;
}
}
if (!found) {
throw std::runtime_error("Too many transfer queues were requested than being available!");
}
break;
default:
throw std::runtime_error("Invalid input for queue flag bits. Valid inputs are 'vk::QueueFlagBits::eGraphics', 'vk::QueueFlagBits::eCompute' and 'vk::QueueFlagBits::eTransfer'.");
}
}
// create all requested queues
for (int i = 0; i < qFamilyProperties.size(); i++) {
uint32_t create = std::abs(initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]);
if (create > 0) {
vk::DeviceQueueCreateInfo qCreateInfo(
vk::DeviceQueueCreateFlags(),
i,
create,
queuePriorities.data()
);
queueCreateInfos.push_back(qCreateInfo);
}
}
}
/**
* Computes the queue handles from @p queuePairs
* @param device The device
* @param queuePairs The queuePairs that were created separately for each queue type (e.g., vk::QueueFlagBits::eGraphics)
* @return An array of queue handles based on the @p queuePairs
*/
std::vector<Queue> getQueues(const vk::Device& device, const std::vector<std::pair<int, int>>& queuePairs) {
std::vector<Queue> queues;
for (auto q : queuePairs) {
const int queueFamilyIndex = q.first; // the queueIndex of the queue family
const int queueIndex = q.second; // the queueIndex within a queue family
queues.push_back({ queueFamilyIndex, queueIndex, device.getQueue(queueFamilyIndex, queueIndex) });
}
return queues;
}
QueueManager 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) {
std::vector<Queue> graphicsQueues = getQueues(device, queuePairsGraphics);
std::vector<Queue> computeQueues = getQueues(device, queuePairsCompute );
std::vector<Queue> transferQueues = getQueues(device, queuePairsTransfer);
return QueueManager( std::move(graphicsQueues), std::move(computeQueues), std::move(transferQueues), 0);
}
QueueManager::QueueManager(std::vector<Queue>&& graphicsQueues, std::vector<Queue>&& computeQueues, std::vector<Queue>&& transferQueues, size_t presentIndex)
: m_graphicsQueues(graphicsQueues), m_computeQueues(computeQueues), m_transferQueues(transferQueues), m_presentIndex(presentIndex)
{}
const Queue &QueueManager::getPresentQueue() const {
return m_graphicsQueues[m_presentIndex];
}
const std::vector<Queue> &QueueManager::getGraphicsQueues() const {
return m_graphicsQueues;
}
const std::vector<Queue> &QueueManager::getComputeQueues() const {
return m_computeQueues;
}
const std::vector<Queue> &QueueManager::getTransferQueues() const {
return m_transferQueues;
}
}
\ No newline at end of file
/**
* @authors Simeon Hermann, Leonie Franken
* @file src/vkcv/ShaderProgram.cpp
* @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline
*/
#include "vkcv/ShaderProgram.hpp"
namespace vkcv {
/**
* Reads the file of a given shader code.
* Only used within the class.
* @param[in] relative path to the shader code
* @return vector of chars as a buffer for the code
*/
std::vector<char> readShaderCode(const std::filesystem::path &shaderPath)
{
std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary);
if (!file.is_open()) {
std::cout << "The file could not be opened." << std::endl;
return std::vector<char>{};
}
size_t fileSize = (size_t)file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
return buffer;
}
ShaderProgram::ShaderProgram() noexcept :
m_Shaders{}
{}
bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath)
{
if(m_Shaders.find(shaderStage) != m_Shaders.end())
std::cout << "Found existing shader stage. Overwriting." << std::endl;
const std::vector<char> shaderCode = readShaderCode(shaderPath);
if (shaderCode.empty())
return false;
else
{
Shader shader{shaderCode, shaderStage};
m_Shaders.insert(std::make_pair(shaderStage, shader));
return true;
}
}
const Shader &ShaderProgram::getShader(ShaderStage shaderStage) const
{
return m_Shaders.at(shaderStage);
}
bool ShaderProgram::existsShader(ShaderStage shaderStage) const
{
if(m_Shaders.find(shaderStage) == m_Shaders.end())
return false;
else
return true;
}
}
#include "Surface.hpp"
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
namespace vkcv {
/**
* creates surface and checks availability
* @param window current window for the surface
* @param instance Vulkan-Instance
* @param physicalDevice Vulkan-PhysicalDevice
* @return created surface
*/
vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice) {
//create surface
VkSurfaceKHR surface;
if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create a window surface!");
}
vk::Bool32 surfaceSupport = false;
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);
}
}
#pragma once
#include <vulkan/vulkan.hpp>
struct GLFWwindow;
namespace vkcv {
vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice);
}
\ No newline at end of file
#include <vkcv/SwapChain.hpp>
namespace vkcv {
SwapChain::SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount)
: m_surface(surface), m_swapchain(swapchain), m_format( format), m_ImageCount(imageCount)
{}
const vk::SwapchainKHR& SwapChain::getSwapchain() const {
return m_swapchain;
}
/**
* gets surface of the swapchain
* @return current surface
*/
vk::SurfaceKHR SwapChain::getSurface() {
return m_surface;
}
/**
* gets the surface of the swapchain
* @return chosen format
*/
vk::SurfaceFormatKHR SwapChain::getSurfaceFormat(){
return m_format;
}
/**
* chooses Extent and clapms values to the available
* @param physicalDevice Vulkan-PhysicalDevice
* @param surface of the swapchain
* @param window of the current application
* @return chosen Extent for the 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;
}
/**
* chooses Surface Format for the current surface
* @param physicalDevice Vulkan-PhysicalDevice
* @param surface of the swapchain
* @return available Format
*/
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];
}
/**
* returns vk::PresentModeKHR::eMailbox if available or vk::PresentModeKHR::eFifo otherwise
* @param physicalDevice Vulkan-PhysicalDevice
* @param surface of the swapchain
* @return available PresentationMode
*/
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;
}
}
// The FIFO present mode is guaranteed by the spec to be supported
return vk::PresentModeKHR::eFifo;
}
/**
* returns the minImageCount +1 for at least doublebuffering, if it's greater than maxImageCount return maxImageCount
* @param physicalDevice Vulkan-PhysicalDevice
* @param surface of the swapchain
* @return available ImageCount
*/
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;
}
/**
* creates and returns a swapchain with default specs
* @param window of the current application
* @param context that keeps instance, physicalDevice and a device.
* @return swapchain
*/
SwapChain SwapChain::create(const Window &window, const Context &context, const vk::SurfaceKHR surface) {
const vk::Instance& instance = context.getInstance();
const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
const vk::Device& device = context.getDevice();
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, swapchain, surfaceFormat, imageCount);
}
SwapChain::~SwapChain() {
// needs to be destroyed by creator
}
uint32_t SwapChain::getImageCount() {
return m_ImageCount;
}
}
#include "vkcv/SyncResources.hpp"
namespace vkcv {
SyncResources createDefaultSyncResources(const vk::Device& device) {
SyncResources resources;
const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits();
const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags);
resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {});
const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits();
vk::FenceCreateInfo fenceInfo(fenceFlags);
resources.presentFinished = device.createFence(fenceInfo, nullptr, {});
resources.swapchainImageAcquired = device.createFence(fenceInfo, nullptr, {});
return resources;
}
void destroySyncResources(const vk::Device& device, const SyncResources& resources) {
device.destroySemaphore(resources.renderFinished);
device.destroyFence(resources.presentFinished);
device.destroyFence(resources.swapchainImageAcquired);
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@
* @brief Window class to handle a basic rendering surface and input
*/
#include <GLFW/glfw3.h>
#include "vkcv/Window.hpp"
namespace vkcv {
......@@ -18,14 +20,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 +38,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 +50,6 @@ namespace vkcv {
glfwPollEvents();
}
GLFWwindow *Window::getWindow() const {
return m_window;
}
int Window::getWidth() const {
int width;
glfwGetWindowSize(m_window, &width, nullptr);
......@@ -63,4 +61,8 @@ namespace vkcv {
glfwGetWindowSize(m_window, nullptr, &height);
return height;
}
}
\ No newline at end of file
GLFWwindow *Window::getWindow() const {
return m_window;
}
}