clean up a RIDICULOUS amount of shit code lmfao, instantiation < static calls (im dumb as hell)

This commit is contained in:
Lillian Salehi 2024-10-16 06:02:15 -05:00
parent 56fb496ec7
commit af7b533f50
18 changed files with 341 additions and 382 deletions

View File

@ -1,27 +1,26 @@
#include "vulkandebuglibs.h"
using namespace Debug;
// This is our messenger object! It handles passing along debug messages to the debug callback we will also set.
VkDebugUtilsMessengerEXT debugMessenger;
// This is the set of "layers" to hook into. Basically, layers are used to tell the messenger what data we want, its a filter. *validation* is the general blanket layer to cover incorrect usage.
namespace debug_libs {
std::vector<const char*> getRequiredExtensions() {
// 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
// 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;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> getRequiredExtensions() {
// 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
// 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;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if(Global::enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
if(Global::enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
return extensions;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
@ -33,119 +32,119 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
std::cout << "\n";
return VK_FALSE;
}
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
// There is absolutely nothing about this i like, those long ass flags for messageType and Severity are just fucking hex values. Khronos should never cook again ToT
// On a serious note, this is just a struct to define the parameters of the debug messenger, nothing super special.
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
createInfo.pUserData = nullptr; // Optional
}
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.
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
if(Global::enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(Global::validationLayers.size());
createInfo.ppEnabledLayerNames = Global::validationLayers.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
// There is absolutely nothing about this i like, those long ass flags for messageType and Severity are just fucking hex values. Khronos should never cook again ToT
// On a serious note, this is just a struct to define the parameters of the debug messenger, nothing super special.
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = debugCallback;
createInfo.pUserData = nullptr; // Optional
}
}
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?");
void Debug::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.
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
if(Global::enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(Global::validationLayers.size());
createInfo.ppEnabledLayerNames = Global::validationLayers.data();
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
}
}
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.
void Debug::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?");
}
}
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
bool Debug::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.
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
for(const char* layerName : Global::validationLayers) {
bool layerFound = false;
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for(const auto& layerProperties : availableLayers) {
if(strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
for(const char* layerName : Global::validationLayers) {
bool layerFound = false;
for(const auto& layerProperties : availableLayers) {
if(strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
}
if(!layerFound) {
return false;
}
}
if(!layerFound) {
return false;
}
return true;
}
return true;
}
VkResult CreateDebugUtilsMessengerEXT(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pDebugMessenger) {
// This function builds out debug messenger structure!
// It's a little odd, we have to look up the address of the vkCreateDebugUtilsMessengerEXT ourselves because its an extension function,
// therefore, not auto-loaded.
VkResult CreateDebugUtilsMessengerEXT(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pDebugMessenger) {
// This function builds out debug messenger structure!
// It's a little odd, we have to look up the address of the vkCreateDebugUtilsMessengerEXT ourselves because its an extension function,
// therefore, not auto-loaded.
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
} else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
}
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.
void Debug::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.
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if(func != nullptr) {
func(instance, debugMessenger, pAllocator);
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if(func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
void Debug::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;
VkDebugUtilsMessengerCreateInfoEXT createInfo;
populateDebugMessengerCreateInfo(createInfo);
if(CreateDebugUtilsMessengerEXT(vulkanInstance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("Failed to set up the Debug Messenger!");
}
}
}
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;
VkDebugUtilsMessengerCreateInfoEXT createInfo;
populateDebugMessengerCreateInfo(createInfo);
if(CreateDebugUtilsMessengerEXT(vulkanInstance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("Failed to set up the Debug Messenger!");
}
}

View File

@ -1,17 +1,14 @@
#pragma once
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <cstring>
#include "../global.h"
namespace Debug {
class vulkandebuglibs {
namespace debug_libs {
class Debug {
public:
void vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInstance& instance);
bool checkValidationLayerSupport();
void checkUnavailableValidationLayers();
void setupDebugMessenger(VkInstance& vulkanInstance);
void DestroyDebugUtilsMessengerEXT(VkInstance instance, const VkAllocationCallbacks* pAllocator);
static void vulkanDebugSetup(VkInstanceCreateInfo& createInfo, VkInstance& instance);
static bool checkValidationLayerSupport();
static void checkUnavailableValidationLayers();
static void setupDebugMessenger(VkInstance& vulkanInstance);
static void DestroyDebugUtilsMessengerEXT(VkInstance instance, const VkAllocationCallbacks* pAllocator);
};
}

View File

@ -1,10 +1,6 @@
#include "devicelibrary.h"
#include "global.h"
#include <vulkan/vulkan_core.h>
namespace DeviceControl {
namespace device_libs {
VkPhysicalDeviceProperties deviceProperties;
@ -13,8 +9,6 @@ namespace DeviceControl {
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
@ -143,7 +137,7 @@ namespace DeviceControl {
}
}
// --------------------------------------- External Functions ----------------------------------------- //
void devicelibrary::pickPhysicalDevice(VkInstance& instance) {
void DeviceControl::pickPhysicalDevice(VkInstance& instance) {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
@ -165,17 +159,17 @@ namespace DeviceControl {
throw std::runtime_error("Failed to find a suitable GPU!");
}
}
void devicelibrary::destroySurface(VkInstance& instance) {
void DeviceControl::destroySurface(VkInstance& instance) {
vkDestroySurfaceKHR(instance, Global::surface, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed surface safely\n" << std::endl;
}
void devicelibrary::createSurface(VkInstance& instance, GLFWwindow* window) {
void DeviceControl::createSurface(VkInstance& instance, GLFWwindow* window) {
if(glfwCreateWindowSurface(instance, window, nullptr, &Global::surface) != VK_SUCCESS) {
throw std::runtime_error("Failed to create window surface!!");
}
if(Global::enableValidationLayers) std::cout << "GLFW Window Surface created successfully\n" << std::endl;
}
void devicelibrary::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,
// 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!
@ -221,7 +215,7 @@ namespace DeviceControl {
vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0, &Global::graphicsQueue);
vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0, &Global::presentQueue);
}
void devicelibrary::createSwapChain(GLFWwindow* window) {
void DeviceControl::createSwapChain(GLFWwindow* window) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(Global::physicalDevice);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
@ -287,11 +281,11 @@ namespace DeviceControl {
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;
}
void devicelibrary::destroySwapChain() {
void DeviceControl::destroySwapChain() {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl;
}
VkImageView devicelibrary::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags) {
VkImageView DeviceControl::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags) {
// This defines the parameters of a newly created image object!
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@ -311,28 +305,27 @@ namespace DeviceControl {
return imageView;
}
void devicelibrary::createImageViews() {
void DeviceControl::createImageViews() {
swapChainImageViews.resize(swapChainImages.size());
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
}
}
void devicelibrary::destroyImageViews() {
void DeviceControl::destroyImageViews() {
for (auto imageView : swapChainImageViews) {
vkDestroyImageView(Global::device, imageView, nullptr);
}
if(Global::enableValidationLayers) std::cout << "Image destroyed safely\n" << std::endl;
}
// --------------------------------------- Getters & Setters ------------------------------------------ //
VkFormat devicelibrary::getImageFormat() {
VkFormat DeviceControl::getImageFormat() {
return swapChainImageFormat;
}
std::vector<VkImageView> devicelibrary::getSwapChainImageViews() {
std::vector<VkImageView> DeviceControl::getSwapChainImageViews() {
return swapChainImageViews;
}
VkExtent2D devicelibrary::getSwapChainExtent() {
VkExtent2D DeviceControl::getSwapChainExtent() {
return swapChainExtent;
}
}

View File

@ -1,33 +1,30 @@
#pragma once
#include "global.h"
#include <GLFW/glfw3.h>
#include <optional>
#include <algorithm>
#include <limits>
#include <ostream>
#include <set>
#include <string>
#include <vector>
namespace DeviceControl {
class devicelibrary {
namespace device_libs {
class DeviceControl {
public:
void pickPhysicalDevice(VkInstance& instance);
void createLogicalDevice();
void createSurface(VkInstance& instance, GLFWwindow* window);
void destroySurface(VkInstance& instance);
void createSwapChain(GLFWwindow* window);
void destroySwapChain();
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags);
void createImageViews();
void destroyImageViews();
void createCommandPool();
void destroyCommandPool();
static void pickPhysicalDevice(VkInstance& instance);
static void createLogicalDevice();
static void createSurface(VkInstance& instance, GLFWwindow* window);
static void destroySurface(VkInstance& instance);
static void createSwapChain(GLFWwindow* window);
static void destroySwapChain();
static VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags);
static void createImageViews();
static void destroyImageViews();
static void createCommandPool();
static void destroyCommandPool();
// ---------- Getters & Setters ----------- //
VkFormat getImageFormat();
std::vector<VkImageView> getSwapChainImageViews();
VkExtent2D getSwapChainExtent();
std::vector<VkFramebuffer> getSwapChainFramebuffers();
static VkFormat getImageFormat();
static std::vector<VkImageView> getSwapChainImageViews();
static VkExtent2D getSwapChainExtent();
static std::vector<VkFramebuffer> getSwapChainFramebuffers();
};
}

View File

@ -1,14 +1,6 @@
#include "entrypoint.h"
DeviceControl::devicelibrary deviceLibs;
Debug::vulkandebuglibs debugController;
Graphics::graphicspipeline graphicsPipeline;
RenderPresent::render renderPresentation;
BuffersLibraries::buffers buffers;
TextureLibraries::texture texture;
ModelLib::model model;
VkInstance vulkaninstance;
//TODO: add global instances?
VkInstance vulkaninstance;
// Getters and Setters!
void EntryApp::setFramebufferResized(bool setter) {
@ -21,8 +13,9 @@ static void framebufferResizeCallback(GLFWwindow* window, int width, int height)
auto app = reinterpret_cast<EntryApp*>(glfwGetWindowUserPointer(window));
app->setFramebufferResized(true);
}
// Initialize GLFW Window. First, Initialize GLFW lib, disable resizing for
// now, and create window.
// Initialize GLFW Window. First, Initialize GLFW lib, disable resizing for
// now, and create window.
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
@ -33,7 +26,7 @@ void initWindow() {
}
void createInstance() {
debugController.checkUnavailableValidationLayers(); // Check if there is a mistake with our Validation Layers.
debug_libs::Debug::checkUnavailableValidationLayers(); // Check if there is a mistake with our Validation Layers.
// Set application info for the vulkan instance!
VkApplicationInfo appInfo{};
@ -49,67 +42,69 @@ void createInstance() {
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, vulkaninstance); // Handoff to the debug library to wrap the validation libs in! (And set the window up!)
debug_libs::Debug::vulkanDebugSetup(createInfo, vulkaninstance); // Handoff to the debug library to wrap the validation libs in! (And set the window up!)
}
void initVulkan() {
// Initialize vulkan and set up pipeline.
createInstance();
debugController.setupDebugMessenger(vulkaninstance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DDEBUG flag (as per the makefile)
deviceLibs.createSurface(vulkaninstance, Global::window);
deviceLibs.pickPhysicalDevice(vulkaninstance);
deviceLibs.createLogicalDevice();
deviceLibs.createSwapChain(Global::window);
deviceLibs.createImageViews();
graphicsPipeline.createRenderPass();
buffers.createDescriptorSetLayout();
graphicsPipeline.createGraphicsPipeline();
graphicsPipeline.createCommandPool();
texture.createDepthResources();
graphicsPipeline.createFramebuffers();
texture.createTextureImage();
texture.createTextureImageView();
texture.createTextureSampler();
model.loadModel();
buffers.createVertexBuffer();
buffers.createIndexBuffer();
buffers.createUniformBuffers();
buffers.createDescriptorPool();
buffers.createDescriptorSets();
graphicsPipeline.createCommandBuffer();
renderPresentation.createSyncObject();
debug_libs::Debug::setupDebugMessenger(vulkaninstance);
device_libs::DeviceControl::createSurface(vulkaninstance, Global::window);
device_libs::DeviceControl::pickPhysicalDevice(vulkaninstance);
device_libs::DeviceControl::createLogicalDevice();
device_libs::DeviceControl::createSwapChain(Global::window);
device_libs::DeviceControl::createImageViews();
graphics_pipeline::Graphics::createRenderPass();
buffers_libs::Buffers::createDescriptorSetLayout();
graphics_pipeline::Graphics::createGraphicsPipeline();
graphics_pipeline::Graphics::createCommandPool();
texture_libs::Texture::createDepthResources();
graphics_pipeline::Graphics::createFramebuffers();
texture_libs::Texture::createTextureImage();
texture_libs::Texture::createTextureImageView();
texture_libs::Texture::createTextureSampler();
modellib::Model::loadModel();
buffers_libs::Buffers::createVertexBuffer();
buffers_libs::Buffers::createIndexBuffer();
buffers_libs::Buffers::createUniformBuffers();
buffers_libs::Buffers::createDescriptorPool();
buffers_libs::Buffers::createDescriptorSets();
graphics_pipeline::Graphics::createCommandBuffer();
render_present::Render::createSyncObject();
}
void mainLoop() { // This loop just updates the GLFW window.
void mainLoop() {
while (!glfwWindowShouldClose(Global::window)) {
glfwPollEvents();
renderPresentation.drawFrame();
render_present::Render::drawFrame();
}
vkDeviceWaitIdle(Global::device);
}
void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library!
renderPresentation.cleanupSwapChain();
texture.createTextureSampler();
texture.destroyTextureImage();
buffers.destroyUniformBuffer();
buffers.destroyDescriptorPool();
void cleanup() {
render_present::Render::cleanupSwapChain();
texture_libs::Texture::createTextureSampler();
texture_libs::Texture::destroyTextureImage();
buffers_libs::Buffers::destroyUniformBuffer();
buffers_libs::Buffers::destroyDescriptorPool();
vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout, nullptr);
graphicsPipeline.destroyGraphicsPipeline();
graphicsPipeline.destroyRenderPass();
graphics_pipeline::Graphics::destroyGraphicsPipeline();
graphics_pipeline::Graphics::destroyRenderPass();
buffers.destroyBuffers();
renderPresentation.destroyFenceSemaphores();
graphicsPipeline.destroyCommandPool();
buffers_libs::Buffers::destroyBuffers();
render_present::Render::destroyFenceSemaphores();
graphics_pipeline::Graphics::destroyCommandPool();
vkDestroyDevice(Global::device, nullptr);
if(Global::enableValidationLayers) {
debugController.DestroyDebugUtilsMessengerEXT(vulkaninstance, nullptr);
debug_libs::Debug::DestroyDebugUtilsMessengerEXT(vulkaninstance, nullptr);
}
deviceLibs.destroySurface(vulkaninstance);
device_libs::DeviceControl::destroySurface(vulkaninstance);
vkDestroyInstance(vulkaninstance, nullptr);
glfwDestroyWindow(Global::window);
glfwTerminate();
}
// External Functions
EntryApp& EntryApp::getInstance() {
static EntryApp instance;

View File

@ -1,11 +1,10 @@
#include <cstdlib>
#include "devicelibrary.h" // Device Library includes global, redundant to include with it here
#include "debug/vulkandebuglibs.h"
#include "graphics/graphicspipeline.h"
#include "graphics/render.h"
#include "graphics/texture.h"
#include "global.h"
#include "graphics/model.h"
#include "graphics/texture.h"
class EntryApp {
public:
static EntryApp& getInstance();

View File

@ -1,6 +1,5 @@
#include "global.h"
#include "devicelibrary.h"
#include <vulkan/vulkan_core.h>
namespace Global {
const std::vector<const char*> validationLayers = {
@ -33,13 +32,13 @@ namespace Global {
std::vector<Vertex> vertices;
// Index buffer definition, showing which points to reuse.
std::vector<uint32_t> indices;
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.
// 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.
// 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.
DeviceControl::devicelibrary deviceLibs;
Global::QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
@ -67,5 +66,4 @@ namespace Global {
}
return indices;
}
}

View File

@ -1,24 +1,27 @@
#pragma once
#include "graphics/texture.h"
#include <cstdint>
#include <vulkan/vulkan_core.h>
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/detail/qualifier.hpp>
#include <glm/ext/vector_float2.hpp>
#include <glm/ext/vector_float3.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/fwd.hpp>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <cstdint>
#include <iostream>
#include <ostream>
#include <vector>
#include <optional>
#include <vulkan/vulkan_core.h>
#include <glm/gtc/matrix_transform.hpp>
#include <array>
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
namespace Global {
// Global variables and includes we are going to use almost everywhere, validation layers hook into everything, and you need to check if they are enabled first,

View File

@ -1,25 +1,17 @@
#include "buffers.h"
#include <chrono>
#include <cstring>
#include "../devicelibrary.h"
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
VkDescriptorPool descriptorPool;
DeviceControl::devicelibrary deviceLibrary;
std::vector<VkBuffer> uniformBuffers;
std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<void*> uniformBuffersMapped;
namespace BuffersLibraries {
uint32_t buffers::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
namespace buffers_libs {
uint32_t Buffers::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
// Graphics cards offer different types of memory to allocate from, here we query to find the right type of memory for our needs.
// Query the available types of memory to iterate over.
VkPhysicalDeviceMemoryProperties memProperties;
@ -66,7 +58,7 @@ namespace BuffersLibraries {
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
void buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
@ -92,7 +84,7 @@ namespace BuffersLibraries {
vkBindBufferMemory(Global::device, buffer, bufferMemory, 0);
}
void buffers::createIndexBuffer() {
void Buffers::createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(Global::indices[0]) * Global::indices.size();
VkBuffer stagingBuffer;
@ -113,7 +105,7 @@ namespace BuffersLibraries {
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void buffers::createVertexBuffer() {
void Buffers::createVertexBuffer() {
// Create a Vertex Buffer to hold the vertex information in memory so it doesn't have to be hardcoded!
// Size denotes the size of the buffer in bytes, usage in this case is the buffer behaviour, using a bitwise OR.
// Sharing mode denostes the same as the images in the swap chain! in this case, only the graphics queue uses this buffer, so we make it exclusive.
@ -136,22 +128,22 @@ namespace BuffersLibraries {
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void buffers::destroyBuffers() {
void Buffers::destroyBuffers() {
vkDestroyBuffer(Global::device, indexBuffer, nullptr);
vkFreeMemory(Global::device, indexBufferMemory, nullptr);
vkDestroyBuffer(Global::device, vertexBuffer, nullptr);
vkFreeMemory(Global::device, vertexBufferMemory, nullptr);
}
VkBuffer buffers::getVertexBuffer() {
VkBuffer Buffers::getVertexBuffer() {
return vertexBuffer;
}
VkBuffer buffers::getIndexBuffer() {
VkBuffer Buffers::getIndexBuffer() {
return indexBuffer;
}
// ------------------------------ Uniform Buffer Setup -------------------------------- //
void buffers::createDescriptorSetLayout() {
void Buffers::createDescriptorSetLayout() {
// Create a table of pointers to data, a Descriptor Set!
// --------------------- UBO Layout --------------------- //
VkDescriptorSetLayoutBinding uboLayoutBinding{};
@ -182,7 +174,7 @@ namespace BuffersLibraries {
throw std::runtime_error("Failed to create descriptor set layout!");
}
}
void buffers::createUniformBuffers() {
void Buffers::createUniformBuffers() {
// Map the uniform buffer to memory as a pointer we can use to write data to later. This stays mapped to memory for the applications lifetime.
// This technique is called "persistent mapping", not having to map the buffer every time we need to update it increases performance, though not free
VkDeviceSize bufferSize = sizeof(Global::UniformBufferObject);
@ -196,7 +188,7 @@ namespace BuffersLibraries {
vkMapMemory(Global::device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]);
}
}
void buffers::updateUniformBuffer(uint32_t currentImage) {
void Buffers::updateUniformBuffer(uint32_t currentImage) {
// Update the uniform buffer every frame to change the position, but notably, use chrono to know exactly how much to move
// so we aren't locked to the framerate as the world time.
@ -208,25 +200,25 @@ namespace BuffersLibraries {
Global::UniformBufferObject ubo{};
ubo.time = time;
// Modify the model projection transformation to rotate around the Z over time.
ubo.model = glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.model = glm::rotate(glm::mat4(1.0f), glm::radians(30.0f), glm::vec3(0.0f, 0.0f, 1.0f));
// Modify the view transformation to look at the object from above at a 45 degree angle.
// This takes the eye position, center position, and the up direction.
ubo.view = glm::lookAt(glm::vec3(4.0f, 4.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
// 45 degree field of view, set aspect ratio, and near and far clipping range.
ubo.proj = glm::perspective(glm::radians(45.0f), deviceLibrary.getSwapChainExtent().width / (float) deviceLibrary.getSwapChainExtent().height, 0.1f, 10.0f);
ubo.proj = glm::perspective(glm::radians(45.0f), device_libs::DeviceControl::getSwapChainExtent().width / (float) device_libs::DeviceControl::getSwapChainExtent().height, 0.1f, 10.0f);
// GLM was created for OpenGL, where the Y coordinate was inverted. This simply flips the sign.
ubo.proj[1][1] *= -1;
memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
}
void buffers::destroyUniformBuffer() {
void Buffers::destroyUniformBuffer() {
for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroyBuffer(Global::device, uniformBuffers[i],nullptr);
vkFreeMemory(Global::device, uniformBuffersMemory[i], nullptr);
}
}
void buffers::createDescriptorPool() {
void Buffers::createDescriptorPool() {
// Create a pool to be used to allocate descriptor sets.
std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@ -244,7 +236,7 @@ namespace BuffersLibraries {
throw std::runtime_error("failed to create descriptor pool!");
}
}
void buffers::createDescriptorSets() {
void Buffers::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(Global::MAX_FRAMES_IN_FLIGHT, Global::descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
@ -289,7 +281,7 @@ namespace BuffersLibraries {
vkUpdateDescriptorSets(Global::device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
}
}
void buffers::destroyDescriptorPool() {
void Buffers::destroyDescriptorPool() {
vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr);
}
}

View File

@ -1,22 +1,24 @@
#pragma once
#include "../global.h"
#include <chrono>
#include <cstring>
#include "../devicelibrary.h"
namespace BuffersLibraries {
class buffers {
namespace buffers_libs {
class Buffers {
public:
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags);
void createIndexBuffer();
void createVertexBuffer();
void destroyBuffers();
VkBuffer getVertexBuffer();
VkBuffer getIndexBuffer();
void createDescriptorSetLayout();
void createUniformBuffers();
void updateUniformBuffer(uint32_t currentImage);
void destroyUniformBuffer();
void createDescriptorPool();
void createDescriptorSets();
void destroyDescriptorPool();
static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags);
static void createIndexBuffer();
static void createVertexBuffer();
static void destroyBuffers();
static VkBuffer getVertexBuffer();
static VkBuffer getIndexBuffer();
static void createDescriptorSetLayout();
static void createUniformBuffers();
static void updateUniformBuffer(uint32_t currentImage);
static void destroyUniformBuffer();
static void createDescriptorPool();
static void createDescriptorSets();
static void destroyDescriptorPool();
};
}

View File

@ -1,23 +1,15 @@
#include "graphicspipeline.h"
#include "buffers.h"
#include "texture.h"
#include <cstdint>
#include <vector>
#include <vulkan/vulkan_core.h>
namespace Graphics {
namespace graphics_pipeline {
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
DeviceControl::devicelibrary deviceLibs;
BuffersLibraries::buffers buffers;
TextureLibraries::texture textureLibs;
std::vector<VkFramebuffer> swapChainFramebuffers;
@ -50,7 +42,7 @@ namespace Graphics {
return shaderModule;
}
void graphicspipeline::destroyGraphicsPipeline() {
void Graphics::destroyGraphicsPipeline() {
vkDestroyPipeline(Global::device, graphicsPipeline, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Graphics Pipeline safely\n" << std::endl;
vkDestroyPipelineLayout(Global::device, pipelineLayout, nullptr);
@ -58,13 +50,12 @@ namespace Graphics {
}
void graphicspipeline::createGraphicsPipeline() {
void Graphics::createGraphicsPipeline() {
// Note to self, for some reason the working directory is not where a read file is called from, but the project folder!
auto vertShaderCode = readFile("src/shaders/vertex.spv");
auto fragShaderCode = readFile("src/shaders/fragment.spv");
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, Global::device);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, Global::device);
// ------------------ STAGE 1 - INPUT ASSEMBLER ---------------- //
// This can get a little complicated, normally, vertices are loaded in sequential order, with an element buffer however, you can specify the indices yourself!
// Using an element buffer means you can reuse vertices, which can lead to optimizations. If you set PrimRestart to TRUE, you can utilize the _STRIP modes with special indices
@ -191,9 +182,9 @@ namespace Graphics {
if(Global::enableValidationLayers) std::cout << "Pipeline Layout created successfully\n" << std::endl;
}
void graphicspipeline::createRenderPass() {
void Graphics::createRenderPass() {
VkAttachmentDescription colorAttachment{};
colorAttachment.format = deviceLibs.getImageFormat();
colorAttachment.format = device_libs::DeviceControl::getImageFormat();
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@ -207,7 +198,7 @@ namespace Graphics {
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentDescription depthAttachment{};
depthAttachment.format = textureLibs.findDepthFormat();
depthAttachment.format = texture_libs::Texture::findDepthFormat();
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@ -250,18 +241,18 @@ namespace Graphics {
}
if(Global::enableValidationLayers) std::cout << "Render pass created successfully\n" << std::endl;
}
void graphicspipeline::destroyRenderPass() {
void Graphics::destroyRenderPass() {
vkDestroyRenderPass(Global::device, renderPass, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Render Pass Safely\n" << std::endl;
}
void graphicspipeline::createFramebuffers() {
void Graphics::createFramebuffers() {
// Resize the container to hold all the framebuffers.
int framebuffersSize = deviceLibs.getSwapChainImageViews().size();
int framebuffersSize = device_libs::DeviceControl::getSwapChainImageViews().size();
swapChainFramebuffers.resize(framebuffersSize);
for(size_t i = 0; i < framebuffersSize; i++) {
std::array<VkImageView, 2> attachments = {
deviceLibs.getSwapChainImageViews()[i],
device_libs::DeviceControl::getSwapChainImageViews()[i],
Global::depthImageView
};
@ -270,8 +261,8 @@ namespace Graphics {
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = deviceLibs.getSwapChainExtent().width;
framebufferInfo.height = deviceLibs.getSwapChainExtent().height;
framebufferInfo.width = device_libs::DeviceControl::getSwapChainExtent().width;
framebufferInfo.height = device_libs::DeviceControl::getSwapChainExtent().height;
framebufferInfo.layers = 1;
if(vkCreateFramebuffer(Global::device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
@ -279,7 +270,7 @@ namespace Graphics {
}
}
}
void graphicspipeline::createCommandPool() {
void Graphics::createCommandPool() {
// Commands in Vulkan are not executed using function calls, you have to record the ops you wish to perform
// to command buffers, pools manage the memory used by the buffer!
Global::QueueFamilyIndices queueFamilyIndices = Global::findQueueFamilies(Global::physicalDevice);
@ -294,10 +285,10 @@ namespace Graphics {
}
if(Global::enableValidationLayers) std::cout << "Command pool created successfully\n" << std::endl;
}
void graphicspipeline::destroyCommandPool() {
void Graphics::destroyCommandPool() {
vkDestroyCommandPool(Global::device, Global::commandPool, nullptr);
}
void graphicspipeline::createCommandBuffer() {
void Graphics::createCommandBuffer() {
Global::commandBuffers.resize(Global::MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocInfo{};
@ -312,7 +303,7 @@ namespace Graphics {
if(Global::enableValidationLayers) std::cout << "Allocated command buffers successfully\n" << std::endl;
}
void graphicspipeline::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
@ -325,7 +316,7 @@ namespace Graphics {
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = deviceLibs.getSwapChainExtent();
renderPassInfo.renderArea.extent = device_libs::DeviceControl::getSwapChainExtent();
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
@ -340,21 +331,21 @@ namespace Graphics {
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float) deviceLibs.getSwapChainExtent().width;
viewport.height = (float) deviceLibs.getSwapChainExtent().height;
viewport.width = (float) device_libs::DeviceControl::getSwapChainExtent().width;
viewport.height = (float) device_libs::DeviceControl::getSwapChainExtent().height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = deviceLibs.getSwapChainExtent();
scissor.extent = device_libs::DeviceControl::getSwapChainExtent();
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
VkBuffer vertexBuffers[] = {buffers.getVertexBuffer()};
VkBuffer vertexBuffers[] = {buffers_libs::Buffers::getVertexBuffer()};
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, buffers.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
vkCmdBindIndexBuffer(commandBuffer, buffers_libs::Buffers::getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &Global::descriptorSets[Global::currentFrame], 0, nullptr);
@ -365,7 +356,7 @@ namespace Graphics {
throw std::runtime_error("failed to record command buffer!");
}
}
std::vector<VkFramebuffer> graphicspipeline::getSwapChainFramebuffers() {
std::vector<VkFramebuffer> Graphics::getSwapChainFramebuffers() {
return swapChainFramebuffers;
}
}

View File

@ -2,20 +2,22 @@
#include "../global.h"
#include "../devicelibrary.h"
#include "buffers.h"
#include "texture.h"
#include <fstream>
namespace Graphics {
class graphicspipeline {
namespace graphics_pipeline {
class Graphics {
public:
void createGraphicsPipeline();
void destroyGraphicsPipeline();
void createRenderPass();
void destroyRenderPass();
void createFramebuffers();
void destroyFramebuffers();
void createCommandPool();
void destroyCommandPool();
void createCommandBuffer();
void recordCommandBuffer(VkCommandBuffer cmndBuffer, uint32_t imageIndex);
std::vector<VkFramebuffer> getSwapChainFramebuffers();
static void createGraphicsPipeline();
static void destroyGraphicsPipeline();
static void createRenderPass();
static void destroyRenderPass();
static void createFramebuffers();
static void destroyFramebuffers();
static void createCommandPool();
static void destroyCommandPool();
static void createCommandBuffer();
static void recordCommandBuffer(VkCommandBuffer cmndBuffer, uint32_t imageIndex);
static std::vector<VkFramebuffer> getSwapChainFramebuffers();
};
}

View File

@ -1,9 +1,4 @@
#define TINY_OBJ_IMPLEMENTATION
#include <tiny_obj_loader.h>
#include "model.h"
#include <unordered_map>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
namespace std {
template<> struct hash<Global::Vertex> {
@ -15,11 +10,11 @@ namespace std {
};
}
namespace ModelLib {
namespace modellib {
std::unordered_map<Global::Vertex, uint32_t> uniqueVertices{};
void model::loadModel() {
void Model::loadModel() {
tinyobj::ObjReaderConfig readerConfig;
tinyobj::ObjReader reader;

View File

@ -1,8 +1,14 @@
#include "../global.h"
namespace ModelLib {
class model {
#define TINY_OBJ_IMPLEMENTATION
#include <tiny_obj_loader.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
namespace modellib {
class Model {
public:
void loadModel();
static void loadModel();
};
}

View File

@ -3,16 +3,11 @@
#include "../devicelibrary.h"
#include "../entrypoint.h"
#include "texture.h"
#include <vulkan/vulkan_core.h>
namespace RenderPresent {
namespace render_present {
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
Graphics::graphicspipeline pipeline;
DeviceControl::devicelibrary deviceLibs;
BuffersLibraries::buffers buffers;
TextureLibraries::texture tex;
void recreateSwapChain() {
int width = 0, height = 0;
@ -23,24 +18,24 @@ namespace RenderPresent {
}
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.
for(auto framebuffer : pipeline.getSwapChainFramebuffers()) {
for(auto framebuffer : graphics_pipeline::Graphics::getSwapChainFramebuffers()) {
vkDestroyFramebuffer(Global::device, framebuffer, nullptr);
}
for(auto imageView : deviceLibs.getSwapChainImageViews()) {
for(auto imageView : device_libs::DeviceControl::getSwapChainImageViews()) {
vkDestroyImageView(Global::device, imageView, nullptr);
}
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
deviceLibs.createSwapChain(Global::window);
deviceLibs.createImageViews();
tex.createDepthResources();
pipeline.createFramebuffers();
device_libs::DeviceControl::createSwapChain(Global::window);
device_libs::DeviceControl::createImageViews();
texture_libs::Texture::createDepthResources();
graphics_pipeline::Graphics::createFramebuffers();
}
// At a high level, rendering in Vulkan consists of 5 steps:
// Wait for the previous frame, acquire a image from the swap chain
// record a comman d buffer which draws the scene onto that image
// submit the recorded command buffer and present the image!
void render::drawFrame() {
void Render::drawFrame() {
vkWaitForFences(Global::device, 1, &inFlightFences[Global::currentFrame], VK_TRUE, UINT64_MAX);
vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]);
@ -54,12 +49,12 @@ namespace RenderPresent {
throw std::runtime_error("failed to acquire swap chain image!");
}
buffers.updateUniformBuffer(Global::currentFrame);
buffers_libs::Buffers::updateUniformBuffer(Global::currentFrame);
vkResetFences(Global::device, 1, &inFlightFences[Global::currentFrame]);
vkResetCommandBuffer(Global::commandBuffers[Global::currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
pipeline.recordCommandBuffer(Global::commandBuffers[Global::currentFrame], imageIndex);
graphics_pipeline::Graphics::recordCommandBuffer(Global::commandBuffers[Global::currentFrame], imageIndex);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@ -127,7 +122,7 @@ namespace RenderPresent {
// doStuffOnceFenceDone()
#pragma endinfo
void render::createSyncObject() {
void Render::createSyncObject() {
imageAvailableSemaphores.resize(Global::MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(Global::MAX_FRAMES_IN_FLIGHT);
inFlightFences.resize(Global::MAX_FRAMES_IN_FLIGHT);
@ -149,21 +144,21 @@ namespace RenderPresent {
}
void render::destroyFenceSemaphores() {
void Render::destroyFenceSemaphores() {
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(Global::device, renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore(Global::device, imageAvailableSemaphores[i], nullptr);
vkDestroyFence(Global::device, inFlightFences[i], nullptr);
}
}
void render::cleanupSwapChain() {
void Render::cleanupSwapChain() {
vkDestroyImageView(Global::device, Global::depthImageView, nullptr);
vkDestroyImage(Global::device, Global::depthImage, nullptr);
vkFreeMemory(Global::device, Global::depthImageMemory, nullptr);
for(auto framebuffer : pipeline.getSwapChainFramebuffers()) {
for(auto framebuffer : graphics_pipeline::Graphics::getSwapChainFramebuffers()) {
vkDestroyFramebuffer(Global::device, framebuffer, nullptr);
}
for(auto imageView : deviceLibs.getSwapChainImageViews()) {
for(auto imageView : device_libs::DeviceControl::getSwapChainImageViews()) {
vkDestroyImageView(Global::device, imageView, nullptr);
}
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);

View File

@ -2,12 +2,12 @@
#include "../global.h"
namespace RenderPresent {
class render {
namespace render_present {
class Render {
public:
void drawFrame();
void createSyncObject();
void destroyFenceSemaphores();
void cleanupSwapChain();
static void drawFrame();
static void createSyncObject();
static void destroyFenceSemaphores();
static void cleanupSwapChain();
};
}

View File

@ -1,20 +1,14 @@
#include <vulkan/vulkan_core.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include "texture.h"
#include "buffers.h"
#include "../devicelibrary.h"
BuffersLibraries::buffers buffer;
DeviceControl::devicelibrary deviceLibraries;
VkImage textureImage;
VkDeviceMemory textureImageMemory;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
namespace TextureLibraries {
namespace texture_libs {
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
// This function specifies all the data in an image object, this is called directly after the creation of an image object.
VkImageCreateInfo imageInfo{};
@ -42,7 +36,7 @@ namespace TextureLibraries {
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = buffer.findMemoryType(memRequirements.memoryTypeBits, properties);
allocInfo.memoryTypeIndex = buffers_libs::Buffers::findMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate image memory!");
@ -186,7 +180,7 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}
// -------------------------------- Image Libraries ------------------------------- //
void texture::createTextureImage() {
void Texture::createTextureImage() {
// Import pixels from image with data on color channels, width and height, and colorspace!
// Its a lot of kind of complicated memory calls to bring it from a file -> to a buffer -> to a image object.
int textureWidth, textureHeight, textureChannels;
@ -199,7 +193,7 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
buffer.createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
buffers_libs::Buffers::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(Global::device, stagingBufferMemory, 0, imageSize, 0, &data);
@ -217,11 +211,11 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void texture::createTextureImageView() {
void Texture::createTextureImageView() {
// Create a texture image view, which is a struct of information about the image.
Global::textureImageView = deviceLibraries.createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
Global::textureImageView = device_libs::DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
}
void texture::createTextureSampler() {
void Texture::createTextureSampler() {
// Create a sampler to access and parse the texture object.
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
@ -263,26 +257,26 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
throw std::runtime_error("failed to create texture sampler!");
}
}
void texture::destroyTextureSampler() {
void Texture::destroyTextureSampler() {
vkDestroySampler(Global::device, Global::textureSampler, nullptr);
vkDestroyImageView(Global::device, Global::textureImageView, nullptr);
}
void texture::destroyTextureImage() {
void Texture::destroyTextureImage() {
vkDestroyImage(Global::device, textureImage, nullptr);
vkFreeMemory(Global::device, textureImageMemory, nullptr);
}
VkFormat texture::findDepthFormat() {
VkFormat Texture::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 texture::createDepthResources() {
void Texture::createDepthResources() {
VkFormat depthFormat = findDepthFormat();
VkExtent2D swapChainExtent = deviceLibraries.getSwapChainExtent();
VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent();
createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory);
Global::depthImageView = deviceLibraries.createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
Global::depthImageView = device_libs::DeviceControl::createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
// Explicit transition from the layout of the image to the depth attachment is unnecessary here, since that will be handled in the render pass!

View File

@ -1,16 +1,17 @@
#pragma once
#include "../global.h"
#include <vulkan/vulkan_core.h>
#include "buffers.h"
#include "../devicelibrary.h"
namespace TextureLibraries {
class texture {
namespace texture_libs {
class Texture {
public:
void createTextureImage();
void createTextureImageView();
void createTextureSampler();
void destroyTextureImage();
void destroyTextureSampler();
VkFormat findDepthFormat();
void createDepthResources();
static void createTextureImage();
static void createTextureImageView();
static void createTextureSampler();
static void destroyTextureImage();
static void destroyTextureSampler();
static VkFormat findDepthFormat();
static void createDepthResources();
};
}