Properly set up transitioning image layouts to pair well with dynamic rendering, refactor some code.

This commit is contained in:
Lillian Salehi 2024-11-05 05:50:51 -06:00
parent 7770063537
commit 4a8f6909a8
5 changed files with 670 additions and 548 deletions

View File

@ -15,34 +15,39 @@ namespace device_libs {
std::vector<VkPresentModeKHR> presentModes; std::vector<VkPresentModeKHR> presentModes;
}; };
const std::vector<const char *> deviceExtensions = { const std::vector<const char *> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME VK_KHR_SWAPCHAIN_EXTENSION_NAME};
};
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
/* Swap chains are weird ngl, it's another one of those Vulkan platform agnosticity. /* Swap chains are weird ngl, it's another one of those Vulkan platform
The swapchain is basically a wrapper for GDI+, DXGI, X11, Wayland, etc. agnosticity. The swapchain is basically a wrapper for GDI+, DXGI, X11,
It lets us use the swap chain rather than create a different framebuffer Wayland, etc. It lets us use the swap chain rather than create a different
handler for every targeted platform. Swap chains handle the ownership framebuffer handler for every targeted platform. Swap chains handle the
of buffers before sending them to the presentation engine. (still no ownership of buffers before sending them to the presentation engine. (still
fucking clue how it works though) */ no fucking clue how it works though) */
SwapChainSupportDetails details; SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, Global::surface, &details.capabilities); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, Global::surface,
&details.capabilities);
uint32_t formatCount; uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount, nullptr); vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount,
nullptr);
if (formatCount != 0) { if (formatCount != 0) {
details.formats.resize(formatCount); details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount, details.formats.data()); vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount,
details.formats.data());
} }
uint32_t presentModeCount; uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, Global::surface, &presentModeCount, details.presentModes.data()); vkGetPhysicalDeviceSurfacePresentModesKHR(
device, Global::surface, &presentModeCount, details.presentModes.data());
if (presentModeCount != 0) { if (presentModeCount != 0) {
details.presentModes.resize(presentModeCount); details.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, Global::surface, &presentModeCount, details.presentModes.data()); vkGetPhysicalDeviceSurfacePresentModesKHR(device, Global::surface,
&presentModeCount,
details.presentModes.data());
} }
return details; return details;
@ -50,12 +55,15 @@ namespace device_libs {
bool checkDeviceExtensionSupport(VkPhysicalDevice device) { bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
uint32_t extensionCount; uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount); std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
availableExtensions.data());
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); std::set<std::string> requiredExtensions(deviceExtensions.begin(),
deviceExtensions.end());
for (const auto &extension : availableExtensions) { for (const auto &extension : availableExtensions) {
requiredExtensions.erase(extension.extensionName); requiredExtensions.erase(extension.extensionName);
@ -65,47 +73,57 @@ namespace device_libs {
} }
bool isDeviceSuitable(VkPhysicalDevice device) { bool isDeviceSuitable(VkPhysicalDevice device) {
// These two are simple, create a structure to hold the apiVersion, driverVersion, vendorID, deviceID and type, name, and a few other settings. // These two are simple, create a structure to hold the apiVersion,
// driverVersion, vendorID, deviceID and type, name, and a few other settings.
// Then populate it by passing in the device and the structure reference. // Then populate it by passing in the device and the structure reference.
vkGetPhysicalDeviceProperties(device, &deviceProperties); vkGetPhysicalDeviceProperties(device, &deviceProperties);
// Similarly, we can pass in the device and a deviceFeatures struct, this is quite special, it holds a struct of optional features the GPU can perform. // Similarly, we can pass in the device and a deviceFeatures struct, this is
// Some, like a geometry shader, and stereoscopic rendering (multiViewport) we want, so we dont return true without them. // quite special, it holds a struct of optional features the GPU can perform.
// Some, like a geometry shader, and stereoscopic rendering (multiViewport) we
// want, so we dont return true without them.
VkPhysicalDeviceFeatures supportedFeatures; VkPhysicalDeviceFeatures supportedFeatures;
vkGetPhysicalDeviceFeatures(device, &supportedFeatures); vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
// We need to find a device that supports graphical operations, or else we cant do much with it! This function just runs over all the queueFamilies and sees if there // We need to find a device that supports graphical operations, or else we
// is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped! // cant do much with it! This function just runs over all the queueFamilies
// and sees if there is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped!
Global::QueueFamilyIndices indices = Global::findQueueFamilies(device); Global::QueueFamilyIndices indices = Global::findQueueFamilies(device);
bool extensionSupported = checkDeviceExtensionSupport(device); bool extensionSupported = checkDeviceExtensionSupport(device);
bool swapChainAdequate = false; bool swapChainAdequate = false;
if (extensionSupported) { if (extensionSupported) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); swapChainAdequate = !swapChainSupport.formats.empty() &&
!swapChainSupport.presentModes.empty();
} }
return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
&& supportedFeatures.samplerAnisotropy supportedFeatures.samplerAnisotropy && indices.isComplete() &&
&& indices.isComplete() extensionSupported && swapChainAdequate;
&& extensionSupported
&& swapChainAdequate;
} }
// -------------------------------------- Swap Chain Settings ----------------------------------------- // // -------------------------------------- Swap Chain Settings
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { // ----------------------------------------- //
// One of three settings we can set, Surface Format controls the color space and format. VkSurfaceFormatKHR chooseSwapSurfaceFormat(
const std::vector<VkSurfaceFormatKHR> &availableFormats) {
// One of three settings we can set, Surface Format controls the color space
// and format.
for (const auto &availableFormat : availableFormats) { for (const auto &availableFormat : availableFormats) {
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
// sRGB & 32bit BGRA // sRGB & 32bit BGRA
return availableFormat; return availableFormat;
} }
} }
return availableFormats[0]; return availableFormats[0];
} }
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) { VkPresentModeKHR chooseSwapPresentMode(
// The second of the three settings, arguably the most important, the presentation mode! This dictates how images are displayed. const std::vector<VkPresentModeKHR> &availablePresentModes) {
// MAILBOX is basically equivalent to triple buffering, it avoids screen tearing with fairly low latency, // The second of the three settings, arguably the most important, the
// However, it is not always supported, so in the case that it isn't, currently we will default to FIFO, // presentation mode! This dictates how images are displayed. MAILBOX is
// This is most similarly to standard V-Sync. // basically equivalent to triple buffering, it avoids screen tearing with
// fairly low latency, However, it is not always supported, so in the case
// that it isn't, currently we will default to FIFO, This is most similarly to
// standard V-Sync.
for (const auto &availablePresentMode : availablePresentModes) { for (const auto &availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentMode; return availablePresentMode;
@ -114,29 +132,37 @@ namespace device_libs {
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
} }
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) { VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities,
// Swap Extent is just a fancy way of saying the resolution of the swap images to display. GLFWwindow *window) {
// This is almost always going to equal the resolution of the window in pixels. // Swap Extent is just a fancy way of saying the resolution of the swap images
// to display. This is almost always going to equal the resolution of the
// window in pixels.
// The max int32 value tells us that the window manager lets us change the windth and height to what we wish! // The max int32 value tells us that the window manager lets us change the
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { // windth and height to what we wish!
if (capabilities.currentExtent.width !=
std::numeric_limits<uint32_t>::max()) {
return capabilities.currentExtent; return capabilities.currentExtent;
} else { } else {
int width, height; int width, height;
glfwGetFramebufferSize(window, &width, &height); glfwGetFramebufferSize(window, &width, &height);
VkExtent2D actualExtent = { VkExtent2D actualExtent = {static_cast<uint32_t>(width),
static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
static_cast<uint32_t>(height) // Clamp the image size to the minimum extent values specified by vulkan for
}; // our window manager.
// Clamp the image size to the minimum extent values specified by vulkan for our window manager. actualExtent.width =
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); std::clamp(actualExtent.width, capabilities.minImageExtent.width,
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); capabilities.maxImageExtent.width);
actualExtent.height =
std::clamp(actualExtent.height, capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
return actualExtent; return actualExtent;
} }
} }
// --------------------------------------- External Functions ----------------------------------------- // // --------------------------------------- External Functions
// ----------------------------------------- //
void DeviceControl::pickPhysicalDevice(VkInstance &instance) { void DeviceControl::pickPhysicalDevice(VkInstance &instance) {
uint32_t deviceCount = 0; uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
@ -144,12 +170,14 @@ namespace device_libs {
if (deviceCount == 0) { if (deviceCount == 0) {
throw std::runtime_error("Failed to find GPU's with Vulkan Support!!"); throw std::runtime_error("Failed to find GPU's with Vulkan Support!!");
} }
std::vector<VkPhysicalDevice> devices(deviceCount); // Direct Initialization is weird af, yo std::vector<VkPhysicalDevice> devices(
deviceCount); // Direct Initialization is weird af, yo
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
for (const auto &device : devices) { for (const auto &device : devices) {
if (isDeviceSuitable(device)) { if (isDeviceSuitable(device)) {
//Once we have buttons or such, maybe ask the user or write a config file for which GPU to use? // Once we have buttons or such, maybe ask the user or write a config file
// for which GPU to use?
Global::physicalDevice = device; Global::physicalDevice = device;
break; break;
} }
@ -162,21 +190,23 @@ namespace device_libs {
vkDestroySurfaceKHR(instance, Global::surface, nullptr); vkDestroySurfaceKHR(instance, Global::surface, nullptr);
} }
void DeviceControl::createSurface(VkInstance &instance, GLFWwindow *window) { void DeviceControl::createSurface(VkInstance &instance, GLFWwindow *window) {
if(glfwCreateWindowSurface(instance, window, nullptr, &Global::surface) != VK_SUCCESS) { if (glfwCreateWindowSurface(instance, window, nullptr, &Global::surface) !=
VK_SUCCESS) {
throw std::runtime_error("Failed to create window surface!!"); throw std::runtime_error("Failed to create window surface!!");
} }
} }
void DeviceControl::createLogicalDevice() { void DeviceControl::createLogicalDevice() {
// Describe how many queues we want for a single family (1) here, right now we are solely interested in graphics capabilites, // Describe how many queues we want for a single family (1) here, right now we
// but Compute Shaders, transfer ops, decode and encode operations can also queued with setup! We also assign each queue a priority. // are solely interested in graphics capabilites, but Compute Shaders,
// We do this by looping over all the queueFamilies and sorting them by indices to fill the queue at the end! // transfer ops, decode and encode operations can also queued with setup! We
Global::QueueFamilyIndices indices = Global::findQueueFamilies(Global::physicalDevice); // also assign each queue a priority. We do this by looping over all the
// queueFamilies and sorting them by indices to fill the queue at the end!
Global::QueueFamilyIndices indices =
Global::findQueueFamilies(Global::physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = { std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(),
indices.graphicsFamily.value(), indices.presentFamily.value()};
indices.presentFamily.value()
};
float queuePriority = 1.0f; float queuePriority = 1.0f;
for (uint32_t queueFamily : uniqueQueueFamilies) { for (uint32_t queueFamily : uniqueQueueFamilies) {
@ -191,6 +221,7 @@ namespace device_libs {
VkPhysicalDeviceVulkan13Features features13{ VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = nullptr, .pNext = nullptr,
.synchronization2 = true,
.dynamicRendering = true, .dynamicRendering = true,
}; };
VkPhysicalDeviceFeatures featuresBase{ VkPhysicalDeviceFeatures featuresBase{
@ -207,28 +238,40 @@ namespace device_libs {
createDeviceInfo.pNext = &deviceFeatures; createDeviceInfo.pNext = &deviceFeatures;
createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createDeviceInfo.pQueueCreateInfos = queueCreateInfos.data(); createDeviceInfo.pQueueCreateInfos = queueCreateInfos.data();
createDeviceInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); createDeviceInfo.queueCreateInfoCount =
createDeviceInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); static_cast<uint32_t>(queueCreateInfos.size());
createDeviceInfo.enabledExtensionCount =
static_cast<uint32_t>(deviceExtensions.size());
createDeviceInfo.ppEnabledExtensionNames = deviceExtensions.data(); createDeviceInfo.ppEnabledExtensionNames = deviceExtensions.data();
if(vkCreateDevice(Global::physicalDevice, &createDeviceInfo, nullptr, &Global::device) != VK_SUCCESS) { if (vkCreateDevice(Global::physicalDevice, &createDeviceInfo, nullptr,
&Global::device) != VK_SUCCESS) {
throw std::runtime_error("Failed to create logical device"); throw std::runtime_error("Failed to create logical device");
} }
vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0, &Global::graphicsQueue); vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0,
vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0, &Global::presentQueue); &Global::graphicsQueue);
vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0,
&Global::presentQueue);
} }
void DeviceControl::createSwapChain(GLFWwindow *window) { void DeviceControl::createSwapChain(GLFWwindow *window) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(Global::physicalDevice); SwapChainSupportDetails swapChainSupport =
querySwapChainSupport(Global::physicalDevice);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); VkSurfaceFormatKHR surfaceFormat =
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); chooseSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode =
chooseSwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities, window); VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities, window);
// Number of images to hold in the swap chain, 1 over the minimum guarantees we won't have to wait on the driver to complete // Number of images to hold in the swap chain, 1 over the minimum guarantees
// internal operations before acquiring another image. Absolutely a TODO to determine the best amount to queue. // we won't have to wait on the driver to complete internal operations before
// acquiring another image. Absolutely a TODO to determine the best amount to
// queue.
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
// Make sure not to queue more than the max! 0 indicates that there is no maximum. // Make sure not to queue more than the max! 0 indicates that there is no
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { // maximum.
if (swapChainSupport.capabilities.maxImageCount > 0 &&
imageCount > swapChainSupport.capabilities.maxImageCount) {
imageCount = swapChainSupport.capabilities.maxImageCount; imageCount = swapChainSupport.capabilities.maxImageCount;
} }
@ -239,18 +282,24 @@ namespace device_libs {
createSwapChainInfo.imageFormat = surfaceFormat.format; createSwapChainInfo.imageFormat = surfaceFormat.format;
createSwapChainInfo.imageColorSpace = surfaceFormat.colorSpace; createSwapChainInfo.imageColorSpace = surfaceFormat.colorSpace;
createSwapChainInfo.imageExtent = extent; createSwapChainInfo.imageExtent = extent;
// Image array layers is always 1 unless we are developing for VR (Spoiler: we are, we will use a build flag.) // Image array layers is always 1 unless we are developing for VR (Spoiler: we
// Image Usage specifies what operations you use the images for, COLOR_ATTACH means we render directly to them, // are, we will use a build flag.) Image Usage specifies what operations you
// if you wanted to render to separate images for things like post processing, you can use TRANSFER_DST and use a // use the images for, COLOR_ATTACH means we render directly to them, if you
// memory operation to transfer the image to a swap chain, this is also a TODO item eventually. // wanted to render to separate images for things like post processing, you
// can use TRANSFER_DST and use a memory operation to transfer the image to a
// swap chain, this is also a TODO item eventually.
createSwapChainInfo.imageArrayLayers = 1; createSwapChainInfo.imageArrayLayers = 1;
createSwapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; createSwapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
// This handles swap chain images across multiple queue families, ie, if the graphics queue family is different from the present queue // This handles swap chain images across multiple queue families, ie, if the
Global::QueueFamilyIndices indices = Global::findQueueFamilies(Global::physicalDevice); // graphics queue family is different from the present queue
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; Global::QueueFamilyIndices indices =
// Usage across multiple queue families without explicit transfer of ownership if they are different queue families. Global::findQueueFamilies(Global::physicalDevice);
// Otherwise, no sharing without explicit handoffs, faster, but not easily supported with multiple families. uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(),
indices.presentFamily.value()};
// Usage across multiple queue families without explicit transfer of ownership
// if they are different queue families. Otherwise, no sharing without
// explicit handoffs, faster, but not easily supported with multiple families.
// Presentation and Graphics families are usually merged on most hardware. // Presentation and Graphics families are usually merged on most hardware.
if (indices.graphicsFamily != indices.presentFamily) { if (indices.graphicsFamily != indices.presentFamily) {
createSwapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; createSwapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
@ -260,24 +309,30 @@ namespace device_libs {
createSwapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createSwapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
} }
// Transformation of image support. // Transformation of image support.
createSwapChainInfo.preTransform = swapChainSupport.capabilities.currentTransform; createSwapChainInfo.preTransform =
swapChainSupport.capabilities.currentTransform;
// Do NOT blend with other windows on the system. // Do NOT blend with other windows on the system.
createSwapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createSwapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createSwapChainInfo.presentMode = presentMode; createSwapChainInfo.presentMode = presentMode;
// This is interesting, clip pixels that are obscured for performance, but that means you wont be able to read them reliably.. // This is interesting, clip pixels that are obscured for performance, but
// I am curious if this would affect screen-space rendering techniques, may be something to note. // that means you wont be able to read them reliably.. I am curious if this
// would affect screen-space rendering techniques, may be something to note.
createSwapChainInfo.clipped = VK_TRUE; createSwapChainInfo.clipped = VK_TRUE;
// This is something that needs to be implemented later, operations like resizing the window invalidate the swap chain and // This is something that needs to be implemented later, operations like
// require you to recreate it and reference the old one specified here, will revisit in a few days. // resizing the window invalidate the swap chain and require you to recreate
// it and reference the old one specified here, will revisit in a few days.
// createSwapChainInfo.oldSwapchain = VK_NULL_HANDLE; // createSwapChainInfo.oldSwapchain = VK_NULL_HANDLE;
if(vkCreateSwapchainKHR(Global::device, &createSwapChainInfo, nullptr, &Global::swapChain) != VK_SUCCESS) { if (vkCreateSwapchainKHR(Global::device, &createSwapChainInfo, nullptr,
&Global::swapChain) != VK_SUCCESS) {
throw std::runtime_error("Failed to create the swap chain!!"); throw std::runtime_error("Failed to create the swap chain!!");
} }
vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount, nullptr); vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount,
nullptr);
swapChainImages.resize(imageCount); swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount, swapChainImages.data()); vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount,
swapChainImages.data());
swapChainImageFormat = surfaceFormat.format; swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent; swapChainExtent = extent;
@ -285,7 +340,9 @@ namespace device_libs {
void DeviceControl::destroySwapChain() { void DeviceControl::destroySwapChain() {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr); vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
} }
VkImageView DeviceControl::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipLevels) { VkImageView DeviceControl::createImageView(VkImage image, VkFormat format,
VkImageAspectFlags flags,
uint32_t mipLevels) {
// This defines the parameters of a newly created image object! // This defines the parameters of a newly created image object!
VkImageViewCreateInfo viewInfo{}; VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@ -300,7 +357,8 @@ namespace device_libs {
viewInfo.subresourceRange.levelCount = mipLevels; viewInfo.subresourceRange.levelCount = mipLevels;
VkImageView imageView; VkImageView imageView;
if (vkCreateImageView(Global::device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { if (vkCreateImageView(Global::device, &viewInfo, nullptr, &imageView) !=
VK_SUCCESS) {
throw std::runtime_error("failed to create image view!"); throw std::runtime_error("failed to create image view!");
} }
@ -310,7 +368,8 @@ namespace device_libs {
Global::swapChainImageViews.resize(swapChainImages.size()); Global::swapChainImageViews.resize(swapChainImages.size());
for (uint32_t i = 0; i < swapChainImages.size(); i++) { for (uint32_t i = 0; i < swapChainImages.size(); i++) {
Global::swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1); Global::swapChainImageViews[i] = createImageView(
swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
} }
} }
void DeviceControl::destroyImageViews() { void DeviceControl::destroyImageViews() {
@ -318,15 +377,12 @@ namespace device_libs {
vkDestroyImageView(Global::device, imageView, nullptr); vkDestroyImageView(Global::device, imageView, nullptr);
} }
} }
// --------------------------------------- Getters & Setters ------------------------------------------ // // --------------------------------------- Getters & Setters
VkFormat* DeviceControl::getImageFormat() { // ------------------------------------------ //
return &swapChainImageFormat; VkFormat *DeviceControl::getImageFormat() { return &swapChainImageFormat; }
} VkExtent2D DeviceControl::getSwapChainExtent() { return swapChainExtent; }
VkExtent2D DeviceControl::getSwapChainExtent() {
return swapChainExtent;
}
std::vector<VkImage> DeviceControl::getSwapChainImages() { std::vector<VkImage> DeviceControl::getSwapChainImages() {
return swapChainImages; return swapChainImages;
} }
} } // namespace device_libs

View File

@ -7,10 +7,9 @@ VkInstance vulkaninstance;
void EntryApp::setFramebufferResized(bool setter) { void EntryApp::setFramebufferResized(bool setter) {
framebufferResized = setter; framebufferResized = setter;
} }
bool EntryApp::getFramebufferResized() const { bool EntryApp::getFramebufferResized() const { return framebufferResized; }
return framebufferResized; static void framebufferResizeCallback(GLFWwindow *window, int width,
} int height) {
static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
auto app = reinterpret_cast<EntryApp *>(glfwGetWindowUserPointer(window)); auto app = reinterpret_cast<EntryApp *>(glfwGetWindowUserPointer(window));
app->setFramebufferResized(true); app->setFramebufferResized(true);
} }
@ -21,7 +20,8 @@ void initWindow() {
glfwInit(); glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
// Settings for the window are set, create window reference. // Settings for the window are set, create window reference.
Global::window = glfwCreateWindow(Global::WIDTH, Global::HEIGHT, "Trimgles :o", nullptr, nullptr); Global::window = glfwCreateWindow(Global::WIDTH, Global::HEIGHT,
"Trimgles :o", nullptr, nullptr);
glfwSetWindowUserPointer(Global::window, &EntryApp::getInstance()); glfwSetWindowUserPointer(Global::window, &EntryApp::getInstance());
glfwSetFramebufferSizeCallback(Global::window, framebufferResizeCallback); glfwSetFramebufferSizeCallback(Global::window, framebufferResizeCallback);
} }
@ -31,31 +31,46 @@ void createInstance() {
// Set application info for the vulkan instance! // Set application info for the vulkan instance!
VkApplicationInfo appInfo{}; VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; // Tell vulkan that appInfo is a Application Info structure appInfo.sType =
VK_STRUCTURE_TYPE_APPLICATION_INFO; // Tell vulkan that appInfo is a
// Application Info structure
appInfo.pApplicationName = "Triangle Test"; // Give the struct a name to use appInfo.pApplicationName = "Triangle Test"; // Give the struct a name to use
appInfo.applicationVersion = VK_MAKE_VERSION(1,0,0); // Create a Major Minor Patch version number for the application! appInfo.applicationVersion = VK_MAKE_VERSION(
appInfo.pEngineName = "Agnosia Engine"; // Give an internal name for the engine running 1, 0,
appInfo.engineVersion = VK_MAKE_VERSION(1,0,0); // Similar to the App version, give vulkan an *engine* version 0); // Create a Major Minor Patch version number for the application!
appInfo.apiVersion = VK_API_VERSION_1_3; // Tell vulkan what the highest API version we will allow this program to run on appInfo.pEngineName =
"Agnosia Engine"; // Give an internal name for the engine running
appInfo.engineVersion = VK_MAKE_VERSION(
1, 0, 0); // Similar to the App version, give vulkan an *engine* version
appInfo.apiVersion =
VK_API_VERSION_1_3; // Tell vulkan what the highest API version we will
// allow this program to run on
// This gets a little weird, Vulkan is platform agnostic, so you need to figure out what extensions to interface with the current system are needed // This gets a little weird, Vulkan is platform agnostic, so you need to
// So, to figure out what extension codes and how many to use, feed the pointer into *glfwGetRequiredInstanceExtensions*, which will get the necessary extensions! // figure out what extensions to interface with the current system are needed
// From there, we can send that over to our createInfo Vulkan info struct to make it fully platform agnostic! // So, to figure out what extension codes and how many to use, feed the
// pointer into *glfwGetRequiredInstanceExtensions*, which will get the
// necessary extensions! From there, we can send that over to our createInfo
// Vulkan info struct to make it fully platform agnostic!
uint32_t glfwExtensionCount = 0; uint32_t glfwExtensionCount = 0;
const char **glfwExtensions; const char **glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); std::vector<const char *> extensions(glfwExtensions,
glfwExtensions + glfwExtensionCount);
VkInstanceCreateInfo createInfo{}; // Define parameters of new vulkan instance VkInstanceCreateInfo createInfo{}; // Define parameters of new vulkan instance
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; // Tell vulkan this is a info structure createInfo.sType =
createInfo.pApplicationInfo = &appInfo; // We just created a new appInfo structure, so we pass the pointer to it. VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; // Tell vulkan this is a info
// structure
createInfo.pApplicationInfo =
&appInfo; // We just created a new appInfo structure, so we pass the
// pointer to it.
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data(); createInfo.ppEnabledExtensionNames = extensions.data();
if (vkCreateInstance(&createInfo, nullptr, &vulkaninstance) != VK_SUCCESS) { if (vkCreateInstance(&createInfo, nullptr, &vulkaninstance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!"); throw std::runtime_error("failed to create instance!");
} }
} }
void initVulkan() { void initVulkan() {
@ -94,12 +109,12 @@ void mainLoop() {
void cleanup() { void cleanup() {
render_present::Render::cleanupSwapChain(); render_present::Render::cleanupSwapChain();
graphics_pipeline::Graphics::destroyGraphicsPipeline(); graphics_pipeline::Graphics::destroyGraphicsPipeline();
//graphics_pipeline::Graphics::destroyRenderPass();
buffers_libs::Buffers::destroyUniformBuffer(); buffers_libs::Buffers::destroyUniformBuffer();
buffers_libs::Buffers::destroyDescriptorPool(); buffers_libs::Buffers::destroyDescriptorPool();
texture_libs::Texture::destroyTextureSampler(); texture_libs::Texture::destroyTextureSampler();
texture_libs::Texture::destroyTextureImage(); texture_libs::Texture::destroyTextureImage();
vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout,
nullptr);
buffers_libs::Buffers::destroyBuffers(); buffers_libs::Buffers::destroyBuffers();
render_present::Render::destroyFenceSemaphores(); render_present::Render::destroyFenceSemaphores();
graphics_pipeline::Graphics::destroyCommandPool(); graphics_pipeline::Graphics::destroyCommandPool();
@ -118,12 +133,8 @@ EntryApp& EntryApp::getInstance() {
} }
EntryApp::EntryApp() : initialized(false), framebufferResized(false) {} EntryApp::EntryApp() : initialized(false), framebufferResized(false) {}
void EntryApp::initialize() { void EntryApp::initialize() { initialized = true; }
initialized = true; bool EntryApp::isInitialized() const { return initialized; }
}
bool EntryApp::isInitialized() const {
return initialized;
}
void EntryApp::run() { void EntryApp::run() {
initWindow(); initWindow();
@ -131,4 +142,3 @@ void EntryApp::run() {
mainLoop(); mainLoop();
cleanup(); cleanup();
} }

View File

@ -26,18 +26,23 @@ namespace Global {
std::vector<uint32_t> indices; std::vector<uint32_t> indices;
Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
// First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. // First we feed in a integer we want to use to hold the number of queued
// These store the flags, the amount of queued items in the family, and timestamp data. Queue families are simply group collections of tasks we want to get done. // items, that fills it, then we create that amount of default constructed
// Next, we check the flags of the queueFamily item, use a bitwise and to see if they match, i.e. support graphical operations, then return that to notify that we have at least one family that supports VK_QUEUE_GRAPHICS_BIT. // *VkQueueFamilyProperties* structs. These store the flags, the amount of
// Which means this device supports graphical operations! // queued items in the family, and timestamp data. Queue families are simply
// We also do the same thing for window presentation, just check to see if its supported. // group collections of tasks we want to get done. Next, we check the flags of
// the queueFamily item, use a bitwise and to see if they match, i.e. support
// graphical operations, then return that to notify that we have at least one
// family that supports VK_QUEUE_GRAPHICS_BIT. Which means this device
// supports graphical operations! We also do the same thing for window
// presentation, just check to see if its supported.
Global::QueueFamilyIndices indices; Global::QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0; uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
queueFamilies.data());
int i = 0; int i = 0;
for (const auto &queueFamily : queueFamilies) { for (const auto &queueFamily : queueFamilies) {
@ -46,7 +51,8 @@ namespace Global {
} }
VkBool32 presentSupport = false; VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, Global::surface, &presentSupport); vkGetPhysicalDeviceSurfaceSupportKHR(device, i, Global::surface,
&presentSupport);
if (presentSupport) { if (presentSupport) {
indices.presentFamily = i; indices.presentFamily = i;
} }
@ -58,4 +64,4 @@ namespace Global {
} }
return indices; return indices;
} }
} } // namespace Global

