diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 7df034a..6b9f60a 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -39,6 +39,7 @@ if(NOT MM_HEADLESS) endif() add_subdirectory("Vulkan-Headers") + target_compile_definitions(Vulkan-Headers INTERFACE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1) # stb utilies add_subdirectory("stb") diff --git a/framework/sdl_service/test/vulkan_test.cpp b/framework/sdl_service/test/vulkan_test.cpp index 0db9b91..44ee063 100644 --- a/framework/sdl_service/test/vulkan_test.cpp +++ b/framework/sdl_service/test/vulkan_test.cpp @@ -4,11 +4,13 @@ #include #include -#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #include #include // mf #include +#include "spdlog/spdlog.h" +#include "vulkan/vulkan_core.h" +#include "vulkan/vulkan_handles.hpp" #include @@ -16,17 +18,19 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE +// create a dispatcher, based on additional vkDevice/vkGetDeviceProcAddr void setup_dispacher(void) { - // TODO: investigate why - // TODO: use SDL? - vk::DynamicLoader dl; +#if 1 + // investigate why this stopped working + static vk::DynamicLoader dl; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); - VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); -} +#else + auto load_res = SDL_Vulkan_LoadLibrary(nullptr); + assert(load_res == 0); + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = reinterpret_cast(SDL_Vulkan_GetVkGetInstanceProcAddr()); +#endif -void setup_instance_dispatcher(const vk::Instance& instance) { - // initialize function pointers for instance - VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); + VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); } bool can_use_layer(std::string_view layer_want) { @@ -131,31 +135,253 @@ vk::Instance create_instance( vk::Instance instance = vk::createInstance(c.get(), nullptr); - setup_instance_dispatcher(instance); + // initialize function pointers for instance + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); return instance; } -void setup_device_dispatcher(const vk::Device& device) { - // function pointer specialization for device - VULKAN_HPP_DEFAULT_DISPATCHER.init(device); -} +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{}; + + 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}; + 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 + }); + + auto images = device.getSwapchainImagesKHR(_swapchain); + SPDLOG_INFO("have {} swapchain images", images.size()); + + return true; + } +}; + +} // MM::Services TEST(sdl_service, window_vulkan) { MM::Engine engine; - { // setup vulkan loggers -#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"); - } engine.addService(); ASSERT_TRUE(engine.enableService()); @@ -163,118 +389,21 @@ TEST(sdl_service, window_vulkan) { auto* sdl_ss_ptr = engine.tryService(); ASSERT_NE(sdl_ss_ptr, nullptr); - setup_dispacher(); - // 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); - // create vulkan instance + engine.addService(); + ASSERT_TRUE(engine.enableService()); - 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 - }; + auto& vk_rend = engine.getService(); + ASSERT_TRUE(vk_rend.createDevice(engine)); + ASSERT_TRUE(vk_rend.createSwapchain(engine)); - // 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 - ); - ASSERT_NE(static_cast(instance), nullptr); - - auto debug_messenger = instance.createDebugUtilsMessengerEXT(debug_utils_messenger_create_info); - - // the surface for the window (no device dependent?) - VkSurfaceKHR surface; - ASSERT_TRUE( - SDL_Vulkan_CreateSurface( - sdl_ss_ptr->win, - instance, - &surface - ) - ); - - // create a dispatcher, based on additional vkDevice/vkGetDeviceProcAddr - auto physicalDevices = instance.enumeratePhysicalDevices(); - ASSERT_TRUE(!physicalDevices.empty()); // make test fail on unsupported machines - - // list devices - for (const auto& ph_device : physicalDevices) { - auto props = ph_device.getProperties(); - SPDLOG_INFO( - "found device: [{}] ({}) '{}'", - props.deviceID, - (props.deviceType == vk::PhysicalDeviceType::eDiscreteGpu ? "discrete" : "other"), - props.deviceName - ); - } - - auto& selected_phys_dev = physicalDevices.front(); - 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" - ); - } - - const float queue_prio = 1.f; // hmmmm - vk::DeviceQueueCreateInfo graphics_queue_create_info { - {}, - 0, // just pick the first one for now - 1, // count - &queue_prio - }; - - vk::PhysicalDeviceFeatures device_features { - }; - - vk::DeviceCreateInfo device_create_info { - {}, - 1, - &graphics_queue_create_info, - // layers - 0, - nullptr, - // extensions - 0, - nullptr, - &device_features - }; - vk::Device device = selected_phys_dev.createDevice(device_create_info, nullptr); - ASSERT_NE(static_cast(device), nullptr); - - setup_device_dispatcher(device); - - vk::Queue graphics_queue = device.getQueue(0, 0); - - // cleanup - device.destroy(); - - instance.destroy(surface); - instance.destroy(debug_messenger); - instance.destroy(); + engine.run(); + engine.disableService(); engine.disableService(); ASSERT_EQ(sdl_ss_ptr->win, nullptr);