Skip to content

Instantly share code, notes, and snippets.

@xdegtyarev
Created October 3, 2025 15:26
Show Gist options
  • Select an option

  • Save xdegtyarev/4aaf50541d9ab351b18218bb4cdc7008 to your computer and use it in GitHub Desktop.

Select an option

Save xdegtyarev/4aaf50541d9ab351b18218bb4cdc7008 to your computer and use it in GitHub Desktop.
vkdemo.cpp
#include <__ranges/subrange.h>
#include <_types/_uint32_t.h>
#include <array>
#include <chrono>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/trigonometric.hpp>
#include <random>
#include <string>
#include <valarray>
#include <vector>
#include <cstddef>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <set>
#include <stdlib.h>
#include <cstdint> // uint32_t
#include <limits> //std::numberic_limits
#include <algorithm> //std::clamp
#include <fstream>//load shaders
#include <vulkan/vk_platform.h>
#include <vulkan/vulkan_core.h>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
#include <glm/glm.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/ext/scalar_uint_sized.hpp>
#include <glm/fwd.hpp>
#include <glm/gtx/hash.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define TINYOBJLOADER_IMPLEMENTATION
#include <tiny_obj_loader.h>
struct UniformBufferObjectTime{
glm::float32 deltaTime;
};
struct UniformBufferObject {
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
struct vertex{
glm::vec3 pos;
glm::vec3 col;
glm::vec2 uv0;
static VkVertexInputBindingDescription getBindingDescription(){
VkVertexInputBindingDescription bindingDescription{};
bindingDescription.binding = 0; // index of binding;
bindingDescription.stride = sizeof(vertex); //bytes from entry to next
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; //move after each vtx or instance (here instance)
return bindingDescription;
}
static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions(){
std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions;
//pos
attributeDescriptions[0].binding = 0; // from which binding data comes
attributeDescriptions[0].location = 0; // to which location in shader it goes
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3;
attributeDescriptions[0].offset = offsetof(vertex, pos);
attributeDescriptions[1].binding = 0; // from which binding data comes
attributeDescriptions[1].location = 1; // to which location in shader it goes
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3;
attributeDescriptions[1].offset = offsetof(vertex, col);
attributeDescriptions[2].binding = 0; // from which binding data comes
attributeDescriptions[2].location = 2; // to which location in shader it goes
attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; // vec3;
attributeDescriptions[2].offset = offsetof(vertex, uv0);
return attributeDescriptions;
}
bool operator==(const vertex& other) const {
return pos == other.pos && col == other.col && uv0 == other.uv0;
}
};
struct particle{
glm::vec2 pos;
glm::vec2 vel;
glm::vec4 col;
static VkVertexInputBindingDescription getBindingDescription(){
VkVertexInputBindingDescription bindingDescription{};
bindingDescription.binding = 0; // index of binding;
bindingDescription.stride = sizeof(particle); //bytes from entry to next
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; //move after each vtx or instance (here instance)
return bindingDescription;
}
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions(){
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions;
//pos
attributeDescriptions[0].binding = 0; // from which binding data comes
attributeDescriptions[0].location = 0; // to which location in shader it goes
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; // vec2;
attributeDescriptions[0].offset = offsetof(particle, pos);
attributeDescriptions[1].binding = 0; // from which binding data comes
attributeDescriptions[1].location = 1; // to which location in shader it goes
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3;
attributeDescriptions[1].offset = offsetof(particle, col);
return attributeDescriptions;
}
};
//hash for vertext type in order to use unordered map
namespace std {
template<> struct hash<vertex> {
size_t operator()(vertex const& vertex) const {
return ((hash<glm::vec3>()(vertex.pos) ^
(hash<glm::vec3>()(vertex.col) << 1)) >> 1) ^
(hash<glm::vec2>()(vertex.uv0) << 1);
}
};
}
//CONFIG + Singletons;
namespace karaage{
const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;
const uint32_t MAX_FRAMES_IN_FLIGHT = 2;
const uint32_t MAX_PARTICLES = 8192;
const char* meshPath = "meshes/viking_room.obj";
const char* texturePath = "textures/viking_room.png";
GLFWwindow* window;
VkInstance vkInstance;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice logicalDevice;
VkQueue graphicsQueue;
VkQueue presentQueue;
VkQueue computeQueue;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
//swapchain
VkSwapchainKHR swapChain;
std::vector<VkImage> swapChainImages;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkFramebuffer> swapChainFramebuffers;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
//color
VkImage colorImage;
VkDeviceMemory colorImageMemory;
VkImageView colorImageView;
//depth
VkImage depthImage;
VkDeviceMemory depthImageMemory;
VkImageView depthImageView;
VkRenderPass renderPass;
VkDescriptorSetLayout descriptorSetLayout;
VkDescriptorPool descriptorPool;
std::vector<VkDescriptorSet> descriptorSets;
VkDescriptorSetLayout computeDescriptorSetLayout;
VkDescriptorPool computeDescriptorPool;
std::vector<VkDescriptorSet> computeDescriptorSets;
VkPipelineLayout pipelineLayout;
VkPipelineLayout particlePipelineLayout;
std::vector<VkPipeline> graphicsPipeline;
VkPipelineLayout graphicsParticlePipelineLayout;
VkPipelineLayout computePipelineLayout;
VkPipeline computePipeline;
VkCommandPool commandPool;
//Vectors cause we allow multiple frames in flight;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> computeCommandBuffers;
//Sync
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkSemaphore> computeFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> computeInFlightFences;
//SSBO for compute;
std::vector<VkBuffer> shaderStorageBuffers;
std::vector<VkDeviceMemory> shaderStorageBuffersMemory;
//
bool framebufferResized = false;
//mesh
std::vector<vertex> vertices;
std::vector<uint32_t> indices;
//VertexData
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
//UBOs
std::vector<VkBuffer> uniformBuffers;
std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<void*> uniformBuffersMapped;
std::vector<VkBuffer> computeUBOs;
std::vector<VkDeviceMemory> computeUBOsMemory;
std::vector<void*> computeUBOsMapped;
//Textures
uint32_t mipLevels;
VkImage textureImage;
VkDeviceMemory textureImageMemory;
VkImageView textureImageView;
VkSampler textureSampler;
//extensions
const std::vector<const char*> physicalDeviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
"VK_KHR_portability_subset"
};
//validation layers
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"//enabling extension
};
#ifdef NDEBUG// NDEBUG is used to mark Not debug code
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
VkDebugUtilsMessengerEXT debugMessenger;
#endif
uint32_t currentFrame = 0;
float lastFrameTime = 0.0f;
double lastTime = 0.0f;
}
//Catching all debug messages from VK layers
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
std::cerr << "[Error] Validation Layers: " << pCallbackData->pMessage << std::endl;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
std::cerr << "[Warning] Validation Layers: " << pCallbackData->pMessage << std::endl;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
std::cerr << "[Info] Validation Layers: " << pCallbackData->pMessage << std::endl;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
std::cerr << "[Verbose] Validation Layers: " << pCallbackData->pMessage << std::endl;
break;
default:
std::cerr << "[Default] Validation Layers: " << pCallbackData->pMessage << std::endl;
break;
}
return VK_FALSE;
}
static std::vector<char> readFile(const std::string& filename){
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if(!file.is_open()){
throw std::runtime_error("[EXCEPTION]Failed to open file");
}
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
VkSampleCountFlagBits getMaxUsableSampleCount(){
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(karaage::physicalDevice, &properties);
VkSampleCountFlags maxSampleCounts =
properties.limits.framebufferColorSampleCounts &
properties.limits.framebufferDepthSampleCounts;
if (maxSampleCounts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
if (maxSampleCounts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
if (maxSampleCounts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
if (maxSampleCounts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
if (maxSampleCounts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
if (maxSampleCounts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
return VK_SAMPLE_COUNT_1_BIT;
}
void onFramebufferResized(GLFWwindow* window, int width, int height){
std::cout
<< "Resizing window request with width: "
<< width
<< " and height: "
<< height
<< std::endl;
karaage::framebufferResized = true;
}
//requesting validation layer support
bool checkValidationLayerSupport(){
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
std::cout << "layers available: " << layerCount << std::endl;
//parse through all validation layers to find if any of them is available
for (const char* layerName: karaage::validationLayers){
bool layerFound = false;
for (const auto& layerProperties: availableLayers){
std::cout << "layer queued " << layerProperties.layerName << std::endl;
if(strcmp(layerName, layerProperties.layerName) == 0){
layerFound = true;
break;
}
}
//nothing found
if(!layerFound){
std::cout << "Layer was not found " << layerName << std::endl;
return false;
}
}
return true;
}
//
void initCLI(){
std::cout
<< std::endl
<< "------------------------------------------------------------------------------------------------"
<< std::endl
<< "karaage engine"
<< std::endl;
}
void initWindow(){
glfwInit();
//calling with no api to avoid OGL context
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
//not yet handling resizing
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
karaage::window = glfwCreateWindow(
karaage::WIDTH,
karaage::HEIGHT,
"karaage",
nullptr,
nullptr);
glfwSetFramebufferSizeCallback(karaage::window, onFramebufferResized);
}
/**
* @brief Gets the required extensions.
*
* @return The required extension.
*/
std::vector<const char*> getRequiredExtensions(){
uint32_t glfwExtentionCount = 0;
const char** glfwExtentions;
glfwExtentions = glfwGetRequiredInstanceExtensions(&glfwExtentionCount);
std::vector<const char*> extensions(glfwExtentions,glfwExtentions + glfwExtentionCount);
if(karaage::enableValidationLayers){
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
return extensions;
}
VkResult CreateDebugUtilsMessengerEXT(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pDebugMessenger){
auto fn = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
instance,
"vkCreateDebugUtilsMessengerEXT"
);
if(fn != nullptr){
return fn(instance,pCreateInfo, pAllocator, pDebugMessenger);
}else{
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
void DestroyDebugUtilsMessengerEXT(
VkInstance instance,
VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator
){
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
instance,
"vkDestroyDebugUtilsMessengerEXT"
);
if(func != nullptr){
func(instance, debugMessenger, pAllocator);
}else{
throw std::runtime_error("[EXCEPTION]Failed to call vkDestroyDebugUtilsMessengerEXT");
}
}
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo){
createInfo = {};
createInfo.sType =
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType =
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
createInfo.pUserData = nullptr; //opt
}
void setupDebugMessenger(){
if(!karaage::enableValidationLayers) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo;
populateDebugMessengerCreateInfo(createInfo);
if(CreateDebugUtilsMessengerEXT(
karaage::vkInstance,
&createInfo, nullptr,
&karaage::debugMessenger
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed to set up debug messenger");
}
}
void createVKInstance(){
if(karaage::enableValidationLayers && !checkValidationLayerSupport()){
throw std::runtime_error("[EXCEPTION]Falidation layers requested, but not available");
}else{
std::cout << "Starting without validation layers" << std::endl;
}
VkApplicationInfo appInfo{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Karaage VK",
.applicationVersion = VK_MAKE_VERSION(0, 0, 1),
.pEngineName = "Karaage",
.engineVersion = VK_MAKE_VERSION(0, 0, 1),
.apiVersion = VK_API_VERSION_1_3
};
VkInstanceCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo
};
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
//adding validation layer names
if(karaage::enableValidationLayers){
createInfo.enabledLayerCount = static_cast<uint32_t>(karaage::validationLayers.size());
createInfo.ppEnabledLayerNames = karaage::validationLayers.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
}else{
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
//forming extensions request
auto enableExtensionsRequest = getRequiredExtensions();
uint32_t enabledExtensionCount = static_cast<uint32_t>(enableExtensionsRequest.size());
createInfo.enabledExtensionCount = enabledExtensionCount;
createInfo.ppEnabledExtensionNames = enableExtensionsRequest.data();
createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
//Creating instance
VkResult result = vkCreateInstance(&createInfo, nullptr, &karaage::vkInstance);
if(result != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed to create instance");
}else{
std::cout << "VK Instance created successfully" << std::endl;
}
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::cout << "Requested: " << enabledExtensionCount << std::endl;
std::cout << "Actual Extensions: " << extensionCount << std::endl;
std::vector<VkExtensionProperties> extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
#ifdef KARAAGE_PRINT_LIST_EXTENSIONS
std::cout << "Listing loaded extensions" << std::endl;
for(const auto& extension: extensions){
std::cout << extension.extensionName << std::endl;
}
#endif
}
struct QueueFamilyIndices{
std::optional<uint32_t> graphicsAndComputeFamily;
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete(){
return graphicsAndComputeFamily.has_value()
&& presentFamily.has_value();
}
};
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice physicalDevice){
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
//checking if any queue has GRAPHICS BIT AND Compute BIT
int i = 0;
for(const auto& queueFamily: queueFamilies){
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(
physicalDevice,
i,
karaage::surface,
&presentSupport
);
//checking if queue is presenting
if(presentSupport){
indices.presentFamily = i;
}
//if graphics and compute
if((queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
(queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT)
) {
indices.graphicsAndComputeFamily = i;
}
//checking if queue is graphics NOT checking now as we have graphics and compute
// if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
// indices.graphicsFamily = i;
// }
//all queues found
if(indices.isComplete()) {
break;
}
++i;
}
return indices;
}
struct SwapChainSupportInfo {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
SwapChainSupportInfo querySwapChainSupportInfo(VkPhysicalDevice physicalDevice){
SwapChainSupportInfo supportInfo;
//getting capabilities
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, karaage::surface, &supportInfo.capabilities);
//getting formats
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, karaage::surface, &formatCount, nullptr);
if(formatCount != 0){
supportInfo.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(
physicalDevice,
karaage::surface,
&formatCount,
supportInfo.formats.data()
);
}
//getting present modes
uint32_t modeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, karaage::surface, &modeCount, nullptr);
if(modeCount != 0){
supportInfo.presentModes.resize(modeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(
physicalDevice,
karaage::surface,
&modeCount,
supportInfo.presentModes.data()
);
}
return supportInfo;
}
//ticking off extensions from req list;
bool checkDeviceExtensionsSupport(VkPhysicalDevice physicalDevice){
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(
karaage::physicalDeviceExtensions.begin(),
karaage::physicalDeviceExtensions.end()
);
for(const auto& extensions: availableExtensions){
requiredExtensions.erase(extensions.extensionName);
}
return requiredExtensions.empty();
}
bool isDeviceSuitable(VkPhysicalDevice physicalDevice){
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
//Getting physical physicalDevice props
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
//Getting physical physicalDevice nfeatures
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures);
bool isSwapChainCompatible = false;
bool isExtensionsSupported = checkDeviceExtensionsSupport(physicalDevice);
if(isExtensionsSupported){
SwapChainSupportInfo swapChainSupportInfo = querySwapChainSupportInfo(physicalDevice);
isSwapChainCompatible
= !swapChainSupportInfo.formats.empty()
&& !swapChainSupportInfo.presentModes.empty();
}
return
(
deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ||
deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
)
&& indices.isComplete()
&& isExtensionsSupported
&& isSwapChainCompatible
&& deviceFeatures.samplerAnisotropy;
}
VkSurfaceFormatKHR chooseSwapChainFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats){
for (const auto& availableFormat: availableFormats){
if(availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB
&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){
return availableFormat;
}
}
//settle with the first available if failing to choose preferred
return availableFormats[0];
}
VkPresentModeKHR choosePresentMode(const std::vector<VkPresentModeKHR>& availableModes){
for(const auto& availableMode: availableModes){
//choosing mailbox is energy is not concern;
if(availableMode == VK_PRESENT_MODE_MAILBOX_KHR){
return availableMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities){
if(capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()){
return capabilities.currentExtent;
}else{
int width, height;
//query current window res;
glfwGetFramebufferSize(karaage::window, &width, &height);
VkExtent2D actualExtent = {
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)
};
actualExtent.width = std::clamp(
actualExtent.width,
capabilities.minImageExtent.width,
capabilities.maxImageExtent.width);
actualExtent.height = std::clamp(
actualExtent.height,
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
return actualExtent;
}
}
void pickPhysicalDevice(){
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(karaage::vkInstance, &deviceCount, nullptr);
if(deviceCount == 0){
throw std::runtime_error("[EXCEPTION]Fkaraage::ERROR] Can't find GPU that supports Vulkan");
}else{
std::cout << "Devices found: " << deviceCount << std::endl;
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(karaage::vkInstance, &deviceCount, devices.data());
//evaluate if devices are suitable
for(const auto& device: devices){
if(isDeviceSuitable(device)){
karaage::physicalDevice = device;
karaage::msaaSamples = getMaxUsableSampleCount();
break;
}
}
if(karaage::physicalDevice == VK_NULL_HANDLE){
throw std::runtime_error("[EXCEPTION]Fkaraage::ERROR] Fetched GPUs don't support Vulkan");
}
}
void createLogicalDevice(){
const uint32_t QUEUE_COUNT = 1;
QueueFamilyIndices indices = findQueueFamilies(karaage::physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = {
indices.graphicsAndComputeFamily.value(),
indices.presentFamily.value()
};
//queue priority is 0..1 and is required
float queuePriority = 1.0f;
for(uint32_t queueFamily: uniqueQueueFamilies){
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = QUEUE_COUNT;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
//defining requried device features
VkPhysicalDeviceFeatures deviceFeatures{
.samplerAnisotropy = VK_TRUE,
.sampleRateShading = VK_TRUE
};
//
VkDeviceCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
//settings queues
.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
//settings features
.pEnabledFeatures = &deviceFeatures,
//settings device-specific extensions (portability subset is requred)
.enabledExtensionCount = static_cast<uint32_t>(karaage::physicalDeviceExtensions.size()),
.ppEnabledExtensionNames = karaage::physicalDeviceExtensions.data(),
};
//settings device-specific validation layers that are ignored
if(karaage::enableValidationLayers){
createInfo.enabledLayerCount = static_cast<uint32_t>(karaage::validationLayers.size());
createInfo.ppEnabledLayerNames = karaage::validationLayers.data();
}else{
createInfo.enabledLayerCount = 0;
}
if(vkCreateDevice(karaage::physicalDevice, &createInfo, nullptr, &karaage::logicalDevice) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed to get logical devices");
}
vkGetDeviceQueue(
karaage::logicalDevice,
indices.graphicsAndComputeFamily.value(),
0,
&karaage::graphicsQueue
);
vkGetDeviceQueue(
karaage::logicalDevice,
indices.graphicsAndComputeFamily.value(),
0,
&karaage::computeQueue
);
vkGetDeviceQueue(
karaage::logicalDevice,
indices.presentFamily.value(),
0,
&karaage::presentQueue
);
}
void createSurface(){
if(glfwCreateWindowSurface(
karaage::vkInstance,
karaage::window,
nullptr,
&karaage::surface
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed to create Window Surface");
}
}
void createSwapChain(){
//request what's avaialable
SwapChainSupportInfo swapChainSupportInfo = querySwapChainSupportInfo(karaage::physicalDevice);
//get best formats
VkSurfaceFormatKHR surfaceFormat = chooseSwapChainFormat(swapChainSupportInfo.formats);
VkPresentModeKHR presentMode = choosePresentMode(swapChainSupportInfo.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupportInfo.capabilities);
//requesting min+1 to wait for driver finish internal operations but less than max;
uint32_t imageCount = swapChainSupportInfo.capabilities.minImageCount + 1;
if(swapChainSupportInfo.capabilities.maxImageCount > 0
&& imageCount > swapChainSupportInfo.capabilities.maxImageCount
){
imageCount = swapChainSupportInfo.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = karaage::surface;
//details
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = extent;
//amount of layers each images has (unless stereoscopic)
createInfo.imageArrayLayers = 1;
//usage type
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
QueueFamilyIndices indices = findQueueFamilies(karaage::physicalDevice);
uint32_t queueFamilyIndices[] = {
indices.graphicsAndComputeFamily.value(),
indices.presentFamily.value()
};
if(indices.graphicsAndComputeFamily != indices.presentFamily){
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
//specifying onwership since graphics and present queues are different
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
}else{
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
//optional params
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
}
//using current transformation
createInfo.preTransform = swapChainSupportInfo.capabilities.currentTransform;
//composite alpha - if alpha should be used for blending with other windows (ignore)
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
//enable unless we really need pixel data for readbacks;
createInfo.clipped = VK_TRUE;
//TODO handle res change and swapchain replacement;
createInfo.oldSwapchain = VK_NULL_HANDLE;
//creating
if(vkCreateSwapchainKHR(
karaage::logicalDevice,
&createInfo,
nullptr,
&karaage::swapChain)
!= VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed to create swapchain");
}
//grab image handles;
vkGetSwapchainImagesKHR(karaage::logicalDevice, karaage::swapChain, &imageCount, nullptr);
karaage::swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(
karaage::logicalDevice,
karaage::swapChain,
&imageCount,
karaage::swapChainImages.data()
);
//store format and extent we used
karaage::swapChainImageFormat = surfaceFormat.format;
karaage::swapChainExtent = extent;
}
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectMask, uint32_t mipLevels){
VkImageViewCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.components = VK_COMPONENT_SWIZZLE_IDENTITY,
.subresourceRange.aspectMask = aspectMask,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.layerCount = 1,
.subresourceRange.levelCount = mipLevels
};
VkImageView imageView;
if(vkCreateImageView(
karaage::logicalDevice,
&createInfo,
nullptr,
&imageView
) != VK_SUCCESS){
throw std::runtime_error("Failed to create texture image view");
}
return imageView;
}
void createImageViews(){
auto imageCount = karaage::swapChainImages.size();
karaage::swapChainImageViews.resize(imageCount);
for(size_t i = 0; i < imageCount; i++){
karaage::swapChainImageViews[i] = createImageView(
karaage::swapChainImages[i],
karaage::swapChainImageFormat,
VK_IMAGE_ASPECT_COLOR_BIT,
1
);
}
}
VkShaderModule createShaderModule(const std::vector<char>& code){
//size of bytecode poiinter is a uint32_t pointer rather than char pointera
//data is aligned as we already use vector;
VkShaderModuleCreateInfo shaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = code.size(),
.pCode = reinterpret_cast<const uint32_t*>(code.data())
};
//creating
VkShaderModule shaderModule;
if(vkCreateShaderModule(karaage::logicalDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create shader module");
}
return shaderModule;
}
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features){
for(auto format:candidates){
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(karaage::physicalDevice, format,&props);
if(tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features)){
return format;
} else if(tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features)){
return format;
}
}
throw std::runtime_error("No suitable format found");
}
VkFormat findDepthFormat(){
return findSupportedFormat(
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
);
}
void createRenderPass(){
VkAttachmentDescription colorAttachement{
//color format should match swapchain
.format = karaage::swapChainImageFormat,
.samples = karaage::msaaSamples,
//load/store op;
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, //clear image on load;
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
//stencil l/s op
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
//layout init/final
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL //MSAA can't be presented directly
};
//definign subpass colorAttachment ref by index
VkAttachmentReference colorAttachementRef{
//which attachement to reference by index in descroptions array
.attachment = 0,
//which layout to have during subpass (vk will auto transition)
//we intent attachement to behave like color buffer;
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
VkAttachmentDescription depthAttachement{
.format = findDepthFormat(),
.samples = karaage::msaaSamples,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
};
VkAttachmentReference depthAttachementRef{
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
};
//resolve target
VkAttachmentDescription colorAttachmentResolve{
.format = karaage::swapChainImageFormat,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, //framebuffer layout
};
//definign subpass colorAttachment ref by index
VkAttachmentReference colorAttachementResolveRef{
.attachment = 2,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
//subpassDependencies:
VkSubpassDependency dependency{
.srcSubpass = VK_SUBPASS_EXTERNAL,//implicit subpass before our;
.dstSubpass = 0,//our subpass
//wait on stages marked here:
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
.srcAccessMask = 0,
//wait in color attachmenet stage involve writing of the color attachement
//no transition until it's necessary and allowed when we want to start writing colors
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
};
//subpassDescription
VkSubpassDescription subpass{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,//bind point (compute/graphics)
.colorAttachmentCount = 1,
.pColorAttachments = &colorAttachementRef,
.pResolveAttachments = &colorAttachementResolveRef,
.pDepthStencilAttachment = &depthAttachementRef
};
std::array<VkAttachmentDescription,3> attachements {colorAttachement,depthAttachement, colorAttachmentResolve};
//other types of attachments (input (read from shader). resolve - multisampling color)
VkRenderPassCreateInfo renderPassInfo{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = static_cast<uint32_t>(attachements.size()),
.pAttachments = attachements.data(),
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
//create pass;
if(vkCreateRenderPass(
karaage::logicalDevice,
&renderPassInfo,
nullptr,
&karaage::renderPass
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create renderpass");
}
}
void createDescriptorSetLayout(){
VkDescriptorSetLayoutBinding uboLayoutBinding{
.binding = 0, //index specified in the shader
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // type specified in shader
.pImmutableSamplers = nullptr,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,//using in VTX shader
};
VkDescriptorSetLayoutBinding samplerLayoutBinding{
.binding = 1,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImmutableSamplers = nullptr,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,//using in FRAG
};
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding,samplerLayoutBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = static_cast<uint32_t>(bindings.size()),
.pBindings = bindings.data()
};
if(vkCreateDescriptorSetLayout(
karaage::logicalDevice,
&layoutInfo,
nullptr,
&karaage::descriptorSetLayout
) != VK_SUCCESS
){
throw std::runtime_error("[EXCEPTION] Failed to create descriptor set layout");
}
std::cout << " Descriptor Set created" << std::endl;
}
void createComputePipeline(){
auto computeShaderCode = readFile("shaders/compute/simple.spv");
std::cout << " ComputeShader binary size:" << computeShaderCode.size() << std::endl;
VkShaderModule computeShaderModule = createShaderModule(computeShaderCode);
//COMPUTE SHADER STAGE
VkPipelineShaderStageCreateInfo computeShaderStageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.module = computeShaderModule,
.pName = "main"
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &karaage::computeDescriptorSetLayout,
};
if (vkCreatePipelineLayout(
karaage::logicalDevice,
&pipelineLayoutInfo,
nullptr,
&karaage::computePipelineLayout
) != VK_SUCCESS) {
throw std::runtime_error("failed to create compute pipeline layout!");
}
VkComputePipelineCreateInfo pipelineInfo{
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.layout = karaage::computePipelineLayout,
.stage = computeShaderStageInfo,
};
if (vkCreateComputePipelines(
karaage::logicalDevice,
VK_NULL_HANDLE,
1,
&pipelineInfo,
nullptr,
&karaage::computePipeline
) != VK_SUCCESS) {
throw std::runtime_error("failed to create compute pipeline!");
}
vkDestroyShaderModule(
karaage::logicalDevice,
computeShaderModule,
nullptr
);
}
void createGraphicsPipeline(){
//read shader bytecode
auto vertShaderCode = readFile("shaders/vs/simple.spv");
std::cout << " VertShader binary size:" << vertShaderCode.size() << std::endl;
auto fragShaderCode = readFile("shaders/frag/simple.spv");
std::cout << " FragShader binary size:" << fragShaderCode.size() << std::endl;
//create shader bytecode wrappers
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
//VERTEX SHADER STAGE
VkPipelineShaderStageCreateInfo vertShaderStageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertShaderModule,
.pName = "main" //entrypoint
};
//
//FRAGMENT SHADER STAGE
VkPipelineShaderStageCreateInfo fragShaderStageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragShaderModule,
.pName = "main"
};
//
//Capture shader-driven stages
VkPipelineShaderStageCreateInfo shaderStages[] = {
vertShaderStageInfo,
fragShaderStageInfo
};
//setting up dynamic states;
//config of dynamic states will be ignored and will require to be set during draw;
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data()
};
//Vertex input format:
// bindings: spacing between data and whether data is per-vertex or per-instance
// attribute descriptions (type of the attribites passed to the vertex shader)
auto bindingDescription = vertex::getBindingDescription();
auto attributeDescriptions = vertex::getAttributeDescriptions();
VkPipelineVertexInputStateCreateInfo vertexInputInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
//bindings
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &bindingDescription,
//attribute description;
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()),
.pVertexAttributeDescriptions = attributeDescriptions.data()
};
//Input Assembly (What kind of geometry will be drawn from VTX (restart primititve?))
VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE
};
//setup viewport (region of framebuffer to render output to)
VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = (float) karaage::swapChainExtent.width,
.height = (float) karaage::swapChainExtent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
//scisor rect (use swapchain extents);
VkRect2D scisor{
.offset = {0,0},
.extent = karaage::swapChainExtent,
};
//Setting dynamic pipestates;
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scisor,
};
//
//Rasterizer (Performs Depth Testing, Face CUlling, Scissor Test (allows edge rendering)
//
VkPipelineRasterizationStateCreateInfo rasterizer{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
//no clampdepth; (not clamp if rendering shadowmaps);
.depthClampEnable = VK_FALSE,
//disable any output to framebuffer (just disacar everything)
.rasterizerDiscardEnable = VK_FALSE,
//filling polygons (other modes require enabling GPU feature)
.polygonMode = VK_POLYGON_MODE_FILL,
//line width, anything larger requires wideLines GPU featurep
.lineWidth = 1.0f,
//Cull backfaces
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
//depth altering
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
//
// Multipsampling
//
VkPipelineMultisampleStateCreateInfo multisampling{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_TRUE,
.rasterizationSamples = karaage::msaaSamples,
.minSampleShading = 0.2f, //closer to 1 is smoother
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE
};
// Depth / Stencil
// VkPipelineDepthStencilStateCreateInfo
//
//Color blending; (combine fragment color with what is in the framebuffer)
//Mix old and new
//or combine old and new
//VkPipelineColorBlendAttachmentState - config per attached framebuffer
//VkPipelineColorBlendStateCreateInfo - global color blend setting
VkPipelineColorBlendAttachmentState colorBlendAttachementState{
.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
| VK_COLOR_COMPONENT_A_BIT,
//no blend
.blendEnable = VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD
};
VkPipelineColorBlendStateCreateInfo colorBlendingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachementState,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f,
};
//Pipeline layout specifying uniform vars that transfered
VkPipelineLayoutCreateInfo pipelineLayoutInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &karaage::descriptorSetLayout,
//push constraints another way of passing dynamic values to shaders;
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr
};
if(vkCreatePipelineLayout(
karaage::logicalDevice,
&pipelineLayoutInfo,
nullptr,
&karaage::pipelineLayout
) != VK_SUCCESS
){
throw std::runtime_error("[EXCEPTION]Failed to create pipeline layout");
}
VkPipelineDepthStencilStateCreateInfo depthStencil{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS,
.depthBoundsTestEnable = VK_FALSE, //keep only if fit specified depth
.minDepthBounds = 0.0f, //for bounds test
.maxDepthBounds = 1.0f, //for bounds test
.stencilTestEnable = VK_FALSE, //stencil test
};
//creating mesh graphics pipelines
VkGraphicsPipelineCreateInfo pipelineInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2, // VS + FS
.pStages = shaderStages,
.pVertexInputState = &vertexInputInfo,
.pInputAssemblyState = &inputAssemblyCreateInfo,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pDepthStencilState = &depthStencil,
.pColorBlendState = &colorBlendingCreateInfo,
.pDynamicState = &dynamicState,
.layout = karaage::pipelineLayout,
.renderPass = karaage::renderPass,
.subpass = 0,//index of subpas where this pipe will be used
};
auto vertParticleShaderCode = readFile("shaders/vs/particle.spv");
std::cout << " VertShader binary size:" << vertParticleShaderCode.size() << std::endl;
auto fragParticleShaderCode = readFile("shaders/frag/particle.spv");
std::cout << " FragShader binary size:" << fragParticleShaderCode.size() << std::endl;
//create shaders for particles
VkShaderModule vertParticleShaderModule = createShaderModule(vertParticleShaderCode);
VkShaderModule fragParticleShaderModule = createShaderModule(fragParticleShaderCode);
//VERTEX SHADER STAGE
VkPipelineShaderStageCreateInfo vertParticleShaderStageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertParticleShaderModule,
.pName = "main" //entrypoint
};
//
//FRAGMENT SHADER STAGE
VkPipelineShaderStageCreateInfo fragParticleShaderStageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragParticleShaderModule,
.pName = "main"
};
//
//Capture shader-driven stages
VkPipelineShaderStageCreateInfo particleShaderStages[] = {
vertParticleShaderStageInfo,
fragParticleShaderStageInfo
};
auto particleBindingDescription = particle::getBindingDescription();
auto particleAttributeDescriptions = particle::getAttributeDescriptions();
VkPipelineVertexInputStateCreateInfo particleVertexInputInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
//bindings
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &particleBindingDescription,
//attribute description;
.vertexAttributeDescriptionCount = static_cast<uint32_t>(particleAttributeDescriptions.size()),
.pVertexAttributeDescriptions = particleAttributeDescriptions.data()
};
//Input Assembly (What kind of geometry will be drawn from VTX (restart primititve?))
VkPipelineInputAssemblyStateCreateInfo particleInputAssemblyCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
.primitiveRestartEnable = VK_FALSE
};
VkPipelineRasterizationStateCreateInfo particleRasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 0.1f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineLayoutCreateInfo particlePipelineLayoutInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pSetLayouts = nullptr
};
if (vkCreatePipelineLayout(
karaage::logicalDevice,
&particlePipelineLayoutInfo,
nullptr,
&karaage::particlePipelineLayout
) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
VkPipelineDepthStencilStateCreateInfo particleDepthStencil{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE, //keep only if fit specified depth
.minDepthBounds = 0.0f, //for bounds test
.maxDepthBounds = 1.0f, //for bounds test
.stencilTestEnable = VK_FALSE, //stencil test
};
VkGraphicsPipelineCreateInfo particlePipelineInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = particleShaderStages,
.pVertexInputState = &particleVertexInputInfo,
.pInputAssemblyState = &particleInputAssemblyCreateInfo,
.pViewportState = &viewportState,
.pRasterizationState = &particleRasterizer,
.pMultisampleState = &multisampling,
.pDepthStencilState = &particleDepthStencil,
.pColorBlendState = &colorBlendingCreateInfo,
.pDynamicState = &dynamicState,
.layout = karaage::particlePipelineLayout,
.renderPass = karaage::renderPass,
.subpass = 0,//index of subpas where this pipe will be used
};
VkGraphicsPipelineCreateInfo pipelineInfos[] = {
pipelineInfo,
particlePipelineInfo
};
karaage::graphicsPipeline.resize(2);
//create pipe no cache assigned;
if(vkCreateGraphicsPipelines(
karaage::logicalDevice,
VK_NULL_HANDLE,
2,
pipelineInfos,
nullptr,
karaage::graphicsPipeline.data()
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION]Failed To crate graphics pipeline");
}
//cleanup loaded shaders
vkDestroyShaderModule(karaage::logicalDevice, fragShaderModule, nullptr);
vkDestroyShaderModule(karaage::logicalDevice, vertShaderModule, nullptr);
vkDestroyShaderModule(karaage::logicalDevice, fragParticleShaderModule, nullptr);
vkDestroyShaderModule(karaage::logicalDevice, vertParticleShaderModule, nullptr);
}
void createFramebuffers(){
//setting same size of framebuffers as SwapChain image view;
karaage::swapChainFramebuffers.resize(karaage::swapChainImageViews.size());
for(size_t i = 0; i < karaage::swapChainImageViews.size(); i++){
std::array<VkImageView,3> attachements = {
karaage::colorImageView,
karaage::depthImageView,
karaage::swapChainImageViews[i],
};
VkFramebufferCreateInfo framebufferInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = karaage::renderPass, //define compatablity
.attachmentCount = static_cast<uint32_t>(attachements.size()),
.pAttachments = attachements.data(),
.width = karaage::swapChainExtent.width,
.height = karaage::swapChainExtent.height,
.layers = 1// num of layers in image array
};
if(vkCreateFramebuffer(
karaage::logicalDevice,
&framebufferInfo,
nullptr,
&karaage::swapChainFramebuffers[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create framebuffer");
}
}
}
void cleanupSwapChain(){
vkDestroyImageView(karaage::logicalDevice,karaage::colorImageView,nullptr);
vkDestroyImage(karaage::logicalDevice, karaage::colorImage,nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::colorImageMemory, nullptr);
vkDestroyImageView(karaage::logicalDevice,karaage::depthImageView,nullptr);
vkDestroyImage(karaage::logicalDevice, karaage::depthImage,nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::depthImageMemory, nullptr);
for(auto framebuffer: karaage::swapChainFramebuffers){
vkDestroyFramebuffer(karaage::logicalDevice, framebuffer, nullptr);
}
for(auto imageView: karaage::swapChainImageViews){
vkDestroyImageView(karaage::logicalDevice, imageView, nullptr);
}
vkDestroySwapchainKHR(karaage::logicalDevice, karaage::swapChain, nullptr);
}
//early decl;
void createDepthResource();
void createColorResource();
//doesn't handle HDR/SDR pipe conversion, only size;
void recreateSwapChain(){
int width = 0, height = 0;
glfwGetFramebufferSize(karaage::window, &width, &height);
while (width == 0 || height == 0) {
glfwWaitEvents();
glfwGetFramebufferSize(karaage::window, &width, &height);
}
//wait for resoruces to be freed
vkDeviceWaitIdle(karaage::logicalDevice);
cleanupSwapChain();
createSwapChain();
createImageViews();
createColorResource();
createDepthResource();
createFramebuffers();
}
void createCommandPool(){
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(karaage::physicalDevice);
VkCommandPoolCreateInfo poolInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queueFamilyIndices.graphicsAndComputeFamily.value(),
};
if(vkCreateCommandPool(karaage::logicalDevice, &poolInfo, nullptr, &karaage::commandPool)!=VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create command pool");
}
}
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties){
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(karaage::physicalDevice, &memProperties);
//loop through all memtypes, typeFilter is BITFIELD of suitable types;
//props define special features of the memory (map to write from cpu);
for(uint32_t i = 0; i < memProperties.memoryTypeCount; i++){
if(
(typeFilter & (1 << i)) //check types
&& ((memProperties.memoryTypes[i].propertyFlags & properties) == properties) //check properties
){
return i;
}
}
throw std::runtime_error("[EXCEPTION] Couldn't find sutable memory type");
}
void createBuffer(
VkDeviceSize size,
VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties,
VkBuffer& buffer,
VkDeviceMemory& bufferMemory
){
VkBufferCreateInfo bufferInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
if(vkCreateBuffer(
karaage::logicalDevice,
&bufferInfo,
nullptr,
&buffer
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create vertex buffer");
}
//No memory allocated yet
//Query memory requirements for the buffer;
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(karaage::logicalDevice, buffer, &memRequirements);
//prep alloc info
//VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT //coherent bit (may affect perf)
VkMemoryAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)
};
//alloc
if(vkAllocateMemory(
karaage::logicalDevice,
&allocInfo,
nullptr,
&bufferMemory
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to alloc vertex buffer");
}
//bind memory to buffer;
if(vkBindBufferMemory(karaage::logicalDevice, buffer, bufferMemory, 0) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to bind mem to vertex buffer");
}
}
VkCommandBuffer BeginSingleTimeCommands(){
//memory transfer uses command buffers
VkCommandBufferAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandPool = karaage::commandPool,
.commandBufferCount = 1
};
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(
karaage::logicalDevice,
&allocInfo,
&commandBuffer
);
//Start cmdbuf
VkCommandBufferBeginInfo beginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT //onetime
};
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
void EndSingleTimeCommands(VkCommandBuffer commandBuffer){
vkEndCommandBuffer(commandBuffer);
//Submit part
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer
};
vkQueueSubmit(karaage::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); //no fences
vkQueueWaitIdle(karaage::graphicsQueue);//TODO: wait for fence
vkFreeCommandBuffers(karaage::logicalDevice, karaage::commandPool, 1, &commandBuffer);
}
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size){
VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
{
//commands
VkBufferCopy copyRegion{
.srcOffset = 0,
.dstOffset = 0,
.size = size,
};
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
}
EndSingleTimeCommands(commandBuffer);
}
void createShaderStorageBuffers(){
// Initialize particles
std::default_random_engine rndEngine((unsigned)time(nullptr));
std::uniform_real_distribution<float> rndDist(0.0f, 1.0f);
// Initial particle positions on a circle
std::vector<particle> particles(karaage::MAX_PARTICLES);
for (auto& particle : particles) {
float r = 1.0f * sqrt(rndDist(rndEngine));
float theta = rndDist(rndEngine) * 2 * 3.14159265358979323846;
float x = r * cos(theta) * karaage::HEIGHT / karaage::WIDTH;
float y = r * sin(theta);
particle.pos = glm::vec2(x, y);
particle.vel = glm::normalize(glm::vec2(x,y)) * 0.00025f;
particle.col = glm::vec4(rndDist(rndEngine), rndDist(rndEngine), rndDist(rndEngine), 1.0f);
}
VkDeviceSize bufferSize = sizeof(particle) * karaage::MAX_PARTICLES;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(
bufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory
);
void* data;
vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data,particles.data(),static_cast<size_t>(bufferSize));
vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory);
karaage::shaderStorageBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::shaderStorageBuffersMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT);
for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){
createBuffer(
bufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::shaderStorageBuffers[i],
karaage::shaderStorageBuffersMemory[i]
);
copyBuffer(
stagingBuffer,
karaage::shaderStorageBuffers[i],
bufferSize
);
}
vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr);
}
void createComputeDescriptorSetLayout() {
std::array<VkDescriptorSetLayoutBinding, 3> layoutBindings{{
{
.binding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pImmutableSamplers = nullptr,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
},
{//double buffered particles[0]
.binding = 1,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pImmutableSamplers = nullptr,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
},
{//double buffered particles[1]
.binding = 2,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pImmutableSamplers = nullptr,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
}
}};
VkDescriptorSetLayoutCreateInfo layoutInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 3,
.pBindings = layoutBindings.data(),
};
if (vkCreateDescriptorSetLayout(
karaage::logicalDevice,
&layoutInfo,
nullptr,
&karaage::computeDescriptorSetLayout
) != VK_SUCCESS) {
throw std::runtime_error("failed to create compute descriptor set layout!");
}
}
void createComputeDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(
karaage::MAX_FRAMES_IN_FLIGHT,
karaage::computeDescriptorSetLayout
);
VkDescriptorSetAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = karaage::computeDescriptorPool,
.descriptorSetCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT),
.pSetLayouts = layouts.data(),
};
karaage::computeDescriptorSets.resize(karaage::MAX_FRAMES_IN_FLIGHT);
if (vkAllocateDescriptorSets(
karaage::logicalDevice,
&allocInfo,
karaage::computeDescriptorSets.data()
) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
for (size_t i = 0; i < karaage::MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo uniformBufferInfo{};
uniformBufferInfo.buffer = karaage::uniformBuffers[i];
uniformBufferInfo.offset = 0;
uniformBufferInfo.range = sizeof(UniformBufferObject);
VkDescriptorBufferInfo storageBufferInfoLastFrame{
.buffer = karaage::shaderStorageBuffers[(i - 1) % karaage::MAX_FRAMES_IN_FLIGHT],
.offset = 0,
.range = sizeof(particle) * karaage::MAX_PARTICLES,
};
VkDescriptorBufferInfo storageBufferInfoCurrentFrame{
.buffer = karaage::shaderStorageBuffers[i],
.offset = 0,
.range = sizeof(particle) * karaage::MAX_PARTICLES,
};
std::array<VkWriteDescriptorSet, 3> descriptorWrites{{
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = karaage::computeDescriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &uniformBufferInfo
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = karaage::computeDescriptorSets[i],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &storageBufferInfoLastFrame,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = karaage::computeDescriptorSets[i],
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &storageBufferInfoCurrentFrame,
}
}};
vkUpdateDescriptorSets(
karaage::logicalDevice,
3,
descriptorWrites.data(),
0,
nullptr
);
}
}
bool hasStencilComponent(VkFormat format){
return
format == VK_FORMAT_D32_SFLOAT_S8_UINT ||
format == VK_FORMAT_D24_UNORM_S8_UINT ||
format == VK_FORMAT_D16_UNORM_S8_UINT ||
format == VK_FORMAT_S8_UINT;
}
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels){
VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
{
VkImageAspectFlags aspectMask = 0;
VkPipelineStageFlags srcStage = 0;
VkPipelineStageFlags dstStage = 0;
VkAccessFlags srcMask = 0;
VkAccessFlags dstMask = 0;
if(
oldLayout == VK_IMAGE_LAYOUT_UNDEFINED &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
){
srcMask = 0;
dstMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
else if(
oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
){
srcMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}else if(
oldLayout == VK_IMAGE_LAYOUT_UNDEFINED &&
newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
){
srcMask = 0;
dstMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (hasStencilComponent(format)) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
}else{
throw std::invalid_argument("unsupported layout transition");
}
VkImageMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = oldLayout,
.newLayout = newLayout,
//no queue transfers ignoring;
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
//what image and part of it is affected
.image = image,
.subresourceRange.aspectMask = aspectMask,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.layerCount = 1,
.subresourceRange.levelCount = mipLevels,
//TODO
.srcAccessMask = srcMask,
.dstAccessMask = dstMask
};
vkCmdPipelineBarrier(
commandBuffer,
srcStage, //what stages are before barrier
dstStage, //what stages wait for barrier
0,//allows reading by regions
0,nullptr,
0,nullptr,
1,&barrier
);
}
EndSingleTimeCommands(commandBuffer);
}
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height){
VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
{
VkBufferImageCopy region{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
//subres
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.imageSubresource.baseArrayLayer = 0,
.imageSubresource.mipLevel = 0,
.imageSubresource.layerCount = 1,
.imageOffset = {0,0,0},
.imageExtent = {width,height,1}
};
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, //assume already transitioned to optimal for copy layout
1,
&region
);
}
EndSingleTimeCommands(commandBuffer);
}
void createImage2D(
uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples,
VkFormat format, VkImageTiling tiling,
VkImageUsageFlags usageFlags, VkMemoryPropertyFlags properties,
VkImage& image, VkDeviceMemory& imageMemory
){
VkImageCreateInfo imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = static_cast<uint32_t>(width),
.extent.height = static_cast<uint32_t>(height),
.extent.depth = 1,
.mipLevels = mipLevels,
.arrayLayers = 1,
.format = format,
.tiling = tiling, //row-major(linear) or optimal
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usageFlags, //destination for the buffer copy and sampled in shader
.samples = numSamples,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE // one queue family (graphics pipe)
};
if(vkCreateImage(
karaage::logicalDevice,
&imageInfo,
nullptr,
&image
) != VK_SUCCESS){
throw std::runtime_error("Failed to create texture image");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(karaage::logicalDevice, image, &memRequirements);
VkMemoryAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)
};
if(vkAllocateMemory(
karaage::logicalDevice,
&allocInfo,
nullptr,
&imageMemory
) != VK_SUCCESS){
throw std::runtime_error("Failed to allocate texture memory");
}
vkBindImageMemory(karaage::logicalDevice, image, imageMemory, 0);
}
void createColorResource(){
VkFormat colorFormat = karaage::swapChainImageFormat;
createImage2D(
karaage::swapChainExtent.width,
karaage::swapChainExtent.height,
1,
karaage::msaaSamples,
colorFormat,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::colorImage,
karaage::colorImageMemory
);
karaage::colorImageView = createImageView(
karaage::colorImage,
colorFormat,
VK_IMAGE_ASPECT_COLOR_BIT,
1
);
// transitionImageLayout(
// karaage::colorImage,
// colorFormat,
// VK_IMAGE_LAYOUT_UNDEFINED,
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
// 1
// );
}
void createDepthResource(){
VkFormat depthFormat = findDepthFormat();
createImage2D(
karaage::swapChainExtent.width,
karaage::swapChainExtent.height,
1,
karaage::msaaSamples,
depthFormat,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::depthImage,
karaage::depthImageMemory
);
karaage::depthImageView = createImageView(
karaage::depthImage,
depthFormat,
VK_IMAGE_ASPECT_DEPTH_BIT,
1
);
// transitionImageLayout(
// karaage::depthImage,
// depthFormat,
// VK_IMAGE_LAYOUT_UNDEFINED,
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
// 1
// );
}
void generateMipmaps(VkImage image, VkFormat imageFormat, uint32_t texWidth, uint32_t texHeight, uint32_t mipLevels){
//check if format supports blitting;
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(
karaage::physicalDevice,
imageFormat,
&formatProperties
);
if(!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)){
throw std::runtime_error("linear blitting is not supported with the requested format");
}
VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
VkImageMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = image,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
.subresourceRange.levelCount = 1,
};
int32_t mipWidth = texWidth;
int32_t mipHeight = texHeight;
//starting at 1
for (uint32_t i = 1; i < karaage::mipLevels; i++) {
uint32_t srcMipLevel = i - 1;
uint32_t dstMipLevel = i;
barrier.subresourceRange.baseMipLevel = srcMipLevel;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
VkImageBlit blit{
.srcOffsets[0] = {0,0,0},
.srcOffsets[1] = {
mipWidth,
mipHeight,
1
},
.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.srcSubresource.mipLevel = srcMipLevel,
.srcSubresource.baseArrayLayer = 0,
.srcSubresource.layerCount = 1,
//DST config
.dstOffsets[0] = { 0, 0, 0 },
.dstOffsets[1] = {
mipWidth > 1 ? mipWidth / 2 : 1,
mipHeight > 1 ? mipHeight / 2 : 1,
1
},
.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.dstSubresource.mipLevel = dstMipLevel,
.dstSubresource.baseArrayLayer = 0,
.dstSubresource.layerCount = 1
};
vkCmdBlitImage(
commandBuffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&blit,
VK_FILTER_LINEAR
);
//barrier to make it shader ready;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
if (mipWidth > 1) mipWidth /= 2;
if (mipHeight > 1) mipHeight /= 2;
}
//last miplevel transferred
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
EndSingleTimeCommands(commandBuffer);
}
/**
* @brief Load image and create vulkan object
*/
void createTextureImage(){
int texWidth, texHeight, texChannels;
stbi_uc* pixels = stbi_load(
karaage::texturePath,
&texWidth,
&texHeight,
&texChannels,
STBI_rgb_alpha
);
karaage::mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth,texHeight)))) + 1;
VkDeviceSize imageSize = texWidth*texHeight*STBI_rgb_alpha;
if(!pixels){
throw std::runtime_error("[EXCEPTION] Failed to load tex image");
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(
imageSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory
);
//mmap
void* data;
vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, imageSize, 0, &data);
memcpy(data,pixels,static_cast<size_t>(imageSize));
vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory);
//cleanup
stbi_image_free(pixels);
//create image2D
createImage2D(
texWidth,
texHeight,
karaage::mipLevels,
VK_SAMPLE_COUNT_1_BIT,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::textureImage,
karaage::textureImageMemory
);
//optimize for copy
transitionImageLayout(
karaage::textureImage,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
karaage::mipLevels
);
//copy
copyBufferToImage(
stagingBuffer,
karaage::textureImage,
static_cast<uint32_t>(texWidth),
static_cast<uint32_t>(texHeight)
);
//transition and mips gen;
generateMipmaps(karaage::textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, karaage::mipLevels);
//transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
//optimize for sampling
// transitionImageLayout(
// karaage::textureImage,
// VK_FORMAT_R8G8B8A8_SRGB,
// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
// karaage::mipLevels
// );
//cleanup staging data;
vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr);
}
void createTextureImageView(){
karaage::textureImageView = createImageView(
karaage::textureImage,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT,
karaage::mipLevels
);
}
void createTextureSampler(){
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(karaage::physicalDevice, &properties);
VkSamplerCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR, //magnified oversampling
.minFilter = VK_FILTER_LINEAR, //minified undersampling
//mode
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.anisotropyEnable = VK_TRUE,
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.mipLodBias = 0.0f,
.minLod = 0.0f,
.maxLod = static_cast<float>(karaage::mipLevels),
};
if(vkCreateSampler(
karaage::logicalDevice,
&createInfo,
nullptr,
&karaage::textureSampler
)!=VK_SUCCESS){
throw std::runtime_error("failed to create sampler");
}
}
void loadModel(){
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn, err;
if(!tinyobj::LoadObj(&attrib,&shapes,&materials,&warn,&err,karaage::meshPath)){
throw std::runtime_error(warn+err);
}
std::unordered_map<vertex, uint32_t> uniqueVertices{};
for(const auto& shape: shapes){
for(const auto& index : shape.mesh.indices){
vertex v{
{
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2],
},
{
1.0f,1.0f,1.0f
},
{
attrib.texcoords[2 * index.texcoord_index + 0],
1.0f - attrib.texcoords[2 * index.texcoord_index + 1],
}
};
if(uniqueVertices.count(v) == 0){
uniqueVertices[v] = static_cast<uint32_t>(karaage::vertices.size());
karaage::vertices.push_back(v);
}
karaage::indices.push_back(uniqueVertices[v]);
}
}
}
void createVertexBuffers(){
VkDeviceSize bufferSize = sizeof(karaage::vertices[0]) * karaage::vertices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
std::cout << "creating staging buffer started" << std::endl;
createBuffer(
bufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, //src in memory transfer operation;
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory
);
std::cout << "creating staging buffer success" << std::endl;
//map the buffer memory
//allows accessing region of specified mem defined by offset and size;
void* data;
vkMapMemory(
karaage::logicalDevice, //that owns mem
stagingBufferMemory,
0, //offset
bufferSize, //size, can be used VK_WHOLE_SIZE to map all of memory;
0, //flags none for now in the API
&data //must be aligned to minMemoryMapAlignment
);
memcpy(data, karaage::vertices.data(), (size_t) bufferSize);
vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory);
createBuffer(
bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, //dst bit in transfer
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::vertexBuffer,
karaage::vertexBufferMemory
);
copyBuffer(stagingBuffer, karaage::vertexBuffer, bufferSize);
vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr);
}
void createIndexBuffer(){
VkDeviceSize bufferSize = sizeof(karaage::indices[0]) * karaage::indices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(
bufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer,
stagingBufferMemory
);
void* data;
vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data, karaage::indices.data(), (size_t) bufferSize);
vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory);
createBuffer(
bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
karaage::indexBuffer,
karaage::indexBufferMemory
);
copyBuffer(stagingBuffer, karaage::indexBuffer, bufferSize);
vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr);
}
void createComputeUniformBuffers() {
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
karaage::computeUBOs.resize(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::computeUBOsMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::computeUBOsMapped.resize(karaage::MAX_FRAMES_IN_FLIGHT);
for (size_t i = 0; i < karaage::MAX_FRAMES_IN_FLIGHT; i++) {
createBuffer(
bufferSize,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
karaage::computeUBOs[i],
karaage::computeUBOsMemory[i]);
vkMapMemory(
karaage::logicalDevice,
karaage::computeUBOsMemory[i],
0,
bufferSize,
0,
&karaage::computeUBOsMapped[i]
);
}
}
void createUniformBuffers(){
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
karaage::uniformBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::uniformBuffersMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::uniformBuffersMapped.resize(karaage::MAX_FRAMES_IN_FLIGHT);
for(size_t i = 0; i < karaage::MAX_FRAMES_IN_FLIGHT; i++){
createBuffer(
bufferSize,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
karaage::uniformBuffers[i],
karaage::uniformBuffersMemory[i]
);
//using persistent mapping here
if(vkMapMemory(
karaage::logicalDevice,
karaage::uniformBuffersMemory[i],
0,
bufferSize,
0,
&karaage::uniformBuffersMapped[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create UBO");
}
}
}
void createComputeDescriptorPool(){
std::array<VkDescriptorPoolSize, 2> poolSizes{{
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT)
},
{
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) * 2
}
}};
VkDescriptorPoolCreateInfo poolInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = static_cast<uint32_t>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
.maxSets = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT)
};
if(vkCreateDescriptorPool(
karaage::logicalDevice,
&poolInfo,
nullptr,
&karaage::computeDescriptorPool
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create descriptor pool");
}
}
void createDescriptorPool(){
std::array<VkDescriptorPoolSize, 2> poolSizes{{
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT)
},
{
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT)
}
}};
VkDescriptorPoolCreateInfo poolInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = static_cast<uint32_t>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
.maxSets = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT)
};
if(vkCreateDescriptorPool(
karaage::logicalDevice,
&poolInfo,
nullptr,
&karaage::descriptorPool
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create descriptor pool");
}
}
void createDescriptorSets(){
std::vector<VkDescriptorSetLayout> layouts(
karaage::MAX_FRAMES_IN_FLIGHT,
karaage::descriptorSetLayout
);
VkDescriptorSetAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = karaage::descriptorPool,
.descriptorSetCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT),
.pSetLayouts = layouts.data()
};
karaage::descriptorSets.resize(karaage::MAX_FRAMES_IN_FLIGHT);
if(vkAllocateDescriptorSets(
karaage::logicalDevice,
&allocInfo,
karaage::descriptorSets.data()
)!= VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] failed to allocate descriptor sets");
}
std::cout << "Descriptor set allocated" << std::endl;
for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){
VkDescriptorBufferInfo bufferInfo{
.buffer = karaage::uniformBuffers[i],
.offset = 0,
.range = sizeof(UniformBufferObject)
};
VkDescriptorImageInfo imageInfo{
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = karaage::textureImageView,
.sampler = karaage::textureSampler
};
std::array<VkWriteDescriptorSet, 2> descriptorWrites{
{
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = karaage::descriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &bufferInfo,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = karaage::descriptorSets[i],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &imageInfo,
}
}
};
vkUpdateDescriptorSets(
karaage::logicalDevice,
static_cast<uint32_t>(descriptorWrites.size()),
descriptorWrites.data(),
0,
nullptr
);
}
}
void createComputeCommandBuffers() {
karaage::computeCommandBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = karaage::commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = (uint32_t)karaage::computeCommandBuffers.size(),
};
if (vkAllocateCommandBuffers(
karaage::logicalDevice,
&allocInfo,
karaage::computeCommandBuffers.data()
) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate compute command buffers!");
}
}
void createCommandBuffers(){
karaage::commandBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo cbAllocInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = karaage::commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,//(primary/secondary; secondary can be reused);
.commandBufferCount = karaage::MAX_FRAMES_IN_FLIGHT
};
if(vkAllocateCommandBuffers(
karaage::logicalDevice,
&cbAllocInfo,
karaage::commandBuffers.data()
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create command buffer");
}
}
void createSyncObjects(){
karaage::imageAvailableSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::renderFinishedSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::inFlightFences.reserve(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::computeFinishedSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT);
karaage::computeInFlightFences.reserve(karaage::MAX_FRAMES_IN_FLIGHT);
VkSemaphoreCreateInfo semaphoreInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
};
VkFenceCreateInfo fenceInfo{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT
};
for(uint32_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){
//GRAPHICS PIPE SYNC OBJECTS
if(vkCreateSemaphore(
karaage::logicalDevice,
&semaphoreInfo,
nullptr,
&karaage::imageAvailableSemaphores[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create image available semaphore");
}
if(vkCreateSemaphore(
karaage::logicalDevice,
&semaphoreInfo,
nullptr,
&karaage::renderFinishedSemaphores[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create render finished semaphore");
}
if(vkCreateFence(
karaage::logicalDevice,
&fenceInfo,
nullptr,
&karaage::inFlightFences[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create inFlight fence");
}
// COMPUTE SYNC objects
if(vkCreateSemaphore(
karaage::logicalDevice,
&semaphoreInfo,
nullptr,
&karaage::computeFinishedSemaphores[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create compute finished semaphore");
}
if(vkCreateFence(
karaage::logicalDevice,
&fenceInfo,
nullptr,
&karaage::computeInFlightFences[i]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to create conpute inFlight fence");
}
}
}
void recordComputeCommandBuffer(VkCommandBuffer commandBuffer){
VkCommandBufferBeginInfo beginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
};
if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to begin recording command buffer");
}
vkCmdBindPipeline(
commandBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE,
karaage::computePipeline
);
vkCmdBindDescriptorSets(
commandBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE,
karaage::computePipelineLayout,
0,
1,
&karaage::computeDescriptorSets[karaage::currentFrame],
0,
nullptr
);
vkCmdDispatch(
commandBuffer,
karaage::MAX_PARTICLES / 256,
1,
1
);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to record compute command buffer!");
}
}
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){
VkCommandBufferBeginInfo beginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = 0, //opt
.pInheritanceInfo = nullptr//opt
};
if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to begin recording command buffer");
}
std::array<VkClearValue,2> clearValues{};
clearValues[0].color = {{0.0f,0.0f,0.0f,1.0f}};
clearValues[1].depthStencil = {1.0f,0};
VkRenderPassBeginInfo renderPassInfo{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = karaage::renderPass,
.framebuffer = karaage::swapChainFramebuffers[imageIndex],
//size of render area;
.renderArea.offset = {0,0},
.renderArea.extent = karaage::swapChainExtent,
//clear
.clearValueCount = static_cast<uint32_t>(clearValues.size()),
.pClearValues = clearValues.data()
};
//to, renderpass detail, how commands will be provided; (inline or secondary buffer);
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
//
//--------------------------------COMMANDS GO HERE:
//
//Bind pipe (graphics or compute)
//particle system draw
VkDeviceSize offsets[] = {0};
//set scissor and viewport (they are dynamic)
VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(karaage::swapChainExtent.width),
.height = static_cast<float>(karaage::swapChainExtent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
// scissor
VkRect2D scissor{
.offset = {0,0},
.extent = karaage::swapChainExtent
};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
//mesh draw
vkCmdBindPipeline(
commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
karaage::graphicsPipeline[0]
);
//bind vertex buffer
VkBuffer vertexBuffers[] = {karaage::vertexBuffer};
vkCmdBindVertexBuffers(
commandBuffer,
0,
1,
vertexBuffers, //old render
offsets
);
vkCmdBindIndexBuffer(
commandBuffer,
karaage::indexBuffer,
0,
VK_INDEX_TYPE_UINT32
);
vkCmdBindDescriptorSets(
commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
karaage::pipelineLayout,
0,
1,
&karaage::descriptorSets[karaage::currentFrame],
0,
nullptr
);
//draw (buf, vtxCount, instanceCount, firstVtx (offset), firstInstance (offset))
vkCmdDrawIndexed(
commandBuffer,
static_cast<uint32_t>(karaage::indices.size()),
1,
0,
0,
0
);
vkCmdBindPipeline(
commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
karaage::graphicsPipeline[1]
);
vkCmdBindVertexBuffers(
commandBuffer,
0,
1,
&karaage::shaderStorageBuffers[karaage::currentFrame],
offsets
);
vkCmdDraw(
commandBuffer,
karaage::MAX_PARTICLES,
1,
0,
0
);
//Finished recording commands to pass;
vkCmdEndRenderPass(commandBuffer);
if(vkEndCommandBuffer(commandBuffer) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to record command buffer");
}
}
//ALL init code
void initVulkan(){
createVKInstance();//done
setupDebugMessenger();//done
createSurface();//done
pickPhysicalDevice();//done
createLogicalDevice();//done
createSwapChain();//done (SC obj)
createImageViews();//done (SC image views)
createRenderPass();//done (where to render)
createComputeDescriptorSetLayout();//done
createDescriptorSetLayout();//done
createGraphicsPipeline();//done (default)
createComputePipeline();//done (default compute)
createCommandPool();
createShaderStorageBuffers(); //particles;
// passes stuff
createColorResource();
createDepthResource();
createFramebuffers();
//model tex
createTextureImage();
createTextureImageView();
createTextureSampler();
//model
loadModel();
createVertexBuffers();
createIndexBuffer();
//shader stuff
createUniformBuffers();
createDescriptorPool();
createDescriptorSets();
createComputeUniformBuffers();
createComputeDescriptorPool();
createComputeDescriptorSets();
createCommandBuffers();
createComputeCommandBuffers();
createSyncObjects();
}
void updateComputeUniformBuffer(uint32_t currentImage) {
UniformBufferObjectTime ubo{};
ubo.deltaTime = karaage::lastFrameTime * 2.0f;
memcpy(karaage::computeUBOsMapped[currentImage], &ubo, sizeof(ubo));
}
//ticked update
void updateUniformBuffer(uint32_t currentFrame){
static auto startTime = std::chrono::high_resolution_clock::now();
auto currentTime = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration<float,std::chrono::seconds::period>(currentTime-startTime).count();
UniformBufferObject ubo{};
ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f,0.0f,1.0f));
ubo.view = glm::lookAt(glm::vec3(2.0f,2.0f,2.0f), glm::vec3(0.0f,0.0f,0.0f), glm::vec3(0.0f,0.0f,1.0f));
ubo.proj = glm::perspective(
glm::radians(45.0f),
karaage::swapChainExtent.width/ (float) karaage::swapChainExtent.height,
0.1f,
10.0f
);
//Y flip
ubo.proj[1][1] *= -1;
//mmcpy
memcpy(karaage::uniformBuffersMapped[currentFrame],&ubo, sizeof(ubo));
}
//Sync using binary semaphores
void drawFrame(){
// Compute submission
vkWaitForFences(
karaage::logicalDevice,
1,
&karaage::computeInFlightFences[karaage::currentFrame],
VK_TRUE,
UINT64_MAX
);
//update compute uniform buffer;
updateComputeUniformBuffer(karaage::currentFrame);
vkResetFences(
karaage::logicalDevice,
1,
&karaage::computeInFlightFences[karaage::currentFrame]
);
vkResetCommandBuffer(
karaage::computeCommandBuffers[karaage::currentFrame],
/*VkCommandBufferResetFlagBits*/ 0
);
recordComputeCommandBuffer(
karaage::computeCommandBuffers[karaage::currentFrame]
);
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &karaage::computeCommandBuffers[karaage::currentFrame],
.signalSemaphoreCount = 1,
.pSignalSemaphores = &karaage::computeFinishedSemaphores[karaage::currentFrame],
};
if (vkQueueSubmit(
karaage::computeQueue,
1,
&submitInfo,
karaage::computeInFlightFences[karaage::currentFrame]
) != VK_SUCCESS) {
throw std::runtime_error("failed to submit compute command buffer!");
};
//
//GRAPHICS SUBMIT
//
vkWaitForFences(
karaage::logicalDevice,
1,
&karaage::inFlightFences[karaage::currentFrame],
VK_TRUE,
UINT64_MAX);
//
//acquire image from swapchain (async)
//
uint32_t imageIndex;
VkResult acquireResult = vkAcquireNextImageKHR(
karaage::logicalDevice,
karaage::swapChain,
UINT64_MAX,
karaage::imageAvailableSemaphores[karaage::currentFrame],
VK_NULL_HANDLE,
&imageIndex
);
//check if acquired image is still valid and recreate
if(
acquireResult == VK_ERROR_OUT_OF_DATE_KHR
|| acquireResult == VK_SUBOPTIMAL_KHR
){
recreateSwapChain();
return;
}else if(
acquireResult != VK_SUCCESS
&& acquireResult != VK_SUBOPTIMAL_KHR
){
throw std::runtime_error("[Exception] Failed to acquire swapchain image");
}
//update GFX UBOs (here we rotate image)
updateUniformBuffer(karaage::currentFrame);
//resetting later as we might not yet sumbitted any work which will cause deadlock
vkResetFences(
karaage::logicalDevice,
1,
&karaage::inFlightFences[karaage::currentFrame]);
//
//record commandbuffer
//
vkResetCommandBuffer(karaage::commandBuffers[karaage::currentFrame], 0);
recordCommandBuffer(karaage::commandBuffers[karaage::currentFrame], imageIndex);
//which semaphores to fait for
VkSemaphore waitSemaphores[] = {
karaage::computeFinishedSemaphores[karaage::currentFrame],
karaage::imageAvailableSemaphores[karaage::currentFrame]
};
//waiting for both stages
VkPipelineStageFlags waitStages[] = {
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
};
//signal render finished
VkSemaphore signalSemaphores[] = {
karaage::renderFinishedSemaphores[karaage::currentFrame]
};
//submit recorded buffer (async)
submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
//what semaphores to wait for at which stage
.waitSemaphoreCount = 2,
.pWaitSemaphores = waitSemaphores,
.pWaitDstStageMask = waitStages,
//setting command buffer
.commandBufferCount = 1,
.pCommandBuffers = &karaage::commandBuffers[karaage::currentFrame],
//what sempaphores to signal once commandbuffers finished execution;
.signalSemaphoreCount = 1,
.pSignalSemaphores = signalSemaphores,
};
if(vkQueueSubmit(
karaage::graphicsQueue,
1,
&submitInfo,
karaage::inFlightFences[karaage::currentFrame]
) != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] Failed to submit graphics queue");
}
VkSwapchainKHR swapChains[] = {
karaage::swapChain
};
//present swapchain image (async)
VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = signalSemaphores,
.swapchainCount = 1,
.pSwapchains = swapChains,
.pImageIndices = &imageIndex,
.pResults = nullptr
};
//PRESENT!
VkResult presentResult = vkQueuePresentKHR(
karaage::presentQueue,
&presentInfo
);
if(presentResult == VK_ERROR_OUT_OF_DATE_KHR
|| presentResult == VK_SUBOPTIMAL_KHR
|| karaage::framebufferResized
){
karaage::framebufferResized = false;
recreateSwapChain();
}else if(presentResult != VK_SUCCESS){
throw std::runtime_error("[EXCEPTION] failed to present swap chain image");
}
karaage::currentFrame = (karaage::currentFrame + 1) % karaage::MAX_FRAMES_IN_FLIGHT;
}
void mainLoop(){
while (!glfwWindowShouldClose(karaage::window)) {
glfwPollEvents();
drawFrame();
}
vkDeviceWaitIdle(karaage::logicalDevice);
}
//dealloc resources
void cleanup(){
std::cout << "[Karaage] Cleanup started" << std::endl;
cleanupSwapChain();
vkDestroySampler(karaage::logicalDevice, karaage::textureSampler, nullptr);
vkDestroyImageView(karaage::logicalDevice, karaage::textureImageView, nullptr);
vkDestroyImage(karaage::logicalDevice, karaage::textureImage, nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::textureImageMemory, nullptr);
for(size_t i = 0; i<2; i++){
vkDestroyPipeline(karaage::logicalDevice, karaage::graphicsPipeline[i], nullptr);
}
vkDestroyPipelineLayout(karaage::logicalDevice, karaage::pipelineLayout, nullptr);
vkDestroyRenderPass(karaage::logicalDevice, karaage::renderPass, nullptr);
for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){
vkDestroyBuffer(karaage::logicalDevice, karaage::uniformBuffers[i], nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::uniformBuffersMemory[i], nullptr);
}
vkDestroyDescriptorPool(karaage::logicalDevice, karaage::computeDescriptorPool, nullptr);
vkDestroyDescriptorSetLayout(karaage::logicalDevice, karaage::computeDescriptorSetLayout, nullptr);
vkDestroyDescriptorPool(karaage::logicalDevice, karaage::descriptorPool, nullptr);
vkDestroyDescriptorSetLayout(karaage::logicalDevice, karaage::descriptorSetLayout, nullptr);
vkDestroyBuffer(karaage::logicalDevice, karaage::indexBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::indexBufferMemory, nullptr);
vkDestroyBuffer(karaage::logicalDevice, karaage::vertexBuffer, nullptr);
vkFreeMemory(karaage::logicalDevice, karaage::vertexBufferMemory, nullptr);
for(uint32_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){
vkDestroySemaphore(karaage::logicalDevice, karaage::imageAvailableSemaphores[i], nullptr);
vkDestroySemaphore(karaage::logicalDevice, karaage::renderFinishedSemaphores[i], nullptr);
vkDestroyFence(karaage::logicalDevice, karaage::inFlightFences[i], nullptr);
}
vkDestroyCommandPool(karaage::logicalDevice, karaage::commandPool, nullptr);
vkDestroyDevice(karaage::logicalDevice, nullptr);
if(karaage::enableValidationLayers){
DestroyDebugUtilsMessengerEXT(
karaage::vkInstance,
karaage::debugMessenger,
nullptr
);
}
vkDestroySurfaceKHR(karaage::vkInstance, karaage::surface, nullptr);
vkDestroyInstance(karaage::vkInstance, nullptr);
glfwDestroyWindow(karaage::window);
glfwTerminate();
}
void run(){
initCLI();
initWindow();
initVulkan();
mainLoop();
cleanup();
}
int main() {
try{
run();
}catch( const std::exception& e){
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment