From 525db7fe6bb4780d945c9209209b5d0dde66379c Mon Sep 17 00:00:00 2001 From: Green Sky Date: Thu, 2 Jun 2022 20:01:23 +0200 Subject: [PATCH] refactor, move vulkan to proper location --- framework/CMakeLists.txt | 1 + framework/sdl_service/CMakeLists.txt | 3 +- framework/sdl_service/test/CMakeLists.txt | 1 - framework/sdl_service/test/vulkan_test.cpp | 462 ------------------ framework/vulkan_renderer/CMakeLists.txt | 26 + .../src/mm/services/vulkan_renderer.cpp | 406 +++++++++++++++ .../src/mm/services/vulkan_renderer.hpp | 69 +++ framework/vulkan_renderer/test/CMakeLists.txt | 13 + .../vulkan_renderer/test/vulkan_test.cpp | 37 ++ 9 files changed, 554 insertions(+), 464 deletions(-) delete mode 100644 framework/sdl_service/test/vulkan_test.cpp create mode 100644 framework/vulkan_renderer/CMakeLists.txt create mode 100644 framework/vulkan_renderer/src/mm/services/vulkan_renderer.cpp create mode 100644 framework/vulkan_renderer/src/mm/services/vulkan_renderer.hpp create mode 100644 framework/vulkan_renderer/test/CMakeLists.txt create mode 100644 framework/vulkan_renderer/test/vulkan_test.cpp diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 822919f..0ad0dcb 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -17,6 +17,7 @@ if(NOT MM_HEADLESS) add_subdirectory(simple_sdl_renderer) add_subdirectory(opengl_primitives) add_subdirectory(opengl_renderer) + add_subdirectory(vulkan_renderer) add_subdirectory(imgui) add_subdirectory(input) add_subdirectory(sound) diff --git a/framework/sdl_service/CMakeLists.txt b/framework/sdl_service/CMakeLists.txt index f383f5b..98b4b16 100644 --- a/framework/sdl_service/CMakeLists.txt +++ b/framework/sdl_service/CMakeLists.txt @@ -40,7 +40,8 @@ else() target_link_libraries(sdl_service glad) endif() -target_link_libraries(sdl_service Vulkan::Headers) +# nope +#target_link_libraries(sdl_service Vulkan::Headers) if(VCPKG_TARGET_TRIPLET) target_link_libraries(sdl_service SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static) diff --git a/framework/sdl_service/test/CMakeLists.txt b/framework/sdl_service/test/CMakeLists.txt index a6fe4a4..d01ccc6 100644 --- a/framework/sdl_service/test/CMakeLists.txt +++ b/framework/sdl_service/test/CMakeLists.txt @@ -1,6 +1,5 @@ add_executable(sdl_service_test ./start_test.cpp - ./vulkan_test.cpp ) target_include_directories(sdl_service_test PRIVATE ".") diff --git a/framework/sdl_service/test/vulkan_test.cpp b/framework/sdl_service/test/vulkan_test.cpp deleted file mode 100644 index 6eed21f..0000000 --- a/framework/sdl_service/test/vulkan_test.cpp +++ /dev/null @@ -1,462 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include // mf -#include -#include "spdlog/spdlog.h" -#include "vulkan/vulkan_core.h" -#include "vulkan/vulkan_handles.hpp" - -#include - -#include - -VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE - -// create a dispatcher, based on additional vkDevice/vkGetDeviceProcAddr -void setup_dispacher(void) { -#if 1 - // investigate why this stopped working - static vk::DynamicLoader dl; - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); -#else - auto load_res = SDL_Vulkan_LoadLibrary(nullptr); - assert(load_res == 0); - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = reinterpret_cast(SDL_Vulkan_GetVkGetInstanceProcAddr()); -#endif - - VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); -} - -bool can_use_layer(std::string_view layer_want) { - for (const auto& layer : vk::enumerateInstanceLayerProperties()) { - if (static_cast(layer.layerName) == layer_want) { - return true; - } - } - - return false; -} - -bool can_use_validation(void) { - return can_use_layer("VK_LAYER_KHRONOS_validation"); -} - -VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* /*pUserData*/ -) { - spdlog::level::level_enum level{}; - switch (messageSeverity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - level = spdlog::level::level_enum::debug; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - level = spdlog::level::level_enum::info; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - level = spdlog::level::level_enum::warn; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - level = spdlog::level::level_enum::err; - break; - default: - level = spdlog::level::level_enum::critical; // what ever - } - - switch (messageType) { - case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: - spdlog::get("VulkanGeneral")->log(level, "{}", pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: - spdlog::get("VulkanValidation")->log(level, "{}", pCallbackData->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: - spdlog::get("VulkanPerformance")->log(level, "{}", pCallbackData->pMessage); - break; - } - - return VK_FALSE; -} - -static const vk::DebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info{ - {}, - vk::DebugUtilsMessageSeverityFlagBitsEXT::eError - | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning - | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo - | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose, - vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral - | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation - | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, - debug_callback -}; - -vk::Instance create_instance( - const vk::ApplicationInfo& app_info, - std::vector extensions = {}, - std::vector layers = {} -) { - // for debugging - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - - // Get the required extension count - unsigned int count; - if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr) != SDL_TRUE) { - return nullptr; - } - - size_t additional_extension_count = extensions.size(); - extensions.resize(additional_extension_count + count); - - // fill sdl extensions - if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, extensions.data() + additional_extension_count) != SDL_TRUE) { - return nullptr; - } - - // Now we can make the Vulkan instance - vk::StructureChain c{ - vk::InstanceCreateInfo{ - {}, - &app_info, - static_cast(layers.size()), - layers.data(), - static_cast(extensions.size()), - extensions.data() - }, - debug_utils_messenger_create_info - }; - - vk::Instance instance = vk::createInstance(c.get(), nullptr); - - // initialize function pointers for instance - VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); - - return instance; -} - -namespace MM::Services { - -class VulkanRenderer : public Service { - private: - VkInstance _instance{}; - VkDebugUtilsMessengerEXT _debug_messenger{}; - - VkSurfaceKHR _surface{}; - - VkPhysicalDevice _physical_device{}; - VkDevice _device{}; - - VkQueue _graphics_queue{}; - //VkQueue _present_queue{}; - - VkSwapchainKHR _swapchain{}; - std::vector _swapchain_images{}; - std::vector _swapchain_image_views{}; - std::vector _swapchain_framebuffers{}; - - public: - VulkanRenderer(void) { -#if 0 - MM::Logger::initSectionLogger("VulkanGeneral"); - // way too noisy otherwise - spdlog::get("VulkanGeneral")->set_level(spdlog::level::level_enum::warn); -#else - // or just dont log to stdio? - MM::Logger::initSectionLogger("VulkanGeneral", false); -#endif - MM::Logger::initSectionLogger("VulkanValidation"); - MM::Logger::initSectionLogger("VulkanPerformance"); - - SPDLOG_INFO("constructed VulkanRenderer"); - } - ~VulkanRenderer(void) {}; - - bool enable(Engine& engine, std::vector& task_array) override { - assert(!VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceLayerProperties); - setup_dispacher(); - assert(VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceLayerProperties); - - // create vulkan instance - const vk::ApplicationInfo app_info { - "app_name", - VK_MAKE_VERSION(1, 0, 0), // app version - "MushMachine", - // TODO: engine version macro or something - VK_MAKE_VERSION(0, 8, 0), // engine version - VK_API_VERSION_1_1 - }; - - // TODO: make validation layer conditional - std::vector layers{}; - if (can_use_validation()) { - layers.push_back("VK_LAYER_KHRONOS_validation"); - SPDLOG_INFO("ENABLED validation layer"); - } else { - SPDLOG_INFO("validation layer NOT AVAILABLE!"); - } - - vk::Instance instance = create_instance( - app_info, - {}, - layers - ); - _instance = instance; - - _debug_messenger = instance.createDebugUtilsMessengerEXT(debug_utils_messenger_create_info); - - return true; - } - - void disable(Engine&) override { - // cleanup - if (_device) { - vk::Device device{_device}; - - for (const auto& fb : _swapchain_framebuffers) { - device.destroy(fb); - } - for (const auto& img_view : _swapchain_image_views) { - device.destroy(img_view); - } - device.destroy(_swapchain); - device.destroy(); - } - - vk::Instance instance{_instance}; - instance.destroy(_surface); - instance.destroy(_debug_messenger); - instance.destroy(); - } - - const char* name(void) override { return "VulkanRenderer"; } - - public: - bool createDevice(Engine& engine) { - // the surface for the window (not device dependent) - if (SDL_Vulkan_CreateSurface(engine.getService().win, _instance, &_surface) != SDL_TRUE) { - SPDLOG_ERROR("creating vulkan surface from window. (is the SDL_WINDOW_VULKAN flag set?)"); - return false; - } - - // convenience hpp wrapper - assert(_surface); - vk::SurfaceKHR surface{_surface}; - assert(_instance); - vk::Instance instance{_instance}; - - auto physical_devices = instance.enumeratePhysicalDevices(); - if (physical_devices.empty()) { - SPDLOG_ERROR("no physical vulkan devices found"); - return false; - } - - // list devices - for (const auto& ph_device : physical_devices) { - auto props = ph_device.getProperties(); - SPDLOG_INFO( - "found device: [{}] ({}) '{}'", - props.deviceID, - (props.deviceType == vk::PhysicalDeviceType::eDiscreteGpu ? "discrete" : "other"), - props.deviceName - ); - } - - auto& selected_phys_dev = physical_devices.front(); - _physical_device = selected_phys_dev; - - for (const auto& fam_props : selected_phys_dev.getQueueFamilyProperties()) { - auto test_bit = [](const auto& flags, const auto& bit) -> bool { - return (flags & bit) == bit; - }; - SPDLOG_INFO( - "QueueFamily: queueCount:{} graphics:{} compute:{} transfer:{}", - fam_props.queueCount, - test_bit(fam_props.queueFlags, vk::QueueFlagBits::eGraphics) ? "true" : "false", - test_bit(fam_props.queueFlags, vk::QueueFlagBits::eCompute) ? "true" : "false", - test_bit(fam_props.queueFlags, vk::QueueFlagBits::eTransfer) ? "true" : "false" - ); - } - - uint32_t queue_fam_index = 0; - - // test for support for swapchain - if (selected_phys_dev.getSurfaceSupportKHR(queue_fam_index, surface) != VK_TRUE) { - SPDLOG_ERROR("selected device does not support the surface"); - return false; - } - - const float queue_prio = 1.f; // hmmmm - vk::DeviceQueueCreateInfo graphics_queue_create_info { - {}, - queue_fam_index, // just pick the first one for now - 1, // count - &queue_prio - }; - - vk::PhysicalDeviceFeatures device_features { - }; - - // do i need this? - std::vector device_extentions{ - VK_KHR_SWAPCHAIN_EXTENSION_NAME - }; - - vk::DeviceCreateInfo device_create_info { - {}, - 1, - &graphics_queue_create_info, - // layers - 0, - nullptr, - // extensions - static_cast(device_extentions.size()), - device_extentions.data(), - &device_features - }; - vk::Device device = selected_phys_dev.createDevice(device_create_info, nullptr); - _device = device; - - // function pointer specialization for device - VULKAN_HPP_DEFAULT_DISPATCHER.init(device); - - //_present_queue = device.getQueue(0, 0); - // we assume it also does present - _graphics_queue = device.getQueue(0, 0); - - return true; - } - - bool createSwapchain(Engine& engine) { - assert(_physical_device); - vk::PhysicalDevice physical_device {_physical_device}; - assert(_device); - vk::Device device {_device}; - - vk::SurfaceFormatKHR swap_surf_format { - vk::Format::eB8G8R8A8Srgb, - vk::ColorSpaceKHR::eSrgbNonlinear, - }; - { // select format - //for (const auto& format : selected_phys_dev.getSurfaceFormatsKHR(surface)) { - //if (format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { - //if (format.format == vk::Format::eB8G8R8A8Srgb) { - - //} - //} - //} - } - - vk::Extent2D surface_extent {}; - { - int w, h; - SDL_Vulkan_GetDrawableSize(engine.getService().win, &w, &h); - surface_extent.width = w; - surface_extent.height = h; - } - - auto phys_surf_caps = physical_device.getSurfaceCapabilitiesKHR(_surface); - - // flush all loggers - spdlog::apply_all([](const auto& logger) { logger->flush(); }); - - assert(VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateSwapchainKHR); - - // create the swapchain - _swapchain = device.createSwapchainKHR({ - {}, - _surface, - phys_surf_caps.minImageCount, - swap_surf_format.format, - swap_surf_format.colorSpace, - surface_extent, - 1, // imageArrayLayers - vk::ImageUsageFlagBits::eColorAttachment, - vk::SharingMode::eExclusive, - // TODO: fill in rest - }); - - { - _swapchain_images.clear(); - auto images = device.getSwapchainImagesKHR(_swapchain); - for (const auto& img : images) { - _swapchain_images.push_back(img); - } - } - SPDLOG_INFO("have {} swapchain images", _swapchain_images.size()); - - _swapchain_image_views.clear(); - for (const auto& img : _swapchain_images) { - _swapchain_image_views.push_back(device.createImageView({ - {}, - img, - vk::ImageViewType::e2D, - swap_surf_format.format, - {}, // comp mapping - { // subres - vk::ImageAspectFlagBits::eColor, - 0, - 1, - 0, - 1, - }, - })); - } - - // TODO: move - - _swapchain_framebuffers.clear(); - for (const auto& img_view : _swapchain_image_views) { - vk::ImageView tmp_img_view = img_view; - _swapchain_framebuffers.push_back(device.createFramebuffer({ - {}, - {}, // rend - 1, - &tmp_img_view, - surface_extent.width, - surface_extent.height, - 1 - })); - } - - return true; - } -}; - -} // MM::Services - -TEST(sdl_service, window_vulkan) { - MM::Engine engine; - - engine.addService(); - ASSERT_TRUE(engine.enableService()); - - auto* sdl_ss_ptr = engine.tryService(); - ASSERT_NE(sdl_ss_ptr, nullptr); - - // create window - ASSERT_EQ(sdl_ss_ptr->win, nullptr); - ASSERT_TRUE(sdl_ss_ptr->createWindow("test vulkan window", 800, 600, SDL_WINDOW_VULKAN)); - ASSERT_NE(sdl_ss_ptr->win, nullptr); - - engine.addService(); - ASSERT_TRUE(engine.enableService()); - - auto& vk_rend = engine.getService(); - ASSERT_TRUE(vk_rend.createDevice(engine)); - ASSERT_TRUE(vk_rend.createSwapchain(engine)); - - engine.run(); - - engine.disableService(); - engine.disableService(); - - ASSERT_EQ(sdl_ss_ptr->win, nullptr); -} - diff --git a/framework/vulkan_renderer/CMakeLists.txt b/framework/vulkan_renderer/CMakeLists.txt new file mode 100644 index 0000000..a101f33 --- /dev/null +++ b/framework/vulkan_renderer/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(vulkan_renderer CXX) + +add_library(vulkan_renderer + src/mm/services/vulkan_renderer.hpp + src/mm/services/vulkan_renderer.cpp +) + +target_include_directories(vulkan_renderer PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") + +target_link_libraries(vulkan_renderer + Vulkan::Headers + + engine + logger + + sdl_service +) + +######################## + +if (BUILD_TESTING) + add_subdirectory(test) +endif() + diff --git a/framework/vulkan_renderer/src/mm/services/vulkan_renderer.cpp b/framework/vulkan_renderer/src/mm/services/vulkan_renderer.cpp new file mode 100644 index 0000000..c4afd74 --- /dev/null +++ b/framework/vulkan_renderer/src/mm/services/vulkan_renderer.cpp @@ -0,0 +1,406 @@ +#include "./vulkan_renderer.hpp" + +#include +#include + +#include + +#include + +#include + +// this needs to be defined only once +VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE + +// create a dispatcher, based on additional vkDevice/vkGetDeviceProcAddr +static void setup_dispacher(void) { +#if 1 + // investigate why this stopped working + static vk::DynamicLoader dl; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); +#else + auto load_res = SDL_Vulkan_LoadLibrary(nullptr); + assert(load_res == 0); + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = reinterpret_cast(SDL_Vulkan_GetVkGetInstanceProcAddr()); +#endif + + VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); +} + +static bool can_use_layer(std::string_view layer_want) { + for (const auto& layer : vk::enumerateInstanceLayerProperties()) { + if (static_cast(layer.layerName) == layer_want) { + return true; + } + } + + return false; +} + +static bool can_use_validation(void) { + return can_use_layer("VK_LAYER_KHRONOS_validation"); +} + +VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* /*pUserData*/ +) { + spdlog::level::level_enum level{}; + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + level = spdlog::level::level_enum::debug; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + level = spdlog::level::level_enum::info; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + level = spdlog::level::level_enum::warn; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + level = spdlog::level::level_enum::err; + break; + default: + level = spdlog::level::level_enum::critical; // what ever + } + + switch (messageType) { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: + spdlog::get("VulkanGeneral")->log(level, "{}", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: + spdlog::get("VulkanValidation")->log(level, "{}", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: + spdlog::get("VulkanPerformance")->log(level, "{}", pCallbackData->pMessage); + break; + } + + return VK_FALSE; +} + +static const vk::DebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info{ + {}, + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError + | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning + | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo + | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose, + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral + | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation + | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, + debug_callback +}; + +static vk::Instance create_instance( + const vk::ApplicationInfo& app_info, + std::vector extensions = {}, + std::vector layers = {} +) { + // for debugging + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + // Get the required extension count + unsigned int count; + if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr) != SDL_TRUE) { + return nullptr; + } + + size_t additional_extension_count = extensions.size(); + extensions.resize(additional_extension_count + count); + + // fill sdl extensions + if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, extensions.data() + additional_extension_count) != SDL_TRUE) { + return nullptr; + } + + // Now we can make the Vulkan instance + vk::StructureChain c{ + vk::InstanceCreateInfo{ + {}, + &app_info, + static_cast(layers.size()), + layers.data(), + static_cast(extensions.size()), + extensions.data() + }, + debug_utils_messenger_create_info + }; + + vk::Instance instance = vk::createInstance(c.get(), nullptr); + + // initialize function pointers for instance + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); + + return instance; +} + +namespace MM::Services { + +VulkanRenderer::VulkanRenderer(void) { +#if 0 + MM::Logger::initSectionLogger("VulkanGeneral"); + // way too noisy otherwise + spdlog::get("VulkanGeneral")->set_level(spdlog::level::level_enum::warn); +#else + // or just dont log to stdio? + MM::Logger::initSectionLogger("VulkanGeneral", false); +#endif + MM::Logger::initSectionLogger("VulkanValidation"); + MM::Logger::initSectionLogger("VulkanPerformance"); + + SPDLOG_INFO("constructed VulkanRenderer"); +} + +VulkanRenderer::~VulkanRenderer(void) { +} + +bool VulkanRenderer::enable(Engine& engine, std::vector& task_array) { + assert(!VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceLayerProperties); + setup_dispacher(); + assert(VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceLayerProperties); + + // create vulkan instance + const vk::ApplicationInfo app_info { + "app_name", + VK_MAKE_VERSION(1, 0, 0), // app version + "MushMachine", + // TODO: engine version macro or something + VK_MAKE_VERSION(0, 8, 0), // engine version + VK_API_VERSION_1_1 + }; + + // TODO: make validation layer conditional + std::vector layers{}; + if (can_use_validation()) { + layers.push_back("VK_LAYER_KHRONOS_validation"); + SPDLOG_INFO("ENABLED validation layer"); + } else { + SPDLOG_INFO("validation layer NOT AVAILABLE!"); + } + + vk::Instance instance = create_instance( + app_info, + {}, + layers + ); + _instance = instance; + + _debug_messenger = instance.createDebugUtilsMessengerEXT(debug_utils_messenger_create_info); + + return true; +} + +void VulkanRenderer::disable(Engine&) { + // cleanup + if (_device) { + vk::Device device{_device}; + + for (const auto& fb : _swapchain_framebuffers) { + device.destroy(fb); + } + for (const auto& img_view : _swapchain_image_views) { + device.destroy(img_view); + } + device.destroy(_swapchain); + device.destroy(); + } + + vk::Instance instance{_instance}; + instance.destroy(_surface); + instance.destroy(_debug_messenger); + instance.destroy(); +} + +bool VulkanRenderer::createDevice(Engine& engine) { + // the surface for the window (not device dependent) + if (SDL_Vulkan_CreateSurface(engine.getService().win, _instance, &_surface) != SDL_TRUE) { + SPDLOG_ERROR("creating vulkan surface from window. (is the SDL_WINDOW_VULKAN flag set?)"); + return false; + } + + // convenience hpp wrapper + assert(_surface); + vk::SurfaceKHR surface{_surface}; + assert(_instance); + vk::Instance instance{_instance}; + + auto physical_devices = instance.enumeratePhysicalDevices(); + if (physical_devices.empty()) { + SPDLOG_ERROR("no physical vulkan devices found"); + return false; + } + + // list devices + for (const auto& ph_device : physical_devices) { + auto props = ph_device.getProperties(); + SPDLOG_INFO( + "found device: [{}] ({}) '{}'", + props.deviceID, + (props.deviceType == vk::PhysicalDeviceType::eDiscreteGpu ? "discrete" : "other"), + props.deviceName + ); + } + + auto& selected_phys_dev = physical_devices.front(); + _physical_device = selected_phys_dev; + + for (const auto& fam_props : selected_phys_dev.getQueueFamilyProperties()) { + auto test_bit = [](const auto& flags, const auto& bit) -> bool { + return (flags & bit) == bit; + }; + SPDLOG_INFO( + "QueueFamily: queueCount:{} graphics:{} compute:{} transfer:{}", + fam_props.queueCount, + test_bit(fam_props.queueFlags, vk::QueueFlagBits::eGraphics) ? "true" : "false", + test_bit(fam_props.queueFlags, vk::QueueFlagBits::eCompute) ? "true" : "false", + test_bit(fam_props.queueFlags, vk::QueueFlagBits::eTransfer) ? "true" : "false" + ); + } + + uint32_t queue_fam_index = 0; + + // test for support for swapchain + if (selected_phys_dev.getSurfaceSupportKHR(queue_fam_index, surface) != VK_TRUE) { + SPDLOG_ERROR("selected device does not support the surface"); + return false; + } + + const float queue_prio = 1.f; // hmmmm + vk::DeviceQueueCreateInfo graphics_queue_create_info { + {}, + queue_fam_index, // just pick the first one for now + 1, // count + &queue_prio + }; + + vk::PhysicalDeviceFeatures device_features { + }; + + // do i need this? + std::vector device_extentions{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + vk::DeviceCreateInfo device_create_info { + {}, + 1, + &graphics_queue_create_info, + // layers + 0, + nullptr, + // extensions + static_cast(device_extentions.size()), + device_extentions.data(), + &device_features + }; + vk::Device device = selected_phys_dev.createDevice(device_create_info, nullptr); + _device = device; + + // function pointer specialization for device + VULKAN_HPP_DEFAULT_DISPATCHER.init(device); + + //_present_queue = device.getQueue(0, 0); + // we assume it also does present + _graphics_queue = device.getQueue(0, 0); + + return true; +} + +bool VulkanRenderer::createSwapchain(Engine& engine) { + assert(_physical_device); + vk::PhysicalDevice physical_device {_physical_device}; + assert(_device); + vk::Device device {_device}; + + vk::SurfaceFormatKHR swap_surf_format { + vk::Format::eB8G8R8A8Srgb, + vk::ColorSpaceKHR::eSrgbNonlinear, + }; + { // select format + //for (const auto& format : selected_phys_dev.getSurfaceFormatsKHR(surface)) { + //if (format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { + //if (format.format == vk::Format::eB8G8R8A8Srgb) { + + //} + //} + //} + } + + vk::Extent2D surface_extent {}; + { + int w, h; + SDL_Vulkan_GetDrawableSize(engine.getService().win, &w, &h); + surface_extent.width = w; + surface_extent.height = h; + } + + auto phys_surf_caps = physical_device.getSurfaceCapabilitiesKHR(_surface); + + // flush all loggers + spdlog::apply_all([](const auto& logger) { logger->flush(); }); + + assert(VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateSwapchainKHR); + + // create the swapchain + _swapchain = device.createSwapchainKHR({ + {}, + _surface, + phys_surf_caps.minImageCount, + swap_surf_format.format, + swap_surf_format.colorSpace, + surface_extent, + 1, // imageArrayLayers + vk::ImageUsageFlagBits::eColorAttachment, + vk::SharingMode::eExclusive, + // TODO: fill in rest + }); + + { + _swapchain_images.clear(); + auto images = device.getSwapchainImagesKHR(_swapchain); + for (const auto& img : images) { + _swapchain_images.push_back(img); + } + } + SPDLOG_INFO("have {} swapchain images", _swapchain_images.size()); + + _swapchain_image_views.clear(); + for (const auto& img : _swapchain_images) { + _swapchain_image_views.push_back(device.createImageView({ + {}, + img, + vk::ImageViewType::e2D, + swap_surf_format.format, + {}, // comp mapping + { // subres + vk::ImageAspectFlagBits::eColor, + 0, + 1, + 0, + 1, + }, + })); + } + + // TODO: move + + _swapchain_framebuffers.clear(); + for (const auto& img_view : _swapchain_image_views) { + vk::ImageView tmp_img_view = img_view; + _swapchain_framebuffers.push_back(device.createFramebuffer({ + {}, + {}, // rend + 1, + &tmp_img_view, + surface_extent.width, + surface_extent.height, + 1 + })); + } + + return true; +} + +} // MM::Services + diff --git a/framework/vulkan_renderer/src/mm/services/vulkan_renderer.hpp b/framework/vulkan_renderer/src/mm/services/vulkan_renderer.hpp new file mode 100644 index 0000000..1282b6d --- /dev/null +++ b/framework/vulkan_renderer/src/mm/services/vulkan_renderer.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +// fwd vk stuff + +#define MM_VK_DEFINE_HANDLE(object) typedef struct object##_T* object; +// TODO: determain what we use +//#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + //#if (VK_USE_64_BIT_PTR_DEFINES==1) + #define MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; + //#else + //#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; + //#endif +//#endif + +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) +MM_VK_DEFINE_HANDLE(VkInstance) +MM_VK_DEFINE_HANDLE(VkPhysicalDevice) +MM_VK_DEFINE_HANDLE(VkDevice) +MM_VK_DEFINE_HANDLE(VkQueue) +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) + +// extensions +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +MM_VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) + +namespace MM::Services { + +class VulkanRenderer : public Service { + private: + // lets use the c-api types + VkInstance _instance{}; + VkDebugUtilsMessengerEXT _debug_messenger{}; + + VkSurfaceKHR _surface{}; + + VkPhysicalDevice _physical_device{}; + VkDevice _device{}; + + VkQueue _graphics_queue{}; + //VkQueue _present_queue{}; + + VkSwapchainKHR _swapchain{}; + std::vector _swapchain_images{}; + std::vector _swapchain_image_views{}; + std::vector _swapchain_framebuffers{}; + + public: + VulkanRenderer(void); + ~VulkanRenderer(void); + + bool enable(Engine& engine, std::vector& task_array) override; + void disable(Engine&) override; + + const char* name(void) override { return "VulkanRenderer"; } + + public: + bool createDevice(Engine& engine); + + bool createSwapchain(Engine& engine); +}; + +} // MM::Services + diff --git a/framework/vulkan_renderer/test/CMakeLists.txt b/framework/vulkan_renderer/test/CMakeLists.txt new file mode 100644 index 0000000..cb88cf3 --- /dev/null +++ b/framework/vulkan_renderer/test/CMakeLists.txt @@ -0,0 +1,13 @@ +add_executable(vulkan_renderer_test + ./vulkan_test.cpp +) + +target_include_directories(vulkan_renderer_test PRIVATE ".") + +target_link_libraries(vulkan_renderer_test + vulkan_renderer + gtest_main +) + +add_test(NAME vulkan_renderer_test COMMAND vulkan_renderer_test) + diff --git a/framework/vulkan_renderer/test/vulkan_test.cpp b/framework/vulkan_renderer/test/vulkan_test.cpp new file mode 100644 index 0000000..12daa7b --- /dev/null +++ b/framework/vulkan_renderer/test/vulkan_test.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include +#include + +#include + +TEST(sdl_service, window_vulkan) { + MM::Engine engine; + + engine.addService(); + ASSERT_TRUE(engine.enableService()); + + auto* sdl_ss_ptr = engine.tryService(); + ASSERT_NE(sdl_ss_ptr, nullptr); + + // create window + ASSERT_EQ(sdl_ss_ptr->win, nullptr); + ASSERT_TRUE(sdl_ss_ptr->createWindow("test vulkan window", 800, 600, SDL_WINDOW_VULKAN)); + ASSERT_NE(sdl_ss_ptr->win, nullptr); + + engine.addService(); + ASSERT_TRUE(engine.enableService()); + + auto& vk_rend = engine.getService(); + ASSERT_TRUE(vk_rend.createDevice(engine)); + ASSERT_TRUE(vk_rend.createSwapchain(engine)); + + engine.run(); + + engine.disableService(); + engine.disableService(); + + ASSERT_EQ(sdl_ss_ptr->win, nullptr); +} +