View File

@ -261,27 +261,35 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer,
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!"); throw std::runtime_error("failed to begin recording command buffer!");
} }
const VkImageMemoryBarrier2 imageMemoryBarrier{
const VkImageMemoryBarrier imageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = 0,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = device_libs::DeviceControl::getSwapChainImages()[imageIndex], .image = device_libs::DeviceControl::getSwapChainImages()[imageIndex],
.subresourceRange = { .subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0, .baseMipLevel = 0,
.levelCount = texture_libs::Texture::getMipLevels(), .levelCount = 1,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1, .layerCount = 1,
}}; },
};
const VkDependencyInfo dependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageMemoryBarrier,
};
vkCmdPipelineBarrier2(commandBuffer, &dependencyInfo);
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0,
nullptr, 1, &imageMemoryBarrier
);
// ------------------- DYNAMIC RENDER INFO ---------------------- // // ------------------- DYNAMIC RENDER INFO ---------------------- //
const VkRenderingAttachmentInfo colorAttachmentInfo{ const VkRenderingAttachmentInfo colorAttachmentInfo{
@ -347,6 +355,36 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer,
vkCmdEndRendering(commandBuffer); vkCmdEndRendering(commandBuffer);
const VkImageMemoryBarrier2 prePresentImageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = device_libs::DeviceControl::getSwapChainImages()[imageIndex],
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
const VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pNext = nullptr,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &prePresentImageBarrier,
};
vkCmdPipelineBarrier2(Global::commandBuffers[Global::currentFrame], &depInfo);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!"); throw std::runtime_error("failed to record command buffer!");
} }

