Compare commits

...

3 Commits

20 changed files with 379 additions and 398 deletions

View File

@ -30,7 +30,7 @@ split again
split
://**createInstance()**//; <<procedure>>
split
:Debug::checkUnavailableValidationLayers();
:**Debug**::checkUnavailableValidationLayers();
:**VkApplicationInfo** appInfo{};
://set appInfo data, Vulkan version,//
//Engine version and name, and title//;
@ -39,16 +39,17 @@ split
:Debug::vulkanDebugSetup(createInfo, vulkaninstance);
end split
split again
:Debug::setupDebugMessenger(VkInstance&);
:**Debug**::setupDebugMessenger(VkInstance&);
note right: Setup debug messenger, print data to console
:glfwCreateWindowSurface(...);
partition "**DeviceControl**" {
:createSurface(...);
note right
This function handles Window System Integration
automatically across platforms based on build environment.
====
//Basically, this is an abstraction of the Window across platforms//
end note
partition "**DeviceControl**" {
:pickPhysicalDevice(...);
note right
Enumerate through GPU's in the
@ -97,11 +98,6 @@ partition "**Graphics**" {
step of the render pipeline and sets the settings we
desire for each step! **HEAVILY** documented.
end note
:createFramebuffers(...);
note right
This function creates framebuffers for all the images
that are queued to be displayed, very important!
end note
:createCommandPool(...);
note right
Commands in Vulkan are not executed using function calls
@ -109,7 +105,20 @@ partition "**Graphics**" {
buffer objects, pools manage the memory used for buffers.
end note
}
partition "**TextureLibraries**" {
:**Texture**::createDepthResources();
note right
This function sets up the image views and sets for the
depth testing buffer to handle objects above or below
other objects!
end note
:**Graphics**::createFramebuffers(...);
note right
This function creates framebuffers for all the images
that are queued to be displayed, very important!
end note
partition "**Texture**" {
:createTextureImage();
note right
This function imports the pixels from an image, puts them
@ -132,6 +141,16 @@ partition "**TextureLibraries**" {
//Mipmap modes//
end note
}
partition "**Model**" {
:loadModel();
note right
Exactly what it sounds like, as of now, call our function
to load .OBJ files with STB. Obviously want to support FBX
in the future but the format is quite complex.
This function simply loads vertices and indices into the
arrays to be parsed!
end note
}
partition "**Buffers**" {
:createVertexBuffer();
note right

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 71 KiB

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.
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);
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*> 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,7 @@
#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;
#include "global.h"
//TODO: add global instances?
VkInstance vulkaninstance;
// Getters and Setters!
void EntryApp::setFramebufferResized(bool setter) {
@ -21,8 +14,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 +27,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 +43,68 @@ 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();
graphics_pipeline::Graphics::destroyGraphicsPipeline();
graphics_pipeline::Graphics::destroyRenderPass();
buffers_libs::Buffers::destroyUniformBuffer();
buffers_libs::Buffers::destroyDescriptorPool();
texture_libs::Texture::destroyTextureSampler();
texture_libs::Texture::destroyTextureImage();
vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout, nullptr);
graphicsPipeline.destroyGraphicsPipeline();
graphicsPipeline.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), time * 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;
@ -28,11 +23,14 @@ namespace ModelLib {
if(!reader.Error().empty()) {
throw std::runtime_error(reader.Error());
}
if(!reader.Warning().empty()) {
throw std::runtime_error(reader.Warning());
}
}
auto& attrib = reader.GetAttrib();
auto& shapes = reader.GetShapes();
auto& materials = reader.GetMaterials();
//auto& materials = reader.GetMaterials();
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
@ -43,19 +41,19 @@ namespace ModelLib {
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]
};
//TODO: Major fix here, running anything but the viking room OBJ crashes on texcoord assignment!
vertex.texCoord = {
attrib.texcoords[2 * index.texcoord_index + 0],
1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
};
vertex.color = {1.0f, 1.0f, 1.0f};
if (uniqueVertices.count(vertex) == 0) {
uniqueVertices[vertex] = static_cast<uint32_t>(Global::vertices.size());
Global::vertices.push_back(vertex);
}
Global::indices.push_back(uniqueVertices[vertex]);
}
}

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();
};
}