From fa14b3fd8fd3c4c5a08fd02e25103a0c974b6006 Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Mon, 7 Oct 2024 05:09:46 -0500 Subject: [PATCH] Implemented image view system for.. viewing images --- Makefile | 13 ++- README.md | 2 +- ...ulkanDebugLibs.cpp => vulkandebuglibs.cpp} | 11 +-- .../{VulkanDebugLibs.h => vulkandebuglibs.h} | 2 +- src/{DeviceLibrary.cpp => devicelibrary.cpp} | 84 ++++++++++++++----- src/{DeviceLibrary.h => devicelibrary.h} | 4 +- src/global.h | 2 +- src/main.cpp | 19 +++-- 8 files changed, 93 insertions(+), 44 deletions(-) rename src/debug/{VulkanDebugLibs.cpp => vulkandebuglibs.cpp} (95%) rename src/debug/{VulkanDebugLibs.h => vulkandebuglibs.h} (94%) rename src/{DeviceLibrary.cpp => devicelibrary.cpp} (81%) rename src/{DeviceLibrary.h => devicelibrary.h} (77%) diff --git a/Makefile b/Makefile index 08e8e73..c1fd1df 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ CPPFLAGS=-g LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi -DEBUGFLAGS=-DDEBUG -fsanitize=address +DEBUGFLAGS=-DDEBUG -fsanitize=address +GDBFLAGS= SRC=$(shell find . -name *.cpp) OBJ=$(SRC:%.cpp=%.o) @@ -13,13 +14,19 @@ all: $(BIN) .PHONY: run run: $(BIN) ./$(BIN) + +.PHONY: gdb +gdb: LDFLAGS+=$(GDBFLAGS) +gdb: $(BIN) + gdb -q $(BIN) .PHONY: debug debug: LDFLAGS+=$(DEBUGFLAGS) debug: $(BIN) ./$(BIN) + .PHONY: dep dep: - sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm + sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb .PHONY: info info: @echo "make: Build executable" @@ -33,7 +40,7 @@ $(BIN): $(OBJ) g++ $(CPPFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) %.o: %.cpp - g++ -c $< -o $@ $(LDFLAGS) + g++ -c -g $< -o $@ $(LDFLAGS) .PHONY: clean clean: diff --git a/README.md b/README.md index f0ac7e2..d381d96 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,5 @@ - Implement features for maximum fidelity/performance ratio - Implement custom Raytracing and Raymarching protocols - Mess with cutting edge design, VDB's, 3D Guassian Splatting, -- 1000000+ FPS on Potato GPUs /s +- 1000000+ FPS on Potato GPUs diff --git a/src/debug/VulkanDebugLibs.cpp b/src/debug/vulkandebuglibs.cpp similarity index 95% rename from src/debug/VulkanDebugLibs.cpp rename to src/debug/vulkandebuglibs.cpp index d3ba2c6..f13d4bc 100644 --- a/src/debug/VulkanDebugLibs.cpp +++ b/src/debug/vulkandebuglibs.cpp @@ -1,3 +1,4 @@ +#include "vulkandebuglibs.h" #include "../global.h" using namespace Debug; @@ -53,7 +54,7 @@ void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& create createInfo.pUserData = nullptr; // Optional } -void VulkanDebugLibs::vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInstance& instance) { +void vulkandebuglibs::vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInstance& instance) { // This function is quite useful, we first populate the debug create info structure, all the parameters dictating how the debug messenger will operate. // The reason we populate the debug messenger so late is actually on purpose, we need to set the createInfo, which depends on the debugMessenger info, // and if we set it before the creation of the instance, we cant debug vkCreateInstance or vkDestroyInstance! It's timed perfectly as of now. @@ -79,14 +80,14 @@ void VulkanDebugLibs::vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInsta } } -void VulkanDebugLibs::checkUnavailableValidationLayers() { +void vulkandebuglibs::checkUnavailableValidationLayers() { // Check if we are trying to hook validation layers in without support. if(Global::enableValidationLayers && !checkValidationLayerSupport()) { throw std::runtime_error("Validation layers request, but not available! Are your SDK path variables set?"); } } -bool VulkanDebugLibs::checkValidationLayerSupport() { +bool vulkandebuglibs::checkValidationLayerSupport() { // This function is used to check Validation Layer Support, validation layers are the debug trace tools in the Vulkan SDK. // layerCount will be used as the var to keep track of the number of requested validation layer // VkLayerProperties is a structure with data on the layername, desc, versions and etc. @@ -131,7 +132,7 @@ VkResult CreateDebugUtilsMessengerEXT( } } -void VulkanDebugLibs::DestroyDebugUtilsMessengerEXT(VkInstance instance, +void vulkandebuglibs::DestroyDebugUtilsMessengerEXT(VkInstance instance, const VkAllocationCallbacks* pAllocator) { // We are doing kind of the same thing as before in the create function, find the address of the DestroyDebugUtils function, and call it. @@ -141,7 +142,7 @@ void VulkanDebugLibs::DestroyDebugUtilsMessengerEXT(VkInstance instance, } } -void VulkanDebugLibs::setupDebugMessenger(VkInstance& vulkanInstance) { +void vulkandebuglibs::setupDebugMessenger(VkInstance& vulkanInstance) { // This is a pretty simple function! we just pass in the values to build the debug messenger, populate the structure with the data we want, // and safely create it, covering for runtime errors as per usual, this is the first thing that will be called! if(!Global::enableValidationLayers) return; diff --git a/src/debug/VulkanDebugLibs.h b/src/debug/vulkandebuglibs.h similarity index 94% rename from src/debug/VulkanDebugLibs.h rename to src/debug/vulkandebuglibs.h index 9b854dc..225dc89 100644 --- a/src/debug/VulkanDebugLibs.h +++ b/src/debug/vulkandebuglibs.h @@ -3,7 +3,7 @@ #include namespace Debug { - class VulkanDebugLibs { + class vulkandebuglibs { public: void vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInstance& instance); diff --git a/src/DeviceLibrary.cpp b/src/devicelibrary.cpp similarity index 81% rename from src/DeviceLibrary.cpp rename to src/devicelibrary.cpp index ffb562a..4e7d9b8 100644 --- a/src/DeviceLibrary.cpp +++ b/src/devicelibrary.cpp @@ -1,4 +1,4 @@ -#include "DeviceLibrary.h" +#include "devicelibrary.h" #include "global.h" #include @@ -10,6 +10,7 @@ #include #include #include +#include namespace DeviceControl { @@ -23,6 +24,7 @@ namespace DeviceControl { std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; + std::vector swapChainImageViews; VkQueue graphicsQueue; VkQueue presentQueue; @@ -142,7 +144,6 @@ namespace DeviceControl { return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.multiViewport - && deviceFeatures.geometryShader && indices.isComplete() && extensionSupported && swapChainAdequate; @@ -166,9 +167,12 @@ namespace DeviceControl { // This is most similarly to standard V-Sync. for(const auto& availablePresentMode : availablePresentModes) { if(availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + if(Global::enableValidationLayers) std::cout << "Using Triple Buffering\n" << std::endl; return availablePresentMode; } } + + if(Global::enableValidationLayers) std::cout << "Using FIFO (V-Sync)\n" << std::endl; return VK_PRESENT_MODE_FIFO_KHR; } VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) { @@ -179,22 +183,22 @@ namespace DeviceControl { if (capabilities.currentExtent.width != std::numeric_limits::max()) { return capabilities.currentExtent; } else { - int width, height; - glfwGetFramebufferSize(window, &width, &height); + int width, height; + glfwGetFramebufferSize(window, &width, &height); - VkExtent2D actualExtent = { - static_cast(width), - static_cast(height) - }; + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; // Clamp the image size to the minimum extent values specified by vulkan for our window manager. - actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); - return actualExtent; + return actualExtent; } } // --------------------------------------- External Functions -----------------------------------------// - void DeviceLibrary::pickPhysicalDevice(VkInstance& instance) { + void devicelibrary::pickPhysicalDevice(VkInstance& instance) { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); @@ -206,7 +210,7 @@ namespace DeviceControl { for(const auto& device : devices) { if(isDeviceSuitable(device)) { - std::cout << "Using device: " << deviceProperties.deviceName << std::endl; + if(Global::enableValidationLayers) std::cout << "Using device: " << deviceProperties.deviceName << std::endl; //Once we have buttons or such, maybe ask the user or write a config file for which GPU to use? physicalDevice = device; break; @@ -216,17 +220,17 @@ namespace DeviceControl { throw std::runtime_error("Failed to find a suitable GPU!"); } } - void DeviceLibrary::destroySurface(VkInstance& instance) { + void devicelibrary::destroySurface(VkInstance& instance) { vkDestroySurfaceKHR(instance, surface, nullptr); - std::cout << "Destroyed surface safely\n" << std::endl; + if(Global::enableValidationLayers) std::cout << "Destroyed surface safely\n" << std::endl; } - void DeviceLibrary::createSurface(VkInstance& instance, GLFWwindow* window) { + void devicelibrary::createSurface(VkInstance& instance, GLFWwindow* window) { if(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("Failed to create window surface!!"); } - std::cout << "GLFW Window Surface created successfully\n" << std::endl; + if(Global::enableValidationLayers) std::cout << "GLFW Window Surface created successfully\n" << std::endl; } - void DeviceLibrary::createLogicalDevice(VkDevice& device) { + void devicelibrary::createLogicalDevice(VkDevice& device) { // Describe how many queues we want for a single family (1) here, right now we are solely interested in graphics capabilites, // but Compute Shaders, transfer ops, decode and encode operations can also queued with setup! We 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! @@ -264,12 +268,12 @@ namespace DeviceControl { if(vkCreateDevice(physicalDevice, &createDeviceInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("Failed to create logical device"); } - std::cout << "Created Logical device successfully!\n" << std::endl; + if(Global::enableValidationLayers) std::cout << "Created Logical device successfully!\n" << std::endl; vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); } - void DeviceLibrary::createSwapChain(GLFWwindow* window, VkDevice& device) { + void devicelibrary::createSwapChain(GLFWwindow* window, VkDevice& device) { SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); @@ -326,7 +330,7 @@ namespace DeviceControl { if(vkCreateSwapchainKHR(device, &createSwapChainInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("Failed to create the swap chain!!"); } - std::cout << "Swap Chain created successfully\n" << std::endl; + if(Global::enableValidationLayers) std::cout << "Swap Chain created successfully\n" << std::endl; vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); @@ -335,8 +339,42 @@ namespace DeviceControl { swapChainImageFormat = surfaceFormat.format; swapChainExtent = extent; } - void DeviceLibrary::destroySwapChain(VkDevice& device) { + void devicelibrary::destroySwapChain(VkDevice& device) { vkDestroySwapchainKHR(device, swapChain, nullptr); - std::cout << "Destroyed Swap Chain safely\n" << std::endl; + if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl; + } + void devicelibrary::createImageViews(VkDevice& device) { + swapChainImageViews.resize(swapChainImages.size()); + for(size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createImageViewInfo{}; + createImageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createImageViewInfo.image = swapChainImages[i]; + // Are we treating images as 1D, 2D or 3D? + createImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createImageViewInfo.format = swapChainImageFormat; + // Allow us to swizzle color channels + createImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createImageViewInfo.subresourceRange.baseMipLevel = 0; + createImageViewInfo.subresourceRange.levelCount = 1; + createImageViewInfo.subresourceRange.baseArrayLayer = 0; + // Yet another setting we would increase for VR applications, and specifically create a swap chain with more layers as well. The other layers would be the eye outputs. + createImageViewInfo.subresourceRange.layerCount = 1; + + if(vkCreateImageView(device, &createImageViewInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create image views!"); + } + if(Global::enableValidationLayers) std::cout << "Image views created successfully\n" << std::endl; + } + } + void devicelibrary::destroyImageViews(VkDevice& device) { + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + if(Global::enableValidationLayers) std::cout << "Image destroyed safely\n" << std::endl; } } diff --git a/src/DeviceLibrary.h b/src/devicelibrary.h similarity index 77% rename from src/DeviceLibrary.h rename to src/devicelibrary.h index 898e79c..13029af 100644 --- a/src/DeviceLibrary.h +++ b/src/devicelibrary.h @@ -1,7 +1,7 @@ #pragma once #include "global.h" namespace DeviceControl { - class DeviceLibrary { + class devicelibrary { public: void pickPhysicalDevice(VkInstance& instance); @@ -10,6 +10,8 @@ namespace DeviceControl { void destroySurface(VkInstance& instance); void createSwapChain(GLFWwindow* window, VkDevice& device); void destroySwapChain(VkDevice& device); + void createImageViews(VkDevice& device); + void destroyImageViews(VkDevice& device); }; } diff --git a/src/global.h b/src/global.h index 7fd8fef..69727da 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,5 @@ #pragma once -#include "debug/VulkanDebugLibs.h" +#include "debug/vulkandebuglibs.h" #include #include #include diff --git a/src/main.cpp b/src/main.cpp index 258be34..98badef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ - -#include "DeviceLibrary.h" // Device Library includes global, redundant to include with it here -#include "debug/VulkanDebugLibs.h" +#include "devicelibrary.h" // Device Library includes global, redundant to include with it here +#include "debug/vulkandebuglibs.h" #include #include @@ -22,8 +21,8 @@ public: } private: - DeviceControl::DeviceLibrary deviceLibs; - Debug::VulkanDebugLibs debugController; + DeviceControl::devicelibrary deviceLibs; + Debug::vulkandebuglibs debugController; GLFWwindow* window; VkInstance instance; @@ -42,15 +41,16 @@ private: void initVulkan() { createInstance(); - debugController.setupDebugMessenger(instance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DNDEBUG flag (as per the makefile) + debugController.setupDebugMessenger(instance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DNDEBUG flag (as per the makefile) deviceLibs.createSurface(instance, window); deviceLibs.pickPhysicalDevice(instance); deviceLibs.createLogicalDevice(device); deviceLibs.createSwapChain(window, device); + deviceLibs.createImageViews(device); } void createInstance() { - debugController.checkUnavailableValidationLayers(); // Check if there is a mistake with our Validation Layers. + debugController.checkUnavailableValidationLayers(); // Check if there is a mistake with our Validation Layers. // Set application info for the vulkan instance! VkApplicationInfo appInfo{}; @@ -66,7 +66,7 @@ private: createInfo.sType = 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. - debugController.vulkanDebugSetup(createInfo, instance); // Handoff to the debug library to wrap the validation libs in! (And set the window up!) + debugController.vulkanDebugSetup(createInfo, instance); // Handoff to the debug library to wrap the validation libs in! (And set the window up!) } void mainLoop() { // This loop just updates the GLFW window. @@ -75,7 +75,8 @@ private: } } - void cleanup() { // Similar to the last handoff, destroy the debug util in a safe manner in the library! + void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library! + deviceLibs.destroyImageViews(device); deviceLibs.destroySwapChain(device); vkDestroyDevice(device, nullptr); if(Global::enableValidationLayers) {