From 021869a3a182c12ffb0b42d50f836133d5a7fa0a Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Tue, 13 Sep 2022 22:43:24 +0100 Subject: [PATCH 01/50] Do vulkan work --- CMakeLists.txt | 33 +++--- include/gfx_device.hpp | 2 +- include/window.hpp | 18 ---- src/components/camera.cpp | 2 +- src/gfx_device_vulkan.cpp | 214 ++++++++++++++++++++++++++++++++------ src/window.cpp | 52 +-------- 6 files changed, 202 insertions(+), 119 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1180d53..f7ea71b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,10 +98,20 @@ if (MINGW) target_link_libraries(${PROJECT_NAME} PUBLIC mingw32) endif() -# Vulkan -find_package(Vulkan REQUIRED) -target_include_directories(${PROJECT_NAME} PRIVATE ${Vulkan_INCLUDE_DIRS}) -target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Vulkan) +# GLAD: +set(GLAD_PROFILE "core" CACHE INTERNAL "" FORCE) +set(GLAD_API "gl=3.3" CACHE INTERNAL "" FORCE) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(GLAD_GENERATOR "c-debug" CACHE INTERNAL "" FORCE) +else() + set(GLAD_GENERATOR "c" CACHE INTERNAL "" FORCE) +endif() +set(GLAD_SPEC "gl" CACHE INTERNAL "" FORCE) +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(dependencies/glad) +set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(${PROJECT_NAME} PUBLIC glad) +target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include) # SDL2: find_package(SDL2) @@ -124,21 +134,6 @@ set(BUILD_SHARED_LIBS OFF) add_subdirectory(dependencies/glm) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glm) -# GLAD: -set(GLAD_PROFILE "core" CACHE INTERNAL "" FORCE) -set(GLAD_API "gl=3.3" CACHE INTERNAL "" FORCE) -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(GLAD_GENERATOR "c-debug" CACHE INTERNAL "" FORCE) -else() - set(GLAD_GENERATOR "c" CACHE INTERNAL "" FORCE) -endif() -set(GLAD_SPEC "gl" CACHE INTERNAL "" FORCE) -set(BUILD_SHARED_LIBS OFF) -add_subdirectory(dependencies/glad) -set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON) -target_link_libraries(${PROJECT_NAME} PUBLIC glad) -target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include) - # spdlog set(SPDLOG_BUILD_SHARED ON CACHE INTERNAL "" FORCE) set(BUILD_SHARED_LIBS ON) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 3a454e5..b5288d1 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -15,7 +15,7 @@ namespace engine::gfx { private: class Impl; - std::unique_ptr pimpl{}; + std::unique_ptr pimpl; }; diff --git a/include/window.hpp b/include/window.hpp index 1079d83..2633ad6 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -28,25 +28,10 @@ public: // Return the title name std::string getTitle() const; - // Make this window the current OpenGL context. - // This is already done in window initialisation. - void makeContextCurrent(); - - // Tell the GPU to render the back buffer to the screen. - // Run this on every frame. - void swapBuffers(); // Update the window state to capture any events that have occurred. // Run this on every frame. void getInputAndEvents(); - // if 'true', swapBuffers() will wait in order to synchronise with the - // monitor's refresh rate. - void setVSync(bool enable); - // Returns true if VSync is enabled. - bool getVSync() const; - - glm::ivec2 getViewportSize(); - void setTitle(std::string title); // Hides the window (it will appear closed to the user). @@ -146,7 +131,6 @@ public: private: SDL_Window* m_handle; - SDL_GLContext m_glContext; bool m_shouldClose = false; @@ -158,8 +142,6 @@ public: // size in screen coordinates glm::ivec2 m_winSize = glm::vec2(640, 480); - // actual framebuffer size - glm::ivec2 m_fbSize; // performance counter frequency uint64_t m_counterFreq; diff --git a/src/components/camera.cpp b/src/components/camera.cpp index edc035b..3c83fdc 100644 --- a/src/components/camera.cpp +++ b/src/components/camera.cpp @@ -54,7 +54,7 @@ void Camera::updateCam(glm::mat4 transform) auto shader = dynamic_cast(lockedPtr.get()); shader->setUniform_m4(VIEW_MAT_UNIFORM, viewMatrix); shader->setUniform_m4(PROJ_MAT_UNIFORM, m_projMatrix); - shader->setUniform_v2(WINDOW_SIZE_UNIFORM, win.getViewportSize()); +// shader->setUniform_v2(WINDOW_SIZE_UNIFORM, win.getViewportSize()); shader->setUniform_v3("lightPos", m_lightPos); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 67448e3..7db4f4e 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -6,10 +6,14 @@ #include "window.hpp" +#include "log.hpp" + #include #include + #include +#include namespace engine::gfx { @@ -17,48 +21,192 @@ namespace engine::gfx { friend Device; VkInstance m_instance; +#ifndef NDEBUG + VkDebugUtilsMessengerEXT m_debugMessenger; +#endif + + static constexpr const char * VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + + std::vector m_layersAvailable{}; + std::optional::iterator> m_validationLayer; + + + + void findAvailableLayers() + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + m_layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); + assert(res == VK_SUCCESS); + +#ifndef NDEBUG + for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { + TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); + if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { + m_validationLayer = it; + } + } + if (m_validationLayer.has_value() == false) { + WARN("The validation layer was not found. Continuing."); + } +#endif + } + + void createInstance(AppInfo appInfo, const std::vector& windowExtensions) + { + VkResult res; + + int appVersionMajor, appVersionMinor, appVersionPatch; + assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + int engineVersionMajor, engineVersionMinor, engineVersionPatch; + assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); + + VkApplicationInfo applicationInfo { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = appInfo.name, + .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), + .pEngineName = "engine", + .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), + .apiVersion = VK_API_VERSION_1_0, + }; + + // make a list of all extensions to use + std::vector extensions{}; + extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); +#ifndef NDEBUG + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif + + std::vector layers{}; + +#ifndef NDEBUG + if (m_validationLayer.has_value()) + layers.push_back(m_validationLayer.value()->layerName); +#endif + + VkInstanceCreateInfo instanceInfo { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = (uint32_t)layers.size(), + .ppEnabledLayerNames = layers.data(), + .enabledExtensionCount = (uint32_t)extensions.size(), + .ppEnabledExtensionNames = extensions.data(), + }; + + res = vkCreateInstance(&instanceInfo, nullptr, &m_instance); + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + CRITICAL("The graphics driver is incompatible with vulkan"); + throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + } + assert(res == VK_SUCCESS); + } + +#ifndef NDEBUG + static VkBool32 debugMessenger( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + + std::string msgType{}; + + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (GENERAL)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + msgType += " (PERF.)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (VALID.)"; + + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } +#endif + + void createDebugMessenger() + { +#ifndef NDEBUG + VkResult res; + + if (m_validationLayer.has_value() == false) return; + + VkDebugUtilsMessengerCreateInfoEXT createInfo { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .messageSeverity = 0, + .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, + .pfnUserCallback = debugMessenger, + .pUserData = nullptr, + }; + + switch (MESSAGE_LEVEL) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + // fall-through + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + // fall-through + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + // fall-through + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + // fall-through + default: + break; + } + + res = vkCreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger); + assert(res == VK_SUCCESS); +#endif + } + + }; - Device::Device(AppInfo appInfo, const Window& window) + + + Device::Device(AppInfo appInfo, const Window& window) : pimpl(std::make_unique()) { - VkResult res; - - int appVersionMajor, appVersionMinor, appVersionPatch; - assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor, engineVersionMinor, engineVersionPatch; - assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); - - VkApplicationInfo applicationInfo{ - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = appInfo.name, - .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), - .pEngineName = "engine", - .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_VERSION_1_0, - }; - - VkInstanceCreateInfo instanceInfo{ - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = nullptr, - .enabledLayerCount = 0, - .ppEnabledLayerNames = nullptr, - .enabledExtensionCount = 0, - .ppEnabledExtensionNames = nullptr, - }; - - res = vkCreateInstance(&instanceInfo, nullptr, &pimpl->m_instance); - std::cout << "ERROR CODE: " << res << std::endl; - assert(res == VK_SUCCESS); - + pimpl->findAvailableLayers(); + pimpl->createInstance(appInfo, window.getRequiredVulkanExtensions()); + pimpl->createDebugMessenger(); } Device::~Device() { vkDestroyInstance(pimpl->m_instance, nullptr); +#ifndef NDEBUG + vkDestroyDebugUtilsMessengerEXT(pimpl->m_instance, pimpl->m_debugMessenger, nullptr); +#endif } } diff --git a/src/window.cpp b/src/window.cpp index 87a6669..e8e6829 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,7 +1,5 @@ #include "window.hpp" -#include - #include #include @@ -28,17 +26,13 @@ Window::Window(const std::string& title) : m_title(title) m_lastFrameStamp = m_startTime - 1; m_avgFpsStart = m_startTime; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - // create the window m_handle = SDL_CreateWindow( m_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(m_winSize.x), static_cast(m_winSize.y), - SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); + 0); if (m_handle == NULL) { SDL_Quit(); throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); @@ -54,6 +48,7 @@ Window::Window(const std::string& title) : m_title(title) const int WINDOWED_MIN_HEIGHT = 480; SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT); + /* m_glContext = SDL_GL_CreateContext(m_handle); if (m_glContext == NULL) { SDL_DestroyWindow(m_handle); @@ -66,14 +61,14 @@ Window::Window(const std::string& title) : m_title(title) SDL_Quit(); throw std::runtime_error("Unable to initialise GLAD"); } + */ - onResize(m_winSize.x, m_winSize.y); +// onResize(m_winSize.x, m_winSize.y); } Window::~Window() { - SDL_GL_DeleteContext(m_glContext); SDL_DestroyWindow(m_handle); SDL_Quit(); } @@ -86,13 +81,6 @@ void Window::onResize(Sint32 width, Sint32 height) m_winSize.x = static_cast(width); m_winSize.y = static_cast(height); - // get framebuffer size - int fbWidth, fbHeight; - SDL_GL_GetDrawableSize(m_handle, &fbWidth, &fbHeight); - m_fbSize.x = static_cast(fbWidth); - m_fbSize.y = static_cast(fbHeight); - glViewport(0, 0, fbWidth, fbHeight); - m_justResized = true; } @@ -195,26 +183,13 @@ std::string Window::getTitle() const return m_title; } -void Window::makeContextCurrent() +void Window::getInputAndEvents() { - if (SDL_GL_MakeCurrent(m_handle, m_glContext) != 0) { - throw std::runtime_error("Failed to make GL context current"); - } -} -void Window::swapBuffers() -{ -#ifndef SDLTEST_NOGFX - SDL_GL_SwapWindow(m_handle); -#endif m_frames++; uint64_t currentFrameStamp = getNanos(); m_lastFrameTime = currentFrameStamp - m_lastFrameStamp; m_lastFrameStamp = currentFrameStamp; -} - -void Window::getInputAndEvents() -{ resetInputDeltas(); @@ -254,23 +229,6 @@ void Window::getInputAndEvents() } -void Window::setVSync(bool enable) -{ - if (SDL_GL_SetSwapInterval(enable ? 1 : 0) != 0) { - throw std::runtime_error("Failed to set swap interval"); - } -} - -bool Window::getVSync() const -{ - return SDL_GL_GetSwapInterval() == 0 ? false : true; -} - -glm::ivec2 Window::getViewportSize() -{ - return m_fbSize; -} - void Window::setTitle(std::string title) { SDL_SetWindowTitle(m_handle, title.c_str()); From 2c4a47541daff688497aa655ee5493161395ba8c Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Tue, 13 Sep 2022 22:55:08 +0100 Subject: [PATCH 02/50] Add volk --- .gitmodules | 3 +++ CMakeLists.txt | 8 ++++++++ dependencies/volk | 1 + src/gfx_device_vulkan.cpp | 5 ++--- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 160000 dependencies/volk diff --git a/.gitmodules b/.gitmodules index d92b0d2..9e12f3b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "dependencies/freetype"] path = dependencies/freetype url = https://gitlab.freedesktop.org/freetype/freetype.git +[submodule "dependencies/volk"] + path = dependencies/volk + url = https://github.com/zeux/volk diff --git a/CMakeLists.txt b/CMakeLists.txt index f7ea71b..06e68e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,14 @@ set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON) target_link_libraries(${PROJECT_NAME} PUBLIC glad) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include) +# Volk +set(VOLK_STATIC_DEFINES "") +set(VOLK_PULL_IN_VULKAN ON) +set(VOLK_INSTALL OFF) +set(VOLK_HEADERS_ONLY ON) +add_subdirectory(dependencies/volk) +target_link_libraries(${PROJECT_NAME} PRIVATE volk_headers) + # SDL2: find_package(SDL2) if (SDL2_FOUND) diff --git a/dependencies/volk b/dependencies/volk new file mode 160000 index 0000000..121a458 --- /dev/null +++ b/dependencies/volk @@ -0,0 +1 @@ +Subproject commit 121a4584f69056d2c6db2eb4104650ce749d4c72 diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 7db4f4e..5d062ea 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -8,7 +8,8 @@ #include "log.hpp" -#include +#define VOLK_IMPLEMENTATION +#include "volk.h" #include @@ -188,8 +189,6 @@ namespace engine::gfx { #endif } - - }; From 40c0c024a151cfcf650d020aa5294ade3fda6d27 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 17 Sep 2022 01:22:35 +0100 Subject: [PATCH 03/50] gfx work --- include/engine.hpp | 6 - include/gfx_device.hpp | 7 +- include/window.hpp | 109 ++++++----- src/engine.cpp | 2 + src/gfx_device_vulkan.cpp | 397 +++++++++++++++++++++++--------------- src/window.cpp | 22 +-- 6 files changed, 308 insertions(+), 235 deletions(-) diff --git a/include/engine.hpp b/include/engine.hpp index 9d2eaf8..6e77afc 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -1,11 +1,5 @@ #pragma once -#include - -#include "log.hpp" -#include -#include - namespace engine { struct AppInfo { diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index b5288d1..b6ff345 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -4,13 +4,16 @@ #include "engine.hpp" -class Window; +#include + +#include + namespace engine::gfx { struct ENGINE_API Device { - Device(AppInfo appInfo, const Window& window); + Device(AppInfo appInfo, SDL_Window* window); ~Device(); private: diff --git a/include/window.hpp b/include/window.hpp index 2633ad6..a7190a6 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -124,77 +124,76 @@ public: bool infoBox(const std::string& title, const std::string& msg); - std::vector getRequiredVulkanExtensions() const; - /* STATIC METHODS */ static void errorBox(const std::string& message); - private: - SDL_Window* m_handle; +public: + SDL_Window* m_handle; - bool m_shouldClose = false; +private: - std::string m_title; + bool m_shouldClose = false; - bool m_fullscreen = false; - bool m_justResized = false; - bool m_keyboardFocus = true; + std::string m_title; - // size in screen coordinates - glm::ivec2 m_winSize = glm::vec2(640, 480); + bool m_fullscreen = false; + bool m_justResized = false; + bool m_keyboardFocus = true; - // performance counter frequency - uint64_t m_counterFreq; + // size in screen coordinates + glm::ivec2 m_winSize = glm::vec2(640, 480); - // number of frames swapped - uint64_t m_frames = 0; - // frame count offset for fpsAvg - uint64_t m_avgFpsStartCount = 0; - // in nanoseconds - uint64_t m_startTime; - // in nanoseconds - uint64_t m_lastFrameStamp; - // in nanoseconds; elapsed time between frames - uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero - // in nanoseconds - uint64_t m_avgFpsStart; + // performance counter frequency + uint64_t m_counterFreq; - // input stuff + // number of frames swapped + uint64_t m_frames = 0; + // frame count offset for fpsAvg + uint64_t m_avgFpsStartCount = 0; + // in nanoseconds + uint64_t m_startTime; + // in nanoseconds + uint64_t m_lastFrameStamp; + // in nanoseconds; elapsed time between frames + uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero + // in nanoseconds + uint64_t m_avgFpsStart; - enum class ButtonDelta { - SAME = 0, - PRESSED, - RELEASED - }; + // input stuff - struct { - std::array keys; - std::array deltas; - } m_keyboard{ }; + enum class ButtonDelta { + SAME = 0, + PRESSED, + RELEASED + }; - struct { - std::array(inputs::MouseButton::M_SIZE)> buttons; - std::array deltas; - Sint32 x; - Sint32 y; - Sint32 dx; - Sint32 dy; - float xscroll; - float yscroll; - bool captured = false; - } m_mouse{ }; + struct { + std::array keys; + std::array deltas; + } m_keyboard{ }; - // private methods + struct { + std::array(inputs::MouseButton::M_SIZE)> buttons; + std::array deltas; + Sint32 x; + Sint32 y; + Sint32 dx; + Sint32 dy; + float xscroll; + float yscroll; + bool captured = false; + } m_mouse{ }; - void onResize(Sint32 width, Sint32 height); - void resetInputDeltas(); + // private methods - // event methods (like callbacks) + void onResize(Sint32 width, Sint32 height); + void resetInputDeltas(); - void onWindowEvent(SDL_WindowEvent& e); - void onKeyEvent(SDL_KeyboardEvent& e); - void onMouseButtonEvent(SDL_MouseButtonEvent& e); - void onMouseMotionEvent(SDL_MouseMotionEvent& e); - void onMouseWheelEvent(SDL_MouseWheelEvent& e); + // event methods (like callbacks) + void onWindowEvent(SDL_WindowEvent& e); + void onKeyEvent(SDL_KeyboardEvent& e); + void onMouseButtonEvent(SDL_MouseButtonEvent& e); + void onMouseMotionEvent(SDL_MouseMotionEvent& e); + void onMouseWheelEvent(SDL_MouseWheelEvent& e); }; diff --git a/src/engine.cpp b/src/engine.cpp index 0f50ff9..c13c74e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,5 +1,7 @@ #include "engine.hpp" +#include + namespace engine { bool versionFromCharArray(const char* version, int* major, int* minor, int* patch) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 5d062ea..2e06cd2 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -3,14 +3,13 @@ #include "gfx_device.hpp" #include "config.h" - -#include "window.hpp" - #include "log.hpp" #define VOLK_IMPLEMENTATION #include "volk.h" +#include + #include #include @@ -18,194 +17,286 @@ namespace engine::gfx { + static std::vector getRequiredVulkanExtensions(SDL_Window* window) + { +#ifdef ENGINE_BUILD_VULKAN + SDL_bool res; + + unsigned int sdlExtensionCount = 0; + res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr); + assert(res == SDL_TRUE); + std::vector requiredExtensions(sdlExtensionCount); + res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, requiredExtensions.data()); + assert(res == SDL_TRUE); + + return requiredExtensions; +#else + return std::vector{}; +#endif + } + + static VkBool32 debugMessenger( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + + std::string msgType{}; + + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (GENERAL)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + msgType += " (PERF.)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (VALID.)"; + + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } + class Device::Impl { friend Device; - VkInstance m_instance; -#ifndef NDEBUG - VkDebugUtilsMessengerEXT m_debugMessenger; -#endif - - static constexpr const char * VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; - static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - - std::vector m_layersAvailable{}; - std::optional::iterator> m_validationLayer; - - - - void findAvailableLayers() + public: + Impl(AppInfo appInfo, SDL_Window* window) { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - m_layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); - assert(res == VK_SUCCESS); - + m_instance = std::make_unique(appInfo, getRequiredVulkanExtensions(window)); #ifndef NDEBUG - for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { - TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); - if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { - m_validationLayer = it; + m_debugMessenger = std::make_unique(m_instance->getHandle()); +#endif + } + + private: + +// VkSurfaceKHR m_surface; + + static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + + class Instance { + + public: + Instance(AppInfo appInfo, const std::vector& windowExtensions) + { + VkResult res; + + findAvailableLayers(); + + int appVersionMajor, appVersionMinor, appVersionPatch; + assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + int engineVersionMajor, engineVersionMinor, engineVersionPatch; + assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); + + VkApplicationInfo applicationInfo{ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = appInfo.name, + .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), + .pEngineName = "engine", + .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), + .apiVersion = VK_API_VERSION_1_0, + }; + + // make a list of all extensions to use + std::vector extensions{}; + extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + + std::vector layers{}; + + if (m_validationLayer.has_value()) { + layers.push_back(m_validationLayer.value()->layerName); + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - } - if (m_validationLayer.has_value() == false) { - WARN("The validation layer was not found. Continuing."); - } -#endif - } - void createInstance(AppInfo appInfo, const std::vector& windowExtensions) - { - VkResult res; + VkInstanceCreateInfo instanceInfo{ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = (uint32_t)layers.size(), + .ppEnabledLayerNames = layers.data(), + .enabledExtensionCount = (uint32_t)extensions.size(), + .ppEnabledExtensionNames = extensions.data(), + }; - int appVersionMajor, appVersionMinor, appVersionPatch; - assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor, engineVersionMinor, engineVersionPatch; - assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); - - VkApplicationInfo applicationInfo { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = appInfo.name, - .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), - .pEngineName = "engine", - .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_API_VERSION_1_0, - }; - - // make a list of all extensions to use - std::vector extensions{}; - extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); -#ifndef NDEBUG - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); -#endif - - std::vector layers{}; + if (m_validationLayer.has_value()) { + VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); + instanceInfo.pNext = &debugMessengerCreateInfo; + } #ifndef NDEBUG - if (m_validationLayer.has_value()) - layers.push_back(m_validationLayer.value()->layerName); + for (const char* ext : extensions) { + TRACE("Using Vulkan instance extension: {}", ext); + } #endif - VkInstanceCreateInfo instanceInfo { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &applicationInfo, - .enabledLayerCount = (uint32_t)layers.size(), - .ppEnabledLayerNames = layers.data(), - .enabledExtensionCount = (uint32_t)extensions.size(), - .ppEnabledExtensionNames = extensions.data(), - }; - - res = vkCreateInstance(&instanceInfo, nullptr, &m_instance); - if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - CRITICAL("The graphics driver is incompatible with vulkan"); - throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + CRITICAL("The graphics driver is incompatible with vulkan"); + throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + } + assert(res == VK_SUCCESS); + + volkLoadInstance(m_handle); + } - assert(res == VK_SUCCESS); - } + + ~Instance() + { + vkDestroyInstance(m_handle, nullptr); + } + + VkInstance getHandle() + { + return m_handle; + } + + private: + + VkInstance m_handle; + + std::vector m_layersAvailable{}; + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + std::optional::iterator> m_validationLayer; + + void findAvailableLayers() + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + m_layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); + assert(res == VK_SUCCESS); #ifndef NDEBUG - static VkBool32 debugMessenger( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { + // find validation layer and print all layers to log + for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { + // TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); + if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { + m_validationLayer = it; + } + } + if (m_validationLayer.has_value() == false) { + CRITICAL("The validation layer was not found. Quitting."); + throw std::runtime_error("Validation layer not found"); + } +#endif + } - std::string msgType{}; + }; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (GENERAL)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) - msgType += " (PERF.)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (VALID.)"; +#ifndef NDEBUG + class DebugMessenger { - switch (messageSeverity) { + public: + DebugMessenger(const VkInstance& instance) : m_instance(instance) + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); + + VkResult res = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &m_debugMessenger); + assert(res == VK_SUCCESS); + } + + ~DebugMessenger() + { + vkDestroyDebugUtilsMessengerEXT(m_instance, m_debugMessenger, nullptr); + } + + static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() + { + VkDebugUtilsMessengerCreateInfoEXT createInfo{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .messageSeverity = 0, + .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, + .pfnUserCallback = debugMessenger, + .pUserData = nullptr, + }; + + switch (MESSAGE_LEVEL) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + // fall-through default: break; + } + + return createInfo; } - return VK_FALSE; - } + + private: + VkDebugUtilsMessengerEXT m_debugMessenger; + const VkInstance& m_instance; + + }; #endif - void createDebugMessenger() - { + std::unique_ptr m_instance; #ifndef NDEBUG - VkResult res; - - if (m_validationLayer.has_value() == false) return; - - VkDebugUtilsMessengerCreateInfoEXT createInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .messageSeverity = 0, - .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, - .pfnUserCallback = debugMessenger, - .pUserData = nullptr, - }; - - switch (MESSAGE_LEVEL) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - // fall-through - default: - break; - } - - res = vkCreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger); - assert(res == VK_SUCCESS); + std::unique_ptr m_debugMessenger; #endif + +/* + + void createSurface(SDL_Window* window) + { + if (SDL_Vulkan_CreateSurface(window, m_instance, &m_surface) == false) { + CRITICAL("Unable to create window surface"); + throw std::runtime_error("Unable to create window surface"); + } } +*/ }; - - - Device::Device(AppInfo appInfo, const Window& window) : pimpl(std::make_unique()) + Device::Device(AppInfo appInfo, SDL_Window* window) { - pimpl->findAvailableLayers(); - pimpl->createInstance(appInfo, window.getRequiredVulkanExtensions()); - pimpl->createDebugMessenger(); + VkResult res; + res = volkInitialize(); + if (res == VK_ERROR_INITIALIZATION_FAILED) { + CRITICAL("Unable to load vulkan, is it installed?"); + throw std::runtime_error("Unable to load vulkan, is it installed?"); + } + assert(res == VK_SUCCESS); + + pimpl = std::make_unique(appInfo, window); + + //pimpl->createDebugMessenger(); } Device::~Device() { - vkDestroyInstance(pimpl->m_instance, nullptr); -#ifndef NDEBUG - vkDestroyDebugUtilsMessengerEXT(pimpl->m_instance, pimpl->m_debugMessenger, nullptr); -#endif + //pimpl->cleanup(); } } diff --git a/src/window.cpp b/src/window.cpp index e8e6829..84c2b77 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,12 +1,10 @@ #include "window.hpp" +#include "log.hpp" + #include #include -#ifdef ENGINE_BUILD_VULKAN -#include -#endif - const uint64_t BILLION = 1000000000; Window::Window(const std::string& title) : m_title(title) @@ -32,7 +30,7 @@ Window::Window(const std::string& title) : m_title(title) SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(m_winSize.x), static_cast(m_winSize.y), - 0); + SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN); if (m_handle == NULL) { SDL_Quit(); throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); @@ -447,20 +445,6 @@ bool Window::infoBox(const std::string& title, const std::string& msg) } } -std::vector Window::getRequiredVulkanExtensions() const -{ -#ifdef ENGINE_BUILD_VULKAN - unsigned int sdlExtensionCount = 0; - SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, nullptr); - std::vector requiredExtensions(sdlExtensionCount); - SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, requiredExtensions.data()); - - return requiredExtensions; -#else - return std::vector{}; -#endif -} - /* STATIC METHODS */ // Display an error message box From ed5eb5153518b5b8867a09f4e8dc84897b5acbda Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 19 Sep 2022 09:57:02 +0100 Subject: [PATCH 04/50] Fix seg fault --- include/gfx_device.hpp | 3 +- src/gfx_device_vulkan.cpp | 193 +++++++++++++++++++------------------- 2 files changed, 99 insertions(+), 97 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index b6ff345..a24eb47 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -4,10 +4,9 @@ #include "engine.hpp" -#include - #include +struct SDL_Window; namespace engine::gfx { diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2e06cd2..2de047e 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -19,7 +19,6 @@ namespace engine::gfx { static std::vector getRequiredVulkanExtensions(SDL_Window* window) { -#ifdef ENGINE_BUILD_VULKAN SDL_bool res; unsigned int sdlExtensionCount = 0; @@ -30,73 +29,70 @@ namespace engine::gfx { assert(res == SDL_TRUE); return requiredExtensions; -#else - return std::vector{}; -#endif - } - - static VkBool32 debugMessenger( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - - std::string msgType{}; - - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (GENERAL)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) - msgType += " (PERF.)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (VALID.)"; - - switch (messageSeverity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - default: - break; - } - return VK_FALSE; - } + } class Device::Impl { - friend Device; public: Impl(AppInfo appInfo, SDL_Window* window) { - m_instance = std::make_unique(appInfo, getRequiredVulkanExtensions(window)); -#ifndef NDEBUG - m_debugMessenger = std::make_unique(m_instance->getHandle()); +#ifdef NDEBUG + findAvailableLayers(m_layerInfo, false); +#else + findAvailableLayers(m_layerInfo, true); #endif + m_instance = std::make_shared(appInfo, m_layerInfo, getRequiredVulkanExtensions(window)); + m_debugMessenger = std::make_unique(m_instance); + } + ~Impl() + { } private: // VkSurfaceKHR m_surface; - static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + struct LayerInfo { + + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + + std::vector layersAvailable{}; + std::optional::iterator> validationLayer; + + } m_layerInfo; + + static void findAvailableLayers(LayerInfo& layerInfo, bool useValidation) + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + layerInfo.layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, layerInfo.layersAvailable.data()); + assert(res == VK_SUCCESS); + + if (useValidation == true) { + // find validation layer and print all layers to log + for (auto it = layerInfo.layersAvailable.begin(); it != layerInfo.layersAvailable.end(); it++) { + if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { + layerInfo.validationLayer = it; + } + } + if (layerInfo.validationLayer.has_value() == false) { + CRITICAL("The validation layer was not found. Quitting."); + throw std::runtime_error("Validation layer not found"); + } + } + } class Instance { public: - Instance(AppInfo appInfo, const std::vector& windowExtensions) + Instance(AppInfo appInfo, const LayerInfo& layerInfo, const std::vector& windowExtensions) { VkResult res; - findAvailableLayers(); - int appVersionMajor, appVersionMinor, appVersionPatch; assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); int engineVersionMajor, engineVersionMinor, engineVersionPatch; @@ -118,8 +114,8 @@ namespace engine::gfx { std::vector layers{}; - if (m_validationLayer.has_value()) { - layers.push_back(m_validationLayer.value()->layerName); + if (layerInfo.validationLayer.has_value()) { + layers.push_back(layerInfo.validationLayer.value()->layerName); extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } @@ -134,8 +130,9 @@ namespace engine::gfx { .ppEnabledExtensionNames = extensions.data(), }; - if (m_validationLayer.has_value()) { - VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); + VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); + + if (layerInfo.validationLayer.has_value()) { instanceInfo.pNext = &debugMessengerCreateInfo; } @@ -158,7 +155,9 @@ namespace engine::gfx { ~Instance() { + INFO("DESTROYING INSTANCE..."); vkDestroyInstance(m_handle, nullptr); + INFO("DESTROYED INSTANCE."); } VkInstance getHandle() @@ -170,55 +169,28 @@ namespace engine::gfx { VkInstance m_handle; - std::vector m_layersAvailable{}; - static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; - std::optional::iterator> m_validationLayer; - - void findAvailableLayers() - { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - m_layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); - assert(res == VK_SUCCESS); - -#ifndef NDEBUG - // find validation layer and print all layers to log - for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { - // TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); - if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { - m_validationLayer = it; - } - } - if (m_validationLayer.has_value() == false) { - CRITICAL("The validation layer was not found. Quitting."); - throw std::runtime_error("Validation layer not found"); - } -#endif - } - }; -#ifndef NDEBUG class DebugMessenger { public: - DebugMessenger(const VkInstance& instance) : m_instance(instance) + DebugMessenger(std::shared_ptr instance) : m_instance(instance) { VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); - VkResult res = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &m_debugMessenger); + VkResult res = vkCreateDebugUtilsMessengerEXT(instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); assert(res == VK_SUCCESS); } ~DebugMessenger() { - vkDestroyDebugUtilsMessengerEXT(m_instance, m_debugMessenger, nullptr); + INFO("DESTROYING MESSENGER..."); + vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); + INFO("DESTROYED MESSENGER."); } + static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() { VkDebugUtilsMessengerCreateInfoEXT createInfo{ @@ -230,7 +202,7 @@ namespace engine::gfx { VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, - .pfnUserCallback = debugMessenger, + .pfnUserCallback = messengerCallback, .pUserData = nullptr, }; @@ -255,16 +227,48 @@ namespace engine::gfx { } private: - VkDebugUtilsMessengerEXT m_debugMessenger; - const VkInstance& m_instance; + VkDebugUtilsMessengerEXT m_messengerHandle; + std::shared_ptr m_instance; + + static VkBool32 messengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + + std::string msgType{}; + + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (GENERAL)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + msgType += " (PERF.)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (VALID.)"; + + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } }; -#endif - std::unique_ptr m_instance; -#ifndef NDEBUG + std::shared_ptr m_instance; std::unique_ptr m_debugMessenger; -#endif /* @@ -291,12 +295,11 @@ namespace engine::gfx { pimpl = std::make_unique(appInfo, window); - //pimpl->createDebugMessenger(); + } Device::~Device() { - //pimpl->cleanup(); } } From 54587d3bbb9ab661b2c327d04ccb021266e109ae Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 19 Sep 2022 18:33:56 +0100 Subject: [PATCH 05/50] More vulkan work --- include/gfx_device.hpp | 3 + src/gfx_device_vulkan.cpp | 244 ++++++++++++++++++++++++++++++-------- src/window.cpp | 14 +-- 3 files changed, 203 insertions(+), 58 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a24eb47..d64daea 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -13,6 +13,9 @@ namespace engine::gfx { struct ENGINE_API Device { Device(AppInfo appInfo, SDL_Window* window); + + Device(const Device&) = delete; + Device& operator=(const Device&) = delete; ~Device(); private: diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2de047e..6ba100c 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace engine::gfx { @@ -37,15 +38,151 @@ namespace engine::gfx { Impl(AppInfo appInfo, SDL_Window* window) { #ifdef NDEBUG - findAvailableLayers(m_layerInfo, false); + // release mode; don't use validation layer + m_layerInfo = std::make_unique(false); #else - findAvailableLayers(m_layerInfo, true); + // debug mode; use validation layer + m_layerInfo = std::make_unique(true); #endif - m_instance = std::make_shared(appInfo, m_layerInfo, getRequiredVulkanExtensions(window)); + m_instance = std::make_shared(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window)); m_debugMessenger = std::make_unique(m_instance); - } - ~Impl() - { + + // enumerate physical devices + uint32_t physDeviceCount = 0; + VkResult res; + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr); + assert(res == VK_SUCCESS); + if (physDeviceCount == 0) { + throw std::runtime_error("No GPU found with vulkan support!"); + } + std::vector physicalDevices(physDeviceCount); + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); + + // find suitable device + const std::vector requiredDeviceExtensions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + for (const auto& dev : physicalDevices) { + + uint32_t extensionCount; + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); + assert(res == VK_SUCCESS); + std::vector availableExtensions(extensionCount); + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); + assert(res == VK_SUCCESS); + + bool suitable = true; + + for (const auto& extToFind : requiredDeviceExtensions) { + + bool extFound = false; + + for (const auto& ext : availableExtensions) { + if (strcmp(extToFind, ext.extensionName) == 0) { + extFound = true; + } + } + + if (!extFound) { + suitable = false; + } + } + + if (suitable) { + physicalDevice = dev; + break; + } + + } + + if (physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("No suitable Vulkan physical device found"); + } + + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(physicalDevice, &devProps); + TRACE("Physical device to use: {}", devProps.deviceName); + + // queue families + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + + std::optional graphicsFamilyIndex; + std::optional transferFamilyIndex; + std::optional computeFamilyIndex; + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkQueueFamilyProperties family = queueFamilies[i]; + if (family.queueCount > 0) { + if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + TRACE("GRAPHICS:"); + graphicsFamilyIndex = i; + } + if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { + TRACE("TRANSFER:"); + transferFamilyIndex = i; + } + if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { + TRACE("COMPUTE:"); + computeFamilyIndex = i; + } + TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); + } + } + if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { + throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag"); + } + + std::vector queueCreateInfos{}; + + // use a set to filter out duplicate indices + std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; + float queuePriority = 1.0f; + for (uint32_t family : uniqueQueueFamilies) { + // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + TRACE("Creating queue from family {}", family); + VkDeviceQueueCreateInfo queueCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = family, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), + .pQueueCreateInfos = queueCreateInfos.data(), + // IGNORED: .enabledLayerCount + // IGNORED: .ppEnabledLayerNames + .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), + .ppEnabledExtensionNames = requiredDeviceExtensions.data(), + .pEnabledFeatures = nullptr, + }; + + VkDevice device; + + res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); + if (res != VK_SUCCESS) { + throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); + } + + volkLoadDevice(device); + + vkDestroyDevice(device, nullptr); + } private: @@ -54,37 +191,36 @@ namespace engine::gfx { struct LayerInfo { + LayerInfo(bool useValidation) + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data()); + assert(res == VK_SUCCESS); + + if (useValidation == true) { + // find validation layer and print all layers to log + for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) { + if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { + validationLayer = it; + } + } + if (validationLayer.has_value() == false) { + throw std::runtime_error("The validation layer was not found. Quitting."); + } + } + } + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; std::vector layersAvailable{}; std::optional::iterator> validationLayer; - } m_layerInfo; - - static void findAvailableLayers(LayerInfo& layerInfo, bool useValidation) - { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - layerInfo.layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, layerInfo.layersAvailable.data()); - assert(res == VK_SUCCESS); - - if (useValidation == true) { - // find validation layer and print all layers to log - for (auto it = layerInfo.layersAvailable.begin(); it != layerInfo.layersAvailable.end(); it++) { - if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { - layerInfo.validationLayer = it; - } - } - if (layerInfo.validationLayer.has_value() == false) { - CRITICAL("The validation layer was not found. Quitting."); - throw std::runtime_error("Validation layer not found"); - } - } - } + }; class Instance { @@ -93,9 +229,9 @@ namespace engine::gfx { { VkResult res; - int appVersionMajor, appVersionMinor, appVersionPatch; + int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor, engineVersionMinor, engineVersionPatch; + int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); VkApplicationInfo applicationInfo{ @@ -112,11 +248,13 @@ namespace engine::gfx { std::vector extensions{}; extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + // also use debug utils extension + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + std::vector layers{}; if (layerInfo.validationLayer.has_value()) { layers.push_back(layerInfo.validationLayer.value()->layerName); - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } VkInstanceCreateInfo instanceInfo{ @@ -144,20 +282,19 @@ namespace engine::gfx { res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - CRITICAL("The graphics driver is incompatible with vulkan"); - throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + throw std::runtime_error("The graphics driver is incompatible with vulkan"); } assert(res == VK_SUCCESS); - volkLoadInstance(m_handle); + volkLoadInstanceOnly(m_handle); } + Instance(const Instance&) = delete; + Instance& operator=(const Instance&) = delete; ~Instance() { - INFO("DESTROYING INSTANCE..."); vkDestroyInstance(m_handle, nullptr); - INFO("DESTROYED INSTANCE."); } VkInstance getHandle() @@ -166,7 +303,6 @@ namespace engine::gfx { } private: - VkInstance m_handle; }; @@ -182,15 +318,13 @@ namespace engine::gfx { assert(res == VK_SUCCESS); } + DebugMessenger(const DebugMessenger&) = delete; + DebugMessenger& operator=(const DebugMessenger&) = delete; ~DebugMessenger() { - INFO("DESTROYING MESSENGER..."); vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); - INFO("DESTROYED MESSENGER."); } - static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() { VkDebugUtilsMessengerCreateInfoEXT createInfo{ @@ -207,16 +341,16 @@ namespace engine::gfx { }; switch (MESSAGE_LEVEL) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + case Severity::VERBOSE: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + case Severity::INFO: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + case Severity::WARNING: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + case Severity::ERROR: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; // fall-through default: @@ -229,6 +363,14 @@ namespace engine::gfx { private: VkDebugUtilsMessengerEXT m_messengerHandle; std::shared_ptr m_instance; + + enum class Severity { + VERBOSE, + INFO, + WARNING, + ERROR + }; + static constexpr Severity MESSAGE_LEVEL = Severity::WARNING; static VkBool32 messengerCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -267,6 +409,7 @@ namespace engine::gfx { }; + std::unique_ptr m_layerInfo; std::shared_ptr m_instance; std::unique_ptr m_debugMessenger; @@ -288,7 +431,6 @@ namespace engine::gfx { VkResult res; res = volkInitialize(); if (res == VK_ERROR_INITIALIZATION_FAILED) { - CRITICAL("Unable to load vulkan, is it installed?"); throw std::runtime_error("Unable to load vulkan, is it installed?"); } assert(res == VK_SUCCESS); @@ -304,4 +446,4 @@ namespace engine::gfx { } -#endif +#endif \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index 84c2b77..cbf92b1 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -89,8 +89,8 @@ void Window::resetInputDeltas() m_keyboard.deltas.fill(ButtonDelta::SAME); m_mouse.deltas.fill(ButtonDelta::SAME); - m_mouse.dx = 0.0f; - m_mouse.dy = 0.0f; + m_mouse.dx = 0; + m_mouse.dy = 0; m_mouse.xscroll = 0.0f; m_mouse.yscroll = 0.0f; } @@ -143,14 +143,14 @@ void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e) button = inputs::MouseButton::M_X2; break; } - - bool buttonWasDown = m_mouse.buttons[static_cast(button)]; + int buttonIndex = static_cast(button); + bool buttonWasDown = m_mouse.buttons.at(buttonIndex); bool buttonIsDown = (e.state == SDL_PRESSED); - m_mouse.buttons[static_cast(button)] = buttonIsDown; + m_mouse.buttons.at(buttonIndex) = buttonIsDown; if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) // only sets delta if it hasn't already been set this frame (to detect very fast presses) - if (m_mouse.deltas[static_cast(button)] == ButtonDelta::SAME) { - m_mouse.deltas[static_cast(button)] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) { + m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; } } } From d455b8d4f7231b88e4b20ac172ead04dfd8137b2 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 19 Sep 2022 22:10:44 +0100 Subject: [PATCH 06/50] Add vulkan device --- src/gfx_device_vulkan.cpp | 303 ++++++++++++++++++++------------------ 1 file changed, 163 insertions(+), 140 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 6ba100c..280cd69 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -45,143 +45,13 @@ namespace engine::gfx { m_layerInfo = std::make_unique(true); #endif m_instance = std::make_shared(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window)); + + volkLoadInstanceOnly(m_instance->getHandle()); + m_debugMessenger = std::make_unique(m_instance); + m_device = std::make_unique(m_instance); - // enumerate physical devices - uint32_t physDeviceCount = 0; - VkResult res; - res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr); - assert(res == VK_SUCCESS); - if (physDeviceCount == 0) { - throw std::runtime_error("No GPU found with vulkan support!"); - } - std::vector physicalDevices(physDeviceCount); - res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); - assert(res == VK_SUCCESS); - - // find suitable device - const std::vector requiredDeviceExtensions{ - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - - for (const auto& dev : physicalDevices) { - - uint32_t extensionCount; - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); - assert(res == VK_SUCCESS); - std::vector availableExtensions(extensionCount); - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); - assert(res == VK_SUCCESS); - - bool suitable = true; - - for (const auto& extToFind : requiredDeviceExtensions) { - - bool extFound = false; - - for (const auto& ext : availableExtensions) { - if (strcmp(extToFind, ext.extensionName) == 0) { - extFound = true; - } - } - - if (!extFound) { - suitable = false; - } - } - - if (suitable) { - physicalDevice = dev; - break; - } - - } - - if (physicalDevice == VK_NULL_HANDLE) { - throw std::runtime_error("No suitable Vulkan physical device found"); - } - - VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(physicalDevice, &devProps); - TRACE("Physical device to use: {}", devProps.deviceName); - - // queue families - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); - - std::optional graphicsFamilyIndex; - std::optional transferFamilyIndex; - std::optional computeFamilyIndex; - - for (uint32_t i = 0; i < queueFamilyCount; i++) { - VkQueueFamilyProperties family = queueFamilies[i]; - if (family.queueCount > 0) { - if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - TRACE("GRAPHICS:"); - graphicsFamilyIndex = i; - } - if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { - TRACE("TRANSFER:"); - transferFamilyIndex = i; - } - if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { - TRACE("COMPUTE:"); - computeFamilyIndex = i; - } - TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); - } - } - if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { - throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag"); - } - - std::vector queueCreateInfos{}; - - // use a set to filter out duplicate indices - std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; - float queuePriority = 1.0f; - for (uint32_t family : uniqueQueueFamilies) { - // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute - TRACE("Creating queue from family {}", family); - VkDeviceQueueCreateInfo queueCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = family, - .queueCount = 1, - .pQueuePriorities = &queuePriority, - }; - queueCreateInfos.push_back(queueCreateInfo); - } - - VkDeviceCreateInfo deviceCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), - .pQueueCreateInfos = queueCreateInfos.data(), - // IGNORED: .enabledLayerCount - // IGNORED: .ppEnabledLayerNames - .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), - .ppEnabledExtensionNames = requiredDeviceExtensions.data(), - .pEnabledFeatures = nullptr, - }; - - VkDevice device; - - res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); - if (res != VK_SUCCESS) { - throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); - } - - volkLoadDevice(device); - - vkDestroyDevice(device, nullptr); + volkLoadDevice(m_device->getHandle()); } @@ -286,8 +156,6 @@ namespace engine::gfx { } assert(res == VK_SUCCESS); - volkLoadInstanceOnly(m_handle); - } Instance(const Instance&) = delete; @@ -314,7 +182,7 @@ namespace engine::gfx { { VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); - VkResult res = vkCreateDebugUtilsMessengerEXT(instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); + VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); assert(res == VK_SUCCESS); } @@ -361,8 +229,8 @@ namespace engine::gfx { } private: - VkDebugUtilsMessengerEXT m_messengerHandle; std::shared_ptr m_instance; + VkDebugUtilsMessengerEXT m_messengerHandle; enum class Severity { VERBOSE, @@ -409,9 +277,164 @@ namespace engine::gfx { }; + class Device { + + public: + Device(std::shared_ptr instance) : m_instance(instance) + { + // enumerate physical devices + uint32_t physDeviceCount = 0; + VkResult res; + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr); + assert(res == VK_SUCCESS); + if (physDeviceCount == 0) { + throw std::runtime_error("No GPU found with vulkan support!"); + } + std::vector physicalDevices(physDeviceCount); + res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); + + // find suitable device + const std::vector requiredDeviceExtensions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + for (const auto& dev : physicalDevices) { + + uint32_t extensionCount; + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); + assert(res == VK_SUCCESS); + std::vector availableExtensions(extensionCount); + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); + assert(res == VK_SUCCESS); + + bool suitable = true; + + for (const auto& extToFind : requiredDeviceExtensions) { + + bool extFound = false; + + for (const auto& ext : availableExtensions) { + if (strcmp(extToFind, ext.extensionName) == 0) { + extFound = true; + } + } + + if (!extFound) { + suitable = false; + } + } + + if (suitable) { + physicalDevice = dev; + break; + } + + } + + if (physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("No suitable Vulkan physical device found"); + } + + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(physicalDevice, &devProps); + TRACE("Selected physical device: {}", devProps.deviceName); + + // queue families + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + + std::optional graphicsFamilyIndex; + std::optional transferFamilyIndex; + std::optional computeFamilyIndex; + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkQueueFamilyProperties family = queueFamilies[i]; + if (family.queueCount > 0) { + if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + TRACE("GRAPHICS:"); + graphicsFamilyIndex = i; + } + if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { + TRACE("TRANSFER:"); + transferFamilyIndex = i; + } + if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { + TRACE("COMPUTE:"); + computeFamilyIndex = i; + } + TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); + } + } + if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { + throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag"); + } + + std::vector queueCreateInfos{}; + + // use a set to filter out duplicate indices + std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; + float queuePriority = 1.0f; + for (uint32_t family : uniqueQueueFamilies) { + // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + TRACE("Creating queue from family {}", family); + VkDeviceQueueCreateInfo queueCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = family, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), + .pQueueCreateInfos = queueCreateInfos.data(), + // IGNORED: .enabledLayerCount + // IGNORED: .ppEnabledLayerNames + .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), + .ppEnabledExtensionNames = requiredDeviceExtensions.data(), + .pEnabledFeatures = nullptr, + }; + + res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &m_handle); + if (res != VK_SUCCESS) { + throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); + } + } + Device(const Device&) = delete; + Device& operator=(const Device&) = delete; + + ~Device() + { + vkDestroyDevice(m_handle, nullptr); + } + + VkDevice getHandle() + { + return m_handle; + } + + private: + std::shared_ptr m_instance; + VkDevice m_handle; + + }; + std::unique_ptr m_layerInfo; std::shared_ptr m_instance; - std::unique_ptr m_debugMessenger; + std::unique_ptr m_debugMessenger; // uses instance + std::unique_ptr m_device; // uses instance /* From 47dbfd4dfe5a41cdaa39da1e1b7c49073061fc58 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Wed, 21 Sep 2022 20:52:26 +0100 Subject: [PATCH 07/50] Add vulkan surface --- include/gfx_device.hpp | 10 ++--- src/gfx_device_vulkan.cpp | 80 +++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index d64daea..949da86 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -10,13 +10,13 @@ struct SDL_Window; namespace engine::gfx { - struct ENGINE_API Device { + struct ENGINE_API GFXDevice { - Device(AppInfo appInfo, SDL_Window* window); + GFXDevice(AppInfo appInfo, SDL_Window* window); - Device(const Device&) = delete; - Device& operator=(const Device&) = delete; - ~Device(); + GFXDevice(const GFXDevice&) = delete; + GFXDevice& operator=(const GFXDevice&) = delete; + ~GFXDevice(); private: class Impl; diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 280cd69..2586eb8 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -32,33 +32,40 @@ namespace engine::gfx { return requiredExtensions; } - class Device::Impl { + static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance) + { + VkSurfaceKHR surface; + + if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) { + throw std::runtime_error("Unable to create window surface"); + } + + return surface; + } + + class GFXDevice::Impl { public: Impl(AppInfo appInfo, SDL_Window* window) { #ifdef NDEBUG - // release mode; don't use validation layer + // release mode: don't use validation layer m_layerInfo = std::make_unique(false); #else - // debug mode; use validation layer + // debug mode: use validation layer m_layerInfo = std::make_unique(true); #endif m_instance = std::make_shared(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window)); - volkLoadInstanceOnly(m_instance->getHandle()); - m_debugMessenger = std::make_unique(m_instance); + + m_surface = std::make_unique(window, m_instance); + m_device = std::make_unique(m_instance); - - volkLoadDevice(m_device->getHandle()); - } private: -// VkSurfaceKHR m_surface; - struct LayerInfo { LayerInfo(bool useValidation) @@ -162,6 +169,7 @@ namespace engine::gfx { Instance& operator=(const Instance&) = delete; ~Instance() { + TRACE("Destroying instance..."); vkDestroyInstance(m_handle, nullptr); } @@ -190,6 +198,7 @@ namespace engine::gfx { DebugMessenger& operator=(const DebugMessenger&) = delete; ~DebugMessenger() { + TRACE("Destroying debug messenger..."); vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); } @@ -277,6 +286,28 @@ namespace engine::gfx { }; + class Surface { + + public: + Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance) + { + m_handle = createSurface(window, instance->getHandle()); + } + Surface(const Surface&) = delete; + Surface& operator=(const Surface&) = delete; + + ~Surface() + { + TRACE("Destroying surface..."); + vkDestroySurfaceKHR(m_instance->getHandle(), m_handle, nullptr); + } + + private: + std::shared_ptr m_instance; + VkSurfaceKHR m_handle; + + }; + class Device { public: @@ -340,7 +371,7 @@ namespace engine::gfx { VkPhysicalDeviceProperties devProps; vkGetPhysicalDeviceProperties(physicalDevice, &devProps); - TRACE("Selected physical device: {}", devProps.deviceName); + INFO("Selected physical device: {}", devProps.deviceName); // queue families @@ -411,12 +442,18 @@ namespace engine::gfx { if (res != VK_SUCCESS) { throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); } + + volkLoadDevice(m_handle); + + vkGetDeviceQueue(m_handle, graphicsFamilyIndex.value(), 0, &graphicsQueue); + } Device(const Device&) = delete; Device& operator=(const Device&) = delete; ~Device() { + TRACE("Destroying device..."); vkDestroyDevice(m_handle, nullptr); } @@ -429,27 +466,19 @@ namespace engine::gfx { std::shared_ptr m_instance; VkDevice m_handle; + VkQueue graphicsQueue = VK_NULL_HANDLE; + }; std::unique_ptr m_layerInfo; std::shared_ptr m_instance; std::unique_ptr m_debugMessenger; // uses instance + std::unique_ptr m_surface; // uses instance std::unique_ptr m_device; // uses instance -/* - - void createSurface(SDL_Window* window) - { - if (SDL_Vulkan_CreateSurface(window, m_instance, &m_surface) == false) { - CRITICAL("Unable to create window surface"); - throw std::runtime_error("Unable to create window surface"); - } - } -*/ - }; - Device::Device(AppInfo appInfo, SDL_Window* window) + GFXDevice::GFXDevice(AppInfo appInfo, SDL_Window* window) { VkResult res; res = volkInitialize(); @@ -463,10 +492,11 @@ namespace engine::gfx { } - Device::~Device() + GFXDevice::~GFXDevice() { + TRACE("Destroying GFXDevice..."); } } -#endif \ No newline at end of file +#endif From f555b86a275ce2dc727f08fa3f1af3968f37cc66 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 22 Sep 2022 13:15:34 +0100 Subject: [PATCH 08/50] Add swapchain --- src/gfx_device_vulkan.cpp | 246 +++++++++++++++++++++++++++++++++----- 1 file changed, 216 insertions(+), 30 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2586eb8..d122471 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -50,18 +50,22 @@ namespace engine::gfx { { #ifdef NDEBUG // release mode: don't use validation layer - m_layerInfo = std::make_unique(false); + LayerInfo layerInfo(false); #else // debug mode: use validation layer - m_layerInfo = std::make_unique(true); + LayerInfo layerInfo(true); #endif - m_instance = std::make_shared(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window)); - volkLoadInstanceOnly(m_instance->getHandle()); - m_debugMessenger = std::make_unique(m_instance); + auto instance = std::make_shared(appInfo, layerInfo, getRequiredVulkanExtensions(window)); - m_surface = std::make_unique(window, m_instance); + volkLoadInstanceOnly(instance->getHandle()); - m_device = std::make_unique(m_instance); + m_debugMessenger = std::make_unique(instance); // instance.use_count ++ + + auto surface = std::make_shared(window, instance); // instance.use_count ++ + + auto device = std::make_shared(instance, surface); // instance.use_count ++ ; surface.use_count ++ + + m_swapchain = std::make_unique(device, surface); } private: @@ -173,7 +177,7 @@ namespace engine::gfx { vkDestroyInstance(m_handle, nullptr); } - VkInstance getHandle() + VkInstance getHandle() const { return m_handle; } @@ -289,9 +293,9 @@ namespace engine::gfx { class Surface { public: - Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance) + Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance), m_window(window) { - m_handle = createSurface(window, instance->getHandle()); + m_handle = createSurface(m_window, instance->getHandle()); } Surface(const Surface&) = delete; Surface& operator=(const Surface&) = delete; @@ -302,16 +306,27 @@ namespace engine::gfx { vkDestroySurfaceKHR(m_instance->getHandle(), m_handle, nullptr); } + VkSurfaceKHR getHandle() const + { + return m_handle; + } + + SDL_Window* getWindow() const + { + return m_window; + } + private: std::shared_ptr m_instance; VkSurfaceKHR m_handle; + SDL_Window* m_window; }; class Device { public: - Device(std::shared_ptr instance) : m_instance(instance) + Device(std::shared_ptr instance, std::shared_ptr surface) : m_instance(instance), m_surface(surface) { // enumerate physical devices uint32_t physDeviceCount = 0; @@ -325,7 +340,8 @@ namespace engine::gfx { res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); assert(res == VK_SUCCESS); - // find suitable device + // find suitable device: + const std::vector requiredDeviceExtensions{ VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; @@ -334,6 +350,7 @@ namespace engine::gfx { for (const auto& dev : physicalDevices) { + // first, check extension support uint32_t extensionCount; res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); assert(res == VK_SUCCESS); @@ -341,27 +358,46 @@ namespace engine::gfx { res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); assert(res == VK_SUCCESS); - bool suitable = true; - for (const auto& extToFind : requiredDeviceExtensions) { - bool extFound = false; - for (const auto& ext : availableExtensions) { if (strcmp(extToFind, ext.extensionName) == 0) { extFound = true; } } - if (!extFound) { - suitable = false; + continue; } } - if (suitable) { - physicalDevice = dev; - break; + // get surface capabilities + res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface->getHandle(), &m_swapchainSupportDetails.caps); + assert (res == VK_SUCCESS); + + // check there is at least one supported surface format + uint32_t surfaceFormatCount = 0; + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, nullptr); + assert(res == VK_SUCCESS); + if (surfaceFormatCount == 0) { + continue; } + m_swapchainSupportDetails.formats.resize(surfaceFormatCount); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, m_swapchainSupportDetails.formats.data()); + assert(res == VK_SUCCESS); + + // check there is at least one supported present mode + uint32_t surfacePresentModeCount = 0; + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, nullptr); + assert(res == VK_SUCCESS); + if (surfacePresentModeCount == 0) { + continue; + } + m_swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); + assert(res == VK_SUCCESS); + + physicalDevice = dev; + break; } @@ -373,6 +409,33 @@ namespace engine::gfx { vkGetPhysicalDeviceProperties(physicalDevice, &devProps); INFO("Selected physical device: {}", devProps.deviceName); + INFO("Supported present modes:"); + for (const auto& presMode : m_swapchainSupportDetails.presentModes) { + switch (presMode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + INFO("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); + break; + case VK_PRESENT_MODE_MAILBOX_KHR: + INFO("\tVK_PRESENT_MODE_MAILBOX_KHR"); + break; + case VK_PRESENT_MODE_FIFO_KHR: + INFO("\tVK_PRESENT_MODE_FIFO_KHR"); + break; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + INFO("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); + break; + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + INFO("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); + break; + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + INFO("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); + break; + default: + INFO("\tUNKNOWN DISPLAY MODE"); + break; + } + } + // queue families uint32_t queueFamilyCount = 0; @@ -403,7 +466,11 @@ namespace engine::gfx { } } if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { - throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag"); + throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); + } + + if (graphicsFamilyIndex.value() != transferFamilyIndex.value()) { + throw std::runtime_error("Vulkan device creation error: graphics and transfer queue families are not the same!"); } std::vector queueCreateInfos{}; @@ -445,7 +512,7 @@ namespace engine::gfx { volkLoadDevice(m_handle); - vkGetDeviceQueue(m_handle, graphicsFamilyIndex.value(), 0, &graphicsQueue); + vkGetDeviceQueue(m_handle, graphicsFamilyIndex.value(), 0, &m_queues.graphicsQueue); } Device(const Device&) = delete; @@ -457,24 +524,143 @@ namespace engine::gfx { vkDestroyDevice(m_handle, nullptr); } - VkDevice getHandle() + VkDevice getHandle() const { return m_handle; } + struct SwapchainSupportDetails { + VkSurfaceCapabilitiesKHR caps{}; + std::vector formats{}; + std::vector presentModes{}; + }; + + SwapchainSupportDetails getSupportDetails() + { + return m_swapchainSupportDetails; + } + private: std::shared_ptr m_instance; - VkDevice m_handle; + std::shared_ptr m_surface; - VkQueue graphicsQueue = VK_NULL_HANDLE; + SwapchainSupportDetails m_swapchainSupportDetails{}; + + VkDevice m_handle = VK_NULL_HANDLE; + + struct Queues { + VkQueue graphicsQueue = VK_NULL_HANDLE; + } m_queues; }; - std::unique_ptr m_layerInfo; - std::shared_ptr m_instance; + class Swapchain { + + public: + Swapchain(std::shared_ptr device, std::shared_ptr surface) : m_device(device), m_surface(surface) + { + VkResult res; + + auto supportDetails = device->getSupportDetails(); + + VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; + + for (const auto& format : supportDetails.formats) { + if ( format.format == VK_FORMAT_B8G8R8A8_SRGB && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) { + chosenSurfaceFormat = format; // prefer using srgb non linear colors + } + } + + VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + for (const auto& presMode : supportDetails.presentModes) { + if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { + chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + } + } + + VkExtent2D chosenSwapExtent{}; + + if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { + chosenSwapExtent = supportDetails.caps.currentExtent; + } else { + // if fb size isn't already found, get it from SDL + int width, height; + SDL_Vulkan_GetDrawableSize(m_surface->getWindow(), &width, &height); + + chosenSwapExtent.width = static_cast(width); + chosenSwapExtent.height = static_cast(height); + + chosenSwapExtent.width = std::clamp( + chosenSwapExtent.width, + supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); + chosenSwapExtent.height = std::clamp( + chosenSwapExtent.height, + supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); + } + + uint32_t imageCount = supportDetails.caps.minImageCount + 1; + if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { + imageCount = supportDetails.caps.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = m_surface->getHandle(), + .minImageCount = imageCount, + .imageFormat = chosenSurfaceFormat.format, + .imageColorSpace = chosenSurfaceFormat.colorSpace, + .imageExtent = chosenSwapExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = supportDetails.caps.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = chosenPresentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + + }; + + res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); + assert(res == VK_SUCCESS); + + /* + uint32_t swapchainImageCount = 0; + res = vkGetSwapchainImagesKHR(m_device.getHandle(), m_handle, &swapchainImageCount, nullptr); + assert(res == VK_SUCCESS); + m_images.resize(swapchainImageCount); + res = vkGetSwapchainImagesKHR(m_device.getHandle(), m_handle, &swapchainImageCount, m_images.data()); + assert(res == VK_SUCCESS); + */ + } + Swapchain(const Swapchain&) = delete; + Swapchain& operator=(const Swapchain&) = delete; + + ~Swapchain() + { + TRACE("Destroying swapchain..."); + vkDestroySwapchainKHR(m_device->getHandle(), m_handle, nullptr); + } + + private: + std::shared_ptr m_device; + std::shared_ptr m_surface; + + VkSwapchainKHR m_handle = VK_NULL_HANDLE; + + std::vector m_images; + + }; + + std::unique_ptr m_debugMessenger; // uses instance - std::unique_ptr m_surface; // uses instance - std::unique_ptr m_device; // uses instance + std::unique_ptr m_swapchain; }; From bf989e55c693b83fc3e10838b04d89fd97921a6c Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 25 Sep 2022 14:52:05 +0100 Subject: [PATCH 09/50] Check for device surface support --- src/gfx_device_vulkan.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index d122471..f1d1002 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -492,6 +492,13 @@ namespace engine::gfx { queueCreateInfos.push_back(queueCreateInfo); } + VkBool32 supported; + res = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &supported); + assert(res == VK_SUCCESS); + if (supported != VK_TRUE) { + throw std::runtime_error("The selected physical device and queue family do not support this surface"); + } + VkDeviceCreateInfo deviceCreateInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = nullptr, @@ -561,7 +568,7 @@ namespace engine::gfx { { VkResult res; - auto supportDetails = device->getSupportDetails(); + auto supportDetails = m_device->getSupportDetails(); VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; From d3a0d560716d068ecf6c3977f5eb4795850f3feb Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 1 Oct 2022 14:57:14 +0100 Subject: [PATCH 10/50] Tidy up code --- src/gfx_device_vulkan.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index f1d1002..965d1e9 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -492,11 +492,12 @@ namespace engine::gfx { queueCreateInfos.push_back(queueCreateInfo); } - VkBool32 supported; - res = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &supported); + // check the physical device is compatible with the surface + VkBool32 graphicsQueueCanPresent; + res = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); assert(res == VK_SUCCESS); - if (supported != VK_TRUE) { - throw std::runtime_error("The selected physical device and queue family do not support this surface"); + if (graphicsQueueCanPresent != VK_TRUE) { + throw std::runtime_error("The selected queue family does not support this surface"); } VkDeviceCreateInfo deviceCreateInfo{ From 3e7525be9a33f508a87133c4530aae56e5b9c19f Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 1 Oct 2022 15:44:12 +0100 Subject: [PATCH 11/50] Check that vulkan 1.3 is supported --- src/gfx_device_vulkan.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 965d1e9..aa85272 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1,3 +1,5 @@ +// The implementation of the graphics layer using Vulkan 1.3 + #ifdef ENGINE_BUILD_VULKAN #include "gfx_device.hpp" @@ -122,7 +124,7 @@ namespace engine::gfx { .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), .pEngineName = "engine", .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_API_VERSION_1_0, + .apiVersion = VK_API_VERSION_1_3, }; // make a list of all extensions to use @@ -396,6 +398,14 @@ namespace engine::gfx { res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); assert(res == VK_SUCCESS); + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(dev, &devProps); + + // check that the device supports vulkan 1.3 + if (devProps.apiVersion < VK_API_VERSION_1_3) { + continue; + } + physicalDevice = dev; break; @@ -681,6 +691,11 @@ namespace engine::gfx { } assert(res == VK_SUCCESS); + uint32_t vulkanVersion = volkGetInstanceVersion(); + if (vulkanVersion < VK_MAKE_VERSION(1, 3, 0)) { + throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); + } + pimpl = std::make_unique(appInfo, window); From 7cf22956f2b55b752a454e41e9b976e3e178087d Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 1 Oct 2022 16:59:42 +0100 Subject: [PATCH 12/50] Improve code --- src/gfx_device_vulkan.cpp | 58 +++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index aa85272..6f955b7 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -487,9 +487,19 @@ namespace engine::gfx { // use a set to filter out duplicate indices std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; + float queuePriority = 1.0f; + for (uint32_t family : uniqueQueueFamilies) { // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + + Queue newQueue{}; + newQueue.familyIndex = family; + newQueue.queueIndex = 0; + if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; + if (transferFamilyIndex == family) newQueue.supportsTransfer = true; + if (computeFamilyIndex == family) newQueue.supportsCompute = true; + TRACE("Creating queue from family {}", family); VkDeviceQueueCreateInfo queueCreateInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, @@ -500,6 +510,7 @@ namespace engine::gfx { .pQueuePriorities = &queuePriority, }; queueCreateInfos.push_back(queueCreateInfo); + m_queues.push_back(newQueue); } // check the physical device is compatible with the surface @@ -530,7 +541,9 @@ namespace engine::gfx { volkLoadDevice(m_handle); - vkGetDeviceQueue(m_handle, graphicsFamilyIndex.value(), 0, &m_queues.graphicsQueue); + for (auto& q : m_queues) { + vkGetDeviceQueue(m_handle, q.familyIndex, q.queueIndex, &q.handle); + } } Device(const Device&) = delete; @@ -558,6 +571,38 @@ namespace engine::gfx { return m_swapchainSupportDetails; } + struct Queue { + uint32_t familyIndex; + uint32_t queueIndex; + bool supportsGraphics = false; + bool supportsTransfer = false; + bool supportsCompute = false; + + VkQueue handle; + }; + + Queue getGraphicsQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsGraphics) return queue; + } + } + + Queue getTransferQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsTransfer) return queue; + } + } + + Queue getComputeQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsCompute) return queue; + } + } + + private: std::shared_ptr m_instance; std::shared_ptr m_surface; @@ -566,9 +611,7 @@ namespace engine::gfx { VkDevice m_handle = VK_NULL_HANDLE; - struct Queues { - VkQueue graphicsQueue = VK_NULL_HANDLE; - } m_queues; + std::vector m_queues{}; }; @@ -648,14 +691,13 @@ namespace engine::gfx { res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); assert(res == VK_SUCCESS); - /* uint32_t swapchainImageCount = 0; - res = vkGetSwapchainImagesKHR(m_device.getHandle(), m_handle, &swapchainImageCount, nullptr); + res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, nullptr); assert(res == VK_SUCCESS); m_images.resize(swapchainImageCount); - res = vkGetSwapchainImagesKHR(m_device.getHandle(), m_handle, &swapchainImageCount, m_images.data()); + res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, m_images.data()); assert(res == VK_SUCCESS); - */ + } Swapchain(const Swapchain&) = delete; Swapchain& operator=(const Swapchain&) = delete; From 718eb7dc5137e0c7042c4dd0a276998ac6de8295 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 1 Oct 2022 18:18:39 +0100 Subject: [PATCH 13/50] Add image views --- src/gfx_device_vulkan.cpp | 54 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 6f955b7..fc374c5 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -586,6 +586,7 @@ namespace engine::gfx { for (const auto& queue : m_queues) { if (queue.supportsGraphics) return queue; } + throw std::runtime_error("Unable to find graphics queue"); } Queue getTransferQueue() @@ -593,6 +594,7 @@ namespace engine::gfx { for (const auto& queue : m_queues) { if (queue.supportsTransfer) return queue; } + throw std::runtime_error("Unable to find transfer queue"); } Queue getComputeQueue() @@ -600,6 +602,7 @@ namespace engine::gfx { for (const auto& queue : m_queues) { if (queue.supportsCompute) return queue; } + throw std::runtime_error("Unable to find compute queue"); } @@ -664,7 +667,7 @@ namespace engine::gfx { uint32_t imageCount = supportDetails.caps.minImageCount + 1; if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { imageCount = supportDetails.caps.maxImageCount; - } + } VkSwapchainCreateInfoKHR createInfo{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, @@ -688,6 +691,18 @@ namespace engine::gfx { }; + std::array queueFamilyIndices{ + m_device->getGraphicsQueue().familyIndex, + m_device->getTransferQueue().familyIndex + }; + + // if graphics and transfer queues aren't in the same family + if (queueFamilyIndices[0] != queueFamilyIndices[1]) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = queueFamilyIndices.size(); + createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); + } + res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); assert(res == VK_SUCCESS); @@ -698,6 +713,36 @@ namespace engine::gfx { res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, m_images.data()); assert(res == VK_SUCCESS); + m_currentFormat = chosenSurfaceFormat.format; + m_currentExtent = chosenSwapExtent; + + // create image views + m_imageViews.resize(m_images.size()); + for (VkImage image : m_images) { + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.image = image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_currentFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + VkImageView imageView; + res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &imageView); + assert (res == VK_SUCCESS); + + m_imageViews.push_back(imageView); + } + } Swapchain(const Swapchain&) = delete; Swapchain& operator=(const Swapchain&) = delete; @@ -705,6 +750,9 @@ namespace engine::gfx { ~Swapchain() { TRACE("Destroying swapchain..."); + for (VkImageView view : m_imageViews) { + vkDestroyImageView(m_device->getHandle(), view, nullptr); + } vkDestroySwapchainKHR(m_device->getHandle(), m_handle, nullptr); } @@ -715,6 +763,10 @@ namespace engine::gfx { VkSwapchainKHR m_handle = VK_NULL_HANDLE; std::vector m_images; + std::vector m_imageViews; + + VkFormat m_currentFormat{}; + VkExtent2D m_currentExtent{}; }; From c738e6ced86722f6526749a3dafa3868e43c9141 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 2 Oct 2022 11:55:59 +0100 Subject: [PATCH 14/50] Improve code --- src/gfx_device_vulkan.cpp | 76 ++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index fc374c5..4b6ce69 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -61,13 +61,14 @@ namespace engine::gfx { volkLoadInstanceOnly(instance->getHandle()); - m_debugMessenger = std::make_unique(instance); // instance.use_count ++ + m_debugMessenger = std::make_unique(instance); // owns the instance + auto surface = std::make_shared(window, instance); // owns the instance - auto surface = std::make_shared(window, instance); // instance.use_count ++ + auto device = std::make_shared(surface); // owns the surface - auto device = std::make_shared(instance, surface); // instance.use_count ++ ; surface.use_count ++ + m_swapchain = std::make_unique(device); // owns the device - m_swapchain = std::make_unique(device, surface); + INFO("Instance use count: {}", instance.use_count()); } private: @@ -318,6 +319,11 @@ namespace engine::gfx { return m_window; } + VkInstance getInstance() const + { + return m_instance->getHandle(); + } + private: std::shared_ptr m_instance; VkSurfaceKHR m_handle; @@ -328,18 +334,18 @@ namespace engine::gfx { class Device { public: - Device(std::shared_ptr instance, std::shared_ptr surface) : m_instance(instance), m_surface(surface) + Device(std::shared_ptr surface) : m_surface(surface) { // enumerate physical devices uint32_t physDeviceCount = 0; VkResult res; - res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr); + res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, nullptr); assert(res == VK_SUCCESS); if (physDeviceCount == 0) { throw std::runtime_error("No GPU found with vulkan support!"); } std::vector physicalDevices(physDeviceCount); - res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data()); + res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, physicalDevices.data()); assert(res == VK_SUCCESS); // find suitable device: @@ -372,6 +378,10 @@ namespace engine::gfx { } } + + + // get swapchain support details: + // get surface capabilities res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface->getHandle(), &m_swapchainSupportDetails.caps); assert (res == VK_SUCCESS); @@ -398,6 +408,9 @@ namespace engine::gfx { res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); assert(res == VK_SUCCESS); + + + // check physical device properties VkPhysicalDeviceProperties devProps; vkGetPhysicalDeviceProperties(dev, &devProps); @@ -409,7 +422,7 @@ namespace engine::gfx { physicalDevice = dev; break; - } + } // end for() if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("No suitable Vulkan physical device found"); @@ -419,34 +432,36 @@ namespace engine::gfx { vkGetPhysicalDeviceProperties(physicalDevice, &devProps); INFO("Selected physical device: {}", devProps.deviceName); - INFO("Supported present modes:"); + TRACE("Supported present modes:"); for (const auto& presMode : m_swapchainSupportDetails.presentModes) { switch (presMode) { case VK_PRESENT_MODE_IMMEDIATE_KHR: - INFO("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); + TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); break; case VK_PRESENT_MODE_MAILBOX_KHR: - INFO("\tVK_PRESENT_MODE_MAILBOX_KHR"); + TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); break; case VK_PRESENT_MODE_FIFO_KHR: - INFO("\tVK_PRESENT_MODE_FIFO_KHR"); + TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); break; case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - INFO("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); + TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); break; case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: - INFO("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); + TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); break; case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: - INFO("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); + TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); break; default: - INFO("\tUNKNOWN DISPLAY MODE"); + TRACE("\tUNKNOWN DISPLAY MODE"); break; } } - // queue families + + + // Get the queue families and find ones that support graphics, transfer, and compute uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); @@ -475,18 +490,20 @@ namespace engine::gfx { TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); } } - if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) { + if ( graphicsFamilyIndex.has_value() == false || + transferFamilyIndex.has_value() == false) { throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); } - if (graphicsFamilyIndex.value() != transferFamilyIndex.value()) { - throw std::runtime_error("Vulkan device creation error: graphics and transfer queue families are not the same!"); - } + // there is no guaranteed support for compute queues std::vector queueCreateInfos{}; // use a set to filter out duplicate indices - std::unordered_set uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() }; + std::unordered_set uniqueQueueFamilies{}; + if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value()); + if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value()); + if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value()); float queuePriority = 1.0f; @@ -605,9 +622,12 @@ namespace engine::gfx { throw std::runtime_error("Unable to find compute queue"); } + std::shared_ptr getSurface() + { + return m_surface; + } private: - std::shared_ptr m_instance; std::shared_ptr m_surface; SwapchainSupportDetails m_swapchainSupportDetails{}; @@ -621,7 +641,7 @@ namespace engine::gfx { class Swapchain { public: - Swapchain(std::shared_ptr device, std::shared_ptr surface) : m_device(device), m_surface(surface) + Swapchain(std::shared_ptr device) : m_device(device) { VkResult res; @@ -651,7 +671,7 @@ namespace engine::gfx { } else { // if fb size isn't already found, get it from SDL int width, height; - SDL_Vulkan_GetDrawableSize(m_surface->getWindow(), &width, &height); + SDL_Vulkan_GetDrawableSize(m_device->getSurface()->getWindow(), &width, &height); chosenSwapExtent.width = static_cast(width); chosenSwapExtent.height = static_cast(height); @@ -673,7 +693,7 @@ namespace engine::gfx { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = nullptr, .flags = 0, - .surface = m_surface->getHandle(), + .surface = m_device->getSurface()->getHandle(), .minImageCount = imageCount, .imageFormat = chosenSurfaceFormat.format, .imageColorSpace = chosenSurfaceFormat.colorSpace, @@ -706,6 +726,7 @@ namespace engine::gfx { res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); assert(res == VK_SUCCESS); + // get all the image handles uint32_t swapchainImageCount = 0; res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, nullptr); assert(res == VK_SUCCESS); @@ -717,7 +738,7 @@ namespace engine::gfx { m_currentExtent = chosenSwapExtent; // create image views - m_imageViews.resize(m_images.size()); + m_imageViews.clear(); for (VkImage image : m_images) { VkImageViewCreateInfo createInfo{}; @@ -758,7 +779,6 @@ namespace engine::gfx { private: std::shared_ptr m_device; - std::shared_ptr m_surface; VkSwapchainKHR m_handle = VK_NULL_HANDLE; From 50ca206428ec709e2361d5ec0b6e35235a33d111 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 2 Oct 2022 13:56:13 +0100 Subject: [PATCH 15/50] do stuff --- include/gfx_device.hpp | 8 ++++++-- src/gfx_device_vulkan.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 949da86..14a4bd8 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -10,17 +10,21 @@ struct SDL_Window; namespace engine::gfx { - struct ENGINE_API GFXDevice { + class ENGINE_API GFXDevice { + public: GFXDevice(AppInfo appInfo, SDL_Window* window); GFXDevice(const GFXDevice&) = delete; GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); + // submit command lists and draw to the screen + void draw(); + private: class Impl; - std::unique_ptr pimpl; + std::unique_ptr m_pimpl; }; diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 4b6ce69..a45a7bb 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -810,8 +810,7 @@ namespace engine::gfx { throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); } - pimpl = std::make_unique(appInfo, window); - + m_pimpl = std::make_unique(appInfo, window); } @@ -820,6 +819,11 @@ namespace engine::gfx { TRACE("Destroying GFXDevice..."); } + void GFXDevice::draw() + { + TRACE("Drawing"); + } + } #endif From d7a79abb1c57f232ad58aa1f82d060e0f91d7882 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 2 Oct 2022 16:34:51 +0100 Subject: [PATCH 16/50] Convert back to a static library --- CMakeLists.txt | 8 +- include/components/component.hpp | 11 +- include/engine_api.h | 2 + include/gfx_device.hpp | 2 +- include/input.hpp | 148 +++---- include/object.hpp | 14 +- include/window.hpp | 290 ++++++------- src/gfx_device_vulkan.cpp | 3 +- src/input.cpp | 297 ++++++------- src/window.cpp | 696 ++++++++++++++++--------------- 10 files changed, 751 insertions(+), 720 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06e68e2..3ec08e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ project(engine LANGUAGES CXX VERSION "0.1.0" ) -add_library(${PROJECT_NAME} SHARED +add_library(${PROJECT_NAME} STATIC "src/engine.cpp" "src/window.cpp" @@ -74,6 +74,8 @@ add_library(${PROJECT_NAME} SHARED target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS") +set_property(TARGET ${PROJECT_NAME} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) @@ -143,8 +145,8 @@ add_subdirectory(dependencies/glm) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glm) # spdlog -set(SPDLOG_BUILD_SHARED ON CACHE INTERNAL "" FORCE) -set(BUILD_SHARED_LIBS ON) +set(SPDLOG_BUILD_SHARED OFF CACHE INTERNAL "" FORCE) +set(BUILD_SHARED_LIBS OFF) add_subdirectory(dependencies/spdlog) target_link_libraries(${PROJECT_NAME} PUBLIC spdlog) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/spdlog/include) diff --git a/include/components/component.hpp b/include/components/component.hpp index 5d3f950..3c0af10 100644 --- a/include/components/component.hpp +++ b/include/components/component.hpp @@ -2,9 +2,12 @@ #include "engine_api.h" +namespace engine { + class Window; + class Input; +} + class Object; -class Window; -class Input; class ResourceManager; class ENGINE_API Component { @@ -30,8 +33,8 @@ public: Object& parent; protected: - Window& win; - Input& inp; + engine::Window& win; + engine::Input& inp; ResourceManager& res; private: diff --git a/include/engine_api.h b/include/engine_api.h index 366c7c1..1e2719d 100644 --- a/include/engine_api.h +++ b/include/engine_api.h @@ -11,3 +11,5 @@ # define ENGINE_API # endif #endif + +#define ENGINE_API \ No newline at end of file diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 14a4bd8..a4d896f 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -8,7 +8,7 @@ struct SDL_Window; -namespace engine::gfx { +namespace engine { class ENGINE_API GFXDevice { diff --git a/include/input.hpp b/include/input.hpp index bc4cb08..86ca3d2 100644 --- a/include/input.hpp +++ b/include/input.hpp @@ -9,81 +9,85 @@ #include #include -class Window; +namespace engine { -enum class InputDevice : int { - MOUSE, - KEYBOARD, - CONTROLLER, - SIZE -}; + class Window; -// This class should be used to get platform/input-device independent input -class ENGINE_API Input { - -public: - - // requires a window reference to get input from - Input(const Window& win); - Input(const Input&) = delete; - Input& operator=(const Input&) = delete; - ~Input(); - - // Add a mouse input - void addInputButton(const std::string& name, inputs::MouseButton button); - void addInputAxis(const std::string& name, inputs::MouseAxis axis); - void addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low); - // Add a keyboard input - void addInputButton(const std::string& name, inputs::Key button); - void addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low); - - void delInputButton(int index); - void delInputAxis(int index); - - void setDeviceActive(enum InputDevice device, bool active); - bool getDeviceActive(enum InputDevice device) const; - - float getAxis(const std::string& axisName) const; - bool getButton(const std::string& buttonName) const; - bool getButtonPress(const std::string& buttonName) const; - bool getButtonRelease(const std::string& buttonName) const; - - -private: - - struct ButtonEntry { - std::string name; - enum InputDevice device; - int button; // enumeration of device buttons or axes + enum class InputDevice : int { + MOUSE, + KEYBOARD, + CONTROLLER, + SIZE }; - struct AxisEntry { - std::string name; - enum InputDevice device; - int axis; - bool isButtonAxis; - int high; - int low; + // This class should be used to get platform/input-device independent input + class ENGINE_API Input { + + public: + + // requires a window reference to get input from + Input(const Window& win); + Input(const Input&) = delete; + Input& operator=(const Input&) = delete; + ~Input(); + + // Add a mouse input + void addInputButton(const std::string& name, inputs::MouseButton button); + void addInputAxis(const std::string& name, inputs::MouseAxis axis); + void addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low); + // Add a keyboard input + void addInputButton(const std::string& name, inputs::Key button); + void addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low); + + void delInputButton(int index); + void delInputAxis(int index); + + void setDeviceActive(enum InputDevice device, bool active); + bool getDeviceActive(enum InputDevice device) const; + + float getAxis(const std::string& axisName) const; + bool getButton(const std::string& buttonName) const; + bool getButtonPress(const std::string& buttonName) const; + bool getButtonRelease(const std::string& buttonName) const; + + + private: + + struct ButtonEntry { + std::string name; + enum InputDevice device; + int button; // enumeration of device buttons or axes + }; + + struct AxisEntry { + std::string name; + enum InputDevice device; + int axis; + bool isButtonAxis; + int high; + int low; + }; + + const Window& m_win; + + std::vector m_buttonEntries; + std::vector m_axisEntries; + + std::array(InputDevice::SIZE)> m_enabledDevices; + + // private methods + + float getDeviceAxis(enum InputDevice device, int axis) const; + bool getDeviceButton(enum InputDevice device, int button) const; + bool getDeviceButtonDown(enum InputDevice device, int button) const; + bool getDeviceButtonUp(enum InputDevice device, int button) const; + + float getButtonAxis(enum InputDevice device, int high, int low) const; + + void addInputButton(const std::string& name, InputDevice device, int button); + void addInputAxis(const std::string& name, InputDevice device, int axis); + void addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low); + }; - const Window& m_win; - - std::vector m_buttonEntries; - std::vector m_axisEntries; - - std::array(InputDevice::SIZE)> m_enabledDevices; - - // private methods - - float getDeviceAxis(enum InputDevice device, int axis) const; - bool getDeviceButton(enum InputDevice device, int button) const; - bool getDeviceButtonDown(enum InputDevice device, int button) const; - bool getDeviceButtonUp(enum InputDevice device, int button) const; - - float getButtonAxis(enum InputDevice device, int high, int low) const; - - void addInputButton(const std::string& name, InputDevice device, int button); - void addInputAxis(const std::string& name, InputDevice device, int axis); - void addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low); - -}; +} \ No newline at end of file diff --git a/include/object.hpp b/include/object.hpp index cfc28f4..e835738 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -12,8 +12,10 @@ #include #include -class Window; -class Input; +namespace engine { + class Window; + class Input; +} class ResourceManager; class SceneRoot; @@ -28,8 +30,8 @@ namespace components { } struct GameIO { - Window * const win; - Input * const input; + engine::Window * const win; + engine::Input * const input; ResourceManager * const resMan; }; @@ -43,8 +45,8 @@ public: Object& operator=(const Object&) = delete; ~Object(); - Window& win; - Input& inp; + engine::Window& win; + engine::Input& inp; ResourceManager& res; SceneRoot& root; diff --git a/include/window.hpp b/include/window.hpp index a7190a6..39640c9 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -17,183 +17,187 @@ ENGINE_API extern const uint64_t BILLION; -class ENGINE_API Window { +namespace engine { -public: - Window(const std::string& title); - Window(const Window&) = delete; - Window& operator=(const Window&) = delete; - ~Window(); + class ENGINE_API Window { - // Return the title name - std::string getTitle() const; + public: + Window(const std::string& title); + Window(const Window&) = delete; + Window& operator=(const Window&) = delete; + ~Window(); - // Update the window state to capture any events that have occurred. - // Run this on every frame. - void getInputAndEvents(); + // Return the title name + std::string getTitle() const; - void setTitle(std::string title); + // Update the window state to capture any events that have occurred. + // Run this on every frame. + void getInputAndEvents(); - // Hides the window (it will appear closed to the user). - void hide(); - // Shows the window again. - void show(); - // Raises the window above other windows and sets the input focus - void focus(); - // Returns true if the window has focus - bool hasFocus() const; + void setTitle(std::string title); - // Sets the close flag, check this with shouldClose() - void setCloseFlag(); - // Returns true if the window should remain open - bool isRunning() const; + // Hides the window (it will appear closed to the user). + void hide(); + // Shows the window again. + void show(); + // Raises the window above other windows and sets the input focus + void focus(); + // Returns true if the window has focus + bool hasFocus() const; - void setFullscreen(bool fullscreen, bool exclusive=true); - void toggleFullscreen(); + // Sets the close flag, check this with shouldClose() + void setCloseFlag(); + // Returns true if the window should remain open + bool isRunning() const; - bool isFullscreen() const; + void setFullscreen(bool fullscreen, bool exclusive = true); + void toggleFullscreen(); - // Relative mouse mode captures the cursor for FPS style use. Returns false if unsupported. - bool setRelativeMouseMode(bool enabled); + bool isFullscreen() const; - // returns true if relative mouse mode is enabled - bool mouseCaptured(); + // Relative mouse mode captures the cursor for FPS style use. Returns false if unsupported. + bool setRelativeMouseMode(bool enabled); - // window events + // returns true if relative mouse mode is enabled + bool mouseCaptured(); - // Returns true if the window was just resized during the previous frame - bool getWindowResized() const; - // Set the window resized flag (to recalculate aspect ratios and such) - inline void setResizedFlag() - { - m_justResized = true; - } + // window events - // keyboard events + // Returns true if the window was just resized during the previous frame + bool getWindowResized() const; + // Set the window resized flag (to recalculate aspect ratios and such) + inline void setResizedFlag() + { + m_justResized = true; + } - // returns true if key is down - bool getKey(inputs::Key key) const; - // returns true if key was just pressed - bool getKeyPress(inputs::Key key) const; - // returns true if key was just released - bool getKeyRelease(inputs::Key key) const; + // keyboard events - // mouse events + // returns true if key is down + bool getKey(inputs::Key key) const; + // returns true if key was just pressed + bool getKeyPress(inputs::Key key) const; + // returns true if key was just released + bool getKeyRelease(inputs::Key key) const; - // returns true if button is down - bool getButton(inputs::MouseButton button) const; - // returns true if button was just pressed - bool getButtonPress(inputs::MouseButton button) const; - // returns true if button was just released - bool getButtonRelease(inputs::MouseButton button) const; + // mouse events - // retrieves x coordinate of the mouse - int getMouseX() const; - // retrieves y coordinate of the mouse - int getMouseY() const; - // retrieves mouse x coordinate normalised for OpenGL - float getMouseNormX() const; - // retrieves mouse y coordinate normalised for OpenGL - float getMouseNormY() const; - // retrieves dx of the mouse since the last frame - int getMouseDX() const; - // retrieves dy of the mouse since the last frame - int getMouseDY() const; - // retrieves amount scrolled vertically - float getMouseScrollX() const; - // retrieves amount scrolled horizontally - float getMouseScrollY() const; + // returns true if button is down + bool getButton(inputs::MouseButton button) const; + // returns true if button was just pressed + bool getButtonPress(inputs::MouseButton button) const; + // returns true if button was just released + bool getButtonRelease(inputs::MouseButton button) const; - // joystick/gamepad events (maybe), other misc events + // retrieves x coordinate of the mouse + int getMouseX() const; + // retrieves y coordinate of the mouse + int getMouseY() const; + // retrieves mouse x coordinate normalised for OpenGL + float getMouseNormX() const; + // retrieves mouse y coordinate normalised for OpenGL + float getMouseNormY() const; + // retrieves dx of the mouse since the last frame + int getMouseDX() const; + // retrieves dy of the mouse since the last frame + int getMouseDY() const; + // retrieves amount scrolled vertically + float getMouseScrollX() const; + // retrieves amount scrolled horizontally + float getMouseScrollY() const; + + // joystick/gamepad events (maybe), other misc events - // returns the performance counter value in nanoseconds; - uint64_t getNanos() const; - // get the time recorded at the end of the last frame - uint64_t getLastFrameStamp() const; + // returns the performance counter value in nanoseconds; + uint64_t getNanos() const; + // get the time recorded at the end of the last frame + uint64_t getLastFrameStamp() const; - // returns the number of frames elapsed since window creation - uint64_t getFrameCount() const; - uint64_t getStartTime() const;; - float dt() const; // returns delta time in seconds - uint64_t getFPS() const; - uint64_t getAvgFPS() const; + // returns the number of frames elapsed since window creation + uint64_t getFrameCount() const; + uint64_t getStartTime() const;; + float dt() const; // returns delta time in seconds + uint64_t getFPS() const; + uint64_t getAvgFPS() const; - void resetAvgFPS(); + void resetAvgFPS(); - bool infoBox(const std::string& title, const std::string& msg); + bool infoBox(const std::string& title, const std::string& msg); - /* STATIC METHODS */ - static void errorBox(const std::string& message); + /* STATIC METHODS */ + static void errorBox(const std::string& message); -public: - SDL_Window* m_handle; + public: + SDL_Window* m_handle; -private: + private: - bool m_shouldClose = false; + bool m_shouldClose = false; - std::string m_title; + std::string m_title; - bool m_fullscreen = false; - bool m_justResized = false; - bool m_keyboardFocus = true; + bool m_fullscreen = false; + bool m_justResized = false; + bool m_keyboardFocus = true; - // size in screen coordinates - glm::ivec2 m_winSize = glm::vec2(640, 480); + // size in screen coordinates + glm::ivec2 m_winSize = glm::vec2(640, 480); - // performance counter frequency - uint64_t m_counterFreq; + // performance counter frequency + uint64_t m_counterFreq; - // number of frames swapped - uint64_t m_frames = 0; - // frame count offset for fpsAvg - uint64_t m_avgFpsStartCount = 0; - // in nanoseconds - uint64_t m_startTime; - // in nanoseconds - uint64_t m_lastFrameStamp; - // in nanoseconds; elapsed time between frames - uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero - // in nanoseconds - uint64_t m_avgFpsStart; + // number of frames swapped + uint64_t m_frames = 0; + // frame count offset for fpsAvg + uint64_t m_avgFpsStartCount = 0; + // in nanoseconds + uint64_t m_startTime; + // in nanoseconds + uint64_t m_lastFrameStamp; + // in nanoseconds; elapsed time between frames + uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero + // in nanoseconds + uint64_t m_avgFpsStart; - // input stuff + // input stuff - enum class ButtonDelta { - SAME = 0, - PRESSED, - RELEASED + enum class ButtonDelta { + SAME = 0, + PRESSED, + RELEASED + }; + + struct { + std::array keys; + std::array deltas; + } m_keyboard{ }; + + struct { + std::array(inputs::MouseButton::M_SIZE)> buttons; + std::array deltas; + Sint32 x; + Sint32 y; + Sint32 dx; + Sint32 dy; + float xscroll; + float yscroll; + bool captured = false; + } m_mouse{ }; + + // private methods + + void onResize(Sint32 width, Sint32 height); + void resetInputDeltas(); + + // event methods (like callbacks) + + void onWindowEvent(SDL_WindowEvent& e); + void onKeyEvent(SDL_KeyboardEvent& e); + void onMouseButtonEvent(SDL_MouseButtonEvent& e); + void onMouseMotionEvent(SDL_MouseMotionEvent& e); + void onMouseWheelEvent(SDL_MouseWheelEvent& e); }; - struct { - std::array keys; - std::array deltas; - } m_keyboard{ }; - - struct { - std::array(inputs::MouseButton::M_SIZE)> buttons; - std::array deltas; - Sint32 x; - Sint32 y; - Sint32 dx; - Sint32 dy; - float xscroll; - float yscroll; - bool captured = false; - } m_mouse{ }; - - // private methods - - void onResize(Sint32 width, Sint32 height); - void resetInputDeltas(); - - // event methods (like callbacks) - - void onWindowEvent(SDL_WindowEvent& e); - void onKeyEvent(SDL_KeyboardEvent& e); - void onMouseButtonEvent(SDL_MouseButtonEvent& e); - void onMouseMotionEvent(SDL_MouseMotionEvent& e); - void onMouseWheelEvent(SDL_MouseWheelEvent& e); -}; +} \ No newline at end of file diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index a45a7bb..5f5330a 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -14,11 +14,12 @@ #include +#include #include #include #include -namespace engine::gfx { +namespace engine { static std::vector getRequiredVulkanExtensions(SDL_Window* window) { diff --git a/src/input.cpp b/src/input.cpp index ace2186..c65598e 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -5,31 +5,33 @@ #include #include -Input::Input(const Window &win) : m_win(win) -{ - m_enabledDevices.fill(true); -} +namespace engine { -Input::~Input() -{ -} + Input::Input(const Window& win) : m_win(win) + { + m_enabledDevices.fill(true); + } -// private methods + Input::~Input() + { + } -float Input::getDeviceAxis(enum InputDevice device, int axis) const -{ - switch (device) { + // private methods + + float Input::getDeviceAxis(enum InputDevice device, int axis) const + { + switch (device) { case InputDevice::MOUSE: switch (static_cast(axis)) { - case inputs::MouseAxis::X: - return static_cast(m_win.getMouseDX()); - case inputs::MouseAxis::Y: - return static_cast(m_win.getMouseDY()); - case inputs::MouseAxis::X_SCR: - return m_win.getMouseScrollX(); - case inputs::MouseAxis::Y_SCR: - return m_win.getMouseScrollY(); - default: break; + case inputs::MouseAxis::X: + return static_cast(m_win.getMouseDX()); + case inputs::MouseAxis::Y: + return static_cast(m_win.getMouseDY()); + case inputs::MouseAxis::X_SCR: + return m_win.getMouseScrollX(); + case inputs::MouseAxis::Y_SCR: + return m_win.getMouseScrollY(); + default: break; } break; case InputDevice::KEYBOARD: @@ -37,13 +39,13 @@ float Input::getDeviceAxis(enum InputDevice device, int axis) const case InputDevice::CONTROLLER: break; default: break; + } + throw std::runtime_error("Error getting device axis"); } - throw std::runtime_error("Error getting device axis"); -} -bool Input::getDeviceButton(enum InputDevice device, int button) const -{ - switch (device) { + bool Input::getDeviceButton(enum InputDevice device, int button) const + { + switch (device) { case InputDevice::MOUSE: return m_win.getButton(static_cast(button)); case InputDevice::KEYBOARD: @@ -51,13 +53,13 @@ bool Input::getDeviceButton(enum InputDevice device, int button) const case InputDevice::CONTROLLER: break; default: break; + } + throw std::runtime_error("Error getting device button"); } - throw std::runtime_error("Error getting device button"); -} -bool Input::getDeviceButtonDown(enum InputDevice device, int button) const -{ - switch (device) { + bool Input::getDeviceButtonDown(enum InputDevice device, int button) const + { + switch (device) { case InputDevice::MOUSE: return m_win.getButtonPress(static_cast(button)); case InputDevice::KEYBOARD: @@ -65,13 +67,13 @@ bool Input::getDeviceButtonDown(enum InputDevice device, int button) const case InputDevice::CONTROLLER: break; default: break; + } + throw std::runtime_error("Error getting device button"); } - throw std::runtime_error("Error getting device button"); -} -bool Input::getDeviceButtonUp(enum InputDevice device, int button) const -{ - switch (device) { + bool Input::getDeviceButtonUp(enum InputDevice device, int button) const + { + switch (device) { case InputDevice::MOUSE: return m_win.getButtonRelease(static_cast(button)); case InputDevice::KEYBOARD: @@ -79,154 +81,157 @@ bool Input::getDeviceButtonUp(enum InputDevice device, int button) const case InputDevice::CONTROLLER: break; default: break; + } + throw std::runtime_error("Error getting device button"); } - throw std::runtime_error("Error getting device button"); -} -float Input::getButtonAxis(enum InputDevice device, int high, int low) const -{ - float value = 0.0f; - if (getDeviceButton(device, high)) value += 1.0f; - if (low != 0) { - if (getDeviceButton(device, low)) value += -1.0f; + float Input::getButtonAxis(enum InputDevice device, int high, int low) const + { + float value = 0.0f; + if (getDeviceButton(device, high)) value += 1.0f; + if (low != 0) { + if (getDeviceButton(device, low)) value += -1.0f; + } + return value; } - return value; -} -// public methods + // public methods -void Input::addInputButton(const std::string& name, InputDevice device, int button) -{ - m_buttonEntries.push_back( { name, device, button } ); -} + void Input::addInputButton(const std::string& name, InputDevice device, int button) + { + m_buttonEntries.push_back({ name, device, button }); + } -void Input::addInputAxis(const std::string& name, InputDevice device, int axis) -{ - m_axisEntries.push_back( { name, device, axis, false, 0, 0 } ); -} + void Input::addInputAxis(const std::string& name, InputDevice device, int axis) + { + m_axisEntries.push_back({ name, device, axis, false, 0, 0 }); + } -void Input::addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low) -{ - m_axisEntries.push_back( { name, device, 0, true, high, low } ); -} + void Input::addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low) + { + m_axisEntries.push_back({ name, device, 0, true, high, low }); + } -// OVERLOADS: + // OVERLOADS: -// Add a mouse input -void Input::addInputButton(const std::string& name, inputs::MouseButton button) -{ - addInputButton(name, InputDevice::MOUSE, static_cast(button)); -} + // Add a mouse input + void Input::addInputButton(const std::string& name, inputs::MouseButton button) + { + addInputButton(name, InputDevice::MOUSE, static_cast(button)); + } -void Input::addInputAxis(const std::string& name, inputs::MouseAxis axis) -{ - addInputAxis(name, InputDevice::MOUSE, static_cast(axis)); -} + void Input::addInputAxis(const std::string& name, inputs::MouseAxis axis) + { + addInputAxis(name, InputDevice::MOUSE, static_cast(axis)); + } -void Input::addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low) -{ - addInputButtonAsAxis(name, InputDevice::MOUSE, static_cast(high), static_cast(low)); -} + void Input::addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low) + { + addInputButtonAsAxis(name, InputDevice::MOUSE, static_cast(high), static_cast(low)); + } -// Add a keyboard input (TODO: add KeyboardButton enum class) -void Input::addInputButton(const std::string& name, inputs::Key button) -{ - addInputButton(name, InputDevice::KEYBOARD, static_cast(button)); -} + // Add a keyboard input (TODO: add KeyboardButton enum class) + void Input::addInputButton(const std::string& name, inputs::Key button) + { + addInputButton(name, InputDevice::KEYBOARD, static_cast(button)); + } -void Input::addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low) -{ - addInputButtonAsAxis(name, InputDevice::KEYBOARD, static_cast(high), static_cast(low)); -} + void Input::addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low) + { + addInputButtonAsAxis(name, InputDevice::KEYBOARD, static_cast(high), static_cast(low)); + } -void Input::delInputButton(int index) -{ - std::vector::iterator it = m_buttonEntries.begin(); - std::advance(it, index); - m_buttonEntries.erase(it); -} + void Input::delInputButton(int index) + { + std::vector::iterator it = m_buttonEntries.begin(); + std::advance(it, index); + m_buttonEntries.erase(it); + } -void Input::delInputAxis(int index) -{ - std::vector::iterator it = m_axisEntries.begin(); - std::advance(it, index); - m_axisEntries.erase(it); -} + void Input::delInputAxis(int index) + { + std::vector::iterator it = m_axisEntries.begin(); + std::advance(it, index); + m_axisEntries.erase(it); + } -void Input::setDeviceActive(enum InputDevice device, bool active) -{ - m_enabledDevices[static_cast(device)] = active; -} + void Input::setDeviceActive(enum InputDevice device, bool active) + { + m_enabledDevices[static_cast(device)] = active; + } -bool Input::getDeviceActive(enum InputDevice device) const -{ - return m_enabledDevices[static_cast(device)]; -} + bool Input::getDeviceActive(enum InputDevice device) const + { + return m_enabledDevices[static_cast(device)]; + } -float Input::getAxis(const std::string& axisName) const -{ - for (const AxisEntry& e : m_axisEntries) { - if (e.name == axisName) { - if (m_enabledDevices[static_cast(e.device)]) { - if (e.isButtonAxis) { - return getButtonAxis(e.device, e.high, e.low); - } else { - return getDeviceAxis(e.device, e.axis); + float Input::getAxis(const std::string& axisName) const + { + for (const AxisEntry& e : m_axisEntries) { + if (e.name == axisName) { + if (m_enabledDevices[static_cast(e.device)]) { + if (e.isButtonAxis) { + return getButtonAxis(e.device, e.high, e.low); + } + else { + return getDeviceAxis(e.device, e.axis); + } } } } + return 0.0f; // instead of throwing an exception, just return nothing + // throw std::runtime_error("Unable to find mapping in input table"); } - return 0.0f; // instead of throwing an exception, just return nothing -// throw std::runtime_error("Unable to find mapping in input table"); -} -bool Input::getButton(const std::string& buttonName) const -{ - bool isDown = false; + bool Input::getButton(const std::string& buttonName) const + { + bool isDown = false; - for (const ButtonEntry& e : m_buttonEntries) { - if (e.name == buttonName) { - if (m_enabledDevices[static_cast(e.device)]) { - if (getDeviceButton(e.device, e.button) == true) { - isDown = true; - break; + for (const ButtonEntry& e : m_buttonEntries) { + if (e.name == buttonName) { + if (m_enabledDevices[static_cast(e.device)]) { + if (getDeviceButton(e.device, e.button) == true) { + isDown = true; + break; + } } } } + return isDown; } - return isDown; -} -bool Input::getButtonPress(const std::string& buttonName) const -{ - bool isPressed = false; + bool Input::getButtonPress(const std::string& buttonName) const + { + bool isPressed = false; - for (const ButtonEntry& e : m_buttonEntries) { - if (e.name == buttonName) { - if (m_enabledDevices[static_cast(e.device)]) { - if (getDeviceButtonDown(e.device, e.button) == true) { - isPressed = true; - break; + for (const ButtonEntry& e : m_buttonEntries) { + if (e.name == buttonName) { + if (m_enabledDevices[static_cast(e.device)]) { + if (getDeviceButtonDown(e.device, e.button) == true) { + isPressed = true; + break; + } } } } + return isPressed; } - return isPressed; -} -bool Input::getButtonRelease(const std::string& buttonName) const -{ - bool isReleased = false; + bool Input::getButtonRelease(const std::string& buttonName) const + { + bool isReleased = false; - for (const ButtonEntry& e : m_buttonEntries) { - if (e.name == buttonName) { - if (m_enabledDevices[static_cast(e.device)]) { - if (getDeviceButtonUp(e.device, e.button) == true) { - isReleased = true; - break; + for (const ButtonEntry& e : m_buttonEntries) { + if (e.name == buttonName) { + if (m_enabledDevices[static_cast(e.device)]) { + if (getDeviceButtonUp(e.device, e.button) == true) { + isReleased = true; + break; + } } } } + return isReleased; } - return isReleased; -} + +} \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index cbf92b1..c16d73c 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -7,100 +7,102 @@ const uint64_t BILLION = 1000000000; -Window::Window(const std::string& title) : m_title(title) -{ +namespace engine { - // init SDL - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError())); - if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL error", errMsg.c_str(), NULL) != 0) { - std::cerr << errMsg << "\nAre you in a graphical environment?\n"; + Window::Window(const std::string& title) : m_title(title) + { + + // init SDL + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError())); + if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL error", errMsg.c_str(), NULL) != 0) { + std::cerr << errMsg << "\nAre you in a graphical environment?\n"; + } + throw std::runtime_error(errMsg); } - throw std::runtime_error(errMsg); - } - m_counterFreq = SDL_GetPerformanceFrequency(); - m_startTime = getNanos(); - m_lastFrameStamp = m_startTime - 1; - m_avgFpsStart = m_startTime; + m_counterFreq = SDL_GetPerformanceFrequency(); + m_startTime = getNanos(); + m_lastFrameStamp = m_startTime - 1; + m_avgFpsStart = m_startTime; - // create the window - m_handle = SDL_CreateWindow( + // create the window + m_handle = SDL_CreateWindow( m_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(m_winSize.x), static_cast(m_winSize.y), SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN); - if (m_handle == NULL) { - SDL_Quit(); - throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); + if (m_handle == NULL) { + SDL_Quit(); + throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); + } + + // get window size + int winWidth, winHeight; + SDL_GetWindowSize(m_handle, &winWidth, &winHeight); + m_winSize.x = winWidth; + m_winSize.y = winHeight; + + const int WINDOWED_MIN_WIDTH = 640; + const int WINDOWED_MIN_HEIGHT = 480; + SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT); + + /* + m_glContext = SDL_GL_CreateContext(m_handle); + if (m_glContext == NULL) { + SDL_DestroyWindow(m_handle); + SDL_Quit(); + throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError())); + } + + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { + SDL_DestroyWindow(m_handle); + SDL_Quit(); + throw std::runtime_error("Unable to initialise GLAD"); + } + */ + + // onResize(m_winSize.x, m_winSize.y); + } - // get window size - int winWidth, winHeight; - SDL_GetWindowSize(m_handle, &winWidth, &winHeight); - m_winSize.x = winWidth; - m_winSize.y = winHeight; - - const int WINDOWED_MIN_WIDTH = 640; - const int WINDOWED_MIN_HEIGHT = 480; - SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT); - - /* - m_glContext = SDL_GL_CreateContext(m_handle); - if (m_glContext == NULL) { + Window::~Window() + { SDL_DestroyWindow(m_handle); SDL_Quit(); - throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError())); } - if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { - SDL_DestroyWindow(m_handle); - SDL_Quit(); - throw std::runtime_error("Unable to initialise GLAD"); + // private methods + + void Window::onResize(Sint32 width, Sint32 height) + { + // get window size + m_winSize.x = static_cast(width); + m_winSize.y = static_cast(height); + + m_justResized = true; } - */ -// onResize(m_winSize.x, m_winSize.y); + void Window::resetInputDeltas() + { + m_justResized = false; -} + m_keyboard.deltas.fill(ButtonDelta::SAME); -Window::~Window() -{ - SDL_DestroyWindow(m_handle); - SDL_Quit(); -} + m_mouse.deltas.fill(ButtonDelta::SAME); + m_mouse.dx = 0; + m_mouse.dy = 0; + m_mouse.xscroll = 0.0f; + m_mouse.yscroll = 0.0f; + } -// private methods + // TODO event methods (like callbacks) -void Window::onResize(Sint32 width, Sint32 height) -{ - // get window size - m_winSize.x = static_cast(width); - m_winSize.y = static_cast(height); + void Window::onWindowEvent(SDL_WindowEvent& e) + { - m_justResized = true; -} - -void Window::resetInputDeltas() -{ - m_justResized = false; - - m_keyboard.deltas.fill(ButtonDelta::SAME); - - m_mouse.deltas.fill(ButtonDelta::SAME); - m_mouse.dx = 0; - m_mouse.dy = 0; - m_mouse.xscroll = 0.0f; - m_mouse.yscroll = 0.0f; -} - -// TODO event methods (like callbacks) - -void Window::onWindowEvent(SDL_WindowEvent &e) -{ - - switch (e.event) { + switch (e.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: onResize(e.data1, e.data2); break; @@ -110,23 +112,23 @@ void Window::onWindowEvent(SDL_WindowEvent &e) case SDL_WINDOWEVENT_FOCUS_LOST: m_keyboardFocus = false; break; + } } -} -void Window::onKeyEvent(SDL_KeyboardEvent &e) -{ - bool keyWasDown = m_keyboard.keys[e.keysym.scancode]; - bool keyIsDown = (e.state == SDL_PRESSED); - m_keyboard.keys[e.keysym.scancode] = keyIsDown; - if (keyIsDown != keyWasDown) { // (if key was pressed or released) - m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + void Window::onKeyEvent(SDL_KeyboardEvent& e) + { + bool keyWasDown = m_keyboard.keys[e.keysym.scancode]; + bool keyIsDown = (e.state == SDL_PRESSED); + m_keyboard.keys[e.keysym.scancode] = keyIsDown; + if (keyIsDown != keyWasDown) { // (if key was pressed or released) + m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + } } -} -void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e) -{ - enum inputs::MouseButton button = inputs::MouseButton::M_INVALID; - switch (e.button) { + void Window::onMouseButtonEvent(SDL_MouseButtonEvent& e) + { + enum inputs::MouseButton button = inputs::MouseButton::M_INVALID; + switch (e.button) { case SDL_BUTTON_LEFT: button = inputs::MouseButton::M_LEFT; break; @@ -142,59 +144,60 @@ void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e) case SDL_BUTTON_X2: button = inputs::MouseButton::M_X2; break; - } - int buttonIndex = static_cast(button); - bool buttonWasDown = m_mouse.buttons.at(buttonIndex); - bool buttonIsDown = (e.state == SDL_PRESSED); - m_mouse.buttons.at(buttonIndex) = buttonIsDown; - if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) - // only sets delta if it hasn't already been set this frame (to detect very fast presses) - if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) { - m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + } + int buttonIndex = static_cast(button); + bool buttonWasDown = m_mouse.buttons.at(buttonIndex); + bool buttonIsDown = (e.state == SDL_PRESSED); + m_mouse.buttons.at(buttonIndex) = buttonIsDown; + if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) + // only sets delta if it hasn't already been set this frame (to detect very fast presses) + if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) { + m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; + } } } -} -void Window::onMouseMotionEvent(SDL_MouseMotionEvent &e) -{ - m_mouse.x = e.x; - m_mouse.y = e.y; - m_mouse.dx = e.xrel; - m_mouse.dy = e.yrel; -} - -void Window::onMouseWheelEvent(SDL_MouseWheelEvent &e) -{ - if (e.direction == SDL_MOUSEWHEEL_NORMAL) { - m_mouse.xscroll = e.preciseX; - m_mouse.yscroll = e.preciseY; - } else { // flipped - m_mouse.xscroll = -e.preciseX; - m_mouse.yscroll = -e.preciseY; + void Window::onMouseMotionEvent(SDL_MouseMotionEvent& e) + { + m_mouse.x = e.x; + m_mouse.y = e.y; + m_mouse.dx = e.xrel; + m_mouse.dy = e.yrel; } -} -// public methods + void Window::onMouseWheelEvent(SDL_MouseWheelEvent& e) + { + if (e.direction == SDL_MOUSEWHEEL_NORMAL) { + m_mouse.xscroll = e.preciseX; + m_mouse.yscroll = e.preciseY; + } + else { // flipped + m_mouse.xscroll = -e.preciseX; + m_mouse.yscroll = -e.preciseY; + } + } -std::string Window::getTitle() const -{ - return m_title; -} + // public methods -void Window::getInputAndEvents() -{ + std::string Window::getTitle() const + { + return m_title; + } - m_frames++; - uint64_t currentFrameStamp = getNanos(); - m_lastFrameTime = currentFrameStamp - m_lastFrameStamp; - m_lastFrameStamp = currentFrameStamp; + void Window::getInputAndEvents() + { - resetInputDeltas(); + m_frames++; + uint64_t currentFrameStamp = getNanos(); + m_lastFrameTime = currentFrameStamp - m_lastFrameStamp; + m_lastFrameStamp = currentFrameStamp; - // loop through all available events - SDL_Event e; - while (SDL_PollEvent(&e)) { - switch (e.type) { + resetInputDeltas(); + + // loop through all available events + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { case SDL_QUIT: setCloseFlag(); @@ -222,233 +225,238 @@ void Window::getInputAndEvents() onMouseWheelEvent(e.wheel); break; + } + } + + } + + void Window::setTitle(std::string title) + { + SDL_SetWindowTitle(m_handle, title.c_str()); + } + + bool Window::getWindowResized() const + { + return m_justResized; + } + + void Window::show() + { + SDL_ShowWindow(m_handle); + } + + void Window::hide() + { + SDL_HideWindow(m_handle); + } + + void Window::focus() + { + SDL_RaiseWindow(m_handle); + m_keyboardFocus = true; + } + + bool Window::hasFocus() const + { + return m_keyboardFocus; + } + + void Window::setCloseFlag() + { + m_shouldClose = true; + } + + bool Window::isRunning() const + { + return !m_shouldClose; + } + + void Window::setFullscreen(bool fullscreen, bool exclusive) + { + if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { + throw std::runtime_error("Unable to set window to fullscreen/windowed"); + } + m_fullscreen = fullscreen; + if (fullscreen) { + int width, height; + SDL_GetWindowSize(m_handle, &width, &height); + onResize(width, height); } } -} - -void Window::setTitle(std::string title) -{ - SDL_SetWindowTitle(m_handle, title.c_str()); -} - -bool Window::getWindowResized() const -{ - return m_justResized; -} - -void Window::show() -{ - SDL_ShowWindow(m_handle); -} - -void Window::hide() -{ - SDL_HideWindow(m_handle); -} - -void Window::focus() -{ - SDL_RaiseWindow(m_handle); - m_keyboardFocus = true; -} - -bool Window::hasFocus() const -{ - return m_keyboardFocus; -} - -void Window::setCloseFlag() -{ - m_shouldClose = true; -} - -bool Window::isRunning() const -{ - return !m_shouldClose; -} - -void Window::setFullscreen(bool fullscreen, bool exclusive) -{ - if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { - throw std::runtime_error("Unable to set window to fullscreen/windowed"); + void Window::toggleFullscreen() + { + setFullscreen(!m_fullscreen); } - m_fullscreen = fullscreen; - if (fullscreen) { - int width, height; - SDL_GetWindowSize(m_handle, &width, &height); - onResize(width, height); + + bool Window::isFullscreen() const + { + return m_fullscreen; } -} -void Window::toggleFullscreen() -{ - setFullscreen(!m_fullscreen); -} - -bool Window::isFullscreen() const -{ - return m_fullscreen; -} - -bool Window::setRelativeMouseMode(bool enabled) -{ - m_mouse.captured = enabled; - int code = SDL_SetRelativeMouseMode(static_cast(enabled)); - if (code != 0) { - throw std::runtime_error("Unable to set relative mouse mode"); - } else { - return true; + bool Window::setRelativeMouseMode(bool enabled) + { + m_mouse.captured = enabled; + int code = SDL_SetRelativeMouseMode(static_cast(enabled)); + if (code != 0) { + throw std::runtime_error("Unable to set relative mouse mode"); + } + else { + return true; + } } -} -bool Window::mouseCaptured() -{ - return m_mouse.captured; -} - -// getting input - -bool Window::getKey(inputs::Key key) const -{ - return m_keyboard.keys[static_cast(key)]; -} - -bool Window::getKeyPress(inputs::Key key) const -{ - return m_keyboard.deltas[static_cast(key)] == ButtonDelta::PRESSED; -} - -bool Window::getKeyRelease(inputs::Key key) const -{ - return m_keyboard.deltas[static_cast(key)] == ButtonDelta::RELEASED; -} - -// TODO mouse input - -bool Window::getButton(inputs::MouseButton button) const -{ - return m_mouse.buttons[static_cast(button)]; -} - -bool Window::getButtonPress(inputs::MouseButton button) const -{ - return m_mouse.deltas[static_cast(button)] == ButtonDelta::PRESSED; -} - -bool Window::getButtonRelease(inputs::MouseButton button) const -{ - return m_mouse.deltas[static_cast(button)] == ButtonDelta::RELEASED; -} - -int Window::getMouseX() const -{ - return static_cast(m_mouse.x); -} - -int Window::getMouseY() const -{ - return static_cast(m_mouse.y); -} - -float Window::getMouseNormX() const -{ - return ((float)m_mouse.x * 2.0f / (float)m_winSize.x) - 1.0f; -} - -float Window::getMouseNormY() const -{ - return ((float)m_mouse.y * -2.0f / (float)m_winSize.y) + 1.0f; -} - -int Window::getMouseDX() const -{ - return static_cast(m_mouse.dx); -} - -int Window::getMouseDY() const -{ - return static_cast(m_mouse.dy); -} - -float Window::getMouseScrollX() const -{ - return m_mouse.xscroll; -} - -float Window::getMouseScrollY() const -{ - return m_mouse.yscroll; -} - -// TODO game pad - -// get timer value -uint64_t Window::getNanos() const -{ - uint64_t count; - - count = SDL_GetPerformanceCounter(); - if (m_counterFreq == BILLION) { - return count; - } else { - return count * (BILLION / m_counterFreq); + bool Window::mouseCaptured() + { + return m_mouse.captured; } -} -uint64_t Window::getLastFrameStamp() const -{ - return m_lastFrameStamp; -} + // getting input -uint64_t Window::getFrameCount() const -{ - return m_frames; -} - -uint64_t Window::getStartTime() const -{ - return m_startTime; -} - -float Window::dt() const -{ - return (float)m_lastFrameTime / (float)BILLION; -} - -uint64_t Window::getFPS() const -{ - if (m_lastFrameTime == 0) return 0; - return BILLION / m_lastFrameTime; -} - -uint64_t Window::getAvgFPS() const -{ - uint64_t delta_t = getNanos() - m_avgFpsStart; - if (delta_t == 0) return 0; - return BILLION * (m_frames - m_avgFpsStartCount) / delta_t; -} - -void Window::resetAvgFPS() -{ - m_avgFpsStart = getNanos(); - m_avgFpsStartCount = getFrameCount(); -} - -bool Window::infoBox(const std::string& title, const std::string& msg) -{ - if (isFullscreen() == false) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), m_handle); - return true; - } else { - return false; + bool Window::getKey(inputs::Key key) const + { + return m_keyboard.keys[static_cast(key)]; } -} -/* STATIC METHODS */ + bool Window::getKeyPress(inputs::Key key) const + { + return m_keyboard.deltas[static_cast(key)] == ButtonDelta::PRESSED; + } -// Display an error message box -void Window::errorBox(const std::string& message) -{ - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL); -} + bool Window::getKeyRelease(inputs::Key key) const + { + return m_keyboard.deltas[static_cast(key)] == ButtonDelta::RELEASED; + } + + // TODO mouse input + + bool Window::getButton(inputs::MouseButton button) const + { + return m_mouse.buttons[static_cast(button)]; + } + + bool Window::getButtonPress(inputs::MouseButton button) const + { + return m_mouse.deltas[static_cast(button)] == ButtonDelta::PRESSED; + } + + bool Window::getButtonRelease(inputs::MouseButton button) const + { + return m_mouse.deltas[static_cast(button)] == ButtonDelta::RELEASED; + } + + int Window::getMouseX() const + { + return static_cast(m_mouse.x); + } + + int Window::getMouseY() const + { + return static_cast(m_mouse.y); + } + + float Window::getMouseNormX() const + { + return ((float)m_mouse.x * 2.0f / (float)m_winSize.x) - 1.0f; + } + + float Window::getMouseNormY() const + { + return ((float)m_mouse.y * -2.0f / (float)m_winSize.y) + 1.0f; + } + + int Window::getMouseDX() const + { + return static_cast(m_mouse.dx); + } + + int Window::getMouseDY() const + { + return static_cast(m_mouse.dy); + } + + float Window::getMouseScrollX() const + { + return m_mouse.xscroll; + } + + float Window::getMouseScrollY() const + { + return m_mouse.yscroll; + } + + // TODO game pad + + // get timer value + uint64_t Window::getNanos() const + { + uint64_t count; + + count = SDL_GetPerformanceCounter(); + if (m_counterFreq == BILLION) { + return count; + } + else { + return count * (BILLION / m_counterFreq); + } + } + + uint64_t Window::getLastFrameStamp() const + { + return m_lastFrameStamp; + } + + uint64_t Window::getFrameCount() const + { + return m_frames; + } + + uint64_t Window::getStartTime() const + { + return m_startTime; + } + + float Window::dt() const + { + return (float)m_lastFrameTime / (float)BILLION; + } + + uint64_t Window::getFPS() const + { + if (m_lastFrameTime == 0) return 0; + return BILLION / m_lastFrameTime; + } + + uint64_t Window::getAvgFPS() const + { + uint64_t delta_t = getNanos() - m_avgFpsStart; + if (delta_t == 0) return 0; + return BILLION * (m_frames - m_avgFpsStartCount) / delta_t; + } + + void Window::resetAvgFPS() + { + m_avgFpsStart = getNanos(); + m_avgFpsStartCount = getFrameCount(); + } + + bool Window::infoBox(const std::string& title, const std::string& msg) + { + if (isFullscreen() == false) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), m_handle); + return true; + } + else { + return false; + } + } + + /* STATIC METHODS */ + + // Display an error message box + void Window::errorBox(const std::string& message) + { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL); + } + +} \ No newline at end of file From 45a4db4dcfdb98c609ff49290619853f53d2fd10 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Tue, 4 Oct 2022 11:54:23 +0100 Subject: [PATCH 17/50] Move game loop into engine lib --- CMakeLists.txt | 3 +- include/engine.hpp | 26 +++++++++++++---- include/engine_api.h | 2 ++ include/gfx_device.hpp | 4 +-- include/util.hpp | 18 ++++++++++++ src/engine.cpp | 59 ++++++++++++++++++++++++++++++++------- src/gfx_device_vulkan.cpp | 16 ++++++----- 7 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 include/util.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ec08e0..afce8dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ add_library(${PROJECT_NAME} STATIC "include/engine.hpp" + "include/util.hpp" + "include/log.hpp" "include/window.hpp" @@ -67,7 +69,6 @@ add_library(${PROJECT_NAME} STATIC "include/gfx.hpp" "include/gfx_device.hpp" - ) # compiling options: diff --git a/include/engine.hpp b/include/engine.hpp index 6e77afc..56b3db5 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -1,13 +1,27 @@ #pragma once +#include + namespace engine { - struct AppInfo { - const char* name; - const char* version; + class Window; + class GFXDevice; + + class Application { + + public: + Application(const char* appName, const char* appVersion); + + Application(const Application&) = delete; + Application& operator=(const Application&) = delete; + + ~Application(); + + void gameLoop(); + + private: + std::unique_ptr m_win; + std::unique_ptr m_gfx; }; - - bool versionFromCharArray(const char* version, int* major, int* minor, int* patch); - } diff --git a/include/engine_api.h b/include/engine_api.h index 1e2719d..3c0015b 100644 --- a/include/engine_api.h +++ b/include/engine_api.h @@ -1,5 +1,6 @@ #pragma once +/* #ifndef ENGINE_API # ifdef _MSC_VER # ifdef ENGINE_EXPORTS @@ -11,5 +12,6 @@ # define ENGINE_API # endif #endif +*/ #define ENGINE_API \ No newline at end of file diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a4d896f..e54bf41 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -2,8 +2,6 @@ #include "engine_api.h" -#include "engine.hpp" - #include struct SDL_Window; @@ -13,7 +11,7 @@ namespace engine { class ENGINE_API GFXDevice { public: - GFXDevice(AppInfo appInfo, SDL_Window* window); + GFXDevice(const char* appName, const char* appVersion, SDL_Window* window); GFXDevice(const GFXDevice&) = delete; GFXDevice& operator=(const GFXDevice&) = delete; diff --git a/include/util.hpp b/include/util.hpp new file mode 100644 index 0000000..6de177d --- /dev/null +++ b/include/util.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace engine { + + inline bool versionFromCharArray(const char* version, int* major, int* minor, int* patch) + { + if (sscanf(version, "%d.%d.%d", major, minor, patch) != 3) { + *major = 0; + *minor = 0; + *patch = 0; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/src/engine.cpp b/src/engine.cpp index c13c74e..e2bc81b 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,18 +1,57 @@ #include "engine.hpp" -#include +#include "window.hpp" +#include "gfx_device.hpp" namespace engine { -bool versionFromCharArray(const char* version, int* major, int* minor, int* patch) -{ - if (sscanf(version, "%d.%d.%d", major, minor, patch) != 3) { - *major = 0; - *minor = 0; - *patch = 0; - return false; + Application::Application(const char* appName, const char* appVersion) + { + m_win = std::make_unique(appName); + m_gfx = std::make_unique(appName, appVersion, m_win->m_handle); + } + + Application::~Application() + { + + } + + void Application::gameLoop() + { + uint64_t lastTick = m_win->getNanos(); + constexpr int TICKFREQ = 20; // in hz + + // single-threaded game loop + while (m_win->isRunning()) { + + /* logic */ + + if (m_win->getLastFrameStamp() >= lastTick + (BILLION / TICKFREQ)) { + lastTick = m_win->getLastFrameStamp(); + + // do tick stuff here + + } + + if (m_win->getKeyPress(inputs::Key::F11)) { + if (m_win->isFullscreen()) { + m_win->setFullscreen(false); + } + else { + m_win->setFullscreen(true, false); // borderless window + } + } + if (m_win->getKeyPress(inputs::Key::ESCAPE)) { + m_win->setCloseFlag(); + } + + /* draw */ + m_gfx->draw(); + + /* poll events */ + m_win->getInputAndEvents(); + + } } - return true; -} } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 5f5330a..9256d45 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -4,6 +4,8 @@ #include "gfx_device.hpp" +#include "util.hpp" + #include "config.h" #include "log.hpp" @@ -49,7 +51,7 @@ namespace engine { class GFXDevice::Impl { public: - Impl(AppInfo appInfo, SDL_Window* window) + Impl(const char* appName, const char* appVersion, SDL_Window* window) { #ifdef NDEBUG // release mode: don't use validation layer @@ -58,7 +60,7 @@ namespace engine { // debug mode: use validation layer LayerInfo layerInfo(true); #endif - auto instance = std::make_shared(appInfo, layerInfo, getRequiredVulkanExtensions(window)); + auto instance = std::make_shared(appName, appVersion, layerInfo, getRequiredVulkanExtensions(window)); volkLoadInstanceOnly(instance->getHandle()); @@ -110,19 +112,19 @@ namespace engine { class Instance { public: - Instance(AppInfo appInfo, const LayerInfo& layerInfo, const std::vector& windowExtensions) + Instance(const char* appName, const char* appVersion, const LayerInfo& layerInfo, const std::vector& windowExtensions) { VkResult res; int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; - assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + assert(versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch)); int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); VkApplicationInfo applicationInfo{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, - .pApplicationName = appInfo.name, + .pApplicationName = appName, .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), .pEngineName = "engine", .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), @@ -797,7 +799,7 @@ namespace engine { }; - GFXDevice::GFXDevice(AppInfo appInfo, SDL_Window* window) + GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) { VkResult res; res = volkInitialize(); @@ -811,7 +813,7 @@ namespace engine { throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); } - m_pimpl = std::make_unique(appInfo, window); + m_pimpl = std::make_unique(appName, appVersion, window); } From c2ec967a8908c1d9f1b7978c5a9e8e0af10f2bd8 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Wed, 5 Oct 2022 17:01:40 +0100 Subject: [PATCH 18/50] Make window handle private with getter --- include/window.hpp | 7 ++++--- src/engine.cpp | 2 +- src/window.cpp | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/window.hpp b/include/window.hpp index 39640c9..7686f1c 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -27,6 +27,8 @@ namespace engine { Window& operator=(const Window&) = delete; ~Window(); + SDL_Window* getHandle() const; + // Return the title name std::string getTitle() const; @@ -129,11 +131,10 @@ namespace engine { /* STATIC METHODS */ static void errorBox(const std::string& message); - public: - SDL_Window* m_handle; - private: + SDL_Window* m_handle; + bool m_shouldClose = false; std::string m_title; diff --git a/src/engine.cpp b/src/engine.cpp index e2bc81b..fade760 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,7 +8,7 @@ namespace engine { Application::Application(const char* appName, const char* appVersion) { m_win = std::make_unique(appName); - m_gfx = std::make_unique(appName, appVersion, m_win->m_handle); + m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); } Application::~Application() diff --git a/src/window.cpp b/src/window.cpp index c16d73c..552240b 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -179,6 +179,11 @@ namespace engine { // public methods + SDL_Window* Window::getHandle() const + { + return m_handle; + } + std::string Window::getTitle() const { return m_title; From 81031c9c10480fdd6130e2ad81edb0be9695934d Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 6 Oct 2022 11:30:44 +0100 Subject: [PATCH 19/50] Move everything into namespace --- include/components/camera.hpp | 2 +- include/components/component.hpp | 67 +++---- include/components/custom.hpp | 2 +- include/components/mesh_renderer.hpp | 2 +- include/components/text_ui_renderer.hpp | 2 +- include/gfx.hpp | 2 +- include/inputs/keyboard.hpp | 2 +- include/inputs/mouse.hpp | 2 +- include/object.hpp | 222 +++++++++++------------ include/resource_manager.hpp | 112 ++++++------ include/resources/font.hpp | 2 +- include/resources/mesh.hpp | 2 +- include/resources/resource.hpp | 28 +-- include/resources/shader.hpp | 4 +- include/resources/texture.hpp | 2 +- include/sceneroot.hpp | 34 ++-- include/transform.hpp | 16 +- include/window.hpp | 6 +- src/components/camera.cpp | 2 +- src/components/component.cpp | 44 ++--- src/components/custom.cpp | 2 +- src/components/mesh_renderer.cpp | 2 +- src/components/text_ui_renderer.cpp | 2 +- src/engine.cpp | 9 +- src/object.cpp | 227 ++++++++++++------------ src/resource_manager.cpp | 109 ++++++------ src/resources/font.cpp | 2 +- src/resources/mesh.cpp | 2 +- src/resources/resource.cpp | 32 ++-- src/resources/shader.cpp | 2 +- src/resources/texture.cpp | 4 +- src/sceneroot.cpp | 132 +++++++------- src/window.cpp | 32 ++-- 33 files changed, 581 insertions(+), 531 deletions(-) diff --git a/include/components/camera.hpp b/include/components/camera.hpp index dc6e91f..4e6db0d 100644 --- a/include/components/camera.hpp +++ b/include/components/camera.hpp @@ -11,7 +11,7 @@ #include #include -namespace components { +namespace engine::components { class ENGINE_API Camera : public Component { diff --git a/include/components/component.hpp b/include/components/component.hpp index 3c0af10..5781db1 100644 --- a/include/components/component.hpp +++ b/include/components/component.hpp @@ -5,42 +5,43 @@ namespace engine { class Window; class Input; -} -class Object; -class ResourceManager; + class Object; + class ResourceManager; -class ENGINE_API Component { + class ENGINE_API Component { -public: + public: + + enum class TypeEnum { + TRANSFORM, + CAMERA, + RENDERER, + UI, + CUSTOM, + }; + + Component(Object* parent, TypeEnum type); + Component(const Component&) = delete; + Component& operator=(const Component&) = delete; + virtual ~Component() = 0; + + int getID(); + TypeEnum getType(); + + Object& parent; + + protected: + engine::Window& win; + engine::Input& inp; + ResourceManager& res; + + private: + static int s_next_component_id; + + int m_id = s_next_component_id; + TypeEnum m_type; - enum class TypeEnum { - TRANSFORM, - CAMERA, - RENDERER, - UI, - CUSTOM, }; - Component(Object* parent, TypeEnum type); - Component(const Component&) = delete; - Component& operator=(const Component&) = delete; - virtual ~Component() = 0; - - int getID(); - TypeEnum getType(); - - Object& parent; - -protected: - engine::Window& win; - engine::Input& inp; - ResourceManager& res; - -private: - static int s_next_component_id; - - int m_id = s_next_component_id; - TypeEnum m_type; - -}; +} \ No newline at end of file diff --git a/include/components/custom.hpp b/include/components/custom.hpp index 96b8434..b258b6d 100644 --- a/include/components/custom.hpp +++ b/include/components/custom.hpp @@ -6,7 +6,7 @@ #include -namespace components { +namespace engine::components { class ENGINE_API CustomComponent : public Component { diff --git a/include/components/mesh_renderer.hpp b/include/components/mesh_renderer.hpp index bd68556..43a468f 100644 --- a/include/components/mesh_renderer.hpp +++ b/include/components/mesh_renderer.hpp @@ -12,7 +12,7 @@ #include #include -namespace components { +namespace engine::components { class ENGINE_API Renderer : public Component { diff --git a/include/components/text_ui_renderer.hpp b/include/components/text_ui_renderer.hpp index 7a82b0e..333649b 100644 --- a/include/components/text_ui_renderer.hpp +++ b/include/components/text_ui_renderer.hpp @@ -9,7 +9,7 @@ #include -namespace components { +namespace engine::components { class ENGINE_API UI : public Component { diff --git a/include/gfx.hpp b/include/gfx.hpp index 6210e26..cee3b3e 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -3,7 +3,7 @@ #include #include -namespace gfx { +namespace engine { enum class ShaderType { VERTEX, diff --git a/include/inputs/keyboard.hpp b/include/inputs/keyboard.hpp index a8a6e66..491bf18 100644 --- a/include/inputs/keyboard.hpp +++ b/include/inputs/keyboard.hpp @@ -2,7 +2,7 @@ // Keyboard scancodes, taken from SDL_scancode.h -namespace inputs { +namespace engine::inputs { enum class Key : int { UNKNOWN = 0, diff --git a/include/inputs/mouse.hpp b/include/inputs/mouse.hpp index 0715f25..712455c 100644 --- a/include/inputs/mouse.hpp +++ b/include/inputs/mouse.hpp @@ -1,6 +1,6 @@ #pragma once -namespace inputs { +namespace engine::inputs { enum class MouseButton : int { M_LEFT, diff --git a/include/object.hpp b/include/object.hpp index e835738..06ecc0a 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -15,128 +15,130 @@ namespace engine { class Window; class Input; -} -class ResourceManager; -class SceneRoot; -class Component; + class ResourceManager; -namespace components { - class Transform; - class Camera; - class Renderer; - class UI; - class CustomComponent; -} + class SceneRoot; + class Component; -struct GameIO { - engine::Window * const win; - engine::Input * const input; - ResourceManager * const resMan; -}; + namespace components { + class Transform; + class Camera; + class Renderer; + class UI; + class CustomComponent; + } -// This object lives until it is deleted by its parent(s) or finally when the "Scene" is destroyed. -// Therefore it is safe to return raw pointers -class ENGINE_API Object { - -public: - Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things); - Object(const Object&) = delete; - Object& operator=(const Object&) = delete; - ~Object(); - - engine::Window& win; - engine::Input& inp; - ResourceManager& res; - - SceneRoot& root; - - std::string getName(); - - Object* getParent(); - - Object* getChild(std::string name); - std::vector getChildren(); - - Object* createChild(std::string name); - void deleteChild(std::string name); - - void printTree(int level = 0); - - // Returns the component of type T - // Returns nullptr if the component is not found. - template T* getComponent(); - - // returns the component added - template T* createComponent(); - - template void deleteComponent(); - - struct CompList { - std::vector> cameras; - std::vector> renderers; - std::vector> uis; - std::vector> customs; + struct GameIO { + engine::Window* const win; + engine::Input* const input; + ResourceManager* const resMan; }; - - // Adds to the provided vector all components of this object and of its children recursively. - // Ignores 'Transform' - void getAllSubComponents(struct CompList& compList, glm::mat4 t); - Transform transform; + // This object lives until it is deleted by its parent(s) or finally when the "Scene" is destroyed. + // Therefore it is safe to return raw pointers + class ENGINE_API Object { -private: - static int s_object_count; - int m_id = s_object_count; - std::string m_name; + public: + Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things); + Object(const Object&) = delete; + Object& operator=(const Object&) = delete; + ~Object(); - std::list> m_children{}; - std::list> m_components{}; + engine::Window& win; + engine::Input& inp; + ResourceManager& res; - // If nullptr, this is the root object - Object* const m_parent; - struct GameIO m_gameIO; + SceneRoot& root; -}; + std::string getName(); -// implementation of template functions + Object* getParent(); -template T* Object::getComponent() -{ - if (std::is_base_of::value == false) { - throw std::runtime_error("getComponent() error: specified type is not a subclass of 'Component'"); - } - for (const auto& component : m_components) { - T* derived = dynamic_cast(component.get()); - if (derived != nullptr) { - return derived; + Object* getChild(std::string name); + std::vector getChildren(); + + Object* createChild(std::string name); + void deleteChild(std::string name); + + void printTree(int level = 0); + + // Returns the component of type T + // Returns nullptr if the component is not found. + template T* getComponent(); + + // returns the component added + template T* createComponent(); + + template void deleteComponent(); + + struct CompList { + std::vector> cameras; + std::vector> renderers; + std::vector> uis; + std::vector> customs; + }; + + // Adds to the provided vector all components of this object and of its children recursively. + // Ignores 'Transform' + void getAllSubComponents(struct CompList& compList, glm::mat4 t); + + Transform transform; + + private: + static int s_object_count; + int m_id = s_object_count; + std::string m_name; + + std::list> m_children{}; + std::list> m_components{}; + + // If nullptr, this is the root object + Object* const m_parent; + struct GameIO m_gameIO; + + }; + + // implementation of template functions + + template T* Object::getComponent() + { + if (std::is_base_of::value == false) { + throw std::runtime_error("getComponent() error: specified type is not a subclass of 'Component'"); } - } - return nullptr; -} - -template T* Object::createComponent() -{ - if (std::is_base_of::value == false) { - throw std::runtime_error("addComponent() error: specified type is not a subclass of 'Component'"); - } - if (getComponent() != nullptr) { - throw std::runtime_error("addComponent() error: attempt to add component of a type already present"); - } - m_components.emplace_back(std::make_unique(this)); - return dynamic_cast(m_components.back().get()); -} - -template void Object::deleteComponent() -{ - if (std::is_base_of::value == false) { - throw std::runtime_error("deleteComponent() error: specified type is not a subclass of 'Component'"); - } - for (auto itr = m_components.begin(); itr != m_components.end(); ++itr) { - if (dynamic_cast((*itr).get()) != nullptr) { - m_components.erase(itr); - return; + for (const auto& component : m_components) { + T* derived = dynamic_cast(component.get()); + if (derived != nullptr) { + return derived; + } } + return nullptr; } - throw std::runtime_error("deleteComponent() error: attempt to delete component that is not present."); -} + + template T* Object::createComponent() + { + if (std::is_base_of::value == false) { + throw std::runtime_error("addComponent() error: specified type is not a subclass of 'Component'"); + } + if (getComponent() != nullptr) { + throw std::runtime_error("addComponent() error: attempt to add component of a type already present"); + } + m_components.emplace_back(std::make_unique(this)); + return dynamic_cast(m_components.back().get()); + } + + template void Object::deleteComponent() + { + if (std::is_base_of::value == false) { + throw std::runtime_error("deleteComponent() error: specified type is not a subclass of 'Component'"); + } + for (auto itr = m_components.begin(); itr != m_components.end(); ++itr) { + if (dynamic_cast((*itr).get()) != nullptr) { + m_components.erase(itr); + return; + } + } + throw std::runtime_error("deleteComponent() error: attempt to delete component that is not present."); + } + +} \ No newline at end of file diff --git a/include/resource_manager.hpp b/include/resource_manager.hpp index 5077098..923e7b1 100644 --- a/include/resource_manager.hpp +++ b/include/resource_manager.hpp @@ -9,73 +9,77 @@ // Doesn't own resources, only holds weak_ptrs -class ENGINE_API ResourceManager { +namespace engine { -public: - ResourceManager(); - ResourceManager(const ResourceManager&) = delete; - ResourceManager& operator=(const ResourceManager&) = delete; - ~ResourceManager() = default; + class ENGINE_API ResourceManager { + + public: + ResourceManager(); + ResourceManager(const ResourceManager&) = delete; + ResourceManager& operator=(const ResourceManager&) = delete; + ~ResourceManager() = default; + + template + std::shared_ptr create(const std::string& name); + + // creates the resource if it is not in the map or the weak_ptr has expired + template + std::shared_ptr get(const std::string& name); + + std::unique_ptr getResourcesListString(); + + std::vector> getAllResourcesOfType(const std::string& type); + + std::filesystem::path getFilePath(const std::string& name); + + private: + std::filesystem::path m_resourcesPath; + std::unordered_map> m_resources; + + }; template - std::shared_ptr create(const std::string& name); + std::shared_ptr ResourceManager::create(const std::string& name) + { + if (std::is_base_of::value == false) { + throw std::runtime_error("ResourceManager::create() error: specified type is not a subclass of 'Resource'"); + } + auto resource = std::make_shared(getFilePath(name)); + m_resources.emplace(name, std::dynamic_pointer_cast(resource)); + return resource; + } - // creates the resource if it is not in the map or the weak_ptr has expired template - std::shared_ptr get(const std::string& name); + std::shared_ptr ResourceManager::get(const std::string& name) + { - std::unique_ptr getResourcesListString(); + if (std::is_base_of::value == false) { + throw std::runtime_error("ResourceManager::get() error: specified type is not a subclass of 'Resource'"); + } - std::vector> getAllResourcesOfType(const std::string& type); + if (m_resources.contains(name)) { - std::filesystem::path getFilePath(const std::string& name); + std::weak_ptr res = m_resources.at(name); -private: - std::filesystem::path m_resourcesPath; - std::unordered_map> m_resources; - -}; - -template -std::shared_ptr ResourceManager::create(const std::string& name) -{ - if (std::is_base_of::value == false) { - throw std::runtime_error("ResourceManager::create() error: specified type is not a subclass of 'Resource'"); - } - auto resource = std::make_shared(getFilePath(name)); - m_resources.emplace(name, std::dynamic_pointer_cast(resource)); - return resource; -} - -template -std::shared_ptr ResourceManager::get(const std::string& name) -{ - - if (std::is_base_of::value == false) { - throw std::runtime_error("ResourceManager::get() error: specified type is not a subclass of 'Resource'"); - } - - if (m_resources.contains(name)) { - - std::weak_ptr res = m_resources.at(name); - - if (res.expired() == false) { - // resource definitely exists - auto castedSharedPtr = std::dynamic_pointer_cast(res.lock()); - if (castedSharedPtr == nullptr) { - throw std::runtime_error("error: attempt to get Resource which already exists as another type"); + if (res.expired() == false) { + // resource definitely exists + auto castedSharedPtr = std::dynamic_pointer_cast(res.lock()); + if (castedSharedPtr == nullptr) { + throw std::runtime_error("error: attempt to get Resource which already exists as another type"); + } + else { + return castedSharedPtr; + } } else { - return castedSharedPtr; + // Resource in map but no longer exists. Delete it. + m_resources.erase(name); } + } - else { - // Resource in map but no longer exists. Delete it. - m_resources.erase(name); - } + + return create(name); } - - return create(name); -} +} \ No newline at end of file diff --git a/include/resources/font.hpp b/include/resources/font.hpp index b284bfd..1e868c3 100644 --- a/include/resources/font.hpp +++ b/include/resources/font.hpp @@ -10,7 +10,7 @@ #include -namespace resources { +namespace engine::resources { class ENGINE_API Font : public Resource { diff --git a/include/resources/mesh.hpp b/include/resources/mesh.hpp index 4eb5349..b270cb0 100644 --- a/include/resources/mesh.hpp +++ b/include/resources/mesh.hpp @@ -20,7 +20,7 @@ struct Vertex { glm::vec2 uv; }; -namespace resources { +namespace engine::resources { class ENGINE_API Mesh : public Resource { diff --git a/include/resources/resource.hpp b/include/resources/resource.hpp index f18ae00..50bc78d 100644 --- a/include/resources/resource.hpp +++ b/include/resources/resource.hpp @@ -5,20 +5,24 @@ #include #include -class ENGINE_API Resource { +namespace engine { -public: - Resource(const std::filesystem::path& resPath, const std::string& type); - Resource(const Resource&) = delete; - Resource& operator=(const Resource&) = delete; - virtual ~Resource() = 0; + class ENGINE_API Resource { - std::string getType(); + public: + Resource(const std::filesystem::path& resPath, const std::string& type); + Resource(const Resource&) = delete; + Resource& operator=(const Resource&) = delete; + virtual ~Resource() = 0; -protected: - std::filesystem::path m_resourcePath; + std::string getType(); -private: - const std::string m_type; + protected: + std::filesystem::path m_resourcePath; -}; + private: + const std::string m_type; + + }; + +} \ No newline at end of file diff --git a/include/resources/shader.hpp b/include/resources/shader.hpp index 60ed5bf..839e00b 100644 --- a/include/resources/shader.hpp +++ b/include/resources/shader.hpp @@ -11,7 +11,7 @@ #include #include -namespace resources { +namespace engine::resources { class ENGINE_API Shader : public Resource { @@ -40,7 +40,7 @@ public: static void invalidate() { - s_activeProgram = -1; + s_activeProgram = std::numeric_limits::max(); } private: diff --git a/include/resources/texture.hpp b/include/resources/texture.hpp index 2c6e334..cdfc9a5 100644 --- a/include/resources/texture.hpp +++ b/include/resources/texture.hpp @@ -6,7 +6,7 @@ #include -namespace resources { +namespace engine::resources { class ENGINE_API Texture : public Resource { diff --git a/include/sceneroot.hpp b/include/sceneroot.hpp index 5096b50..a3bc312 100644 --- a/include/sceneroot.hpp +++ b/include/sceneroot.hpp @@ -6,24 +6,28 @@ #include -// Holds everything you would expect to find in a game scene -class ENGINE_API SceneRoot : public Object { +namespace engine { -public: - // create a new empty scene - SceneRoot(struct GameIO things); - SceneRoot(const std::filesystem::path& file, struct GameIO things); + // Holds everything you would expect to find in a game scene + class ENGINE_API SceneRoot : public Object { - SceneRoot(const SceneRoot&) = delete; - SceneRoot& operator=(const SceneRoot&) = delete; - ~SceneRoot(); + public: + // create a new empty scene + SceneRoot(struct GameIO things); + SceneRoot(const std::filesystem::path& file, struct GameIO things); - void updateStuff(); + SceneRoot(const SceneRoot&) = delete; + SceneRoot& operator=(const SceneRoot&) = delete; + ~SceneRoot(); - void activateCam(int id); - void deactivateCam(int id); + void updateStuff(); -private: - std::vector m_activeCameras{}; + void activateCam(int id); + void deactivateCam(int id); -}; + private: + std::vector m_activeCameras{}; + + }; + +} \ No newline at end of file diff --git a/include/transform.hpp b/include/transform.hpp index dbb3122..938a6ff 100644 --- a/include/transform.hpp +++ b/include/transform.hpp @@ -6,12 +6,16 @@ #include #include -struct ENGINE_API Transform { +namespace engine { -// Scale, rotate (XYZ), translate + struct ENGINE_API Transform { - glm::vec3 position{0.0f}; - glm::quat rotation{}; - glm::vec3 scale{1.0f}; + // Scale, rotate (XYZ), translate -}; \ No newline at end of file + glm::vec3 position{ 0.0f }; + glm::quat rotation{}; + glm::vec3 scale{ 1.0f }; + + }; + +} \ No newline at end of file diff --git a/include/window.hpp b/include/window.hpp index 7686f1c..544a458 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -22,7 +22,7 @@ namespace engine { class ENGINE_API Window { public: - Window(const std::string& title); + Window(const std::string& title, bool resizable = true); Window(const Window&) = delete; Window& operator=(const Window&) = delete; ~Window(); @@ -52,7 +52,7 @@ namespace engine { // Returns true if the window should remain open bool isRunning() const; - void setFullscreen(bool fullscreen, bool exclusive = true); + void setFullscreen(bool fullscreen, bool exclusive = false); void toggleFullscreen(); bool isFullscreen() const; @@ -139,6 +139,8 @@ namespace engine { std::string m_title; + bool m_resizable; + bool m_fullscreen = false; bool m_justResized = false; bool m_keyboardFocus = true; diff --git a/src/components/camera.cpp b/src/components/camera.cpp index 3c83fdc..e7222b2 100644 --- a/src/components/camera.cpp +++ b/src/components/camera.cpp @@ -13,7 +13,7 @@ static const std::string VIEW_MAT_UNIFORM = "viewMat"; static const std::string PROJ_MAT_UNIFORM = "projMat"; static const std::string WINDOW_SIZE_UNIFORM = "windowSize"; -namespace components { +namespace engine::components { glm::vec4 Camera::s_clearColor{-999.0f, -999.0f, -999.0f, -999.0f}; diff --git a/src/components/component.cpp b/src/components/component.cpp index 9bc9f45..ca91f33 100644 --- a/src/components/component.cpp +++ b/src/components/component.cpp @@ -4,27 +4,31 @@ #include -int Component::s_next_component_id = 0; +namespace engine { -Component::Component(Object* parent, TypeEnum type) : - m_type(type), parent(*parent), - win(parent->win), - inp(parent->inp), - res(parent->res) -{ - s_next_component_id++; -} + int Component::s_next_component_id = 0; -Component::~Component() -{ -} + Component::Component(Object* parent, TypeEnum type) : + m_type(type), parent(*parent), + win(parent->win), + inp(parent->inp), + res(parent->res) + { + s_next_component_id++; + } -int Component::getID() -{ - return m_id; -} + Component::~Component() + { + } -Component::TypeEnum Component::getType() -{ - return m_type; -} + int Component::getID() + { + return m_id; + } + + Component::TypeEnum Component::getType() + { + return m_type; + } + +} \ No newline at end of file diff --git a/src/components/custom.cpp b/src/components/custom.cpp index a1feee8..931b59b 100644 --- a/src/components/custom.cpp +++ b/src/components/custom.cpp @@ -1,6 +1,6 @@ #include "components/custom.hpp" -namespace components { +namespace engine::components { CustomComponent::CustomComponent(Object* parent) : Component(parent, TypeEnum::CUSTOM) { diff --git a/src/components/mesh_renderer.cpp b/src/components/mesh_renderer.cpp index 189fdc2..8c98701 100644 --- a/src/components/mesh_renderer.cpp +++ b/src/components/mesh_renderer.cpp @@ -6,7 +6,7 @@ #include -namespace components { +namespace engine::components { Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER) { diff --git a/src/components/text_ui_renderer.cpp b/src/components/text_ui_renderer.cpp index 5a26fff..a7011bb 100644 --- a/src/components/text_ui_renderer.cpp +++ b/src/components/text_ui_renderer.cpp @@ -4,7 +4,7 @@ #include "resource_manager.hpp" #include "resources/texture.hpp" -namespace components { +namespace engine::components { UI::UI(Object* parent) : Component(parent, TypeEnum::UI) { diff --git a/src/engine.cpp b/src/engine.cpp index fade760..44b4112 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -7,7 +7,7 @@ namespace engine { Application::Application(const char* appName, const char* appVersion) { - m_win = std::make_unique(appName); + m_win = std::make_unique(appName, true); m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); } @@ -34,12 +34,7 @@ namespace engine { } if (m_win->getKeyPress(inputs::Key::F11)) { - if (m_win->isFullscreen()) { - m_win->setFullscreen(false); - } - else { - m_win->setFullscreen(true, false); // borderless window - } + m_win->toggleFullscreen(); } if (m_win->getKeyPress(inputs::Key::ESCAPE)) { m_win->setCloseFlag(); diff --git a/src/object.cpp b/src/object.cpp index f81c599..11d74d3 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -7,127 +7,132 @@ #include -int Object::s_object_count = 0; +namespace engine { -Object::Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things) - : m_name(name), m_parent(parent), root(root), - m_gameIO(things), - win(*things.win), - inp(*things.input), - res(*things.resMan) -{ - s_object_count++; -} + int Object::s_object_count = 0; -Object::~Object() -{ -} + Object::Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things) + : m_name(name), m_parent(parent), root(root), + m_gameIO(things), + win(*things.win), + inp(*things.input), + res(*things.resMan) + { + s_object_count++; + } -std::string Object::getName() -{ - return m_name; -} + Object::~Object() + { + } -Object* Object::getParent() -{ - return m_parent; -} + std::string Object::getName() + { + return m_name; + } -Object* Object::getChild(std::string name) -{ - for (const auto& child : m_children) { - if (name == child->getName()) { - return child.get(); + Object* Object::getParent() + { + return m_parent; + } + + Object* Object::getChild(std::string name) + { + for (const auto& child : m_children) { + if (name == child->getName()) { + return child.get(); + } + } + return nullptr; + } + + std::vector Object::getChildren() + { + std::vector newVector{}; + for (const auto& child : m_children) { + newVector.push_back(child.get()); + } + return newVector; + } + + Object* Object::createChild(std::string name) + { + if (getChild(name) != nullptr) { + throw std::runtime_error("Attempt to create child object with existing name"); + } + m_children.emplace_back(std::make_unique(name, this, root, m_gameIO)); + return m_children.back().get(); + } + + void Object::deleteChild(std::string name) + { + for (auto itr = m_children.begin(); itr != m_children.end(); ++itr) { + if ((*itr)->getName() == name) { + m_children.erase(itr); + return; + } + } + throw std::runtime_error("Unable to delete child '" + name + "' as it does not exist"); + } + + void Object::printTree(int level) + { + std::string buf; + for (int i = 0; i < level; i++) { + if (i + 1 == level) { + buf += "\\_______"; + } + else { + buf += " "; + } + } + buf += m_name; + INFO(buf); + for (const auto& child : this->getChildren()) { + child->printTree(level + 1); } } - return nullptr; -} -std::vector Object::getChildren() -{ - std::vector newVector{}; - for (const auto& child : m_children) { - newVector.push_back(child.get()); - } - return newVector; -} + void Object::getAllSubComponents(struct CompList& compList, glm::mat4 parentTransform) + { + using namespace components; -Object* Object::createChild(std::string name) -{ - if (getChild(name) != nullptr) { - throw std::runtime_error("Attempt to create child object with existing name"); - } - m_children.emplace_back(std::make_unique(name, this, root, m_gameIO)); - return m_children.back().get(); -} + glm::mat4 objTransform{ 1.0f }; -void Object::deleteChild(std::string name) -{ - for (auto itr = m_children.begin(); itr != m_children.end(); ++itr) { - if ((*itr)->getName() == name) { - m_children.erase(itr); - return; + auto t = transform; + + // rotation + objTransform = glm::mat4_cast(t.rotation); + + // position + reinterpret_cast(objTransform[3]) = t.position; + + // scale (effectively applied first + objTransform = glm::scale(objTransform, t.scale); + + const glm::mat4 newTransform = parentTransform * objTransform; + + for (const auto& compUnq : m_components) { + const auto comp = compUnq.get(); + switch (comp->getType()) { + case Component::TypeEnum::CAMERA: + compList.cameras.emplace_back(dynamic_cast(comp), newTransform); + break; + case Component::TypeEnum::RENDERER: + compList.renderers.emplace_back(dynamic_cast(comp), newTransform); + break; + case Component::TypeEnum::UI: + compList.uis.emplace_back(dynamic_cast(comp), newTransform); + break; + case Component::TypeEnum::CUSTOM: + compList.customs.emplace_back(dynamic_cast(comp), newTransform); + break; + default: + break; + } + } + for (const auto& child : m_children) { + child->getAllSubComponents(compList, newTransform); } } - throw std::runtime_error("Unable to delete child '" + name + "' as it does not exist"); -} -void Object::printTree(int level) -{ - std::string buf; - for (int i = 0; i < level; i++) { - if (i+1 == level) { - buf += "\\_______"; - } else { - buf += " "; - } - } - buf += m_name; - INFO(buf); - for (const auto& child : this->getChildren()) { - child->printTree(level+1); - } -} - -void Object::getAllSubComponents(struct CompList& compList, glm::mat4 parentTransform) -{ - using namespace components; - - glm::mat4 objTransform{1.0f}; - - auto t = transform; - - // rotation - objTransform = glm::mat4_cast(t.rotation); - - // position - reinterpret_cast(objTransform[3]) = t.position; - - // scale (effectively applied first - objTransform = glm::scale(objTransform, t.scale); - - const glm::mat4 newTransform = parentTransform * objTransform; - - for (const auto& compUnq : m_components) { - const auto comp = compUnq.get(); - switch (comp->getType()) { - case Component::TypeEnum::CAMERA: - compList.cameras.emplace_back(dynamic_cast(comp), newTransform); - break; - case Component::TypeEnum::RENDERER: - compList.renderers.emplace_back(dynamic_cast(comp), newTransform); - break; - case Component::TypeEnum::UI: - compList.uis.emplace_back(dynamic_cast(comp), newTransform); - break; - case Component::TypeEnum::CUSTOM: - compList.customs.emplace_back(dynamic_cast(comp), newTransform); - break; - default: - break; - } - } - for (const auto& child : m_children) { - child->getAllSubComponents(compList, newTransform); - } -} +} \ No newline at end of file diff --git a/src/resource_manager.cpp b/src/resource_manager.cpp index 6a2da36..dfef6da 100644 --- a/src/resource_manager.cpp +++ b/src/resource_manager.cpp @@ -8,68 +8,73 @@ #include "log.hpp" -ResourceManager::ResourceManager() -{ +namespace engine { + + ResourceManager::ResourceManager() + { #ifdef _MSC_VER - CHAR exeDirBuf[MAX_PATH + 1]; - GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1); - std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); - (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); + CHAR exeDirBuf[MAX_PATH + 1]; + GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1); + std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); + (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); #else - std::filesystem::path cwd = std::filesystem::current_path(); + std::filesystem::path cwd = std::filesystem::current_path(); #endif - if (std::filesystem::is_directory(cwd / "res")) { - m_resourcesPath = cwd / "res"; - } else { - m_resourcesPath = cwd.parent_path() / "share" / "sdltest"; - } - - if (std::filesystem::is_directory(m_resourcesPath) == false) { - m_resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest"; - } - - if (std::filesystem::is_directory(m_resourcesPath) == false) { - throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); - } -} - -std::unique_ptr ResourceManager::getResourcesListString() -{ - auto bufPtr = std::make_unique(); - std::string& buf = *bufPtr; - int maxLength = 0; - for (const auto& [name, ptr] : m_resources) { - if (name.length() > maxLength) - maxLength = name.length(); - } - for (const auto& [name, ptr] : m_resources) { - buf += name; - for (int i = 0; i < (maxLength - name.length() + 4); i++) { - buf += " "; + if (std::filesystem::is_directory(cwd / "res")) { + m_resourcesPath = cwd / "res"; + } + else { + m_resourcesPath = cwd.parent_path() / "share" / "sdltest"; + } + + if (std::filesystem::is_directory(m_resourcesPath) == false) { + m_resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest"; + } + + if (std::filesystem::is_directory(m_resourcesPath) == false) { + throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); } - buf += std::to_string(ptr.use_count()) + "\n"; } - return bufPtr; -} + + std::unique_ptr ResourceManager::getResourcesListString() + { + auto bufPtr = std::make_unique(); + std::string& buf = *bufPtr; + int maxLength = 0; + for (const auto& [name, ptr] : m_resources) { + if (name.length() > maxLength) + maxLength = name.length(); + } + for (const auto& [name, ptr] : m_resources) { + buf += name; + for (int i = 0; i < (maxLength - name.length() + 4); i++) { + buf += " "; + } + buf += std::to_string(ptr.use_count()) + "\n"; + } + return bufPtr; + } -std::vector> ResourceManager::getAllResourcesOfType(const std::string& type) -{ - std::vector> resources; - for (const auto& [name, ptr] : m_resources) { - if (ptr.expired() == false) { - if (ptr.lock()->getType() == type) { - resources.push_back(ptr); + std::vector> ResourceManager::getAllResourcesOfType(const std::string& type) + { + std::vector> resources; + for (const auto& [name, ptr] : m_resources) { + if (ptr.expired() == false) { + if (ptr.lock()->getType() == type) { + resources.push_back(ptr); + } } } + return resources; } - return resources; -} -// private + // private -std::filesystem::path ResourceManager::getFilePath(const std::string& name) -{ - return m_resourcesPath / name; -} + std::filesystem::path ResourceManager::getFilePath(const std::string& name) + { + return m_resourcesPath / name; + } + +} \ No newline at end of file diff --git a/src/resources/font.cpp b/src/resources/font.cpp index 41df828..8a505aa 100644 --- a/src/resources/font.cpp +++ b/src/resources/font.cpp @@ -3,7 +3,7 @@ #include #include FT_FREETYPE_H -namespace resources { +namespace engine::resources { Font::Font(const std::filesystem::path& resPath) : Resource(resPath, "font") { diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index c155882..b6092f3 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -1,6 +1,6 @@ #include "resources/mesh.hpp" -namespace resources { +namespace engine::resources { struct MeshFileHeader { unsigned int vertex_count; diff --git a/src/resources/resource.cpp b/src/resources/resource.cpp index faa40b7..457ecef 100644 --- a/src/resources/resource.cpp +++ b/src/resources/resource.cpp @@ -2,19 +2,23 @@ #include -Resource::Resource(const std::filesystem::path& resPath, const std::string& type) : m_resourcePath(resPath), m_type(type) -{ - if (m_type != "mesh") - TRACE("Creating {} resource: {}", type, resPath.filename().string()); -} +namespace engine { -Resource::~Resource() -{ - if (m_type != "mesh") - TRACE("Destroyed {} resource: {}", m_type, m_resourcePath.filename().string()); -} + Resource::Resource(const std::filesystem::path& resPath, const std::string& type) : m_resourcePath(resPath), m_type(type) + { + if (m_type != "mesh") + TRACE("Creating {} resource: {}", type, resPath.filename().string()); + } -std::string Resource::getType() -{ - return m_type; -} + Resource::~Resource() + { + if (m_type != "mesh") + TRACE("Destroyed {} resource: {}", m_type, m_resourcePath.filename().string()); + } + + std::string Resource::getType() + { + return m_type; + } + +} \ No newline at end of file diff --git a/src/resources/shader.cpp b/src/resources/shader.cpp index 59c1a7f..85dec72 100644 --- a/src/resources/shader.cpp +++ b/src/resources/shader.cpp @@ -50,7 +50,7 @@ static GLuint compile(const char *path, GLenum type) } return handle; } -namespace resources { +namespace engine::resources { // I've got to do this because of GL's stupid state machine GLuint Shader::s_activeProgram = 0; diff --git a/src/resources/texture.cpp b/src/resources/texture.cpp index c46afbf..9bc9119 100644 --- a/src/resources/texture.cpp +++ b/src/resources/texture.cpp @@ -7,7 +7,7 @@ #include -namespace resources { +namespace engine::resources { // -1 means invalid / no bind GLuint Texture::s_binded_texture = -1; @@ -28,7 +28,7 @@ static bool readPNG(const std::string& path, std::vector& texbuf, int * return false; } - const size_t size = x * y * n; + const size_t size = (size_t)x * (size_t)y * (size_t)n; texbuf.resize(size); memcpy(texbuf.data(), data, size); diff --git a/src/sceneroot.cpp b/src/sceneroot.cpp index 3ceba42..9d952b7 100644 --- a/src/sceneroot.cpp +++ b/src/sceneroot.cpp @@ -14,77 +14,81 @@ #include "log.hpp" -SceneRoot::SceneRoot(struct GameIO things) : Object("root", nullptr, *this, things) -{ -} +namespace engine { -SceneRoot::SceneRoot(const std::filesystem::path& file, struct GameIO things) : SceneRoot(things) -{ - // TODO: make this a resource - //loadFromSceneFile(file); -} - -SceneRoot::~SceneRoot() -{ -} - -// private methods - -// public methods - -void SceneRoot::updateStuff() -{ - - using namespace components; - using namespace glm; - - struct CompList compList{}; - - getAllSubComponents(compList, glm::mat4{1.0f}); - - // update - - for (const auto& [c, t] : compList.customs) { - c->onUpdate(t); + SceneRoot::SceneRoot(struct GameIO things) : Object("root", nullptr, *this, things) + { } - // render + SceneRoot::SceneRoot(const std::filesystem::path& file, struct GameIO things) : SceneRoot(things) + { + // TODO: make this a resource + //loadFromSceneFile(file); + } - for (const auto& [c, t] : compList.cameras) { - for (int id : m_activeCameras) { - if (c->getID() == id) { - c->updateCam(t); - for (const auto& [c, t] : compList.renderers) { - c->render(t); + SceneRoot::~SceneRoot() + { + } + + // private methods + + // public methods + + void SceneRoot::updateStuff() + { + + using namespace components; + using namespace glm; + + struct CompList compList {}; + + getAllSubComponents(compList, glm::mat4{ 1.0f }); + + // update + + for (const auto& [c, t] : compList.customs) { + c->onUpdate(t); + } + + // render + + for (const auto& [c, t] : compList.cameras) { + for (int id : m_activeCameras) { + if (c->getID() == id) { + c->updateCam(t); + for (const auto& [c, t] : compList.renderers) { + c->render(t); + } + + break; } + } + } - break; + for (const auto& [c, t] : compList.uis) { + c->render(t); + } + + } + + void SceneRoot::activateCam(int id) + { + auto& v = m_activeCameras; + + if (std::find(v.begin(), v.end(), id) == v.end()) { + v.push_back(id); + } + } + + void SceneRoot::deactivateCam(int id) + { + auto& v = m_activeCameras; + + for (auto it = v.begin(); it != v.end(); it++) { + if (*it == id) { + v.erase(it); } } } - for (const auto& [c, t] : compList.uis) { - c->render(t); - } - -} - -void SceneRoot::activateCam(int id) -{ - auto& v = m_activeCameras; - - if (std::find(v.begin(), v.end(), id) == v.end()) { - v.push_back(id); - } -} - -void SceneRoot::deactivateCam(int id) -{ - auto& v = m_activeCameras; - - for (auto it = v.begin(); it != v.end(); it++) { - if (*it == id) { - v.erase(it); - } - } -} +} \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index 552240b..0e694be 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -9,7 +9,7 @@ const uint64_t BILLION = 1000000000; namespace engine { - Window::Window(const std::string& title) : m_title(title) + Window::Window(const std::string& title, bool resizable) : m_title(title), m_resizable(resizable) { // init SDL @@ -26,13 +26,23 @@ namespace engine { m_lastFrameStamp = m_startTime - 1; m_avgFpsStart = m_startTime; + Uint32 windowFlags = SDL_WINDOW_SHOWN; + +#ifdef ENGINE_BUILD_VULKAN + windowFlags |= SDL_WINDOW_VULKAN; +#endif + + if (m_resizable) { + windowFlags |= SDL_WINDOW_RESIZABLE; + } + // create the window m_handle = SDL_CreateWindow( m_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(m_winSize.x), static_cast(m_winSize.y), - SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN); + windowFlags); if (m_handle == NULL) { SDL_Quit(); throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); @@ -278,14 +288,16 @@ namespace engine { void Window::setFullscreen(bool fullscreen, bool exclusive) { - if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { - throw std::runtime_error("Unable to set window to fullscreen/windowed"); - } - m_fullscreen = fullscreen; - if (fullscreen) { - int width, height; - SDL_GetWindowSize(m_handle, &width, &height); - onResize(width, height); + if (m_resizable) { + if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { + throw std::runtime_error("Unable to set window to fullscreen/windowed"); + } + m_fullscreen = fullscreen; + if (fullscreen) { + int width, height; + SDL_GetWindowSize(m_handle, &width, &height); + onResize(width, height); + } } } From a9b0918235b386a4c90a30ee902b0e5911db72ab Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 6 Oct 2022 16:26:29 +0100 Subject: [PATCH 20/50] Start adding depth buffer --- src/gfx_device_vulkan.cpp | 68 +++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 9256d45..3359a66 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -71,7 +71,6 @@ namespace engine { m_swapchain = std::make_unique(device); // owns the device - INFO("Instance use count: {}", instance.use_count()); } private: @@ -565,6 +564,27 @@ namespace engine { vkGetDeviceQueue(m_handle, q.familyIndex, q.queueIndex, &q.handle); } + Queue gfxQueue = getGraphicsQueue(); + + VkCommandPoolCreateInfo gfxCmdPoolInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = gfxQueue.familyIndex, + }; + + res = vkCreateCommandPool(m_handle, &gfxCmdPoolInfo, nullptr, &m_gfxCommandPool); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo gfxCmdBufInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = m_gfxCommandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); + assert(res == VK_SUCCESS); + } Device(const Device&) = delete; Device& operator=(const Device&) = delete; @@ -572,6 +592,7 @@ namespace engine { ~Device() { TRACE("Destroying device..."); + vkDestroyCommandPool(m_handle, m_gfxCommandPool, nullptr); vkDestroyDevice(m_handle, nullptr); } @@ -601,6 +622,16 @@ namespace engine { VkQueue handle; }; + VkCommandBuffer getGraphicsCommandBuffer() + { + return m_gfxCommandBuffer; + } + + std::shared_ptr getSurface() + { + return m_surface; + } + Queue getGraphicsQueue() { for (const auto& queue : m_queues) { @@ -617,18 +648,13 @@ namespace engine { throw std::runtime_error("Unable to find transfer queue"); } - Queue getComputeQueue() + /*Queue getComputeQueue() { for (const auto& queue : m_queues) { if (queue.supportsCompute) return queue; } throw std::runtime_error("Unable to find compute queue"); - } - - std::shared_ptr getSurface() - { - return m_surface; - } + }*/ private: std::shared_ptr m_surface; @@ -639,10 +665,15 @@ namespace engine { std::vector m_queues{}; + VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; + VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; + }; class Swapchain { + // this class also creates a depth buffer image + public: Swapchain(std::shared_ptr device) : m_device(device) { @@ -765,6 +796,24 @@ namespace engine { assert (res == VK_SUCCESS); m_imageViews.push_back(imageView); + + // create depth buffer + + VkImageCreateInfo depthImageInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_D16_UNORM, + .extent = { + .width = chosenSwapExtent.width, + .height = chosenSwapExtent.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = + }; + } } @@ -794,6 +843,7 @@ namespace engine { }; + std::unique_ptr m_debugMessenger; // uses instance std::unique_ptr m_swapchain; @@ -824,7 +874,7 @@ namespace engine { void GFXDevice::draw() { - TRACE("Drawing"); + } } From 04cc5fbcc93abbe2cba7dd09f0d5ebf6217ca561 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 7 Oct 2022 12:20:39 +0100 Subject: [PATCH 21/50] do stuff --- src/gfx_device_vulkan.cpp | 69 ++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 3359a66..9785709 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -48,6 +48,13 @@ namespace engine { return surface; } + static VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) + { + for (VkFormat format : candidates) { + + } + } + class GFXDevice::Impl { public: @@ -356,8 +363,6 @@ namespace engine { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - for (const auto& dev : physicalDevices) { // first, check extension support @@ -421,17 +426,17 @@ namespace engine { continue; } - physicalDevice = dev; + m_physicalDevice = dev; break; } // end for() - if (physicalDevice == VK_NULL_HANDLE) { + if (m_physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("No suitable Vulkan physical device found"); } VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(physicalDevice, &devProps); + vkGetPhysicalDeviceProperties(m_physicalDevice, &devProps); INFO("Selected physical device: {}", devProps.deviceName); TRACE("Supported present modes:"); @@ -466,9 +471,9 @@ namespace engine { // Get the queue families and find ones that support graphics, transfer, and compute uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr); std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data()); std::optional graphicsFamilyIndex; std::optional transferFamilyIndex; @@ -534,7 +539,7 @@ namespace engine { // check the physical device is compatible with the surface VkBool32 graphicsQueueCanPresent; - res = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); + res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); assert(res == VK_SUCCESS); if (graphicsQueueCanPresent != VK_TRUE) { throw std::runtime_error("The selected queue family does not support this surface"); @@ -553,7 +558,7 @@ namespace engine { .pEnabledFeatures = nullptr, }; - res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &m_handle); + res = vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_handle); if (res != VK_SUCCESS) { throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); } @@ -601,6 +606,11 @@ namespace engine { return m_handle; } + VkPhysicalDevice getPhysicalDevice() const + { + return m_physicalDevice; + } + struct SwapchainSupportDetails { VkSurfaceCapabilitiesKHR caps{}; std::vector formats{}; @@ -663,6 +673,8 @@ namespace engine { VkDevice m_handle = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + std::vector m_queues{}; VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; @@ -672,8 +684,6 @@ namespace engine { class Swapchain { - // this class also creates a depth buffer image - public: Swapchain(std::shared_ptr device) : m_device(device) { @@ -798,21 +808,8 @@ namespace engine { m_imageViews.push_back(imageView); // create depth buffer - - VkImageCreateInfo depthImageInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_D16_UNORM, - .extent = { - .width = chosenSwapExtent.width, - .height = chosenSwapExtent.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = - }; + + m_depthBuffer = std::make_unique(m_currentExtent); } @@ -840,6 +837,26 @@ namespace engine { VkFormat m_currentFormat{}; VkExtent2D m_currentExtent{}; + class DepthBuffer { + public: + DepthBuffer(VkExtent2D bufferExtent) + { + VkImageCreateInfo depthImageInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + }; + } + DepthBuffer(const DepthBuffer&) = delete; + DepthBuffer& operator=(const DepthBuffer&) = delete; + ~DepthBuffer() + { + + } + }; + + std::unique_ptr m_depthBuffer; + }; From aeda8914da6cdddbbe6c3e89d0abff71b1db5bbc Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 7 Oct 2022 15:18:09 +0100 Subject: [PATCH 22/50] Add more stuff --- include/gfx_device.hpp | 27 +++++++ src/gfx_device_vulkan.cpp | 148 +++++++++++++++++++++++++++++++++----- 2 files changed, 157 insertions(+), 18 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index e54bf41..a05be31 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -7,6 +7,31 @@ struct SDL_Window; namespace engine { + + namespace gfx { + + enum class BufferUsage { + DEFAULT, + UPLOAD, + READBACK, + }; + + enum class BindFlag { + NONE = 0, + UNIFORM_BUFFER = 1 << 0, + }; + + struct BufferDesc { + uint64_t size; + BufferUsage usage; + BindFlag bindFlags; + }; + + // handles (incomplete types) + + class BufferHandle; + + }; class ENGINE_API GFXDevice { @@ -20,6 +45,8 @@ namespace engine { // submit command lists and draw to the screen void draw(); + bool createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out); + private: class Impl; std::unique_ptr m_pimpl; diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 9785709..68566b3 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -48,11 +48,17 @@ namespace engine { return surface; } - static VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) + static uint32_t getMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) { - for (VkFormat format : candidates) { - + VkPhysicalDeviceMemoryProperties memProps; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); + for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { + if (typeFilter & (1 << i)) { + return i; + } } + + throw std::runtime_error("Failed to find a suitable memory type"); } class GFXDevice::Impl { @@ -807,12 +813,12 @@ namespace engine { m_imageViews.push_back(imageView); - // create depth buffer - - m_depthBuffer = std::make_unique(m_currentExtent); - } + // create depth buffer + + m_depthBuffer = std::make_unique(m_currentExtent, m_device.get()); + } Swapchain(const Swapchain&) = delete; Swapchain& operator=(const Swapchain&) = delete; @@ -837,30 +843,136 @@ namespace engine { VkFormat m_currentFormat{}; VkExtent2D m_currentExtent{}; - class DepthBuffer { + class DepthStencil { public: - DepthBuffer(VkExtent2D bufferExtent) + DepthStencil(VkExtent2D bufferExtent, Device* device) : m_device(device) { - VkImageCreateInfo depthImageInfo{ + // find a suitable format + + const std::vector formatCandidates{ + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + }; + + const VkFormatFeatureFlags formatFeatures = + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + + std::optional chosenFormat; + + for (VkFormat format : formatCandidates) { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(m_device->getPhysicalDevice(), format, &props); + + if ((props.optimalTilingFeatures & formatFeatures) == formatFeatures) { + chosenFormat = format; + break; + } + } + + if (chosenFormat.has_value() == false) { + throw std::runtime_error("Unable to find a suitable depth-stencil format"); + } + + m_format = chosenFormat.value(); + + switch (m_format) { + case VK_FORMAT_D24_UNORM_S8_UINT: + INFO("VK_FORMAT_D24_UNORM_S8_UINT"); + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + INFO("VK_FORMAT_D32_SFLOAT_S8_UINT"); + break; + default: + break; + } + + VkImageCreateInfo imageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, .flags = 0, .imageType = VK_IMAGE_TYPE_2D, + .format = m_format, + .extent = { + .width = bufferExtent.width, + .height = bufferExtent.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; - } - DepthBuffer(const DepthBuffer&) = delete; - DepthBuffer& operator=(const DepthBuffer&) = delete; - ~DepthBuffer() - { + + VkResult res; + + res = vkCreateImage(m_device->getHandle(), &imageCreateInfo, nullptr, &m_image); + assert(res == VK_SUCCESS); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(m_device->getHandle(), m_image, &memReqs); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.pNext = nullptr, + allocInfo.allocationSize = memReqs.size, + allocInfo.memoryTypeIndex = getMemoryType(m_device->getPhysicalDevice(), memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + res = vkAllocateMemory(m_device->getHandle(), &allocInfo, nullptr, &m_imageMemory); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(m_device->getHandle(), m_image, m_imageMemory, 0); + assert(res == VK_SUCCESS); + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.image = m_image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &m_imageView); + assert (res == VK_SUCCESS); } + DepthStencil(const DepthStencil&) = delete; + DepthStencil& operator=(const DepthStencil&) = delete; + ~DepthStencil() + { + vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); + vkFreeMemory(m_device->getHandle(), m_imageMemory, nullptr); + vkDestroyImage(m_device->getHandle(), m_image, nullptr); + } + + private: + + VkFormat m_format; + + Device* m_device; + + VkImage m_image = VK_NULL_HANDLE; + VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; + VkImageView m_imageView = VK_NULL_HANDLE; + }; - std::unique_ptr m_depthBuffer; + std::unique_ptr m_depthBuffer; }; - - std::unique_ptr m_debugMessenger; // uses instance std::unique_ptr m_swapchain; From ffed4a9deb69e787f60ce117f6aa9c8c8a43ae39 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 8 Oct 2022 12:28:36 +0100 Subject: [PATCH 23/50] Do something --- src/gfx_device_vulkan.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 68566b3..60fac9d 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1006,6 +1006,11 @@ namespace engine { } + bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + { + return false; + } + } #endif From 2fd4453de60e9759687e9b44bff56fef960a02cc Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 8 Oct 2022 16:20:31 +0100 Subject: [PATCH 24/50] Add vma --- .gitmodules | 3 +++ dependencies/VulkanMemoryAllocator | 1 + src/engine.cpp | 2 +- src/gfx_device_vulkan.cpp | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) create mode 160000 dependencies/VulkanMemoryAllocator diff --git a/.gitmodules b/.gitmodules index 9e12f3b..d064ea5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "dependencies/volk"] path = dependencies/volk url = https://github.com/zeux/volk +[submodule "dependencies/VulkanMemoryAllocator"] + path = dependencies/VulkanMemoryAllocator + url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator diff --git a/dependencies/VulkanMemoryAllocator b/dependencies/VulkanMemoryAllocator new file mode 160000 index 0000000..c351692 --- /dev/null +++ b/dependencies/VulkanMemoryAllocator @@ -0,0 +1 @@ +Subproject commit c351692490513cdb0e5a2c925aaf7ea4a9b672f4 diff --git a/src/engine.cpp b/src/engine.cpp index 44b4112..37f7175 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -7,7 +7,7 @@ namespace engine { Application::Application(const char* appName, const char* appVersion) { - m_win = std::make_unique(appName, true); + m_win = std::make_unique(appName, false); m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 60fac9d..81442ac 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -952,6 +952,7 @@ namespace engine { DepthStencil& operator=(const DepthStencil&) = delete; ~DepthStencil() { + TRACE("Destroying DepthStencil..."); vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); vkFreeMemory(m_device->getHandle(), m_imageMemory, nullptr); vkDestroyImage(m_device->getHandle(), m_image, nullptr); From 6f0bd2c800a92ea6cecba75d4c73215b62edcc67 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 9 Oct 2022 14:57:41 +0100 Subject: [PATCH 25/50] Begin implementing vma --- CMakeLists.txt | 3 ++ src/gfx_device_vulkan.cpp | 71 +++++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index afce8dc..ec0f042 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,9 @@ set(VOLK_HEADERS_ONLY ON) add_subdirectory(dependencies/volk) target_link_libraries(${PROJECT_NAME} PRIVATE volk_headers) +# Vulkan Memory Allocator +target_include_directories(${PROJECT_NAME} PRIVATE dependencies/VulkanMemoryAllocator/include) + # SDL2: find_package(SDL2) if (SDL2_FOUND) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 81442ac..a7be894 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -10,7 +10,11 @@ #include "log.hpp" #define VOLK_IMPLEMENTATION -#include "volk.h" +#include + +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_IMPLEMENTATION +#include #include @@ -80,9 +84,9 @@ namespace engine { m_debugMessenger = std::make_unique(instance); // owns the instance auto surface = std::make_shared(window, instance); // owns the instance - auto device = std::make_shared(surface); // owns the surface + m_device = std::make_unique(surface); // owns the surface - m_swapchain = std::make_unique(device); // owns the device + m_swapchain = std::make_unique(m_device.get()); // owns the device } @@ -596,6 +600,8 @@ namespace engine { res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); assert(res == VK_SUCCESS); + m_allocator = std::make_unique(this); + } Device(const Device&) = delete; Device& operator=(const Device&) = delete; @@ -617,6 +623,11 @@ namespace engine { return m_physicalDevice; } + VkInstance getInstance() const + { + return m_surface->getInstance(); + } + struct SwapchainSupportDetails { VkSurfaceCapabilitiesKHR caps{}; std::vector formats{}; @@ -672,6 +683,49 @@ namespace engine { throw std::runtime_error("Unable to find compute queue"); }*/ + class GPUAllocator { + public: + GPUAllocator(const Device* device) + { + VmaVulkanFunctions functions{ + .vkGetInstanceProcAddr = vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = vkGetDeviceProcAddr, + }; + + VmaAllocatorCreateInfo createInfo{ + .flags = 0, + .physicalDevice = device->getPhysicalDevice(), + .device = device->getHandle(), + .preferredLargeHeapBlockSize = 0, + .pAllocationCallbacks = nullptr, + .pDeviceMemoryCallbacks = nullptr, + .pHeapSizeLimit = nullptr, + .pVulkanFunctions = &functions, + .instance = device->getInstance(), + .vulkanApiVersion = VK_API_VERSION_1_3, + .pTypeExternalMemoryHandleTypes = nullptr + }; + + VkResult res = vmaCreateAllocator(&createInfo, &m_handle); + assert(res == VK_SUCCESS); + + } + GPUAllocator(const GPUAllocator&) = delete; + GPUAllocator& operator=(const GPUAllocator&) = delete; + ~GPUAllocator() + { + + } + + VmaAllocator getHandle() const + { + return m_handle; + } + + private: + VmaAllocator m_handle = nullptr; + }; + private: std::shared_ptr m_surface; @@ -686,12 +740,14 @@ namespace engine { VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; + std::unique_ptr m_allocator; + }; class Swapchain { public: - Swapchain(std::shared_ptr device) : m_device(device) + Swapchain(Device* device) : m_device(device) { VkResult res; @@ -817,7 +873,7 @@ namespace engine { // create depth buffer - m_depthBuffer = std::make_unique(m_currentExtent, m_device.get()); + m_depthBuffer = std::make_unique(m_currentExtent, m_device); } Swapchain(const Swapchain&) = delete; @@ -833,7 +889,7 @@ namespace engine { } private: - std::shared_ptr m_device; + Device* m_device; VkSwapchainKHR m_handle = VK_NULL_HANDLE; @@ -975,6 +1031,9 @@ namespace engine { }; std::unique_ptr m_debugMessenger; // uses instance + + std::unique_ptr m_device; + std::unique_ptr m_swapchain; }; From 0a5022360d954da10d8e936f4a038dc84156a5cf Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 9 Oct 2022 15:15:29 +0100 Subject: [PATCH 26/50] add forgotten destructor --- src/gfx_device_vulkan.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index a7be894..8c598e8 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -13,6 +13,8 @@ #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 +#define VMA_VULKAN_VERSION 1003000 #define VMA_IMPLEMENTATION #include @@ -688,8 +690,32 @@ namespace engine { GPUAllocator(const Device* device) { VmaVulkanFunctions functions{ - .vkGetInstanceProcAddr = vkGetInstanceProcAddr, - .vkGetDeviceProcAddr = vkGetDeviceProcAddr, + .vkGetInstanceProcAddr = nullptr, + .vkGetDeviceProcAddr = nullptr, + .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = vkAllocateMemory, + .vkFreeMemory = vkFreeMemory, + .vkMapMemory = vkMapMemory, + .vkUnmapMemory = vkUnmapMemory, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = vkBindBufferMemory, + .vkBindImageMemory = vkBindImageMemory, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, + .vkCreateBuffer = vkCreateBuffer, + .vkDestroyBuffer = vkDestroyBuffer, + .vkCreateImage = vkCreateImage, + .vkDestroyImage = vkDestroyImage, + .vkCmdCopyBuffer = vkCmdCopyBuffer, + .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, + .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, + .vkBindBufferMemory2KHR = vkBindBufferMemory2, + .vkBindImageMemory2KHR = vkBindImageMemory2, + .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, + .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, + .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, }; VmaAllocatorCreateInfo createInfo{ @@ -714,7 +740,7 @@ namespace engine { GPUAllocator& operator=(const GPUAllocator&) = delete; ~GPUAllocator() { - + vmaDestroyAllocator(m_handle); } VmaAllocator getHandle() const From aabd3151cd09355ff929e102fbb4fa9e73ec07df Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 9 Oct 2022 16:35:50 +0100 Subject: [PATCH 27/50] Work on vulkan --- TODO | 3 + src/gfx_device_vulkan.cpp | 184 ++++++++++++++++++-------------------- 2 files changed, 89 insertions(+), 98 deletions(-) diff --git a/TODO b/TODO index bd9deb9..b9a529a 100644 --- a/TODO +++ b/TODO @@ -15,3 +15,6 @@ Add support for shadows and other complex lighting. Also add post-processing. For font rendering, put all ASCII characters in one large texture and use 'instancing' (and uniform buffer objects?) to reduce draw calls. + +# VULKAN # +The entire vulkan backend needs redesigning without so many classes diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 8c598e8..ecb9463 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -86,9 +86,10 @@ namespace engine { m_debugMessenger = std::make_unique(instance); // owns the instance auto surface = std::make_shared(window, instance); // owns the instance - m_device = std::make_unique(surface); // owns the surface + auto device = std::make_shared(surface); // owns the surface + auto allocator = std::make_shared(device); // owns the device - m_swapchain = std::make_unique(m_device.get()); // owns the device + m_swapchain = std::make_unique(device, allocator); // owns the device, allocator } @@ -602,8 +603,6 @@ namespace engine { res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); assert(res == VK_SUCCESS); - m_allocator = std::make_unique(this); - } Device(const Device&) = delete; Device& operator=(const Device&) = delete; @@ -685,73 +684,6 @@ namespace engine { throw std::runtime_error("Unable to find compute queue"); }*/ - class GPUAllocator { - public: - GPUAllocator(const Device* device) - { - VmaVulkanFunctions functions{ - .vkGetInstanceProcAddr = nullptr, - .vkGetDeviceProcAddr = nullptr, - .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, - .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, - .vkAllocateMemory = vkAllocateMemory, - .vkFreeMemory = vkFreeMemory, - .vkMapMemory = vkMapMemory, - .vkUnmapMemory = vkUnmapMemory, - .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, - .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, - .vkBindBufferMemory = vkBindBufferMemory, - .vkBindImageMemory = vkBindImageMemory, - .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, - .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, - .vkCreateBuffer = vkCreateBuffer, - .vkDestroyBuffer = vkDestroyBuffer, - .vkCreateImage = vkCreateImage, - .vkDestroyImage = vkDestroyImage, - .vkCmdCopyBuffer = vkCmdCopyBuffer, - .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, - .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, - .vkBindBufferMemory2KHR = vkBindBufferMemory2, - .vkBindImageMemory2KHR = vkBindImageMemory2, - .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, - .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, - .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, - }; - - VmaAllocatorCreateInfo createInfo{ - .flags = 0, - .physicalDevice = device->getPhysicalDevice(), - .device = device->getHandle(), - .preferredLargeHeapBlockSize = 0, - .pAllocationCallbacks = nullptr, - .pDeviceMemoryCallbacks = nullptr, - .pHeapSizeLimit = nullptr, - .pVulkanFunctions = &functions, - .instance = device->getInstance(), - .vulkanApiVersion = VK_API_VERSION_1_3, - .pTypeExternalMemoryHandleTypes = nullptr - }; - - VkResult res = vmaCreateAllocator(&createInfo, &m_handle); - assert(res == VK_SUCCESS); - - } - GPUAllocator(const GPUAllocator&) = delete; - GPUAllocator& operator=(const GPUAllocator&) = delete; - ~GPUAllocator() - { - vmaDestroyAllocator(m_handle); - } - - VmaAllocator getHandle() const - { - return m_handle; - } - - private: - VmaAllocator m_handle = nullptr; - }; - private: std::shared_ptr m_surface; @@ -766,14 +698,81 @@ namespace engine { VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; - std::unique_ptr m_allocator; + }; + class GPUAllocator { + public: + GPUAllocator(std::shared_ptr device) : m_device(device) + { + VmaVulkanFunctions functions{ + .vkGetInstanceProcAddr = nullptr, + .vkGetDeviceProcAddr = nullptr, + .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = vkAllocateMemory, + .vkFreeMemory = vkFreeMemory, + .vkMapMemory = vkMapMemory, + .vkUnmapMemory = vkUnmapMemory, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = vkBindBufferMemory, + .vkBindImageMemory = vkBindImageMemory, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, + .vkCreateBuffer = vkCreateBuffer, + .vkDestroyBuffer = vkDestroyBuffer, + .vkCreateImage = vkCreateImage, + .vkDestroyImage = vkDestroyImage, + .vkCmdCopyBuffer = vkCmdCopyBuffer, + .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, + .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, + .vkBindBufferMemory2KHR = vkBindBufferMemory2, + .vkBindImageMemory2KHR = vkBindImageMemory2, + .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, + .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, + .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, + }; + + VmaAllocatorCreateInfo createInfo{ + .flags = 0, + .physicalDevice = m_device->getPhysicalDevice(), + .device = m_device->getHandle(), + .preferredLargeHeapBlockSize = 0, + .pAllocationCallbacks = nullptr, + .pDeviceMemoryCallbacks = nullptr, + .pHeapSizeLimit = nullptr, + .pVulkanFunctions = &functions, + .instance = m_device->getInstance(), + .vulkanApiVersion = VK_API_VERSION_1_3, + .pTypeExternalMemoryHandleTypes = nullptr + }; + + VkResult res = vmaCreateAllocator(&createInfo, &m_handle); + assert(res == VK_SUCCESS); + + } + GPUAllocator(const GPUAllocator&) = delete; + GPUAllocator& operator=(const GPUAllocator&) = delete; + ~GPUAllocator() + { + TRACE("Destroying allocator..."); + vmaDestroyAllocator(m_handle); + } + + VmaAllocator getHandle() const + { + return m_handle; + } + + private: + std::shared_ptr m_device; + VmaAllocator m_handle = nullptr; }; class Swapchain { public: - Swapchain(Device* device) : m_device(device) + Swapchain(std::shared_ptr device, std::shared_ptr allocator) : m_device(device), m_allocator(allocator) { VkResult res; @@ -899,7 +898,7 @@ namespace engine { // create depth buffer - m_depthBuffer = std::make_unique(m_currentExtent, m_device); + m_depthBuffer = std::make_unique(m_currentExtent, m_device.get(), m_allocator.get()); } Swapchain(const Swapchain&) = delete; @@ -915,7 +914,8 @@ namespace engine { } private: - Device* m_device; + std::shared_ptr m_device; + std::shared_ptr m_allocator; VkSwapchainKHR m_handle = VK_NULL_HANDLE; @@ -927,8 +927,9 @@ namespace engine { class DepthStencil { public: - DepthStencil(VkExtent2D bufferExtent, Device* device) : m_device(device) + DepthStencil(VkExtent2D bufferExtent, Device* device, GPUAllocator* allocator) : m_device(device), m_allocator(allocator) { + // find a suitable format const std::vector formatCandidates{ @@ -990,24 +991,12 @@ namespace engine { .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + VkResult res; - res = vkCreateImage(m_device->getHandle(), &imageCreateInfo, nullptr, &m_image); - assert(res == VK_SUCCESS); - - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(m_device->getHandle(), m_image, &memReqs); - - VkMemoryAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.pNext = nullptr, - allocInfo.allocationSize = memReqs.size, - allocInfo.memoryTypeIndex = getMemoryType(m_device->getPhysicalDevice(), memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - res = vkAllocateMemory(m_device->getHandle(), &allocInfo, nullptr, &m_imageMemory); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(m_device->getHandle(), m_image, m_imageMemory, 0); + res = vmaCreateImage(m_allocator->getHandle(), &imageCreateInfo, &allocInfo, &m_image, &m_allocation, nullptr); assert(res == VK_SUCCESS); VkImageViewCreateInfo createInfo{}; @@ -1028,7 +1017,7 @@ namespace engine { res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &m_imageView); assert (res == VK_SUCCESS); - + } DepthStencil(const DepthStencil&) = delete; DepthStencil& operator=(const DepthStencil&) = delete; @@ -1036,8 +1025,8 @@ namespace engine { { TRACE("Destroying DepthStencil..."); vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); - vkFreeMemory(m_device->getHandle(), m_imageMemory, nullptr); - vkDestroyImage(m_device->getHandle(), m_image, nullptr); + // destroy allocation + vmaDestroyImage(m_allocator->getHandle(), m_image, m_allocation); } private: @@ -1045,9 +1034,10 @@ namespace engine { VkFormat m_format; Device* m_device; + GPUAllocator* m_allocator; VkImage m_image = VK_NULL_HANDLE; - VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; + VmaAllocation m_allocation; VkImageView m_imageView = VK_NULL_HANDLE; }; @@ -1058,8 +1048,6 @@ namespace engine { std::unique_ptr m_debugMessenger; // uses instance - std::unique_ptr m_device; - std::unique_ptr m_swapchain; }; From 15028907f66c123c976ec2f8d52225a6e6ecd15f Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 14 Oct 2022 13:56:28 +0100 Subject: [PATCH 28/50] begin rewriting gfx_device_vulkan --- src/gfx_device_vulkan (2).cpp | 1094 ++++++++++++++++++++++++++++++ src/gfx_device_vulkan.cpp | 1207 ++++++--------------------------- 2 files changed, 1304 insertions(+), 997 deletions(-) create mode 100644 src/gfx_device_vulkan (2).cpp diff --git a/src/gfx_device_vulkan (2).cpp b/src/gfx_device_vulkan (2).cpp new file mode 100644 index 0000000..4886cb5 --- /dev/null +++ b/src/gfx_device_vulkan (2).cpp @@ -0,0 +1,1094 @@ +#if 0 + +// The implementation of the graphics layer using Vulkan 1.3 + +#ifdef ENGINE_BUILD_VULKAN + +#include "gfx_device.hpp" + +#include "util.hpp" + +#include "config.h" +#include "log.hpp" + +#define VOLK_IMPLEMENTATION +#include + +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 +#define VMA_VULKAN_VERSION 1003000 +#define VMA_IMPLEMENTATION +#include + +#include + +#include + +#include +#include +#include +#include + +namespace engine { + + static std::vector getRequiredVulkanExtensions(SDL_Window* window) + { + SDL_bool res; + + unsigned int sdlExtensionCount = 0; + res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr); + assert(res == SDL_TRUE); + std::vector requiredExtensions(sdlExtensionCount); + res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, requiredExtensions.data()); + assert(res == SDL_TRUE); + + return requiredExtensions; + } + + static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance) + { + VkSurfaceKHR surface; + + if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) { + throw std::runtime_error("Unable to create window surface"); + } + + return surface; + } + + static uint32_t getMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties memProps; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); + for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { + if (typeFilter & (1 << i)) { + return i; + } + } + + throw std::runtime_error("Failed to find a suitable memory type"); + } + + class GFXDevice::Impl { + + public: + Impl(const char* appName, const char* appVersion, SDL_Window* window) + { +#ifdef NDEBUG + // release mode: don't use validation layer + LayerInfo layerInfo(false); +#else + // debug mode: use validation layer + LayerInfo layerInfo(true); +#endif + auto instance = std::make_shared(appName, appVersion, layerInfo, getRequiredVulkanExtensions(window)); + + volkLoadInstanceOnly(instance->getHandle()); + + m_debugMessenger = std::make_unique(instance); // owns the instance + auto surface = std::make_shared(window, instance); // owns the instance + + auto device = std::make_shared(surface); // owns the surface + auto allocator = std::make_shared(device); // owns the device + + m_swapchain = std::make_unique(device, allocator); // owns the device, allocator + + } + + private: + + struct LayerInfo { + + LayerInfo(bool useValidation) + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data()); + assert(res == VK_SUCCESS); + + if (useValidation == true) { + // find validation layer and print all layers to log + for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) { + if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { + validationLayer = it; + } + } + if (validationLayer.has_value() == false) { + throw std::runtime_error("The validation layer was not found. Quitting."); + } + } + } + + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + + std::vector layersAvailable{}; + std::optional::iterator> validationLayer; + + }; + + class Instance { + + public: + Instance(const char* appName, const char* appVersion, const LayerInfo& layerInfo, const std::vector& windowExtensions) + { + VkResult res; + + int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; + assert(versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; + assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); + + VkApplicationInfo applicationInfo{ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = appName, + .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), + .pEngineName = "engine", + .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), + .apiVersion = VK_API_VERSION_1_3, + }; + + // make a list of all extensions to use + std::vector extensions{}; + extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + + // also use debug utils extension + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + std::vector layers{}; + + if (layerInfo.validationLayer.has_value()) { + layers.push_back(layerInfo.validationLayer.value()->layerName); + } + + VkInstanceCreateInfo instanceInfo{ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = (uint32_t)layers.size(), + .ppEnabledLayerNames = layers.data(), + .enabledExtensionCount = (uint32_t)extensions.size(), + .ppEnabledExtensionNames = extensions.data(), + }; + + VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); + + if (layerInfo.validationLayer.has_value()) { + instanceInfo.pNext = &debugMessengerCreateInfo; + } + +#ifndef NDEBUG + for (const char* ext : extensions) { + TRACE("Using Vulkan instance extension: {}", ext); + } +#endif + + res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + throw std::runtime_error("The graphics driver is incompatible with vulkan"); + } + assert(res == VK_SUCCESS); + + } + + Instance(const Instance&) = delete; + Instance& operator=(const Instance&) = delete; + ~Instance() + { + TRACE("Destroying instance..."); + vkDestroyInstance(m_handle, nullptr); + } + + VkInstance getHandle() const + { + return m_handle; + } + + private: + VkInstance m_handle; + + }; + + class DebugMessenger { + + public: + DebugMessenger(std::shared_ptr instance) : m_instance(instance) + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); + + VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); + assert(res == VK_SUCCESS); + } + + DebugMessenger(const DebugMessenger&) = delete; + DebugMessenger& operator=(const DebugMessenger&) = delete; + ~DebugMessenger() + { + TRACE("Destroying debug messenger..."); + vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); + } + + static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() + { + VkDebugUtilsMessengerCreateInfoEXT createInfo{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .messageSeverity = 0, + .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, + .pfnUserCallback = messengerCallback, + .pUserData = nullptr, + }; + + switch (MESSAGE_LEVEL) { + case Severity::VERBOSE: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + // fall-through + case Severity::INFO: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + // fall-through + case Severity::WARNING: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + // fall-through + case Severity::ERROR: + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + // fall-through + default: + break; + } + + return createInfo; + } + + private: + std::shared_ptr m_instance; + VkDebugUtilsMessengerEXT m_messengerHandle; + + enum class Severity { + VERBOSE, + INFO, + WARNING, + ERROR + }; + static constexpr Severity MESSAGE_LEVEL = Severity::WARNING; + + static VkBool32 messengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + + std::string msgType{}; + + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (GENERAL)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + msgType += " (PERF.)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (VALID.)"; + + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } + + }; + + class Surface { + + public: + Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance), m_window(window) + { + m_handle = createSurface(m_window, instance->getHandle()); + } + Surface(const Surface&) = delete; + Surface& operator=(const Surface&) = delete; + + ~Surface() + { + TRACE("Destroying surface..."); + vkDestroySurfaceKHR(m_instance->getHandle(), m_handle, nullptr); + } + + VkSurfaceKHR getHandle() const + { + return m_handle; + } + + SDL_Window* getWindow() const + { + return m_window; + } + + VkInstance getInstance() const + { + return m_instance->getHandle(); + } + + private: + std::shared_ptr m_instance; + VkSurfaceKHR m_handle; + SDL_Window* m_window; + + }; + + class Device { + + public: + Device(std::shared_ptr surface) : m_surface(surface) + { + // enumerate physical devices + uint32_t physDeviceCount = 0; + VkResult res; + res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, nullptr); + assert(res == VK_SUCCESS); + if (physDeviceCount == 0) { + throw std::runtime_error("No GPU found with vulkan support!"); + } + std::vector physicalDevices(physDeviceCount); + res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); + + // find suitable device: + + const std::vector requiredDeviceExtensions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + for (const auto& dev : physicalDevices) { + + // first, check extension support + uint32_t extensionCount; + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); + assert(res == VK_SUCCESS); + std::vector availableExtensions(extensionCount); + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); + assert(res == VK_SUCCESS); + + for (const auto& extToFind : requiredDeviceExtensions) { + bool extFound = false; + for (const auto& ext : availableExtensions) { + if (strcmp(extToFind, ext.extensionName) == 0) { + extFound = true; + } + } + if (!extFound) { + continue; + } + } + + + + // get swapchain support details: + + // get surface capabilities + res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface->getHandle(), &m_swapchainSupportDetails.caps); + assert (res == VK_SUCCESS); + + // check there is at least one supported surface format + uint32_t surfaceFormatCount = 0; + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, nullptr); + assert(res == VK_SUCCESS); + if (surfaceFormatCount == 0) { + continue; + } + m_swapchainSupportDetails.formats.resize(surfaceFormatCount); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, m_swapchainSupportDetails.formats.data()); + assert(res == VK_SUCCESS); + + // check there is at least one supported present mode + uint32_t surfacePresentModeCount = 0; + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, nullptr); + assert(res == VK_SUCCESS); + if (surfacePresentModeCount == 0) { + continue; + } + m_swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); + assert(res == VK_SUCCESS); + + + + // check physical device properties + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(dev, &devProps); + + // check that the device supports vulkan 1.3 + if (devProps.apiVersion < VK_API_VERSION_1_3) { + continue; + } + + m_physicalDevice = dev; + break; + + } // end for() + + if (m_physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("No suitable Vulkan physical device found"); + } + + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(m_physicalDevice, &devProps); + INFO("Selected physical device: {}", devProps.deviceName); + + TRACE("Supported present modes:"); + for (const auto& presMode : m_swapchainSupportDetails.presentModes) { + switch (presMode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); + break; + case VK_PRESENT_MODE_MAILBOX_KHR: + TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); + break; + case VK_PRESENT_MODE_FIFO_KHR: + TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); + break; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); + break; + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); + break; + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); + break; + default: + TRACE("\tUNKNOWN DISPLAY MODE"); + break; + } + } + + + + // Get the queue families and find ones that support graphics, transfer, and compute + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data()); + + std::optional graphicsFamilyIndex; + std::optional transferFamilyIndex; + std::optional computeFamilyIndex; + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkQueueFamilyProperties family = queueFamilies[i]; + if (family.queueCount > 0) { + if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + TRACE("GRAPHICS:"); + graphicsFamilyIndex = i; + } + if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { + TRACE("TRANSFER:"); + transferFamilyIndex = i; + } + if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { + TRACE("COMPUTE:"); + computeFamilyIndex = i; + } + TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); + } + } + if ( graphicsFamilyIndex.has_value() == false || + transferFamilyIndex.has_value() == false) { + throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); + } + + // there is no guaranteed support for compute queues + + std::vector queueCreateInfos{}; + + // use a set to filter out duplicate indices + std::unordered_set uniqueQueueFamilies{}; + if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value()); + if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value()); + if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value()); + + float queuePriority = 1.0f; + + for (uint32_t family : uniqueQueueFamilies) { + // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + + Queue newQueue{}; + newQueue.familyIndex = family; + newQueue.queueIndex = 0; + if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; + if (transferFamilyIndex == family) newQueue.supportsTransfer = true; + if (computeFamilyIndex == family) newQueue.supportsCompute = true; + + TRACE("Creating queue from family {}", family); + VkDeviceQueueCreateInfo queueCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = family, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + queueCreateInfos.push_back(queueCreateInfo); + m_queues.push_back(newQueue); + } + + // check the physical device is compatible with the surface + VkBool32 graphicsQueueCanPresent; + res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); + assert(res == VK_SUCCESS); + if (graphicsQueueCanPresent != VK_TRUE) { + throw std::runtime_error("The selected queue family does not support this surface"); + } + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), + .pQueueCreateInfos = queueCreateInfos.data(), + // IGNORED: .enabledLayerCount + // IGNORED: .ppEnabledLayerNames + .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), + .ppEnabledExtensionNames = requiredDeviceExtensions.data(), + .pEnabledFeatures = nullptr, + }; + + res = vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_handle); + if (res != VK_SUCCESS) { + throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); + } + + volkLoadDevice(m_handle); + + for (auto& q : m_queues) { + vkGetDeviceQueue(m_handle, q.familyIndex, q.queueIndex, &q.handle); + } + + Queue gfxQueue = getGraphicsQueue(); + + VkCommandPoolCreateInfo gfxCmdPoolInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = gfxQueue.familyIndex, + }; + + res = vkCreateCommandPool(m_handle, &gfxCmdPoolInfo, nullptr, &m_gfxCommandPool); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo gfxCmdBufInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = m_gfxCommandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); + assert(res == VK_SUCCESS); + + } + Device(const Device&) = delete; + Device& operator=(const Device&) = delete; + + ~Device() + { + TRACE("Destroying device..."); + vkDestroyCommandPool(m_handle, m_gfxCommandPool, nullptr); + vkDestroyDevice(m_handle, nullptr); + } + + VkDevice getHandle() const + { + return m_handle; + } + + VkPhysicalDevice getPhysicalDevice() const + { + return m_physicalDevice; + } + + VkInstance getInstance() const + { + return m_surface->getInstance(); + } + + struct SwapchainSupportDetails { + VkSurfaceCapabilitiesKHR caps{}; + std::vector formats{}; + std::vector presentModes{}; + }; + + SwapchainSupportDetails getSupportDetails() + { + return m_swapchainSupportDetails; + } + + struct Queue { + uint32_t familyIndex; + uint32_t queueIndex; + bool supportsGraphics = false; + bool supportsTransfer = false; + bool supportsCompute = false; + + VkQueue handle; + }; + + VkCommandBuffer getGraphicsCommandBuffer() + { + return m_gfxCommandBuffer; + } + + std::shared_ptr getSurface() + { + return m_surface; + } + + Queue getGraphicsQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsGraphics) return queue; + } + throw std::runtime_error("Unable to find graphics queue"); + } + + Queue getTransferQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsTransfer) return queue; + } + throw std::runtime_error("Unable to find transfer queue"); + } + + /*Queue getComputeQueue() + { + for (const auto& queue : m_queues) { + if (queue.supportsCompute) return queue; + } + throw std::runtime_error("Unable to find compute queue"); + }*/ + + private: + std::shared_ptr m_surface; + + SwapchainSupportDetails m_swapchainSupportDetails{}; + + VkDevice m_handle = VK_NULL_HANDLE; + + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + + std::vector m_queues{}; + + VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; + VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; + + }; + + class GPUAllocator { + public: + GPUAllocator(std::shared_ptr device) : m_device(device) + { + VmaVulkanFunctions functions{ + .vkGetInstanceProcAddr = nullptr, + .vkGetDeviceProcAddr = nullptr, + .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = vkAllocateMemory, + .vkFreeMemory = vkFreeMemory, + .vkMapMemory = vkMapMemory, + .vkUnmapMemory = vkUnmapMemory, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = vkBindBufferMemory, + .vkBindImageMemory = vkBindImageMemory, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, + .vkCreateBuffer = vkCreateBuffer, + .vkDestroyBuffer = vkDestroyBuffer, + .vkCreateImage = vkCreateImage, + .vkDestroyImage = vkDestroyImage, + .vkCmdCopyBuffer = vkCmdCopyBuffer, + .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, + .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, + .vkBindBufferMemory2KHR = vkBindBufferMemory2, + .vkBindImageMemory2KHR = vkBindImageMemory2, + .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, + .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, + .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, + }; + + VmaAllocatorCreateInfo createInfo{ + .flags = 0, + .physicalDevice = m_device->getPhysicalDevice(), + .device = m_device->getHandle(), + .preferredLargeHeapBlockSize = 0, + .pAllocationCallbacks = nullptr, + .pDeviceMemoryCallbacks = nullptr, + .pHeapSizeLimit = nullptr, + .pVulkanFunctions = &functions, + .instance = m_device->getInstance(), + .vulkanApiVersion = VK_API_VERSION_1_3, + .pTypeExternalMemoryHandleTypes = nullptr + }; + + VkResult res = vmaCreateAllocator(&createInfo, &m_handle); + assert(res == VK_SUCCESS); + + } + GPUAllocator(const GPUAllocator&) = delete; + GPUAllocator& operator=(const GPUAllocator&) = delete; + ~GPUAllocator() + { + TRACE("Destroying allocator..."); + vmaDestroyAllocator(m_handle); + } + + VmaAllocator getHandle() const + { + return m_handle; + } + + private: + std::shared_ptr m_device; + VmaAllocator m_handle = nullptr; + }; + + class Swapchain { + + public: + Swapchain(std::shared_ptr device, std::shared_ptr allocator) : m_device(device), m_allocator(allocator) + { + VkResult res; + + auto supportDetails = m_device->getSupportDetails(); + + VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; + + for (const auto& format : supportDetails.formats) { + if ( format.format == VK_FORMAT_B8G8R8A8_SRGB && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) { + chosenSurfaceFormat = format; // prefer using srgb non linear colors + } + } + + VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + for (const auto& presMode : supportDetails.presentModes) { + if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { + chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + } + } + + VkExtent2D chosenSwapExtent{}; + + if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { + chosenSwapExtent = supportDetails.caps.currentExtent; + } else { + // if fb size isn't already found, get it from SDL + int width, height; + SDL_Vulkan_GetDrawableSize(m_device->getSurface()->getWindow(), &width, &height); + + chosenSwapExtent.width = static_cast(width); + chosenSwapExtent.height = static_cast(height); + + chosenSwapExtent.width = std::clamp( + chosenSwapExtent.width, + supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); + chosenSwapExtent.height = std::clamp( + chosenSwapExtent.height, + supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); + } + + uint32_t imageCount = supportDetails.caps.minImageCount + 1; + if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { + imageCount = supportDetails.caps.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = m_device->getSurface()->getHandle(), + .minImageCount = imageCount, + .imageFormat = chosenSurfaceFormat.format, + .imageColorSpace = chosenSurfaceFormat.colorSpace, + .imageExtent = chosenSwapExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = supportDetails.caps.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = chosenPresentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + + }; + + std::array queueFamilyIndices{ + m_device->getGraphicsQueue().familyIndex, + m_device->getTransferQueue().familyIndex + }; + + // if graphics and transfer queues aren't in the same family + if (queueFamilyIndices[0] != queueFamilyIndices[1]) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = queueFamilyIndices.size(); + createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); + } + + res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); + assert(res == VK_SUCCESS); + + // get all the image handles + uint32_t swapchainImageCount = 0; + res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, nullptr); + assert(res == VK_SUCCESS); + m_images.resize(swapchainImageCount); + res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, m_images.data()); + assert(res == VK_SUCCESS); + + m_currentFormat = chosenSurfaceFormat.format; + m_currentExtent = chosenSwapExtent; + + // create image views + m_imageViews.clear(); + for (VkImage image : m_images) { + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.image = image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_currentFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + VkImageView imageView; + res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &imageView); + assert (res == VK_SUCCESS); + + m_imageViews.push_back(imageView); + + } + + // create depth buffer + + m_depthBuffer = std::make_unique(m_currentExtent, m_device.get(), m_allocator.get()); + + } + Swapchain(const Swapchain&) = delete; + Swapchain& operator=(const Swapchain&) = delete; + + ~Swapchain() + { + TRACE("Destroying swapchain..."); + for (VkImageView view : m_imageViews) { + vkDestroyImageView(m_device->getHandle(), view, nullptr); + } + vkDestroySwapchainKHR(m_device->getHandle(), m_handle, nullptr); + } + + private: + std::shared_ptr m_device; + std::shared_ptr m_allocator; + + VkSwapchainKHR m_handle = VK_NULL_HANDLE; + + std::vector m_images; + std::vector m_imageViews; + + VkFormat m_currentFormat{}; + VkExtent2D m_currentExtent{}; + + class DepthStencil { + public: + DepthStencil(VkExtent2D bufferExtent, Device* device, GPUAllocator* allocator) : m_device(device), m_allocator(allocator) + { + + // find a suitable format + + const std::vector formatCandidates{ + VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_D32_SFLOAT_S8_UINT, + }; + + const VkFormatFeatureFlags formatFeatures = + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + + std::optional chosenFormat; + + for (VkFormat format : formatCandidates) { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(m_device->getPhysicalDevice(), format, &props); + + if ((props.optimalTilingFeatures & formatFeatures) == formatFeatures) { + chosenFormat = format; + break; + } + } + + if (chosenFormat.has_value() == false) { + throw std::runtime_error("Unable to find a suitable depth-stencil format"); + } + + m_format = chosenFormat.value(); + + switch (m_format) { + case VK_FORMAT_D24_UNORM_S8_UINT: + INFO("VK_FORMAT_D24_UNORM_S8_UINT"); + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + INFO("VK_FORMAT_D32_SFLOAT_S8_UINT"); + break; + default: + break; + } + + VkImageCreateInfo imageCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = m_format, + .extent = { + .width = bufferExtent.width, + .height = bufferExtent.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + + VkResult res; + + res = vmaCreateImage(m_allocator->getHandle(), &imageCreateInfo, &allocInfo, &m_image, &m_allocation, nullptr); + assert(res == VK_SUCCESS); + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.image = m_image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &m_imageView); + assert (res == VK_SUCCESS); + + } + DepthStencil(const DepthStencil&) = delete; + DepthStencil& operator=(const DepthStencil&) = delete; + ~DepthStencil() + { + TRACE("Destroying DepthStencil..."); + vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); + // destroy allocation + vmaDestroyImage(m_allocator->getHandle(), m_image, m_allocation); + } + + private: + + VkFormat m_format; + + Device* m_device; + GPUAllocator* m_allocator; + + VkImage m_image = VK_NULL_HANDLE; + VmaAllocation m_allocation; + VkImageView m_imageView = VK_NULL_HANDLE; + + }; + + std::unique_ptr m_depthBuffer; + + }; + + std::unique_ptr m_debugMessenger; // uses instance + + std::unique_ptr m_swapchain; + + }; + + GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) + { + VkResult res; + res = volkInitialize(); + if (res == VK_ERROR_INITIALIZATION_FAILED) { + throw std::runtime_error("Unable to load vulkan, is it installed?"); + } + assert(res == VK_SUCCESS); + + uint32_t vulkanVersion = volkGetInstanceVersion(); + if (vulkanVersion < VK_MAKE_VERSION(1, 3, 0)) { + throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); + } + + m_pimpl = std::make_unique(appName, appVersion, window); + + } + + GFXDevice::~GFXDevice() + { + TRACE("Destroying GFXDevice..."); + } + + void GFXDevice::draw() + { + + } + + bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + { + return false; + } + +} + +#endif + +#endif \ No newline at end of file diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index ecb9463..48ae3c7 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -22,13 +22,17 @@ #include -#include -#include -#include -#include - namespace engine { + // structures + + struct LayerInfo { + std::vector layersAvailable{}; + std::optional::iterator> validationLayer; + }; + + // functions + static std::vector getRequiredVulkanExtensions(SDL_Window* window) { SDL_bool res; @@ -41,1024 +45,135 @@ namespace engine { assert(res == SDL_TRUE); return requiredExtensions; - } - - static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance) - { - VkSurfaceKHR surface; - - if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) { - throw std::runtime_error("Unable to create window surface"); - } - - return surface; } - static uint32_t getMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) + static LayerInfo getAvailableLayers(bool useValidation) { - VkPhysicalDeviceMemoryProperties memProps; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); - for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { - if (typeFilter & (1 << i)) { - return i; + constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + + LayerInfo info; + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + info.layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, info.layersAvailable.data()); + assert(res == VK_SUCCESS); + + if (useValidation == true) { + // find validation layer and print all layers to log + for (auto it = info.layersAvailable.begin(); it != info.layersAvailable.end(); it++) { + if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { + info.validationLayer = it; + } + } + if (info.validationLayer.has_value() == false) { + throw std::runtime_error("The validation layer was not found. Quitting."); } } - throw std::runtime_error("Failed to find a suitable memory type"); + return info; } - class GFXDevice::Impl { + static VkBool32 messengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { - public: - Impl(const char* appName, const char* appVersion, SDL_Window* window) - { -#ifdef NDEBUG - // release mode: don't use validation layer - LayerInfo layerInfo(false); -#else - // debug mode: use validation layer - LayerInfo layerInfo(true); -#endif - auto instance = std::make_shared(appName, appVersion, layerInfo, getRequiredVulkanExtensions(window)); + std::string msgType{}; - volkLoadInstanceOnly(instance->getHandle()); + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (GENERAL)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + msgType += " (PERF.)"; + if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + msgType += " (VALID.)"; - m_debugMessenger = std::make_unique(instance); // owns the instance - auto surface = std::make_shared(window, instance); // owns the instance + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } - auto device = std::make_shared(surface); // owns the surface - auto allocator = std::make_shared(device); // owns the device + static VkDebugUtilsMessengerCreateInfoEXT getDebugMessengerCreateInfo() + { + VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .messageSeverity = 0, + .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, + .pfnUserCallback = messengerCallback, + .pUserData = nullptr, + }; - m_swapchain = std::make_unique(device, allocator); // owns the device, allocator + enum class MessageSeverity { + VERBOSE, + INFO, + WARNING, + ERROR + }; + constexpr MessageSeverity MESSAGE_LEVEL = MessageSeverity::WARNING; + switch (MESSAGE_LEVEL) { + case MessageSeverity::VERBOSE: + debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + // fall-through + case MessageSeverity::INFO: + debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + // fall-through + case MessageSeverity::WARNING: + debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + // fall-through + case MessageSeverity::ERROR: + debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + // fall-through + default: + break; } - private: + return debugMessengerInfo; + } - struct LayerInfo { - - LayerInfo(bool useValidation) - { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data()); - assert(res == VK_SUCCESS); - - if (useValidation == true) { - // find validation layer and print all layers to log - for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) { - if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { - validationLayer = it; - } - } - if (validationLayer.has_value() == false) { - throw std::runtime_error("The validation layer was not found. Quitting."); - } - } - } - - static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; - - std::vector layersAvailable{}; - std::optional::iterator> validationLayer; - - }; - - class Instance { - - public: - Instance(const char* appName, const char* appVersion, const LayerInfo& layerInfo, const std::vector& windowExtensions) - { - VkResult res; - - int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; - assert(versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; - assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); - - VkApplicationInfo applicationInfo{ - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = appName, - .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), - .pEngineName = "engine", - .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_API_VERSION_1_3, - }; - - // make a list of all extensions to use - std::vector extensions{}; - extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); - - // also use debug utils extension - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - - std::vector layers{}; - - if (layerInfo.validationLayer.has_value()) { - layers.push_back(layerInfo.validationLayer.value()->layerName); - } - - VkInstanceCreateInfo instanceInfo{ - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &applicationInfo, - .enabledLayerCount = (uint32_t)layers.size(), - .ppEnabledLayerNames = layers.data(), - .enabledExtensionCount = (uint32_t)extensions.size(), - .ppEnabledExtensionNames = extensions.data(), - }; - - VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); - - if (layerInfo.validationLayer.has_value()) { - instanceInfo.pNext = &debugMessengerCreateInfo; - } - -#ifndef NDEBUG - for (const char* ext : extensions) { - TRACE("Using Vulkan instance extension: {}", ext); - } -#endif - - res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); - if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - throw std::runtime_error("The graphics driver is incompatible with vulkan"); - } - assert(res == VK_SUCCESS); - - } - - Instance(const Instance&) = delete; - Instance& operator=(const Instance&) = delete; - ~Instance() - { - TRACE("Destroying instance..."); - vkDestroyInstance(m_handle, nullptr); - } - - VkInstance getHandle() const - { - return m_handle; - } - - private: - VkInstance m_handle; - - }; - - class DebugMessenger { - - public: - DebugMessenger(std::shared_ptr instance) : m_instance(instance) - { - VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); - - VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); - assert(res == VK_SUCCESS); - } - - DebugMessenger(const DebugMessenger&) = delete; - DebugMessenger& operator=(const DebugMessenger&) = delete; - ~DebugMessenger() - { - TRACE("Destroying debug messenger..."); - vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); - } - - static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() - { - VkDebugUtilsMessengerCreateInfoEXT createInfo{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .messageSeverity = 0, - .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, - .pfnUserCallback = messengerCallback, - .pUserData = nullptr, - }; - - switch (MESSAGE_LEVEL) { - case Severity::VERBOSE: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - // fall-through - case Severity::INFO: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; - // fall-through - case Severity::WARNING: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - // fall-through - case Severity::ERROR: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - // fall-through - default: - break; - } - - return createInfo; - } - - private: - std::shared_ptr m_instance; - VkDebugUtilsMessengerEXT m_messengerHandle; - - enum class Severity { - VERBOSE, - INFO, - WARNING, - ERROR - }; - static constexpr Severity MESSAGE_LEVEL = Severity::WARNING; - - static VkBool32 messengerCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - - std::string msgType{}; - - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (GENERAL)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) - msgType += " (PERF.)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (VALID.)"; - - switch (messageSeverity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - default: - break; - } - return VK_FALSE; - } - - }; - - class Surface { - - public: - Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance), m_window(window) - { - m_handle = createSurface(m_window, instance->getHandle()); - } - Surface(const Surface&) = delete; - Surface& operator=(const Surface&) = delete; - - ~Surface() - { - TRACE("Destroying surface..."); - vkDestroySurfaceKHR(m_instance->getHandle(), m_handle, nullptr); - } - - VkSurfaceKHR getHandle() const - { - return m_handle; - } - - SDL_Window* getWindow() const - { - return m_window; - } - - VkInstance getInstance() const - { - return m_instance->getHandle(); - } - - private: - std::shared_ptr m_instance; - VkSurfaceKHR m_handle; - SDL_Window* m_window; - - }; - - class Device { - - public: - Device(std::shared_ptr surface) : m_surface(surface) - { - // enumerate physical devices - uint32_t physDeviceCount = 0; - VkResult res; - res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, nullptr); - assert(res == VK_SUCCESS); - if (physDeviceCount == 0) { - throw std::runtime_error("No GPU found with vulkan support!"); - } - std::vector physicalDevices(physDeviceCount); - res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, physicalDevices.data()); - assert(res == VK_SUCCESS); - - // find suitable device: - - const std::vector requiredDeviceExtensions{ - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - for (const auto& dev : physicalDevices) { - - // first, check extension support - uint32_t extensionCount; - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); - assert(res == VK_SUCCESS); - std::vector availableExtensions(extensionCount); - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); - assert(res == VK_SUCCESS); - - for (const auto& extToFind : requiredDeviceExtensions) { - bool extFound = false; - for (const auto& ext : availableExtensions) { - if (strcmp(extToFind, ext.extensionName) == 0) { - extFound = true; - } - } - if (!extFound) { - continue; - } - } - - - - // get swapchain support details: - - // get surface capabilities - res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface->getHandle(), &m_swapchainSupportDetails.caps); - assert (res == VK_SUCCESS); - - // check there is at least one supported surface format - uint32_t surfaceFormatCount = 0; - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, nullptr); - assert(res == VK_SUCCESS); - if (surfaceFormatCount == 0) { - continue; - } - m_swapchainSupportDetails.formats.resize(surfaceFormatCount); - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, m_swapchainSupportDetails.formats.data()); - assert(res == VK_SUCCESS); - - // check there is at least one supported present mode - uint32_t surfacePresentModeCount = 0; - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, nullptr); - assert(res == VK_SUCCESS); - if (surfacePresentModeCount == 0) { - continue; - } - m_swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); - assert(res == VK_SUCCESS); - - - - // check physical device properties - VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(dev, &devProps); - - // check that the device supports vulkan 1.3 - if (devProps.apiVersion < VK_API_VERSION_1_3) { - continue; - } - - m_physicalDevice = dev; - break; - - } // end for() - - if (m_physicalDevice == VK_NULL_HANDLE) { - throw std::runtime_error("No suitable Vulkan physical device found"); - } - - VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(m_physicalDevice, &devProps); - INFO("Selected physical device: {}", devProps.deviceName); - - TRACE("Supported present modes:"); - for (const auto& presMode : m_swapchainSupportDetails.presentModes) { - switch (presMode) { - case VK_PRESENT_MODE_IMMEDIATE_KHR: - TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); - break; - case VK_PRESENT_MODE_MAILBOX_KHR: - TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); - break; - case VK_PRESENT_MODE_FIFO_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); - break; - case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); - break; - case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); - break; - case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); - break; - default: - TRACE("\tUNKNOWN DISPLAY MODE"); - break; - } - } - - - - // Get the queue families and find ones that support graphics, transfer, and compute - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data()); - - std::optional graphicsFamilyIndex; - std::optional transferFamilyIndex; - std::optional computeFamilyIndex; - - for (uint32_t i = 0; i < queueFamilyCount; i++) { - VkQueueFamilyProperties family = queueFamilies[i]; - if (family.queueCount > 0) { - if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - TRACE("GRAPHICS:"); - graphicsFamilyIndex = i; - } - if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { - TRACE("TRANSFER:"); - transferFamilyIndex = i; - } - if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { - TRACE("COMPUTE:"); - computeFamilyIndex = i; - } - TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); - } - } - if ( graphicsFamilyIndex.has_value() == false || - transferFamilyIndex.has_value() == false) { - throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); - } - - // there is no guaranteed support for compute queues - - std::vector queueCreateInfos{}; - - // use a set to filter out duplicate indices - std::unordered_set uniqueQueueFamilies{}; - if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value()); - if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value()); - if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value()); - - float queuePriority = 1.0f; - - for (uint32_t family : uniqueQueueFamilies) { - // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute - - Queue newQueue{}; - newQueue.familyIndex = family; - newQueue.queueIndex = 0; - if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; - if (transferFamilyIndex == family) newQueue.supportsTransfer = true; - if (computeFamilyIndex == family) newQueue.supportsCompute = true; - - TRACE("Creating queue from family {}", family); - VkDeviceQueueCreateInfo queueCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = family, - .queueCount = 1, - .pQueuePriorities = &queuePriority, - }; - queueCreateInfos.push_back(queueCreateInfo); - m_queues.push_back(newQueue); - } - - // check the physical device is compatible with the surface - VkBool32 graphicsQueueCanPresent; - res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); - assert(res == VK_SUCCESS); - if (graphicsQueueCanPresent != VK_TRUE) { - throw std::runtime_error("The selected queue family does not support this surface"); - } - - VkDeviceCreateInfo deviceCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), - .pQueueCreateInfos = queueCreateInfos.data(), - // IGNORED: .enabledLayerCount - // IGNORED: .ppEnabledLayerNames - .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), - .ppEnabledExtensionNames = requiredDeviceExtensions.data(), - .pEnabledFeatures = nullptr, - }; - - res = vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_handle); - if (res != VK_SUCCESS) { - throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); - } - - volkLoadDevice(m_handle); - - for (auto& q : m_queues) { - vkGetDeviceQueue(m_handle, q.familyIndex, q.queueIndex, &q.handle); - } - - Queue gfxQueue = getGraphicsQueue(); - - VkCommandPoolCreateInfo gfxCmdPoolInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = gfxQueue.familyIndex, - }; - - res = vkCreateCommandPool(m_handle, &gfxCmdPoolInfo, nullptr, &m_gfxCommandPool); - assert(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo gfxCmdBufInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = m_gfxCommandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1 - }; - - res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); - assert(res == VK_SUCCESS); - - } - Device(const Device&) = delete; - Device& operator=(const Device&) = delete; - - ~Device() - { - TRACE("Destroying device..."); - vkDestroyCommandPool(m_handle, m_gfxCommandPool, nullptr); - vkDestroyDevice(m_handle, nullptr); - } - - VkDevice getHandle() const - { - return m_handle; - } - - VkPhysicalDevice getPhysicalDevice() const - { - return m_physicalDevice; - } - - VkInstance getInstance() const - { - return m_surface->getInstance(); - } - - struct SwapchainSupportDetails { - VkSurfaceCapabilitiesKHR caps{}; - std::vector formats{}; - std::vector presentModes{}; - }; - - SwapchainSupportDetails getSupportDetails() - { - return m_swapchainSupportDetails; - } - - struct Queue { - uint32_t familyIndex; - uint32_t queueIndex; - bool supportsGraphics = false; - bool supportsTransfer = false; - bool supportsCompute = false; - - VkQueue handle; - }; - - VkCommandBuffer getGraphicsCommandBuffer() - { - return m_gfxCommandBuffer; - } - - std::shared_ptr getSurface() - { - return m_surface; - } - - Queue getGraphicsQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsGraphics) return queue; - } - throw std::runtime_error("Unable to find graphics queue"); - } - - Queue getTransferQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsTransfer) return queue; - } - throw std::runtime_error("Unable to find transfer queue"); - } - - /*Queue getComputeQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsCompute) return queue; - } - throw std::runtime_error("Unable to find compute queue"); - }*/ - - private: - std::shared_ptr m_surface; - - SwapchainSupportDetails m_swapchainSupportDetails{}; - - VkDevice m_handle = VK_NULL_HANDLE; - - VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; - - std::vector m_queues{}; - - VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; - VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; - - }; - - class GPUAllocator { - public: - GPUAllocator(std::shared_ptr device) : m_device(device) - { - VmaVulkanFunctions functions{ - .vkGetInstanceProcAddr = nullptr, - .vkGetDeviceProcAddr = nullptr, - .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, - .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, - .vkAllocateMemory = vkAllocateMemory, - .vkFreeMemory = vkFreeMemory, - .vkMapMemory = vkMapMemory, - .vkUnmapMemory = vkUnmapMemory, - .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, - .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, - .vkBindBufferMemory = vkBindBufferMemory, - .vkBindImageMemory = vkBindImageMemory, - .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, - .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, - .vkCreateBuffer = vkCreateBuffer, - .vkDestroyBuffer = vkDestroyBuffer, - .vkCreateImage = vkCreateImage, - .vkDestroyImage = vkDestroyImage, - .vkCmdCopyBuffer = vkCmdCopyBuffer, - .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, - .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, - .vkBindBufferMemory2KHR = vkBindBufferMemory2, - .vkBindImageMemory2KHR = vkBindImageMemory2, - .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, - .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, - .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, - }; - - VmaAllocatorCreateInfo createInfo{ - .flags = 0, - .physicalDevice = m_device->getPhysicalDevice(), - .device = m_device->getHandle(), - .preferredLargeHeapBlockSize = 0, - .pAllocationCallbacks = nullptr, - .pDeviceMemoryCallbacks = nullptr, - .pHeapSizeLimit = nullptr, - .pVulkanFunctions = &functions, - .instance = m_device->getInstance(), - .vulkanApiVersion = VK_API_VERSION_1_3, - .pTypeExternalMemoryHandleTypes = nullptr - }; - - VkResult res = vmaCreateAllocator(&createInfo, &m_handle); - assert(res == VK_SUCCESS); - - } - GPUAllocator(const GPUAllocator&) = delete; - GPUAllocator& operator=(const GPUAllocator&) = delete; - ~GPUAllocator() - { - TRACE("Destroying allocator..."); - vmaDestroyAllocator(m_handle); - } - - VmaAllocator getHandle() const - { - return m_handle; - } - - private: - std::shared_ptr m_device; - VmaAllocator m_handle = nullptr; - }; - - class Swapchain { - - public: - Swapchain(std::shared_ptr device, std::shared_ptr allocator) : m_device(device), m_allocator(allocator) - { - VkResult res; - - auto supportDetails = m_device->getSupportDetails(); - - VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; - - for (const auto& format : supportDetails.formats) { - if ( format.format == VK_FORMAT_B8G8R8A8_SRGB && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) { - chosenSurfaceFormat = format; // prefer using srgb non linear colors - } - } - - VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; - - for (const auto& presMode : supportDetails.presentModes) { - if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { - chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing - } - } - - VkExtent2D chosenSwapExtent{}; - - if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { - chosenSwapExtent = supportDetails.caps.currentExtent; - } else { - // if fb size isn't already found, get it from SDL - int width, height; - SDL_Vulkan_GetDrawableSize(m_device->getSurface()->getWindow(), &width, &height); - - chosenSwapExtent.width = static_cast(width); - chosenSwapExtent.height = static_cast(height); - - chosenSwapExtent.width = std::clamp( - chosenSwapExtent.width, - supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); - chosenSwapExtent.height = std::clamp( - chosenSwapExtent.height, - supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); - } - - uint32_t imageCount = supportDetails.caps.minImageCount + 1; - if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { - imageCount = supportDetails.caps.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo{ - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = nullptr, - .flags = 0, - .surface = m_device->getSurface()->getHandle(), - .minImageCount = imageCount, - .imageFormat = chosenSurfaceFormat.format, - .imageColorSpace = chosenSurfaceFormat.colorSpace, - .imageExtent = chosenSwapExtent, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .preTransform = supportDetails.caps.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = chosenPresentMode, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - - }; - - std::array queueFamilyIndices{ - m_device->getGraphicsQueue().familyIndex, - m_device->getTransferQueue().familyIndex - }; - - // if graphics and transfer queues aren't in the same family - if (queueFamilyIndices[0] != queueFamilyIndices[1]) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = queueFamilyIndices.size(); - createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); - } - - res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); - assert(res == VK_SUCCESS); - - // get all the image handles - uint32_t swapchainImageCount = 0; - res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, nullptr); - assert(res == VK_SUCCESS); - m_images.resize(swapchainImageCount); - res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, m_images.data()); - assert(res == VK_SUCCESS); - - m_currentFormat = chosenSurfaceFormat.format; - m_currentExtent = chosenSwapExtent; - - // create image views - m_imageViews.clear(); - for (VkImage image : m_images) { - - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.image = image; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_currentFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - VkImageView imageView; - res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &imageView); - assert (res == VK_SUCCESS); - - m_imageViews.push_back(imageView); - - } - - // create depth buffer - - m_depthBuffer = std::make_unique(m_currentExtent, m_device.get(), m_allocator.get()); - - } - Swapchain(const Swapchain&) = delete; - Swapchain& operator=(const Swapchain&) = delete; - - ~Swapchain() - { - TRACE("Destroying swapchain..."); - for (VkImageView view : m_imageViews) { - vkDestroyImageView(m_device->getHandle(), view, nullptr); - } - vkDestroySwapchainKHR(m_device->getHandle(), m_handle, nullptr); - } - - private: - std::shared_ptr m_device; - std::shared_ptr m_allocator; - - VkSwapchainKHR m_handle = VK_NULL_HANDLE; - - std::vector m_images; - std::vector m_imageViews; - - VkFormat m_currentFormat{}; - VkExtent2D m_currentExtent{}; - - class DepthStencil { - public: - DepthStencil(VkExtent2D bufferExtent, Device* device, GPUAllocator* allocator) : m_device(device), m_allocator(allocator) - { - - // find a suitable format - - const std::vector formatCandidates{ - VK_FORMAT_D24_UNORM_S8_UINT, - VK_FORMAT_D32_SFLOAT_S8_UINT, - }; - - const VkFormatFeatureFlags formatFeatures = - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - - std::optional chosenFormat; - - for (VkFormat format : formatCandidates) { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(m_device->getPhysicalDevice(), format, &props); - - if ((props.optimalTilingFeatures & formatFeatures) == formatFeatures) { - chosenFormat = format; - break; - } - } - - if (chosenFormat.has_value() == false) { - throw std::runtime_error("Unable to find a suitable depth-stencil format"); - } - - m_format = chosenFormat.value(); - - switch (m_format) { - case VK_FORMAT_D24_UNORM_S8_UINT: - INFO("VK_FORMAT_D24_UNORM_S8_UINT"); - break; - case VK_FORMAT_D32_SFLOAT_S8_UINT: - INFO("VK_FORMAT_D32_SFLOAT_S8_UINT"); - break; - default: - break; - } - - VkImageCreateInfo imageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = m_format, - .extent = { - .width = bufferExtent.width, - .height = bufferExtent.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 1, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - - VmaAllocationCreateInfo allocInfo{}; - allocInfo.usage = VMA_MEMORY_USAGE_AUTO; - - VkResult res; - - res = vmaCreateImage(m_allocator->getHandle(), &imageCreateInfo, &allocInfo, &m_image, &m_allocation, nullptr); - assert(res == VK_SUCCESS); - - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.image = m_image; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_format; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &m_imageView); - assert (res == VK_SUCCESS); - - } - DepthStencil(const DepthStencil&) = delete; - DepthStencil& operator=(const DepthStencil&) = delete; - ~DepthStencil() - { - TRACE("Destroying DepthStencil..."); - vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); - // destroy allocation - vmaDestroyImage(m_allocator->getHandle(), m_image, m_allocation); - } - - private: - - VkFormat m_format; - - Device* m_device; - GPUAllocator* m_allocator; - - VkImage m_image = VK_NULL_HANDLE; - VmaAllocation m_allocation; - VkImageView m_imageView = VK_NULL_HANDLE; - - }; - - std::unique_ptr m_depthBuffer; - - }; - - std::unique_ptr m_debugMessenger; // uses instance - - std::unique_ptr m_swapchain; + struct GFXDevice::Impl { + + VkInstance m_instance = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE; }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) { + m_pimpl = std::make_unique(); + VkResult res; + + // initialise vulkan + res = volkInitialize(); if (res == VK_ERROR_INITIALIZATION_FAILED) { throw std::runtime_error("Unable to load vulkan, is it installed?"); } + assert(res == VK_SUCCESS); uint32_t vulkanVersion = volkGetInstanceVersion(); @@ -1066,13 +181,111 @@ namespace engine { throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); } - m_pimpl = std::make_unique(appName, appVersion, window); + bool useValidation; +#ifdef NDEBUG + useValidation = false; // release mode +#else + useValidation = true; // debug mode +#endif + + + + // get the both the engine and application versions + int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; + assert(versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; + assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); + + VkApplicationInfo applicationInfo{ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = appName, + .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), + .pEngineName = "engine", + .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), + .apiVersion = VK_API_VERSION_1_3, + }; + + + + // make a list of all extensions to use + std::vector extensions{}; + + const std::vector windowExtensions = getRequiredVulkanExtensions(window); + extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + + // also use debug utils extension + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + + + // make a list of layers to use + std::vector layers{}; + + const LayerInfo layerInfo = getAvailableLayers(useValidation); + + if (layerInfo.validationLayer.has_value()) { + layers.push_back(layerInfo.validationLayer.value()->layerName); + } + + VkInstanceCreateInfo instanceInfo{ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = (uint32_t)layers.size(), + .ppEnabledLayerNames = layers.data(), + .enabledExtensionCount = (uint32_t)extensions.size(), + .ppEnabledExtensionNames = extensions.data(), + }; + + // add the debug messenger + VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo; + if (layerInfo.validationLayer.has_value()) { + debugMessengerInfo = getDebugMessengerCreateInfo(); + instanceInfo.pNext = &debugMessengerInfo; + } + else { + instanceInfo.pNext = nullptr; + } + + + +#ifndef NDEBUG + for (const char* ext : extensions) { + TRACE("Using Vulkan instance extension: {}", ext); + } +#endif + + res = vkCreateInstance(&instanceInfo, nullptr, &m_pimpl->m_instance); + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + throw std::runtime_error("The graphics driver is incompatible with vulkan"); + } + assert(res == VK_SUCCESS); + + + + // load the instance functions + volkLoadInstanceOnly(m_pimpl->m_instance); + + + + // create the debug messenger + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo(); + + VkResult res = vkCreateDebugUtilsMessengerEXT(m_pimpl->m_instance, &createInfo, nullptr, &m_pimpl->m_debugMessenger); + assert(res == VK_SUCCESS); + } } GFXDevice::~GFXDevice() { TRACE("Destroying GFXDevice..."); + + vkDestroyDebugUtilsMessengerEXT(m_pimpl->m_instance, m_pimpl->m_debugMessenger, nullptr); + vkDestroyInstance(m_pimpl->m_instance, nullptr); } void GFXDevice::draw() From 729fa0930205782368305ef92fb9e6655912d7fb Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 14 Oct 2022 13:56:58 +0100 Subject: [PATCH 29/50] Move file out of src/ --- src/gfx_device_vulkan (2).cpp => gfx_device_vulkan (2).cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/gfx_device_vulkan (2).cpp => gfx_device_vulkan (2).cpp (100%) diff --git a/src/gfx_device_vulkan (2).cpp b/gfx_device_vulkan (2).cpp similarity index 100% rename from src/gfx_device_vulkan (2).cpp rename to gfx_device_vulkan (2).cpp From fecf1cccc5bd86ae1668d10de7ee4688f3febb63 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 17 Oct 2022 17:36:25 +0100 Subject: [PATCH 30/50] Re-implement most of the vulkan init code --- include/gfx_device.hpp | 2 +- src/gfx_device_vulkan.cpp | 562 +++++++++++++++++++++++++++++++++++++- 2 files changed, 554 insertions(+), 10 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a05be31..574a883 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -49,7 +49,7 @@ namespace engine { private: class Impl; - std::unique_ptr m_pimpl; + std::unique_ptr pimpl; }; diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 48ae3c7..2777cbc 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1,4 +1,5 @@ -// The implementation of the graphics layer using Vulkan 1.3 +// The implementation of the graphics layer using Vulkan 1.3. +// This uses SDL specific code #ifdef ENGINE_BUILD_VULKAN @@ -21,9 +22,14 @@ #include #include +#include +#include namespace engine { + // singleton variable + static GFXDevice* s_gfx_device_instance = nullptr; + // structures struct LayerInfo { @@ -31,6 +37,40 @@ namespace engine { std::optional::iterator> validationLayer; }; + struct SwapchainSupportDetails { + VkSurfaceCapabilitiesKHR caps{}; + std::vector formats{}; + std::vector presentModes{}; + }; + + struct Queue { + uint32_t familyIndex; + uint32_t queueIndex; + bool supportsGraphics = false; + bool supportsTransfer = false; + bool supportsCompute = false; + + VkQueue handle; + }; + + struct Swapchain { + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + + VkExtent2D extent; + VkFormat format; + + std::vector images{}; + std::vector imageViews{}; + }; + + // enums + + enum class QueueFlags : uint32_t { + GRAPHICS = (1 << 0), + TRANSFER = (1 << 1), + COMPUTE = (1 << 2), + }; + // functions static std::vector getRequiredVulkanExtensions(SDL_Window* window) @@ -154,16 +194,196 @@ namespace engine { return debugMessengerInfo; } + static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance) + { + VkSurfaceKHR surface; + + if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) { + throw std::runtime_error("Unable to create window surface"); + } + + return surface; + } + + // returns the index of the queue supporting the requested flags + static Queue getQueueSupporting(const std::vector queues, QueueFlags flags) + { + uint32_t bitmask = static_cast(flags); + + for (int i = 0; i < queues.size(); i++) { + + if (bitmask & static_cast(QueueFlags::GRAPHICS)) { + if (queues[i].supportsGraphics == false) continue; + } + + if (bitmask & static_cast(QueueFlags::TRANSFER)) { + if (queues[i].supportsTransfer == false) continue; + } + + if (bitmask & static_cast(QueueFlags::COMPUTE)) { + if (queues[i].supportsCompute == false) continue; + } + + return queues[i]; + + } + + throw std::runtime_error("Unable to find the requested queue"); + } + + static void createSwapchain(VkDevice device, const std::vector queues, SDL_Window* window, VkSurfaceKHR surface, const SwapchainSupportDetails& supportDetails, Swapchain* swapchain) + { + VkResult res; + + VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; + + for (const auto& format : supportDetails.formats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + chosenSurfaceFormat = format; // prefer using srgb non linear colors + } + } + + VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + for (const auto& presMode : supportDetails.presentModes) { + if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { + chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + } + } + + VkExtent2D chosenSwapExtent{}; + + if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { + chosenSwapExtent = supportDetails.caps.currentExtent; + } + else { + // if fb size isn't already found, get it from SDL + int width, height; + SDL_Vulkan_GetDrawableSize(window, &width, &height); + + chosenSwapExtent.width = static_cast(width); + chosenSwapExtent.height = static_cast(height); + + chosenSwapExtent.width = std::clamp( + chosenSwapExtent.width, + supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); + chosenSwapExtent.height = std::clamp( + chosenSwapExtent.height, + supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); + } + + uint32_t imageCount = supportDetails.caps.minImageCount + 1; + if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { + imageCount = supportDetails.caps.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{ + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = surface, + .minImageCount = imageCount, + .imageFormat = chosenSurfaceFormat.format, + .imageColorSpace = chosenSurfaceFormat.colorSpace, + .imageExtent = chosenSwapExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = supportDetails.caps.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = chosenPresentMode, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + + }; + + std::array queueFamilyIndices{ + getQueueSupporting(queues, QueueFlags::GRAPHICS).familyIndex, + getQueueSupporting(queues, QueueFlags::TRANSFER).familyIndex + }; + + // if graphics and transfer queues aren't in the same family + if (queueFamilyIndices[0] != queueFamilyIndices[1]) { + throw std::runtime_error("Graphics and transfer queues must be in the same family"); + } + + res = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain->swapchain); + assert(res == VK_SUCCESS); + + // get all the image handles + uint32_t swapchainImageCount = 0; + res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, nullptr); + assert(res == VK_SUCCESS); + swapchain->images.resize(swapchainImageCount); + res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, swapchain->images.data()); + assert(res == VK_SUCCESS); + + swapchain->format = chosenSurfaceFormat.format; + swapchain->extent = chosenSwapExtent; + + // create image views + swapchain->imageViews.clear(); + for (VkImage image : swapchain->images) { + + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.image = image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapchain->format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + VkImageView imageView; + res = vkCreateImageView(device, &createInfo, nullptr, &imageView); + assert(res == VK_SUCCESS); + + swapchain->imageViews.push_back(imageView); + + } + + } + struct GFXDevice::Impl { - VkInstance m_instance = VK_NULL_HANDLE; - VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE; + VkInstance instance = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; + + VkSurfaceKHR surface = VK_NULL_HANDLE; + SwapchainSupportDetails swapchainSupportDetails{}; // available capabilities, formats, and present modes + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + + std::vector queues{}; + VkCommandPool commandPool = VK_NULL_HANDLE; + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + + VmaAllocator allocator = nullptr; + + Swapchain swapchain{}; }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) { - m_pimpl = std::make_unique(); + // ensure there is only one instance of this class + if (s_gfx_device_instance != nullptr) { + throw std::runtime_error("Multiple gfxdevice classes cannot be created"); + } + s_gfx_device_instance = this; + + pimpl = std::make_unique(); VkResult res; @@ -257,7 +477,7 @@ namespace engine { } #endif - res = vkCreateInstance(&instanceInfo, nullptr, &m_pimpl->m_instance); + res = vkCreateInstance(&instanceInfo, nullptr, &pimpl->instance); if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { throw std::runtime_error("The graphics driver is incompatible with vulkan"); } @@ -266,7 +486,7 @@ namespace engine { // load the instance functions - volkLoadInstanceOnly(m_pimpl->m_instance); + volkLoadInstanceOnly(pimpl->instance); @@ -274,18 +494,342 @@ namespace engine { { VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo(); - VkResult res = vkCreateDebugUtilsMessengerEXT(m_pimpl->m_instance, &createInfo, nullptr, &m_pimpl->m_debugMessenger); + VkResult res = vkCreateDebugUtilsMessengerEXT(pimpl->instance, &createInfo, nullptr, &pimpl->debugMessenger); assert(res == VK_SUCCESS); } + + + // get the surface + pimpl->surface = createSurface(window, pimpl->instance); + + + + // Select a physical device and get capabilities, features, and display modes. + // Create a logical device and create the queues and their corresponding command buffers. + { + // enumerate physical devices + uint32_t physDeviceCount = 0; + VkResult res; + res = vkEnumeratePhysicalDevices(pimpl->instance, &physDeviceCount, nullptr); + assert(res == VK_SUCCESS); + if (physDeviceCount == 0) { + throw std::runtime_error("No GPU found with vulkan support!"); + } + std::vector physicalDevices(physDeviceCount); + res = vkEnumeratePhysicalDevices(pimpl->instance, &physDeviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); + + // find suitable device: + + const std::vector requiredDeviceExtensions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + for (const auto& dev : physicalDevices) { + + // first, check extension support + uint32_t extensionCount; + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); + assert(res == VK_SUCCESS); + std::vector availableExtensions(extensionCount); + res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); + assert(res == VK_SUCCESS); + + for (const auto& extToFind : requiredDeviceExtensions) { + bool extFound = false; + for (const auto& ext : availableExtensions) { + if (strcmp(extToFind, ext.extensionName) == 0) { + extFound = true; + } + } + if (!extFound) { + continue; + } + } + + + + // get swapchain support details: + + // get surface capabilities + res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, pimpl->surface, &pimpl->swapchainSupportDetails.caps); + assert(res == VK_SUCCESS); + + // check there is at least one supported surface format + uint32_t surfaceFormatCount = 0; + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, nullptr); + assert(res == VK_SUCCESS); + if (surfaceFormatCount == 0) { + continue; + } + pimpl->swapchainSupportDetails.formats.resize(surfaceFormatCount); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, pimpl->swapchainSupportDetails.formats.data()); + assert(res == VK_SUCCESS); + + // check there is at least one supported present mode + uint32_t surfacePresentModeCount = 0; + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, nullptr); + assert(res == VK_SUCCESS); + if (surfacePresentModeCount == 0) { + continue; + } + pimpl->swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); + res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, pimpl->swapchainSupportDetails.presentModes.data()); + assert(res == VK_SUCCESS); + + + + // check physical device properties + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(dev, &devProps); + + // check that the device supports vulkan 1.3 + if (devProps.apiVersion < VK_API_VERSION_1_3) { + continue; + } + + pimpl->physicalDevice = dev; + break; + + } // end for() + + if (pimpl->physicalDevice == VK_NULL_HANDLE) { + throw std::runtime_error("No suitable Vulkan physical device found"); + } + + VkPhysicalDeviceProperties devProps; + vkGetPhysicalDeviceProperties(pimpl->physicalDevice, &devProps); + INFO("Selected physical device: {}", devProps.deviceName); + + TRACE("Supported present modes:"); + for (const auto& presMode : pimpl->swapchainSupportDetails.presentModes) { + switch (presMode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); + break; + case VK_PRESENT_MODE_MAILBOX_KHR: + TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); + break; + case VK_PRESENT_MODE_FIFO_KHR: + TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); + break; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); + break; + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); + break; + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); + break; + default: + TRACE("\tUNKNOWN DISPLAY MODE"); + break; + } + } + + + + // Get the queue families and find ones that support graphics, transfer, and compute + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(pimpl->physicalDevice, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(pimpl->physicalDevice, &queueFamilyCount, queueFamilies.data()); + + std::optional graphicsFamilyIndex; + std::optional transferFamilyIndex; + std::optional computeFamilyIndex; + + for (uint32_t i = 0; i < queueFamilyCount; i++) { + VkQueueFamilyProperties family = queueFamilies[i]; + if (family.queueCount > 0) { + if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + TRACE("GRAPHICS:"); + graphicsFamilyIndex = i; + } + if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { + TRACE("TRANSFER:"); + transferFamilyIndex = i; + } + if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { + TRACE("COMPUTE:"); + computeFamilyIndex = i; + } + TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); + } + } + if (graphicsFamilyIndex.has_value() == false || + transferFamilyIndex.has_value() == false) { + throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); + } + + // there is no guaranteed support for compute queues + + std::vector queueCreateInfos{}; + + // use a set to filter out duplicate indices + std::unordered_set uniqueQueueFamilies{}; + if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value()); + if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value()); + if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value()); + + float queuePriority = 1.0f; + + for (uint32_t family : uniqueQueueFamilies) { + // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute + + Queue newQueue{}; + newQueue.familyIndex = family; + newQueue.queueIndex = 0; + if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; + if (transferFamilyIndex == family) newQueue.supportsTransfer = true; + if (computeFamilyIndex == family) newQueue.supportsCompute = true; + + TRACE("Creating queue from family {}", family); + VkDeviceQueueCreateInfo queueCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = family, + .queueCount = 1, + .pQueuePriorities = &queuePriority, + }; + queueCreateInfos.push_back(queueCreateInfo); + pimpl->queues.push_back(newQueue); + } + + // check the physical device is compatible with the surface + VkBool32 graphicsQueueCanPresent; + res = vkGetPhysicalDeviceSurfaceSupportKHR(pimpl->physicalDevice, graphicsFamilyIndex.value(), pimpl->surface, &graphicsQueueCanPresent); + assert(res == VK_SUCCESS); + if (graphicsQueueCanPresent != VK_TRUE) { + throw std::runtime_error("The selected queue family does not support this surface"); + } + + VkDeviceCreateInfo deviceCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), + .pQueueCreateInfos = queueCreateInfos.data(), + // IGNORED: .enabledLayerCount + // IGNORED: .ppEnabledLayerNames + .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), + .ppEnabledExtensionNames = requiredDeviceExtensions.data(), + .pEnabledFeatures = nullptr, + }; + + res = vkCreateDevice(pimpl->physicalDevice, &deviceCreateInfo, nullptr, &pimpl->device); + if (res != VK_SUCCESS) { + throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); + } + + volkLoadDevice(pimpl->device); + + for (auto& q : pimpl->queues) { + vkGetDeviceQueue(pimpl->device, q.familyIndex, q.queueIndex, &q.handle); + } + + Queue gfxQueue = getQueueSupporting(pimpl->queues, QueueFlags::GRAPHICS); + + VkCommandPoolCreateInfo gfxCmdPoolInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = gfxQueue.familyIndex, + }; + + res = vkCreateCommandPool(pimpl->device, &gfxCmdPoolInfo, nullptr, &pimpl->commandPool); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo gfxCmdBufInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = pimpl->commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + res = vkAllocateCommandBuffers(pimpl->device, &gfxCmdBufInfo, &pimpl->commandBuffer); + assert(res == VK_SUCCESS); + } + + + + // now make the memory allocator using vk_mem_alloc.h + { + VmaVulkanFunctions functions{ + .vkGetInstanceProcAddr = nullptr, + .vkGetDeviceProcAddr = nullptr, + .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, + .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, + .vkAllocateMemory = vkAllocateMemory, + .vkFreeMemory = vkFreeMemory, + .vkMapMemory = vkMapMemory, + .vkUnmapMemory = vkUnmapMemory, + .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, + .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, + .vkBindBufferMemory = vkBindBufferMemory, + .vkBindImageMemory = vkBindImageMemory, + .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, + .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, + .vkCreateBuffer = vkCreateBuffer, + .vkDestroyBuffer = vkDestroyBuffer, + .vkCreateImage = vkCreateImage, + .vkDestroyImage = vkDestroyImage, + .vkCmdCopyBuffer = vkCmdCopyBuffer, + .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, + .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, + .vkBindBufferMemory2KHR = vkBindBufferMemory2, + .vkBindImageMemory2KHR = vkBindImageMemory2, + .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, + .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, + .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, + }; + + VmaAllocatorCreateInfo createInfo{ + .flags = 0, + .physicalDevice = pimpl->physicalDevice, + .device = pimpl->device, + .preferredLargeHeapBlockSize = 0, + .pAllocationCallbacks = nullptr, + .pDeviceMemoryCallbacks = nullptr, + .pHeapSizeLimit = nullptr, + .pVulkanFunctions = &functions, + .instance = pimpl->instance, + .vulkanApiVersion = VK_API_VERSION_1_3, + .pTypeExternalMemoryHandleTypes = nullptr + }; + + VkResult res = vmaCreateAllocator(&createInfo, &pimpl->allocator); + assert(res == VK_SUCCESS); + } + + + + // Now make the swapchain + createSwapchain(pimpl->device, pimpl->queues, window, pimpl->surface, pimpl->swapchainSupportDetails, &pimpl->swapchain); + } GFXDevice::~GFXDevice() { TRACE("Destroying GFXDevice..."); - vkDestroyDebugUtilsMessengerEXT(m_pimpl->m_instance, m_pimpl->m_debugMessenger, nullptr); - vkDestroyInstance(m_pimpl->m_instance, nullptr); + for (VkImageView view : pimpl->swapchain.imageViews) { + vkDestroyImageView(pimpl->device, view, nullptr); + } + vkDestroySwapchainKHR(pimpl->device, pimpl->swapchain.swapchain, nullptr); + + vmaDestroyAllocator(pimpl->allocator); + + vkDestroyCommandPool(pimpl->device, pimpl->commandPool, nullptr); + vkDestroyDevice(pimpl->device, nullptr); + vkDestroySurfaceKHR(pimpl->instance, pimpl->surface, nullptr); + vkDestroyDebugUtilsMessengerEXT(pimpl->instance, pimpl->debugMessenger, nullptr); + vkDestroyInstance(pimpl->instance, nullptr); + + s_gfx_device_instance = nullptr; } void GFXDevice::draw() From 4d572441518882515f65ae48e802b0da78034b71 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Tue, 18 Oct 2022 12:11:45 +0100 Subject: [PATCH 31/50] Add swapchain --- src/gfx_device_vulkan.cpp | 152 +++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 44 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2777cbc..5e74aba 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -4,9 +4,7 @@ #ifdef ENGINE_BUILD_VULKAN #include "gfx_device.hpp" - #include "util.hpp" - #include "config.h" #include "log.hpp" @@ -27,9 +25,6 @@ namespace engine { - // singleton variable - static GFXDevice* s_gfx_device_instance = nullptr; - // structures struct LayerInfo { @@ -57,10 +52,18 @@ namespace engine { VkSwapchainKHR swapchain = VK_NULL_HANDLE; VkExtent2D extent; - VkFormat format; + VkSurfaceFormatKHR surfaceFormat; + VkPresentModeKHR presentMode; std::vector images{}; std::vector imageViews{}; + std::vector framebuffers{}; + + VkRenderPass renderpass; + + uint32_t swapchainImageIndex = 0; + VkSemaphore acquireSemaphore = VK_NULL_HANDLE; + VkSemaphore releaseSemaphore = VK_NULL_HANDLE; }; // enums @@ -231,45 +234,43 @@ namespace engine { throw std::runtime_error("Unable to find the requested queue"); } + // This is called not just on initialisation, but also when the window is resized. static void createSwapchain(VkDevice device, const std::vector queues, SDL_Window* window, VkSurfaceKHR surface, const SwapchainSupportDetails& supportDetails, Swapchain* swapchain) { VkResult res; - VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; - + swapchain->surfaceFormat = supportDetails.formats[0]; for (const auto& format : supportDetails.formats) { if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - chosenSurfaceFormat = format; // prefer using srgb non linear colors + swapchain->surfaceFormat = format; // prefer using srgb non linear colors } } - VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; + swapchain->presentMode = VK_PRESENT_MODE_FIFO_KHR; // This mode is always available for (const auto& presMode : supportDetails.presentModes) { if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { - chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing } } - VkExtent2D chosenSwapExtent{}; - if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { - chosenSwapExtent = supportDetails.caps.currentExtent; + swapchain->extent = supportDetails.caps.currentExtent; } else { // if fb size isn't already found, get it from SDL int width, height; SDL_Vulkan_GetDrawableSize(window, &width, &height); - chosenSwapExtent.width = static_cast(width); - chosenSwapExtent.height = static_cast(height); + swapchain->extent.width = static_cast(width); + swapchain->extent.height = static_cast(height); - chosenSwapExtent.width = std::clamp( - chosenSwapExtent.width, + swapchain->extent.width = std::clamp( + swapchain->extent.width, supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); - chosenSwapExtent.height = std::clamp( - chosenSwapExtent.height, + swapchain->extent.height = std::clamp( + swapchain->extent.height, supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); } @@ -284,9 +285,9 @@ namespace engine { .flags = 0, .surface = surface, .minImageCount = imageCount, - .imageFormat = chosenSurfaceFormat.format, - .imageColorSpace = chosenSurfaceFormat.colorSpace, - .imageExtent = chosenSwapExtent, + .imageFormat = swapchain->surfaceFormat.format, + .imageColorSpace = swapchain->surfaceFormat.colorSpace, + .imageExtent = swapchain->extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, @@ -294,9 +295,9 @@ namespace engine { .pQueueFamilyIndices = nullptr, .preTransform = supportDetails.caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = chosenPresentMode, + .presentMode = swapchain->presentMode, .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, + .oldSwapchain = swapchain->swapchain, }; @@ -313,6 +314,11 @@ namespace engine { res = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain->swapchain); assert(res == VK_SUCCESS); + if (createInfo.oldSwapchain != VK_NULL_HANDLE) { + // if recreating swapchain, destroy old one + vkDestroySwapchainKHR(device, createInfo.oldSwapchain, nullptr); + } + // get all the image handles uint32_t swapchainImageCount = 0; res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, nullptr); @@ -321,19 +327,52 @@ namespace engine { res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, swapchain->images.data()); assert(res == VK_SUCCESS); - swapchain->format = chosenSurfaceFormat.format; - swapchain->extent = chosenSwapExtent; + // create the render pass + { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapchain->surfaceFormat.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; - // create image views - swapchain->imageViews.clear(); - for (VkImage image : swapchain->images) { + 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 createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + createInfo.attachmentCount = 1; + createInfo.pAttachments = &colorAttachment; + createInfo.subpassCount = 1; + createInfo.pSubpasses = &subpass; + + //if (swapchain->renderpass != VK_NULL_HANDLE) { + // vkDestroyRenderPass(device, swapchain->renderpass, nullptr); + // swapchain->renderpass = VK_NULL_HANDLE; + //} + res = vkCreateRenderPass(device, &createInfo, nullptr, &swapchain->renderpass); + } + + // create image views and framebuffers + swapchain->imageViews.resize(swapchain->images.size()); + swapchain->framebuffers.resize(swapchain->images.size()); + for (int i = 0; i < swapchain->images.size(); i++) { VkImageViewCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.pNext = nullptr; - createInfo.image = image; + createInfo.image = swapchain->images[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapchain->format; + createInfo.format = swapchain->surfaceFormat.format; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; @@ -343,13 +382,40 @@ namespace engine { createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - - VkImageView imageView; - res = vkCreateImageView(device, &createInfo, nullptr, &imageView); + res = vkCreateImageView(device, &createInfo, nullptr, &swapchain->imageViews[i]); assert(res == VK_SUCCESS); - swapchain->imageViews.push_back(imageView); + VkImageView attachments[] = { + swapchain->imageViews[i] + }; + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = swapchain->renderpass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapchain->extent.width; + framebufferInfo.height = swapchain->extent.height; + framebufferInfo.layers = 1; + + if (swapchain->framebuffers[i] != VK_NULL_HANDLE) { + vkDestroyFramebuffer(device, swapchain->framebuffers[i], nullptr); + } + res = vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchain->framebuffers[i]); + assert(res == VK_SUCCESS); + + } + + // create the swapchain semaphores + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + if (swapchain->acquireSemaphore == VK_NULL_HANDLE) { + res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &swapchain->acquireSemaphore); + assert(res == VK_SUCCESS); + } + if (swapchain->releaseSemaphore == VK_NULL_HANDLE) { + res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &swapchain->releaseSemaphore); + assert(res == VK_SUCCESS); } } @@ -377,12 +443,6 @@ namespace engine { GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) { - // ensure there is only one instance of this class - if (s_gfx_device_instance != nullptr) { - throw std::runtime_error("Multiple gfxdevice classes cannot be created"); - } - s_gfx_device_instance = this; - pimpl = std::make_unique(); VkResult res; @@ -816,9 +876,15 @@ namespace engine { { TRACE("Destroying GFXDevice..."); + vkDestroySemaphore(pimpl->device, pimpl->swapchain.releaseSemaphore, nullptr); + vkDestroySemaphore(pimpl->device, pimpl->swapchain.acquireSemaphore, nullptr); for (VkImageView view : pimpl->swapchain.imageViews) { vkDestroyImageView(pimpl->device, view, nullptr); } + for (VkFramebuffer fb : pimpl->swapchain.framebuffers) { + vkDestroyFramebuffer(pimpl->device, fb, nullptr); + } + vkDestroyRenderPass(pimpl->device, pimpl->swapchain.renderpass, nullptr); vkDestroySwapchainKHR(pimpl->device, pimpl->swapchain.swapchain, nullptr); vmaDestroyAllocator(pimpl->allocator); @@ -828,8 +894,6 @@ namespace engine { vkDestroySurfaceKHR(pimpl->instance, pimpl->surface, nullptr); vkDestroyDebugUtilsMessengerEXT(pimpl->instance, pimpl->debugMessenger, nullptr); vkDestroyInstance(pimpl->instance, nullptr); - - s_gfx_device_instance = nullptr; } void GFXDevice::draw() From 92ff208c355cbfc437527c755b0be72f8fdde5e8 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 20 Oct 2022 20:28:51 +0100 Subject: [PATCH 32/50] Finally get something to show on screen --- include/gfx_device.hpp | 7 +- src/engine.cpp | 10 +- src/gfx_device_vulkan.cpp | 326 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 331 insertions(+), 12 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 574a883..ee959b8 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -45,10 +45,15 @@ namespace engine { // submit command lists and draw to the screen void draw(); + void createPipeline(const char* vertShaderPath, const char* fragShaderPath); + bool createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out); + // waits for the gpu to stop doing stuff to allow for a safe cleanup + void waitIdle(); + private: - class Impl; + struct Impl; std::unique_ptr pimpl; }; diff --git a/src/engine.cpp b/src/engine.cpp index 37f7175..b96943c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -2,6 +2,7 @@ #include "window.hpp" #include "gfx_device.hpp" +#include "resource_manager.hpp" namespace engine { @@ -9,6 +10,10 @@ namespace engine { { m_win = std::make_unique(appName, false); m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); + + engine::ResourceManager resMan{}; + + m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str()); } Application::~Application() @@ -19,7 +24,7 @@ namespace engine { void Application::gameLoop() { uint64_t lastTick = m_win->getNanos(); - constexpr int TICKFREQ = 20; // in hz + constexpr int TICKFREQ = 1; // in hz // single-threaded game loop while (m_win->isRunning()) { @@ -30,6 +35,7 @@ namespace engine { lastTick = m_win->getLastFrameStamp(); // do tick stuff here + m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms"); } @@ -47,6 +53,8 @@ namespace engine { m_win->getInputAndEvents(); } + + m_gfx->waitIdle(); } } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 5e74aba..0e6338b 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -22,10 +22,12 @@ #include #include #include +#include +#include namespace engine { - // structures + // structures and enums struct LayerInfo { std::vector layersAvailable{}; @@ -61,12 +63,14 @@ namespace engine { VkRenderPass renderpass; - uint32_t swapchainImageIndex = 0; - VkSemaphore acquireSemaphore = VK_NULL_HANDLE; - VkSemaphore releaseSemaphore = VK_NULL_HANDLE; + VkSemaphore acquireSemaphore = VK_NULL_HANDLE; // waits until the image is available + VkSemaphore releaseSemaphore = VK_NULL_HANDLE; // waits until rendering finishes }; - // enums + struct Pipeline { + VkPipelineLayout layout = VK_NULL_HANDLE; + VkPipeline handle = VK_NULL_HANDLE; + }; enum class QueueFlags : uint32_t { GRAPHICS = (1 << 0), @@ -74,8 +78,23 @@ namespace engine { COMPUTE = (1 << 2), }; + + // functions + static std::vector readFile(const std::string& filename) + { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + if (file.is_open() == false) { + throw std::runtime_error("Unable to open file " + filename); + } + std::vector buffer(file.tellg()); + file.seekg(0); + file.read(buffer.data(), buffer.size()); + file.close(); + return buffer; + } + static std::vector getRequiredVulkanExtensions(SDL_Window* window) { SDL_bool res; @@ -251,7 +270,7 @@ namespace engine { for (const auto& presMode : supportDetails.presentModes) { if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { - swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + //swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing } } @@ -348,6 +367,14 @@ namespace engine { subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; createInfo.attachmentCount = 1; @@ -355,6 +382,9 @@ namespace engine { createInfo.subpassCount = 1; createInfo.pSubpasses = &subpass; + createInfo.dependencyCount = 1; + createInfo.pDependencies = &dependency; + //if (swapchain->renderpass != VK_NULL_HANDLE) { // vkDestroyRenderPass(device, swapchain->renderpass, nullptr); // swapchain->renderpass = VK_NULL_HANDLE; @@ -420,6 +450,25 @@ namespace engine { } + static VkShaderModule createShaderModule(VkDevice device, const std::vector& code) + { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("failed to create shader module!"); + } + + return shaderModule; + } + + + + // class definitions + struct GFXDevice::Impl { VkInstance instance = VK_NULL_HANDLE; @@ -432,6 +481,7 @@ namespace engine { VkDevice device = VK_NULL_HANDLE; std::vector queues{}; + Queue gfxQueue; VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE; @@ -439,6 +489,10 @@ namespace engine { Swapchain swapchain{}; + VkFence inFlightFence = VK_NULL_HANDLE; + + Pipeline pipeline{}; + }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) @@ -543,7 +597,7 @@ namespace engine { } assert(res == VK_SUCCESS); - + // load the instance functions volkLoadInstanceOnly(pimpl->instance); @@ -792,12 +846,12 @@ namespace engine { vkGetDeviceQueue(pimpl->device, q.familyIndex, q.queueIndex, &q.handle); } - Queue gfxQueue = getQueueSupporting(pimpl->queues, QueueFlags::GRAPHICS); + pimpl->gfxQueue = getQueueSupporting(pimpl->queues, QueueFlags::GRAPHICS); VkCommandPoolCreateInfo gfxCmdPoolInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = gfxQueue.familyIndex, + .queueFamilyIndex = pimpl->gfxQueue.familyIndex, }; res = vkCreateCommandPool(pimpl->device, &gfxCmdPoolInfo, nullptr, &pimpl->commandPool); @@ -865,17 +919,33 @@ namespace engine { assert(res == VK_SUCCESS); } - + // Now make the swapchain createSwapchain(pimpl->device, pimpl->queues, window, pimpl->surface, pimpl->swapchainSupportDetails, &pimpl->swapchain); + + + // for testing purposes, create a pipeline with a simple shader + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + fenceInfo.pNext = nullptr; + res = vkCreateFence(pimpl->device, &fenceInfo, nullptr, &pimpl->inFlightFence); + } GFXDevice::~GFXDevice() { TRACE("Destroying GFXDevice..."); + if (pimpl->pipeline.handle != VK_NULL_HANDLE) { + vkDestroyPipeline(pimpl->device, pimpl->pipeline.handle, nullptr); + vkDestroyPipelineLayout(pimpl->device, pimpl->pipeline.layout, nullptr); + } + + vkDestroyFence(pimpl->device, pimpl->inFlightFence, nullptr); + vkDestroySemaphore(pimpl->device, pimpl->swapchain.releaseSemaphore, nullptr); vkDestroySemaphore(pimpl->device, pimpl->swapchain.acquireSemaphore, nullptr); for (VkImageView view : pimpl->swapchain.imageViews) { @@ -898,7 +968,238 @@ namespace engine { void GFXDevice::draw() { + VkResult res; + + res = vkWaitForFences(pimpl->device, 1, &pimpl->inFlightFence, VK_TRUE, UINT64_MAX); + assert(res == VK_SUCCESS); + res = vkResetFences(pimpl->device, 1, &pimpl->inFlightFence); + assert(res == VK_SUCCESS); + + uint32_t imageIndex; + vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphore, VK_NULL_HANDLE, &imageIndex); + + vkResetCommandBuffer(pimpl->commandBuffer, 0); + // now record command buffer + { + VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + res = vkBeginCommandBuffer(pimpl->commandBuffer, &beginInfo); + assert(res == VK_SUCCESS); + + VkRenderPassBeginInfo renderPassInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + renderPassInfo.renderPass = pimpl->swapchain.renderpass; + renderPassInfo.framebuffer = pimpl->swapchain.framebuffers[imageIndex]; + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = pimpl->swapchain.extent; + + VkClearValue clearColor{ {0.0f, 0.0f, 0.0f, 0.0f} }; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)pimpl->swapchain.extent.width; + viewport.height = (float)pimpl->swapchain.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(pimpl->commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent = pimpl->swapchain.extent; + vkCmdSetScissor(pimpl->commandBuffer, 0, 1, &scissor); + + vkCmdDraw(pimpl->commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(pimpl->commandBuffer); + + res = vkEndCommandBuffer(pimpl->commandBuffer); + assert(res == VK_SUCCESS); + } + + VkSubmitInfo submitInfo{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; + + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &pimpl->swapchain.acquireSemaphore; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &pimpl->commandBuffer; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &pimpl->swapchain.releaseSemaphore; + + res = vkQueueSubmit(pimpl->gfxQueue.handle, 1, &submitInfo, pimpl->inFlightFence); + assert(res == VK_SUCCESS); + + VkPresentInfoKHR presentInfo{ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &pimpl->swapchain.releaseSemaphore; + + VkSwapchainKHR swapchains[] = { pimpl->swapchain.swapchain }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapchains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + res = vkQueuePresentKHR(pimpl->gfxQueue.handle, &presentInfo); + assert(res == VK_SUCCESS); + } + + void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + { + + VkResult res; + + auto vertShaderCode = readFile(vertShaderPath); + auto fragShaderCode = readFile(fragShaderPath); + INFO("Opened shader: {}", std::filesystem::path(vertShaderPath).filename().string()); + + VkShaderModule vertShaderModule = createShaderModule(pimpl->device, vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(pimpl->device, fragShaderCode); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + vertShaderStageInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + fragShaderStageInfo.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo shaderStages[2] = { vertShaderStageInfo, fragShaderStageInfo }; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)pimpl->swapchain.extent.width; + viewport.height = (float)pimpl->swapchain.extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent = pimpl->swapchain.extent; + + std::vector dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicState{}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = dynamicStates.size(); + dynamicState.pDynamicStates = dynamicStates.data(); + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; // ignored + rasterizer.depthBiasClamp = 0.0f; // ignored + rasterizer.depthBiasSlopeFactor = 0.0f; // ignored + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // ignored + multisampling.pSampleMask = nullptr; // ignored + multisampling.alphaToCoverageEnable = VK_FALSE; // ignored + multisampling.alphaToOneEnable = VK_FALSE; // ignored + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // ignored + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // ignored + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // ignored + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // ignored + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // ignored + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // ignored + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; // ignored + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; // ignored + colorBlending.blendConstants[1] = 0.0f; // ignored + colorBlending.blendConstants[2] = 0.0f; // ignored + colorBlending.blendConstants[3] = 0.0f; // ignored + + VkPipelineLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + // everything is 0 because we're not using uniforms + layoutInfo.setLayoutCount = 0; + layoutInfo.pSetLayouts = nullptr; + layoutInfo.pushConstantRangeCount = 0; + layoutInfo.pPushConstantRanges = nullptr; + + res = vkCreatePipelineLayout(pimpl->device, &layoutInfo, nullptr, &pimpl->pipeline.layout); + assert(res == VK_SUCCESS); + + VkGraphicsPipelineCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + createInfo.stageCount = 2; + createInfo.pStages = shaderStages; + createInfo.pVertexInputState = &vertexInputInfo; + createInfo.pInputAssemblyState = &inputAssembly; + createInfo.pViewportState = &viewportState; + createInfo.pRasterizationState = &rasterizer; + createInfo.pMultisampleState = &multisampling; + createInfo.pDepthStencilState = nullptr; + createInfo.pColorBlendState = &colorBlending; + createInfo.pDynamicState = &dynamicState; + createInfo.layout = pimpl->pipeline.layout; + createInfo.renderPass = pimpl->swapchain.renderpass; + createInfo.subpass = 0; + createInfo.basePipelineHandle = VK_NULL_HANDLE; + createInfo.basePipelineIndex = -1; + + res = vkCreateGraphicsPipelines(pimpl->device, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pimpl->pipeline.handle); + assert(res == VK_SUCCESS); + + vkDestroyShaderModule(pimpl->device, fragShaderModule, nullptr); + vkDestroyShaderModule(pimpl->device, vertShaderModule, nullptr); + } bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) @@ -906,6 +1207,11 @@ namespace engine { return false; } + void GFXDevice::waitIdle() + { + vkDeviceWaitIdle(pimpl->device); + } + } #endif From ddefcc196734798b0cec131223c1d548110cb352 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 21 Oct 2022 12:31:46 +0100 Subject: [PATCH 33/50] Add stuff --- CMakeLists.txt | 2 + src/gfx_device_null.cpp | 48 ++++++++++++++++++++++ src/gfx_device_opengl45.cpp | 82 +++++++++++++++++++++++++++++++++++++ src/gfx_device_vulkan.cpp | 1 + 4 files changed, 133 insertions(+) create mode 100644 src/gfx_device_null.cpp create mode 100644 src/gfx_device_opengl45.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec0f042..72b10d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ add_library(${PROJECT_NAME} STATIC "src/resource_manager.cpp" "src/gfx_device_vulkan.cpp" + "src/gfx_device_null.cpp" + "src/gfx_device_opengl45.cpp" # PUBLIC API diff --git a/src/gfx_device_null.cpp b/src/gfx_device_null.cpp new file mode 100644 index 0000000..61110a3 --- /dev/null +++ b/src/gfx_device_null.cpp @@ -0,0 +1,48 @@ +// The implementation of the graphics layer using Vulkan 1.3. +// This uses SDL specific code + +#ifdef ENGINE_BUILD_NULLGFX + +#include "gfx_device.hpp" +#include "util.hpp" +#include "config.h" +#include "log.hpp" + +namespace engine { + + // class definitions + + struct GFXDevice::Impl { + + }; + + GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) + { + pimpl = std::make_unique(); + } + + GFXDevice::~GFXDevice() + { + TRACE("Destroying GFXDevice..."); + } + + void GFXDevice::draw() + { + } + + void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + { + } + + bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + { + return true; + } + + void GFXDevice::waitIdle() + { + } + +} + +#endif diff --git a/src/gfx_device_opengl45.cpp b/src/gfx_device_opengl45.cpp new file mode 100644 index 0000000..9b42ec9 --- /dev/null +++ b/src/gfx_device_opengl45.cpp @@ -0,0 +1,82 @@ +// The implementation of the graphics layer using Vulkan 1.3. +// This uses SDL specific code + +#ifdef ENGINE_BUILD_OPENGL + +#include "gfx_device.hpp" +#include "util.hpp" +#include "config.h" +#include "log.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace engine { + + // structures and enums + + static std::vector readFile(const std::string& filename) + { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + if (file.is_open() == false) { + throw std::runtime_error("Unable to open file " + filename); + } + std::vector buffer(file.tellg()); + file.seekg(0); + file.read(buffer.data(), buffer.size()); + file.close(); + return buffer; + } + + + + // class definitions + + struct GFXDevice::Impl { + + SDL_GLContext context = nullptr; + + }; + + GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) + { + pimpl = std::make_unique(); + + pimpl->context = SDL_GL_CreateContext(window); + + } + + GFXDevice::~GFXDevice() + { + TRACE("Destroying GFXDevice..."); + } + + void GFXDevice::draw() + { + } + + void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + { + } + + bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + { + return false; + } + + void GFXDevice::waitIdle() + { + glFinish(); + } + +} + +#endif diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 0e6338b..7571d1d 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace engine { From 36c07aa4d17ec26a8611c8311d8b86db8d31dcab Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 21 Oct 2022 13:58:59 +0100 Subject: [PATCH 34/50] Add support for window resizing with Vulkan --- CMakeLists.txt | 2 + gfx_device_vulkan (2).cpp | 1094 ------------------------------------- include/gfx_device.hpp | 8 +- src/engine.cpp | 4 +- src/gfx_device_vulkan.cpp | 162 +++--- src/resources/shader.cpp | 2 +- src/resources/texture.cpp | 2 +- 7 files changed, 89 insertions(+), 1185 deletions(-) delete mode 100644 gfx_device_vulkan (2).cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 72b10d4..2c42ec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,8 @@ set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) if (MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W3) target_compile_options(${PROJECT_NAME} PRIVATE /MP) + + target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) endif() target_include_directories(${PROJECT_NAME} PUBLIC include) diff --git a/gfx_device_vulkan (2).cpp b/gfx_device_vulkan (2).cpp deleted file mode 100644 index 4886cb5..0000000 --- a/gfx_device_vulkan (2).cpp +++ /dev/null @@ -1,1094 +0,0 @@ -#if 0 - -// The implementation of the graphics layer using Vulkan 1.3 - -#ifdef ENGINE_BUILD_VULKAN - -#include "gfx_device.hpp" - -#include "util.hpp" - -#include "config.h" -#include "log.hpp" - -#define VOLK_IMPLEMENTATION -#include - -#define VMA_STATIC_VULKAN_FUNCTIONS 0 -#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 -#define VMA_VULKAN_VERSION 1003000 -#define VMA_IMPLEMENTATION -#include - -#include - -#include - -#include -#include -#include -#include - -namespace engine { - - static std::vector getRequiredVulkanExtensions(SDL_Window* window) - { - SDL_bool res; - - unsigned int sdlExtensionCount = 0; - res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr); - assert(res == SDL_TRUE); - std::vector requiredExtensions(sdlExtensionCount); - res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, requiredExtensions.data()); - assert(res == SDL_TRUE); - - return requiredExtensions; - } - - static VkSurfaceKHR createSurface(SDL_Window* window, VkInstance instance) - { - VkSurfaceKHR surface; - - if (SDL_Vulkan_CreateSurface(window, instance, &surface) == false) { - throw std::runtime_error("Unable to create window surface"); - } - - return surface; - } - - static uint32_t getMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProps; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); - for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) { - if (typeFilter & (1 << i)) { - return i; - } - } - - throw std::runtime_error("Failed to find a suitable memory type"); - } - - class GFXDevice::Impl { - - public: - Impl(const char* appName, const char* appVersion, SDL_Window* window) - { -#ifdef NDEBUG - // release mode: don't use validation layer - LayerInfo layerInfo(false); -#else - // debug mode: use validation layer - LayerInfo layerInfo(true); -#endif - auto instance = std::make_shared(appName, appVersion, layerInfo, getRequiredVulkanExtensions(window)); - - volkLoadInstanceOnly(instance->getHandle()); - - m_debugMessenger = std::make_unique(instance); // owns the instance - auto surface = std::make_shared(window, instance); // owns the instance - - auto device = std::make_shared(surface); // owns the surface - auto allocator = std::make_shared(device); // owns the device - - m_swapchain = std::make_unique(device, allocator); // owns the device, allocator - - } - - private: - - struct LayerInfo { - - LayerInfo(bool useValidation) - { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data()); - assert(res == VK_SUCCESS); - - if (useValidation == true) { - // find validation layer and print all layers to log - for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) { - if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { - validationLayer = it; - } - } - if (validationLayer.has_value() == false) { - throw std::runtime_error("The validation layer was not found. Quitting."); - } - } - } - - static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; - - std::vector layersAvailable{}; - std::optional::iterator> validationLayer; - - }; - - class Instance { - - public: - Instance(const char* appName, const char* appVersion, const LayerInfo& layerInfo, const std::vector& windowExtensions) - { - VkResult res; - - int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; - assert(versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; - assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); - - VkApplicationInfo applicationInfo{ - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = appName, - .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), - .pEngineName = "engine", - .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_API_VERSION_1_3, - }; - - // make a list of all extensions to use - std::vector extensions{}; - extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); - - // also use debug utils extension - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - - std::vector layers{}; - - if (layerInfo.validationLayer.has_value()) { - layers.push_back(layerInfo.validationLayer.value()->layerName); - } - - VkInstanceCreateInfo instanceInfo{ - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &applicationInfo, - .enabledLayerCount = (uint32_t)layers.size(), - .ppEnabledLayerNames = layers.data(), - .enabledExtensionCount = (uint32_t)extensions.size(), - .ppEnabledExtensionNames = extensions.data(), - }; - - VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); - - if (layerInfo.validationLayer.has_value()) { - instanceInfo.pNext = &debugMessengerCreateInfo; - } - -#ifndef NDEBUG - for (const char* ext : extensions) { - TRACE("Using Vulkan instance extension: {}", ext); - } -#endif - - res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); - if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - throw std::runtime_error("The graphics driver is incompatible with vulkan"); - } - assert(res == VK_SUCCESS); - - } - - Instance(const Instance&) = delete; - Instance& operator=(const Instance&) = delete; - ~Instance() - { - TRACE("Destroying instance..."); - vkDestroyInstance(m_handle, nullptr); - } - - VkInstance getHandle() const - { - return m_handle; - } - - private: - VkInstance m_handle; - - }; - - class DebugMessenger { - - public: - DebugMessenger(std::shared_ptr instance) : m_instance(instance) - { - VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); - - VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance->getHandle(), &createInfo, nullptr, &m_messengerHandle); - assert(res == VK_SUCCESS); - } - - DebugMessenger(const DebugMessenger&) = delete; - DebugMessenger& operator=(const DebugMessenger&) = delete; - ~DebugMessenger() - { - TRACE("Destroying debug messenger..."); - vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); - } - - static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() - { - VkDebugUtilsMessengerCreateInfoEXT createInfo{ - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .messageSeverity = 0, - .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, - .pfnUserCallback = messengerCallback, - .pUserData = nullptr, - }; - - switch (MESSAGE_LEVEL) { - case Severity::VERBOSE: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - // fall-through - case Severity::INFO: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; - // fall-through - case Severity::WARNING: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - // fall-through - case Severity::ERROR: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - // fall-through - default: - break; - } - - return createInfo; - } - - private: - std::shared_ptr m_instance; - VkDebugUtilsMessengerEXT m_messengerHandle; - - enum class Severity { - VERBOSE, - INFO, - WARNING, - ERROR - }; - static constexpr Severity MESSAGE_LEVEL = Severity::WARNING; - - static VkBool32 messengerCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - - std::string msgType{}; - - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (GENERAL)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) - msgType += " (PERF.)"; - if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - msgType += " (VALID.)"; - - switch (messageSeverity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; - default: - break; - } - return VK_FALSE; - } - - }; - - class Surface { - - public: - Surface(SDL_Window* window, std::shared_ptr instance) : m_instance(instance), m_window(window) - { - m_handle = createSurface(m_window, instance->getHandle()); - } - Surface(const Surface&) = delete; - Surface& operator=(const Surface&) = delete; - - ~Surface() - { - TRACE("Destroying surface..."); - vkDestroySurfaceKHR(m_instance->getHandle(), m_handle, nullptr); - } - - VkSurfaceKHR getHandle() const - { - return m_handle; - } - - SDL_Window* getWindow() const - { - return m_window; - } - - VkInstance getInstance() const - { - return m_instance->getHandle(); - } - - private: - std::shared_ptr m_instance; - VkSurfaceKHR m_handle; - SDL_Window* m_window; - - }; - - class Device { - - public: - Device(std::shared_ptr surface) : m_surface(surface) - { - // enumerate physical devices - uint32_t physDeviceCount = 0; - VkResult res; - res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, nullptr); - assert(res == VK_SUCCESS); - if (physDeviceCount == 0) { - throw std::runtime_error("No GPU found with vulkan support!"); - } - std::vector physicalDevices(physDeviceCount); - res = vkEnumeratePhysicalDevices(m_surface->getInstance(), &physDeviceCount, physicalDevices.data()); - assert(res == VK_SUCCESS); - - // find suitable device: - - const std::vector requiredDeviceExtensions{ - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - for (const auto& dev : physicalDevices) { - - // first, check extension support - uint32_t extensionCount; - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr); - assert(res == VK_SUCCESS); - std::vector availableExtensions(extensionCount); - res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data()); - assert(res == VK_SUCCESS); - - for (const auto& extToFind : requiredDeviceExtensions) { - bool extFound = false; - for (const auto& ext : availableExtensions) { - if (strcmp(extToFind, ext.extensionName) == 0) { - extFound = true; - } - } - if (!extFound) { - continue; - } - } - - - - // get swapchain support details: - - // get surface capabilities - res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface->getHandle(), &m_swapchainSupportDetails.caps); - assert (res == VK_SUCCESS); - - // check there is at least one supported surface format - uint32_t surfaceFormatCount = 0; - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, nullptr); - assert(res == VK_SUCCESS); - if (surfaceFormatCount == 0) { - continue; - } - m_swapchainSupportDetails.formats.resize(surfaceFormatCount); - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface->getHandle(), &surfaceFormatCount, m_swapchainSupportDetails.formats.data()); - assert(res == VK_SUCCESS); - - // check there is at least one supported present mode - uint32_t surfacePresentModeCount = 0; - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, nullptr); - assert(res == VK_SUCCESS); - if (surfacePresentModeCount == 0) { - continue; - } - m_swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface->getHandle(), &surfacePresentModeCount, m_swapchainSupportDetails.presentModes.data()); - assert(res == VK_SUCCESS); - - - - // check physical device properties - VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(dev, &devProps); - - // check that the device supports vulkan 1.3 - if (devProps.apiVersion < VK_API_VERSION_1_3) { - continue; - } - - m_physicalDevice = dev; - break; - - } // end for() - - if (m_physicalDevice == VK_NULL_HANDLE) { - throw std::runtime_error("No suitable Vulkan physical device found"); - } - - VkPhysicalDeviceProperties devProps; - vkGetPhysicalDeviceProperties(m_physicalDevice, &devProps); - INFO("Selected physical device: {}", devProps.deviceName); - - TRACE("Supported present modes:"); - for (const auto& presMode : m_swapchainSupportDetails.presentModes) { - switch (presMode) { - case VK_PRESENT_MODE_IMMEDIATE_KHR: - TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); - break; - case VK_PRESENT_MODE_MAILBOX_KHR: - TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); - break; - case VK_PRESENT_MODE_FIFO_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); - break; - case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); - break; - case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); - break; - case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); - break; - default: - TRACE("\tUNKNOWN DISPLAY MODE"); - break; - } - } - - - - // Get the queue families and find ones that support graphics, transfer, and compute - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data()); - - std::optional graphicsFamilyIndex; - std::optional transferFamilyIndex; - std::optional computeFamilyIndex; - - for (uint32_t i = 0; i < queueFamilyCount; i++) { - VkQueueFamilyProperties family = queueFamilies[i]; - if (family.queueCount > 0) { - if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - TRACE("GRAPHICS:"); - graphicsFamilyIndex = i; - } - if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) { - TRACE("TRANSFER:"); - transferFamilyIndex = i; - } - if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) { - TRACE("COMPUTE:"); - computeFamilyIndex = i; - } - TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount); - } - } - if ( graphicsFamilyIndex.has_value() == false || - transferFamilyIndex.has_value() == false) { - throw std::runtime_error("Unable to find queues with the GRAPHICS or TRANSFER family flags"); - } - - // there is no guaranteed support for compute queues - - std::vector queueCreateInfos{}; - - // use a set to filter out duplicate indices - std::unordered_set uniqueQueueFamilies{}; - if (graphicsFamilyIndex.has_value()) uniqueQueueFamilies.insert(graphicsFamilyIndex.value()); - if (transferFamilyIndex.has_value()) uniqueQueueFamilies.insert(transferFamilyIndex.value()); - if (computeFamilyIndex.has_value()) uniqueQueueFamilies.insert(computeFamilyIndex.value()); - - float queuePriority = 1.0f; - - for (uint32_t family : uniqueQueueFamilies) { - // create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute - - Queue newQueue{}; - newQueue.familyIndex = family; - newQueue.queueIndex = 0; - if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; - if (transferFamilyIndex == family) newQueue.supportsTransfer = true; - if (computeFamilyIndex == family) newQueue.supportsCompute = true; - - TRACE("Creating queue from family {}", family); - VkDeviceQueueCreateInfo queueCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = family, - .queueCount = 1, - .pQueuePriorities = &queuePriority, - }; - queueCreateInfos.push_back(queueCreateInfo); - m_queues.push_back(newQueue); - } - - // check the physical device is compatible with the surface - VkBool32 graphicsQueueCanPresent; - res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, graphicsFamilyIndex.value(), m_surface->getHandle(), &graphicsQueueCanPresent); - assert(res == VK_SUCCESS); - if (graphicsQueueCanPresent != VK_TRUE) { - throw std::runtime_error("The selected queue family does not support this surface"); - } - - VkDeviceCreateInfo deviceCreateInfo{ - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueCreateInfoCount = (uint32_t)queueCreateInfos.size(), - .pQueueCreateInfos = queueCreateInfos.data(), - // IGNORED: .enabledLayerCount - // IGNORED: .ppEnabledLayerNames - .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), - .ppEnabledExtensionNames = requiredDeviceExtensions.data(), - .pEnabledFeatures = nullptr, - }; - - res = vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_handle); - if (res != VK_SUCCESS) { - throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res)); - } - - volkLoadDevice(m_handle); - - for (auto& q : m_queues) { - vkGetDeviceQueue(m_handle, q.familyIndex, q.queueIndex, &q.handle); - } - - Queue gfxQueue = getGraphicsQueue(); - - VkCommandPoolCreateInfo gfxCmdPoolInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = gfxQueue.familyIndex, - }; - - res = vkCreateCommandPool(m_handle, &gfxCmdPoolInfo, nullptr, &m_gfxCommandPool); - assert(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo gfxCmdBufInfo{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = m_gfxCommandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1 - }; - - res = vkAllocateCommandBuffers(m_handle, &gfxCmdBufInfo, &m_gfxCommandBuffer); - assert(res == VK_SUCCESS); - - } - Device(const Device&) = delete; - Device& operator=(const Device&) = delete; - - ~Device() - { - TRACE("Destroying device..."); - vkDestroyCommandPool(m_handle, m_gfxCommandPool, nullptr); - vkDestroyDevice(m_handle, nullptr); - } - - VkDevice getHandle() const - { - return m_handle; - } - - VkPhysicalDevice getPhysicalDevice() const - { - return m_physicalDevice; - } - - VkInstance getInstance() const - { - return m_surface->getInstance(); - } - - struct SwapchainSupportDetails { - VkSurfaceCapabilitiesKHR caps{}; - std::vector formats{}; - std::vector presentModes{}; - }; - - SwapchainSupportDetails getSupportDetails() - { - return m_swapchainSupportDetails; - } - - struct Queue { - uint32_t familyIndex; - uint32_t queueIndex; - bool supportsGraphics = false; - bool supportsTransfer = false; - bool supportsCompute = false; - - VkQueue handle; - }; - - VkCommandBuffer getGraphicsCommandBuffer() - { - return m_gfxCommandBuffer; - } - - std::shared_ptr getSurface() - { - return m_surface; - } - - Queue getGraphicsQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsGraphics) return queue; - } - throw std::runtime_error("Unable to find graphics queue"); - } - - Queue getTransferQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsTransfer) return queue; - } - throw std::runtime_error("Unable to find transfer queue"); - } - - /*Queue getComputeQueue() - { - for (const auto& queue : m_queues) { - if (queue.supportsCompute) return queue; - } - throw std::runtime_error("Unable to find compute queue"); - }*/ - - private: - std::shared_ptr m_surface; - - SwapchainSupportDetails m_swapchainSupportDetails{}; - - VkDevice m_handle = VK_NULL_HANDLE; - - VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; - - std::vector m_queues{}; - - VkCommandPool m_gfxCommandPool = VK_NULL_HANDLE; - VkCommandBuffer m_gfxCommandBuffer = VK_NULL_HANDLE; - - }; - - class GPUAllocator { - public: - GPUAllocator(std::shared_ptr device) : m_device(device) - { - VmaVulkanFunctions functions{ - .vkGetInstanceProcAddr = nullptr, - .vkGetDeviceProcAddr = nullptr, - .vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties, - .vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties, - .vkAllocateMemory = vkAllocateMemory, - .vkFreeMemory = vkFreeMemory, - .vkMapMemory = vkMapMemory, - .vkUnmapMemory = vkUnmapMemory, - .vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges, - .vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges, - .vkBindBufferMemory = vkBindBufferMemory, - .vkBindImageMemory = vkBindImageMemory, - .vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements, - .vkGetImageMemoryRequirements = vkGetImageMemoryRequirements, - .vkCreateBuffer = vkCreateBuffer, - .vkDestroyBuffer = vkDestroyBuffer, - .vkCreateImage = vkCreateImage, - .vkDestroyImage = vkDestroyImage, - .vkCmdCopyBuffer = vkCmdCopyBuffer, - .vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2, - .vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2, - .vkBindBufferMemory2KHR = vkBindBufferMemory2, - .vkBindImageMemory2KHR = vkBindImageMemory2, - .vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2, - .vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements, - .vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements, - }; - - VmaAllocatorCreateInfo createInfo{ - .flags = 0, - .physicalDevice = m_device->getPhysicalDevice(), - .device = m_device->getHandle(), - .preferredLargeHeapBlockSize = 0, - .pAllocationCallbacks = nullptr, - .pDeviceMemoryCallbacks = nullptr, - .pHeapSizeLimit = nullptr, - .pVulkanFunctions = &functions, - .instance = m_device->getInstance(), - .vulkanApiVersion = VK_API_VERSION_1_3, - .pTypeExternalMemoryHandleTypes = nullptr - }; - - VkResult res = vmaCreateAllocator(&createInfo, &m_handle); - assert(res == VK_SUCCESS); - - } - GPUAllocator(const GPUAllocator&) = delete; - GPUAllocator& operator=(const GPUAllocator&) = delete; - ~GPUAllocator() - { - TRACE("Destroying allocator..."); - vmaDestroyAllocator(m_handle); - } - - VmaAllocator getHandle() const - { - return m_handle; - } - - private: - std::shared_ptr m_device; - VmaAllocator m_handle = nullptr; - }; - - class Swapchain { - - public: - Swapchain(std::shared_ptr device, std::shared_ptr allocator) : m_device(device), m_allocator(allocator) - { - VkResult res; - - auto supportDetails = m_device->getSupportDetails(); - - VkSurfaceFormatKHR chosenSurfaceFormat = supportDetails.formats[0]; - - for (const auto& format : supportDetails.formats) { - if ( format.format == VK_FORMAT_B8G8R8A8_SRGB && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) { - chosenSurfaceFormat = format; // prefer using srgb non linear colors - } - } - - VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR; - - for (const auto& presMode : supportDetails.presentModes) { - if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { - chosenPresentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing - } - } - - VkExtent2D chosenSwapExtent{}; - - if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { - chosenSwapExtent = supportDetails.caps.currentExtent; - } else { - // if fb size isn't already found, get it from SDL - int width, height; - SDL_Vulkan_GetDrawableSize(m_device->getSurface()->getWindow(), &width, &height); - - chosenSwapExtent.width = static_cast(width); - chosenSwapExtent.height = static_cast(height); - - chosenSwapExtent.width = std::clamp( - chosenSwapExtent.width, - supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); - chosenSwapExtent.height = std::clamp( - chosenSwapExtent.height, - supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); - } - - uint32_t imageCount = supportDetails.caps.minImageCount + 1; - if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { - imageCount = supportDetails.caps.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo{ - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = nullptr, - .flags = 0, - .surface = m_device->getSurface()->getHandle(), - .minImageCount = imageCount, - .imageFormat = chosenSurfaceFormat.format, - .imageColorSpace = chosenSurfaceFormat.colorSpace, - .imageExtent = chosenSwapExtent, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .preTransform = supportDetails.caps.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = chosenPresentMode, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - - }; - - std::array queueFamilyIndices{ - m_device->getGraphicsQueue().familyIndex, - m_device->getTransferQueue().familyIndex - }; - - // if graphics and transfer queues aren't in the same family - if (queueFamilyIndices[0] != queueFamilyIndices[1]) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = queueFamilyIndices.size(); - createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); - } - - res = vkCreateSwapchainKHR(m_device->getHandle(), &createInfo, nullptr, &m_handle); - assert(res == VK_SUCCESS); - - // get all the image handles - uint32_t swapchainImageCount = 0; - res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, nullptr); - assert(res == VK_SUCCESS); - m_images.resize(swapchainImageCount); - res = vkGetSwapchainImagesKHR(m_device->getHandle(), m_handle, &swapchainImageCount, m_images.data()); - assert(res == VK_SUCCESS); - - m_currentFormat = chosenSurfaceFormat.format; - m_currentExtent = chosenSwapExtent; - - // create image views - m_imageViews.clear(); - for (VkImage image : m_images) { - - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.image = image; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_currentFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - VkImageView imageView; - res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &imageView); - assert (res == VK_SUCCESS); - - m_imageViews.push_back(imageView); - - } - - // create depth buffer - - m_depthBuffer = std::make_unique(m_currentExtent, m_device.get(), m_allocator.get()); - - } - Swapchain(const Swapchain&) = delete; - Swapchain& operator=(const Swapchain&) = delete; - - ~Swapchain() - { - TRACE("Destroying swapchain..."); - for (VkImageView view : m_imageViews) { - vkDestroyImageView(m_device->getHandle(), view, nullptr); - } - vkDestroySwapchainKHR(m_device->getHandle(), m_handle, nullptr); - } - - private: - std::shared_ptr m_device; - std::shared_ptr m_allocator; - - VkSwapchainKHR m_handle = VK_NULL_HANDLE; - - std::vector m_images; - std::vector m_imageViews; - - VkFormat m_currentFormat{}; - VkExtent2D m_currentExtent{}; - - class DepthStencil { - public: - DepthStencil(VkExtent2D bufferExtent, Device* device, GPUAllocator* allocator) : m_device(device), m_allocator(allocator) - { - - // find a suitable format - - const std::vector formatCandidates{ - VK_FORMAT_D24_UNORM_S8_UINT, - VK_FORMAT_D32_SFLOAT_S8_UINT, - }; - - const VkFormatFeatureFlags formatFeatures = - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - - std::optional chosenFormat; - - for (VkFormat format : formatCandidates) { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(m_device->getPhysicalDevice(), format, &props); - - if ((props.optimalTilingFeatures & formatFeatures) == formatFeatures) { - chosenFormat = format; - break; - } - } - - if (chosenFormat.has_value() == false) { - throw std::runtime_error("Unable to find a suitable depth-stencil format"); - } - - m_format = chosenFormat.value(); - - switch (m_format) { - case VK_FORMAT_D24_UNORM_S8_UINT: - INFO("VK_FORMAT_D24_UNORM_S8_UINT"); - break; - case VK_FORMAT_D32_SFLOAT_S8_UINT: - INFO("VK_FORMAT_D32_SFLOAT_S8_UINT"); - break; - default: - break; - } - - VkImageCreateInfo imageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = m_format, - .extent = { - .width = bufferExtent.width, - .height = bufferExtent.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 1, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - - VmaAllocationCreateInfo allocInfo{}; - allocInfo.usage = VMA_MEMORY_USAGE_AUTO; - - VkResult res; - - res = vmaCreateImage(m_allocator->getHandle(), &imageCreateInfo, &allocInfo, &m_image, &m_allocation, nullptr); - assert(res == VK_SUCCESS); - - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.image = m_image; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_format; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - res = vkCreateImageView(m_device->getHandle(), &createInfo, nullptr, &m_imageView); - assert (res == VK_SUCCESS); - - } - DepthStencil(const DepthStencil&) = delete; - DepthStencil& operator=(const DepthStencil&) = delete; - ~DepthStencil() - { - TRACE("Destroying DepthStencil..."); - vkDestroyImageView(m_device->getHandle(), m_imageView, nullptr); - // destroy allocation - vmaDestroyImage(m_allocator->getHandle(), m_image, m_allocation); - } - - private: - - VkFormat m_format; - - Device* m_device; - GPUAllocator* m_allocator; - - VkImage m_image = VK_NULL_HANDLE; - VmaAllocation m_allocation; - VkImageView m_imageView = VK_NULL_HANDLE; - - }; - - std::unique_ptr m_depthBuffer; - - }; - - std::unique_ptr m_debugMessenger; // uses instance - - std::unique_ptr m_swapchain; - - }; - - GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) - { - VkResult res; - res = volkInitialize(); - if (res == VK_ERROR_INITIALIZATION_FAILED) { - throw std::runtime_error("Unable to load vulkan, is it installed?"); - } - assert(res == VK_SUCCESS); - - uint32_t vulkanVersion = volkGetInstanceVersion(); - if (vulkanVersion < VK_MAKE_VERSION(1, 3, 0)) { - throw std::runtime_error("The loaded Vulkan version must be at least 1.3"); - } - - m_pimpl = std::make_unique(appName, appVersion, window); - - } - - GFXDevice::~GFXDevice() - { - TRACE("Destroying GFXDevice..."); - } - - void GFXDevice::draw() - { - - } - - bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) - { - return false; - } - -} - -#endif - -#endif \ No newline at end of file diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index ee959b8..9f1c021 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -42,14 +42,16 @@ namespace engine { GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); - // submit command lists and draw to the screen + // Call once per frame. Executes all queued draw calls and renders to the screen. void draw(); - + + // creates the equivalent of an OpenGL shader program & vertex attrib configuration void createPipeline(const char* vertShaderPath, const char* fragShaderPath); + // creates a vertex array for holding mesh data bool createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out); - // waits for the gpu to stop doing stuff to allow for a safe cleanup + // wait until all the active GPU queues have finished working void waitIdle(); private: diff --git a/src/engine.cpp b/src/engine.cpp index b96943c..c1d9c0f 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,7 +8,7 @@ namespace engine { Application::Application(const char* appName, const char* appVersion) { - m_win = std::make_unique(appName, false); + m_win = std::make_unique(appName, true); m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); engine::ResourceManager resMan{}; @@ -35,7 +35,7 @@ namespace engine { lastTick = m_win->getLastFrameStamp(); // do tick stuff here - m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms"); + m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms, " + std::to_string(m_win->getFPS()) + " fps"); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 7571d1d..7a9b7df 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -35,12 +35,6 @@ namespace engine { std::optional::iterator> validationLayer; }; - struct SwapchainSupportDetails { - VkSurfaceCapabilitiesKHR caps{}; - std::vector formats{}; - std::vector presentModes{}; - }; - struct Queue { uint32_t familyIndex; uint32_t queueIndex; @@ -73,6 +67,11 @@ namespace engine { VkPipeline handle = VK_NULL_HANDLE; }; + struct AllocatedBuffer { + VkBuffer buffer = VK_NULL_HANDLE; + VmaAllocation allocation = nullptr; + }; + enum class QueueFlags : uint32_t { GRAPHICS = (1 << 0), TRANSFER = (1 << 1), @@ -255,12 +254,47 @@ namespace engine { } // This is called not just on initialisation, but also when the window is resized. - static void createSwapchain(VkDevice device, const std::vector queues, SDL_Window* window, VkSurfaceKHR surface, const SwapchainSupportDetails& supportDetails, Swapchain* swapchain) + static void createSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, const std::vector queues, SDL_Window* window, VkSurfaceKHR surface, Swapchain* swapchain) { VkResult res; - swapchain->surfaceFormat = supportDetails.formats[0]; - for (const auto& format : supportDetails.formats) { + // get surface capabilities + VkSurfaceCapabilitiesKHR caps; + res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &caps); + assert(res == VK_SUCCESS); + + // check there is at least one supported surface format + uint32_t surfaceFormatCount = 0; + std::vector formats{}; + res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, nullptr); + assert(res == VK_SUCCESS); + formats.resize(surfaceFormatCount); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, formats.data()); + assert(res == VK_SUCCESS); + + // check there is at least one supported present mode + uint32_t surfacePresentModeCount = 0; + std::vector presentModes{}; + res = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &surfacePresentModeCount, nullptr); + assert(res == VK_SUCCESS); + presentModes.resize(surfacePresentModeCount); + res = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &surfacePresentModeCount, presentModes.data()); + assert(res == VK_SUCCESS); + + + + // delete old framebuffers + for (VkFramebuffer fb : swapchain->framebuffers) { + vkDestroyFramebuffer(device, fb, nullptr); + } + + // delete old image views + for (VkImageView view : swapchain->imageViews) { + vkDestroyImageView(device, view, nullptr); + } + + swapchain->surfaceFormat = formats[0]; + for (const auto& format : formats) { if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { swapchain->surfaceFormat = format; // prefer using srgb non linear colors @@ -269,14 +303,14 @@ namespace engine { swapchain->presentMode = VK_PRESENT_MODE_FIFO_KHR; // This mode is always available - for (const auto& presMode : supportDetails.presentModes) { + for (const auto& presMode : presentModes) { if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { //swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing } } - if (supportDetails.caps.currentExtent.width != std::numeric_limits::max()) { - swapchain->extent = supportDetails.caps.currentExtent; + if (caps.currentExtent.width != std::numeric_limits::max()) { + swapchain->extent = caps.currentExtent; } else { // if fb size isn't already found, get it from SDL @@ -288,15 +322,15 @@ namespace engine { swapchain->extent.width = std::clamp( swapchain->extent.width, - supportDetails.caps.minImageExtent.width, supportDetails.caps.maxImageExtent.width); + caps.minImageExtent.width, caps.maxImageExtent.width); swapchain->extent.height = std::clamp( swapchain->extent.height, - supportDetails.caps.minImageExtent.height, supportDetails.caps.maxImageExtent.height); + caps.minImageExtent.height, caps.maxImageExtent.height); } - uint32_t imageCount = supportDetails.caps.minImageCount + 1; - if (supportDetails.caps.maxImageCount > 0 && imageCount > supportDetails.caps.maxImageCount) { - imageCount = supportDetails.caps.maxImageCount; + uint32_t imageCount = caps.minImageCount + 1; + if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) { + imageCount = caps.maxImageCount; } VkSwapchainCreateInfoKHR createInfo{ @@ -313,7 +347,7 @@ namespace engine { .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, - .preTransform = supportDetails.caps.currentTransform, + .preTransform = caps.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = swapchain->presentMode, .clipped = VK_TRUE, @@ -348,7 +382,7 @@ namespace engine { assert(res == VK_SUCCESS); // create the render pass - { + if (swapchain->renderpass == VK_NULL_HANDLE) { VkAttachmentDescription colorAttachment{}; colorAttachment.format = swapchain->surfaceFormat.format; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; @@ -394,6 +428,7 @@ namespace engine { } // create image views and framebuffers + swapchain->imageViews.resize(swapchain->images.size()); swapchain->framebuffers.resize(swapchain->images.size()); for (int i = 0; i < swapchain->images.size(); i++) { @@ -429,9 +464,6 @@ namespace engine { framebufferInfo.height = swapchain->extent.height; framebufferInfo.layers = 1; - if (swapchain->framebuffers[i] != VK_NULL_HANDLE) { - vkDestroyFramebuffer(device, swapchain->framebuffers[i], nullptr); - } res = vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchain->framebuffers[i]); assert(res == VK_SUCCESS); @@ -474,9 +506,10 @@ namespace engine { VkInstance instance = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; + + SDL_Window* window = nullptr; VkSurfaceKHR surface = VK_NULL_HANDLE; - SwapchainSupportDetails swapchainSupportDetails{}; // available capabilities, formats, and present modes VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; @@ -502,6 +535,8 @@ namespace engine { VkResult res; + pimpl->window = window; + // initialise vulkan res = volkInitialize(); @@ -665,36 +700,6 @@ namespace engine { - // get swapchain support details: - - // get surface capabilities - res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, pimpl->surface, &pimpl->swapchainSupportDetails.caps); - assert(res == VK_SUCCESS); - - // check there is at least one supported surface format - uint32_t surfaceFormatCount = 0; - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, nullptr); - assert(res == VK_SUCCESS); - if (surfaceFormatCount == 0) { - continue; - } - pimpl->swapchainSupportDetails.formats.resize(surfaceFormatCount); - res = vkGetPhysicalDeviceSurfaceFormatsKHR(dev, pimpl->surface, &surfaceFormatCount, pimpl->swapchainSupportDetails.formats.data()); - assert(res == VK_SUCCESS); - - // check there is at least one supported present mode - uint32_t surfacePresentModeCount = 0; - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, nullptr); - assert(res == VK_SUCCESS); - if (surfacePresentModeCount == 0) { - continue; - } - pimpl->swapchainSupportDetails.presentModes.resize(surfacePresentModeCount); - res = vkGetPhysicalDeviceSurfacePresentModesKHR(dev, pimpl->surface, &surfacePresentModeCount, pimpl->swapchainSupportDetails.presentModes.data()); - assert(res == VK_SUCCESS); - - - // check physical device properties VkPhysicalDeviceProperties devProps; vkGetPhysicalDeviceProperties(dev, &devProps); @@ -717,33 +722,6 @@ namespace engine { vkGetPhysicalDeviceProperties(pimpl->physicalDevice, &devProps); INFO("Selected physical device: {}", devProps.deviceName); - TRACE("Supported present modes:"); - for (const auto& presMode : pimpl->swapchainSupportDetails.presentModes) { - switch (presMode) { - case VK_PRESENT_MODE_IMMEDIATE_KHR: - TRACE("\tVK_PRESENT_MODE_IMMEDIATE_KHR"); - break; - case VK_PRESENT_MODE_MAILBOX_KHR: - TRACE("\tVK_PRESENT_MODE_MAILBOX_KHR"); - break; - case VK_PRESENT_MODE_FIFO_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_KHR"); - break; - case VK_PRESENT_MODE_FIFO_RELAXED_KHR: - TRACE("\tVK_PRESENT_MODE_FIFO_RELAXED_KHR"); - break; - case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR"); - break; - case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: - TRACE("\tVK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR"); - break; - default: - TRACE("\tUNKNOWN DISPLAY MODE"); - break; - } - } - // Get the queue families and find ones that support graphics, transfer, and compute @@ -923,7 +901,7 @@ namespace engine { // Now make the swapchain - createSwapchain(pimpl->device, pimpl->queues, window, pimpl->surface, pimpl->swapchainSupportDetails, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, window, pimpl->surface, &pimpl->swapchain); @@ -977,9 +955,18 @@ namespace engine { assert(res == VK_SUCCESS); uint32_t imageIndex; - vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphore, VK_NULL_HANDLE, &imageIndex); + res = vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphore, VK_NULL_HANDLE, &imageIndex); + if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { + // recreate swapchain + waitIdle(); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); + } + else { + assert(res == VK_SUCCESS); + } - vkResetCommandBuffer(pimpl->commandBuffer, 0); + res = vkResetCommandBuffer(pimpl->commandBuffer, 0); + assert(res == VK_SUCCESS); // now record command buffer { @@ -1049,7 +1036,14 @@ namespace engine { presentInfo.pResults = nullptr; res = vkQueuePresentKHR(pimpl->gfxQueue.handle, &presentInfo); - assert(res == VK_SUCCESS); + if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { + // recreate swapchain + waitIdle(); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); + } + else { + assert(res == VK_SUCCESS); + } } void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) diff --git a/src/resources/shader.cpp b/src/resources/shader.cpp index 85dec72..993badf 100644 --- a/src/resources/shader.cpp +++ b/src/resources/shader.cpp @@ -30,7 +30,7 @@ static GLuint compile(const char *path, GLenum type) // compile shader GLuint handle = glCreateShader(type); - GLint size = src->size(); + GLint size = (GLint)src->size(); GLchar *data = src->data(); glShaderSource(handle, 1, &data, &size); glCompileShader(handle); diff --git a/src/resources/texture.cpp b/src/resources/texture.cpp index 9bc9119..fab9af1 100644 --- a/src/resources/texture.cpp +++ b/src/resources/texture.cpp @@ -62,7 +62,7 @@ static bool readGLRaw(const std::string& path, std::vector& texbuf, int uint64_t end = ftell(fp); texbuf.resize(end); - fseek(fp, tex_data_offset, SEEK_SET); + fseek(fp, (long)tex_data_offset, SEEK_SET); fread(texbuf.data(), 1, end, fp); fclose(fp); From f697d377910dee089c0a4656c141780920b4d0ee Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 21 Oct 2022 14:17:28 +0100 Subject: [PATCH 35/50] Fix resize error --- src/gfx_device_vulkan.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 7a9b7df..dccc244 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -954,15 +954,16 @@ namespace engine { res = vkResetFences(pimpl->device, 1, &pimpl->inFlightFence); assert(res == VK_SUCCESS); - uint32_t imageIndex; + uint32_t imageIndex = 0; res = vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphore, VK_NULL_HANDLE, &imageIndex); - if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { + if (res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); + return; } else { - assert(res == VK_SUCCESS); + assert(res == VK_SUCCESS || res == VK_SUBOPTIMAL_KHR); } res = vkResetCommandBuffer(pimpl->commandBuffer, 0); From eb1884ee8effc8003daea47cb9bff6ed5564886f Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Fri, 21 Oct 2022 17:03:36 +0100 Subject: [PATCH 36/50] Add buffer code, doesn't work --- include/gfx.hpp | 12 ------------ include/gfx_device.hpp | 3 ++- src/engine.cpp | 9 ++++++++- src/gfx_device_vulkan.cpp | 41 +++++++++++++++++++++++++++++++-------- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index cee3b3e..b5f2f83 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -10,23 +10,11 @@ namespace engine { FRAGMENT, }; - struct Shader { - ShaderType type; - uint32_t handle; - }; - - typedef uint32_t Program; - enum class BufferType { VERTEX, INDEX, }; - struct Buffer { - BufferType type; - uint32_t handle; - }; - enum class Primitive { POINTS, LINES, diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 9f1c021..f43c550 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -49,7 +49,8 @@ namespace engine { void createPipeline(const char* vertShaderPath, const char* fragShaderPath); // creates a vertex array for holding mesh data - bool createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out); + gfx::BufferHandle* createVertexBuffer(const gfx::BufferDesc& desc, const void* vertices, const void* indices); + void destroyBuffer(const gfx::BufferHandle* buffer); // wait until all the active GPU queues have finished working void waitIdle(); diff --git a/src/engine.cpp b/src/engine.cpp index c1d9c0f..5d69831 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -3,6 +3,7 @@ #include "window.hpp" #include "gfx_device.hpp" #include "resource_manager.hpp" +#include "log.hpp" namespace engine { @@ -14,11 +15,17 @@ namespace engine { engine::ResourceManager resMan{}; m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str()); + + gfx::BufferDesc bufferDesc { + .size = 65536, + }; + gfx::BufferHandle* buffer = m_gfx->createBuffer(bufferDesc, nullptr); + + m_gfx->destroyBuffer(buffer); } Application::~Application() { - } void Application::gameLoop() diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index dccc244..0379543 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -56,6 +56,8 @@ namespace engine { std::vector imageViews{}; std::vector framebuffers{}; + VkQueue activeQueue{}; + VkRenderPass renderpass; VkSemaphore acquireSemaphore = VK_NULL_HANDLE; // waits until the image is available @@ -67,17 +69,19 @@ namespace engine { VkPipeline handle = VK_NULL_HANDLE; }; - struct AllocatedBuffer { - VkBuffer buffer = VK_NULL_HANDLE; - VmaAllocation allocation = nullptr; - }; - enum class QueueFlags : uint32_t { GRAPHICS = (1 << 0), TRANSFER = (1 << 1), COMPUTE = (1 << 2), }; + // handles + + struct gfx::BufferHandle { + VkBuffer buffer = VK_NULL_HANDLE; + VmaAllocation allocation = nullptr; + }; + // functions @@ -516,6 +520,7 @@ namespace engine { std::vector queues{}; Queue gfxQueue; + Queue presentQueue; VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE; @@ -825,6 +830,8 @@ namespace engine { vkGetDeviceQueue(pimpl->device, q.familyIndex, q.queueIndex, &q.handle); } + pimpl->presentQueue = getQueueSupporting(pimpl->queues, QueueFlags::TRANSFER); + pimpl->gfxQueue = getQueueSupporting(pimpl->queues, QueueFlags::GRAPHICS); VkCommandPoolCreateInfo gfxCmdPoolInfo{ @@ -1036,7 +1043,7 @@ namespace engine { presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = nullptr; - res = vkQueuePresentKHR(pimpl->gfxQueue.handle, &presentInfo); + res = vkQueuePresentKHR(pimpl->presentQueue.handle, &presentInfo); if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); @@ -1198,9 +1205,27 @@ namespace engine { } - bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + gfx::BufferHandle* GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data) { - return false; + auto out = new gfx::BufferHandle{}; + + VkBufferCreateInfo createInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + createInfo.size = desc.size; + createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + + VkResult res = vmaCreateBuffer(pimpl->allocator, &createInfo, &allocInfo, &out->buffer, &out->allocation, nullptr); + assert(res == VK_SUCCESS); + + return out; + } + + void GFXDevice::destroyBuffer(const gfx::BufferHandle* buffer) + { + vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation); + delete buffer; } void GFXDevice::waitIdle() From b6a7634cc565abbfa7befe2bdd04e021f057afc0 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 22 Oct 2022 13:15:25 +0100 Subject: [PATCH 37/50] Get vertex buffers working --- include/gfx.hpp | 29 +++++++++--- include/gfx_device.hpp | 36 ++++----------- src/engine.cpp | 47 +++++++++++++++++--- src/gfx_device_opengl45.cpp | 2 +- src/gfx_device_vulkan.cpp | 89 ++++++++++++++++++++++++++++++++----- 5 files changed, 151 insertions(+), 52 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index b5f2f83..c275dc1 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -2,8 +2,9 @@ #include #include +#include -namespace engine { +namespace engine::gfx { enum class ShaderType { VERTEX, @@ -23,10 +24,28 @@ namespace engine { TRIANGLE_STRIP, }; - enum class IndexBufferFormat { - UNSIGNED_8_BITS, - UNSIGNED_16_BITS, - UNSIGNED_32_BITS, + enum class VertexAttribFormat { + VEC2, + VEC3, }; + struct VertexBufferDesc { + uint64_t size; + }; + + struct VertexAttribDescription { + uint32_t location; + VertexAttribFormat format; + uint32_t offset; + }; + + struct VertexFormat { + uint32_t stride; + std::vector attributeDescriptions; + }; + + // handles (incomplete types) + + struct VertexBuffer; + } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index f43c550..a3646da 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -2,36 +2,13 @@ #include "engine_api.h" +#include "gfx.hpp" + #include struct SDL_Window; namespace engine { - - namespace gfx { - - enum class BufferUsage { - DEFAULT, - UPLOAD, - READBACK, - }; - - enum class BindFlag { - NONE = 0, - UNIFORM_BUFFER = 1 << 0, - }; - - struct BufferDesc { - uint64_t size; - BufferUsage usage; - BindFlag bindFlags; - }; - - // handles (incomplete types) - - class BufferHandle; - - }; class ENGINE_API GFXDevice { @@ -42,15 +19,18 @@ namespace engine { GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); + // adds a vertex buffer draw call to the queue + void drawBuffer(const gfx::VertexBuffer* vb); + // Call once per frame. Executes all queued draw calls and renders to the screen. void draw(); // creates the equivalent of an OpenGL shader program & vertex attrib configuration - void createPipeline(const char* vertShaderPath, const char* fragShaderPath); + void createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); // creates a vertex array for holding mesh data - gfx::BufferHandle* createVertexBuffer(const gfx::BufferDesc& desc, const void* vertices, const void* indices); - void destroyBuffer(const gfx::BufferHandle* buffer); + gfx::VertexBuffer* createVertexBuffer(uint32_t size, const void* vertices, const void* indices); + void destroyVertexBuffer(const gfx::VertexBuffer* buffer); // wait until all the active GPU queues have finished working void waitIdle(); diff --git a/src/engine.cpp b/src/engine.cpp index 5d69831..ad31eb8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -5,6 +5,11 @@ #include "resource_manager.hpp" #include "log.hpp" +#include + +static engine::gfx::VertexBuffer* buffer; +static engine::gfx::VertexBuffer* buffer2; + namespace engine { Application::Application(const char* appName, const char* appVersion) @@ -14,22 +19,48 @@ namespace engine { engine::ResourceManager resMan{}; - m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str()); - - gfx::BufferDesc bufferDesc { - .size = 65536, + struct Vertex { + glm::vec2 pos; + glm::vec3 col; }; - gfx::BufferHandle* buffer = m_gfx->createBuffer(bufferDesc, nullptr); - m_gfx->destroyBuffer(buffer); + gfx::VertexFormat vertFormat{ + .stride = (uint32_t)sizeof(Vertex), + }; + + vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); + vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); + + m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); + + + + const std::vector vertices = { + { { 0.0f, -0.5f}, {1.0f, 0.0f, 0.0f} }, + { { 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f} }, + { {-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f} } + }; + buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), nullptr); + + const std::vector vertices2 = { + { { 0.9f, -0.9f}, {1.0f, 0.0f, 0.0f} }, + { { 0.9f, -0.8f}, {1.0f, 0.0f, 0.0f} }, + { { 0.8f, -0.9f}, {1.0f, 0.0f, 0.0f} } + }; + buffer2 = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices2.size(), vertices2.data(), nullptr); + } Application::~Application() { + m_gfx->destroyVertexBuffer(buffer); + m_gfx->destroyVertexBuffer(buffer2); } void Application::gameLoop() { + TRACE("Begin game loop..."); + uint64_t lastTick = m_win->getNanos(); constexpr int TICKFREQ = 1; // in hz @@ -54,6 +85,10 @@ namespace engine { } /* draw */ + + m_gfx->drawBuffer(buffer); + m_gfx->drawBuffer(buffer2); + m_gfx->draw(); /* poll events */ diff --git a/src/gfx_device_opengl45.cpp b/src/gfx_device_opengl45.cpp index 9b42ec9..929ff25 100644 --- a/src/gfx_device_opengl45.cpp +++ b/src/gfx_device_opengl45.cpp @@ -1,4 +1,4 @@ -// The implementation of the graphics layer using Vulkan 1.3. +// The implementation of the graphics layer using OpenGL 4.5 // This uses SDL specific code #ifdef ENGINE_BUILD_OPENGL diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 0379543..447f101 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace engine { @@ -77,13 +78,29 @@ namespace engine { // handles - struct gfx::BufferHandle { + struct gfx::VertexBuffer { VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; + uint32_t size; }; + // enum converters + + namespace vkinternal { + + static VkFormat getVertexAttribFormat(gfx::VertexAttribFormat fmt) + { + switch (fmt) { + case gfx::VertexAttribFormat::VEC2: + return VK_FORMAT_R32G32_SFLOAT; + case gfx::VertexAttribFormat::VEC3: + return VK_FORMAT_R32G32B32_SFLOAT; + } + } + } + // functions static std::vector readFile(const std::string& filename) @@ -532,6 +549,8 @@ namespace engine { Pipeline pipeline{}; + std::queue drawQueue{}; + }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) @@ -952,6 +971,11 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } + void GFXDevice::drawBuffer(const gfx::VertexBuffer* vb) + { + pimpl->drawQueue.push(vb); + } + void GFXDevice::draw() { VkResult res; @@ -995,7 +1019,6 @@ namespace engine { renderPassInfo.pClearValues = &clearColor; vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); VkViewport viewport{}; viewport.x = 0.0f; @@ -1011,7 +1034,20 @@ namespace engine { scissor.extent = pimpl->swapchain.extent; vkCmdSetScissor(pimpl->commandBuffer, 0, 1, &scissor); - vkCmdDraw(pimpl->commandBuffer, 3, 1, 0, 0); + // run queued draw calls + + vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); + VkDeviceSize offsets[] = { 0 }; + while (pimpl->drawQueue.empty() == false) { + + const auto* buffer = pimpl->drawQueue.front(); + + vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &buffer->buffer, offsets); + vkCmdDraw(pimpl->commandBuffer, buffer->size, 1, 0, 0); + + pimpl->drawQueue.pop(); + + } vkCmdEndRenderPass(pimpl->commandBuffer); @@ -1054,11 +1090,28 @@ namespace engine { } } - void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) { VkResult res; + // get vertex attrib layout: + VkVertexInputBindingDescription bindingDescription{ }; + bindingDescription.binding = 0; + bindingDescription.stride = vertexFormat.stride; + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::vector attribDescs{}; + attribDescs.reserve(vertexFormat.attributeDescriptions.size()); + for (const auto& desc : vertexFormat.attributeDescriptions) { + VkVertexInputAttributeDescription vulkanAttribDesc{}; + vulkanAttribDesc.binding = 0; + vulkanAttribDesc.location = desc.location; + vulkanAttribDesc.offset = desc.offset; + vulkanAttribDesc.format = vkinternal::getVertexAttribFormat(desc.format); + attribDescs.push_back(vulkanAttribDesc); + } + auto vertShaderCode = readFile(vertShaderPath); auto fragShaderCode = readFile(fragShaderPath); INFO("Opened shader: {}", std::filesystem::path(vertShaderPath).filename().string()); @@ -1080,12 +1133,13 @@ namespace engine { VkPipelineShaderStageCreateInfo shaderStages[2] = { vertShaderStageInfo, fragShaderStageInfo }; + // this sets "vertex attribute pointers" VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = attribDescs.size(); + vertexInputInfo.pVertexAttributeDescriptions = attribDescs.data(); VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -1205,24 +1259,35 @@ namespace engine { } - gfx::BufferHandle* GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data) + gfx::VertexBuffer* GFXDevice::createVertexBuffer(uint32_t size, const void* vertices, const void* indices) { - auto out = new gfx::BufferHandle{}; + auto out = new gfx::VertexBuffer{}; + out->size = size; VkBufferCreateInfo createInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - createInfo.size = desc.size; + createInfo.size = out->size; createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.flags = 0; VmaAllocationCreateInfo allocInfo{}; allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; VkResult res = vmaCreateBuffer(pimpl->allocator, &createInfo, &allocInfo, &out->buffer, &out->allocation, nullptr); assert(res == VK_SUCCESS); + void* data; + res = vmaMapMemory(pimpl->allocator, out->allocation, &data); + assert(res == VK_SUCCESS); + memcpy(data, vertices, out->size); + vmaUnmapMemory(pimpl->allocator, out->allocation); + return out; } - void GFXDevice::destroyBuffer(const gfx::BufferHandle* buffer) + void GFXDevice::destroyVertexBuffer(const gfx::VertexBuffer* buffer) { vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation); delete buffer; From 2042a9a3b31d53b70dd13eab7b3956c55723f161 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 22 Oct 2022 13:19:47 +0100 Subject: [PATCH 38/50] fix bug --- src/gfx_device_vulkan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 447f101..a011b75 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -98,6 +98,7 @@ namespace engine { case gfx::VertexAttribFormat::VEC3: return VK_FORMAT_R32G32B32_SFLOAT; } + throw std::runtime_error("Unknown vertex attribute format"); } } From 14e9833a931400feaa62ed449c710aca78464e15 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 23 Oct 2022 12:05:09 +0100 Subject: [PATCH 39/50] Make changes --- src/engine.cpp | 13 +++-- src/gfx_device_vulkan.cpp | 111 ++++++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index ad31eb8..41b61a7 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,7 +8,6 @@ #include static engine::gfx::VertexBuffer* buffer; -static engine::gfx::VertexBuffer* buffer2; namespace engine { @@ -40,21 +39,22 @@ namespace engine { { { 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f} }, { {-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f} } }; - buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), nullptr); + const std::vector indices{ + 0, 1, 2, + }; + buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), indices.data()); const std::vector vertices2 = { { { 0.9f, -0.9f}, {1.0f, 0.0f, 0.0f} }, { { 0.9f, -0.8f}, {1.0f, 0.0f, 0.0f} }, { { 0.8f, -0.9f}, {1.0f, 0.0f, 0.0f} } }; - buffer2 = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices2.size(), vertices2.data(), nullptr); } Application::~Application() { m_gfx->destroyVertexBuffer(buffer); - m_gfx->destroyVertexBuffer(buffer2); } void Application::gameLoop() @@ -73,8 +73,8 @@ namespace engine { lastTick = m_win->getLastFrameStamp(); // do tick stuff here - m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms, " + std::to_string(m_win->getFPS()) + " fps"); - + m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms, " + std::to_string(m_win->getAvgFPS()) + " fps"); + m_win->resetAvgFPS(); } if (m_win->getKeyPress(inputs::Key::F11)) { @@ -87,7 +87,6 @@ namespace engine { /* draw */ m_gfx->drawBuffer(buffer); - m_gfx->drawBuffer(buffer2); m_gfx->draw(); diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index a011b75..e95a795 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -327,7 +327,7 @@ namespace engine { for (const auto& presMode : presentModes) { if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { - //swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing + swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing } } @@ -520,6 +520,53 @@ namespace engine { return shaderModule; } + static void copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) + { + VkResult res; + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + res = vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); + assert(res == VK_SUCCESS); + + { // record the command buffer + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + res = vkBeginCommandBuffer(commandBuffer, &beginInfo); + assert(res == VK_SUCCESS); + + VkBufferCopy copyRegion{}; + copyRegion.srcOffset = 0; + copyRegion.dstOffset = 0; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + res = vkEndCommandBuffer(commandBuffer); + assert(res == VK_SUCCESS); + } + + // submit + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + assert(res == VK_SUCCESS); + + res = vkQueueWaitIdle(queue); + assert(res == VK_SUCCESS); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + + } + // class definitions @@ -1262,28 +1309,58 @@ namespace engine { gfx::VertexBuffer* GFXDevice::createVertexBuffer(uint32_t size, const void* vertices, const void* indices) { + VkResult res; + auto out = new gfx::VertexBuffer{}; out->size = size; - VkBufferCreateInfo createInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - createInfo.size = out->size; - createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.flags = 0; + VkBuffer stagingBuffer; + VmaAllocation stagingAllocation; - VmaAllocationCreateInfo allocInfo{}; - allocInfo.usage = VMA_MEMORY_USAGE_AUTO; - allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + // first create the staging buffer + { + VkBufferCreateInfo stagingBufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + stagingBufferInfo.size = out->size; + stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + stagingBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + stagingBufferInfo.flags = 0; - VkResult res = vmaCreateBuffer(pimpl->allocator, &createInfo, &allocInfo, &out->buffer, &out->allocation, nullptr); - assert(res == VK_SUCCESS); + VmaAllocationCreateInfo stagingAllocInfo{}; + stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; + stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + stagingAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - void* data; - res = vmaMapMemory(pimpl->allocator, out->allocation, &data); - assert(res == VK_SUCCESS); - memcpy(data, vertices, out->size); - vmaUnmapMemory(pimpl->allocator, out->allocation); + res = vmaCreateBuffer(pimpl->allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr); + assert(res == VK_SUCCESS); + + void* data; + res = vmaMapMemory(pimpl->allocator, stagingAllocation, &data); + assert(res == VK_SUCCESS); + memcpy(data, vertices, out->size); + vmaUnmapMemory(pimpl->allocator, stagingAllocation); + } + + // create the actual buffer on the GPU + { + VkBufferCreateInfo gpuBufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + gpuBufferInfo.size = out->size; + gpuBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + gpuBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + gpuBufferInfo.flags = 0; + + VmaAllocationCreateInfo gpuAllocationInfo{}; + gpuAllocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + gpuAllocationInfo.flags = 0; + + res = vmaCreateBuffer(pimpl->allocator, &gpuBufferInfo, &gpuAllocationInfo, &out->buffer, &out->allocation, nullptr); + assert(res == VK_SUCCESS); + } + + // copy the data from the staging buffer to the gpu buffer + copyBuffer(pimpl->device, pimpl->commandPool, pimpl->gfxQueue.handle, stagingBuffer, out->buffer, out->size); + + // destroy staging buffer + vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation); return out; } From f7a76f2e8d0b6ff13a55279ac733088e49cb7407 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 23 Oct 2022 20:26:42 +0100 Subject: [PATCH 40/50] Group sources in visual studio --- CMakeLists.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c42ec8..f066720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.8) # options @@ -6,8 +6,7 @@ project(engine LANGUAGES CXX VERSION "0.1.0" ) -add_library(${PROJECT_NAME} STATIC - +set(SRC_FILES "src/engine.cpp" "src/window.cpp" "src/input.cpp" #TODO make input_manager @@ -33,8 +32,9 @@ add_library(${PROJECT_NAME} STATIC "src/gfx_device_vulkan.cpp" "src/gfx_device_null.cpp" "src/gfx_device_opengl45.cpp" +) - # PUBLIC API +set(INCLUDE_FILES "include/engine_api.h" @@ -73,6 +73,16 @@ add_library(${PROJECT_NAME} STATIC "include/gfx_device.hpp" ) +add_library(${PROJECT_NAME} STATIC + + ${SRC_FILES} + ${INCLUDE_FILES} + +) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" PREFIX "Source" FILES ${SRC_FILES}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Include" FILES ${INCLUDE_FILES}) + # compiling options: target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS") From d6522730ae412db6e74f35260ca29a560ea9bd96 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sun, 23 Oct 2022 20:37:56 +0100 Subject: [PATCH 41/50] Initialise some member variables --- src/gfx_device_vulkan.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index e95a795..ee34ea0 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -39,9 +39,9 @@ namespace engine { struct Queue { uint32_t familyIndex; uint32_t queueIndex; - bool supportsGraphics = false; - bool supportsTransfer = false; - bool supportsCompute = false; + bool supportsGraphics; + bool supportsTransfer; + bool supportsCompute; VkQueue handle; }; @@ -584,8 +584,8 @@ namespace engine { VkDevice device = VK_NULL_HANDLE; std::vector queues{}; - Queue gfxQueue; - Queue presentQueue; + Queue gfxQueue{}; + Queue presentQueue{}; VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE; @@ -848,6 +848,9 @@ namespace engine { Queue newQueue{}; newQueue.familyIndex = family; newQueue.queueIndex = 0; + newQueue.supportsGraphics = false; + newQueue.supportsTransfer = false; + newQueue.supportsCompute = false; if (graphicsFamilyIndex == family) newQueue.supportsGraphics = true; if (transferFamilyIndex == family) newQueue.supportsTransfer = true; if (computeFamilyIndex == family) newQueue.supportsCompute = true; From a24b951df18bc3fffb8774644421042645ea2edc Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 00:19:07 +0100 Subject: [PATCH 42/50] Add support for multiple pipelines --- include/gfx.hpp | 2 +- include/gfx_device.hpp | 5 +-- src/engine.cpp | 18 +++-------- src/gfx_device_vulkan.cpp | 64 ++++++++++++++++++++++----------------- 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index c275dc1..94cbf30 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -45,7 +45,7 @@ namespace engine::gfx { }; // handles (incomplete types) - + struct Pipeline; struct VertexBuffer; } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a3646da..75833d2 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -20,13 +20,14 @@ namespace engine { ~GFXDevice(); // adds a vertex buffer draw call to the queue - void drawBuffer(const gfx::VertexBuffer* vb); + void drawBuffer(const gfx::Pipeline* pipeline, const gfx::VertexBuffer* vb); // Call once per frame. Executes all queued draw calls and renders to the screen. void draw(); // creates the equivalent of an OpenGL shader program & vertex attrib configuration - void createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); + gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); + void destroyPipeline(const gfx::Pipeline* pipeline); // creates a vertex array for holding mesh data gfx::VertexBuffer* createVertexBuffer(uint32_t size, const void* vertices, const void* indices); diff --git a/src/engine.cpp b/src/engine.cpp index 41b61a7..6fc83c2 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -7,6 +7,7 @@ #include +static engine::gfx::Pipeline* pipeline; static engine::gfx::VertexBuffer* buffer; namespace engine { @@ -17,22 +18,16 @@ namespace engine { m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); engine::ResourceManager resMan{}; - struct Vertex { glm::vec2 pos; glm::vec3 col; }; - gfx::VertexFormat vertFormat{ .stride = (uint32_t)sizeof(Vertex), }; - vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); - - m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); - - + pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); const std::vector vertices = { { { 0.0f, -0.5f}, {1.0f, 0.0f, 0.0f} }, @@ -44,17 +39,12 @@ namespace engine { }; buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), indices.data()); - const std::vector vertices2 = { - { { 0.9f, -0.9f}, {1.0f, 0.0f, 0.0f} }, - { { 0.9f, -0.8f}, {1.0f, 0.0f, 0.0f} }, - { { 0.8f, -0.9f}, {1.0f, 0.0f, 0.0f} } - }; - } Application::~Application() { m_gfx->destroyVertexBuffer(buffer); + m_gfx->destroyPipeline(pipeline); } void Application::gameLoop() @@ -86,7 +76,7 @@ namespace engine { /* draw */ - m_gfx->drawBuffer(buffer); + m_gfx->drawBuffer(pipeline, buffer); m_gfx->draw(); diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index ee34ea0..6e9d446 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace engine { @@ -65,11 +66,6 @@ namespace engine { VkSemaphore releaseSemaphore = VK_NULL_HANDLE; // waits until rendering finishes }; - struct Pipeline { - VkPipelineLayout layout = VK_NULL_HANDLE; - VkPipeline handle = VK_NULL_HANDLE; - }; - enum class QueueFlags : uint32_t { GRAPHICS = (1 << 0), TRANSFER = (1 << 1), @@ -84,6 +80,11 @@ namespace engine { uint32_t size; }; + struct gfx::Pipeline { + VkPipelineLayout layout = VK_NULL_HANDLE; + VkPipeline handle = VK_NULL_HANDLE; + }; + // enum converters @@ -595,9 +596,7 @@ namespace engine { VkFence inFlightFence = VK_NULL_HANDLE; - Pipeline pipeline{}; - - std::queue drawQueue{}; + std::map> drawQueues{}; }; @@ -995,11 +994,6 @@ namespace engine { { TRACE("Destroying GFXDevice..."); - if (pimpl->pipeline.handle != VK_NULL_HANDLE) { - vkDestroyPipeline(pimpl->device, pimpl->pipeline.handle, nullptr); - vkDestroyPipelineLayout(pimpl->device, pimpl->pipeline.layout, nullptr); - } - vkDestroyFence(pimpl->device, pimpl->inFlightFence, nullptr); vkDestroySemaphore(pimpl->device, pimpl->swapchain.releaseSemaphore, nullptr); @@ -1022,9 +1016,9 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } - void GFXDevice::drawBuffer(const gfx::VertexBuffer* vb) + void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::VertexBuffer* vb) { - pimpl->drawQueue.push(vb); + pimpl->drawQueues[pipeline].push(vb); } void GFXDevice::draw() @@ -1087,19 +1081,20 @@ namespace engine { // run queued draw calls - vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); VkDeviceSize offsets[] = { 0 }; - while (pimpl->drawQueue.empty() == false) { - - const auto* buffer = pimpl->drawQueue.front(); - - vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &buffer->buffer, offsets); - vkCmdDraw(pimpl->commandBuffer, buffer->size, 1, 0, 0); - - pimpl->drawQueue.pop(); + for (auto& [pipeline, queue] : pimpl->drawQueues) { + vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); + while (queue.empty() == false) { + const gfx::VertexBuffer* buffer = queue.front(); + vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &buffer->buffer, offsets); + vkCmdDraw(pimpl->commandBuffer, buffer->size, 1, 0, 0); + queue.pop(); + } } + pimpl->drawQueues.clear(); + vkCmdEndRenderPass(pimpl->commandBuffer); res = vkEndCommandBuffer(pimpl->commandBuffer); @@ -1141,11 +1136,13 @@ namespace engine { } } - void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) + gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) { VkResult res; + gfx::Pipeline* pipeline = new gfx::Pipeline; + // get vertex attrib layout: VkVertexInputBindingDescription bindingDescription{ }; bindingDescription.binding = 0; @@ -1281,7 +1278,7 @@ namespace engine { layoutInfo.pushConstantRangeCount = 0; layoutInfo.pPushConstantRanges = nullptr; - res = vkCreatePipelineLayout(pimpl->device, &layoutInfo, nullptr, &pimpl->pipeline.layout); + res = vkCreatePipelineLayout(pimpl->device, &layoutInfo, nullptr, &pipeline->layout); assert(res == VK_SUCCESS); VkGraphicsPipelineCreateInfo createInfo{}; @@ -1296,18 +1293,29 @@ namespace engine { createInfo.pDepthStencilState = nullptr; createInfo.pColorBlendState = &colorBlending; createInfo.pDynamicState = &dynamicState; - createInfo.layout = pimpl->pipeline.layout; + createInfo.layout = pipeline->layout; createInfo.renderPass = pimpl->swapchain.renderpass; createInfo.subpass = 0; createInfo.basePipelineHandle = VK_NULL_HANDLE; createInfo.basePipelineIndex = -1; - res = vkCreateGraphicsPipelines(pimpl->device, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pimpl->pipeline.handle); + res = vkCreateGraphicsPipelines(pimpl->device, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline->handle); assert(res == VK_SUCCESS); vkDestroyShaderModule(pimpl->device, fragShaderModule, nullptr); vkDestroyShaderModule(pimpl->device, vertShaderModule, nullptr); + return pipeline; + + } + + void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline) + { + + vkDestroyPipeline(pimpl->device, pipeline->handle, nullptr); + vkDestroyPipelineLayout(pimpl->device, pipeline->layout, nullptr); + + delete pipeline; } gfx::VertexBuffer* GFXDevice::createVertexBuffer(uint32_t size, const void* vertices, const void* indices) From ce12534ad80309e3a829d6ad3cebdc6124cc4111 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 01:10:48 +0100 Subject: [PATCH 43/50] Add indexed buffer support --- include/gfx.hpp | 2 +- include/gfx_device.hpp | 11 ++--- src/engine.cpp | 22 +++++----- src/gfx_device_opengl45.cpp | 36 +++++++++++++---- src/gfx_device_vulkan.cpp | 81 +++++++++++++++++++++++++++++-------- 5 files changed, 114 insertions(+), 38 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index 94cbf30..eb7f2c1 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -46,6 +46,6 @@ namespace engine::gfx { // handles (incomplete types) struct Pipeline; - struct VertexBuffer; + struct Buffer; } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 75833d2..3b61743 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -20,18 +20,19 @@ namespace engine { ~GFXDevice(); // adds a vertex buffer draw call to the queue - void drawBuffer(const gfx::Pipeline* pipeline, const gfx::VertexBuffer* vb); + void drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count); + + void drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount); // Call once per frame. Executes all queued draw calls and renders to the screen. - void draw(); + void renderFrame(); // creates the equivalent of an OpenGL shader program & vertex attrib configuration gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); void destroyPipeline(const gfx::Pipeline* pipeline); - // creates a vertex array for holding mesh data - gfx::VertexBuffer* createVertexBuffer(uint32_t size, const void* vertices, const void* indices); - void destroyVertexBuffer(const gfx::VertexBuffer* buffer); + gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); + void destroyBuffer(const gfx::Buffer* buffer); // wait until all the active GPU queues have finished working void waitIdle(); diff --git a/src/engine.cpp b/src/engine.cpp index 6fc83c2..47797a7 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -8,7 +8,8 @@ #include static engine::gfx::Pipeline* pipeline; -static engine::gfx::VertexBuffer* buffer; +static engine::gfx::Buffer* vb; +static engine::gfx::Buffer* ib; namespace engine { @@ -30,20 +31,23 @@ namespace engine { pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); const std::vector vertices = { - { { 0.0f, -0.5f}, {1.0f, 0.0f, 0.0f} }, + { { 0.5f, -0.5f}, {1.0f, 0.0f, 0.0f} }, { { 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f} }, - { {-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f} } + { {-0.5f, -0.5f}, {0.0f, 0.0f, 1.0f} }, + { {-0.5f, 0.5f}, {0.0f, 1.0f, 1.0f} }, }; - const std::vector indices{ - 0, 1, 2, + vb = m_gfx->createBuffer(gfx::BufferType::VERTEX, sizeof(Vertex) * vertices.size(), vertices.data()); + const std::vector indices = { + 0, 1, 2, 2, 1, 3, }; - buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), indices.data()); + ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint16_t) * indices.size(), indices.data()); } Application::~Application() { - m_gfx->destroyVertexBuffer(buffer); + m_gfx->destroyBuffer(vb); + m_gfx->destroyBuffer(ib); m_gfx->destroyPipeline(pipeline); } @@ -76,9 +80,9 @@ namespace engine { /* draw */ - m_gfx->drawBuffer(pipeline, buffer); + m_gfx->drawIndexed(pipeline, vb, ib, 6); - m_gfx->draw(); + m_gfx->renderFrame(); /* poll events */ m_win->getInputAndEvents(); diff --git a/src/gfx_device_opengl45.cpp b/src/gfx_device_opengl45.cpp index 929ff25..8648681 100644 --- a/src/gfx_device_opengl45.cpp +++ b/src/gfx_device_opengl45.cpp @@ -57,19 +57,41 @@ namespace engine { GFXDevice::~GFXDevice() { TRACE("Destroying GFXDevice..."); + + SDL_GL_DeleteContext(pimpl->context); } - void GFXDevice::draw() - { - } - - void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count) { } - bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount) { - return false; + } + + void GFXDevice::renderFrame() + { + + } + + gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) + { + return nullptr; + } + + void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline) + { + + } + + gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data) + { + return nullptr; + } + + void GFXDevice::destroyBuffer(const gfx::Buffer* buffer) + { + } void GFXDevice::waitIdle() diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 6e9d446..c20fbfe 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -66,6 +66,12 @@ namespace engine { VkSemaphore releaseSemaphore = VK_NULL_HANDLE; // waits until rendering finishes }; + struct DrawCall { + const gfx::Buffer* vertexBuffer = nullptr; + const gfx::Buffer* indexBuffer = nullptr; // if this is nullptr, don't use indexed + uint32_t count = 0; + }; + enum class QueueFlags : uint32_t { GRAPHICS = (1 << 0), TRANSFER = (1 << 1), @@ -74,10 +80,11 @@ namespace engine { // handles - struct gfx::VertexBuffer { + struct gfx::Buffer { + gfx::BufferType type; VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; - uint32_t size; + VkDeviceSize size = 0; }; struct gfx::Pipeline { @@ -101,6 +108,18 @@ namespace engine { } throw std::runtime_error("Unknown vertex attribute format"); } + + static VkBufferUsageFlagBits getBufferUsageFlag(gfx::BufferType type) + { + switch (type) { + case gfx::BufferType::VERTEX: + return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case gfx::BufferType::INDEX: + return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + } + throw std::runtime_error("Unknown buffer type"); + } + } // functions @@ -596,7 +615,7 @@ namespace engine { VkFence inFlightFence = VK_NULL_HANDLE; - std::map> drawQueues{}; + std::map> drawQueues{}; }; @@ -1016,12 +1035,34 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } - void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::VertexBuffer* vb) + void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count) { - pimpl->drawQueues[pipeline].push(vb); + assert(vertexBuffer->type == gfx::BufferType::VERTEX); + + DrawCall call{ + .vertexBuffer = vertexBuffer, + .indexBuffer = nullptr, // don't use indexed drawing + .count = count, + }; + + pimpl->drawQueues[pipeline].push(call); } - void GFXDevice::draw() + void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count) + { + assert(vertexBuffer->type == gfx::BufferType::VERTEX); + assert(indexBuffer->type == gfx::BufferType::INDEX); + + DrawCall call{ + .vertexBuffer = vertexBuffer, + .indexBuffer = indexBuffer, // don't use indexed drawing + .count = count, + }; + + pimpl->drawQueues[pipeline].push(call); + } + + void GFXDevice::renderFrame() { VkResult res; @@ -1086,9 +1127,15 @@ namespace engine { for (auto& [pipeline, queue] : pimpl->drawQueues) { vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); while (queue.empty() == false) { - const gfx::VertexBuffer* buffer = queue.front(); - vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &buffer->buffer, offsets); - vkCmdDraw(pimpl->commandBuffer, buffer->size, 1, 0, 0); + DrawCall call = queue.front(); + vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &call.vertexBuffer->buffer, offsets); + if (call.indexBuffer == nullptr) { + // do a simple draw call + vkCmdDraw(pimpl->commandBuffer, call.count, 1, 0, 0); + } else { + vkCmdBindIndexBuffer(pimpl->commandBuffer, call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(pimpl->commandBuffer, call.count, 1, 0, 0, 0); + } queue.pop(); } } @@ -1318,13 +1365,15 @@ namespace engine { delete pipeline; } - gfx::VertexBuffer* GFXDevice::createVertexBuffer(uint32_t size, const void* vertices, const void* indices) + gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data) { VkResult res; - auto out = new gfx::VertexBuffer{}; + auto out = new gfx::Buffer{}; out->size = size; + out->type = type; + VkBuffer stagingBuffer; VmaAllocation stagingAllocation; @@ -1344,10 +1393,10 @@ namespace engine { res = vmaCreateBuffer(pimpl->allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr); assert(res == VK_SUCCESS); - void* data; - res = vmaMapMemory(pimpl->allocator, stagingAllocation, &data); + void* dataDest; + res = vmaMapMemory(pimpl->allocator, stagingAllocation, &dataDest); assert(res == VK_SUCCESS); - memcpy(data, vertices, out->size); + memcpy(dataDest, data, out->size); vmaUnmapMemory(pimpl->allocator, stagingAllocation); } @@ -1355,7 +1404,7 @@ namespace engine { { VkBufferCreateInfo gpuBufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; gpuBufferInfo.size = out->size; - gpuBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + gpuBufferInfo.usage = vkinternal::getBufferUsageFlag(type) | VK_BUFFER_USAGE_TRANSFER_DST_BIT; gpuBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; gpuBufferInfo.flags = 0; @@ -1376,7 +1425,7 @@ namespace engine { return out; } - void GFXDevice::destroyVertexBuffer(const gfx::VertexBuffer* buffer) + void GFXDevice::destroyBuffer(const gfx::Buffer* buffer) { vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation); delete buffer; From 6542b7039b7121c51ed74bfc5985427b79f4c2ae Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 01:14:08 +0100 Subject: [PATCH 44/50] Add null device --- src/gfx_device_null.cpp | 51 +++++++++++++++++++++++++++++-------- src/gfx_device_opengl45.cpp | 1 + src/gfx_device_vulkan.cpp | 1 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/gfx_device_null.cpp b/src/gfx_device_null.cpp index 61110a3..6874ecd 100644 --- a/src/gfx_device_null.cpp +++ b/src/gfx_device_null.cpp @@ -1,6 +1,6 @@ -// The implementation of the graphics layer using Vulkan 1.3. -// This uses SDL specific code +// This implementation of the graphics layer does nothing +//#define ENGINE_BUILD_NULLGFX #ifdef ENGINE_BUILD_NULLGFX #include "gfx_device.hpp" @@ -8,12 +8,23 @@ #include "config.h" #include "log.hpp" +#include + +#include +#include +#include +#include +#include +#include + namespace engine { + // structures and enums + // class definitions struct GFXDevice::Impl { - + }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) @@ -26,17 +37,37 @@ namespace engine { TRACE("Destroying GFXDevice..."); } - void GFXDevice::draw() - { - } - - void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count) { } - bool GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data, gfx::BufferHandle* out) + void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount) { - return true; + } + + void GFXDevice::renderFrame() + { + + } + + gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) + { + return nullptr; + } + + void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline) + { + + } + + gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data) + { + return nullptr; + } + + void GFXDevice::destroyBuffer(const gfx::Buffer* buffer) + { + } void GFXDevice::waitIdle() diff --git a/src/gfx_device_opengl45.cpp b/src/gfx_device_opengl45.cpp index 8648681..dfd89c2 100644 --- a/src/gfx_device_opengl45.cpp +++ b/src/gfx_device_opengl45.cpp @@ -1,6 +1,7 @@ // The implementation of the graphics layer using OpenGL 4.5 // This uses SDL specific code +//#undef ENGINE_BUILD_OPENGL #ifdef ENGINE_BUILD_OPENGL #include "gfx_device.hpp" diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index c20fbfe..19c51cb 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1,6 +1,7 @@ // The implementation of the graphics layer using Vulkan 1.3. // This uses SDL specific code +//#undef ENGINE_BUILD_VULKAN #ifdef ENGINE_BUILD_VULKAN #include "gfx_device.hpp" From e811349e5b9bddb4d687b1926df7d1ab6039632f Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 09:34:09 +0100 Subject: [PATCH 45/50] Make index buffers use 32 bit ints --- include/components/text_ui_renderer.hpp | 1 + include/resources/mesh.hpp | 32 +++++------ src/components/mesh_renderer.cpp | 6 +-- src/components/text_ui_renderer.cpp | 5 +- src/engine.cpp | 4 +- src/gfx_device_vulkan.cpp | 2 +- src/resources/mesh.cpp | 71 ++++++------------------- 7 files changed, 38 insertions(+), 83 deletions(-) diff --git a/include/components/text_ui_renderer.hpp b/include/components/text_ui_renderer.hpp index 333649b..420f9d2 100644 --- a/include/components/text_ui_renderer.hpp +++ b/include/components/text_ui_renderer.hpp @@ -6,6 +6,7 @@ #include "resources/font.hpp" #include "resources/mesh.hpp" +#include "resources/shader.hpp" #include diff --git a/include/resources/mesh.hpp b/include/resources/mesh.hpp index b270cb0..fc48973 100644 --- a/include/resources/mesh.hpp +++ b/include/resources/mesh.hpp @@ -4,9 +4,7 @@ #include "resource.hpp" -#include "resources/shader.hpp" - -#include +#include "gfx.hpp" #include #include @@ -20,34 +18,30 @@ struct Vertex { glm::vec2 uv; }; +namespace engine { + class GFXDevice; +} + namespace engine::resources { class ENGINE_API Mesh : public Resource { public: - Mesh(const std::vector& vertices); - Mesh(const std::vector& vertices, const std::vector& indices); - Mesh(const std::filesystem::path& resPath); + Mesh(GFXDevice* gfx, const std::vector& vertices); + Mesh(GFXDevice* gfx, const std::vector& vertices, const std::vector& indices); + Mesh(GFXDevice* gfx, const std::filesystem::path& resPath); ~Mesh() override; - void drawMesh(const Shader& shader); - - static void invalidate() - { - s_active_vao = -1; - } + void drawMesh(const gfx::Pipeline* pipeline); std::vector m_vertices; - std::vector m_indices; + std::vector m_indices; private: - static int s_active_vao; + const gfx::Buffer* vb; + const gfx::Buffer* ib; - GLuint m_vao; - GLuint m_vbo; - GLuint m_ebo; - - void bindVAO() const; + GFXDevice* gfx; void initMesh(); diff --git a/src/components/mesh_renderer.cpp b/src/components/mesh_renderer.cpp index 8c98701..b0c061b 100644 --- a/src/components/mesh_renderer.cpp +++ b/src/components/mesh_renderer.cpp @@ -33,13 +33,13 @@ void Renderer::render(glm::mat4 transform) m_shader->setUniform_v3("baseColor", m_color ); m_shader->setUniform_v3("emission", m_emission ); - if (m_mesh) - m_mesh->drawMesh(*m_shader); +// if (m_mesh) +// m_mesh->drawMesh(*m_shader); } void Renderer::setMesh(const std::string& name) { - m_mesh = parent.res.get(name); +// m_mesh = parent.res.get(name); } void Renderer::setTexture(const std::string& name) diff --git a/src/components/text_ui_renderer.cpp b/src/components/text_ui_renderer.cpp index a7011bb..2d9effe 100644 --- a/src/components/text_ui_renderer.cpp +++ b/src/components/text_ui_renderer.cpp @@ -57,6 +57,7 @@ void UI::render(glm::mat4 transform) float w = glyph.size.x * scale; float h = glyph.size.y * scale; + /* resources::Mesh mesh({ {{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}}, {{xpos, ypos , 0.0f}, {}, {0.0f, 1.0f}}, @@ -64,11 +65,11 @@ void UI::render(glm::mat4 transform) {{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}}, {{xpos + w, ypos, 0.0f}, {}, {1.0f, 1.0f}}, {{xpos + w, ypos + h, 0.0f}, {}, {1.0f, 0.0f}}, - }); + });*/ glBindTexture(GL_TEXTURE_2D, glyph.textureID); - mesh.drawMesh(*m_shader); +// mesh.drawMesh(*m_shader); x += (glyph.advance >> 6) * scale; diff --git a/src/engine.cpp b/src/engine.cpp index 47797a7..197d8f5 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -37,10 +37,10 @@ namespace engine { { {-0.5f, 0.5f}, {0.0f, 1.0f, 1.0f} }, }; vb = m_gfx->createBuffer(gfx::BufferType::VERTEX, sizeof(Vertex) * vertices.size(), vertices.data()); - const std::vector indices = { + const std::vector indices = { 0, 1, 2, 2, 1, 3, }; - ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint16_t) * indices.size(), indices.data()); + ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint32_t) * indices.size(), indices.data()); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 19c51cb..167f136 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1134,7 +1134,7 @@ namespace engine { // do a simple draw call vkCmdDraw(pimpl->commandBuffer, call.count, 1, 0, 0); } else { - vkCmdBindIndexBuffer(pimpl->commandBuffer, call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdBindIndexBuffer(pimpl->commandBuffer, call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(pimpl->commandBuffer, call.count, 1, 0, 0, 0); } queue.pop(); diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index b6092f3..9633493 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -1,5 +1,7 @@ #include "resources/mesh.hpp" +#include "gfx_device.hpp" + namespace engine::resources { struct MeshFileHeader { @@ -8,7 +10,7 @@ struct MeshFileHeader { int material; }; -static void loadMeshFromFile(const std::filesystem::path& path, std::vector* vertices, std::vector* indices) +static void loadMeshFromFile(const std::filesystem::path& path, std::vector* vertices, std::vector* indices) { // TODO @@ -26,73 +28,34 @@ static void loadMeshFromFile(const std::filesystem::path& path, std::vectorresize(header.index_count); vertices->resize(header.vertex_count); - fread(&(*indices)[0], sizeof(unsigned int) * header.index_count, 1, fp); - fread(&((*vertices)[0].pos[0]), sizeof(float) * 8 * header.vertex_count, 1, fp); - + fread(indices->data(), sizeof(unsigned int) * header.index_count, 1, fp); + fread(vertices->data(), sizeof(float) * 8 * header.vertex_count, 1, fp); fclose(fp); } -static void loadObjFromFile(const std::filesystem::path& path, std::vector* vertices, std::vector* indices) -{ - -} - -// -1 means invalidated -int Mesh::s_active_vao = -1; - -void Mesh::bindVAO() const -{ - if (s_active_vao != m_vao) { - glBindVertexArray(m_vao); - s_active_vao = m_vao; - } -} - void Mesh::initMesh() { - glGenVertexArrays(1, &m_vao); - bindVAO(); - glGenBuffers(1, &m_vbo); - glGenBuffers(1, &m_ebo); - - glBindBuffer(GL_ARRAY_BUFFER, m_vbo); - glBufferData(GL_ARRAY_BUFFER, m_vertices.size()*sizeof(Vertex), &m_vertices[0], GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), &(m_indices[0]), GL_STATIC_DRAW); - - // position - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, pos)); - // normal - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, norm)); - // uv - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, uv)); - + vb = gfx->createBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data()); + ib = gfx->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data()); } -void Mesh::drawMesh(const Shader& shader) +void Mesh::drawMesh(const gfx::Pipeline* pipeline) { - bindVAO(); - shader.makeActive(); -#ifndef SDLTEST_NOGFX - glDrawElements(GL_TRIANGLES, static_cast(m_indices.size()), GL_UNSIGNED_INT, 0); -#endif + gfx->drawIndexed(pipeline, vb, ib, m_indices.size()); } -Mesh::Mesh(const std::vector& vertices) : Resource("", "mesh") +Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices) : Resource("", "mesh"), gfx(gfx) { // constructor for custom meshes without an index array m_vertices = vertices; // COPY over vertices - for (int i = 0; i < m_vertices.size(); i++) { + for (uint32_t i = 0; i < m_vertices.size(); i++) { m_indices.push_back(i); } initMesh(); } -Mesh::Mesh(const std::vector& vertices, const std::vector& indices) : Resource("", "mesh") +Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices, const std::vector& indices) : Resource("", "mesh"), gfx(gfx) { m_vertices = vertices; // COPY over vertices m_indices = indices; // COPY over indices; @@ -100,7 +63,7 @@ Mesh::Mesh(const std::vector& vertices, const std::vector& } // To be used with the resource manager -Mesh::Mesh(const std::filesystem::path& resPath) : Resource(resPath, "mesh") +Mesh::Mesh(GFXDevice* gfx, const std::filesystem::path& resPath) : Resource(resPath, "mesh"), gfx(gfx) { loadMeshFromFile(resPath, &m_vertices, &m_indices); initMesh(); @@ -108,12 +71,8 @@ Mesh::Mesh(const std::filesystem::path& resPath) : Resource(resPath, "mesh") Mesh::~Mesh() { - glDeleteVertexArrays(1, &m_vao); - glDeleteBuffers(1, &m_vbo); - glDeleteBuffers(1, &m_ebo); - if (s_active_vao == m_vao) { - s_active_vao = -1; - } + gfx->destroyBuffer(ib); + gfx->destroyBuffer(vb); } } From 46895c0d4ba4df3e90713b7aff8847dfd7d03991 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 14:30:05 +0100 Subject: [PATCH 46/50] Support multiple frames rendering at once --- include/gfx.hpp | 1 + include/gfx_device.hpp | 2 +- src/engine.cpp | 14 +++- src/gfx_device_vulkan.cpp | 150 +++++++++++++++++++++++++++----------- 4 files changed, 121 insertions(+), 46 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index eb7f2c1..028db74 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -14,6 +14,7 @@ namespace engine::gfx { enum class BufferType { VERTEX, INDEX, + UNIFORM, }; enum class Primitive { diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index 3b61743..c184583 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -28,7 +28,7 @@ namespace engine { void renderFrame(); // creates the equivalent of an OpenGL shader program & vertex attrib configuration - gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); + gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize); void destroyPipeline(const gfx::Pipeline* pipeline); gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); diff --git a/src/engine.cpp b/src/engine.cpp index 197d8f5..cf6c14d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -10,6 +10,7 @@ static engine::gfx::Pipeline* pipeline; static engine::gfx::Buffer* vb; static engine::gfx::Buffer* ib; +static engine::gfx::Buffer* ub; namespace engine { @@ -28,7 +29,12 @@ namespace engine { }; vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); - pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); + struct UBO { + glm::mat4 model{}; + glm::mat4 view{}; + glm::mat4 proj{}; + }; + pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat, sizeof(UBO)); const std::vector vertices = { { { 0.5f, -0.5f}, {1.0f, 0.0f, 0.0f} }, @@ -42,12 +48,16 @@ namespace engine { }; ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint32_t) * indices.size(), indices.data()); + UBO initialUbo{}; +// ub = m_gfx->createBuffer(gfx::BufferType::UNIFORM, sizeof(UBO), &initialUbo); + } Application::~Application() { - m_gfx->destroyBuffer(vb); +// m_gfx->destroyBuffer(ub); m_gfx->destroyBuffer(ib); + m_gfx->destroyBuffer(vb); m_gfx->destroyPipeline(pipeline); } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 167f136..2b9f138 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -31,6 +31,8 @@ namespace engine { + static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! + // structures and enums struct LayerInfo { @@ -63,8 +65,8 @@ namespace engine { VkRenderPass renderpass; - VkSemaphore acquireSemaphore = VK_NULL_HANDLE; // waits until the image is available - VkSemaphore releaseSemaphore = VK_NULL_HANDLE; // waits until rendering finishes + std::array acquireSemaphores{}; // waits until the image is available + std::array releaseSemaphores{}; // waits until rendering finishes }; struct DrawCall { @@ -91,6 +93,7 @@ namespace engine { struct gfx::Pipeline { VkPipelineLayout layout = VK_NULL_HANDLE; VkPipeline handle = VK_NULL_HANDLE; + std::vector uniformBuffers{}; }; @@ -515,13 +518,17 @@ namespace engine { // create the swapchain semaphores VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - if (swapchain->acquireSemaphore == VK_NULL_HANDLE) { - res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &swapchain->acquireSemaphore); - assert(res == VK_SUCCESS); + for (auto& acquireSemaphore : swapchain->acquireSemaphores) { + if (acquireSemaphore == VK_NULL_HANDLE) { + res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &acquireSemaphore); + assert(res == VK_SUCCESS); + } } - if (swapchain->releaseSemaphore == VK_NULL_HANDLE) { - res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &swapchain->releaseSemaphore); - assert(res == VK_SUCCESS); + for (auto& releaseSemaphore : swapchain->releaseSemaphores) { + if (releaseSemaphore == VK_NULL_HANDLE) { + res = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &releaseSemaphore); + assert(res == VK_SUCCESS); + } } } @@ -588,8 +595,6 @@ namespace engine { } - - // class definitions struct GFXDevice::Impl { @@ -608,16 +613,21 @@ namespace engine { Queue gfxQueue{}; Queue presentQueue{}; VkCommandPool commandPool = VK_NULL_HANDLE; - VkCommandBuffer commandBuffer = VK_NULL_HANDLE; VmaAllocator allocator = nullptr; Swapchain swapchain{}; - VkFence inFlightFence = VK_NULL_HANDLE; + uint64_t FRAMECOUNT = 0; + + std::array commandBuffers{}; + std::array inFlightFences{}; std::map> drawQueues{}; + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + VkDescriptorSetLayout uboLayout{}; + }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) @@ -939,8 +949,10 @@ namespace engine { .commandBufferCount = 1 }; - res = vkAllocateCommandBuffers(pimpl->device, &gfxCmdBufInfo, &pimpl->commandBuffer); - assert(res == VK_SUCCESS); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + res = vkAllocateCommandBuffers(pimpl->device, &gfxCmdBufInfo, &pimpl->commandBuffers[i]); + assert(res == VK_SUCCESS); + } } @@ -1001,12 +1013,28 @@ namespace engine { - // for testing purposes, create a pipeline with a simple shader VkFenceCreateInfo fenceInfo{}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; fenceInfo.pNext = nullptr; - res = vkCreateFence(pimpl->device, &fenceInfo, nullptr, &pimpl->inFlightFence); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + res = vkCreateFence(pimpl->device, &fenceInfo, nullptr, &pimpl->inFlightFences[i]); + assert(res == VK_SUCCESS); + } + + // create uniform buffer stuff + pimpl->uboLayoutBinding.binding = 0; + pimpl->uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pimpl->uboLayoutBinding.descriptorCount = 1; + pimpl->uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; + pimpl->uboLayoutBinding.pImmutableSamplers = nullptr; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = 1; + descriptorSetLayoutInfo.pBindings = &pimpl->uboLayoutBinding; + res = vkCreateDescriptorSetLayout(pimpl->device, &descriptorSetLayoutInfo, nullptr, &pimpl->uboLayout); + assert(res == VK_SUCCESS); + + } @@ -1014,10 +1042,16 @@ namespace engine { { TRACE("Destroying GFXDevice..."); - vkDestroyFence(pimpl->device, pimpl->inFlightFence, nullptr); + vkDestroyDescriptorSetLayout(pimpl->device, pimpl->uboLayout, nullptr); - vkDestroySemaphore(pimpl->device, pimpl->swapchain.releaseSemaphore, nullptr); - vkDestroySemaphore(pimpl->device, pimpl->swapchain.acquireSemaphore, nullptr); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + + vkDestroyFence(pimpl->device, pimpl->inFlightFences[i], nullptr); + + vkDestroySemaphore(pimpl->device, pimpl->swapchain.releaseSemaphores[i], nullptr); + vkDestroySemaphore(pimpl->device, pimpl->swapchain.acquireSemaphores[i], nullptr); + + } for (VkImageView view : pimpl->swapchain.imageViews) { vkDestroyImageView(pimpl->device, view, nullptr); } @@ -1067,13 +1101,15 @@ namespace engine { { VkResult res; - res = vkWaitForFences(pimpl->device, 1, &pimpl->inFlightFence, VK_TRUE, UINT64_MAX); + const uint32_t frameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT; + + res = vkWaitForFences(pimpl->device, 1, &pimpl->inFlightFences[frameIndex], VK_TRUE, UINT64_MAX); assert(res == VK_SUCCESS); - res = vkResetFences(pimpl->device, 1, &pimpl->inFlightFence); + res = vkResetFences(pimpl->device, 1, &pimpl->inFlightFences[frameIndex]); assert(res == VK_SUCCESS); uint32_t imageIndex = 0; - res = vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphore, VK_NULL_HANDLE, &imageIndex); + res = vkAcquireNextImageKHR(pimpl->device, pimpl->swapchain.swapchain, UINT64_MAX, pimpl->swapchain.acquireSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex); if (res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); @@ -1084,7 +1120,7 @@ namespace engine { assert(res == VK_SUCCESS || res == VK_SUBOPTIMAL_KHR); } - res = vkResetCommandBuffer(pimpl->commandBuffer, 0); + res = vkResetCommandBuffer(pimpl->commandBuffers[frameIndex], 0); assert(res == VK_SUCCESS); // now record command buffer @@ -1092,7 +1128,7 @@ namespace engine { VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; beginInfo.flags = 0; beginInfo.pInheritanceInfo = nullptr; - res = vkBeginCommandBuffer(pimpl->commandBuffer, &beginInfo); + res = vkBeginCommandBuffer(pimpl->commandBuffers[frameIndex], &beginInfo); assert(res == VK_SUCCESS); VkRenderPassBeginInfo renderPassInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; @@ -1105,7 +1141,7 @@ namespace engine { renderPassInfo.clearValueCount = 1; renderPassInfo.pClearValues = &clearColor; - vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(pimpl->commandBuffers[frameIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport{}; viewport.x = 0.0f; @@ -1114,28 +1150,28 @@ namespace engine { viewport.height = (float)pimpl->swapchain.extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; - vkCmdSetViewport(pimpl->commandBuffer, 0, 1, &viewport); + vkCmdSetViewport(pimpl->commandBuffers[frameIndex], 0, 1, &viewport); VkRect2D scissor{}; scissor.offset = { 0, 0 }; scissor.extent = pimpl->swapchain.extent; - vkCmdSetScissor(pimpl->commandBuffer, 0, 1, &scissor); + vkCmdSetScissor(pimpl->commandBuffers[frameIndex], 0, 1, &scissor); // run queued draw calls VkDeviceSize offsets[] = { 0 }; for (auto& [pipeline, queue] : pimpl->drawQueues) { - vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); + vkCmdBindPipeline(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); while (queue.empty() == false) { DrawCall call = queue.front(); - vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &call.vertexBuffer->buffer, offsets); + vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); if (call.indexBuffer == nullptr) { // do a simple draw call - vkCmdDraw(pimpl->commandBuffer, call.count, 1, 0, 0); + vkCmdDraw(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0); } else { - vkCmdBindIndexBuffer(pimpl->commandBuffer, call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(pimpl->commandBuffer, call.count, 1, 0, 0, 0); + vkCmdBindIndexBuffer(pimpl->commandBuffers[frameIndex], call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0, 0); } queue.pop(); } @@ -1143,9 +1179,9 @@ namespace engine { pimpl->drawQueues.clear(); - vkCmdEndRenderPass(pimpl->commandBuffer); + vkCmdEndRenderPass(pimpl->commandBuffers[frameIndex]); - res = vkEndCommandBuffer(pimpl->commandBuffer); + res = vkEndCommandBuffer(pimpl->commandBuffers[frameIndex]); assert(res == VK_SUCCESS); } @@ -1153,19 +1189,19 @@ namespace engine { VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &pimpl->swapchain.acquireSemaphore; + submitInfo.pWaitSemaphores = &pimpl->swapchain.acquireSemaphores[frameIndex]; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &pimpl->commandBuffer; + submitInfo.pCommandBuffers = &pimpl->commandBuffers[frameIndex]; submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &pimpl->swapchain.releaseSemaphore; + submitInfo.pSignalSemaphores = &pimpl->swapchain.releaseSemaphores[frameIndex]; - res = vkQueueSubmit(pimpl->gfxQueue.handle, 1, &submitInfo, pimpl->inFlightFence); + res = vkQueueSubmit(pimpl->gfxQueue.handle, 1, &submitInfo, pimpl->inFlightFences[frameIndex]); assert(res == VK_SUCCESS); VkPresentInfoKHR presentInfo{ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &pimpl->swapchain.releaseSemaphore; + presentInfo.pWaitSemaphores = &pimpl->swapchain.releaseSemaphores[frameIndex]; VkSwapchainKHR swapchains[] = { pimpl->swapchain.swapchain }; presentInfo.swapchainCount = 1; @@ -1182,15 +1218,40 @@ namespace engine { else { assert(res == VK_SUCCESS); } + + pimpl->FRAMECOUNT++; } - gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) + gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize) { VkResult res; gfx::Pipeline* pipeline = new gfx::Pipeline; + // create uniform buffers + pipeline->uniformBuffers.resize(FRAMES_IN_FLIGHT); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + auto buf = new gfx::Buffer{}; + buf->size = uniformBufferSize; + buf->type = gfx::BufferType::UNIFORM; + + VkBufferCreateInfo bufferInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferInfo.size = buf->size; + bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; // prefer CPU memory for uniforms + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + res = vmaCreateBuffer(pimpl->allocator, &bufferInfo, &allocInfo, &buf->buffer, &buf->allocation, nullptr); + assert(res == VK_SUCCESS); + + pipeline->uniformBuffers[i] = buf; + } + // get vertex attrib layout: VkVertexInputBindingDescription bindingDescription{ }; bindingDescription.binding = 0; @@ -1320,9 +1381,8 @@ namespace engine { VkPipelineLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - // everything is 0 because we're not using uniforms - layoutInfo.setLayoutCount = 0; - layoutInfo.pSetLayouts = nullptr; + layoutInfo.setLayoutCount = 1; + layoutInfo.pSetLayouts = &pimpl->uboLayout; layoutInfo.pushConstantRangeCount = 0; layoutInfo.pPushConstantRanges = nullptr; @@ -1363,6 +1423,10 @@ namespace engine { vkDestroyPipeline(pimpl->device, pipeline->handle, nullptr); vkDestroyPipelineLayout(pimpl->device, pipeline->layout, nullptr); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + destroyBuffer(pipeline->uniformBuffers[i]); + } + delete pipeline; } From df02820c7dc7fbb36f0342fb757507d3b42a74b9 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 24 Oct 2022 15:16:04 +0100 Subject: [PATCH 47/50] do stuff --- include/gfx_device.hpp | 7 ++-- src/engine.cpp | 25 +++++++------ src/gfx_device_vulkan.cpp | 75 ++++++++++++++++++++++++++++++++------- src/resources/mesh.cpp | 2 +- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index c184583..cdb4bd6 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -19,10 +19,9 @@ namespace engine { GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); - // adds a vertex buffer draw call to the queue - void drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count); - - void drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount); + // adds a draw call to the queue + // vertexBuffer is required. indexBuffer and uniformData can be NULL + void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData); // Call once per frame. Executes all queued draw calls and renders to the screen. void renderFrame(); diff --git a/src/engine.cpp b/src/engine.cpp index cf6c14d..ce9763b 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -6,6 +6,13 @@ #include "log.hpp" #include +#include + +struct UBO { + glm::mat4 model{}; + glm::mat4 view{}; + glm::mat4 proj{}; +}; static engine::gfx::Pipeline* pipeline; static engine::gfx::Buffer* vb; @@ -29,11 +36,7 @@ namespace engine { }; vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); - struct UBO { - glm::mat4 model{}; - glm::mat4 view{}; - glm::mat4 proj{}; - }; + pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat, sizeof(UBO)); const std::vector vertices = { @@ -48,9 +51,6 @@ namespace engine { }; ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint32_t) * indices.size(), indices.data()); - UBO initialUbo{}; -// ub = m_gfx->createBuffer(gfx::BufferType::UNIFORM, sizeof(UBO), &initialUbo); - } Application::~Application() @@ -89,8 +89,13 @@ namespace engine { } /* draw */ - - m_gfx->drawIndexed(pipeline, vb, ib, 6); + UBO initialUbo{ + .model = glm::mat4{1.0f}, + .view = glm::mat4{1.0f}, + .proj = glm::mat4{1.0f} + }; + initialUbo.model = glm::scale(glm::mat4{ 1.0f }, glm::vec3{ 0.5f, 0.5f, 1.0f }); + m_gfx->draw(pipeline, vb, ib, 6, &initialUbo); m_gfx->renderFrame(); diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 2b9f138..bdfd434 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -94,6 +94,8 @@ namespace engine { VkPipelineLayout layout = VK_NULL_HANDLE; VkPipeline handle = VK_NULL_HANDLE; std::vector uniformBuffers{}; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + std::array descriptorSets{}; }; @@ -1070,29 +1072,32 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } - void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count) + void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData) { assert(vertexBuffer->type == gfx::BufferType::VERTEX); + assert(vertexBuffer != nullptr); + assert(indexBuffer == nullptr || indexBuffer->type == gfx::BufferType::INDEX); + + VkResult res; DrawCall call{ .vertexBuffer = vertexBuffer, - .indexBuffer = nullptr, // don't use indexed drawing + .indexBuffer = indexBuffer, // will be ignored if nullptr .count = count, }; - pimpl->drawQueues[pipeline].push(call); - } + if (uniformData != nullptr) { + uint32_t frameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT; - void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count) - { - assert(vertexBuffer->type == gfx::BufferType::VERTEX); - assert(indexBuffer->type == gfx::BufferType::INDEX); + void* dest; + res = vmaMapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation, &dest); + assert(res == VK_SUCCESS); - DrawCall call{ - .vertexBuffer = vertexBuffer, - .indexBuffer = indexBuffer, // don't use indexed drawing - .count = count, - }; + memcpy(dest, uniformData, pipeline->uniformBuffers[frameIndex]->size); + + vmaUnmapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation); + + } pimpl->drawQueues[pipeline].push(call); } @@ -1163,9 +1168,11 @@ namespace engine { for (auto& [pipeline, queue] : pimpl->drawQueues) { vkCmdBindPipeline(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); + vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &pipeline->descriptorSets[frameIndex], 0, nullptr); while (queue.empty() == false) { DrawCall call = queue.front(); vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); + if (call.indexBuffer == nullptr) { // do a simple draw call vkCmdDraw(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0); @@ -1252,6 +1259,46 @@ namespace engine { pipeline->uniformBuffers[i] = buf; } + // create descriptor pool for uniform buffers + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = FRAMES_IN_FLIGHT; + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = FRAMES_IN_FLIGHT; + res = vkCreateDescriptorPool(pimpl->device, &poolInfo, nullptr, &pipeline->descriptorPool); + assert(res == VK_SUCCESS); + + std::array layouts; + layouts.fill(pimpl->uboLayout); + VkDescriptorSetAllocateInfo dSetAllocInfo{}; + dSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + dSetAllocInfo.descriptorPool = pipeline->descriptorPool; + dSetAllocInfo.descriptorSetCount = FRAMES_IN_FLIGHT; + dSetAllocInfo.pSetLayouts = layouts.data(); + res = vkAllocateDescriptorSets(pimpl->device, &dSetAllocInfo, pipeline->descriptorSets.data()); + + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = pipeline->uniformBuffers[i]->buffer; + bufferInfo.offset = 0; + bufferInfo.range = uniformBufferSize; + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = pipeline->descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; + descriptorWrite.pTexelBufferView = nullptr; + + vkUpdateDescriptorSets(pimpl->device, 1, &descriptorWrite, 0, nullptr); + } + // get vertex attrib layout: VkVertexInputBindingDescription bindingDescription{ }; bindingDescription.binding = 0; @@ -1423,6 +1470,8 @@ namespace engine { vkDestroyPipeline(pimpl->device, pipeline->handle, nullptr); vkDestroyPipelineLayout(pimpl->device, pipeline->layout, nullptr); + vkDestroyDescriptorPool(pimpl->device, pipeline->descriptorPool, nullptr); + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { destroyBuffer(pipeline->uniformBuffers[i]); } diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index 9633493..81e96f5 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -42,7 +42,7 @@ void Mesh::initMesh() void Mesh::drawMesh(const gfx::Pipeline* pipeline) { - gfx->drawIndexed(pipeline, vb, ib, m_indices.size()); + gfx->draw(pipeline, vb, ib, m_indices.size(), nullptr); } Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices) : Resource("", "mesh"), gfx(gfx) From d04e4a168a9783bf18f45c9a2d663162aab9e1cd Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 27 Oct 2022 17:58:30 +0100 Subject: [PATCH 48/50] Do a ton of stuff. can't remember what --- include/engine.hpp | 7 +++- include/gfx_device.hpp | 4 ++- include/object.hpp | 12 +++---- include/resources/mesh.hpp | 15 +++----- include/resources/shader.hpp | 2 -- include/sceneroot.hpp | 1 - src/engine.cpp | 68 ++++++++++++------------------------ src/gfx_device_vulkan.cpp | 39 +++++++++++++-------- src/resources/mesh.cpp | 19 ++++------ src/sceneroot.cpp | 6 ---- 10 files changed, 74 insertions(+), 99 deletions(-) diff --git a/include/engine.hpp b/include/engine.hpp index 56b3db5..5f6818a 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -5,7 +5,10 @@ namespace engine { class Window; + class Input; class GFXDevice; + class ResourceManager; + class SceneRoot; class Application { @@ -21,7 +24,9 @@ namespace engine { private: std::unique_ptr m_win; - std::unique_ptr m_gfx; + std::unique_ptr m_input; + std::unique_ptr m_res; + std::unique_ptr m_scene; }; } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index cdb4bd6..a0b325d 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -20,7 +20,7 @@ namespace engine { ~GFXDevice(); // adds a draw call to the queue - // vertexBuffer is required. indexBuffer and uniformData can be NULL + // vertexBuffer is required, indexBuffer can be NULL, uniformData is required void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData); // Call once per frame. Executes all queued draw calls and renders to the screen. @@ -42,4 +42,6 @@ namespace engine { }; + extern GFXDevice* gfxdev; + } diff --git a/include/object.hpp b/include/object.hpp index 06ecc0a..b47dfae 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -15,7 +15,6 @@ namespace engine { class Window; class Input; - class ResourceManager; class SceneRoot; @@ -30,9 +29,9 @@ namespace engine { } struct GameIO { - engine::Window* const win; - engine::Input* const input; - ResourceManager* const resMan; + Window* win; + Input* input; + ResourceManager* resMan; }; // This object lives until it is deleted by its parent(s) or finally when the "Scene" is destroyed. @@ -45,10 +44,11 @@ namespace engine { Object& operator=(const Object&) = delete; ~Object(); - engine::Window& win; - engine::Input& inp; + Window& win; + Input& inp; ResourceManager& res; + SceneRoot& root; std::string getName(); diff --git a/include/resources/mesh.hpp b/include/resources/mesh.hpp index fc48973..6c9a384 100644 --- a/include/resources/mesh.hpp +++ b/include/resources/mesh.hpp @@ -18,30 +18,23 @@ struct Vertex { glm::vec2 uv; }; -namespace engine { - class GFXDevice; -} - namespace engine::resources { class ENGINE_API Mesh : public Resource { public: - Mesh(GFXDevice* gfx, const std::vector& vertices); - Mesh(GFXDevice* gfx, const std::vector& vertices, const std::vector& indices); - Mesh(GFXDevice* gfx, const std::filesystem::path& resPath); + Mesh(const std::vector& vertices); + Mesh(const std::vector& vertices, const std::vector& indices); + Mesh(const std::filesystem::path& resPath); ~Mesh() override; - void drawMesh(const gfx::Pipeline* pipeline); - std::vector m_vertices; std::vector m_indices; -private: const gfx::Buffer* vb; const gfx::Buffer* ib; - GFXDevice* gfx; +private: void initMesh(); diff --git a/include/resources/shader.hpp b/include/resources/shader.hpp index 839e00b..511ac7a 100644 --- a/include/resources/shader.hpp +++ b/include/resources/shader.hpp @@ -4,8 +4,6 @@ #include "resource.hpp" -#include - #include #include diff --git a/include/sceneroot.hpp b/include/sceneroot.hpp index a3bc312..9850523 100644 --- a/include/sceneroot.hpp +++ b/include/sceneroot.hpp @@ -14,7 +14,6 @@ namespace engine { public: // create a new empty scene SceneRoot(struct GameIO things); - SceneRoot(const std::filesystem::path& file, struct GameIO things); SceneRoot(const SceneRoot&) = delete; SceneRoot& operator=(const SceneRoot&) = delete; diff --git a/src/engine.cpp b/src/engine.cpp index ce9763b..49710d7 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,12 +1,15 @@ #include "engine.hpp" -#include "window.hpp" -#include "gfx_device.hpp" -#include "resource_manager.hpp" #include "log.hpp" -#include -#include +#include "window.hpp" +#include "input.hpp" +#include "resource_manager.hpp" +#include "sceneroot.hpp" + +#include "gfx_device.hpp" + +#include "resources/mesh.hpp" struct UBO { glm::mat4 model{}; @@ -17,48 +20,27 @@ struct UBO { static engine::gfx::Pipeline* pipeline; static engine::gfx::Buffer* vb; static engine::gfx::Buffer* ib; -static engine::gfx::Buffer* ub; namespace engine { Application::Application(const char* appName, const char* appVersion) { m_win = std::make_unique(appName, true); - m_gfx = std::make_unique(appName, appVersion, m_win->getHandle()); - engine::ResourceManager resMan{}; - struct Vertex { - glm::vec2 pos; - glm::vec3 col; - }; - gfx::VertexFormat vertFormat{ - .stride = (uint32_t)sizeof(Vertex), - }; - vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); - vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); - - pipeline = m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat, sizeof(UBO)); - - const std::vector vertices = { - { { 0.5f, -0.5f}, {1.0f, 0.0f, 0.0f} }, - { { 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f} }, - { {-0.5f, -0.5f}, {0.0f, 0.0f, 1.0f} }, - { {-0.5f, 0.5f}, {0.0f, 1.0f, 1.0f} }, - }; - vb = m_gfx->createBuffer(gfx::BufferType::VERTEX, sizeof(Vertex) * vertices.size(), vertices.data()); - const std::vector indices = { - 0, 1, 2, 2, 1, 3, - }; - ib = m_gfx->createBuffer(gfx::BufferType::INDEX, sizeof(uint32_t) * indices.size(), indices.data()); + gfxdev = new GFXDevice(appName, appVersion, m_win->getHandle()); + m_input = std::make_unique(*m_win); + m_res = std::make_unique(); + GameIO things{}; + things.win = m_win.get(); + things.input = m_input.get(); + things.resMan = m_res.get(); + m_scene = std::make_unique(things); } Application::~Application() { -// m_gfx->destroyBuffer(ub); - m_gfx->destroyBuffer(ib); - m_gfx->destroyBuffer(vb); - m_gfx->destroyPipeline(pipeline); + delete gfxdev; } void Application::gameLoop() @@ -68,6 +50,8 @@ namespace engine { uint64_t lastTick = m_win->getNanos(); constexpr int TICKFREQ = 1; // in hz + auto myMesh = m_res->get("meshes/monke.mesh"); + // single-threaded game loop while (m_win->isRunning()) { @@ -88,23 +72,17 @@ namespace engine { m_win->setCloseFlag(); } - /* draw */ - UBO initialUbo{ - .model = glm::mat4{1.0f}, - .view = glm::mat4{1.0f}, - .proj = glm::mat4{1.0f} - }; - initialUbo.model = glm::scale(glm::mat4{ 1.0f }, glm::vec3{ 0.5f, 0.5f, 1.0f }); - m_gfx->draw(pipeline, vb, ib, 6, &initialUbo); + m_scene->updateStuff(); - m_gfx->renderFrame(); + /* draw */ + gfxdev->renderFrame(); /* poll events */ m_win->getInputAndEvents(); } - m_gfx->waitIdle(); + gfxdev->waitIdle(); } } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index bdfd434..5384917 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -31,7 +31,12 @@ namespace engine { - static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! + // EXTERNED GLOBAL VARIABLE + GFXDevice* gfxdev = nullptr; + + static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! (on Intel IGPU) + + static constexpr size_t UNIFORM_BUFFER_MAX_SIZE = 256; // bytes // structures and enums @@ -73,6 +78,7 @@ namespace engine { const gfx::Buffer* vertexBuffer = nullptr; const gfx::Buffer* indexBuffer = nullptr; // if this is nullptr, don't use indexed uint32_t count = 0; + uint8_t uniformData[UNIFORM_BUFFER_MAX_SIZE]; }; enum class QueueFlags : uint32_t { @@ -634,6 +640,11 @@ namespace engine { GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) { + if (gfxdev != nullptr) { + throw std::runtime_error("There can only be one graphics device"); + } + gfxdev = this; + pimpl = std::make_unique(); VkResult res; @@ -1077,8 +1088,7 @@ namespace engine { assert(vertexBuffer->type == gfx::BufferType::VERTEX); assert(vertexBuffer != nullptr); assert(indexBuffer == nullptr || indexBuffer->type == gfx::BufferType::INDEX); - - VkResult res; + assert(uniformData != nullptr); DrawCall call{ .vertexBuffer = vertexBuffer, @@ -1086,18 +1096,9 @@ namespace engine { .count = count, }; - if (uniformData != nullptr) { - uint32_t frameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT; + size_t uniformDataSize = pipeline->uniformBuffers[pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT]->size; - void* dest; - res = vmaMapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation, &dest); - assert(res == VK_SUCCESS); - - memcpy(dest, uniformData, pipeline->uniformBuffers[frameIndex]->size); - - vmaUnmapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation); - - } + memcpy(call.uniformData, uniformData, uniformDataSize); pimpl->drawQueues[pipeline].push(call); } @@ -1171,6 +1172,15 @@ namespace engine { vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &pipeline->descriptorSets[frameIndex], 0, nullptr); while (queue.empty() == false) { DrawCall call = queue.front(); + + void* uniformDest; + res = vmaMapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation, &uniformDest); + assert(res == VK_SUCCESS); + + memcpy(uniformDest, call.uniformData, pipeline->uniformBuffers[frameIndex]->size); + + vmaUnmapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation); + vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); if (call.indexBuffer == nullptr) { @@ -1180,6 +1190,7 @@ namespace engine { vkCmdBindIndexBuffer(pimpl->commandBuffers[frameIndex], call.indexBuffer->buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(pimpl->commandBuffers[frameIndex], call.count, 1, 0, 0, 0); } + queue.pop(); } } diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index 81e96f5..ce8de33 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -36,16 +36,11 @@ static void loadMeshFromFile(const std::filesystem::path& path, std::vectorcreateBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data()); - ib = gfx->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data()); + vb = gfxdev->createBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data()); + ib = gfxdev->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data()); } -void Mesh::drawMesh(const gfx::Pipeline* pipeline) -{ - gfx->draw(pipeline, vb, ib, m_indices.size(), nullptr); -} - -Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices) : Resource("", "mesh"), gfx(gfx) +Mesh::Mesh(const std::vector& vertices) : Resource("", "mesh") { // constructor for custom meshes without an index array m_vertices = vertices; // COPY over vertices @@ -55,7 +50,7 @@ Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices) : Resource("", " initMesh(); } -Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices, const std::vector& indices) : Resource("", "mesh"), gfx(gfx) +Mesh::Mesh(const std::vector& vertices, const std::vector& indices) : Resource("", "mesh") { m_vertices = vertices; // COPY over vertices m_indices = indices; // COPY over indices; @@ -63,7 +58,7 @@ Mesh::Mesh(GFXDevice* gfx, const std::vector& vertices, const std::vecto } // To be used with the resource manager -Mesh::Mesh(GFXDevice* gfx, const std::filesystem::path& resPath) : Resource(resPath, "mesh"), gfx(gfx) +Mesh::Mesh(const std::filesystem::path& resPath) : Resource(resPath, "mesh") { loadMeshFromFile(resPath, &m_vertices, &m_indices); initMesh(); @@ -71,8 +66,8 @@ Mesh::Mesh(GFXDevice* gfx, const std::filesystem::path& resPath) : Resource(resP Mesh::~Mesh() { - gfx->destroyBuffer(ib); - gfx->destroyBuffer(vb); + gfxdev->destroyBuffer(ib); + gfxdev->destroyBuffer(vb); } } diff --git a/src/sceneroot.cpp b/src/sceneroot.cpp index 9d952b7..31ac13d 100644 --- a/src/sceneroot.cpp +++ b/src/sceneroot.cpp @@ -20,12 +20,6 @@ namespace engine { { } - SceneRoot::SceneRoot(const std::filesystem::path& file, struct GameIO things) : SceneRoot(things) - { - // TODO: make this a resource - //loadFromSceneFile(file); - } - SceneRoot::~SceneRoot() { } From d628414d697394f038aa0203165b7b67349dbf5c Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Thu, 27 Oct 2022 23:06:56 +0100 Subject: [PATCH 49/50] break stuff --- include/engine.hpp | 1 - include/gfx_device.hpp | 2 + include/object.hpp | 4 +- include/resources/shader.hpp | 55 ++------ src/components/camera.cpp | 30 +---- src/components/mesh_renderer.cpp | 27 ++-- src/components/text_ui_renderer.cpp | 3 + src/engine.cpp | 13 +- src/gfx_device_vulkan.cpp | 8 ++ src/resources/mesh.cpp | 8 ++ src/resources/shader.cpp | 194 ++-------------------------- 11 files changed, 65 insertions(+), 280 deletions(-) diff --git a/include/engine.hpp b/include/engine.hpp index 5f6818a..3361fd7 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -6,7 +6,6 @@ namespace engine { class Window; class Input; - class GFXDevice; class ResourceManager; class SceneRoot; diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a0b325d..a5106c2 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -19,6 +19,8 @@ namespace engine { GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); + void getViewportSize(uint32_t *w, uint32_t *h); + // adds a draw call to the queue // vertexBuffer is required, indexBuffer can be NULL, uniformData is required void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData); diff --git a/include/object.hpp b/include/object.hpp index b47dfae..86105c4 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -6,6 +6,8 @@ #include "transform.hpp" +#include "components/component.hpp" + #include #include #include @@ -141,4 +143,4 @@ namespace engine { throw std::runtime_error("deleteComponent() error: attempt to delete component that is not present."); } -} \ No newline at end of file +} diff --git a/include/resources/shader.hpp b/include/resources/shader.hpp index 511ac7a..5b88253 100644 --- a/include/resources/shader.hpp +++ b/include/resources/shader.hpp @@ -9,6 +9,10 @@ #include #include +namespace engine::gfx { + class Pipeline; +} + namespace engine::resources { class ENGINE_API Shader : public Resource { @@ -17,58 +21,17 @@ public: Shader(const std::filesystem::path& resPath); ~Shader() override; - enum class UniformType { - FLOAT_MAT4 = GL_FLOAT_MAT4, - FLOAT_VEC2 = GL_FLOAT_VEC2, - FLOAT_VEC3 = GL_FLOAT_VEC3, - SAMPLER_2D = GL_SAMPLER_2D, - NOTFOUND + struct UniformBuffer { + glm::mat4 transform; }; - - void makeActive() const; - bool setUniform_m4(const std::string& name, const glm::mat4&) const; - bool setUniform_v2(const std::string& name, const glm::vec2&) const; - bool setUniform_v3(const std::string& name, const glm::vec3&) const; - bool setUniform_i(const std::string& name, int) const; - bool setUniform_f(const std::string& name, float) const; - - UniformType getUniformType(const std::string& name) const; - int getAttribLocation(const std::string& name) const; - - static void invalidate() + gfx::Pipeline* getPipeline() { - s_activeProgram = std::numeric_limits::max(); + return m_pipeline; } private: - - struct Uniform { - GLint size; - UniformType type; - GLuint location; - }; - - struct Attribute { - GLint size; - UniformType type; - GLuint location; - }; - - // fields - - GLuint m_program; - - std::map m_uniforms{}; - std::map m_attributes{}; - - // static members - - // Only valid if glUseProgram is never called outside this class's method - static GLuint s_activeProgram; - - // -1 if not found - int getUniformLocation(const std::string& name) const; + gfx::Pipeline* m_pipeline = nullptr; }; diff --git a/src/components/camera.cpp b/src/components/camera.cpp index e7222b2..4c786f9 100644 --- a/src/components/camera.cpp +++ b/src/components/camera.cpp @@ -7,11 +7,9 @@ #include "window.hpp" -#include "log.hpp" +#include "gfx_device.hpp" -static const std::string VIEW_MAT_UNIFORM = "viewMat"; -static const std::string PROJ_MAT_UNIFORM = "projMat"; -static const std::string WINDOW_SIZE_UNIFORM = "windowSize"; +#include "log.hpp" namespace engine::components { @@ -20,11 +18,6 @@ glm::vec4 Camera::s_clearColor{-999.0f, -999.0f, -999.0f, -999.0f}; Camera::Camera(Object* parent) : Component(parent, TypeEnum::CAMERA) { parent->root.activateCam(getID()); - glEnable(GL_DEPTH_TEST); - - glDisable(GL_STENCIL_TEST); - - glEnable(GL_CULL_FACE); } Camera::~Camera() @@ -52,26 +45,17 @@ void Camera::updateCam(glm::mat4 transform) // shader ref count increased by 1, but only in this scope auto lockedPtr = resPtr.lock(); auto shader = dynamic_cast(lockedPtr.get()); - shader->setUniform_m4(VIEW_MAT_UNIFORM, viewMatrix); - shader->setUniform_m4(PROJ_MAT_UNIFORM, m_projMatrix); -// shader->setUniform_v2(WINDOW_SIZE_UNIFORM, win.getViewportSize()); - shader->setUniform_v3("lightPos", m_lightPos); + // SET VIEW TRANSFORM HERE } - if (s_clearColor != clearColor) { - s_clearColor = clearColor; - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - } - - glClear((m_noClear ? 0 : GL_COLOR_BUFFER_BIT) | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - } static glm::vec2 getViewportSize() { - GLint64 viewportParams[4]; - glGetInteger64v(GL_VIEWPORT, viewportParams); - return { viewportParams[2], viewportParams[3] }; + uint32_t width; + uint32_t height; + gfxdev->getViewportSize(&width, &height); + return {width, height}; } void Camera::usePerspective(float fovDeg) diff --git a/src/components/mesh_renderer.cpp b/src/components/mesh_renderer.cpp index b0c061b..a4e8e17 100644 --- a/src/components/mesh_renderer.cpp +++ b/src/components/mesh_renderer.cpp @@ -4,14 +4,16 @@ #include "resource_manager.hpp" +#include "gfx_device.hpp" + #include namespace engine::components { Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER) { - m_shader = this->parent.res.get("shaders/basic.glsl"); - m_texture = this->parent.res.get("textures/missing.png"); + m_shader = this->parent.res.get("shader.glsl"); +// m_texture = this->parent.res.get("textures/missing.png"); } Renderer::~Renderer() @@ -21,30 +23,21 @@ Renderer::~Renderer() void Renderer::render(glm::mat4 transform) { + resources::Shader::UniformBuffer ub { + glm::mat4{1.0f}, + }; - m_shader->setUniform_f("ambientStrength", 0.4f); - m_shader->setUniform_v3("ambientColor", { 1.0f, 1.0f, 1.0f }); - - m_shader->setUniform_v3("lightColor", { 1.0f, 1.0f, 1.0f }); - - m_shader->setUniform_m4("modelMat", transform ); - - m_texture->bindTexture(); - m_shader->setUniform_v3("baseColor", m_color ); - m_shader->setUniform_v3("emission", m_emission ); - -// if (m_mesh) -// m_mesh->drawMesh(*m_shader); + gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_indices.size(), &ub); } void Renderer::setMesh(const std::string& name) { -// m_mesh = parent.res.get(name); + m_mesh = parent.res.get(name); } void Renderer::setTexture(const std::string& name) { - m_texture = parent.res.get(name); +// m_texture = parent.res.get(name); } } diff --git a/src/components/text_ui_renderer.cpp b/src/components/text_ui_renderer.cpp index 2d9effe..2491e24 100644 --- a/src/components/text_ui_renderer.cpp +++ b/src/components/text_ui_renderer.cpp @@ -23,12 +23,15 @@ UI::~UI() void UI::render(glm::mat4 transform) { + /* glActiveTexture(GL_TEXTURE0); m_shader->setUniform_m4("modelMat", transform); m_shader->setUniform_v3("textColor", m_color); m_shader->setUniform_i("textScaling", (int)m_scaled); + */ + std::vector glyphs; for (char c : m_text) { glyphs.push_back(m_font->getChar(c)); diff --git a/src/engine.cpp b/src/engine.cpp index 49710d7..86d9c36 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -11,11 +11,8 @@ #include "resources/mesh.hpp" -struct UBO { - glm::mat4 model{}; - glm::mat4 view{}; - glm::mat4 proj{}; -}; +#include "components/mesh_renderer.hpp" +#include "components/camera.hpp" static engine::gfx::Pipeline* pipeline; static engine::gfx::Buffer* vb; @@ -28,7 +25,6 @@ namespace engine { m_win = std::make_unique(appName, true); gfxdev = new GFXDevice(appName, appVersion, m_win->getHandle()); - m_input = std::make_unique(*m_win); m_res = std::make_unique(); GameIO things{}; @@ -36,6 +32,9 @@ namespace engine { things.input = m_input.get(); things.resMan = m_res.get(); m_scene = std::make_unique(things); + + m_scene->createChild("player")->createComponent()->setMesh("meshes/cube.mesh"); + m_scene->createChild("cam")->createComponent(); } Application::~Application() @@ -50,8 +49,6 @@ namespace engine { uint64_t lastTick = m_win->getNanos(); constexpr int TICKFREQ = 1; // in hz - auto myMesh = m_res->get("meshes/monke.mesh"); - // single-threaded game loop while (m_win->isRunning()) { diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 5384917..79fe32e 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1083,6 +1083,12 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } + void GFXDevice::getViewportSize(uint32_t *w, uint32_t *h) + { + *w = pimpl->swapchain.extent.width; + *h = pimpl->swapchain.extent.height; + } + void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData) { assert(vertexBuffer->type == gfx::BufferType::VERTEX); @@ -1101,6 +1107,7 @@ namespace engine { memcpy(call.uniformData, uniformData, uniformDataSize); pimpl->drawQueues[pipeline].push(call); + } void GFXDevice::renderFrame() @@ -1171,6 +1178,7 @@ namespace engine { vkCmdBindPipeline(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 0, 1, &pipeline->descriptorSets[frameIndex], 0, nullptr); while (queue.empty() == false) { + DrawCall call = queue.front(); void* uniformDest; diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index ce8de33..a70ce8d 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -2,6 +2,8 @@ #include "gfx_device.hpp" +#include "log.hpp" + namespace engine::resources { struct MeshFileHeader { @@ -38,6 +40,12 @@ void Mesh::initMesh() { vb = gfxdev->createBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data()); ib = gfxdev->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data()); + + TRACE("Vertices:"); + + for (const auto& v : m_vertices) { + TRACE("pos: {}, {}, {}", v.pos.x, v.pos.y, v.pos.z); + } } Mesh::Mesh(const std::vector& vertices) : Resource("", "mesh") diff --git a/src/resources/shader.cpp b/src/resources/shader.cpp index 993badf..8c62b16 100644 --- a/src/resources/shader.cpp +++ b/src/resources/shader.cpp @@ -1,8 +1,8 @@ #include "resources/shader.hpp" -#include +#include "log.hpp" -#include +#include "gfx_device.hpp" #include #include @@ -24,200 +24,26 @@ static std::unique_ptr> readFile(const char * path) return buf; } -static GLuint compile(const char *path, GLenum type) -{ - auto src = readFile(path); - - // compile shader - GLuint handle = glCreateShader(type); - GLint size = (GLint)src->size(); - GLchar *data = src->data(); - glShaderSource(handle, 1, &data, &size); - glCompileShader(handle); - - // check for compilation error - GLint compiled; - glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled); - if (compiled == 0) { - GLint log_len; - glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_len); - GLchar *log_msg = (GLchar *)calloc(1, log_len); - if (log_msg == NULL) { - throw std::runtime_error("Error allocating memory for shader compilation error log"); - } - glGetShaderInfoLog(handle, log_len, NULL, log_msg); - throw std::runtime_error("Shader compilation error in " + std::string(path) + " log:\n" + std::string(log_msg)); - } return handle; -} - namespace engine::resources { -// I've got to do this because of GL's stupid state machine -GLuint Shader::s_activeProgram = 0; - Shader::Shader(const std::filesystem::path& resPath) : Resource(resPath, "shader") { - const std::string vertexShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".vert")).string(); - const std::string fragmentShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".frag")).string(); - GLuint vs = compile(vertexShaderPath.c_str(), GL_VERTEX_SHADER); - GLuint fs = compile(fragmentShaderPath.c_str(), GL_FRAGMENT_SHADER); - m_program = glCreateProgram(); - glAttachShader(m_program, vs); - glAttachShader(m_program, fs); - glLinkProgram(m_program); - glValidateProgram(m_program); + gfx::VertexFormat vertexFormat {}; + vertexFormat.attributeDescriptions.emplace_back(0, gfx::VertexAttribFormat::VEC3, 0); // pos + vertexFormat.attributeDescriptions.emplace_back(1, gfx::VertexAttribFormat::VEC3, sizeof(glm::vec3)); // norm + vertexFormat.attributeDescriptions.emplace_back(2, gfx::VertexAttribFormat::VEC2, sizeof(glm::vec3) + sizeof(glm::vec3)); // uv - // flag shader objects for deletion, this does not take effect until the program is deleted - glDeleteShader(vs); - glDeleteShader(fs); + const std::string vertexShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".vert.spv")).string(); + const std::string fragmentShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".frag.spv")).string(); - GLint linked, validated; - glGetProgramiv(m_program, GL_LINK_STATUS, &linked); - glGetProgramiv(m_program, GL_VALIDATE_STATUS, &validated); - if (linked == 0 || validated == 0) { - GLint log_len; - glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &log_len); - GLchar *log_msg = (GLchar *)calloc(1, log_len); - if (log_msg == NULL) { - throw std::runtime_error("Error allocating memory for shader linking error log"); - } - glGetProgramInfoLog(m_program, log_len, NULL, log_msg); - throw std::runtime_error("Program linking error with " + vertexShaderPath + " and " + fragmentShaderPath + " log:\n" + log_msg); - } - - DEBUG("For shader {}:", resPath.filename().string()); - - // now get uniforms - GLint count; - glGetProgramiv(m_program, GL_ACTIVE_UNIFORMS, &count); - for (int i = 0; i < count; i++) { - char nameBuf[64] = {}; - GLint size; - GLenum type; - glGetActiveUniform(m_program, i, 63, NULL, &size, &type, nameBuf); - m_uniforms[nameBuf] = Uniform{size, static_cast(type), (GLuint)i}; - DEBUG("\tuniform {}", nameBuf); -} - - // now get all attributes - glGetProgramiv(m_program, GL_ACTIVE_ATTRIBUTES, &count); - for (int i = 0; i < count; i++) { - char nameBuf[64] = {}; - GLint size; - GLenum type; - glGetActiveAttrib(m_program, i, 63, NULL, &size, &type, nameBuf); - m_attributes[nameBuf] = Attribute{size, static_cast(type), (GLuint)i}; - DEBUG("\tattrib {}", nameBuf); - } + m_pipeline = gfxdev->createPipeline(vertexShaderPath.c_str(), fragmentShaderPath.c_str(), vertexFormat, sizeof(UniformBuffer)); } Shader::~Shader() { - glDeleteProgram(m_program); -} - -void Shader::makeActive() const -{ - if (s_activeProgram != m_program) { - glUseProgram(m_program); - s_activeProgram = m_program; - } -} - -int Shader::getUniformLocation(const std::string& name) const -{ - auto it = m_uniforms.find(name); - if (it != m_uniforms.end()) { - Uniform u = it->second; - return u.location; - } - else { - return -1; - } -} - -bool Shader::setUniform_m4(const std::string& name, const glm::mat4& m) const -{ - makeActive(); - int loc = getUniformLocation(name); - if (loc != -1) { - glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]); - return true; - } - else { - return false; - } -} - -bool Shader::setUniform_v2(const std::string& name, const glm::vec2& v) const -{ - makeActive(); - int loc = getUniformLocation(name); - if (loc != -1) { - glUniform2f(loc, v.x, v.y); - return true; - } - else { - return false; - } - -} - -bool Shader::setUniform_v3(const std::string& name, const glm::vec3& v) const -{ - makeActive(); - int loc = getUniformLocation(name); - if (loc != -1) { - glUniform3f(loc, v.x, v.y, v.z); - return true; - } - else { - return false; - } -} - -bool Shader::setUniform_i(const std::string& name, int n) const -{ - makeActive(); - int loc = getUniformLocation(name); - if (loc != -1) { - glUniform1i(loc, n); - return true; - } - else { - return false; - } -} - -bool Shader::setUniform_f(const std::string& name, float n) const -{ - makeActive(); - int loc = getUniformLocation(name); - if (loc != -1) { - glUniform1f(loc, n); - return true; - } - else { - return false; - } -} - -Shader::UniformType Shader::getUniformType(const std::string& name) const -{ - auto it = m_uniforms.find(name); - if (it != m_uniforms.end()) { - return it->second.type; - } - else { - return UniformType::NOTFOUND; - } -} - -int Shader::getAttribLocation(const std::string& name) const -{ - return m_attributes.at(name).location; + gfxdev->destroyPipeline(m_pipeline); } } From 99b287309dd9b87491723e8feb19816cd1158552 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Mon, 31 Oct 2022 16:21:07 +0000 Subject: [PATCH 50/50] Add vulkan push constant support --- include/components/camera.hpp | 5 +- include/components/mesh_renderer.hpp | 2 +- include/engine.hpp | 30 ++++- include/gfx_device.hpp | 4 +- include/object.hpp | 1 - include/resources/shader.hpp | 5 +- src/components/camera.cpp | 15 ++- src/components/mesh_renderer.cpp | 8 +- src/engine.cpp | 30 +++-- src/gfx_device_vulkan.cpp | 184 ++++++++++++++++++++++----- src/object.cpp | 4 +- src/resource_manager.cpp | 2 + src/resources/mesh.cpp | 16 ++- src/resources/shader.cpp | 1 + src/sceneroot.cpp | 10 +- 15 files changed, 241 insertions(+), 76 deletions(-) diff --git a/include/components/camera.hpp b/include/components/camera.hpp index 4e6db0d..e5f4e6a 100644 --- a/include/components/camera.hpp +++ b/include/components/camera.hpp @@ -8,8 +8,9 @@ #include -#include -#include +#include +#include +#include namespace engine::components { diff --git a/include/components/mesh_renderer.hpp b/include/components/mesh_renderer.hpp index 43a468f..588e7ce 100644 --- a/include/components/mesh_renderer.hpp +++ b/include/components/mesh_renderer.hpp @@ -21,7 +21,7 @@ public: ~Renderer() override; // called every frame, do not call manually - void render(glm::mat4 transform); + void render(glm::mat4 model); void setMesh(const std::string& name); void setTexture(const std::string& name); diff --git a/include/engine.hpp b/include/engine.hpp index 3361fd7..809ecea 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -21,11 +21,33 @@ namespace engine { void gameLoop(); + Window* window() + { + return m_win; + } + + Input* input() + { + return m_input; + } + + ResourceManager* resources() + { + return m_res; + } + + SceneRoot* scene() + { + return m_scene; + } + + + private: - std::unique_ptr m_win; - std::unique_ptr m_input; - std::unique_ptr m_res; - std::unique_ptr m_scene; + Window* m_win; + Input* m_input; + ResourceManager* m_res; + SceneRoot* m_scene; }; } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a5106c2..5d8ca33 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -23,7 +23,7 @@ namespace engine { // adds a draw call to the queue // vertexBuffer is required, indexBuffer can be NULL, uniformData is required - void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData); + void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData); // Call once per frame. Executes all queued draw calls and renders to the screen. void renderFrame(); @@ -32,6 +32,8 @@ namespace engine { gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize); void destroyPipeline(const gfx::Pipeline* pipeline); + void updateUniformBuffer(const gfx::Pipeline* pipeline, void* data); + gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); void destroyBuffer(const gfx::Buffer* buffer); diff --git a/include/object.hpp b/include/object.hpp index 86105c4..b6c4f71 100644 --- a/include/object.hpp +++ b/include/object.hpp @@ -23,7 +23,6 @@ namespace engine { class Component; namespace components { - class Transform; class Camera; class Renderer; class UI; diff --git a/include/resources/shader.hpp b/include/resources/shader.hpp index 5b88253..e547c82 100644 --- a/include/resources/shader.hpp +++ b/include/resources/shader.hpp @@ -10,7 +10,7 @@ #include namespace engine::gfx { - class Pipeline; + struct Pipeline; } namespace engine::resources { @@ -22,7 +22,8 @@ public: ~Shader() override; struct UniformBuffer { - glm::mat4 transform; + glm::mat4 v; + glm::mat4 p; }; gfx::Pipeline* getPipeline() diff --git a/src/components/camera.cpp b/src/components/camera.cpp index 4c786f9..d5b3ff3 100644 --- a/src/components/camera.cpp +++ b/src/components/camera.cpp @@ -38,6 +38,14 @@ void Camera::updateCam(glm::mat4 transform) glm::mat4 viewMatrix = glm::inverse(transform); + struct { + glm::mat4 view; + glm::mat4 proj; + } uniformData{}; + + uniformData.view = viewMatrix; + uniformData.proj = m_projMatrix; + using namespace resources; auto resPtrs = parent.res.getAllResourcesOfType("shader"); @@ -46,6 +54,7 @@ void Camera::updateCam(glm::mat4 transform) auto lockedPtr = resPtr.lock(); auto shader = dynamic_cast(lockedPtr.get()); // SET VIEW TRANSFORM HERE + gfxdev->updateUniformBuffer(shader->getPipeline(), &uniformData); } } @@ -69,7 +78,8 @@ void Camera::usePerspective(float fovDeg) glm::vec2 viewportDim = getViewportSize(); float fovRad = glm::radians(fovDeg); - m_projMatrix = glm::perspectiveFov(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR); + m_projMatrix = glm::perspectiveFovRH_ZO(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR); + m_projMatrix[1][1] *= -1; } void Camera::useOrtho() @@ -79,7 +89,8 @@ void Camera::useOrtho() glm::vec2 viewportDim = getViewportSize(); float aspect = viewportDim.x / viewportDim.y; - m_projMatrix = glm::ortho(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f); + m_projMatrix = glm::orthoRH_ZO(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f); + m_projMatrix[1][1] *= -1; } } diff --git a/src/components/mesh_renderer.cpp b/src/components/mesh_renderer.cpp index a4e8e17..fa4deb4 100644 --- a/src/components/mesh_renderer.cpp +++ b/src/components/mesh_renderer.cpp @@ -6,6 +6,8 @@ #include "gfx_device.hpp" +#include "log.hpp" + #include namespace engine::components { @@ -23,11 +25,7 @@ Renderer::~Renderer() void Renderer::render(glm::mat4 transform) { - resources::Shader::UniformBuffer ub { - glm::mat4{1.0f}, - }; - - gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_indices.size(), &ub); + gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_vertices.size(), &transform); } void Renderer::setMesh(const std::string& name) diff --git a/src/engine.cpp b/src/engine.cpp index 86d9c36..1ecc2c9 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -14,32 +14,34 @@ #include "components/mesh_renderer.hpp" #include "components/camera.hpp" -static engine::gfx::Pipeline* pipeline; -static engine::gfx::Buffer* vb; -static engine::gfx::Buffer* ib; - namespace engine { Application::Application(const char* appName, const char* appVersion) { - m_win = std::make_unique(appName, true); + m_win = new Window(appName, true); gfxdev = new GFXDevice(appName, appVersion, m_win->getHandle()); - m_input = std::make_unique(*m_win); - m_res = std::make_unique(); - GameIO things{}; - things.win = m_win.get(); - things.input = m_input.get(); - things.resMan = m_res.get(); - m_scene = std::make_unique(things); - m_scene->createChild("player")->createComponent()->setMesh("meshes/cube.mesh"); - m_scene->createChild("cam")->createComponent(); + m_input = new Input(*m_win); + m_res = new ResourceManager(); + + GameIO things{ + m_win, + m_input, + m_res + }; + m_scene = new SceneRoot(things); } Application::~Application() { + delete m_scene; + delete m_res; + delete m_input; + delete gfxdev; + + delete m_win; } void Application::gameLoop() diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 79fe32e..133abb2 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -36,7 +36,7 @@ namespace engine { static constexpr uint32_t FRAMES_IN_FLIGHT = 2; // This improved FPS by 5x! (on Intel IGPU) - static constexpr size_t UNIFORM_BUFFER_MAX_SIZE = 256; // bytes + static constexpr size_t PUSH_CONSTANT_MAX_SIZE = 128; // bytes // structures and enums @@ -55,6 +55,13 @@ namespace engine { VkQueue handle; }; + + struct DepthBuffer { + VkImage image; + VmaAllocation allocation; + VkImageView view; + }; + struct Swapchain { VkSwapchainKHR swapchain = VK_NULL_HANDLE; @@ -66,6 +73,8 @@ namespace engine { std::vector imageViews{}; std::vector framebuffers{}; + DepthBuffer depthBuffer{}; + VkQueue activeQueue{}; VkRenderPass renderpass; @@ -78,7 +87,7 @@ namespace engine { const gfx::Buffer* vertexBuffer = nullptr; const gfx::Buffer* indexBuffer = nullptr; // if this is nullptr, don't use indexed uint32_t count = 0; - uint8_t uniformData[UNIFORM_BUFFER_MAX_SIZE]; + uint8_t pushConstantData[PUSH_CONSTANT_MAX_SIZE]; }; enum class QueueFlags : uint32_t { @@ -307,8 +316,59 @@ namespace engine { throw std::runtime_error("Unable to find the requested queue"); } + static DepthBuffer createDepthBuffer(VkDevice device, VmaAllocator allocator, VkExtent2D extent) + { + DepthBuffer db{}; + + VkResult res; + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = extent.width; + imageInfo.extent.height = extent.height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = VK_FORMAT_D32_SFLOAT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; + + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + allocInfo.priority = 1.0f; + + res = vmaCreateImage(allocator, &imageInfo, &allocInfo, &db.image, &db.allocation, nullptr); + assert(res == VK_SUCCESS); + + VkImageViewCreateInfo imageViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + imageViewInfo.image = db.image; + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewInfo.format = VK_FORMAT_D32_SFLOAT; + imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + imageViewInfo.subresourceRange.baseMipLevel = 0; + imageViewInfo.subresourceRange.levelCount = 1; + imageViewInfo.subresourceRange.baseArrayLayer = 0; + imageViewInfo.subresourceRange.layerCount = 1; + res = vkCreateImageView(device, &imageViewInfo, nullptr, &db.view); + assert(res == VK_SUCCESS); + + return db; + } + + static void destroyDepthBuffer(DepthBuffer db, VkDevice device, VmaAllocator allocator) + { + vkDestroyImageView(device, db.view, nullptr); + vmaDestroyImage(allocator, db.image, db.allocation); + } + // This is called not just on initialisation, but also when the window is resized. - static void createSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, const std::vector queues, SDL_Window* window, VkSurfaceKHR surface, Swapchain* swapchain) + static void createSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, VmaAllocator allocator, std::vector queues, SDL_Window* window, VkSurfaceKHR surface, Swapchain* swapchain) { VkResult res; @@ -363,6 +423,8 @@ namespace engine { } } + VkExtent2D oldExtent = swapchain->extent; + if (caps.currentExtent.width != std::numeric_limits::max()) { swapchain->extent = caps.currentExtent; } @@ -435,6 +497,16 @@ namespace engine { res = vkGetSwapchainImagesKHR(device, swapchain->swapchain, &swapchainImageCount, swapchain->images.data()); assert(res == VK_SUCCESS); + // create depth buffer if old depth buffer is wrong size + if (swapchain->swapchain == VK_NULL_HANDLE) { + swapchain->depthBuffer = createDepthBuffer(device, allocator, swapchain->extent); + } + else if (swapchain->extent.width != oldExtent.width || swapchain->extent.height != oldExtent.height) { + destroyDepthBuffer(swapchain->depthBuffer, device, allocator); + swapchain->depthBuffer = createDepthBuffer(device, allocator, swapchain->extent); + } + + // create the render pass if (swapchain->renderpass == VK_NULL_HANDLE) { VkAttachmentDescription colorAttachment{}; @@ -451,26 +523,42 @@ namespace engine { colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentDescription depthAttachment{}; + depthAttachment.format = VK_FORMAT_D32_SFLOAT; + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthAttachmentRef{}; + depthAttachmentRef.attachment = 1; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; + subpass.pDepthStencilAttachment = &depthAttachmentRef; VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + std::array attachments = { colorAttachment, depthAttachment }; VkRenderPassCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - createInfo.attachmentCount = 1; - createInfo.pAttachments = &colorAttachment; + createInfo.attachmentCount = (uint32_t)attachments.size(); + createInfo.pAttachments = attachments.data(); createInfo.subpassCount = 1; createInfo.pSubpasses = &subpass; - createInfo.dependencyCount = 1; createInfo.pDependencies = &dependency; @@ -505,15 +593,16 @@ namespace engine { res = vkCreateImageView(device, &createInfo, nullptr, &swapchain->imageViews[i]); assert(res == VK_SUCCESS); - VkImageView attachments[] = { - swapchain->imageViews[i] + std::array attachments = { + swapchain->imageViews[i], + swapchain->depthBuffer.view }; VkFramebufferCreateInfo framebufferInfo{}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = swapchain->renderpass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; + framebufferInfo.attachmentCount = (uint32_t)attachments.size(); + framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = swapchain->extent.width; framebufferInfo.height = swapchain->extent.height; framebufferInfo.layers = 1; @@ -1022,7 +1111,7 @@ namespace engine { // Now make the swapchain - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, window, pimpl->surface, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, window, pimpl->surface, &pimpl->swapchain); @@ -1071,6 +1160,7 @@ namespace engine { for (VkFramebuffer fb : pimpl->swapchain.framebuffers) { vkDestroyFramebuffer(pimpl->device, fb, nullptr); } + destroyDepthBuffer(pimpl->swapchain.depthBuffer, pimpl->device, pimpl->allocator); vkDestroyRenderPass(pimpl->device, pimpl->swapchain.renderpass, nullptr); vkDestroySwapchainKHR(pimpl->device, pimpl->swapchain.swapchain, nullptr); @@ -1089,12 +1179,11 @@ namespace engine { *h = pimpl->swapchain.extent.height; } - void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* uniformData) + void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData) { assert(vertexBuffer->type == gfx::BufferType::VERTEX); assert(vertexBuffer != nullptr); assert(indexBuffer == nullptr || indexBuffer->type == gfx::BufferType::INDEX); - assert(uniformData != nullptr); DrawCall call{ .vertexBuffer = vertexBuffer, @@ -1102,9 +1191,7 @@ namespace engine { .count = count, }; - size_t uniformDataSize = pipeline->uniformBuffers[pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT]->size; - - memcpy(call.uniformData, uniformData, uniformDataSize); + memcpy(call.pushConstantData, pushConstantData, PUSH_CONSTANT_MAX_SIZE); pimpl->drawQueues[pipeline].push(call); @@ -1126,7 +1213,7 @@ namespace engine { if (res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); return; } else { @@ -1150,9 +1237,11 @@ namespace engine { renderPassInfo.renderArea.offset = { 0, 0 }; renderPassInfo.renderArea.extent = pimpl->swapchain.extent; - VkClearValue clearColor{ {0.0f, 0.0f, 0.0f, 0.0f} }; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; + std::array clearValues{}; + clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} }; + clearValues[1].depthStencil = { 1.0f, 0 }; + renderPassInfo.clearValueCount = (uint32_t)clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); vkCmdBeginRenderPass(pimpl->commandBuffers[frameIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -1181,13 +1270,7 @@ namespace engine { DrawCall call = queue.front(); - void* uniformDest; - res = vmaMapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation, &uniformDest); - assert(res == VK_SUCCESS); - - memcpy(uniformDest, call.uniformData, pipeline->uniformBuffers[frameIndex]->size); - - vmaUnmapMemory(pimpl->allocator, pipeline->uniformBuffers[frameIndex]->allocation); + vkCmdPushConstants(pimpl->commandBuffers[frameIndex], pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, PUSH_CONSTANT_MAX_SIZE, call.pushConstantData); vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); @@ -1239,7 +1322,7 @@ namespace engine { if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, &pimpl->swapchain); } else { assert(res == VK_SUCCESS); @@ -1405,7 +1488,7 @@ namespace engine { rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.lineWidth = 1.0f; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; // ignored rasterizer.depthBiasClamp = 0.0f; // ignored @@ -1445,12 +1528,29 @@ namespace engine { colorBlending.blendConstants[2] = 0.0f; // ignored colorBlending.blendConstants[3] = 0.0f; // ignored + VkPipelineDepthStencilStateCreateInfo depthStencil{}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + depthStencil.front = {}; + depthStencil.back = {}; + + VkPushConstantRange pushConstantRange{}; + pushConstantRange.offset = 0; + pushConstantRange.size = PUSH_CONSTANT_MAX_SIZE; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + VkPipelineLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layoutInfo.setLayoutCount = 1; layoutInfo.pSetLayouts = &pimpl->uboLayout; - layoutInfo.pushConstantRangeCount = 0; - layoutInfo.pPushConstantRanges = nullptr; + layoutInfo.pushConstantRangeCount = 1; + layoutInfo.pPushConstantRanges = &pushConstantRange; res = vkCreatePipelineLayout(pimpl->device, &layoutInfo, nullptr, &pipeline->layout); assert(res == VK_SUCCESS); @@ -1464,7 +1564,7 @@ namespace engine { createInfo.pViewportState = &viewportState; createInfo.pRasterizationState = &rasterizer; createInfo.pMultisampleState = &multisampling; - createInfo.pDepthStencilState = nullptr; + createInfo.pDepthStencilState = &depthStencil; createInfo.pColorBlendState = &colorBlending; createInfo.pDynamicState = &dynamicState; createInfo.layout = pipeline->layout; @@ -1498,6 +1598,20 @@ namespace engine { delete pipeline; } + void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, void* data) + { + VkResult res; + + void* uniformDest; + for (gfx::Buffer* buffer : pipeline->uniformBuffers) { + res = vmaMapMemory(pimpl->allocator, buffer->allocation, &uniformDest); + assert(res == VK_SUCCESS); + memcpy(uniformDest, data, buffer->size); + vmaUnmapMemory(pimpl->allocator, buffer->allocation); + } + + } + gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data) { VkResult res; diff --git a/src/object.cpp b/src/object.cpp index 11d74d3..46efc52 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -7,6 +7,8 @@ #include +#include + namespace engine { int Object::s_object_count = 0; @@ -109,7 +111,7 @@ namespace engine { // scale (effectively applied first objTransform = glm::scale(objTransform, t.scale); - const glm::mat4 newTransform = parentTransform * objTransform; + glm::mat4 newTransform = parentTransform * objTransform; for (const auto& compUnq : m_components) { const auto comp = compUnq.get(); diff --git a/src/resource_manager.cpp b/src/resource_manager.cpp index dfef6da..6d0ee8c 100644 --- a/src/resource_manager.cpp +++ b/src/resource_manager.cpp @@ -35,6 +35,8 @@ namespace engine { if (std::filesystem::is_directory(m_resourcesPath) == false) { throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); } + + m_resourcesPath = "C:/Users/Bailey/source/repos/game/res"; } std::unique_ptr ResourceManager::getResourcesListString() diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index a70ce8d..8d276a9 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -7,9 +7,9 @@ namespace engine::resources { struct MeshFileHeader { - unsigned int vertex_count; - unsigned int index_count; - int material; + uint32_t vertex_count; + uint32_t index_count; + int32_t material; }; static void loadMeshFromFile(const std::filesystem::path& path, std::vector* vertices, std::vector* indices) @@ -30,7 +30,7 @@ static void loadMeshFromFile(const std::filesystem::path& path, std::vectorresize(header.index_count); vertices->resize(header.vertex_count); - fread(indices->data(), sizeof(unsigned int) * header.index_count, 1, fp); + fread(indices->data(), sizeof(uint32_t) * header.index_count, 1, fp); fread(vertices->data(), sizeof(float) * 8 * header.vertex_count, 1, fp); fclose(fp); @@ -41,11 +41,19 @@ void Mesh::initMesh() vb = gfxdev->createBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data()); ib = gfxdev->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data()); + TRACE("VB PTR in mesh: {}", (void*)vb); + TRACE("Vertices:"); for (const auto& v : m_vertices) { TRACE("pos: {}, {}, {}", v.pos.x, v.pos.y, v.pos.z); } + + TRACE("Indices:"); + + for (const uint32_t i : m_indices) { + TRACE("\t{}", i); + } } Mesh::Mesh(const std::vector& vertices) : Resource("", "mesh") diff --git a/src/resources/shader.cpp b/src/resources/shader.cpp index 8c62b16..25c643e 100644 --- a/src/resources/shader.cpp +++ b/src/resources/shader.cpp @@ -30,6 +30,7 @@ Shader::Shader(const std::filesystem::path& resPath) : Resource(resPath, "shader { gfx::VertexFormat vertexFormat {}; + vertexFormat.stride = 8 * sizeof(float); vertexFormat.attributeDescriptions.emplace_back(0, gfx::VertexAttribFormat::VEC3, 0); // pos vertexFormat.attributeDescriptions.emplace_back(1, gfx::VertexAttribFormat::VEC3, sizeof(glm::vec3)); // norm vertexFormat.attributeDescriptions.emplace_back(2, gfx::VertexAttribFormat::VEC2, sizeof(glm::vec3) + sizeof(glm::vec3)); // uv diff --git a/src/sceneroot.cpp b/src/sceneroot.cpp index 31ac13d..5b8f1dc 100644 --- a/src/sceneroot.cpp +++ b/src/sceneroot.cpp @@ -7,6 +7,8 @@ #include "components/mesh_renderer.hpp" #include "components/text_ui_renderer.hpp" +#include "gfx_device.hpp" + #include #include @@ -46,12 +48,12 @@ namespace engine { // render - for (const auto& [c, t] : compList.cameras) { + for (const auto& [c, camt] : compList.cameras) { for (int id : m_activeCameras) { if (c->getID() == id) { - c->updateCam(t); - for (const auto& [c, t] : compList.renderers) { - c->render(t); + c->updateCam(camt); + for (const auto& [ren, ren_t] : compList.renderers) { + ren->render(ren_t); } break;