2201 lines
92 KiB
C++
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, ©Region);
|
|
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, ®ion);
|
|
|
|
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, ®ion);
|
|
}
|
|
|
|
|
|
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
|
|
|