View File

@ -1,8 +1,9 @@
#include "render.h"
#include "graphicspipeline.h"
#include "../devicelibrary.h" #include "../devicelibrary.h"
#include "../entrypoint.h" #include "../entrypoint.h"
#include "graphicspipeline.h"
#include "render.h"
#include "texture.h" #include "texture.h"
#include <vulkan/vulkan_core.h>
namespace render_present { namespace render_present {
std::vector<VkSemaphore> imageAvailableSemaphores; std::vector<VkSemaphore> imageAvailableSemaphores;
@ -17,7 +18,8 @@ namespace render_present {
glfwWaitEvents(); glfwWaitEvents();
} }
vkDeviceWaitIdle(Global::device); vkDeviceWaitIdle(Global::device);
// Don't really wanna do this but I also don't want to create an extra class instance just to call the cleanup function. // Don't really wanna do this but I also don't want to create an extra class
// instance just to call the cleanup function.
for (auto imageView : Global::swapChainImageViews) { for (auto imageView : Global::swapChainImageViews) {
vkDestroyImageView(Global::device, imageView, nullptr); vkDestroyImageView(Global::device, imageView, nullptr);
@ -33,12 +35,15 @@ namespace render_present {
// record a comman d buffer which draws the scene onto that image // record a comman d buffer which draws the scene onto that image
// submit the recorded command buffer and present the image! // submit the recorded command buffer and present the image!
void Render::drawFrame() { void Render::drawFrame() {
vkWaitForFences(Global::device, 1, &inFlightFences[Global::currentFrame],
vkWaitForFences(Global::device, 1, &inFlightFences[Global::currentFrame], VK_TRUE, UINT64_MAX); VK_TRUE, UINT64_MAX);
vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]); vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]);
uint32_t imageIndex; uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(Global::device, Global::swapChain, UINT64_MAX, imageAvailableSemaphores[Global::currentFrame], VK_NULL_HANDLE, &imageIndex); VkResult result =
vkAcquireNextImageKHR(Global::device, Global::swapChain, UINT64_MAX,
imageAvailableSemaphores[Global::currentFrame],
VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) { if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapChain(); recreateSwapChain();
return; return;
@ -50,26 +55,31 @@ namespace render_present {
vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]); vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]);
vkResetCommandBuffer(Global::commandBuffers[Global::currentFrame], /*VkCommandBufferResetFlagBits*/ 0); vkResetCommandBuffer(Global::commandBuffers[Global::currentFrame],
graphics_pipeline::Graphics::recordCommandBuffer(Global::commandBuffers[Global::currentFrame], imageIndex); /*VkCommandBufferResetFlagBits*/ 0);
graphics_pipeline::Graphics::recordCommandBuffer(
Global::commandBuffers[Global::currentFrame], imageIndex);
VkSubmitInfo submitInfo{}; VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[Global::currentFrame]}; VkSemaphore waitSemaphores[] = {
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; imageAvailableSemaphores[Global::currentFrame]};
VkPipelineStageFlags waitStages[] = {
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfo.waitSemaphoreCount = 1; submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages; submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &Global::commandBuffers[Global::currentFrame]; submitInfo.pCommandBuffers = &Global::commandBuffers[Global::currentFrame];
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[Global::currentFrame]}; VkSemaphore signalSemaphores[] = {
renderFinishedSemaphores[Global::currentFrame]};
submitInfo.signalSemaphoreCount = 1; submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores; submitInfo.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, inFlightFences[Global::currentFrame]) != VK_SUCCESS) { if (vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo,
inFlightFences[Global::currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!"); throw std::runtime_error("failed to submit draw command buffer!");
} }
@ -82,35 +92,36 @@ namespace render_present {
VkSwapchainKHR swapChains[] = {Global::swapChain}; VkSwapchainKHR swapChains[] = {Global::swapChain};
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains; presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex; presentInfo.pImageIndices = &imageIndex;
result = vkQueuePresentKHR(Global::presentQueue, &presentInfo); result = vkQueuePresentKHR(Global::presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || EntryApp::getInstance().getFramebufferResized()) {
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR ||
EntryApp::getInstance().getFramebufferResized()) {
EntryApp::getInstance().setFramebufferResized(false); EntryApp::getInstance().setFramebufferResized(false);
recreateSwapChain(); recreateSwapChain();
} else if (result != VK_SUCCESS) { } else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!"); throw std::runtime_error("failed to present swap chain image!");
} }
Global::currentFrame = (Global::currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT; Global::currentFrame =
(Global::currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT;
} }
#pragma info #pragma info
// SEMAPHORES // SEMAPHORES
// Synchronization of execution on the GPU in Vulkan is *explicit* The Order of ops is up to us to // Synchronization of execution on the GPU in Vulkan is *explicit* The Order of
// define the how we want things to run. // ops is up to us to define the how we want things to run. Similarly,
// Similarly, Semaphores are used to add order between queue ops. There are 2 kinds of Semaphores; binary, and timeline. // Semaphores are used to add order between queue ops. There are 2 kinds of
// We are using Binary semaphores, which can be signaled or unsignaled. // Semaphores; binary, and timeline. We are using Binary semaphores, which can
// Semaphores are initizalized unsignaled, the way we use them to order queue operations is by providing the same semaphore in one queue op and a wait in another. // be signaled or unsignaled. Semaphores are initizalized unsignaled, the way we
// For example: // use them to order queue operations is by providing the same semaphore in one
// VkCommandBuffer QueueOne, QueueTwo = ... // queue op and a wait in another. For example: VkCommandBuffer QueueOne,
// VkSemaphore semaphore = ... // QueueTwo = ... VkSemaphore semaphore = ... enqueue QueueOne, Signal semaphore
// enqueue QueueOne, Signal semaphore when done, start now. // when done, start now. vkQueueSubmit(work: QueueOne, signal: semaphore, wait:
// vkQueueSubmit(work: QueueOne, signal: semaphore, wait: none) // none) enqueue QueueTwo, wait on semaphore to start vkQueueSubmit(
// enqueue QueueTwo, wait on semaphore to start
// vkQueueSubmit(
// work: QueueTwo, signal: None, wait: semaphore) // work: QueueTwo, signal: None, wait: semaphore)
// FENCES // FENCES
// Fences are basically semaphores for the CPU! Otherwise known as the host. If the host needs to know when the GPU has finished a task, we use a fence. // Fences are basically semaphores for the CPU! Otherwise known as the host. If
// the host needs to know when the GPU has finished a task, we use a fence.
// VkCommandBuffer cmndBuf = ... // VkCommandBuffer cmndBuf = ...
// VkFence fence = ... // VkFence fence = ...
// Start work immediately, signal fence when done. // Start work immediately, signal fence when done.
@ -132,14 +143,15 @@ namespace render_present {
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
if(vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || if (vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr,
vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || &imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateFence(Global::device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr,
&renderFinishedSemaphores[i]) != VK_SUCCESS ||
vkCreateFence(Global::device, &fenceInfo, nullptr,
&inFlightFences[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to create semaphores!"); throw std::runtime_error("Failed to create semaphores!");
} }
} }
} }
void Render::destroyFenceSemaphores() { void Render::destroyFenceSemaphores() {
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
@ -159,4 +171,4 @@ namespace render_present {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr); vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
} }
} } // namespace render_present