vulkan-project/vulkan_instance.cpp
2022-10-07 23:30:44 +02:00

2201 lines
92 KiB
C++

#include "vulkan_instance.hpp"
#include "exceptions.hpp"
#include "font.hpp"
#include "vk_enum_string.h"
#include <stdexcept>
#include <utility>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
#include <fstream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <cstring>
#include <algorithm>
#include <vulkan/vulkan_core.h>
namespace gz::vk {
using std::uint32_t;
const unsigned int WIDTH = 600;
const unsigned int HEIGHT = 600;
const std::vector<const char*> extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME
};
const std::vector<const char*> instanceExtensions = {
};
using gz::Exception;
using gz::VkException;
using gz::VkUserError;
using gz::getVkException;
//
// DEBUG
//
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation",
};
#ifdef NDEBUG
constexpr bool enableValidationLayers = false;
#else
constexpr bool enableValidationLayers = true;
#endif
constexpr VkDebugUtilsMessageSeverityFlagsEXT debugMessageSevereties =
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;
constexpr VkDebugUtilsMessageTypeFlagsEXT debugMessageTypes =
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
const std::vector<VkValidationFeatureEnableEXT> validationFeaturesEnable {
VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT
};
const VkValidationFeaturesEXT validationFeatures {
VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, // sType
nullptr, // pNext
static_cast<uint32_t>(validationFeaturesEnable.size()), // enabled feature count
validationFeaturesEnable.data(), // enable features
0, // disabled feature count
nullptr // disabled features
};
/* constexpr VkDebugUtilsMessageTypeFlagsEXT debugFlags = */
/* VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT | */
/* VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; */
constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BBLUE;
constexpr VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCI {
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // sType
&validationFeatures, // pNext
/* nullptr, // pNext */
NO_FLAGS, // flags
debugMessageSevereties, // messageSeverety
debugMessageTypes, // messageType
VulkanInstance::debugLog, // pfnUserCallback
nullptr, // pUserData
};
//
// EXTENSIONS
//
bool checkRequiredExtensionsAvailable(const std::vector<const char*>& requiredExtensions, const std::vector<VkExtensionProperties>& availableExtensions) {
bool extensionAvailable;
for (const auto& requiredExtension : requiredExtensions) {
extensionAvailable = false;
for (const auto& extension : availableExtensions) {
if (strcmp(requiredExtension, extension.extensionName) == 0) {
extensionAvailable = true;
break;
}
}
if (!extensionAvailable) {
return false;
}
}
return true;
}
std::vector<const char*> getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> requiredExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers) {
requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return requiredExtensions;
}
// check if all extensions are supported by device
bool deviceExtensionsSupported(VkPhysicalDevice device) {
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
bool extensionAvailable;
for (const char* requiredExtension : extensions) {
extensionAvailable = false;
for (const auto& extensionProperties : availableExtensions) {
if (strcmp(requiredExtension, extensionProperties.extensionName) == 0) {
extensionAvailable = true;
break;
}
}
if (!extensionAvailable) { return false; }
}
return true;
}
// check if validation layers are supported/installed
bool validationLayersSupported() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
bool layerAvailable;
for (const char* requiredLayer : validationLayers) {
layerAvailable = false;
for (const auto& layerProperties : availableLayers) {
if (strcmp(requiredLayer, layerProperties.layerName) == 0) {
layerAvailable = true;
break;
}
}
if (!layerAvailable) { return false; }
}
return true;
}
//
// App
//
void VulkanInstance::createInstance() {
VkApplicationInfo appInfo;
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pEngineName = "Glowzwiebel Engine";
appInfo.pApplicationName = "Hallo Dreieck";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo instanceCI;
instanceCI.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCI.pApplicationInfo = &appInfo;
instanceCI.pNext = &debugUtilsMessengerCI;
instanceCI.flags = 0;
instanceCI.ppEnabledExtensionNames = instanceExtensions.data();
instanceCI.enabledExtensionCount = instanceExtensions.size();
if (enableValidationLayers) {
if (!validationLayersSupported()) {
throw std::runtime_error("Validation layers enabled but not available.");
}
instanceCI.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
instanceCI.ppEnabledLayerNames = validationLayers.data();
}
else {
instanceCI.enabledLayerCount = 0;
instanceCI.ppEnabledLayerNames = nullptr;
}
std::vector<const char*> requiredExtensions = getRequiredExtensions();
instanceCI.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size());
instanceCI.ppEnabledExtensionNames = requiredExtensions.data();;
// log requiredExtensions
std::string message;
message.reserve(80);
message += "Required extensions (";
message += std::to_string(instanceCI.enabledExtensionCount) + ')';
for (uint32_t i = 0; i < requiredExtensions.size(); i++) {
message += "\n\t";
message += std::to_string(i + 1) + ": " + requiredExtensions[i];
}
vLog(message);
VkResult result = vkCreateInstance(&instanceCI, nullptr, &instance);
if (result != VK_SUCCESS) {
vLog("Failed to create instance.", "VkResult:", STR_VK_RESULT(result));
throw std::runtime_error("Failed to create instance.");
}
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); // get count
std::vector<VkExtensionProperties> extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
// log available extensions
message = "Available extensions (";
message += std::to_string(extensionCount) + "):";
for (uint32_t i = 0; i < extensions.size(); i++) {
message += "\n\t";
message += std::to_string(i + 1) + ": " + extensions[i].extensionName;
}
vLog(message);
if (!checkRequiredExtensionsAvailable(requiredExtensions, extensions)) {
throw std::runtime_error("Not all required extensions are available.");
}
}
gz::Log VulkanInstance::vLog("vulkan.log", false, true, "Vulkan", VULKAN_MESSAGE_PREFIX_COLOR);
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
gz::Color messageColor;
switch(messageSeverety) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
messageColor = gz::Color::RESET;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
messageColor = gz::Color::WHITE;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
messageColor = gz::Color::YELLOW;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
messageColor = gz::Color::RED;
break;
default:
messageColor = gz::Color::RESET;
}
std::string type;
gz::Color typeColor;
switch(messageType) {
case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
type = "General";
typeColor = gz::Color::GREEN;
break;
case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
type = "Performance";
typeColor = gz::Color::CYAN;
break;
case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
type = "Validation";
typeColor = gz::Color::MAGENTA;
break;
default:
type = "None";
typeColor = gz::Color::WHITE;
}
size_t offset = 0;
if (strcmp(pCallbackData->pMessageIdName, "UNASSIGNED-DEBUG-PRINTF") == 0) {
type = "Shader";
typeColor = gz::Color::YELLOW;
messageColor = gz::Color::RESET;
std::string_view message(pCallbackData->pMessage);
offset = message.find_first_of('|');
if (offset == std::string::npos) { offset = 0; }
else { offset += 2; } // skip "| "
}
vLog.clog(typeColor, std::move(type), messageColor, pCallbackData->pMessage+offset);
return VK_FALSE;
}
//
// DEVICE
//
void VulkanInstance::selectPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0) {
vLog.error("Could not find any GPU.");
throw std::runtime_error("Could not find any GPU.");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
// find best gpu
std::vector<unsigned int> deviceScores;
for (const auto& device : devices) {
deviceScores.push_back(rateDevice(device));
}
unsigned int bestGPUIndex = std::max_element(deviceScores.begin(), deviceScores.end()) - deviceScores.begin();
if (deviceScores[bestGPUIndex] == 0) {
vLog.error("Could not find suitable GPU.");
throw std::runtime_error("Could not find suitable GPU.");
}
physicalDevice = devices[bestGPUIndex];
vkGetPhysicalDeviceProperties(physicalDevice, &phDevProperties);
vkGetPhysicalDeviceFeatures(physicalDevice, &phDevFeatures);
qFamilyIndices = findQueueFamilies(physicalDevice);
vLog("Selected GPU:", phDevProperties.deviceName, "with the following queue family indices:", qFamilyIndices);
}
const unsigned int SCORE_DISCRETE_GPU = 5000;
const unsigned int SCORE_INTEGRATED_GPU = 2000;
const unsigned int SCORE_VIRTUAL_GPU = 1000;
const unsigned int SCORE_HAS_ALL_QUEUE_FAMILIES = 100;
const unsigned int SCORE_HAS_FEATURE = 100;
unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) {
unsigned int score;
// rate type
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(device, &properties);
switch(properties.deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
score = SCORE_DISCRETE_GPU;
break;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
score = SCORE_INTEGRATED_GPU;
break;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
score = SCORE_VIRTUAL_GPU;
break;
default:
return 0;
}
qFamilyIndices = findQueueFamilies(device);
// has needed queue families
if (qFamilyIndices.hasAllValues()) {
score += SCORE_HAS_ALL_QUEUE_FAMILIES;
}
else if (!qFamilyIndices.hasNecessaryValues()) {
return 0;
}
// supports all extensions
if (!deviceExtensionsSupported(device)) {
return 0;
}
// swap chain support
SwapChainSupport scDetails = querySwapChainSupport(device);
if (scDetails.formats.empty() or scDetails.presentModes.empty()) {
return 0;
}
// rate memory
VkPhysicalDeviceMemoryProperties memProperties{};
vkGetPhysicalDeviceMemoryProperties(device, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryHeapCount; i++) {
score += memProperties.memoryHeaps[i].size / 1e6;
}
VkPhysicalDeviceFeatures features{};
vkGetPhysicalDeviceFeatures(device, &features);
if (features.samplerAnisotropy == VK_TRUE) { score += SCORE_HAS_FEATURE; }
vLog("rateDevice: GPU: ", properties.deviceName, " - Score: ", score);
return score;
}
void VulkanInstance::setValidSettings() {
// anisotropic filtering
settings.setAllowedValues<bool>("anisotropy_enable", { false, vkBool2Bool(phDevFeatures.samplerAnisotropy) }, gz::SM_LIST);
settings.setAllowedValues<float>("max_anisotropy", { 1.0f, phDevProperties.limits.maxSamplerAnisotropy }, gz::SM_RANGE);
}
VkFormat VulkanInstance::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
for (const VkFormat& format : candidates) {
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
if (tiling == VK_IMAGE_TILING_LINEAR and
(formatProperties.linearTilingFeatures & features) == features) {
return format;
}
else if (tiling == VK_IMAGE_TILING_OPTIMAL and
(formatProperties.optimalTilingFeatures & features) == features) {
return format;
}
}
throw VkException(std::string("Could not find a suitable format. tiling: ") + STR_VK_IMAGE_TILING(tiling) + ".", "findSupportedFormat");
}
//
// QUEUE
//
QueueFamilyIndices VulkanInstance::findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;
uint32_t qFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> qFamilies(qFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, qFamilies.data());
vLog("findQueueFamilies: found", qFamilyCount, "queue families:");
VkBool32 presentSupport = false;
for (unsigned int i = 0; i < qFamilyCount; i++) {
// check for queue with graphic capabilities
if (qFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
else if (qFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { // only transfer, not graphics
indices.transferFamily = i;
}
// check for surface support
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (presentSupport == VK_TRUE) {
indices.presentFamily = i;
}
vLog("findQueueFamilies:", i, "-", qFamilies[i].queueFlags);
if (indices.hasAllValues()) {
vLog("findQueueFamilies: Found all wanted families.");
break;
}
}
return indices;
}
//
// LOGICAL DEVICE
//
void VulkanInstance::createLogicalDevice() {
std::unordered_set<uint32_t> qFamiliyIndicesSet = { // must be unique
qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value(),
};
if (qFamilyIndices.transferFamily.has_value()) {
qFamiliyIndicesSet.insert(qFamilyIndices.transferFamily.value());
}
std::vector<VkDeviceQueueCreateInfo> deviceQueueCI;
for (uint32_t index : qFamiliyIndicesSet) {
deviceQueueCI.push_back(VkDeviceQueueCreateInfo());
deviceQueueCI.back().sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCI.back().queueFamilyIndex = index;
deviceQueueCI.back().queueCount = 1;
float qPriority = 1.0f;
deviceQueueCI.back().pQueuePriorities = &qPriority;
}
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = bool2VkBool(settings.getOr<bool>("anisotropy_enable", false));
VkDeviceCreateInfo deviceCI{};
deviceCI.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCI.pQueueCreateInfos = deviceQueueCI.data();
deviceCI.queueCreateInfoCount = static_cast<uint32_t>(deviceQueueCI.size());
deviceCI.pEnabledFeatures = &deviceFeatures;
deviceCI.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
deviceCI.ppEnabledExtensionNames = extensions.data();
VkResult result = vkCreateDevice(physicalDevice, &deviceCI, nullptr, &device);
if (result != VK_SUCCESS) {
vLog.error("createLogicalDevice: Failed to create logical device.", "VkResult:", STR_VK_RESULT(result));
throw std::runtime_error("createLogicalDevice: Failed to create logical device.");
}
// get handles
uint32_t index = 0;
vkGetDeviceQueue(device, qFamilyIndices.graphicsFamily.value(), index, &graphicsQ);
vkGetDeviceQueue(device, qFamilyIndices.presentFamily.value(), index, &presentQ);
if (qFamilyIndices.transferFamily.has_value()) {
vkGetDeviceQueue(device, qFamilyIndices.transferFamily.value(), index, &transferQ);
}
else {
transferQ = graphicsQ;
}
vLog("createLogicalDevice: Created logical device.");
}
//
// SURFACE
//
void VulkanInstance::createSurface() {
VkResult result = glfwCreateWindowSurface(instance, window, nullptr, &surface);
if (result != VK_SUCCESS) {
vLog.error("createSurface: Failed to create window surface.", "VkResult:", STR_VK_RESULT(result));
throw std::runtime_error("createSurface: Failed to create window surface.");
}
}
//
// SWAP CHAIN
//
SwapChainSupport VulkanInstance::querySwapChainSupport(VkPhysicalDevice physicalDevice) {
SwapChainSupport scDetails;
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
scDetails.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, scDetails.formats.data());
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr);
scDetails.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, scDetails.presentModes.data());
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &scDetails.capabilities);
return scDetails;
}
VkSurfaceFormatKHR VulkanInstance::selectSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
vLog("selectSwapChainSurfaceFormat:", availableFormats.size(), "formats available.");
for (const auto& format : availableFormats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return format;
}
}
return availableFormats.front();
}
VkPresentModeKHR VulkanInstance::selectSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availableModes) {
for (const auto& mode : availableModes) {
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return mode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D VulkanInstance::selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
// if width = max(uint32_t) then surface size will be determined by a swapchain targeting it
if (capabilities.currentExtent.width == std::numeric_limits<uint32_t>::max()) {
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D actualExtent {};
actualExtent.width = std::clamp(static_cast<uint32_t>(width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
actualExtent.height = std::clamp(static_cast<uint32_t>(height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return actualExtent;
}
else {
return capabilities.currentExtent;
}
}
void VulkanInstance::createSwapChain() {
SwapChainSupport scDetails = querySwapChainSupport(physicalDevice);
VkSurfaceFormatKHR surfaceFormat = selectSwapChainSurfaceFormat(scDetails.formats);
uint32_t imageCount = scDetails.capabilities.minImageCount + 1;
// maxImageCount = 0 -> no max
if (scDetails.capabilities.maxImageCount > 0 and imageCount < scDetails.capabilities.maxImageCount) {
imageCount = scDetails.capabilities.maxImageCount;
}
scImageFormat = surfaceFormat.format;
scExtent = selectSwapExtent(scDetails.capabilities);
VkSwapchainCreateInfoKHR swapChainCI{};
swapChainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCI.surface = surface;
swapChainCI.minImageCount = imageCount;
swapChainCI.imageFormat = scImageFormat;
swapChainCI.imageColorSpace = surfaceFormat.colorSpace;
swapChainCI.imageExtent = scExtent;
swapChainCI.imageArrayLayers = 1;
// if postProcessing: VK_IMAGE_USAGE_TRANSFER_DST_BIT
swapChainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
/* QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); */
if (qFamilyIndices.graphicsFamily.value() != qFamilyIndices.presentFamily.value()) {
swapChainCI.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapChainCI.queueFamilyIndexCount = 2;
uint32_t qIndices[] = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value() };
swapChainCI.pQueueFamilyIndices = qIndices;
}
else {
swapChainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
swapChainCI.preTransform = scDetails.capabilities.currentTransform;
swapChainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapChainCI.presentMode = selectSwapChainPresentMode(scDetails.presentModes);
swapChainCI.clipped = VK_TRUE;
swapChainCI.oldSwapchain = VK_NULL_HANDLE;
VkResult result = vkCreateSwapchainKHR(device, &swapChainCI, nullptr, &swapChain);
if (result != VK_SUCCESS) {
vLog.error("createSwapChain: Couldn not create swap chain", "VkResult:", STR_VK_RESULT(result));
throw std::runtime_error("createSwapChain: Couldn not create swap chain");
}
// get image handles
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
scImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, scImages.data());
vLog("createSwapChain: Created swap chain.");
}
void VulkanInstance::recreateSwapChain() {
int width, height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0) { // minimized
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
vLog("recreateSwapChain: new framebuffer size:", width, height);
}
vkDeviceWaitIdle(device);
cleanupSwapChain();
createSwapChain();
createImageViews();
createRenderPassBegin();
createRenderPassEnd();
/* createDescriptorSetLayout(); */
/* createGraphicsPipeline<Vertex3D>("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */
createGraphicsPipeline<Vertex2D>("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]);
createDepthImageAndView();
recreateFramebuffers();
/* createUniformBuffers(); */
createCommandBuffers(commandBuffersBegin);
createCommandBuffers(commandBuffersEnd);
/* createDescriptorPool(); */
/* createDescriptorSets(); */
}
void VulkanInstance::cleanupSwapChain() {
vkDestroyImageView(device, depthImageView, nullptr);
vkDestroyImage(device, depthImage, nullptr);
vkFreeMemory(device, depthImageMemory, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
for (auto& framebuffer : scFramebuffers) {
destroyFramebuffers(framebuffer.first);
}
// destroy pipelines
auto it = pipelines.begin();
while (it != pipelines.end()) {
it = pipelines.erase(it, device);
}
vkDestroyRenderPass(device, renderPassBegin, nullptr);
vkDestroyRenderPass(device, renderPassEnd, nullptr);
for (auto& imageView : scImageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
}
//
// IMAGE VIEW
//
void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags) {
VkImageViewCreateInfo imageViewCI{};
imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageViewCI.format = format;
imageViewCI.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCI.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCI.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCI.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCI.subresourceRange.aspectMask = aspectFlags;
imageViewCI.subresourceRange.baseMipLevel = 0;
imageViewCI.subresourceRange.levelCount = 1;
imageViewCI.subresourceRange.baseArrayLayer = 0;
imageViewCI.subresourceRange.layerCount = 1;
imageViewCI.image = image;
VkResult result = vkCreateImageView(device, &imageViewCI, nullptr, &imageView);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create image view", "createImageViews");
}
}
void VulkanInstance::createImageViews() {
scImageViews.resize(scImages.size());
for (unsigned int i = 0; i < scImageViews.size(); i++) {
createImageView(scImageFormat, scImages[i], scImageViews[i], VK_IMAGE_ASPECT_COLOR_BIT);
}
vLog("createImageViews: Created", scImageViews.size(), "image views.");
}
//
// RENDER PASS
//
void VulkanInstance::createRenderPassBegin() {
VkAttachmentDescription colorBlendAttachment{};
colorBlendAttachment.format = scImageFormat;
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
/* VkAttachmentDescription depthAttachment{}; */
/* depthAttachment.format = findDepthFormat(); */
/* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */
/* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
/* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
/* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */
/* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
/* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */
/* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
/* VkAttachmentReference depthAttachmentRef{}; */
/* depthAttachmentRef.attachment = 1; */
/* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
/* VkSubpassDependency dependency{}; */
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
/* dependency.dstSubpass = 0; */
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
/* dependency.srcAccessMask = 0; */
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
std::array<VkAttachmentDescription, 1> attachments = { colorBlendAttachment };
VkRenderPassCreateInfo renderPassCI{};
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassCI.pAttachments = attachments.data();
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpass;
renderPassCI.dependencyCount = 1;
renderPassCI.pDependencies = &dependency;
/* renderPassCI.dependencyCount = 0; */
/* renderPassCI.pDependencies = nullptr; */
/* renderPassCI.correlatedViewMaskCount = 0; */
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
VkResult result = vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPassBegin);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create renderPassBegin", "createRenderPassBegin");
}
vLog("createRenderPass: Created renderpassBegin.");
}
void VulkanInstance::createRenderPassEnd() {
VkAttachmentDescription colorBlendAttachment{};
colorBlendAttachment.format = scImageFormat;
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 0;
/* subpass.pColorAttachments = &colorAttachmentRef; */
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
/* VkSubpassDependency dependency{}; */
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
/* dependency.dstSubpass = 0; */
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
/* dependency.srcAccessMask = 0; */
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
std::array<VkAttachmentDescription, 1> attachments = { colorBlendAttachment };
VkRenderPassCreateInfo renderPassCI{};
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassCI.pAttachments = attachments.data();
renderPassCI.subpassCount = 1;
renderPassCI.pSubpasses = &subpass;
renderPassCI.dependencyCount = 0;
/* renderPassCI.pDependencies = &dependency; */
/* renderPassCI.dependencyCount = 0; */
/* renderPassCI.pDependencies = nullptr; */
/* renderPassCI.correlatedViewMaskCount = 0; */
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
VkResult result = vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPassEnd);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create renderPassEnd", "createRenderPassEnd");
}
vLog("createRenderPass: Created renderPassEnd.");
}
//
// DESCRIPTORS
//
void VulkanInstance::createDescriptorSetLayout() {
// 1) uniform buffer object
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = bindingUniformBuffer;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
/* uboLayoutBinding.pImmutableSamplers = nullptr; */
// 2) combined image sampler
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = bindingCombinedImageSampler;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
/* samplerLayoutBinding.pImmutableSamplers = nullptr; */
std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(bindings.size());
descriptorSetLayoutCI.pBindings = bindings.data();
VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout");
}
vLog("createDescriptorSetLayout: Created descriptor set layout.");
}
void VulkanInstance::createDescriptorPool() {
std::array<VkDescriptorPoolSize, 2> poolSizes;
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
VkDescriptorPoolCreateInfo poolCI{};
poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolCI.pPoolSizes = poolSizes.data();
poolCI.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
VkResult result = vkCreateDescriptorPool(device, &poolCI, nullptr, &descriptorPool);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create descriptor pool", "createDescriptorPool");
}
vLog("createDescriptorPool: Created descriptor pool.");
}
void VulkanInstance::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
VkDescriptorSetAllocateInfo setAI{};
setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
setAI.descriptorPool = descriptorPool;
setAI.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
setAI.pSetLayouts = layouts.data();
descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
VkResult result = vkAllocateDescriptorSets(device, &setAI, descriptorSets.data());
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create descriptor sets", "createDescriptorPool");
}
// configure sets
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo bufferI{};
// TODO
/* bufferI.buffer = uniformBuffers[i]; */
bufferI.offset = 0;
bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject);
VkDescriptorImageInfo imageI{};
imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageI.imageView = textureImageView;
imageI.sampler = textureSampler;
std::array<VkWriteDescriptorSet, 2> descriptorW{};
descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorW[0].dstSet = descriptorSets[i];
descriptorW[0].dstBinding = bindingUniformBuffer;
descriptorW[0].dstArrayElement = 0;
descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorW[0].descriptorCount = 1;
descriptorW[0].pBufferInfo = &bufferI;
/* descriptorW[0].pImageInfo = nullptr; */
/* descriptorW[0].pTexelBufferView = nullptr; */
descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorW[1].dstSet = descriptorSets[i];
descriptorW[1].dstBinding = bindingCombinedImageSampler;
descriptorW[1].dstArrayElement = 0;
descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorW[1].descriptorCount = 1;
/* descriptorW[1].pBufferInfo = &bufferI; */
descriptorW[1].pImageInfo = &imageI;
/* descriptorW[1].pTexelBufferView = nullptr; */
uint32_t descriptorWriteCount = static_cast<uint32_t>(descriptorW.size());
uint32_t descriptorCopyCount = 0;
vkUpdateDescriptorSets(device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr);
}
vLog("createDescriptorSets: Created descriptor sets.");
}
//
// PIPELINE
//
/* void App::createGraphicsPipeline() { */
/* auto vertShaderCode = readFile("shaders/vert.spv"); */
/* auto fragShaderCode = readFile("shaders/frag.spv"); */
/* VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); */
/* VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); */
/* VkPipelineShaderStageCreateInfo vertexShaderStageCI{}; */
/* vertexShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; */
/* vertexShaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; */
/* vertexShaderStageCI.module = vertShaderModule; */
/* vertexShaderStageCI.pName = "main"; */
/* /1* vssCreateInfo.pSpecializationInfo = nullptr; *1/ */
/* VkPipelineShaderStageCreateInfo fragmentShaderStageCI{}; */
/* fragmentShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; */
/* fragmentShaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; */
/* fragmentShaderStageCI.module = fragShaderModule; */
/* fragmentShaderStageCI.pName = "main"; */
/* /1* vssCreateInfo.pSpecializationInfo = nullptr; *1/ */
/* VkPipelineShaderStageCreateInfo shaderStagesCI[] = { vertexShaderStageCI, fragmentShaderStageCI }; */
/* VkVertexInputBindingDescription bindingD = Vertex::getBindingDescription(); */
/* // array<VkVertexInputAttributeDescription> */
/* auto attributeD = Vertex::getAttributeDescriptions(); */
/* VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; */
/* vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; */
/* vertexInputStateCI.vertexBindingDescriptionCount = 1; */
/* vertexInputStateCI.pVertexBindingDescriptions = &bindingD; */
/* vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeD.size());; */
/* vertexInputStateCI.pVertexAttributeDescriptions = attributeD.data(); */
/* VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; */
/* inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; */
/* inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; */
/* inputAssemblyStateCI.primitiveRestartEnable = VK_FALSE; */
/* VkViewport viewport{}; */
/* viewport.x = 0.0f; */
/* viewport.y = 0.0f; */
/* viewport.width = static_cast<float>(scExtent.width); */
/* viewport.height = static_cast<float>(scExtent.height); */
/* viewport.minDepth = 0.0f; */
/* viewport.maxDepth = 1.0f; */
/* VkRect2D scissor{}; */
/* scissor.offset = {0, 0}; */
/* scissor.extent = scExtent; */
/* VkPipelineViewportStateCreateInfo viewportStateCI{}; */
/* viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; */
/* viewportStateCI.viewportCount = 1; */
/* viewportStateCI.pViewports = &viewport; */
/* viewportStateCI.scissorCount = 1; */
/* viewportStateCI.pScissors = &scissor; */
/* VkPipelineRasterizationStateCreateInfo rasterizerCI{}; */
/* rasterizerCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; */
/* rasterizerCI.depthClampEnable = VK_FALSE; */
/* rasterizerCI.rasterizerDiscardEnable = VK_FALSE; */
/* rasterizerCI.polygonMode = VK_POLYGON_MODE_FILL; */
/* rasterizerCI.lineWidth = 1.0f; */
/* rasterizerCI.cullMode = VK_CULL_MODE_BACK_BIT; */
/* // not clockwise because of inverted y axis from glm */
/* rasterizerCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; */
/* rasterizerCI.depthBiasEnable = VK_FALSE; */
/* /1* rasterizerCI.depthBiasConstantFactor = 0.0f; *1/ */
/* /1* rasterizerCI.depthBiasClamp = 0.0f; *1/ */
/* /1* rasterizerCI.depthBiasSlopeFactor = 0.0f; *1/ */
/* VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; */
/* multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; */
/* multisampleStateCI.sampleShadingEnable = VK_FALSE; */
/* multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; */
/* /1* multisampleStateCI.minSampleShading = 1.0f; *1/ */
/* /1* multisampleStateCI.pSampleMask = nullptr; *1/ */
/* /1* multisampleStateCI.alphaToCoverageEnable = VK_FALSE; *1/ */
/* /1* multisampleStateCI.alphaToOneEnable = VK_FALSE; *1/ */
/* VkPipelineColorBlendAttachmentState colorBlendAttachment{}; */
/* colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; */
/* colorBlendAttachment.blendEnable = VK_FALSE; */
/* /1* colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; *1/ */
/* /1* colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; *1/ */
/* /1* colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; *1/ */
/* /1* colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; *1/ */
/* /1* colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; *1/ */
/* /1* colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; *1/ */
/* VkPipelineColorBlendStateCreateInfo colorBlendCI{}; */
/* colorBlendCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; */
/* colorBlendCI.logicOpEnable = VK_FALSE; */
/* /1* colorBlendCI.logicOp = VK_LOGIC_OP_COPY; *1/ */
/* colorBlendCI.attachmentCount = 1; */
/* colorBlendCI.pAttachments = &colorBlendAttachment; */
/* colorBlendCI.blendConstants[0] = 0.0f; */
/* colorBlendCI.blendConstants[1] = 0.0f; */
/* colorBlendCI.blendConstants[2] = 0.0f; */
/* colorBlendCI.blendConstants[3] = 0.0f; */
/* /1* std::vector<VkDynamicState> dynamicStates = { *1/ */
/* /1* VK_DYNAMIC_STATE_VIEWPORT, *1/ */
/* /1* VK_DYNAMIC_STATE_LINE_WIDTH *1/ */
/* /1* }; *1/ */
/* /1* VkPipelineDynamicStateCreateInfo dynamicState{}; *1/ */
/* /1* dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; *1/ */
/* /1* dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()); *1/ */
/* /1* dynamicState.pDynamicStates = dynamicStates.data(); *1/ */
/* VkPipelineDepthStencilStateCreateInfo depthStencilCI{}; */
/* depthStencilCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; */
/* depthStencilCI.depthTestEnable = VK_TRUE; */
/* depthStencilCI.depthWriteEnable = VK_TRUE; */
/* depthStencilCI.depthCompareOp = VK_COMPARE_OP_LESS; */
/* depthStencilCI.depthBoundsTestEnable = VK_FALSE; */
/* /1* depthStencilCI.minDepthBounds = 0.0f; *1/ */
/* /1* depthStencilCI.maxDepthBounds = 1.0f; *1/ */
/* depthStencilCI.stencilTestEnable = VK_FALSE; */
/* /1* depthStencilCI.front = {}; *1/ */
/* /1* depthStencilCI.back = {}; *1/ */
/* VkPipelineLayoutCreateInfo pipelineLayoutCI{}; */
/* pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; */
/* /1* pipelineLayoutCI.pushConstantRangeCount = 0; *1/ */
/* /1* pipelineLayoutCI.pPushConstantRanges = nullptr; *1/ */
/* pipelineLayoutCI.setLayoutCount = 1; */
/* pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; */
/* VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout); */
/* if (result != VK_SUCCESS) { */
/* throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline"); */
/* } */
/* VkGraphicsPipelineCreateInfo pipelineCI{}; */
/* pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; */
/* pipelineCI.stageCount = 2; */
/* pipelineCI.pStages = shaderStagesCI; */
/* pipelineCI.pVertexInputState = &vertexInputStateCI; */
/* pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; */
/* pipelineCI.pViewportState = &viewportStateCI; */
/* pipelineCI.pRasterizationState = &rasterizerCI; */
/* pipelineCI.pMultisampleState = &multisampleStateCI; */
/* pipelineCI.pDepthStencilState = &depthStencilCI; */
/* pipelineCI.pColorBlendState = &colorBlendCI; */
/* /1* pipelineCI.pDynamicState = nullptr; *1/ */
/* pipelineCI.layout = pipelineLayout; */
/* pipelineCI.renderPass = renderPass; */
/* pipelineCI.subpass = 0; */
/* /1* pipelineCI.basePipelineHandle = VK_NULL_HANDLE; *1/ */
/* /1* pipelineCI.basePipelineIndex = -1; *1/ */
/* result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &graphicsPipeline); */
/* if (result != VK_SUCCESS) { */
/* throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline"); */
/* } */
/* vkDestroyShaderModule(device, vertShaderModule, nullptr); */
/* vkDestroyShaderModule(device, fragShaderModule, nullptr); */
/* vLog("createGraphicsPipeline: Created graphics pipeline."); */
/* } */
template<VertexType VertexT>
void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline) {
auto vertShaderCode = readFile(vertexShader);
auto fragShaderCode = readFile(fragmentShader);
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
VkPipelineShaderStageCreateInfo vertexShaderStageCI{};
vertexShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertexShaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertexShaderStageCI.module = vertShaderModule;
vertexShaderStageCI.pName = "main";
/* vssCreateInfo.pSpecializationInfo = nullptr; */
VkPipelineShaderStageCreateInfo fragmentShaderStageCI{};
fragmentShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragmentShaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragmentShaderStageCI.module = fragShaderModule;
fragmentShaderStageCI.pName = "main";
/* vssCreateInfo.pSpecializationInfo = nullptr; */
VkPipelineShaderStageCreateInfo shaderStagesCI[] = { vertexShaderStageCI, fragmentShaderStageCI };
VkVertexInputBindingDescription bindingD = VertexT::getBindingDescription();
// array<VkVertexInputAttributeDescription>
auto attributeD = VertexT::getAttributeDescriptions();
VkPipelineVertexInputStateCreateInfo vertexInputStateCI{};
vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputStateCI.vertexBindingDescriptionCount = 1;
vertexInputStateCI.pVertexBindingDescriptions = &bindingD;
vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeD.size());;
vertexInputStateCI.pVertexAttributeDescriptions = attributeD.data();
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssemblyStateCI.primitiveRestartEnable = VK_FALSE;
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(scExtent.width);
viewport.height = static_cast<float>(scExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = scExtent;
VkPipelineViewportStateCreateInfo viewportStateCI{};
viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCI.viewportCount = 1;
viewportStateCI.pViewports = &viewport;
viewportStateCI.scissorCount = 1;
viewportStateCI.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerCI{};
rasterizerCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerCI.depthClampEnable = VK_FALSE;
rasterizerCI.rasterizerDiscardEnable = VK_FALSE;
rasterizerCI.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerCI.lineWidth = 1.0f;
rasterizerCI.cullMode = VK_CULL_MODE_BACK_BIT;
// not clockwise because of inverted y axis from glm
rasterizerCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizerCI.depthBiasEnable = VK_FALSE;
/* rasterizerCI.depthBiasConstantFactor = 0.0f; */
/* rasterizerCI.depthBiasClamp = 0.0f; */
/* rasterizerCI.depthBiasSlopeFactor = 0.0f; */
VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleStateCI.sampleShadingEnable = VK_FALSE;
multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
/* multisampleStateCI.minSampleShading = 1.0f; */
/* multisampleStateCI.pSampleMask = nullptr; */
/* multisampleStateCI.alphaToCoverageEnable = VK_FALSE; */
/* multisampleStateCI.alphaToOneEnable = VK_FALSE; */
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
/* colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; */
/* colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; */
/* colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; */
/* colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; */
/* colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; */
/* colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; */
VkPipelineColorBlendStateCreateInfo colorBlendCI{};
colorBlendCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendCI.logicOpEnable = VK_FALSE;
/* colorBlendCI.logicOp = VK_LOGIC_OP_COPY; */
colorBlendCI.attachmentCount = 1;
colorBlendCI.pAttachments = &colorBlendAttachment;
colorBlendCI.blendConstants[0] = 0.0f;
colorBlendCI.blendConstants[1] = 0.0f;
colorBlendCI.blendConstants[2] = 0.0f;
colorBlendCI.blendConstants[3] = 0.0f;
/* std::vector<VkDynamicState> dynamicStates = { */
/* VK_DYNAMIC_STATE_VIEWPORT, */
/* VK_DYNAMIC_STATE_LINE_WIDTH */
/* }; */
/* VkPipelineDynamicStateCreateInfo dynamicState{}; */
/* dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; */
/* dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()); */
/* dynamicState.pDynamicStates = dynamicStates.data(); */
VkPipelineDepthStencilStateCreateInfo depthStencilCI{};
depthStencilCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilCI.depthTestEnable = VK_TRUE;
depthStencilCI.depthWriteEnable = VK_TRUE;
depthStencilCI.depthCompareOp = VK_COMPARE_OP_LESS;
depthStencilCI.depthBoundsTestEnable = VK_FALSE;
/* depthStencilCI.minDepthBounds = 0.0f; */
/* depthStencilCI.maxDepthBounds = 1.0f; */
depthStencilCI.stencilTestEnable = VK_FALSE;
/* depthStencilCI.front = {}; */
/* depthStencilCI.back = {}; */
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
/* pipelineLayoutCI.pushConstantRangeCount = 0; */
/* pipelineLayoutCI.pPushConstantRanges = nullptr; */
/* pipelineLayoutCI.setLayoutCount = 1; */
/* pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; */
VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipeline.layout);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline");
}
VkGraphicsPipelineCreateInfo pipelineCI{};
pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCI.stageCount = 2;
pipelineCI.pStages = shaderStagesCI;
pipelineCI.pVertexInputState = &vertexInputStateCI;
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCI.pRasterizationState = &rasterizerCI;
pipelineCI.pMultisampleState = &multisampleStateCI;
if (useDepthStencil) {
pipelineCI.pDepthStencilState = &depthStencilCI;
}
else {
pipelineCI.pDepthStencilState = nullptr;
}
pipelineCI.pColorBlendState = &colorBlendCI;
/* pipelineCI.pDynamicState = nullptr; */
pipelineCI.layout = pipeline.layout;
pipelineCI.renderPass = renderPass;
pipelineCI.subpass = 0;
/* pipelineCI.basePipelineHandle = VK_NULL_HANDLE; */
/* pipelineCI.basePipelineIndex = -1; */
result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &pipeline.pipeline);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline");
}
vkDestroyShaderModule(device, vertShaderModule, nullptr);
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vLog("createGraphicsPipeline: Created graphics pipeline.");
}
template void VulkanInstance::createGraphicsPipeline<Vertex2D>(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
template void VulkanInstance::createGraphicsPipeline<Vertex3D>(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
//
// BUFFER
//
void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode, std::vector<uint32_t>* qFamiliesWithAccess) {
VkBufferCreateInfo bufferCI{};
bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCI.size = size;
bufferCI.usage = usage;
bufferCI.sharingMode = sharingMode;
std::vector<uint32_t> queueFamiliesWithAccess;
if (sharingMode == VK_SHARING_MODE_CONCURRENT) {
if (qFamiliesWithAccess == nullptr) {
throw VkUserError("Sharing mode is VK_SHARING_MODE_CONCURRENT but qFamiliesWithAccess is nullptr", "createBuffer");
}
bufferCI.queueFamilyIndexCount = static_cast<uint32_t>(qFamiliesWithAccess->size());
bufferCI.pQueueFamilyIndices = qFamiliesWithAccess->data();
}
VkResult result = vkCreateBuffer(device, &bufferCI, nullptr, &buffer);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create buffer", "createBuffer");
}
VkMemoryRequirements memoryRequirements{};
vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements);
VkMemoryAllocateInfo allocI{};
allocI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocI.allocationSize = memoryRequirements.size;
VkMemoryPropertyFlags wantedProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
allocI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, wantedProperties);
result = vkAllocateMemory(device, &allocI, nullptr, &bufferMemory);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to allocate buffer memory!", "createBuffer");
}
vkBindBufferMemory(device, buffer, bufferMemory, NO_OFFSET);
}
VkCommandBuffer VulkanInstance::beginSingleTimeCommands(VkCommandPool commandPool) {
VkCommandBufferAllocateInfo allocI{};
allocI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocI.commandPool = commandPool;
allocI.commandBufferCount = 1;
VkCommandBuffer cmdBuffer;
vkAllocateCommandBuffers(device, &allocI, &cmdBuffer);
VkCommandBufferBeginInfo beginI{};
beginI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuffer, &beginI);
return cmdBuffer;
}
void VulkanInstance::endSingleTimeCommands(VkCommandBuffer cmdBuffer, VkCommandPool commandPool, VkQueue q) {
VkResult result = vkEndCommandBuffer(cmdBuffer);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to end commandBuffer", "endSingleTimeCommands");
}
VkSubmitInfo submitI{};
submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitI.commandBufferCount = 1;
submitI.pCommandBuffers = &cmdBuffer;
uint32_t submitCount = 1;
result = vkQueueSubmit(q, submitCount, &submitI, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to submit commandBuffer", "endSingleTimeCommands");
}
result = vkQueueWaitIdle(q);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to wait for queue idle", "endSingleTimeCommands");
}
const uint32_t commandBufferCount = 1;
vkFreeCommandBuffers(device, commandPool, commandBufferCount, &cmdBuffer);
}
void VulkanInstance::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
VkCommandBuffer cmdBuffer = beginSingleTimeCommands(commandPoolTransfer);
VkBufferCopy copyRegion{};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = size;
uint32_t copyRegionCount = 1;
vkCmdCopyBuffer(cmdBuffer, srcBuffer, dstBuffer, copyRegionCount, &copyRegion);
endSingleTimeCommands(cmdBuffer, commandPoolTransfer, transferQ);
}
uint32_t VulkanInstance::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memoryProperties{};
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) and (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { // if ith bit of typefilter is set
return i;
}
}
throw VkException("Could not find suitable memory type", "findMemoryType");
}
//
// VERTEX BUFFER
//
template<VertexType VertexT>
void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize) {
vertexBufferSize = vertexCount * sizeof(VertexT);
// create vertex buffer
VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE;
std::vector<uint32_t> qFamiliesWithAccess;
if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family
qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() };
}
createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory , sharingMode, &qFamiliesWithAccess);
vLog("createVertexBuffer: Created vertex buffer with size", vertexBufferSize);
}
template void VulkanInstance::createVertexBuffer<Vertex2D>(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize);
template void VulkanInstance::createVertexBuffer<Vertex3D>(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize);
//
// INDEX BUFFER
//
template<SupportedIndexType T>
void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize) {
indexBufferSize = indexCount * sizeof(T);
// create index buffer
VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE;
std::vector<uint32_t> qFamiliesWithAccess;
if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family
qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() };
}
createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory, sharingMode, &qFamiliesWithAccess);
vLog("createIndexBuffer: Created index buffer with size:", indexBufferSize);
}
template void VulkanInstance::createIndexBuffer<uint16_t>(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize);
template void VulkanInstance::createIndexBuffer<uint32_t>(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize);
//
// FRAMEBUFFERS
//
int VulkanInstance::createFramebuffers(std::vector<VkImageView>& imageViews, VkRenderPass& renderPass) {
if (framebufferIDs.empty()) {
int MAX_FRAMEBUFFER_COUNT = 32;
for (int i = 0; i < MAX_FRAMEBUFFER_COUNT; i++) {
framebufferIDs.insert(i);
}
}
int id = *framebufferIDs.begin();
framebufferIDs.erase(id);
scFramebuffers[id].second = renderPass;
createFramebuffers_(scFramebuffers[id].first, imageViews, renderPass);
vLog("createFramebuffers: Created frame buffers, id:", id);
return id;
}
void VulkanInstance::createFramebuffers_(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass) {
framebuffers.resize(scImageViews.size());
VkFramebufferCreateInfo framebufferCI{};
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCI.renderPass = renderPass;
framebufferCI.width = scExtent.width;
framebufferCI.height = scExtent.height;
framebufferCI.layers = 1;
for (unsigned int i = 0; i < scImageViews.size(); i++) {
// TODO include depth image view for 3D
std::vector<VkImageView> attachments = { imageViews[i] }; //, depthImageView };
framebufferCI.pAttachments = attachments.data();
framebufferCI.attachmentCount = static_cast<uint32_t>(attachments.size());
VkResult result = vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffers[i]);
if (result != VK_SUCCESS) {
throw getVkException(result, "Could not create framebuffer", "createFramebuffers_");
}
}
}
void VulkanInstance::recreateFramebuffers() {
for (auto& framebufferPair: scFramebuffers) {
// TODO!!!
/* createFramebuffers_(framebufferPair.second.first, framebufferPair.second.second); */
}
}
std::vector<VkFramebuffer>& VulkanInstance::getFramebuffers(int id) {
return scFramebuffers[id].first;
}
void VulkanInstance::destroyFramebuffers(int id) {
if (scFramebuffers.contains(id)) {
for (auto& framebuffer : scFramebuffers[id].first) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
scFramebuffers.erase(id);
framebufferIDs.insert(id);
}
}
void VulkanInstance::frameBufferResizedCallback(GLFWwindow* window, int width, int height) {
VulkanInstance* app = reinterpret_cast<VulkanInstance*>(glfwGetWindowUserPointer(window));
app->frameBufferResized = true;
}
//
// COMMAND POOL
//
void VulkanInstance::createCommandPools() {
/* QueueFamilyIndicjes queueFamilyIndices = findQueueFamilies(physicalDevice); */
VkCommandPoolCreateInfo commandPoolGraphicsCI{};
commandPoolGraphicsCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
commandPoolGraphicsCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
commandPoolGraphicsCI.queueFamilyIndex = qFamilyIndices.graphicsFamily.value();
VkResult result = vkCreateCommandPool(device, &commandPoolGraphicsCI, nullptr, &commandPoolGraphics);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create graphics command pool", "createCommandPool");
}
if (qFamilyIndices.transferFamily.has_value()) {
VkCommandPoolCreateInfo commandPoolTransferCI{};
commandPoolTransferCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
commandPoolTransferCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
commandPoolTransferCI.queueFamilyIndex = qFamilyIndices.transferFamily.value();
result = vkCreateCommandPool(device, &commandPoolTransferCI, nullptr, &commandPoolTransfer);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create transfer command pool", "createCommandPool");
}
}
else {
commandPoolTransfer = commandPoolGraphics;
}
}
//
// COMMAND BUFFER
//
void VulkanInstance::createCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers) {
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo commandBufferAI{};
commandBufferAI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAI.commandPool = commandPoolGraphics;
commandBufferAI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
commandBufferAI.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
VkResult result = vkAllocateCommandBuffers(device, &commandBufferAI, commandBuffers.data());
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create command buffer", "createCommandBuffer");
}
}
void VulkanInstance::createSyncObjects() {
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
VkSemaphoreCreateInfo semaphoreCI{};
semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceCI{};
fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkResult result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &imageAvailableSemaphores[i]);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create imageAvailableSemaphores", "createSyncObjects");
}
result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &renderFinishedSemaphores[i]);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create renderFinishedSemaphores", "createSyncObjects");
}
result = vkCreateFence(device, &fenceCI, nullptr, &inFlightFences[i]);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create inFlightFences", "createSyncObjects");
}
}
}
//
// IMAGE UTILITY
//
void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory) {
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.extent.width = static_cast<uint32_t>(width);
imageCI.extent.height = static_cast<uint32_t>(height);
imageCI.extent.depth = 1;
imageCI.mipLevels = 1;
imageCI.arrayLayers = 1;
imageCI.format = format;
// use linear when direct texel access is needed
imageCI.tiling = tiling;
imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCI.usage = usage;
imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
/* imageCI.flags = 0; */
VkResult result = vkCreateImage(device, &imageCI, nullptr, &image);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create image", "createTextureImage");
}
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(device, image, &memoryRequirements);
VkMemoryAllocateInfo memoryAI{};
memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAI.allocationSize = memoryRequirements.size;
memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties);
result = vkAllocateMemory(device, &memoryAI, nullptr, &imageMemory);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to allocate memory for image", "createTextureImage");
}
vkBindImageMemory(device, image, imageMemory, NO_OFFSET);
}
void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
VkCommandBuffer cmdBuffer = beginSingleTimeCommands(commandPoolTransfer);
VkBufferImageCopy region{};
region.bufferOffset = NO_OFFSET;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = { width, height, 1 };
const uint32_t regionCount = 1;
vkCmdCopyBufferToImage(cmdBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, &region);
endSingleTimeCommands(cmdBuffer, commandPoolTransfer, transferQ);
}
void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) {
VkImageCopy region{};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
/* std::array<VkOffset3D, 2> offsets; */
/* offsets[0] = { 0, 0, 0 }; */
/* offsets[1] = { 0, 0, 0 }; */
region.srcOffset = { 0, 0, 0 };
region.dstOffset = { 0, 0, 0 };
region.extent = { width, height, 1 };
const uint32_t regionCount = 1;
vkCmdCopyImage(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, &region);
}
bool hasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D32_SFLOAT_S8_UINT or format == VK_FORMAT_D24_UNORM_S8_UINT;
}
void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer) {
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
// not using barrier for queue famlily ownership transfer
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = 0;
VkPipelineStageFlags srcStage;
VkPipelineStageFlags dstStage;
VkCommandPool commandPool;
VkQueue q;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and
newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (hasStencilComponent(format)) {
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
commandPool = commandPoolGraphics;
q = graphicsQ;
}
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
commandPool = commandPoolTransfer;
q = transferQ;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and
newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
commandPool = commandPoolGraphics;
q = graphicsQ;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and
newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
commandPool = commandPoolGraphics;
q = graphicsQ;
}
else {
throw VkUserError(std::string("transitionImageLayout: Unsupported layout transition") + STR_VK_IMAGE_LAYOUT(oldLayout) + "->" + STR_VK_IMAGE_LAYOUT(newLayout), "transitionImageLayout");;
}
// if not provided, get a single time command buffer
VkCommandBuffer cmdBuffer_;
if (cmdBuffer == nullptr) {
cmdBuffer_ = beginSingleTimeCommands(commandPool);
}
else {
cmdBuffer_ = *cmdBuffer;
}
uint32_t memBarrierCount = 0, bufBarrierCount = 0;
uint32_t imBarrierCount = 1;
vkCmdPipelineBarrier(cmdBuffer_, srcStage, dstStage, NO_FLAGS,
memBarrierCount, nullptr,
bufBarrierCount, nullptr,
imBarrierCount, &barrier);
if (cmdBuffer == nullptr) {
endSingleTimeCommands(cmdBuffer_, commandPool, q);
}
}
//
// DEPTH
//
VkFormat VulkanInstance::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 VulkanInstance::createDepthImageAndView() {
VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
VkFormat depthFormat = findDepthFormat();
createImage(scExtent.width, scExtent.height, depthFormat, tiling, imageUsage, memoryProperties, depthImage, depthImageMemory);
createImageView(depthFormat, depthImage, depthImageView, VK_IMAGE_ASPECT_DEPTH_BIT);
transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
//
// TEXTURE
//
void VulkanInstance::createTextureImageAndView() {
int textureWidth, textureHeight, textureChannels;
stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
if (!pixels) {
throw Exception("Failed to load texture image", "createTextureImage");
}
const size_t BYTES_PER_PIXEL = 4;
VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL;
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);
void* data;
vkMapMemory(device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(device, stagingBufferMemory);
stbi_image_free(pixels);
createImage(textureWidth, textureHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
textureImage, textureImageMemory);
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(textureWidth), static_cast<uint32_t>(textureHeight));
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkDestroyBuffer(device, stagingBuffer, nullptr);
vkFreeMemory(device, stagingBufferMemory, nullptr);
createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT);
}
void VulkanInstance::createTextureSampler() {
VkSamplerCreateInfo samplerCI{};
samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
// TODO: LINEAR or NEAREST
samplerCI.magFilter = VK_FILTER_LINEAR;
samplerCI.minFilter = VK_FILTER_LINEAR;
samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCI.anisotropyEnable = bool2VkBool(settings.get<bool>("anisotropy_enable"));
samplerCI.maxAnisotropy = settings.get<float>("max_anisotropy");
samplerCI.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
// TODO
samplerCI.unnormalizedCoordinates = VK_FALSE;
samplerCI.compareEnable = VK_FALSE;
samplerCI.compareOp = VK_COMPARE_OP_ALWAYS;
samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCI.mipLodBias = 0.0f;
samplerCI.minLod = 0.0f;
samplerCI.maxLod = 0.0f;
VkResult result = vkCreateSampler(device, &samplerCI, nullptr, &textureSampler);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create texture sampler.", "createTextureSampler");
}
}
//
// SHADER MODULE
//
VkShaderModule VulkanInstance::createShaderModule(const std::vector<char>& code) {
VkShaderModuleCreateInfo smCreateInfo{};
smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
smCreateInfo.codeSize = code.size();
smCreateInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
VkShaderModule shaderModule{};
VkResult result = vkCreateShaderModule(device, &smCreateInfo, nullptr, &shaderModule);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to create shader module. Code size: " + std::to_string(code.size()), "createShaderModule");
}
return shaderModule;
}
//
// SHADERS
//
std::vector<char> VulkanInstance::readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
std::vector<char> buffer;
if (file.is_open()) {
size_t fileSize = file.tellg();
buffer.resize(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
}
else {
vLog.error("readFile: Failed to read file:", filename);
}
file.close();
return buffer;
}
//
// MODELS
//
void VulkanInstance::loadModel() {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warnings, errors;
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
throw gz::Exception("Error loading obj: " + errors, "loadModel");
}
if (!warnings.empty()) {
vLog.warning("Warning from tinyobj::LoadObj: ", warnings);
}
std::unordered_map<Vertex3D, uint32_t> uniqueVertices;
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
Vertex3D vertex{};
vertex.pos = {
attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]
};
vertex.texCoord = {
attrib.vertices[2 * index.texcoord_index + 0],
// obj: y = 0 means bottom, vulkan: y = 0 means top
1.0f - attrib.vertices[2 * index.texcoord_index + 1],
};
vertex.color = { 1.0f, 1.0f, 1.0f };
if (!uniqueVertices.contains(vertex)) {
uniqueVertices[vertex] = model.vertices.size();
model.vertices.push_back(vertex);
}
/* model.vertices.push_back(vertex); */
model.indices.push_back(uniqueVertices[vertex]);
}
}
}
//
// DEBUG
//
void VulkanInstance::setupDebugMessenger() {
// get the address of the vkCreateDebugUtilsMessengerEXT function
VkResult result = runVkResultFunction<PFN_vkCreateDebugUtilsMessengerEXT>("vkCreateDebugUtilsMessengerEXT", instance, &debugUtilsMessengerCI, nullptr, &debugMessenger);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to initialise debug messenger", "setupDebugMessenger");
}
/* auto f = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); */
/* if (f == nullptr) { throw std::runtime_error("Failed to initialise debug messenger."); } */
/* else { */
/* f(instance, &debugUtilsMessengerCreateInfo, nullptr, &debugMessenger); */
/* } */
}
void VulkanInstance::cleanupDebugMessenger() {
runVkVoidFunction<PFN_vkDestroyDebugUtilsMessengerEXT>("vkDestroyDebugUtilsMessengerEXT", instance, debugMessenger, nullptr);
/* throw std::runtime_error("Failed to destroy debug messenger."); */
}
//
// APP
//
void VulkanInstance::createWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, frameBufferResizedCallback);
}
void VulkanInstance::init() {
createWindow();
if (glfwVulkanSupported() == GLFW_FALSE) {
throw std::runtime_error("Vulkan not supported.");
}
createInstance();
setupDebugMessenger();
createSurface();
selectPhysicalDevice();
setValidSettings();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPassBegin();
createRenderPassEnd();
/* createDescriptorSetLayout(); */
// after descriptorSetLayout
createGraphicsPipeline<Vertex2D>("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]);
/* createGraphicsPipeline<Vertex3D>("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */
createCommandPools();
createDepthImageAndView(); // after CommandPools
createTextureImageAndView(); // after CommandPools
/* createFramebuffers(); // after depth image */
createTextureSampler();
loadModel();
/* createVertexBuffer(VERTEX_BUFFER_SIZE); */
/* createIndexBuffer<uint32_t>(INDEX_BUFFER_SIZE); */
/* createUniformBuffers(); */
createCommandBuffers(commandBuffersBegin);
createCommandBuffers(commandBuffersEnd);
createSyncObjects();
/* createDescriptorPool(); */
/* createDescriptorSets(); // after UniformBuffers */
}
void VulkanInstance::cleanup() {
vkDeviceWaitIdle(device);
vkDestroyImageView(device, textureImageView, nullptr);
vkDestroySampler(device, textureSampler, nullptr);
vkDestroyImage(device, textureImage, nullptr);
vkFreeMemory(device, textureImageMemory, nullptr);
cleanupSwapChain();
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
vkDestroyFence(device, inFlightFences[i], nullptr);
}
/* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast<uint32_t>(commandBuffers2D.size()), commandBuffers2D.data()); */
/* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast<uint32_t>(commandBuffers3D.size()), commandBuffers3D.data()); */
vkDestroyCommandPool(device, commandPoolGraphics, nullptr);
vkDestroyCommandPool(device, commandPoolTransfer, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
cleanupDebugMessenger();
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
//
// APP
//
void VulkanInstance::mainLoop() {
vkDeviceWaitIdle(device);
}
uint32_t VulkanInstance::beginFrameDraw() {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
uint32_t blockFor = 0;
/* uint64_t blockFor = UINT64_MAX; */
VkResult result = vkAcquireNextImageKHR(device, swapChain, blockFor, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR or frameBufferResized) { // result == VK_SUBOPTIMAL_KHR or
vLog("drawFrame: result:", STR_VK_RESULT(result), "frameBufferResized:", frameBufferResized);
frameBufferResized = false;
recreateSwapChain();
return imageIndex;
}
else if (result != VK_SUBOPTIMAL_KHR and result != VK_SUCCESS) {
vLog.error("Failed to acquire swap chain image. Result:", STR_VK_RESULT(result));
throw std::runtime_error("Failed to acquire swap chain image");
}
vkResetFences(device, 1, &inFlightFences[currentFrame]);
// clear image
vkResetCommandBuffer(commandBuffersBegin[currentFrame], NO_FLAGS);
VkCommandBufferBeginInfo commandBufferBI{};
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
result = vkBeginCommandBuffer(commandBuffersBegin[currentFrame], &commandBufferBI);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to begin clear command buffer", "beginFrameDraw");
}
// transition to transfer dst layout
transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &commandBuffersBegin[currentFrame]);
/* VkRenderPassBeginInfo renderPassBI{}; */
/* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */
/* renderPassBI.renderPass = renderPassBegin; */
/* renderPassBI.framebuffer = scFramebuffers[imageIndex]; */
/* renderPassBI.renderArea.offset = { 0, 0 }; */
/* renderPassBI.renderArea.extent = scExtent; */
/* std::array<VkClearValue, 2> clearValues{}; */
/* clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; */
/* clearValues[1].depthStencil = {1.0f, 0}; */
/* renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size()); */
/* renderPassBI.pClearValues = clearValues.data(); */
/* vkCmdBeginRenderPass(commandBuffersBegin[imageIndex], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */
/* vkCmdEndRenderPass(commandBuffersBegin[imageIndex]); */
result = vkEndCommandBuffer(commandBuffersBegin[currentFrame]);
if (result != VK_SUCCESS) {
vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result));
throw getVkException(result, "Failed to record 2D - command buffer", "beginFrameDraw");
}
commandBuffersToSubmitThisFrame.push_back(commandBuffersBegin[currentFrame]);
return imageIndex;
}
void VulkanInstance::endFrameDraw(uint32_t imageIndex) {
vkResetCommandBuffer(commandBuffersEnd[currentFrame], NO_FLAGS);
VkCommandBufferBeginInfo commandBufferBI{};
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VkResult result = vkBeginCommandBuffer(commandBuffersEnd[currentFrame], &commandBufferBI);
if (result != VK_SUCCESS) {
throw getVkException(result, "Failed to begin command buffer", "beginFrameDraw");
}
// transition to present layout
transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, &commandBuffersEnd[currentFrame]);
/* VkRenderPassBeginInfo renderPassBI{}; */
/* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */
/* renderPassBI.renderPass = renderPassEnd; */
/* renderPassBI.framebuffer = scFramebuffers[imageIndex]; */
/* renderPassBI.renderArea.offset = { 0, 0 }; */
/* renderPassBI.renderArea.extent = scExtent; */
/* vkCmdBeginRenderPass(commandBuffersEnd[imageIndex], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */
/* vkCmdEndRenderPass(commandBuffersEnd[imageIndex]); */
result = vkEndCommandBuffer(commandBuffersEnd[currentFrame]);
if (result != VK_SUCCESS) {
vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result));
throw getVkException(result, "Failed to record command buffer", "beginFrameDraw");
}
commandBuffersToSubmitThisFrame.push_back(commandBuffersEnd[currentFrame]);
VkSubmitInfo submitI{};
submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitI.waitSemaphoreCount = 1;
submitI.pWaitSemaphores = waitSemaphores;
submitI.pWaitDstStageMask = waitStages;
submitI.commandBufferCount = static_cast<uint32_t>(commandBuffersToSubmitThisFrame.size());
submitI.pCommandBuffers = commandBuffersToSubmitThisFrame.data();
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
submitI.signalSemaphoreCount = 1;
submitI.pSignalSemaphores = signalSemaphores;
uint32_t submitCount = 1;
result = vkQueueSubmit(graphicsQ, submitCount, &submitI, inFlightFences[currentFrame]);
if (result != VK_SUCCESS) {
vLog.error("Failed to submit draw command buffer", "VkResult:", STR_VK_RESULT(result));
throw std::runtime_error("Failed to submit draw command buffer");
}
commandBuffersToSubmitThisFrame.clear();
VkPresentInfoKHR presentI{};
presentI.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentI.waitSemaphoreCount = 1;
presentI.pWaitSemaphores = signalSemaphores;
vLog("endFrameDraw:: imageIndex:", imageIndex);
VkSwapchainKHR swapChains[] = {swapChain};
presentI.swapchainCount = 1;
presentI.pSwapchains = swapChains;
presentI.pImageIndices = &imageIndex;
presentI.pResults = nullptr;
vkQueuePresentKHR(presentQ, &presentI);
currentFrame = ++currentFrame % MAX_FRAMES_IN_FLIGHT;
}
} // namespace gz::vk