From e55808b32f2b2eb0fc6ea42d425858ea6060f097 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Wed, 1 Jun 2022 14:41:35 +0200 Subject: [PATCH] next try, this time with vk headers and hpp --- .gitmodules | 4 + external/CMakeLists.txt | 2 + external/Vulkan-Headers | 1 + framework/sdl_service/CMakeLists.txt | 2 + framework/sdl_service/test/CMakeLists.txt | 5 +- framework/sdl_service/test/vulkan_test.cpp | 282 +++++++++++++++++++++ 6 files changed, 295 insertions(+), 1 deletion(-) create mode 160000 external/Vulkan-Headers create mode 100644 framework/sdl_service/test/vulkan_test.cpp diff --git a/.gitmodules b/.gitmodules index f476259..9957619 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,7 @@ path = external/physfs/physfs url = https://github.com/icculus/physfs.git branch = main +[submodule "external/Vulkan-Headers"] + path = external/Vulkan-Headers + url = https://github.com/KhronosGroup/Vulkan-Headers.git + shallow = true diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index aa4c6e9..7df034a 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -38,6 +38,8 @@ if(NOT MM_HEADLESS) add_subdirectory("glad-debug") endif() + add_subdirectory("Vulkan-Headers") + # stb utilies add_subdirectory("stb") diff --git a/external/Vulkan-Headers b/external/Vulkan-Headers new file mode 160000 index 0000000..245d25c --- /dev/null +++ b/external/Vulkan-Headers @@ -0,0 +1 @@ +Subproject commit 245d25ce8c3337919dc7916d0e62e31a0d8748ab diff --git a/framework/sdl_service/CMakeLists.txt b/framework/sdl_service/CMakeLists.txt index 4ca7020..f383f5b 100644 --- a/framework/sdl_service/CMakeLists.txt +++ b/framework/sdl_service/CMakeLists.txt @@ -40,6 +40,8 @@ else() target_link_libraries(sdl_service glad) endif() +target_link_libraries(sdl_service Vulkan::Headers) + if(VCPKG_TARGET_TRIPLET) target_link_libraries(sdl_service SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static) endif() diff --git a/framework/sdl_service/test/CMakeLists.txt b/framework/sdl_service/test/CMakeLists.txt index d3f6046..a6fe4a4 100644 --- a/framework/sdl_service/test/CMakeLists.txt +++ b/framework/sdl_service/test/CMakeLists.txt @@ -1,4 +1,7 @@ -add_executable(sdl_service_test start_test.cpp) +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 new file mode 100644 index 0000000..0db9b91 --- /dev/null +++ b/framework/sdl_service/test/vulkan_test.cpp @@ -0,0 +1,282 @@ +#include + +#include +#include +#include + +#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 +#include + +#include // mf +#include + +#include + +#include + +VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE + +void setup_dispacher(void) { + // TODO: investigate why + // TODO: use SDL? + vk::DynamicLoader dl; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); + VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); +} + +void setup_instance_dispatcher(const vk::Instance& instance) { + // initialize function pointers for instance + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); +} + +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); + + setup_instance_dispatcher(instance); + + return instance; +} + +void setup_device_dispatcher(const vk::Device& device) { + // function pointer specialization for device + VULKAN_HPP_DEFAULT_DISPATCHER.init(device); +} + + +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()); + + 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 + + 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 + ); + 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.disableService(); + + ASSERT_EQ(sdl_ss_ptr->win, nullptr); +} +