From e7b9d4a32fcfebd18ee1fce05069b0a87ba991d3 Mon Sep 17 00:00:00 2001 From: Piotr Krygier Date: Wed, 6 Jul 2022 12:21:37 +0200 Subject: [PATCH] Changed project back to C++ --- CMakeLists.txt | 23 - Makefile | 43 +- include/localeVulkan.hpp | 134 +++-- include/localeWindow.hpp | 40 +- include/localization.hpp | 35 +- include/log.hpp | 90 ---- include/logger.hpp | 73 +++ include/vulkanBase.hpp | 18 +- include/window.hpp | 10 +- shaders/shader.frag | 9 + shaders/shader.vert | 20 + src/logger.cpp | 58 +++ src/main.cpp | 12 +- src/vulkanBase.cpp | 1002 ++++++++++++++++++++++++++++++++------ src/window.cpp | 26 +- 15 files changed, 1181 insertions(+), 412 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 include/log.hpp create mode 100644 include/logger.hpp create mode 100644 shaders/shader.frag create mode 100644 shaders/shader.vert create mode 100644 src/logger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index c85f841a..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -# set the project name -project(RedScarfEngine VERSION 0.1.0) - -# specify the C++ standard -set(CMAKE_C++_STANDARD 14) -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED True) - -if (MSVC) - # warning level 4 and all warnings as errors - add_compile_options(/W4 /WX) -else() - # lots of warnings and all warnings as errors - add_compile_options(-Wall -Wextra -pedantic -Werror) -endif() - -add_subdirectory(Graphics) -add_subdirectory(RedScarfEngine) - -install(TARGETS RedScarfEngine GraphicsEngine - DESTINATION ${PROJECT_SOURCE_DIR}/bin) diff --git a/Makefile b/Makefile index a7201925..b6df5858 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,45 @@ CC := /usr/bin/g++ INCLUDE_DIR := -I./include -I./third_party/vma/include -CFLAGS := -g -Werror -Wall -std=c++17 $(INCLUDE_DIR) +LFLAGS := -lglfw -lvulkan +CFLAGS := -g -Wall -Werror -std=c++20 $(INCLUDE_DIR) $(LFLAGS) SRC_DIR := src BUILD_DIR := ./build TARGET := RedScarfEngine SOURCES := main.cpp \ vulkanBase.cpp \ - window.cpp + window.cpp \ + logger.cpp \ -OBJECTS := $(SOURCES:.cpp=.o) +OBJECTS := $(SOURCES:%=$(BUILD_DIR)/%) +OBJECTS := $(OBJECTS:cpp=o) -all: $(BUILD_DIR)/$(OBJECTS) - $(CC) $(CFLAGS) -o $(BUILD_DIR)/$(TARGET) $(BUILD_DIR)/$(OBJECTS) +SHADERS_DIR := ./shaders -$(BUILD_DIR)/main.o: $(SRC_DIR)/main.cpp $(BUILD_DIR)/vulkanBase.o - $(CC) $(CFLAGS) -c $(SRC_DIR)/main.cpp +SHADER_OBJECTS := vertex.spv \ + fragments.spv \ -$(BUILD_DIR)/vulkanBase.o: $(SRC_DIR)/vulkanBase.cpp - $(CC) $(CFLAGS) -c $(SRC_DIR)/vulkanBase.cpp +all: prepare_dirs $(OBJECTS) $(SHADER_OBJECTS) + $(CC) $(CFLAGS) -o $(BUILD_DIR)/$(TARGET) $(OBJECTS) + +# Build shader files +vertex.spv: $(SHADERS_DIR)/shader.vert + glslc $^ -o $(BUILD_DIR)/$@ + +fragments.spv: $(SHADERS_DIR)/shader.frag + glslc $^ -o $(BUILD_DIR)/$@ + +$(BUILD_DIR)/%.spv: + echo $(basename $^) + glslc $^ -o $@ + +# Generic build command +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp + $(CC) $(CFLAGS) -c $^ -o $@ + +.PHONY: prepare_dirs +prepare_dirs: + mkdir -p ./build + +.PHONY: clean +clean: + rm -rf ./build diff --git a/include/localeVulkan.hpp b/include/localeVulkan.hpp index ec33b05d..c1f759d6 100644 --- a/include/localeVulkan.hpp +++ b/include/localeVulkan.hpp @@ -4,16 +4,24 @@ * @brief Error messages for Vulkan initializers * @version 0.1 * @date 2022-05-13 - * + * * @copyright Copyright (c) 2022 - * + * */ -#ifndef LOCALE_VULKAN_H -#define LOCALE_VULKAN_H +#pragma once + +#include +#include #include "localization.hpp" + +namespace rse::locale::vulkan +{ + +#define vulkanErrorMesssages vulkanErrorMesssages_nolocale[SELECTED_LANGUAGE] + enum Errors { VULKAN_INTANCE_INIT_FAILED, @@ -27,78 +35,58 @@ enum Errors VULKAN_SURFACE_CREATION_FAILED, VULKAN_SWAPCHAIN_CREATION_FAILED, VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED, + VULKAN_SHADER_UNABLE_TO_OPEN, + VULKAN_SHADER_UNABLE_TO_CREATE_SHADER, + VULKAN_PIPELAYOUT_CREATION_FAILED, + VULKAN_RENDERPASS_CREATION_FAILED, + VULKAN_GRAPHICSPIPELINE_CREATION_FAILED, + VULKAN_FRAMEBUFFERS_CREATION_FAILED, + VULKAN_RECORD_COMMAND_BUFFER_FAILED, + VULKAN_SYNCOBJCTS_CREATION_FAILED, LAST_MESSAGE }; -const struct MessageString_t vulkanErrorMesssages[LAST_MESSAGE] = -{ - { - 38, - {/* VULKAN_INTANCE_INIT_FAILED */ - "Vulkan instance initialization failed" +std::array, LAST_LANGUAGE> vulkanErrorMesssages_nolocale = { + {/* EN_US */ + /* VULKAN_INTANCE_INIT_FAILED */ + "Vulkan instance initialization failed", + /* VULKAN_NO_PHYSICAL_DEVICES */ + "No physical graphical physical devices found in system", + /* VULKAN_NO_SUITABLE_PHYSICAL_DEVICES */ + "No suitable physical device found", + /* VULKAN_QUEUE_NOT_SUPPORTED */ + "Selected physical device does not support required queue capabilities", + /* VULKAN_FAILED_TO_CREATE_DEVICE */ + "Failed to create logical device", + /* VULKAN_ALLOCATOR_CREATION_FAILED */ + "Failed to create Vulkan Memory Allocator", + /* VULKAN_COMMAND_POOL_CREATION_FAILED */ + "Failed to create command pool", + /* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */ + "Failed to allocate command buffers", + /* VULKAN_SURFACE_CREATION_FAILED */ + "Failed to create window surface", + /* VULKAN_SWAPCHAIN_CREATION_FAILED */ + "Failed to create swapchain", + /* VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED */ + "Failed to create swapchain image view", + /* VULKAN_SHADER_UNABLE_TO_OPEN */ + "Failed to open a file containing shader code", + /* VULKAN_SHADER_UNABLE_TO_CREATE_SHADER */ + "Failed to create a shader", + /* VULKAN_PIPELAYOUT_CREATION_FAILED */ + "Failed to create pipeline layout", + /* VULKAN_RENDERPASS_CREATION_FAILED */ + "Failed to create render pass", + /* VULKAN_GRAPHICSPIPELINE_CREATION_FAILED */ + "Failed to create graphics pipeline", + /* VULKAN_FRAMEBUFFERS_CREATION_FAILED */ + "Failed to create framebuffer", + /* VULKAN_RECORD_COMMAND_BUFFER_FAILED */ + "Failed to record command buffer. Validate your commands.", + /* VULKAN_SYNCOBJCTS_CREATION_FAILED */ + "Failed to create sync objects" } - }, - { - 55, - {/* VULKAN_NO_PHYSICAL_DEVICES */ - "No physical graphical physical devices found in system" - } - }, - { - 35, - {/* VULKAN_NO_SUITABLE_PHYSICAL_DEVICES */ - "No suitable physical device found" - } - }, - { - 70, - {/* VULKAN_QUEUE_NOT_SUPPORTED */ - "Selected physical device does not support required queue capabilities" - } - }, - { - 23, - {/* VULKAN_FAILED_TO_CREATE_DEVICE */ - "Failed to create logical device" - } - }, - { - 41, - {/* VULKAN_ALLOCATOR_CREATION_FAILED */ - "Failed to create Vulkan Memory Allocator" - } - }, - { - 30, - {/* VULKAN_COMMAND_POOL_CREATION_FAILED */ - "Failed to create command pool" - } - }, - { - 35, - {/* VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED */ - "Failed to allocate command buffers" - } - }, - { - 32, - {/* VULKAN_SURFACE_CREATION_FAILED */ - "Failed to create window surface" - } - }, - { - 27, - {/* VULKAN_SWAPCHAIN_CREATION_FAILED */ - "Failed to create swapchain" - } - }, - { - 38, - {/* VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED */ - "Failed to create swapchain image view" - } - }, }; - -#endif +} // namespace rse::locale::vulkan \ No newline at end of file diff --git a/include/localeWindow.hpp b/include/localeWindow.hpp index 959dc31a..030b1d8e 100644 --- a/include/localeWindow.hpp +++ b/include/localeWindow.hpp @@ -9,7 +9,17 @@ * */ -#include "localization.h" +#pragma once + +#include + +#include "localization.hpp" + + +namespace rse::locale::window +{ + +#define windowErrorMesssages windowErrorMesssages_nolocale[SELECTED_LANGUAGE] enum Errors { @@ -19,23 +29,15 @@ enum Errors LAST_MESSAGE }; -const struct MessageString_t windowErrorMesssages[LAST_MESSAGE] = -{ - { - 17, - {/* WINDOW_GLFW_INIT_FAILED */ - "GLFW init failed!" +std::array, LAST_MESSAGE> windowErrorMesssages_nolocale = { + {/* EN_US */ + /* WINDOW_GLFW_INIT_FAILED */ + "GLFW init failed!" + /* WINDOW_VULKAN_NOT_SUPPORTED */ + "Unable to load Vulkan" + /*WINDOW_NOT_CREATED*/ + "Window context creation failed" } - }, - { - 22, - {/* WINDOW_VULKAN_NOT_SUPPORTED */ - "Unable to load Vulkan" - } - }, - { - 31, - {/*WINDOW_NOT_CREATED*/ - "Window context creation failed" - }} }; + +} // namespace rse::locale::window \ No newline at end of file diff --git a/include/localization.hpp b/include/localization.hpp index 3c3bbe2b..1d638cb3 100644 --- a/include/localization.hpp +++ b/include/localization.hpp @@ -1,5 +1,5 @@ /** - * @file localization.h + * @file localization.hpp * @author Piotr Krygier (everyonencancode@gmail.com) * @brief Simple localization header * @version 0.1 @@ -9,14 +9,11 @@ * */ -#ifndef LOCALIZATION_H -#define LOCALIZATION_H +#pragma once +#define SELECTED_LANGUAGE rse::locale::EN_US -#include - -#define SELECTED_LANGUAGE EN_US - -#define LOCALIZE(string) string.message[SELECTED_LANGUAGE] +namespace rse::locale +{ /** * @brief Available languages @@ -28,24 +25,4 @@ enum Languages_t LAST_LANGUAGE }; -/** - * @brief - * - */ -struct MessageString_t -{ - /** - * @brief Maximum message length. I.e. if French version is longer than English, use French length. - * - */ - size_t maxMessageLength; - - /** - * - * @brief Array holding list of translations for specific message - * - */ - const char* const message[LAST_LANGUAGE]; -}; - -#endif /* LOCALIZATION_H */ \ No newline at end of file +} // namespace rse::locale diff --git a/include/log.hpp b/include/log.hpp deleted file mode 100644 index bcff0d8a..00000000 --- a/include/log.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file log.hxx - * @author Piotr Krygier (everyonencancode@gmail.com) - * @brief Logging interfaces - * @version 0.1 - * @date 2022-02-11 - * - * @copyright Copyright (c) 2022 - * - */ - -#ifndef LOG_H -#define LOG_H - -#include - -/* Controls level of logging. TODO: Move this to cmake or some config */ -#define LOG_LEVEL 6 - -#define LOG(level, ...) _LOG_##level(__VA_ARGS__) - -#if LOG_LEVEL >= 1 -#define _LOG_FATAL(...) __LOG(FATAL, __VA_ARGS__) -#else -#define _LOG_FATAL(...) -#endif - -#if LOG_LEVEL >= 2 -#define _LOG_ERROR(...) __LOG(ERROR, __VA_ARGS__) -#else -#define _LOG_ERROR(...) -#endif - -#if LOG_LEVEL >= 3 -#define _LOG_WARNING(...) __LOG(WARNING, __VA_ARGS__) -#else -#define _LOG_WARNING(...) -#endif - -#if LOG_LEVEL >= 4 -#define _LOG_INFO(...) __LOG(INFO, __VA_ARGS__) -#else -#define _LOG_INFO(...) -#endif - -#if LOG_LEVEL >= 5 -#define _LOG_DEBUG(...) __LOG(DEBUG, __VA_ARGS__) -#else -#define _LOG_DEBUG(...) -#endif - -#if LOG_LEVEL >= 6 -#define _LOG_TRACE(...) __LOG(TRACE, __VA_ARGS__) -#else -#define _LOG_TRACE(...) -#endif - -/* Macro magic, that makes logging work. It's better than C++ solution anyway... */ -#define _LOG_SIZEOF(...) __LOG_SIZEOF(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0) -#define __LOG_SIZEOF(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, N, \ - ...) \ - N -#define _LOG_EVAL(x) x -#define __LOG(level, ...) ___LOG(_LOG_SIZEOF(__VA_ARGS__), level, __VA_ARGS__) -#define ___LOG(n, ...) ____LOG(n, __VA_ARGS__) -#define ____LOG(n, ...) ____LOG##n(__VA_ARGS__) -#define ____LOG1(level, format) \ - fprintf(stdout, \ - "[" #level \ - "]" \ - "\t%s:%d\t" format "\n", \ - __builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__, __LINE__) -#define ____LOG2(level, format, ...) \ - fprintf(stdout, \ - "[" #level \ - "]" \ - "\t%s:%d\t" format "\n", \ - __builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__, __LINE__, __VA_ARGS__) - -typedef enum -{ - TRACE = 1, - DEBUG = 2, - INFO = 3, - WARNING = 4, - ERROR = 5, - FATAL = 6 -} LogLevel; - -#endif /* LOG_H */ \ No newline at end of file diff --git a/include/logger.hpp b/include/logger.hpp new file mode 100644 index 00000000..fbb7ea00 --- /dev/null +++ b/include/logger.hpp @@ -0,0 +1,73 @@ +/** + * @file log.hpp + * @author Piotr Krygier (everyonencancode@gmail.com) + * @brief Logging interfaces + * @version 0.1 + * @date 2022-02-11 + * + * @copyright Copyright (c) 2022 + * + */ + +#pragma once + +#include + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOGLEVEL_WARNING +#endif + +#define LOGT rse::log::Logger(rse::log::LOGLEVEL_TRACE) +#define LOGD rse::log::Logger(rse::log::LOGLEVEL_DEBUG) +#define LOGI rse::log::Logger(rse::log::LOGLEVEL_INFO) +#define LOGW rse::log::Logger(rse::log::LOGLEVEL_WARNING) +#define LOGE rse::log::Logger(rse::log::LOGLEVEL_ERROR) +#define LOGF rse::log::Logger(rse::log::LOGLEVEL_FATAL) + + +namespace rse::log +{ + +/** + * @brief Log level. Is not an enum class, because level is being compared to enable prints + * + */ +typedef enum +{ + LOGLEVEL_TRACE = 1, + LOGLEVEL_DEBUG = 2, + LOGLEVEL_INFO = 3, + LOGLEVEL_WARNING = 4, + LOGLEVEL_ERROR = 5, + LOGLEVEL_FATAL = 6 +} LogLevel; + +/** + * @brief Class used indirectly as a logger. + * + */ +class Logger { + private: + std::stringstream mMessageBuffer; + + public: + /** + * @brief Construct a new Logger object + * + */ + Logger(LogLevel); + ~Logger(); + + /** + * @brief For log printing. I want it to work like log << "Message " << args; + * + */ + template + Logger& operator<<(T&& message) + { + mMessageBuffer << message; + return *this; + } +}; + +} // namespace rse::log \ No newline at end of file diff --git a/include/vulkanBase.hpp b/include/vulkanBase.hpp index a2807ecd..d6d31262 100644 --- a/include/vulkanBase.hpp +++ b/include/vulkanBase.hpp @@ -4,15 +4,21 @@ * @brief Vulkan wrapper functions used by other parts of Graphics component * @version 0.1 * @date 2022-05-13 - * + * * @copyright Copyright (c) 2022 - * + * */ -#ifndef VULKAN_BASE_H -#define VULKAN_BASE_H +#pragma once -int initVulkan(); +#include + +namespace rse::graphics::vulkanbase +{ + +uint8_t initVulkan(); void deinitVulkan(); -#endif /* VULKAN_BASE_H */ \ No newline at end of file +void drawFrame(); + +} /* namespace rse::graphics::vulkanbase */ \ No newline at end of file diff --git a/include/window.hpp b/include/window.hpp index 6f52b1e4..e22f9f9a 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -9,14 +9,16 @@ * */ -#ifndef WINDOW_HANDLER_H -#define WINDOW_HANDLER_H +#pragma once -/* Vulkan header MUST be included before glfw */ #include +/* Vulkan header MUST be included before glfw */ #include +namespace rse::graphics::window +{ + /** * @brief Initialize window * @@ -42,4 +44,4 @@ void windowTerminate(); GLFWwindow* getWindowHandle(); -#endif /* WINDOW_HANDLER_H */ \ No newline at end of file +} /* namespace rse::graphics::window */ \ No newline at end of file diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 00000000..13009da8 --- /dev/null +++ b/shaders/shader.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 00000000..66d67662 --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} \ No newline at end of file diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 00000000..bd315ceb --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,58 @@ +/** + * @file logger.cpp + * @author Piotr Krygier (everyonecancode@gmail.com) + * @brief Implementation for the logger + * @version 0.1 + * @date 2022-07-06 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "logger.hpp" + +#include + +namespace rse::log +{ + +Logger::Logger(LogLevel level) +{ + if (level < LOG_LEVEL) + { + return; + } + + switch (level) + { + case LOGLEVEL_TRACE: + mMessageBuffer << "[TRACE] "; + break; + case LOGLEVEL_DEBUG: + mMessageBuffer << "[DEBUG] "; + break; + case LOGLEVEL_INFO: + mMessageBuffer << "[INFO] "; + break; + case LOGLEVEL_WARNING: + mMessageBuffer << "[WARNING] "; + break; + case LOGLEVEL_ERROR: + mMessageBuffer << "[ERROR] "; + break; + case LOGLEVEL_FATAL: + mMessageBuffer << "[FATAL] "; + break; + + default: + break; + } + +} + +Logger::~Logger() +{ + std::cout << mMessageBuffer.str() << std::endl; +} + +} // namespace rse::log diff --git a/src/main.cpp b/src/main.cpp index 019f7555..371089b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -#include "log.hpp" +#include "logger.hpp" #include "window.hpp" #include "vulkanBase.hpp" @@ -8,15 +8,15 @@ int main(int argc, char** argv) (void)argc; (void)argv; - windowInit(); + rse::graphics::window::windowInit(); - initVulkan(); + rse::graphics::vulkanbase::initVulkan(); - windowLoop(); + rse::graphics::window::windowLoop(); - deinitVulkan(); + rse::graphics::vulkanbase::deinitVulkan(); - windowTerminate(); + rse::graphics::window::windowTerminate(); return 0; } diff --git a/src/vulkanBase.cpp b/src/vulkanBase.cpp index 98d7498f..a454d1bd 100644 --- a/src/vulkanBase.cpp +++ b/src/vulkanBase.cpp @@ -15,15 +15,46 @@ #include "vulkanBase.hpp" #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wparentheses" +#define VMA_IMPLEMENTATION #include +#pragma GCC diagnostic pop #include #include "localeVulkan.hpp" -#include "log.hpp" +#include "logger.hpp" #include "window.hpp" #include #include +#include +#include +#include + +namespace rse::graphics::vulkanbase +{ + +using namespace locale::vulkan; + + +/* Macro for checking status of function execution in initVulkan. Created to avoid writing boilerplate code */ +#define STATUS_CHECK(FUNC) \ + status = FUNC; \ + if (VULKAN_ERROR_NO_ERROR != status) { \ + return status; \ + } + +#define APPLICATION_NAME "RedScarfEngine PoC" +#define ENGINE_NAME "RedScarf Engine" +#define SWAP_BUFFER_COUNT 2U enum VULKAN_ERROR { @@ -39,30 +70,31 @@ enum VULKAN_ERROR VULKAN_ERROR_SURFACE_CREATION_FAILED, VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED, VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED, + VULKAN_ERROR_SHADER_FILE_OPEN_FAILED, + VULKAN_ERROR_SHADER_CREATION_FAILED, + VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED, + VULKAN_ERROR_RENDERPASS_CREATION_FAILED, + VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED, + VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED, + VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED, + VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED, }; -/* Macro for checking status of function execution in initVulkan. Created to avoid writing boilerplate code */ -#define STATUS_CHECK(FUNC) \ - status = FUNC; \ - if (VULKAN_ERROR_NO_ERROR != status) { \ - return status; \ - } - -#define APPLICATION_NAME "RedScarfEngine PoC" -#define ENGINE_NAME "RedScarf Engine" -#define SWAP_BUFFER_COUNT 2U +enum class ShaderStage +{ + VERTEX, + FRAGMENTS +}; /* Define Validation Layers for debug build */ #ifndef NDEBUG -std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; +const std::vector gEnabledInstanceLayersNames = {"VK_LAYER_KHRONOS_validation"}; #else -std::vector gEnabledInstanceLayersNames = {}; +std::vector gEnabledInstanceLayersNames = {}; #endif -#define ENABLED_DEVICE_EXTENSIONS_COUNT 1U - /* Extensions required by GLFW. TODO: Check for extensions required on other platforms */ -std::vector gEnabledInstanceExtensionsNames = { +const std::vector gEnabledInstanceExtensionsNames = { "VK_KHR_surface", "VK_KHR_xcb_surface", "VK_KHR_device_group_creation", @@ -71,23 +103,47 @@ std::vector gEnabledInstanceExtensionsNames = { #endif }; -std::vector gEnabledDeviceExtensionsNames[ENABLED_DEVICE_EXTENSIONS_COUNT] = { +const std::vector gEnabledDeviceExtensionsNames = { "VK_KHR_swapchain", }; +const std::map gShaderPaths = { + { + ShaderStage::VERTEX, + "./vertex.spv", + }, + { + ShaderStage::FRAGMENTS, + "./fragments.spv", + } +}; + uint32_t gQueueFamilyIndices[1] = {0U}; /* TODO: Change when more families are needed */ uint32_t gSwapchainImagesCount = 0U; +uint32_t gCurrentFrame = 0U; VkInstance gVulkanInstance = VK_NULL_HANDLE; VkSurfaceKHR gSurface = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT gDebugMessenger = VK_NULL_HANDLE; VkPhysicalDevice gPhysicalDevice = VK_NULL_HANDLE; VkDevice gDevice = VK_NULL_HANDLE; +VkQueue gGraphicsQueue; +VkQueue gPresentQueue; VmaAllocator gAllocator = VK_NULL_HANDLE; VkCommandPool gCommandPool = VK_NULL_HANDLE; -VkCommandBuffer* gpCommandBuffers = NULL; /* This one is an array. Allocated during runtime */ +std::vector gpCommandBuffers; VkSwapchainKHR gSwapchain = VK_NULL_HANDLE; -VkImage* gpSwapChainImages = NULL; -VkImageView* gpSwapChainImageViews = NULL; +std::vector gSwapChainImages; +std::vector gSwapChainImageViews; +VkExtent2D gSwapchainExtent; +std::vector gShaderModules; +VkRenderPass gRenderPass = VK_NULL_HANDLE; +VkPipelineLayout gPipelineLayout = VK_NULL_HANDLE; +VkPipeline gGraphicsPipeline = VK_NULL_HANDLE; +std::vector gSwapchainFramebuffers; +std::vector gImageAvailableSemaphores; +std::vector gRenderFinishedSemaphores; +std::vector gInFlightFences; + /** * @brief Create the Vulkan Instance object @@ -95,32 +151,32 @@ VkImageView* gpSwapChainImageViews = NULL; * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_INSTANCE_INIT_FAILED */ -static int createInstance(); +static uint8_t createInstance(); /** * @brief Create a Surface, connection between Vulkan and actual window * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ -static int createSurface(); +static uint8_t createSurface(); /** * @brief Choose physical device. Used later for vkDevice * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND * VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND */ -static int pickPhysicalDevice(); +static uint8_t pickPhysicalDevice(); /** * @brief Create vkDevice object * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_QUEUE_NOT_SUPPORTED * VULKAN_ERROR_DEVICE_CREATION_FAILED */ -static int createDevice(); +static uint8_t createDevice(); #ifndef NDEBUG /** @@ -132,41 +188,75 @@ static void setupDebugMessenger(); /** * @brief Create a Memory Allocator object * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_ALLOCATOR_CREATION_FAILED */ -static int createMemoryAllocator(); +static uint8_t createMemoryAllocator(); /** * @brief Create Command Pools * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED */ -static int createCommandPools(); +static uint8_t createCommandPools(); /** * @brief Allocate Command Buffers * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED */ -static int allocateCommandBuffers(); +static uint8_t allocateCommandBuffers(); /** * @brief Create a Swapchain * - * @return int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: * VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED */ -static int createSwapchain(); +static uint8_t createSwapchain(); /** * @brief Create a Swapchain Image Views * - * @return int int VULKAN_ERROR_NO_ERROR on success. Possible errors: + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: */ -static int createSwapchainImageViews(); +static uint8_t createSwapchainImageViews(); + +/** + * @brief Creates shader stages from hardcoded shader paths. Changes input + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_SHADER_FILE_OPEN_FAILED + * VULKAN_ERROR_SHADER_CREATION_FAILED + */ + +/** + * @brief Creates shader stages from hardcoded shader paths. Changes input + * + * @param shaderStages Empty vector, that will be filled with data + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_SHADER_FILE_OPEN_FAILED + * VULKAN_ERROR_SHADER_CREATION_FAILED + */ +static uint8_t fillShaderStages(std::vector* shaderStages); + +/** + * @brief Reads file and puts its content into char buffer + * + * @param fileName Path to file + * @return std::vector Binary buffer data + */ +static std::vector readFile(const std::string& fileName); + +/** + * @brief Creates shader module + * + * @param binaryData Shader compiled code + * @return std::optional Handle to compiled shader module or nullopt + */ +static std::optional createShaderModule(const std::vector& binaryData); /** * @brief Calback for debug messenger @@ -177,6 +267,71 @@ static int createSwapchainImageViews(); * @param pUserData Data provided by the user * @return VkBool32 Should always return VK_FALSE. The VK_TRUE value is reserved for use in layer development */ +static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + +/** + * @brief Proxy for vkCreateDebugUtilsMessengerEXT + * + * @param instance Vulkan instance object + * @param pCreateInfo VkDebugUtilsMessengerCreateInfoEXT structure + * @param pAllocator Memory allocator + * @param pDebugMessenger Debug messanger handle + * @return VkResult VK_SUCCESS on success + */ +static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger); + +/** + * @brief Proxy for vkDestroyDebugUtilsMessengerEXT + * + * @param instance Vulkan instance object + * @param debugMessenger Debug messanger handle + * @param pAllocator Memory allocator + */ +static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator); + +/** + * @brief Create a Render Pass + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_RENDERPASS_CREATION_FAILED + */ +static uint8_t createRenderPass(); + +/** + * @brief Create a Pipeline Layout + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED + */ +static uint8_t createPipelineLayout(); + +/** + * @brief Create Graphics Pipeline + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + * VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED + */ +static uint8_t createGraphicsPipeline(); + +/** + * @brief Create Frambuffers + * + * @return uint8_t VULKAN_ERROR_NO_ERROR on success. Possible errors: + */ +static uint8_t createFrambuffers(); + + +static void recreateSwapchain(); + + + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, @@ -191,23 +346,14 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityF return VK_FALSE; } -/** - * @brief Proxy for vkCreateDebugUtilsMessengerEXT - * - * @param instance Vulkan instance object - * @param pCreateInfo VkDebugUtilsMessengerCreateInfoEXT structure - * @param pAllocator Memory allocator - * @param pDebugMessenger Debug messanger handle - * @return VkResult VK_SUCCESS on success - */ static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { - PFN_vkCreateDebugUtilsMessengerEXT func = NULL; + PFN_vkCreateDebugUtilsMessengerEXT func = nullptr; func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); - if (func != NULL) { + if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; @@ -216,19 +362,12 @@ static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugU return VK_SUCCESS; } -/** - * @brief Proxy for vkDestroyDebugUtilsMessengerEXT - * - * @param instance Vulkan instance object - * @param debugMessenger Debug messanger handle - * @param pAllocator Memory allocator - */ static void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != NULL) { + if (func != nullptr) { func(instance, debugMessenger, pAllocator); } } @@ -238,6 +377,7 @@ static void setupDebugMessenger() VkDebugUtilsMessengerCreateInfoEXT createInfo; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.pNext = nullptr; 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; @@ -245,16 +385,16 @@ static void setupDebugMessenger() VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; createInfo.pfnUserCallback = debugCallback; - createInfo.pUserData = NULL; // Optional + createInfo.pUserData = nullptr; // Optional createInfo.flags = 0U; - if (CreateDebugUtilsMessengerEXT(gVulkanInstance, &createInfo, NULL, &gDebugMessenger) != VK_SUCCESS) { + if (CreateDebugUtilsMessengerEXT(gVulkanInstance, &createInfo, nullptr, &gDebugMessenger) != VK_SUCCESS) { fprintf(stderr, "failed to set up debug messenger!"); } } #endif -static int createInstance() +static uint8_t createInstance() { uint32_t extensionCount = 0; uint32_t layersCount = 0; @@ -264,13 +404,13 @@ static int createInstance() std::vector extensionsProperties; /* TODO: Remove or use later layer and extension properties */ - vkEnumerateInstanceLayerProperties(&layersCount, NULL); + vkEnumerateInstanceLayerProperties(&layersCount, nullptr); layerProperties.resize(layersCount); - vkEnumerateInstanceLayerProperties(&layersCount, layerProperties); + vkEnumerateInstanceLayerProperties(&layersCount, layerProperties.data()); - vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL); - extensionsProperties = malloc(sizeof(VkExtensionProperties) * extensionCount); - vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionsProperties); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + extensionsProperties.resize(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensionsProperties.data()); printf("Supported Layers: \n"); for (size_t i = 0; i < layersCount; i++) { @@ -283,60 +423,60 @@ static int createInstance() } applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - applicationInfo.pNext = NULL; + applicationInfo.pNext = nullptr; applicationInfo.pApplicationName = APPLICATION_NAME; applicationInfo.applicationVersion = VK_MAKE_VERSION(0, 1, 0); applicationInfo.pEngineName = ENGINE_NAME; applicationInfo.engineVersion = VK_MAKE_VERSION(0, 1, 0); - applicationInfo.apiVersion = VK_API_VERSION_1_2; /* TODO: Wait for package update to 1.3 */ + applicationInfo.apiVersion = VK_API_VERSION_1_3; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pNext = NULL; + createInfo.pNext = nullptr; createInfo.flags = 0U; createInfo.pApplicationInfo = &applicationInfo; createInfo.enabledLayerCount = gEnabledInstanceLayersNames.size(); #ifndef NDEBUG - createInfo.ppEnabledLayerNames = gEnabledInstanceLayersNames; + createInfo.ppEnabledLayerNames = gEnabledInstanceLayersNames.data(); #else - createInfo.ppEnabledLayerNames = NULL; + createInfo.ppEnabledLayerNames = nullptr; #endif createInfo.enabledExtensionCount = gEnabledInstanceExtensionsNames.size(); - createInfo.ppEnabledExtensionNames = gEnabledInstanceExtensionsNames; + createInfo.ppEnabledExtensionNames = gEnabledInstanceExtensionsNames.data(); - VkResult result = vkCreateInstance(&createInfo, NULL, &gVulkanInstance); + VkResult result = vkCreateInstance(&createInfo, nullptr, &gVulkanInstance); if (VK_SUCCESS != result) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_INTANCE_INIT_FAILED])); + LOGF << vulkanErrorMesssages[VULKAN_INTANCE_INIT_FAILED]; return VULKAN_ERROR_INSTANCE_INIT_FAILED; } return VULKAN_ERROR_NO_ERROR; } -static int createSurface() +static uint8_t createSurface() { - if (VK_SUCCESS != glfwCreateWindowSurface(gVulkanInstance, getWindowHandle(), NULL, &gSurface)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_SURFACE_CREATION_FAILED])); + if (VK_SUCCESS != glfwCreateWindowSurface(gVulkanInstance, window::getWindowHandle(), nullptr, &gSurface)) { + LOGF << vulkanErrorMesssages[VULKAN_SURFACE_CREATION_FAILED]; return VULKAN_ERROR_SURFACE_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } -static int pickPhysicalDevice() +static uint8_t pickPhysicalDevice() { uint32_t physicalDeviceCount = 0U; - VkPhysicalDevice* pPhysicalDevices = NULL; + std::vector pPhysicalDevices; /* Get number of existing physical devices */ - vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, NULL); + vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, nullptr); if (0 == physicalDeviceCount) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND])); + LOGF << vulkanErrorMesssages[VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND]; return VULKAN_ERROR_NO_PHYSICAL_DEVICE_FOUND; } - pPhysicalDevices = malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount); - vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, pPhysicalDevices); + pPhysicalDevices.resize(physicalDeviceCount); + vkEnumeratePhysicalDevices(gVulkanInstance, &physicalDeviceCount, pPhysicalDevices.data()); /* Check for suitability */ for (size_t physicalDeviceIdx = 0U; physicalDeviceIdx < physicalDeviceCount; ++physicalDeviceIdx) { @@ -352,30 +492,29 @@ static int pickPhysicalDevice() } } - free(pPhysicalDevices); - if (VK_NULL_HANDLE == gPhysicalDevice) { /* No suitable device found */ - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_NO_SUITABLE_PHYSICAL_DEVICES])); + LOGF << vulkanErrorMesssages[VULKAN_NO_SUITABLE_PHYSICAL_DEVICES]; return VULKAN_ERROR_NO_SUITABLE_PHYSICAL_DEVICE_FOUND; } return VULKAN_ERROR_NO_ERROR; } -static int createDevice() +static uint8_t createDevice() { uint32_t queueFamiliesPropertyCount = 0U; ssize_t supportingQueueFamilityIdx = -1; - VkQueueFamilyProperties* pQueueFamilyProperties = NULL; - VkDeviceCreateInfo deviceCreateInfo = {0}; - VkDeviceQueueCreateInfo deviceQueueCreateInfos[1] = {0}; /* TODO: Change when we want more queues */ - float* pQueuePriorities = NULL; + ssize_t supportingSufraceFamilityIdx = -1; + std::vector pQueueFamilyProperties; + VkDeviceCreateInfo deviceCreateInfo; + VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; /* TODO: Change when we want more queues */ + std::vector pQueuePriorities; /* Get information about supported queue families */ - vkGetPhysicalDeviceQueueFamilyProperties(gPhysicalDevice, &queueFamiliesPropertyCount, NULL); - pQueueFamilyProperties = malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertyCount); - vkGetPhysicalDeviceQueueFamilyProperties(gPhysicalDevice, &queueFamiliesPropertyCount, pQueueFamilyProperties); + vkGetPhysicalDeviceQueueFamilyProperties(gPhysicalDevice, &queueFamiliesPropertyCount, nullptr); + pQueueFamilyProperties.resize(queueFamiliesPropertyCount); + vkGetPhysicalDeviceQueueFamilyProperties(gPhysicalDevice, &queueFamiliesPropertyCount, pQueueFamilyProperties.data()); /* Get queue families that support graphics operations AND have most * queues. */ @@ -388,54 +527,68 @@ static int createDevice() numberOfQueues = pQueueFamilyProperties[familyIdx].queueCount; } } + /* Check for family with surface support */ + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(gPhysicalDevice, familyIdx, gSurface, &presentSupport); + if (presentSupport) + { + // FIXME: Search for best, not first one + supportingSufraceFamilityIdx = familyIdx; + } } } /* This is not magic number ;P. We want to support exact number of required queues flags */ if (0 > supportingQueueFamilityIdx) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_QUEUE_NOT_SUPPORTED])); + LOGF << vulkanErrorMesssages[VULKAN_QUEUE_NOT_SUPPORTED]; return VULKAN_ERROR_QUEUE_NOT_SUPPORTED; } gQueueFamilyIndices[0] = supportingQueueFamilityIdx; /* TODO: Change when more families are needed */ - pQueuePriorities = malloc(pQueueFamilyProperties[supportingQueueFamilityIdx].queueCount * sizeof(float)); + pQueuePriorities.resize(pQueueFamilyProperties[supportingQueueFamilityIdx].queueCount); for (size_t i = 0; i < pQueueFamilyProperties[supportingQueueFamilityIdx].queueCount; i++) { pQueuePriorities[i] = 1.0f; /* TODO: When queues have different tasks, change this if needed */ } deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfos[0].pNext = NULL; + deviceQueueCreateInfos[0].pNext = nullptr; deviceQueueCreateInfos[0].flags = 0; deviceQueueCreateInfos[0].queueFamilyIndex = supportingQueueFamilityIdx; deviceQueueCreateInfos[0].queueCount = pQueueFamilyProperties[supportingQueueFamilityIdx].queueCount; - deviceQueueCreateInfos[0].pQueuePriorities = pQueuePriorities; + deviceQueueCreateInfos[0].pQueuePriorities = pQueuePriorities.data(); - free(pQueueFamilyProperties); /* Not needed anymore */ + deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfos[1].pNext = nullptr; + deviceQueueCreateInfos[1].flags = 0; + deviceQueueCreateInfos[1].queueFamilyIndex = supportingSufraceFamilityIdx; + deviceQueueCreateInfos[1].queueCount = pQueueFamilyProperties[supportingSufraceFamilityIdx].queueCount; + deviceQueueCreateInfos[1].pQueuePriorities = pQueuePriorities.data(); //FIXME: This is wrong for this queue! deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.pNext = NULL; + deviceCreateInfo.pNext = nullptr; deviceCreateInfo.flags = 0U; - deviceCreateInfo.queueCreateInfoCount = 1u; /* For now one is enough. TODO: Change when needed. */ + deviceCreateInfo.queueCreateInfoCount = 2u; /* For now one is enough. TODO: Change when needed. */ deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; deviceCreateInfo.enabledLayerCount = 0; - deviceCreateInfo.ppEnabledLayerNames = NULL; - deviceCreateInfo.enabledExtensionCount = ENABLED_DEVICE_EXTENSIONS_COUNT; - deviceCreateInfo.ppEnabledExtensionNames = gEnabledDeviceExtensionsNames; - deviceCreateInfo.pEnabledFeatures = NULL; /* TODO: Enable features, when needed */ + deviceCreateInfo.ppEnabledLayerNames = nullptr; + deviceCreateInfo.enabledExtensionCount = gEnabledDeviceExtensionsNames.size(); + deviceCreateInfo.ppEnabledExtensionNames = gEnabledDeviceExtensionsNames.data(); + deviceCreateInfo.pEnabledFeatures = nullptr; /* TODO: Enable features, when needed */ - if (VK_SUCCESS != vkCreateDevice(gPhysicalDevice, &deviceCreateInfo, NULL, &gDevice)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE])); - free(pQueuePriorities); + if (VK_SUCCESS != vkCreateDevice(gPhysicalDevice, &deviceCreateInfo, nullptr, &gDevice)) { + LOGF << vulkanErrorMesssages[VULKAN_FAILED_TO_CREATE_DEVICE]; return VULKAN_ERROR_DEVICE_CREATION_FAILED; } - free(pQueuePriorities); + vkGetDeviceQueue(gDevice, supportingQueueFamilityIdx, 0, &gGraphicsQueue); + vkGetDeviceQueue(gDevice, supportingSufraceFamilityIdx, 0, &gPresentQueue); + return VULKAN_ERROR_NO_ERROR; } -static int createMemoryAllocator() +static uint8_t createMemoryAllocator() { VmaVulkanFunctions vulkanFunctions = {0}; vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; @@ -449,51 +602,50 @@ static int createMemoryAllocator() allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; if (VK_SUCCESS != vmaCreateAllocator(&allocatorCreateInfo, &gAllocator)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_ERROR_ALLOCATOR_CREATION_FAILED])); + LOGF << vulkanErrorMesssages[VULKAN_ERROR_ALLOCATOR_CREATION_FAILED]; return VULKAN_ERROR_ALLOCATOR_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } -static int createCommandPools() +static uint8_t createCommandPools() { VkCommandPoolCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - createInfo.pNext = NULL; - createInfo.flags = 0U; + createInfo.pNext = nullptr; + createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; createInfo.queueFamilyIndex = gQueueFamilyIndices[0]; /* TODO: Change when more families are needed */ - if (VK_SUCCESS != vkCreateCommandPool(gDevice, &createInfo, NULL, &gCommandPool)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_COMMAND_POOL_CREATION_FAILED])); + if (VK_SUCCESS != vkCreateCommandPool(gDevice, &createInfo, nullptr, &gCommandPool)) { + LOGF << vulkanErrorMesssages[VULKAN_COMMAND_POOL_CREATION_FAILED]; return VULKAN_ERROR_COMMAND_POOL_CREATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } -static int allocateCommandBuffers() +static uint8_t allocateCommandBuffers() { VkCommandBufferAllocateInfo allocateInfo; /* Allocate memory for command buffers */ - gpCommandBuffers = malloc(sizeof(VkCommandBuffer) * SWAP_BUFFER_COUNT); - + gpCommandBuffers.resize(SWAP_BUFFER_COUNT); allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.pNext = NULL; + allocateInfo.pNext = nullptr; allocateInfo.commandPool = gCommandPool; allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; /* TODO: When needed, add secondary command buffer */ allocateInfo.commandBufferCount = SWAP_BUFFER_COUNT; - if (VK_SUCCESS != vkAllocateCommandBuffers(gDevice, &allocateInfo, gpCommandBuffers)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED])); + if (VK_SUCCESS != vkAllocateCommandBuffers(gDevice, &allocateInfo, gpCommandBuffers.data())) { + LOGF << vulkanErrorMesssages[VULKAN_COMMAND_BUFFER_ALLOCATION_FAILED]; return VULKAN_ERROR_COMMAND_BUFFER_ALLOCATION_FAILED; } return VULKAN_ERROR_NO_ERROR; } -static int createSwapchain() +static uint8_t createSwapchain() { VkSwapchainCreateInfoKHR createInfo; VkSurfaceCapabilitiesKHR physicalDeviceSurfaceCapabilities; @@ -501,17 +653,22 @@ static int createSwapchain() vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gPhysicalDevice, gSurface, &physicalDeviceSurfaceCapabilities); + /* Store extent in global variable, for later use */ + gSwapchainExtent = physicalDeviceSurfaceCapabilities.currentExtent; + /* Check if device supports surface for presentation */ vkGetPhysicalDeviceSurfaceSupportKHR(gPhysicalDevice, gQueueFamilyIndices[0], gSurface, &surfaceSupported); + + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.pNext = NULL; + createInfo.pNext = nullptr; createInfo.flags = 0U; createInfo.surface = gSurface; - createInfo.minImageCount = SWAP_BUFFER_COUNT; + createInfo.minImageCount = SWAP_BUFFER_COUNT + 1; createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB; /* TODO: Check for supported formats */ createInfo.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; /* TODO: Check for supported color space */ - createInfo.imageExtent = physicalDeviceSurfaceCapabilities.currentExtent; + createInfo.imageExtent = gSwapchainExtent; createInfo.imageArrayLayers = 1U; /* For non-stereoscopic-3D applications, this value is 1. */ createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; /* TODO: Probably change later? Dunno */ createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; /* Using only one queue family, so this is ok */ @@ -523,29 +680,29 @@ static int createSwapchain() createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; - if (VK_SUCCESS != vkCreateSwapchainKHR(gDevice, &createInfo, NULL, &gSwapchain)) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED])); + if (VK_SUCCESS != vkCreateSwapchainKHR(gDevice, &createInfo, nullptr, &gSwapchain)) { + LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_CREATION_FAILED]; return VULKAN_ERROR_SWAPCHAIN_CREATION_FAILED; } /* Obtain swapchain images */ - vkGetSwapchainImagesKHR(gDevice, gSwapchain, &gSwapchainImagesCount, NULL); - gpSwapChainImages = malloc(sizeof(VkImage) * gSwapchainImagesCount); - vkGetSwapchainImagesKHR(gDevice, gSwapchain, &gSwapchainImagesCount, gpSwapChainImages); + vkGetSwapchainImagesKHR(gDevice, gSwapchain, &gSwapchainImagesCount, nullptr); + gSwapChainImages.resize(gSwapchainImagesCount); + vkGetSwapchainImagesKHR(gDevice, gSwapchain, &gSwapchainImagesCount, gSwapChainImages.data()); return VULKAN_ERROR_NO_ERROR; } -static int createSwapchainImageViews() +static uint8_t createSwapchainImageViews() { - gpSwapChainImageViews = malloc(sizeof(VkImageView) * gSwapchainImagesCount); + gSwapChainImageViews.resize(gSwapchainImagesCount); for (size_t i = 0; i < gSwapchainImagesCount; i++) { VkImageViewCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.pNext = NULL; + createInfo.pNext = nullptr; createInfo.flags = 0U; - createInfo.image = gpSwapChainImages[i]; + createInfo.image = gSwapChainImages[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; /* 2D Texture */ createInfo.format = VK_FORMAT_B8G8R8A8_SRGB; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; @@ -558,9 +715,9 @@ static int createSwapchainImageViews() createInfo.subresourceRange.baseArrayLayer = 0U; createInfo.subresourceRange.layerCount = 1U; - if(VK_SUCCESS != vkCreateImageView(gDevice, &createInfo, NULL, &gpSwapChainImageViews[i])) + if(VK_SUCCESS != vkCreateImageView(gDevice, &createInfo, nullptr, &gSwapChainImageViews[i])) { - LOG(FATAL, "%s", LOCALIZE(vulkanErrorMesssages[VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED])); + LOGF << vulkanErrorMesssages[VULKAN_SWAPCHAIN_IMVIEW_CREATION_FAILED]; return VULKAN_ERROR_SWAPCHAIN_IMVIEW_CREATION_FAILED; } } @@ -568,9 +725,487 @@ static int createSwapchainImageViews() return VULKAN_ERROR_NO_ERROR; } -int initVulkan() +static std::vector readFile(const std::string& fileName) { - int status = VULKAN_ERROR_NO_ERROR; + std::ifstream file{fileName, std::ios::ate | std::ios::binary}; + if (!file.is_open()) + { + return {}; + } + + /* Get file size and create data buffer */ + size_t fileSize = static_cast(file.tellg()); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + + return buffer; +} + + +static std::optional createShaderModule(const std::vector& binaryData) +{ + VkShaderModule shaderModule; + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0U; + createInfo.codeSize = binaryData.size(); + createInfo.pCode = reinterpret_cast(binaryData.data()); + + if(VK_SUCCESS != vkCreateShaderModule(gDevice, &createInfo, nullptr, &shaderModule)) + { + LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_CREATE_SHADER]; + return std::nullopt; + } + + return shaderModule; +} + +static uint8_t fillShaderStages(std::vector* shaderStages) +{ + + auto vertexShaderCode = readFile(gShaderPaths.at(ShaderStage::VERTEX)); + auto fragmentsShaderCode = readFile(gShaderPaths.at(ShaderStage::FRAGMENTS)); + if (0 == vertexShaderCode.size() || 0 == fragmentsShaderCode.size()) + { + LOGF << vulkanErrorMesssages[VULKAN_SHADER_UNABLE_TO_OPEN]; + return VULKAN_ERROR_SHADER_FILE_OPEN_FAILED; + } + + std::optional vertexShaderModule = createShaderModule(vertexShaderCode); + std::optional fragmentsShaderModule = createShaderModule(fragmentsShaderCode); + + if (!vertexShaderModule.has_value() || !fragmentsShaderModule.has_value()) + { + return VULKAN_ERROR_SHADER_CREATION_FAILED; + } + + /* Vertex Shader Pipeline Stage */ + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.pNext = nullptr; + vertShaderStageInfo.flags = 0U; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertexShaderModule.value(); + vertShaderStageInfo.pName = "main"; /* Entry point function */ + vertShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */ + + /* Fragments Shader Pipeline Stage */ + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.pNext = nullptr; + fragShaderStageInfo.flags = 0U; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragmentsShaderModule.value(); + fragShaderStageInfo.pName = "main"; /* Entry point function */ + fragShaderStageInfo.pSpecializationInfo = nullptr; /* Specialization in a SPIR-V module */ + + shaderStages->push_back(vertShaderStageInfo); + shaderStages->push_back(fragShaderStageInfo); + + // FIXME: Actually use those shader stages + gShaderModules.push_back(std::move(vertexShaderModule.value())); + gShaderModules.push_back(std::move(fragmentsShaderModule.value())); + + return VULKAN_ERROR_NO_ERROR; +} + +static void fillDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState) +{ + /* + * Select pipeline stages that will be set dynamically. Those that are selected are ignored in + * pipeline create structures. I'll list ignored members of structs, that will be ignored due to the + * settings and also functions, that will need to be called to set specific state. + */ + std::vector dynamicStates { + VK_DYNAMIC_STATE_VIEWPORT, /* VkPipelineViewportStateCreateInfo:pViewport; vkCmdSetViewport() */ + VK_DYNAMIC_STATE_SCISSOR /* VkPipelineViewportStateCreateInfo:pScissors; vkCmdSetScissors() */ + }; + + dynamicState->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState->pNext = nullptr; + dynamicState->flags = 0U; + dynamicState->dynamicStateCount = static_cast(dynamicStates.size()); + + /* dynamicState is constructed localy. To avoid creation of VkPipelineDynamicStateCreateInfo structure in + * main pipeline construction function, we have to do a little memory trickery here and allocate memory for + * dynamic state pointer manually. We must also free this memory later */ + VkDynamicState* tmp = new VkDynamicState[dynamicStates.size()]; + std::copy(dynamicStates.begin(), dynamicStates.end(), tmp); + dynamicState->pDynamicStates = tmp; +} + +static void freeDynamicPipelineStateInfo(VkPipelineDynamicStateCreateInfo* dynamicState) +{ + delete[] dynamicState->pDynamicStates; +} + +static void fillVertexInputInfo(VkPipelineVertexInputStateCreateInfo* vertexInputInfo) +{ + vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo->pNext = nullptr; + vertexInputInfo->flags = 0U; + vertexInputInfo->vertexBindingDescriptionCount = 0U; + vertexInputInfo->pVertexBindingDescriptions = nullptr; + vertexInputInfo->vertexAttributeDescriptionCount = 0U; + vertexInputInfo->pVertexAttributeDescriptions = nullptr; + + // FIXME: Shaders have hardcoded vertices for now. Change this later +} + +static void fillInputAssemblyInfo(VkPipelineInputAssemblyStateCreateInfo* inputAssembly) +{ + inputAssembly->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly->pNext = nullptr; + inputAssembly->flags = 0U; + inputAssembly->topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; //TODO: Change to _STRIP + inputAssembly->primitiveRestartEnable = VK_FALSE; /* If I want to break lines during _STRIP, + * I must set this to VK_TRUE */ + } + +static void fillViewportInfo(VkPipelineViewportStateCreateInfo* viewportState) +{ + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(gSwapchainExtent.width); + viewport.height = static_cast(gSwapchainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + /* Setting Scissors to fill entire window*/ + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = gSwapchainExtent; + + viewportState->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState->viewportCount = 1; + viewportState->pViewports = new VkViewport(viewport); + viewportState->scissorCount = 1; + viewportState->pScissors = new VkRect2D(scissor); +} + +static void freeViewportInfo(VkPipelineViewportStateCreateInfo* viewportState) +{ + delete viewportState->pViewports; + delete viewportState->pScissors; +} + +static void fillRasterizationInfo(VkPipelineRasterizationStateCreateInfo* rasterizer) +{ + rasterizer->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer->pNext = nullptr; + rasterizer->flags = 0U; + rasterizer->depthClampEnable = VK_FALSE; /* VK_TRUE to play with shadow maps */ + rasterizer->rasterizerDiscardEnable = VK_FALSE; /* Disable/Enable output to framebuffer*/ + rasterizer->polygonMode = VK_POLYGON_MODE_FILL; + rasterizer->cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer->frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer->depthBiasEnable = VK_FALSE; + rasterizer->depthBiasConstantFactor = 0.0f; + rasterizer->depthBiasClamp = 0.0f; + rasterizer->depthBiasSlopeFactor = 0.0f; + rasterizer->lineWidth = 1.0f; +} + +static void fillMultisamplingInfo(VkPipelineMultisampleStateCreateInfo* multisampling) +{ + multisampling->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling->pNext = nullptr; + multisampling->flags = 0U; + multisampling->rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling->sampleShadingEnable = VK_FALSE; + multisampling->minSampleShading = 1.0f; + multisampling->pSampleMask = nullptr; + multisampling->alphaToCoverageEnable = VK_FALSE; + multisampling->alphaToOneEnable = VK_FALSE; +} + +// static void setDepthAndStencilTesting() +// { +// //TODO: Fill +// } + +static void fillColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending) +{ + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; + + colorBlending->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending->pNext = nullptr; + colorBlending->flags = 0U; + colorBlending->logicOpEnable = VK_FALSE; + colorBlending->logicOp = VK_LOGIC_OP_COPY; + colorBlending->attachmentCount = 1U; + colorBlending->pAttachments = new VkPipelineColorBlendAttachmentState(colorBlendAttachment); + colorBlending->blendConstants[0] = 0.0f; + colorBlending->blendConstants[1] = 0.0f; + colorBlending->blendConstants[2] = 0.0f; + colorBlending->blendConstants[3] = 0.0f; +} + +static void freeColorBlendInfo(VkPipelineColorBlendStateCreateInfo* colorBlending) +{ + delete colorBlending->pAttachments; +} + + +static uint8_t createRenderPass() +{ + VkAttachmentDescription colorAttachment{}; + colorAttachment.flags = 0U; + colorAttachment.format = VK_FORMAT_B8G8R8A8_SRGB; //FIXME: This is from swapchain. Change, so it won't break when swapchain uses different format + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + if (VK_SUCCESS != vkCreateRenderPass(gDevice, &renderPassInfo, nullptr, &gRenderPass)) { + LOGF << vulkanErrorMesssages[VULKAN_RENDERPASS_CREATION_FAILED]; + return VULKAN_ERROR_RENDERPASS_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +static uint8_t createPipelineLayout() +{ + VkPipelineLayoutCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0U; + createInfo.setLayoutCount = 0; + createInfo.pSetLayouts = nullptr; + createInfo.pushConstantRangeCount = 0U; + createInfo.pPushConstantRanges = nullptr; + + + if(VK_SUCCESS != vkCreatePipelineLayout(gDevice, &createInfo, nullptr, &gPipelineLayout)) + { + LOGF << vulkanErrorMesssages[VULKAN_PIPELAYOUT_CREATION_FAILED]; + return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +static uint8_t createGraphicsPipeline() +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; + + std::vector shaderStages{}; + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + VkPipelineViewportStateCreateInfo viewportState{}; + VkPipelineRasterizationStateCreateInfo rasterizer{}; + VkPipelineMultisampleStateCreateInfo multisampling{}; + VkPipelineColorBlendStateCreateInfo colorBlending{}; + VkPipelineDynamicStateCreateInfo dynamicState{}; + + STATUS_CHECK(fillShaderStages(&shaderStages)); + fillVertexInputInfo(&vertexInputInfo); + fillInputAssemblyInfo(&inputAssembly); + fillViewportInfo(&viewportState); + fillRasterizationInfo(&rasterizer); + fillMultisamplingInfo(&multisampling); + fillColorBlendInfo(&colorBlending); + fillDynamicPipelineStateInfo(&dynamicState); + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.pNext = nullptr; + pipelineInfo.flags = 0U; + pipelineInfo.stageCount = shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pTessellationState = nullptr; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = gPipelineLayout; + pipelineInfo.renderPass = gRenderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (VK_SUCCESS != vkCreateGraphicsPipelines(gDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &gGraphicsPipeline)) + { + LOGF << vulkanErrorMesssages[VULKAN_GRAPHICSPIPELINE_CREATION_FAILED]; + status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED; + } + + freeDynamicPipelineStateInfo(&dynamicState); + freeViewportInfo(&viewportState); + freeColorBlendInfo(&colorBlending); + + return status; +} + +static uint8_t createFrambuffers() +{ + gSwapchainFramebuffers.resize(gSwapChainImageViews.size()); + + for (size_t i = 0; i < gSwapChainImageViews.size(); i++) { + VkImageView attachments[] = { + gSwapChainImageViews[i] + }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = gRenderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = gSwapchainExtent.width; + framebufferInfo.height = gSwapchainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(gDevice, &framebufferInfo, nullptr, &gSwapchainFramebuffers[i]) != VK_SUCCESS) { + LOGF << vulkanErrorMesssages[VULKAN_FRAMEBUFFERS_CREATION_FAILED]; + return VULKAN_ERROR_FRAMEBUFFERS_CREATION_FAILED; + } + } + + return VULKAN_ERROR_NO_ERROR; +} + +static uint8_t recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) +{ + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; // Optional + beginInfo.pInheritanceInfo = nullptr; // Optional + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = gRenderPass; + renderPassInfo.framebuffer = gSwapchainFramebuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = gSwapchainExtent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, gGraphicsPipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(gSwapchainExtent.width); + viewport.height = static_cast(gSwapchainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = gSwapchainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer)) { + LOGF << vulkanErrorMesssages[VULKAN_RECORD_COMMAND_BUFFER_FAILED]; + return VULKAN_ERROR_RECORD_COMMAND_BUFFER_FAILED; + } + + return VULKAN_ERROR_NO_ERROR; +} + +static uint8_t createSyncObjects() +{ + gImageAvailableSemaphores.resize(SWAP_BUFFER_COUNT); + gRenderFinishedSemaphores.resize(SWAP_BUFFER_COUNT); + gInFlightFences.resize(SWAP_BUFFER_COUNT); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) + { + if (VK_SUCCESS != vkCreateSemaphore(gDevice, &semaphoreInfo, nullptr, &gImageAvailableSemaphores[i]) || + VK_SUCCESS != vkCreateSemaphore(gDevice, &semaphoreInfo, nullptr, &gRenderFinishedSemaphores[i]) || + VK_SUCCESS != vkCreateFence(gDevice, &fenceInfo, nullptr, &gInFlightFences[i])) { + LOGF << vulkanErrorMesssages[VULKAN_SYNCOBJCTS_CREATION_FAILED]; + return VULKAN_ERROR_SYNCOBJCTS_CREATION_FAILED; + } + } + + return VULKAN_ERROR_NO_ERROR; +} + +static void cleanupSwapchain() +{ + vkDeviceWaitIdle(gDevice); + for (size_t i = 0; i < gSwapchainFramebuffers.size(); i++) { + vkDestroyFramebuffer(gDevice, gSwapchainFramebuffers[i], nullptr); + } + + for (size_t i = 0; i < gSwapChainImageViews.size(); i++) { + vkDestroyImageView(gDevice, gSwapChainImageViews[i], nullptr); + } + + vkDestroySwapchainKHR(gDevice, gSwapchain, nullptr); +} + +static void recreateSwapchain() +{ + cleanupSwapchain(); + createSwapchain(); + createSwapchainImageViews(); + createFrambuffers(); +} + +uint8_t initVulkan() +{ + uint8_t status = VULKAN_ERROR_NO_ERROR; STATUS_CHECK(createInstance()); #ifndef NDEBUG @@ -584,22 +1219,99 @@ int initVulkan() STATUS_CHECK(allocateCommandBuffers()); STATUS_CHECK(createSwapchain()); STATUS_CHECK(createSwapchainImageViews()); + STATUS_CHECK(createRenderPass()); + STATUS_CHECK(createPipelineLayout()); + STATUS_CHECK(createGraphicsPipeline()); + STATUS_CHECK(createFrambuffers()); + STATUS_CHECK(createSyncObjects()); return status; } +void drawFrame() +{ + vkWaitForFences(gDevice, 1, &gInFlightFences[gCurrentFrame], VK_TRUE, UINT64_MAX); + + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(gDevice, gSwapchain, UINT64_MAX, gImageAvailableSemaphores[gCurrentFrame], VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + recreateSwapchain(); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + // throw std::runtime_error("failed to acquire swap chain image!"); + return; + } + + /* Only reset the fence if we are submitting work */ + vkResetFences(gDevice, 1, &gInFlightFences[gCurrentFrame]); + + vkResetCommandBuffer(gpCommandBuffers[gCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0); + recordCommandBuffer(gpCommandBuffers[gCurrentFrame], imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {gImageAvailableSemaphores[gCurrentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &gpCommandBuffers[gCurrentFrame]; + + VkSemaphore signalSemaphores[] = {gRenderFinishedSemaphores[gCurrentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(gGraphicsQueue, 1, &submitInfo, gInFlightFences[gCurrentFrame]) != VK_SUCCESS) { + throw std::runtime_error("failed to submit draw command buffer!"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {gSwapchain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + vkQueuePresentKHR(gPresentQueue, &presentInfo); + + gCurrentFrame = (gCurrentFrame + 1) % SWAP_BUFFER_COUNT; +} + void deinitVulkan() { - for (size_t i = 0; i < gSwapchainImagesCount; i++) { - vkDestroyImageView(gDevice ,gpSwapChainImageViews[i], NULL); + cleanupSwapchain(); + + for (size_t i = 0; i < SWAP_BUFFER_COUNT; ++i) { + vkDestroySemaphore(gDevice, gImageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(gDevice, gRenderFinishedSemaphores[i], nullptr); + vkDestroyFence(gDevice, gInFlightFences[i], nullptr); } - vkDestroySwapchainKHR(gDevice, gSwapchain, NULL); - vkDestroyCommandPool(gDevice, gCommandPool, NULL); + vkDestroyPipeline(gDevice, gGraphicsPipeline, nullptr); + vkDestroyPipelineLayout(gDevice, gPipelineLayout, nullptr); + vkDestroyRenderPass(gDevice, gRenderPass, nullptr); + + for (size_t i = 0; i < gShaderModules.size(); i++) { + vkDestroyShaderModule(gDevice, gShaderModules[i], nullptr); + } + + vkDestroyCommandPool(gDevice, gCommandPool, nullptr); vmaDestroyAllocator(gAllocator); - vkDestroyDevice(gDevice, NULL); + vkDestroyDevice(gDevice, nullptr); #ifndef NDEBUG - DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, NULL); + DestroyDebugUtilsMessengerEXT(gVulkanInstance, gDebugMessenger, nullptr); #endif - vkDestroySurfaceKHR(gVulkanInstance, gSurface, NULL); - vkDestroyInstance(gVulkanInstance, NULL); + vkDestroySurfaceKHR(gVulkanInstance, gSurface, nullptr); + vkDestroyInstance(gVulkanInstance, nullptr); } + +} // namespace rse::graphics::vulkanbase \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index 6d83e2a8..54067932 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -9,10 +9,16 @@ * */ -#include "window.h" +#include "window.hpp" -#include "localeWindow.h" -#include "log.h" +#include "localeWindow.hpp" +#include "logger.hpp" +#include "vulkanBase.hpp" + +namespace rse::graphics::window +{ + +using namespace locale::window; /* Error codes */ typedef enum { @@ -33,7 +39,7 @@ GLFWwindow *gpWindowHandle = NULL; static void errrorCallback(int error, const char* description) { (void)error; - LOG(ERROR, "Error: %s", description); + LOGE << "Error: " << description; } /** @@ -55,19 +61,20 @@ static void keyCallback(GLFWwindow* window, int key, int scancode, int action, i } } + uint16_t windowInit() { /* Initiliaze GLFW AP */ if (GLFW_TRUE != glfwInit()) { - LOG(FATAL, "%s", LOCALIZE(windowErrorMesssages[WINDOW_GLFW_INIT_FAILED])); + LOGF << windowErrorMesssages[WINDOW_GLFW_INIT_FAILED]; return WINDOW_ERROR_GLFW_INIT_FAILED; } /* Check for Vulkan support */ if (GLFW_FALSE == glfwVulkanSupported()) { - LOG(FATAL, "%s", LOCALIZE(windowErrorMesssages[WINDOW_VULKAN_NOT_SUPPORTED])); + LOGF << windowErrorMesssages[WINDOW_VULKAN_NOT_SUPPORTED]; return WINDOW_ERROR_VULKAN_NOT_LOADED; } @@ -80,7 +87,7 @@ uint16_t windowInit() gpWindowHandle = glfwCreateWindow(640, 480, "RedScarfEngine", NULL, NULL); if (NULL == gpWindowHandle) { - LOG(FATAL, "%s", LOCALIZE(windowErrorMesssages[WINDOW_NOT_CREATED])); + LOGF << windowErrorMesssages[WINDOW_NOT_CREATED]; return WINDOW_ERROR_WINDOW_NOT_CREATED; } @@ -93,6 +100,7 @@ uint16_t windowLoop() { while (!glfwWindowShouldClose(gpWindowHandle)) { glfwPollEvents(); + rse::graphics::vulkanbase::drawFrame(); } return WINDOW_ERROR_NO_ERROR; @@ -108,4 +116,6 @@ void windowTerminate() GLFWwindow* getWindowHandle() { return gpWindowHandle; -} \ No newline at end of file +} + +} /* namespace rse::graphics::window */ \ No newline at end of file