From 7c86f188c93973722d45911efbc553bf91844f13 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Mon, 20 Mar 2023 13:52:42 +0900 Subject: [PATCH 01/13] Fix : Added prerequisite checks for using VK_Layer_Validation --- intern/ghost/intern/GHOST_ContextVK.cpp | 32 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 5ac0e4727e8..961f3b9f0e5 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -23,7 +23,7 @@ #include #include #include - +#include /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ #define STRICT_REQUIREMENTS 1 @@ -79,7 +79,31 @@ static const char *vulkan_error_as_string(VkResult result) return "Unknown Error"; } } - +static bool is_vklayer_exist() +{ + const char *ev_val = getenv("VK_LAYER_PATH"); + bool exists = false; + if (ev_val != nullptr) { + std::string _json = std::string(ev_val) + "/VkLayer_khronos_validation.json"; + std::ifstream infile(_json.c_str()); + exists = infile.good(); + } + if (!exists) { +#if defined(_WIN32) + fprintf(stderr, + "Warning: Layer requested, we are trying to use the VulkanValidationLayer explicitly. " + "\n Set the path ..VulkanSDK\1.2.198.1\Bin of VulkanSDK (version1.2.198.1) to the " + "environment variable VK_LAYER_PATH.\nSee more details " + "https://vulkan.lunarg.com/doc/sdk/1.3.239.0/windows/layer_configuration.html."); +#elif !defined(__APPLE__) + fprintf(stderr, + "Warning: Layer requested, we are trying to use the VulkanValidationLayer explicitly. " + "\n Set the path ..vulkan/explicit_layer.d of VulkanSDK (version1.2.198.1) to the " + "environment variable VK_LAYER_PATH.\n"See more details https://vulkan.lunarg.com/doc/sdk/1.3.239.0/linux/layer_configuration.html."); +#endif + } + return exists; +} #define __STR(A) "" #A #define VK_CHECK(__expression) \ do { \ @@ -864,7 +888,9 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vector layers_enabled; if (m_debug) { - enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); + if (is_vklayer_exist()) { + enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); + } } vector extensions_device; -- 2.30.2 From b2e7ef170809dbd74d8cd861c97dc04280caeaff Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Tue, 21 Mar 2023 23:59:45 +0900 Subject: [PATCH 02/13] Fix:Refactoring ContextVK --- intern/ghost/intern/GHOST_ContextVK.cpp | 41 ++++++++++++++----------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 961f3b9f0e5..3f89238074f 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -79,30 +79,35 @@ static const char *vulkan_error_as_string(VkResult result) return "Unknown Error"; } } -static bool is_vklayer_exist() +static bool is_vklayer_exist(const char* vk_extension_config) { const char *ev_val = getenv("VK_LAYER_PATH"); bool exists = false; if (ev_val != nullptr) { - std::string _json = std::string(ev_val) + "/VkLayer_khronos_validation.json"; - std::ifstream infile(_json.c_str()); - exists = infile.good(); + const size_t size_max = strlen(ev_val) + strlen(vk_extension_config) + 2; + char *filename = (char *)malloc(size_max); + memset(filename, 0, size_max); + strcpy(filename, ev_val); + strcat(filename, "/"); + strcat(filename, vk_extension_config); + struct stat buffer; + exists = (stat(filename, &buffer) == 0); + free(filename); } - if (!exists) { + if (exists) { + return exists; + } + #if defined(_WIN32) - fprintf(stderr, - "Warning: Layer requested, we are trying to use the VulkanValidationLayer explicitly. " - "\n Set the path ..VulkanSDK\1.2.198.1\Bin of VulkanSDK (version1.2.198.1) to the " - "environment variable VK_LAYER_PATH.\nSee more details " - "https://vulkan.lunarg.com/doc/sdk/1.3.239.0/windows/layer_configuration.html."); -#elif !defined(__APPLE__) - fprintf(stderr, - "Warning: Layer requested, we are trying to use the VulkanValidationLayer explicitly. " - "\n Set the path ..vulkan/explicit_layer.d of VulkanSDK (version1.2.198.1) to the " - "environment variable VK_LAYER_PATH.\n"See more details https://vulkan.lunarg.com/doc/sdk/1.3.239.0/linux/layer_configuration.html."); + printf("Warning: VK_LAYER_KHRONOS_validation is deactivated.\nSet `..VulkanSDK/1.2.198.1/Bin` of VulkanSDK (version1.2.198.1) to " + "VK_LAYER_PATH."); +#else + printf("Warning: VK_LAYER_KHRONOS_validation is deactivated.\nSet `..vulkan/explicit_layer.d` of VulkanSDK (version1.2.198.1) to " + "VK_LAYER_PATH."); #endif - } - return exists; + + return false; + } #define __STR(A) "" #A #define VK_CHECK(__expression) \ @@ -888,7 +893,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vector layers_enabled; if (m_debug) { - if (is_vklayer_exist()) { + if (is_vklayer_exist("VkLayer_khronos_validation.json")) { enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); } } -- 2.30.2 From 15bc51a7e74185b48f238bbf88f199ec899c4102 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Tue, 21 Mar 2023 15:31:35 +0900 Subject: [PATCH 03/13] Fix:Include sys/stat.h --- intern/ghost/intern/GHOST_ContextVK.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 3f89238074f..1fd044c719c 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include "sys/stat.h" /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ #define STRICT_REQUIREMENTS 1 -- 2.30.2 From c0f2c1d00da56adb259f9de57f16215a476a0b4f Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 01:18:51 +0900 Subject: [PATCH 04/13] FIX: Suppress warnings. --- intern/ghost/intern/GHOST_ContextVK.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 1fd044c719c..b6a38f056de 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -82,6 +82,9 @@ static const char *vulkan_error_as_string(VkResult result) static bool is_vklayer_exist(const char* vk_extension_config) { const char *ev_val = getenv("VK_LAYER_PATH"); + if (ev_val == nullptr) { + return false; + } bool exists = false; if (ev_val != nullptr) { const size_t size_max = strlen(ev_val) + strlen(vk_extension_config) + 2; @@ -94,20 +97,8 @@ static bool is_vklayer_exist(const char* vk_extension_config) exists = (stat(filename, &buffer) == 0); free(filename); } - if (exists) { - return exists; - } - -#if defined(_WIN32) - printf("Warning: VK_LAYER_KHRONOS_validation is deactivated.\nSet `..VulkanSDK/1.2.198.1/Bin` of VulkanSDK (version1.2.198.1) to " - "VK_LAYER_PATH."); -#else - printf("Warning: VK_LAYER_KHRONOS_validation is deactivated.\nSet `..vulkan/explicit_layer.d` of VulkanSDK (version1.2.198.1) to " - "VK_LAYER_PATH."); -#endif - - return false; + return exists; } #define __STR(A) "" #A #define VK_CHECK(__expression) \ -- 2.30.2 From 8e6e0bfc9754b7fb0414a61792d0246e4fc94e20 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 01:48:42 +0900 Subject: [PATCH 05/13] FIX:Suppress warnings2 --- intern/ghost/intern/GHOST_ContextVK.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index b6a38f056de..0af83b6f3be 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -85,19 +85,15 @@ static bool is_vklayer_exist(const char* vk_extension_config) if (ev_val == nullptr) { return false; } - bool exists = false; - if (ev_val != nullptr) { - const size_t size_max = strlen(ev_val) + strlen(vk_extension_config) + 2; - char *filename = (char *)malloc(size_max); - memset(filename, 0, size_max); - strcpy(filename, ev_val); - strcat(filename, "/"); - strcat(filename, vk_extension_config); - struct stat buffer; - exists = (stat(filename, &buffer) == 0); - free(filename); - } - + const size_t size_max = strlen(ev_val) + strlen(vk_extension_config) + 2; + char *filename = (char *)malloc(size_max); + memset(filename, 0, size_max); + strcpy(filename, ev_val); + strcat(filename, "/"); + strcat(filename, vk_extension_config); + struct stat buffer; + bool exists = (stat(filename, &buffer) == 0); + free(filename); return exists; } #define __STR(A) "" #A -- 2.30.2 From bfb5aede139bad421570f4b09f265f86b301205b Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 21:52:30 +0900 Subject: [PATCH 06/13] FIX:Refactoring about Vulkan Layer Validation --- intern/ghost/intern/GHOST_ContextVK.cpp | 55 ++++++++++++++++--------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 0af83b6f3be..615f7b59138 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -23,7 +23,8 @@ #include #include #include -#include "sys/stat.h" +#include +#include /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ #define STRICT_REQUIREMENTS 1 @@ -79,21 +80,18 @@ static const char *vulkan_error_as_string(VkResult result) return "Unknown Error"; } } -static bool is_vklayer_exist(const char* vk_extension_config) +static bool vklayer_config_exist(const char* vk_extension_config) { const char *ev_val = getenv("VK_LAYER_PATH"); if (ev_val == nullptr) { return false; } - const size_t size_max = strlen(ev_val) + strlen(vk_extension_config) + 2; - char *filename = (char *)malloc(size_max); - memset(filename, 0, size_max); - strcpy(filename, ev_val); - strcat(filename, "/"); - strcat(filename, vk_extension_config); + std::stringstream filename; + filename << ev_val; + filename << "/"; + filename << vk_extension_config; struct stat buffer; - bool exists = (stat(filename, &buffer) == 0); - free(filename); + bool exists = (stat(filename.str().c_str(), &buffer) == 0); return exists; } #define __STR(A) "" #A @@ -118,6 +116,10 @@ static bool is_vklayer_exist(const char* vk_extension_config) /* Triple buffering. */ const int MAX_FRAMES_IN_FLIGHT = 2; +enum class VkDynamicLibraryType : std::uint8_t { + VkValidationLayer = 0, + VkDynamicLibraryAll +}; GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual, #ifdef _WIN32 @@ -417,16 +419,31 @@ static bool checkLayerSupport(vector &layers_available, const static void enableLayer(vector &layers_available, vector &layers_enabled, - const char *layer_name, - const bool debug) + VkDynamicLibraryType library_type, + const bool warning) { - if (checkLayerSupport(layers_available, layer_name)) { - layers_enabled.push_back(layer_name); + std::string layer_name = ""; + std::string config_name = ""; + switch (library_type) { + case VkDynamicLibraryType::VkValidationLayer: + layer_name = "VK_LAYER_KHRONOS_validation"; + config_name = "VkLayer_khronos_validation.json"; + break; + case VkDynamicLibraryType::VkDynamicLibraryAll: + default: + return; + } + + if (checkLayerSupport(layers_available, "VK_LAYER_KHRONOS_validation") && + vklayer_config_exist("VkLayer_khronos_validation.json")) { + layers_enabled.push_back("VK_LAYER_KHRONOS_validation"); } - else if (debug) { - fprintf( - stderr, "Warning: Layer requested, but not supported by the platform. [%s]\n", layer_name); + else if (warning) { + fprintf(stderr, + "Warning: Layer requested, but not supported by the platform. [%s]\n", + layer_name.c_str()); } + } static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) @@ -880,9 +897,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vector layers_enabled; if (m_debug) { - if (is_vklayer_exist("VkLayer_khronos_validation.json")) { - enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); - } + enableLayer(layers_available, layers_enabled, VkDynamicLibraryType::VkValidationLayer,true); } vector extensions_device; -- 2.30.2 From b0d5f6dfd9d99951ccaab13c309553f73001522d Mon Sep 17 00:00:00 2001 From: vnapdv Date: Fri, 24 Mar 2023 22:40:21 +0900 Subject: [PATCH 07/13] FIX:Refactoring Vulkan Layer Validation2 --- intern/ghost/intern/GHOST_ContextVK.cpp | 2116 +++++++++++------------ 1 file changed, 1051 insertions(+), 1065 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 615f7b59138..6aca911ccb9 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -1,1065 +1,1051 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup GHOST - */ - -#include "GHOST_ContextVK.h" - -#ifdef _WIN32 -# include -#elif defined(__APPLE__) -# include -#else /* X11 */ -# include -# ifdef WITH_GHOST_WAYLAND -# include -# endif -#endif - -#include - -#include -#include -#include -#include -#include -#include -/* Set to 0 to allow devices that do not have the required features. - * This allows development on OSX until we really needs these features. */ -#define STRICT_REQUIREMENTS 1 - -using namespace std; - -static const char *vulkan_error_as_string(VkResult result) -{ -#define FORMAT_ERROR(X) \ - case X: { \ - return "" #X; \ - } - - switch (result) { - FORMAT_ERROR(VK_NOT_READY); - FORMAT_ERROR(VK_TIMEOUT); - FORMAT_ERROR(VK_EVENT_SET); - FORMAT_ERROR(VK_EVENT_RESET); - FORMAT_ERROR(VK_INCOMPLETE); - FORMAT_ERROR(VK_ERROR_OUT_OF_HOST_MEMORY); - FORMAT_ERROR(VK_ERROR_OUT_OF_DEVICE_MEMORY); - FORMAT_ERROR(VK_ERROR_INITIALIZATION_FAILED); - FORMAT_ERROR(VK_ERROR_DEVICE_LOST); - FORMAT_ERROR(VK_ERROR_MEMORY_MAP_FAILED); - FORMAT_ERROR(VK_ERROR_LAYER_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_EXTENSION_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_FEATURE_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DRIVER); - FORMAT_ERROR(VK_ERROR_TOO_MANY_OBJECTS); - FORMAT_ERROR(VK_ERROR_FORMAT_NOT_SUPPORTED); - FORMAT_ERROR(VK_ERROR_FRAGMENTED_POOL); - FORMAT_ERROR(VK_ERROR_UNKNOWN); - FORMAT_ERROR(VK_ERROR_OUT_OF_POOL_MEMORY); - FORMAT_ERROR(VK_ERROR_INVALID_EXTERNAL_HANDLE); - FORMAT_ERROR(VK_ERROR_FRAGMENTATION); - FORMAT_ERROR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); - FORMAT_ERROR(VK_ERROR_SURFACE_LOST_KHR); - FORMAT_ERROR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); - FORMAT_ERROR(VK_SUBOPTIMAL_KHR); - FORMAT_ERROR(VK_ERROR_OUT_OF_DATE_KHR); - FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); - FORMAT_ERROR(VK_ERROR_VALIDATION_FAILED_EXT); - FORMAT_ERROR(VK_ERROR_INVALID_SHADER_NV); - FORMAT_ERROR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); - FORMAT_ERROR(VK_ERROR_NOT_PERMITTED_EXT); - FORMAT_ERROR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); - FORMAT_ERROR(VK_THREAD_IDLE_KHR); - FORMAT_ERROR(VK_THREAD_DONE_KHR); - FORMAT_ERROR(VK_OPERATION_DEFERRED_KHR); - FORMAT_ERROR(VK_OPERATION_NOT_DEFERRED_KHR); - FORMAT_ERROR(VK_PIPELINE_COMPILE_REQUIRED_EXT); - default: - return "Unknown Error"; - } -} -static bool vklayer_config_exist(const char* vk_extension_config) -{ - const char *ev_val = getenv("VK_LAYER_PATH"); - if (ev_val == nullptr) { - return false; - } - std::stringstream filename; - filename << ev_val; - filename << "/"; - filename << vk_extension_config; - struct stat buffer; - bool exists = (stat(filename.str().c_str(), &buffer) == 0); - return exists; -} -#define __STR(A) "" #A -#define VK_CHECK(__expression) \ - do { \ - VkResult r = (__expression); \ - if (r != VK_SUCCESS) { \ - fprintf(stderr, \ - "Vulkan Error : %s:%d : %s failled with %s\n", \ - __FILE__, \ - __LINE__, \ - __STR(__expression), \ - vulkan_error_as_string(r)); \ - return GHOST_kFailure; \ - } \ - } while (0) - -#define DEBUG_PRINTF(...) \ - if (m_debug) { \ - printf(__VA_ARGS__); \ - } - -/* Triple buffering. */ -const int MAX_FRAMES_IN_FLIGHT = 2; -enum class VkDynamicLibraryType : std::uint8_t { - VkValidationLayer = 0, - VkDynamicLibraryAll -}; - -GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual, -#ifdef _WIN32 - HWND hwnd, -#elif defined(__APPLE__) - CAMetalLayer *metal_layer, -#else - GHOST_TVulkanPlatformType platform, - /* X11 */ - Window window, - Display *display, - /* Wayland */ - wl_surface *wayland_surface, - wl_display *wayland_display, -#endif - int contextMajorVersion, - int contextMinorVersion, - int debug) - : GHOST_Context(stereoVisual), -#ifdef _WIN32 - m_hwnd(hwnd), -#elif defined(__APPLE__) - m_metal_layer(metal_layer), -#else - m_platform(platform), - /* X11 */ - m_display(display), - m_window(window), - /* Wayland */ - m_wayland_surface(wayland_surface), - m_wayland_display(wayland_display), -#endif - m_context_major_version(contextMajorVersion), - m_context_minor_version(contextMinorVersion), - m_debug(debug), - m_instance(VK_NULL_HANDLE), - m_physical_device(VK_NULL_HANDLE), - m_device(VK_NULL_HANDLE), - m_command_pool(VK_NULL_HANDLE), - m_surface(VK_NULL_HANDLE), - m_swapchain(VK_NULL_HANDLE), - m_render_pass(VK_NULL_HANDLE) -{ -} - -GHOST_ContextVK::~GHOST_ContextVK() -{ - if (m_device) { - vkDeviceWaitIdle(m_device); - } - - destroySwapchain(); - - if (m_command_pool != VK_NULL_HANDLE) { - vkDestroyCommandPool(m_device, m_command_pool, NULL); - } - if (m_device != VK_NULL_HANDLE) { - vkDestroyDevice(m_device, NULL); - } - if (m_surface != VK_NULL_HANDLE) { - vkDestroySurfaceKHR(m_instance, m_surface, NULL); - } - if (m_instance != VK_NULL_HANDLE) { - vkDestroyInstance(m_instance, NULL); - } -} - -GHOST_TSuccess GHOST_ContextVK::destroySwapchain() -{ - if (m_device != VK_NULL_HANDLE) { - vkDeviceWaitIdle(m_device); - } - - m_in_flight_images.resize(0); - - for (auto semaphore : m_image_available_semaphores) { - vkDestroySemaphore(m_device, semaphore, NULL); - } - for (auto semaphore : m_render_finished_semaphores) { - vkDestroySemaphore(m_device, semaphore, NULL); - } - for (auto fence : m_in_flight_fences) { - vkDestroyFence(m_device, fence, NULL); - } - for (auto framebuffer : m_swapchain_framebuffers) { - vkDestroyFramebuffer(m_device, framebuffer, NULL); - } - if (m_render_pass != VK_NULL_HANDLE) { - vkDestroyRenderPass(m_device, m_render_pass, NULL); - } - for (auto command_buffer : m_command_buffers) { - vkFreeCommandBuffers(m_device, m_command_pool, 1, &command_buffer); - } - for (auto imageView : m_swapchain_image_views) { - vkDestroyImageView(m_device, imageView, NULL); - } - if (m_swapchain != VK_NULL_HANDLE) { - vkDestroySwapchainKHR(m_device, m_swapchain, NULL); - } - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::swapBuffers() -{ - if (m_swapchain == VK_NULL_HANDLE) { - return GHOST_kFailure; - } - - vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, UINT64_MAX); - - VkResult result = vkAcquireNextImageKHR(m_device, - m_swapchain, - UINT64_MAX, - m_image_available_semaphores[m_currentFrame], - VK_NULL_HANDLE, - &m_currentImage); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ - destroySwapchain(); - createSwapchain(); - return GHOST_kSuccess; - } - else if (result != VK_SUCCESS) { - fprintf(stderr, - "Error: Failed to acquire swap chain image : %s\n", - vulkan_error_as_string(result)); - return GHOST_kFailure; - } - - /* Check if a previous frame is using this image (i.e. there is its fence to wait on) */ - if (m_in_flight_images[m_currentImage] != VK_NULL_HANDLE) { - vkWaitForFences(m_device, 1, &m_in_flight_images[m_currentImage], VK_TRUE, UINT64_MAX); - } - m_in_flight_images[m_currentImage] = m_in_flight_fences[m_currentFrame]; - - VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; - - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &m_image_available_semaphores[m_currentFrame]; - submit_info.pWaitDstStageMask = wait_stages; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &m_command_buffers[m_currentImage]; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &m_render_finished_semaphores[m_currentFrame]; - - vkResetFences(m_device, 1, &m_in_flight_fences[m_currentFrame]); - - VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, m_in_flight_fences[m_currentFrame])); - do { - result = vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, 10000); - } while (result == VK_TIMEOUT); - - VK_CHECK(vkQueueWaitIdle(m_graphic_queue)); - - VkPresentInfoKHR present_info = {}; - present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &m_render_finished_semaphores[m_currentFrame]; - present_info.swapchainCount = 1; - present_info.pSwapchains = &m_swapchain; - present_info.pImageIndices = &m_currentImage; - present_info.pResults = NULL; - - result = vkQueuePresentKHR(m_present_queue, &present_info); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ - destroySwapchain(); - createSwapchain(); - return GHOST_kSuccess; - } - else if (result != VK_SUCCESS) { - fprintf(stderr, - "Error: Failed to present swap chain image : %s\n", - vulkan_error_as_string(result)); - return GHOST_kFailure; - } - - m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer( - void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) -{ - if (m_swapchain == VK_NULL_HANDLE) { - return GHOST_kFailure; - } - *((VkImage *)image) = m_swapchain_images[m_currentImage]; - *((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage]; - *((VkRenderPass *)render_pass) = m_render_pass; - *((VkExtent2D *)extent) = m_render_extent; - *fb_id = m_swapchain_id * 10 + m_currentFrame; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance, - void *r_physical_device, - void *r_device, - uint32_t *r_graphic_queue_family, - void *r_queue) -{ - *((VkInstance *)r_instance) = m_instance; - *((VkPhysicalDevice *)r_physical_device) = m_physical_device; - *((VkDevice *)r_device) = m_device; - *r_graphic_queue_family = m_queue_family_graphic; - *((VkQueue *)r_queue) = m_graphic_queue; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer) -{ - if (m_command_buffers.empty()) { - return GHOST_kFailure; - } - - if (m_swapchain == VK_NULL_HANDLE) { - *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0]; - } - else { - *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage]; - } - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::activateDrawingContext() -{ - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::releaseDrawingContext() -{ - return GHOST_kSuccess; -} - -static vector getExtensionsAvailable() -{ - uint32_t extension_count = 0; - vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); - - vector extensions(extension_count); - vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions.data()); - - return extensions; -} - -static bool checkExtensionSupport(vector &extensions_available, - const char *extension_name) -{ - for (const auto &extension : extensions_available) { - if (strcmp(extension_name, extension.extensionName) == 0) { - return true; - } - } - return false; -} - -static void requireExtension(vector &extensions_available, - vector &extensions_enabled, - const char *extension_name) -{ - if (checkExtensionSupport(extensions_available, extension_name)) { - extensions_enabled.push_back(extension_name); - } - else { - fprintf(stderr, "Error: %s not found.\n", extension_name); - } -} - -static vector getLayersAvailable() -{ - uint32_t layer_count = 0; - vkEnumerateInstanceLayerProperties(&layer_count, NULL); - - vector layers(layer_count); - vkEnumerateInstanceLayerProperties(&layer_count, layers.data()); - - return layers; -} - -static bool checkLayerSupport(vector &layers_available, const char *layer_name) -{ - for (const auto &layer : layers_available) { - if (strcmp(layer_name, layer.layerName) == 0) { - return true; - } - } - return false; -} - -static void enableLayer(vector &layers_available, - vector &layers_enabled, - VkDynamicLibraryType library_type, - const bool warning) -{ - std::string layer_name = ""; - std::string config_name = ""; - switch (library_type) { - case VkDynamicLibraryType::VkValidationLayer: - layer_name = "VK_LAYER_KHRONOS_validation"; - config_name = "VkLayer_khronos_validation.json"; - break; - case VkDynamicLibraryType::VkDynamicLibraryAll: - default: - return; - } - - if (checkLayerSupport(layers_available, "VK_LAYER_KHRONOS_validation") && - vklayer_config_exist("VkLayer_khronos_validation.json")) { - layers_enabled.push_back("VK_LAYER_KHRONOS_validation"); - } - else if (warning) { - fprintf(stderr, - "Warning: Layer requested, but not supported by the platform. [%s]\n", - layer_name.c_str()); - } - -} - -static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) -{ - uint32_t ext_count; - vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL); - - vector available_exts(ext_count); - vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, available_exts.data()); - - for (const auto &extension_needed : required_exts) { - bool found = false; - for (const auto &extension : available_exts) { - if (strcmp(extension_needed, extension.extensionName) == 0) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -GHOST_TSuccess GHOST_ContextVK::pickPhysicalDevice(vector required_exts) -{ - m_physical_device = VK_NULL_HANDLE; - - uint32_t device_count = 0; - vkEnumeratePhysicalDevices(m_instance, &device_count, NULL); - - vector physical_devices(device_count); - vkEnumeratePhysicalDevices(m_instance, &device_count, physical_devices.data()); - - int best_device_score = -1; - for (const auto &physical_device : physical_devices) { - VkPhysicalDeviceProperties device_properties; - vkGetPhysicalDeviceProperties(physical_device, &device_properties); - - VkPhysicalDeviceFeatures features; - vkGetPhysicalDeviceFeatures(physical_device, &features); - - DEBUG_PRINTF("%s : \n", device_properties.deviceName); - - if (!device_extensions_support(physical_device, required_exts)) { - DEBUG_PRINTF(" - Device does not support required device extensions.\n"); - continue; - } - - if (m_surface != VK_NULL_HANDLE) { - uint32_t format_count; - vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, m_surface, &format_count, NULL); - - uint32_t present_count; - vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, m_surface, &present_count, NULL); - - /* For now anything will do. */ - if (format_count == 0 || present_count == 0) { - DEBUG_PRINTF(" - Device does not support presentation.\n"); - continue; - } - } - - if (!features.geometryShader) { - /* Needed for wide lines emulation and barycentric coords and a few others. */ - DEBUG_PRINTF(" - Device does not support geometryShader.\n"); - } - if (!features.dualSrcBlend) { - DEBUG_PRINTF(" - Device does not support dualSrcBlend.\n"); - } - if (!features.logicOp) { - /* Needed by UI. */ - DEBUG_PRINTF(" - Device does not support logicOp.\n"); - } - -#if STRICT_REQUIREMENTS - if (!features.geometryShader || !features.dualSrcBlend || !features.logicOp) { - continue; - } -#endif - - int device_score = 0; - switch (device_properties.deviceType) { - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: - device_score = 400; - break; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: - device_score = 300; - break; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: - device_score = 200; - break; - case VK_PHYSICAL_DEVICE_TYPE_CPU: - device_score = 100; - break; - default: - break; - } - if (device_score > best_device_score) { - m_physical_device = physical_device; - best_device_score = device_score; - } - DEBUG_PRINTF(" - Device suitable.\n"); - } - - if (m_physical_device == VK_NULL_HANDLE) { - fprintf(stderr, "Error: No suitable Vulkan Device found!\n"); - return GHOST_kFailure; - } - - return GHOST_kSuccess; -} - -static GHOST_TSuccess getGraphicQueueFamily(VkPhysicalDevice device, uint32_t *r_queue_index) -{ - uint32_t queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); - - vector queue_families(queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); - - *r_queue_index = 0; - for (const auto &queue_family : queue_families) { - /* Every vulkan implementation by spec must have one queue family that support both graphics - * and compute pipelines. We select this one; compute only queue family hints at async compute - * implementations. */ - if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) && - (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) { - return GHOST_kSuccess; - } - (*r_queue_index)++; - } - - fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n"); - return GHOST_kFailure; -} - -static GHOST_TSuccess getPresetQueueFamily(VkPhysicalDevice device, - VkSurfaceKHR surface, - uint32_t *r_queue_index) -{ - uint32_t queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); - - vector queue_families(queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); - - *r_queue_index = 0; - for (int i = 0; i < queue_family_count; i++) { - VkBool32 present_support = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, *r_queue_index, surface, &present_support); - - if (present_support) { - return GHOST_kSuccess; - } - (*r_queue_index)++; - } - - fprintf(stderr, "Couldn't find any Present queue family on selected device\n"); - return GHOST_kFailure; -} - -static GHOST_TSuccess create_render_pass(VkDevice device, - VkFormat format, - VkRenderPass *r_renderPass) -{ - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - - VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, NULL, r_renderPass)); - - return GHOST_kSuccess; -} - -static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, - VkSurfaceKHR surface, - VkPresentModeKHR *r_presentMode) -{ - uint32_t present_count; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL); - vector presents(present_count); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data()); - /* MAILBOX is the lowest latency V-Sync enabled mode so use it if available */ - for (auto present_mode : presents) { - if (present_mode == VK_PRESENT_MODE_FIFO_KHR) { - *r_presentMode = present_mode; - return GHOST_kSuccess; - } - } - /* FIFO present mode is always available. */ - for (auto present_mode : presents) { - if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { - *r_presentMode = present_mode; - return GHOST_kSuccess; - } - } - - fprintf(stderr, "Error: FIFO present mode is not supported by the swap chain!\n"); - - return GHOST_kFailure; -} - -GHOST_TSuccess GHOST_ContextVK::createCommandPools() -{ - VkCommandPoolCreateInfo poolInfo = {}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = m_queue_family_graphic; - - VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool)); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer() -{ - assert(m_command_pool != VK_NULL_HANDLE); - assert(m_command_buffers.size() == 0); - m_command_buffers.resize(1); - VkCommandBufferAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.commandPool = m_command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); - - VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers() -{ - assert(m_command_pool != VK_NULL_HANDLE); - m_command_buffers.resize(m_swapchain_image_views.size()); - - VkCommandBufferAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.commandPool = m_command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); - - VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createSwapchain() -{ - m_swapchain_id++; - - VkPhysicalDevice device = m_physical_device; - - uint32_t format_count; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, NULL); - vector formats(format_count); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, formats.data()); - - /* TODO choose appropriate format. */ - VkSurfaceFormatKHR format = formats[0]; - - VkPresentModeKHR present_mode; - if (!selectPresentMode(device, m_surface, &present_mode)) { - return GHOST_kFailure; - } - - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_surface, &capabilities); - - m_render_extent = capabilities.currentExtent; - if (m_render_extent.width == UINT32_MAX) { - /* Window Manager is going to set the surface size based on the given size. - * Choose something between minImageExtent and maxImageExtent. */ - m_render_extent.width = 1280; - m_render_extent.height = 720; - if (capabilities.minImageExtent.width > m_render_extent.width) { - m_render_extent.width = capabilities.minImageExtent.width; - } - if (capabilities.minImageExtent.height > m_render_extent.height) { - m_render_extent.height = capabilities.minImageExtent.height; - } - } - - /* Driver can stall if only using minimal image count. */ - uint32_t image_count = capabilities.minImageCount; - /* Note: maxImageCount == 0 means no limit. */ - if (image_count > capabilities.maxImageCount && capabilities.maxImageCount > 0) { - image_count = capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - create_info.surface = m_surface; - create_info.minImageCount = image_count; - create_info.imageFormat = format.format; - create_info.imageColorSpace = format.colorSpace; - create_info.imageExtent = m_render_extent; - create_info.imageArrayLayers = 1; - create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - create_info.preTransform = capabilities.currentTransform; - create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - create_info.presentMode = present_mode; - create_info.clipped = VK_TRUE; - create_info.oldSwapchain = VK_NULL_HANDLE; /* TODO Window resize */ - - uint32_t queueFamilyIndices[] = {m_queue_family_graphic, m_queue_family_present}; - - if (m_queue_family_graphic != m_queue_family_present) { - create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - create_info.queueFamilyIndexCount = 2; - create_info.pQueueFamilyIndices = queueFamilyIndices; - } - else { - create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - create_info.queueFamilyIndexCount = 0; - create_info.pQueueFamilyIndices = NULL; - } - - VK_CHECK(vkCreateSwapchainKHR(m_device, &create_info, NULL, &m_swapchain)); - - create_render_pass(m_device, format.format, &m_render_pass); - - /* image_count may not be what we requested! Getter for final value. */ - vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, NULL); - m_swapchain_images.resize(image_count); - vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, m_swapchain_images.data()); - - m_in_flight_images.resize(image_count, VK_NULL_HANDLE); - m_swapchain_image_views.resize(image_count); - m_swapchain_framebuffers.resize(image_count); - for (int i = 0; i < image_count; i++) { - VkImageViewCreateInfo view_create_info = {}; - view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_create_info.image = m_swapchain_images[i]; - view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_create_info.format = format.format; - view_create_info.components = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - }; - view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_create_info.subresourceRange.baseMipLevel = 0; - view_create_info.subresourceRange.levelCount = 1; - view_create_info.subresourceRange.baseArrayLayer = 0; - view_create_info.subresourceRange.layerCount = 1; - - VK_CHECK(vkCreateImageView(m_device, &view_create_info, NULL, &m_swapchain_image_views[i])); - - VkImageView attachments[] = {m_swapchain_image_views[i]}; - - VkFramebufferCreateInfo fb_create_info = {}; - fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - fb_create_info.renderPass = m_render_pass; - fb_create_info.attachmentCount = 1; - fb_create_info.pAttachments = attachments; - fb_create_info.width = m_render_extent.width; - fb_create_info.height = m_render_extent.height; - fb_create_info.layers = 1; - - VK_CHECK(vkCreateFramebuffer(m_device, &fb_create_info, NULL, &m_swapchain_framebuffers[i])); - } - - m_image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT); - m_render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT); - m_in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT); - for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - - VkSemaphoreCreateInfo semaphore_info = {}; - semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_image_available_semaphores[i])); - VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_render_finished_semaphores[i])); - - VkFenceCreateInfo fence_info = {}; - fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - VK_CHECK(vkCreateFence(m_device, &fence_info, NULL, &m_in_flight_fences[i])); - } - - createGraphicsCommandBuffers(); - - return GHOST_kSuccess; -} - -const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const -{ -#ifdef _WIN32 - return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; -#elif defined(__APPLE__) - return VK_EXT_METAL_SURFACE_EXTENSION_NAME; -#else /* UNIX/Linux */ - switch (m_platform) { - case GHOST_kVulkanPlatformX11: - return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; - break; -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: - return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; - break; -# endif - } -#endif - return NULL; -} - -GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() -{ -#ifdef _WIN32 - const bool use_window_surface = (m_hwnd != NULL); -#elif defined(__APPLE__) - const bool use_window_surface = (m_metal_layer != NULL); -#else /* UNIX/Linux */ - bool use_window_surface = false; - switch (m_platform) { - case GHOST_kVulkanPlatformX11: - use_window_surface = (m_display != NULL) && (m_window != (Window)NULL); - break; -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: - use_window_surface = (m_wayland_display != NULL) && (m_wayland_surface != NULL); - break; -# endif - } -#endif - - auto layers_available = getLayersAvailable(); - auto extensions_available = getExtensionsAvailable(); - - vector layers_enabled; - if (m_debug) { - enableLayer(layers_available, layers_enabled, VkDynamicLibraryType::VkValidationLayer,true); - } - - vector extensions_device; - vector extensions_enabled; - - if (use_window_surface) { - const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension(); - - requireExtension(extensions_available, extensions_enabled, "VK_KHR_surface"); - requireExtension(extensions_available, extensions_enabled, native_surface_extension_name); - - extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - } - extensions_device.push_back("VK_KHR_dedicated_allocation"); - extensions_device.push_back("VK_KHR_get_memory_requirements2"); - /* Enable MoltenVK required instance extensions. */ -#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME - requireExtension( - extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2"); -#endif - - VkApplicationInfo app_info = {}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pApplicationName = "Blender"; - app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - app_info.pEngineName = "Blender"; - app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); - app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0); - - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.pApplicationInfo = &app_info; - create_info.enabledLayerCount = static_cast(layers_enabled.size()); - create_info.ppEnabledLayerNames = layers_enabled.data(); - create_info.enabledExtensionCount = static_cast(extensions_enabled.size()); - create_info.ppEnabledExtensionNames = extensions_enabled.data(); - - VK_CHECK(vkCreateInstance(&create_info, NULL, &m_instance)); - - if (use_window_surface) { -#ifdef _WIN32 - VkWin32SurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surface_create_info.hinstance = GetModuleHandle(NULL); - surface_create_info.hwnd = m_hwnd; - VK_CHECK(vkCreateWin32SurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); -#elif defined(__APPLE__) - VkMetalSurfaceCreateInfoEXT info = {}; - info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; - info.pNext = NULL; - info.flags = 0; - info.pLayer = m_metal_layer; - VK_CHECK(vkCreateMetalSurfaceEXT(m_instance, &info, nullptr, &m_surface)); -#else - switch (m_platform) { - case GHOST_kVulkanPlatformX11: { - VkXlibSurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - surface_create_info.dpy = m_display; - surface_create_info.window = m_window; - VK_CHECK(vkCreateXlibSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); - break; - } -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: { - VkWaylandSurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - surface_create_info.display = m_wayland_display; - surface_create_info.surface = m_wayland_surface; - VK_CHECK(vkCreateWaylandSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); - break; - } -# endif - } - -#endif - } - - if (!pickPhysicalDevice(extensions_device)) { - return GHOST_kFailure; - } - -#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME - /* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be - * enabled. See - * https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/ - if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) { - extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - } -#endif - - vector queue_create_infos; - - { - /* A graphic queue is required to draw anything. */ - if (!getGraphicQueueFamily(m_physical_device, &m_queue_family_graphic)) { - return GHOST_kFailure; - } - - float queue_priorities[] = {1.0f}; - VkDeviceQueueCreateInfo graphic_queue_create_info = {}; - graphic_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - graphic_queue_create_info.queueFamilyIndex = m_queue_family_graphic; - graphic_queue_create_info.queueCount = 1; - graphic_queue_create_info.pQueuePriorities = queue_priorities; - queue_create_infos.push_back(graphic_queue_create_info); - } - - if (use_window_surface) { - /* A present queue is required only if we render to a window. */ - if (!getPresetQueueFamily(m_physical_device, m_surface, &m_queue_family_present)) { - return GHOST_kFailure; - } - - float queue_priorities[] = {1.0f}; - VkDeviceQueueCreateInfo present_queue_create_info = {}; - present_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - present_queue_create_info.queueFamilyIndex = m_queue_family_present; - present_queue_create_info.queueCount = 1; - present_queue_create_info.pQueuePriorities = queue_priorities; - - /* Each queue must be unique. */ - if (m_queue_family_graphic != m_queue_family_present) { - queue_create_infos.push_back(present_queue_create_info); - } - } - - VkPhysicalDeviceFeatures device_features = {}; -#if STRICT_REQUIREMENTS - device_features.geometryShader = VK_TRUE; - device_features.dualSrcBlend = VK_TRUE; - device_features.logicOp = VK_TRUE; -#endif - - VkDeviceCreateInfo device_create_info = {}; - device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - device_create_info.queueCreateInfoCount = static_cast(queue_create_infos.size()); - device_create_info.pQueueCreateInfos = queue_create_infos.data(); - /* layers_enabled are the same as instance extensions. - * This is only needed for 1.0 implementations. */ - device_create_info.enabledLayerCount = static_cast(layers_enabled.size()); - device_create_info.ppEnabledLayerNames = layers_enabled.data(); - device_create_info.enabledExtensionCount = static_cast(extensions_device.size()); - device_create_info.ppEnabledExtensionNames = extensions_device.data(); - device_create_info.pEnabledFeatures = &device_features; - - VK_CHECK(vkCreateDevice(m_physical_device, &device_create_info, NULL, &m_device)); - - vkGetDeviceQueue(m_device, m_queue_family_graphic, 0, &m_graphic_queue); - - createCommandPools(); - if (use_window_surface) { - vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); - createSwapchain(); - } - else { - createGraphicsCommandBuffer(); - } - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles() -{ - return GHOST_kSuccess; -} +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#include "GHOST_ContextVK.h" + +#ifdef _WIN32 +# include +#elif defined(__APPLE__) +# include +#else /* X11 */ +# include +# ifdef WITH_GHOST_WAYLAND +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +/* Set to 0 to allow devices that do not have the required features. + * This allows development on OSX until we really needs these features. */ +#define STRICT_REQUIREMENTS 1 + +using namespace std; + +static const char *vulkan_error_as_string(VkResult result) +{ +#define FORMAT_ERROR(X) \ + case X: { \ + return "" #X; \ + } + + switch (result) { + FORMAT_ERROR(VK_NOT_READY); + FORMAT_ERROR(VK_TIMEOUT); + FORMAT_ERROR(VK_EVENT_SET); + FORMAT_ERROR(VK_EVENT_RESET); + FORMAT_ERROR(VK_INCOMPLETE); + FORMAT_ERROR(VK_ERROR_OUT_OF_HOST_MEMORY); + FORMAT_ERROR(VK_ERROR_OUT_OF_DEVICE_MEMORY); + FORMAT_ERROR(VK_ERROR_INITIALIZATION_FAILED); + FORMAT_ERROR(VK_ERROR_DEVICE_LOST); + FORMAT_ERROR(VK_ERROR_MEMORY_MAP_FAILED); + FORMAT_ERROR(VK_ERROR_LAYER_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_EXTENSION_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_FEATURE_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DRIVER); + FORMAT_ERROR(VK_ERROR_TOO_MANY_OBJECTS); + FORMAT_ERROR(VK_ERROR_FORMAT_NOT_SUPPORTED); + FORMAT_ERROR(VK_ERROR_FRAGMENTED_POOL); + FORMAT_ERROR(VK_ERROR_UNKNOWN); + FORMAT_ERROR(VK_ERROR_OUT_OF_POOL_MEMORY); + FORMAT_ERROR(VK_ERROR_INVALID_EXTERNAL_HANDLE); + FORMAT_ERROR(VK_ERROR_FRAGMENTATION); + FORMAT_ERROR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); + FORMAT_ERROR(VK_ERROR_SURFACE_LOST_KHR); + FORMAT_ERROR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + FORMAT_ERROR(VK_SUBOPTIMAL_KHR); + FORMAT_ERROR(VK_ERROR_OUT_OF_DATE_KHR); + FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + FORMAT_ERROR(VK_ERROR_VALIDATION_FAILED_EXT); + FORMAT_ERROR(VK_ERROR_INVALID_SHADER_NV); + FORMAT_ERROR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); + FORMAT_ERROR(VK_ERROR_NOT_PERMITTED_EXT); + FORMAT_ERROR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); + FORMAT_ERROR(VK_THREAD_IDLE_KHR); + FORMAT_ERROR(VK_THREAD_DONE_KHR); + FORMAT_ERROR(VK_OPERATION_DEFERRED_KHR); + FORMAT_ERROR(VK_OPERATION_NOT_DEFERRED_KHR); + FORMAT_ERROR(VK_PIPELINE_COMPILE_REQUIRED_EXT); + default: + return "Unknown Error"; + } +} +static bool vklayer_config_exist(const char* vk_extension_config) +{ + const char *ev_val = getenv("VK_LAYER_PATH"); + if (ev_val == nullptr) { + return false; + } + std::stringstream filename; + filename << ev_val; + filename << "/"; + filename << vk_extension_config; + struct stat buffer; + return (stat(filename.str().c_str(), &buffer) == 0); +} + +#define __STR(A) "" #A +#define VK_CHECK(__expression) \ + do { \ + VkResult r = (__expression); \ + if (r != VK_SUCCESS) { \ + fprintf(stderr, \ + "Vulkan Error : %s:%d : %s failled with %s\n", \ + __FILE__, \ + __LINE__, \ + __STR(__expression), \ + vulkan_error_as_string(r)); \ + return GHOST_kFailure; \ + } \ + } while (0) + +#define DEBUG_PRINTF(...) \ + if (m_debug) { \ + printf(__VA_ARGS__); \ + } + +/* Triple buffering. */ +const int MAX_FRAMES_IN_FLIGHT = 2; + +GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual, +#ifdef _WIN32 + HWND hwnd, +#elif defined(__APPLE__) + CAMetalLayer *metal_layer, +#else + GHOST_TVulkanPlatformType platform, + /* X11 */ + Window window, + Display *display, + /* Wayland */ + wl_surface *wayland_surface, + wl_display *wayland_display, +#endif + int contextMajorVersion, + int contextMinorVersion, + int debug) + : GHOST_Context(stereoVisual), +#ifdef _WIN32 + m_hwnd(hwnd), +#elif defined(__APPLE__) + m_metal_layer(metal_layer), +#else + m_platform(platform), + /* X11 */ + m_display(display), + m_window(window), + /* Wayland */ + m_wayland_surface(wayland_surface), + m_wayland_display(wayland_display), +#endif + m_context_major_version(contextMajorVersion), + m_context_minor_version(contextMinorVersion), + m_debug(debug), + m_instance(VK_NULL_HANDLE), + m_physical_device(VK_NULL_HANDLE), + m_device(VK_NULL_HANDLE), + m_command_pool(VK_NULL_HANDLE), + m_surface(VK_NULL_HANDLE), + m_swapchain(VK_NULL_HANDLE), + m_render_pass(VK_NULL_HANDLE) +{ +} + +GHOST_ContextVK::~GHOST_ContextVK() +{ + if (m_device) { + vkDeviceWaitIdle(m_device); + } + + destroySwapchain(); + + if (m_command_pool != VK_NULL_HANDLE) { + vkDestroyCommandPool(m_device, m_command_pool, NULL); + } + if (m_device != VK_NULL_HANDLE) { + vkDestroyDevice(m_device, NULL); + } + if (m_surface != VK_NULL_HANDLE) { + vkDestroySurfaceKHR(m_instance, m_surface, NULL); + } + if (m_instance != VK_NULL_HANDLE) { + vkDestroyInstance(m_instance, NULL); + } +} + +GHOST_TSuccess GHOST_ContextVK::destroySwapchain() +{ + if (m_device != VK_NULL_HANDLE) { + vkDeviceWaitIdle(m_device); + } + + m_in_flight_images.resize(0); + + for (auto semaphore : m_image_available_semaphores) { + vkDestroySemaphore(m_device, semaphore, NULL); + } + for (auto semaphore : m_render_finished_semaphores) { + vkDestroySemaphore(m_device, semaphore, NULL); + } + for (auto fence : m_in_flight_fences) { + vkDestroyFence(m_device, fence, NULL); + } + for (auto framebuffer : m_swapchain_framebuffers) { + vkDestroyFramebuffer(m_device, framebuffer, NULL); + } + if (m_render_pass != VK_NULL_HANDLE) { + vkDestroyRenderPass(m_device, m_render_pass, NULL); + } + for (auto command_buffer : m_command_buffers) { + vkFreeCommandBuffers(m_device, m_command_pool, 1, &command_buffer); + } + for (auto imageView : m_swapchain_image_views) { + vkDestroyImageView(m_device, imageView, NULL); + } + if (m_swapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(m_device, m_swapchain, NULL); + } + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::swapBuffers() +{ + if (m_swapchain == VK_NULL_HANDLE) { + return GHOST_kFailure; + } + + vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, UINT64_MAX); + + VkResult result = vkAcquireNextImageKHR(m_device, + m_swapchain, + UINT64_MAX, + m_image_available_semaphores[m_currentFrame], + VK_NULL_HANDLE, + &m_currentImage); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ + destroySwapchain(); + createSwapchain(); + return GHOST_kSuccess; + } + else if (result != VK_SUCCESS) { + fprintf(stderr, + "Error: Failed to acquire swap chain image : %s\n", + vulkan_error_as_string(result)); + return GHOST_kFailure; + } + + /* Check if a previous frame is using this image (i.e. there is its fence to wait on) */ + if (m_in_flight_images[m_currentImage] != VK_NULL_HANDLE) { + vkWaitForFences(m_device, 1, &m_in_flight_images[m_currentImage], VK_TRUE, UINT64_MAX); + } + m_in_flight_images[m_currentImage] = m_in_flight_fences[m_currentFrame]; + + VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &m_image_available_semaphores[m_currentFrame]; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &m_command_buffers[m_currentImage]; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &m_render_finished_semaphores[m_currentFrame]; + + vkResetFences(m_device, 1, &m_in_flight_fences[m_currentFrame]); + + VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, m_in_flight_fences[m_currentFrame])); + do { + result = vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, 10000); + } while (result == VK_TIMEOUT); + + VK_CHECK(vkQueueWaitIdle(m_graphic_queue)); + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &m_render_finished_semaphores[m_currentFrame]; + present_info.swapchainCount = 1; + present_info.pSwapchains = &m_swapchain; + present_info.pImageIndices = &m_currentImage; + present_info.pResults = NULL; + + result = vkQueuePresentKHR(m_present_queue, &present_info); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ + destroySwapchain(); + createSwapchain(); + return GHOST_kSuccess; + } + else if (result != VK_SUCCESS) { + fprintf(stderr, + "Error: Failed to present swap chain image : %s\n", + vulkan_error_as_string(result)); + return GHOST_kFailure; + } + + m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer( + void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) +{ + if (m_swapchain == VK_NULL_HANDLE) { + return GHOST_kFailure; + } + *((VkImage *)image) = m_swapchain_images[m_currentImage]; + *((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage]; + *((VkRenderPass *)render_pass) = m_render_pass; + *((VkExtent2D *)extent) = m_render_extent; + *fb_id = m_swapchain_id * 10 + m_currentFrame; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_family, + void *r_queue) +{ + *((VkInstance *)r_instance) = m_instance; + *((VkPhysicalDevice *)r_physical_device) = m_physical_device; + *((VkDevice *)r_device) = m_device; + *r_graphic_queue_family = m_queue_family_graphic; + *((VkQueue *)r_queue) = m_graphic_queue; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer) +{ + if (m_command_buffers.empty()) { + return GHOST_kFailure; + } + + if (m_swapchain == VK_NULL_HANDLE) { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0]; + } + else { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage]; + } + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::activateDrawingContext() +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::releaseDrawingContext() +{ + return GHOST_kSuccess; +} + +static vector getExtensionsAvailable() +{ + uint32_t extension_count = 0; + vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); + + vector extensions(extension_count); + vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions.data()); + + return extensions; +} + +static bool checkExtensionSupport(vector &extensions_available, + const char *extension_name) +{ + for (const auto &extension : extensions_available) { + if (strcmp(extension_name, extension.extensionName) == 0) { + return true; + } + } + return false; +} + +static void requireExtension(vector &extensions_available, + vector &extensions_enabled, + const char *extension_name) +{ + if (checkExtensionSupport(extensions_available, extension_name)) { + extensions_enabled.push_back(extension_name); + } + else { + fprintf(stderr, "Error: %s not found.\n", extension_name); + } +} + +static vector getLayersAvailable() +{ + uint32_t layer_count = 0; + vkEnumerateInstanceLayerProperties(&layer_count, NULL); + + vector layers(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, layers.data()); + + return layers; +} + +static bool checkLayerSupport(vector &layers_available, const char *layer_name) +{ + for (const auto &layer : layers_available) { + if (strcmp(layer_name, layer.layerName) == 0) { + return true; + } + } + return false; +} + +static void enableLayer(vector &layers_available, + vector &layers_enabled, + const char* layer_name, + const bool warning) +{ + + if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { + if (checkLayerSupport(layers_available, layer_name) && + vklayer_config_exist("VkLayer_khronos_validation.json")) { + layers_enabled.push_back(layer_name); + } + else if (warning) { + fprintf(stderr, + "Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); + } + } + +} + +static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) +{ + uint32_t ext_count; + vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL); + + vector available_exts(ext_count); + vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, available_exts.data()); + + for (const auto &extension_needed : required_exts) { + bool found = false; + for (const auto &extension : available_exts) { + if (strcmp(extension_needed, extension.extensionName) == 0) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; +} + +GHOST_TSuccess GHOST_ContextVK::pickPhysicalDevice(vector required_exts) +{ + m_physical_device = VK_NULL_HANDLE; + + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(m_instance, &device_count, NULL); + + vector physical_devices(device_count); + vkEnumeratePhysicalDevices(m_instance, &device_count, physical_devices.data()); + + int best_device_score = -1; + for (const auto &physical_device : physical_devices) { + VkPhysicalDeviceProperties device_properties; + vkGetPhysicalDeviceProperties(physical_device, &device_properties); + + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physical_device, &features); + + DEBUG_PRINTF("%s : \n", device_properties.deviceName); + + if (!device_extensions_support(physical_device, required_exts)) { + DEBUG_PRINTF(" - Device does not support required device extensions.\n"); + continue; + } + + if (m_surface != VK_NULL_HANDLE) { + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, m_surface, &format_count, NULL); + + uint32_t present_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, m_surface, &present_count, NULL); + + /* For now anything will do. */ + if (format_count == 0 || present_count == 0) { + DEBUG_PRINTF(" - Device does not support presentation.\n"); + continue; + } + } + + if (!features.geometryShader) { + /* Needed for wide lines emulation and barycentric coords and a few others. */ + DEBUG_PRINTF(" - Device does not support geometryShader.\n"); + } + if (!features.dualSrcBlend) { + DEBUG_PRINTF(" - Device does not support dualSrcBlend.\n"); + } + if (!features.logicOp) { + /* Needed by UI. */ + DEBUG_PRINTF(" - Device does not support logicOp.\n"); + } + +#if STRICT_REQUIREMENTS + if (!features.geometryShader || !features.dualSrcBlend || !features.logicOp) { + continue; + } +#endif + + int device_score = 0; + switch (device_properties.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + device_score = 400; + break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + device_score = 300; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + device_score = 200; + break; + case VK_PHYSICAL_DEVICE_TYPE_CPU: + device_score = 100; + break; + default: + break; + } + if (device_score > best_device_score) { + m_physical_device = physical_device; + best_device_score = device_score; + } + DEBUG_PRINTF(" - Device suitable.\n"); + } + + if (m_physical_device == VK_NULL_HANDLE) { + fprintf(stderr, "Error: No suitable Vulkan Device found!\n"); + return GHOST_kFailure; + } + + return GHOST_kSuccess; +} + +static GHOST_TSuccess getGraphicQueueFamily(VkPhysicalDevice device, uint32_t *r_queue_index) +{ + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); + + vector queue_families(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + *r_queue_index = 0; + for (const auto &queue_family : queue_families) { + /* Every vulkan implementation by spec must have one queue family that support both graphics + * and compute pipelines. We select this one; compute only queue family hints at async compute + * implementations. */ + if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) && + (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) { + return GHOST_kSuccess; + } + (*r_queue_index)++; + } + + fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n"); + return GHOST_kFailure; +} + +static GHOST_TSuccess getPresetQueueFamily(VkPhysicalDevice device, + VkSurfaceKHR surface, + uint32_t *r_queue_index) +{ + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); + + vector queue_families(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + *r_queue_index = 0; + for (int i = 0; i < queue_family_count; i++) { + VkBool32 present_support = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, *r_queue_index, surface, &present_support); + + if (present_support) { + return GHOST_kSuccess; + } + (*r_queue_index)++; + } + + fprintf(stderr, "Couldn't find any Present queue family on selected device\n"); + return GHOST_kFailure; +} + +static GHOST_TSuccess create_render_pass(VkDevice device, + VkFormat format, + VkRenderPass *r_renderPass) +{ + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, NULL, r_renderPass)); + + return GHOST_kSuccess; +} + +static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, + VkSurfaceKHR surface, + VkPresentModeKHR *r_presentMode) +{ + uint32_t present_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL); + vector presents(present_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data()); + /* MAILBOX is the lowest latency V-Sync enabled mode so use it if available */ + for (auto present_mode : presents) { + if (present_mode == VK_PRESENT_MODE_FIFO_KHR) { + *r_presentMode = present_mode; + return GHOST_kSuccess; + } + } + /* FIFO present mode is always available. */ + for (auto present_mode : presents) { + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { + *r_presentMode = present_mode; + return GHOST_kSuccess; + } + } + + fprintf(stderr, "Error: FIFO present mode is not supported by the swap chain!\n"); + + return GHOST_kFailure; +} + +GHOST_TSuccess GHOST_ContextVK::createCommandPools() +{ + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = m_queue_family_graphic; + + VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool)); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer() +{ + assert(m_command_pool != VK_NULL_HANDLE); + assert(m_command_buffers.size() == 0); + m_command_buffers.resize(1); + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = m_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); + + VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers() +{ + assert(m_command_pool != VK_NULL_HANDLE); + m_command_buffers.resize(m_swapchain_image_views.size()); + + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = m_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); + + VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createSwapchain() +{ + m_swapchain_id++; + + VkPhysicalDevice device = m_physical_device; + + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, NULL); + vector formats(format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, formats.data()); + + /* TODO choose appropriate format. */ + VkSurfaceFormatKHR format = formats[0]; + + VkPresentModeKHR present_mode; + if (!selectPresentMode(device, m_surface, &present_mode)) { + return GHOST_kFailure; + } + + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_surface, &capabilities); + + m_render_extent = capabilities.currentExtent; + if (m_render_extent.width == UINT32_MAX) { + /* Window Manager is going to set the surface size based on the given size. + * Choose something between minImageExtent and maxImageExtent. */ + m_render_extent.width = 1280; + m_render_extent.height = 720; + if (capabilities.minImageExtent.width > m_render_extent.width) { + m_render_extent.width = capabilities.minImageExtent.width; + } + if (capabilities.minImageExtent.height > m_render_extent.height) { + m_render_extent.height = capabilities.minImageExtent.height; + } + } + + /* Driver can stall if only using minimal image count. */ + uint32_t image_count = capabilities.minImageCount; + /* Note: maxImageCount == 0 means no limit. */ + if (image_count > capabilities.maxImageCount && capabilities.maxImageCount > 0) { + image_count = capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.surface = m_surface; + create_info.minImageCount = image_count; + create_info.imageFormat = format.format; + create_info.imageColorSpace = format.colorSpace; + create_info.imageExtent = m_render_extent; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + create_info.preTransform = capabilities.currentTransform; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = present_mode; + create_info.clipped = VK_TRUE; + create_info.oldSwapchain = VK_NULL_HANDLE; /* TODO Window resize */ + + uint32_t queueFamilyIndices[] = {m_queue_family_graphic, m_queue_family_present}; + + if (m_queue_family_graphic != m_queue_family_present) { + create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + create_info.queueFamilyIndexCount = 2; + create_info.pQueueFamilyIndices = queueFamilyIndices; + } + else { + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = NULL; + } + + VK_CHECK(vkCreateSwapchainKHR(m_device, &create_info, NULL, &m_swapchain)); + + create_render_pass(m_device, format.format, &m_render_pass); + + /* image_count may not be what we requested! Getter for final value. */ + vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, NULL); + m_swapchain_images.resize(image_count); + vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, m_swapchain_images.data()); + + m_in_flight_images.resize(image_count, VK_NULL_HANDLE); + m_swapchain_image_views.resize(image_count); + m_swapchain_framebuffers.resize(image_count); + for (int i = 0; i < image_count; i++) { + VkImageViewCreateInfo view_create_info = {}; + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.image = m_swapchain_images[i]; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = format.format; + view_create_info.components = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_create_info.subresourceRange.baseMipLevel = 0; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.baseArrayLayer = 0; + view_create_info.subresourceRange.layerCount = 1; + + VK_CHECK(vkCreateImageView(m_device, &view_create_info, NULL, &m_swapchain_image_views[i])); + + VkImageView attachments[] = {m_swapchain_image_views[i]}; + + VkFramebufferCreateInfo fb_create_info = {}; + fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_create_info.renderPass = m_render_pass; + fb_create_info.attachmentCount = 1; + fb_create_info.pAttachments = attachments; + fb_create_info.width = m_render_extent.width; + fb_create_info.height = m_render_extent.height; + fb_create_info.layers = 1; + + VK_CHECK(vkCreateFramebuffer(m_device, &fb_create_info, NULL, &m_swapchain_framebuffers[i])); + } + + m_image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT); + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + + VkSemaphoreCreateInfo semaphore_info = {}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_image_available_semaphores[i])); + VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_render_finished_semaphores[i])); + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + VK_CHECK(vkCreateFence(m_device, &fence_info, NULL, &m_in_flight_fences[i])); + } + + createGraphicsCommandBuffers(); + + return GHOST_kSuccess; +} + +const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const +{ +#ifdef _WIN32 + return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; +#elif defined(__APPLE__) + return VK_EXT_METAL_SURFACE_EXTENSION_NAME; +#else /* UNIX/Linux */ + switch (m_platform) { + case GHOST_kVulkanPlatformX11: + return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; + break; +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: + return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; + break; +# endif + } +#endif + return NULL; +} + +GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() +{ +#ifdef _WIN32 + const bool use_window_surface = (m_hwnd != NULL); +#elif defined(__APPLE__) + const bool use_window_surface = (m_metal_layer != NULL); +#else /* UNIX/Linux */ + bool use_window_surface = false; + switch (m_platform) { + case GHOST_kVulkanPlatformX11: + use_window_surface = (m_display != NULL) && (m_window != (Window)NULL); + break; +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: + use_window_surface = (m_wayland_display != NULL) && (m_wayland_surface != NULL); + break; +# endif + } +#endif + + auto layers_available = getLayersAvailable(); + auto extensions_available = getExtensionsAvailable(); + + vector layers_enabled; + if (m_debug) { + enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation",true); + } + + vector extensions_device; + vector extensions_enabled; + + if (use_window_surface) { + const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension(); + + requireExtension(extensions_available, extensions_enabled, "VK_KHR_surface"); + requireExtension(extensions_available, extensions_enabled, native_surface_extension_name); + + extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } + extensions_device.push_back("VK_KHR_dedicated_allocation"); + extensions_device.push_back("VK_KHR_get_memory_requirements2"); + /* Enable MoltenVK required instance extensions. */ +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + requireExtension( + extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2"); +#endif + + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "Blender"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "Blender"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0); + + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = static_cast(layers_enabled.size()); + create_info.ppEnabledLayerNames = layers_enabled.data(); + create_info.enabledExtensionCount = static_cast(extensions_enabled.size()); + create_info.ppEnabledExtensionNames = extensions_enabled.data(); + + VK_CHECK(vkCreateInstance(&create_info, NULL, &m_instance)); + + if (use_window_surface) { +#ifdef _WIN32 + VkWin32SurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surface_create_info.hinstance = GetModuleHandle(NULL); + surface_create_info.hwnd = m_hwnd; + VK_CHECK(vkCreateWin32SurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); +#elif defined(__APPLE__) + VkMetalSurfaceCreateInfoEXT info = {}; + info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + info.pNext = NULL; + info.flags = 0; + info.pLayer = m_metal_layer; + VK_CHECK(vkCreateMetalSurfaceEXT(m_instance, &info, nullptr, &m_surface)); +#else + switch (m_platform) { + case GHOST_kVulkanPlatformX11: { + VkXlibSurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surface_create_info.dpy = m_display; + surface_create_info.window = m_window; + VK_CHECK(vkCreateXlibSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); + break; + } +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: { + VkWaylandSurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + surface_create_info.display = m_wayland_display; + surface_create_info.surface = m_wayland_surface; + VK_CHECK(vkCreateWaylandSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); + break; + } +# endif + } + +#endif + } + + if (!pickPhysicalDevice(extensions_device)) { + return GHOST_kFailure; + } + +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + /* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be + * enabled. See + * https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/ + if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) { + extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + } +#endif + + vector queue_create_infos; + + { + /* A graphic queue is required to draw anything. */ + if (!getGraphicQueueFamily(m_physical_device, &m_queue_family_graphic)) { + return GHOST_kFailure; + } + + float queue_priorities[] = {1.0f}; + VkDeviceQueueCreateInfo graphic_queue_create_info = {}; + graphic_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + graphic_queue_create_info.queueFamilyIndex = m_queue_family_graphic; + graphic_queue_create_info.queueCount = 1; + graphic_queue_create_info.pQueuePriorities = queue_priorities; + queue_create_infos.push_back(graphic_queue_create_info); + } + + if (use_window_surface) { + /* A present queue is required only if we render to a window. */ + if (!getPresetQueueFamily(m_physical_device, m_surface, &m_queue_family_present)) { + return GHOST_kFailure; + } + + float queue_priorities[] = {1.0f}; + VkDeviceQueueCreateInfo present_queue_create_info = {}; + present_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + present_queue_create_info.queueFamilyIndex = m_queue_family_present; + present_queue_create_info.queueCount = 1; + present_queue_create_info.pQueuePriorities = queue_priorities; + + /* Each queue must be unique. */ + if (m_queue_family_graphic != m_queue_family_present) { + queue_create_infos.push_back(present_queue_create_info); + } + } + + VkPhysicalDeviceFeatures device_features = {}; +#if STRICT_REQUIREMENTS + device_features.geometryShader = VK_TRUE; + device_features.dualSrcBlend = VK_TRUE; + device_features.logicOp = VK_TRUE; +#endif + + VkDeviceCreateInfo device_create_info = {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.queueCreateInfoCount = static_cast(queue_create_infos.size()); + device_create_info.pQueueCreateInfos = queue_create_infos.data(); + /* layers_enabled are the same as instance extensions. + * This is only needed for 1.0 implementations. */ + device_create_info.enabledLayerCount = static_cast(layers_enabled.size()); + device_create_info.ppEnabledLayerNames = layers_enabled.data(); + device_create_info.enabledExtensionCount = static_cast(extensions_device.size()); + device_create_info.ppEnabledExtensionNames = extensions_device.data(); + device_create_info.pEnabledFeatures = &device_features; + + VK_CHECK(vkCreateDevice(m_physical_device, &device_create_info, NULL, &m_device)); + + vkGetDeviceQueue(m_device, m_queue_family_graphic, 0, &m_graphic_queue); + + createCommandPools(); + if (use_window_surface) { + vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); + createSwapchain(); + } + else { + createGraphicsCommandBuffer(); + } + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles() +{ + return GHOST_kSuccess; +} -- 2.30.2 From 1a743c51930eb362c050956f17e325ce5d0f6708 Mon Sep 17 00:00:00 2001 From: vnapdv Date: Fri, 24 Mar 2023 22:45:04 +0900 Subject: [PATCH 08/13] FIX:Refactoring Vulkan Layer Validation3 --- intern/ghost/intern/GHOST_ContextVK.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 6aca911ccb9..ed6d482aab9 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -88,8 +88,7 @@ static bool vklayer_config_exist(const char* vk_extension_config) } std::stringstream filename; filename << ev_val; - filename << "/"; - filename << vk_extension_config; + filename << "/" << vk_extension_config; struct stat buffer; return (stat(filename.str().c_str(), &buffer) == 0); } @@ -425,8 +424,7 @@ static void enableLayer(vector &layers_available, layers_enabled.push_back(layer_name); } else if (warning) { - fprintf(stderr, - "Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); + fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); } } -- 2.30.2 From e9d75254018194b81e1fa4ee1c1bae5d0f35d958 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 14:06:25 +0900 Subject: [PATCH 09/13] FIX:Refactoring Vulkan Layer Validation4 --- intern/ghost/intern/GHOST_ContextVK.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index ed6d482aab9..491a3fe8a75 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -18,13 +18,14 @@ #endif #include - +#include #include #include #include #include -#include -#include +#include + + /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ #define STRICT_REQUIREMENTS 1 -- 2.30.2 From 91b662925bafb2e4010faf0a30469e4cdfb8875b Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 14:14:45 +0900 Subject: [PATCH 10/13] Revert:ContextVK --- intern/ghost/intern/GHOST_ContextVK.cpp | 38 +++++++------------------ 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 491a3fe8a75..32d595ac15c 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -18,13 +18,11 @@ #endif #include -#include + #include #include #include #include -#include - /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ @@ -81,18 +79,6 @@ static const char *vulkan_error_as_string(VkResult result) return "Unknown Error"; } } -static bool vklayer_config_exist(const char* vk_extension_config) -{ - const char *ev_val = getenv("VK_LAYER_PATH"); - if (ev_val == nullptr) { - return false; - } - std::stringstream filename; - filename << ev_val; - filename << "/" << vk_extension_config; - struct stat buffer; - return (stat(filename.str().c_str(), &buffer) == 0); -} #define __STR(A) "" #A #define VK_CHECK(__expression) \ @@ -415,20 +401,16 @@ static bool checkLayerSupport(vector &layers_available, const static void enableLayer(vector &layers_available, vector &layers_enabled, - const char* layer_name, - const bool warning) + const char *layer_name, + const bool debug) { - - if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { - if (checkLayerSupport(layers_available, layer_name) && - vklayer_config_exist("VkLayer_khronos_validation.json")) { - layers_enabled.push_back(layer_name); - } - else if (warning) { - fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); - } + if (checkLayerSupport(layers_available, layer_name)) { + layers_enabled.push_back(layer_name); + } + else if (debug) { + fprintf( + stderr, "Warning: Layer requested, but not supported by the platform. [%s]\n", layer_name); } - } static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) @@ -882,7 +864,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vector layers_enabled; if (m_debug) { - enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation",true); + enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); } vector extensions_device; -- 2.30.2 From 69e981f1b1cf109f28f181e86259185054e8800c Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 14:18:11 +0900 Subject: [PATCH 11/13] FIX:Refactoring Vulkan Layer Validation5 --- intern/ghost/intern/GHOST_ContextVK.cpp | 35 +++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 32d595ac15c..254cbb6450a 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -19,10 +19,12 @@ #include +#include #include #include #include #include +#include /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ @@ -80,6 +82,19 @@ static const char *vulkan_error_as_string(VkResult result) } } +static bool vklayer_config_exist(const char* vk_extension_config) +{ + const char *ev_val = getenv("VK_LAYER_PATH"); + if (ev_val == nullptr) { + return false; + } + std::stringstream filename; + filename << ev_val; + filename << "/" << vk_extension_config; + struct stat buffer; + return (stat(filename.str().c_str(), &buffer) == 0); +} + #define __STR(A) "" #A #define VK_CHECK(__expression) \ do { \ @@ -401,16 +416,20 @@ static bool checkLayerSupport(vector &layers_available, const static void enableLayer(vector &layers_available, vector &layers_enabled, - const char *layer_name, - const bool debug) + const char* layer_name, + const bool warning) { - if (checkLayerSupport(layers_available, layer_name)) { - layers_enabled.push_back(layer_name); - } - else if (debug) { - fprintf( - stderr, "Warning: Layer requested, but not supported by the platform. [%s]\n", layer_name); + + if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { + if (checkLayerSupport(layers_available, layer_name) && + vklayer_config_exist("VkLayer_khronos_validation.json")) { + layers_enabled.push_back(layer_name); + } + else if (warning) { + fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); + } } + } static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) -- 2.30.2 From 9067d01d35997b2f0f37131e59f6855f80e99771 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Fri, 24 Mar 2023 14:52:41 +0900 Subject: [PATCH 12/13] FIX:End of Line --- intern/ghost/intern/GHOST_ContextVK.cpp | 2102 +++++++++++------------ 1 file changed, 1051 insertions(+), 1051 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 254cbb6450a..65a33a468db 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -1,1051 +1,1051 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup GHOST - */ - -#include "GHOST_ContextVK.h" - -#ifdef _WIN32 -# include -#elif defined(__APPLE__) -# include -#else /* X11 */ -# include -# ifdef WITH_GHOST_WAYLAND -# include -# endif -#endif - -#include - -#include -#include -#include -#include -#include -#include - -/* Set to 0 to allow devices that do not have the required features. - * This allows development on OSX until we really needs these features. */ -#define STRICT_REQUIREMENTS 1 - -using namespace std; - -static const char *vulkan_error_as_string(VkResult result) -{ -#define FORMAT_ERROR(X) \ - case X: { \ - return "" #X; \ - } - - switch (result) { - FORMAT_ERROR(VK_NOT_READY); - FORMAT_ERROR(VK_TIMEOUT); - FORMAT_ERROR(VK_EVENT_SET); - FORMAT_ERROR(VK_EVENT_RESET); - FORMAT_ERROR(VK_INCOMPLETE); - FORMAT_ERROR(VK_ERROR_OUT_OF_HOST_MEMORY); - FORMAT_ERROR(VK_ERROR_OUT_OF_DEVICE_MEMORY); - FORMAT_ERROR(VK_ERROR_INITIALIZATION_FAILED); - FORMAT_ERROR(VK_ERROR_DEVICE_LOST); - FORMAT_ERROR(VK_ERROR_MEMORY_MAP_FAILED); - FORMAT_ERROR(VK_ERROR_LAYER_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_EXTENSION_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_FEATURE_NOT_PRESENT); - FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DRIVER); - FORMAT_ERROR(VK_ERROR_TOO_MANY_OBJECTS); - FORMAT_ERROR(VK_ERROR_FORMAT_NOT_SUPPORTED); - FORMAT_ERROR(VK_ERROR_FRAGMENTED_POOL); - FORMAT_ERROR(VK_ERROR_UNKNOWN); - FORMAT_ERROR(VK_ERROR_OUT_OF_POOL_MEMORY); - FORMAT_ERROR(VK_ERROR_INVALID_EXTERNAL_HANDLE); - FORMAT_ERROR(VK_ERROR_FRAGMENTATION); - FORMAT_ERROR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); - FORMAT_ERROR(VK_ERROR_SURFACE_LOST_KHR); - FORMAT_ERROR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); - FORMAT_ERROR(VK_SUBOPTIMAL_KHR); - FORMAT_ERROR(VK_ERROR_OUT_OF_DATE_KHR); - FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); - FORMAT_ERROR(VK_ERROR_VALIDATION_FAILED_EXT); - FORMAT_ERROR(VK_ERROR_INVALID_SHADER_NV); - FORMAT_ERROR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); - FORMAT_ERROR(VK_ERROR_NOT_PERMITTED_EXT); - FORMAT_ERROR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); - FORMAT_ERROR(VK_THREAD_IDLE_KHR); - FORMAT_ERROR(VK_THREAD_DONE_KHR); - FORMAT_ERROR(VK_OPERATION_DEFERRED_KHR); - FORMAT_ERROR(VK_OPERATION_NOT_DEFERRED_KHR); - FORMAT_ERROR(VK_PIPELINE_COMPILE_REQUIRED_EXT); - default: - return "Unknown Error"; - } -} - -static bool vklayer_config_exist(const char* vk_extension_config) -{ - const char *ev_val = getenv("VK_LAYER_PATH"); - if (ev_val == nullptr) { - return false; - } - std::stringstream filename; - filename << ev_val; - filename << "/" << vk_extension_config; - struct stat buffer; - return (stat(filename.str().c_str(), &buffer) == 0); -} - -#define __STR(A) "" #A -#define VK_CHECK(__expression) \ - do { \ - VkResult r = (__expression); \ - if (r != VK_SUCCESS) { \ - fprintf(stderr, \ - "Vulkan Error : %s:%d : %s failled with %s\n", \ - __FILE__, \ - __LINE__, \ - __STR(__expression), \ - vulkan_error_as_string(r)); \ - return GHOST_kFailure; \ - } \ - } while (0) - -#define DEBUG_PRINTF(...) \ - if (m_debug) { \ - printf(__VA_ARGS__); \ - } - -/* Triple buffering. */ -const int MAX_FRAMES_IN_FLIGHT = 2; - -GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual, -#ifdef _WIN32 - HWND hwnd, -#elif defined(__APPLE__) - CAMetalLayer *metal_layer, -#else - GHOST_TVulkanPlatformType platform, - /* X11 */ - Window window, - Display *display, - /* Wayland */ - wl_surface *wayland_surface, - wl_display *wayland_display, -#endif - int contextMajorVersion, - int contextMinorVersion, - int debug) - : GHOST_Context(stereoVisual), -#ifdef _WIN32 - m_hwnd(hwnd), -#elif defined(__APPLE__) - m_metal_layer(metal_layer), -#else - m_platform(platform), - /* X11 */ - m_display(display), - m_window(window), - /* Wayland */ - m_wayland_surface(wayland_surface), - m_wayland_display(wayland_display), -#endif - m_context_major_version(contextMajorVersion), - m_context_minor_version(contextMinorVersion), - m_debug(debug), - m_instance(VK_NULL_HANDLE), - m_physical_device(VK_NULL_HANDLE), - m_device(VK_NULL_HANDLE), - m_command_pool(VK_NULL_HANDLE), - m_surface(VK_NULL_HANDLE), - m_swapchain(VK_NULL_HANDLE), - m_render_pass(VK_NULL_HANDLE) -{ -} - -GHOST_ContextVK::~GHOST_ContextVK() -{ - if (m_device) { - vkDeviceWaitIdle(m_device); - } - - destroySwapchain(); - - if (m_command_pool != VK_NULL_HANDLE) { - vkDestroyCommandPool(m_device, m_command_pool, NULL); - } - if (m_device != VK_NULL_HANDLE) { - vkDestroyDevice(m_device, NULL); - } - if (m_surface != VK_NULL_HANDLE) { - vkDestroySurfaceKHR(m_instance, m_surface, NULL); - } - if (m_instance != VK_NULL_HANDLE) { - vkDestroyInstance(m_instance, NULL); - } -} - -GHOST_TSuccess GHOST_ContextVK::destroySwapchain() -{ - if (m_device != VK_NULL_HANDLE) { - vkDeviceWaitIdle(m_device); - } - - m_in_flight_images.resize(0); - - for (auto semaphore : m_image_available_semaphores) { - vkDestroySemaphore(m_device, semaphore, NULL); - } - for (auto semaphore : m_render_finished_semaphores) { - vkDestroySemaphore(m_device, semaphore, NULL); - } - for (auto fence : m_in_flight_fences) { - vkDestroyFence(m_device, fence, NULL); - } - for (auto framebuffer : m_swapchain_framebuffers) { - vkDestroyFramebuffer(m_device, framebuffer, NULL); - } - if (m_render_pass != VK_NULL_HANDLE) { - vkDestroyRenderPass(m_device, m_render_pass, NULL); - } - for (auto command_buffer : m_command_buffers) { - vkFreeCommandBuffers(m_device, m_command_pool, 1, &command_buffer); - } - for (auto imageView : m_swapchain_image_views) { - vkDestroyImageView(m_device, imageView, NULL); - } - if (m_swapchain != VK_NULL_HANDLE) { - vkDestroySwapchainKHR(m_device, m_swapchain, NULL); - } - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::swapBuffers() -{ - if (m_swapchain == VK_NULL_HANDLE) { - return GHOST_kFailure; - } - - vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, UINT64_MAX); - - VkResult result = vkAcquireNextImageKHR(m_device, - m_swapchain, - UINT64_MAX, - m_image_available_semaphores[m_currentFrame], - VK_NULL_HANDLE, - &m_currentImage); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ - destroySwapchain(); - createSwapchain(); - return GHOST_kSuccess; - } - else if (result != VK_SUCCESS) { - fprintf(stderr, - "Error: Failed to acquire swap chain image : %s\n", - vulkan_error_as_string(result)); - return GHOST_kFailure; - } - - /* Check if a previous frame is using this image (i.e. there is its fence to wait on) */ - if (m_in_flight_images[m_currentImage] != VK_NULL_HANDLE) { - vkWaitForFences(m_device, 1, &m_in_flight_images[m_currentImage], VK_TRUE, UINT64_MAX); - } - m_in_flight_images[m_currentImage] = m_in_flight_fences[m_currentFrame]; - - VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; - - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &m_image_available_semaphores[m_currentFrame]; - submit_info.pWaitDstStageMask = wait_stages; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &m_command_buffers[m_currentImage]; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &m_render_finished_semaphores[m_currentFrame]; - - vkResetFences(m_device, 1, &m_in_flight_fences[m_currentFrame]); - - VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, m_in_flight_fences[m_currentFrame])); - do { - result = vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, 10000); - } while (result == VK_TIMEOUT); - - VK_CHECK(vkQueueWaitIdle(m_graphic_queue)); - - VkPresentInfoKHR present_info = {}; - present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &m_render_finished_semaphores[m_currentFrame]; - present_info.swapchainCount = 1; - present_info.pSwapchains = &m_swapchain; - present_info.pImageIndices = &m_currentImage; - present_info.pResults = NULL; - - result = vkQueuePresentKHR(m_present_queue, &present_info); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ - destroySwapchain(); - createSwapchain(); - return GHOST_kSuccess; - } - else if (result != VK_SUCCESS) { - fprintf(stderr, - "Error: Failed to present swap chain image : %s\n", - vulkan_error_as_string(result)); - return GHOST_kFailure; - } - - m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer( - void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) -{ - if (m_swapchain == VK_NULL_HANDLE) { - return GHOST_kFailure; - } - *((VkImage *)image) = m_swapchain_images[m_currentImage]; - *((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage]; - *((VkRenderPass *)render_pass) = m_render_pass; - *((VkExtent2D *)extent) = m_render_extent; - *fb_id = m_swapchain_id * 10 + m_currentFrame; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance, - void *r_physical_device, - void *r_device, - uint32_t *r_graphic_queue_family, - void *r_queue) -{ - *((VkInstance *)r_instance) = m_instance; - *((VkPhysicalDevice *)r_physical_device) = m_physical_device; - *((VkDevice *)r_device) = m_device; - *r_graphic_queue_family = m_queue_family_graphic; - *((VkQueue *)r_queue) = m_graphic_queue; - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer) -{ - if (m_command_buffers.empty()) { - return GHOST_kFailure; - } - - if (m_swapchain == VK_NULL_HANDLE) { - *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0]; - } - else { - *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage]; - } - - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::activateDrawingContext() -{ - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::releaseDrawingContext() -{ - return GHOST_kSuccess; -} - -static vector getExtensionsAvailable() -{ - uint32_t extension_count = 0; - vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); - - vector extensions(extension_count); - vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions.data()); - - return extensions; -} - -static bool checkExtensionSupport(vector &extensions_available, - const char *extension_name) -{ - for (const auto &extension : extensions_available) { - if (strcmp(extension_name, extension.extensionName) == 0) { - return true; - } - } - return false; -} - -static void requireExtension(vector &extensions_available, - vector &extensions_enabled, - const char *extension_name) -{ - if (checkExtensionSupport(extensions_available, extension_name)) { - extensions_enabled.push_back(extension_name); - } - else { - fprintf(stderr, "Error: %s not found.\n", extension_name); - } -} - -static vector getLayersAvailable() -{ - uint32_t layer_count = 0; - vkEnumerateInstanceLayerProperties(&layer_count, NULL); - - vector layers(layer_count); - vkEnumerateInstanceLayerProperties(&layer_count, layers.data()); - - return layers; -} - -static bool checkLayerSupport(vector &layers_available, const char *layer_name) -{ - for (const auto &layer : layers_available) { - if (strcmp(layer_name, layer.layerName) == 0) { - return true; - } - } - return false; -} - -static void enableLayer(vector &layers_available, - vector &layers_enabled, - const char* layer_name, - const bool warning) -{ - - if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { - if (checkLayerSupport(layers_available, layer_name) && - vklayer_config_exist("VkLayer_khronos_validation.json")) { - layers_enabled.push_back(layer_name); - } - else if (warning) { - fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); - } - } - -} - -static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) -{ - uint32_t ext_count; - vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL); - - vector available_exts(ext_count); - vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, available_exts.data()); - - for (const auto &extension_needed : required_exts) { - bool found = false; - for (const auto &extension : available_exts) { - if (strcmp(extension_needed, extension.extensionName) == 0) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -GHOST_TSuccess GHOST_ContextVK::pickPhysicalDevice(vector required_exts) -{ - m_physical_device = VK_NULL_HANDLE; - - uint32_t device_count = 0; - vkEnumeratePhysicalDevices(m_instance, &device_count, NULL); - - vector physical_devices(device_count); - vkEnumeratePhysicalDevices(m_instance, &device_count, physical_devices.data()); - - int best_device_score = -1; - for (const auto &physical_device : physical_devices) { - VkPhysicalDeviceProperties device_properties; - vkGetPhysicalDeviceProperties(physical_device, &device_properties); - - VkPhysicalDeviceFeatures features; - vkGetPhysicalDeviceFeatures(physical_device, &features); - - DEBUG_PRINTF("%s : \n", device_properties.deviceName); - - if (!device_extensions_support(physical_device, required_exts)) { - DEBUG_PRINTF(" - Device does not support required device extensions.\n"); - continue; - } - - if (m_surface != VK_NULL_HANDLE) { - uint32_t format_count; - vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, m_surface, &format_count, NULL); - - uint32_t present_count; - vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, m_surface, &present_count, NULL); - - /* For now anything will do. */ - if (format_count == 0 || present_count == 0) { - DEBUG_PRINTF(" - Device does not support presentation.\n"); - continue; - } - } - - if (!features.geometryShader) { - /* Needed for wide lines emulation and barycentric coords and a few others. */ - DEBUG_PRINTF(" - Device does not support geometryShader.\n"); - } - if (!features.dualSrcBlend) { - DEBUG_PRINTF(" - Device does not support dualSrcBlend.\n"); - } - if (!features.logicOp) { - /* Needed by UI. */ - DEBUG_PRINTF(" - Device does not support logicOp.\n"); - } - -#if STRICT_REQUIREMENTS - if (!features.geometryShader || !features.dualSrcBlend || !features.logicOp) { - continue; - } -#endif - - int device_score = 0; - switch (device_properties.deviceType) { - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: - device_score = 400; - break; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: - device_score = 300; - break; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: - device_score = 200; - break; - case VK_PHYSICAL_DEVICE_TYPE_CPU: - device_score = 100; - break; - default: - break; - } - if (device_score > best_device_score) { - m_physical_device = physical_device; - best_device_score = device_score; - } - DEBUG_PRINTF(" - Device suitable.\n"); - } - - if (m_physical_device == VK_NULL_HANDLE) { - fprintf(stderr, "Error: No suitable Vulkan Device found!\n"); - return GHOST_kFailure; - } - - return GHOST_kSuccess; -} - -static GHOST_TSuccess getGraphicQueueFamily(VkPhysicalDevice device, uint32_t *r_queue_index) -{ - uint32_t queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); - - vector queue_families(queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); - - *r_queue_index = 0; - for (const auto &queue_family : queue_families) { - /* Every vulkan implementation by spec must have one queue family that support both graphics - * and compute pipelines. We select this one; compute only queue family hints at async compute - * implementations. */ - if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) && - (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) { - return GHOST_kSuccess; - } - (*r_queue_index)++; - } - - fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n"); - return GHOST_kFailure; -} - -static GHOST_TSuccess getPresetQueueFamily(VkPhysicalDevice device, - VkSurfaceKHR surface, - uint32_t *r_queue_index) -{ - uint32_t queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); - - vector queue_families(queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); - - *r_queue_index = 0; - for (int i = 0; i < queue_family_count; i++) { - VkBool32 present_support = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, *r_queue_index, surface, &present_support); - - if (present_support) { - return GHOST_kSuccess; - } - (*r_queue_index)++; - } - - fprintf(stderr, "Couldn't find any Present queue family on selected device\n"); - return GHOST_kFailure; -} - -static GHOST_TSuccess create_render_pass(VkDevice device, - VkFormat format, - VkRenderPass *r_renderPass) -{ - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - - VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, NULL, r_renderPass)); - - return GHOST_kSuccess; -} - -static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, - VkSurfaceKHR surface, - VkPresentModeKHR *r_presentMode) -{ - uint32_t present_count; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL); - vector presents(present_count); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data()); - /* MAILBOX is the lowest latency V-Sync enabled mode so use it if available */ - for (auto present_mode : presents) { - if (present_mode == VK_PRESENT_MODE_FIFO_KHR) { - *r_presentMode = present_mode; - return GHOST_kSuccess; - } - } - /* FIFO present mode is always available. */ - for (auto present_mode : presents) { - if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { - *r_presentMode = present_mode; - return GHOST_kSuccess; - } - } - - fprintf(stderr, "Error: FIFO present mode is not supported by the swap chain!\n"); - - return GHOST_kFailure; -} - -GHOST_TSuccess GHOST_ContextVK::createCommandPools() -{ - VkCommandPoolCreateInfo poolInfo = {}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = m_queue_family_graphic; - - VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool)); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer() -{ - assert(m_command_pool != VK_NULL_HANDLE); - assert(m_command_buffers.size() == 0); - m_command_buffers.resize(1); - VkCommandBufferAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.commandPool = m_command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); - - VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers() -{ - assert(m_command_pool != VK_NULL_HANDLE); - m_command_buffers.resize(m_swapchain_image_views.size()); - - VkCommandBufferAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.commandPool = m_command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); - - VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::createSwapchain() -{ - m_swapchain_id++; - - VkPhysicalDevice device = m_physical_device; - - uint32_t format_count; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, NULL); - vector formats(format_count); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, formats.data()); - - /* TODO choose appropriate format. */ - VkSurfaceFormatKHR format = formats[0]; - - VkPresentModeKHR present_mode; - if (!selectPresentMode(device, m_surface, &present_mode)) { - return GHOST_kFailure; - } - - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_surface, &capabilities); - - m_render_extent = capabilities.currentExtent; - if (m_render_extent.width == UINT32_MAX) { - /* Window Manager is going to set the surface size based on the given size. - * Choose something between minImageExtent and maxImageExtent. */ - m_render_extent.width = 1280; - m_render_extent.height = 720; - if (capabilities.minImageExtent.width > m_render_extent.width) { - m_render_extent.width = capabilities.minImageExtent.width; - } - if (capabilities.minImageExtent.height > m_render_extent.height) { - m_render_extent.height = capabilities.minImageExtent.height; - } - } - - /* Driver can stall if only using minimal image count. */ - uint32_t image_count = capabilities.minImageCount; - /* Note: maxImageCount == 0 means no limit. */ - if (image_count > capabilities.maxImageCount && capabilities.maxImageCount > 0) { - image_count = capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - create_info.surface = m_surface; - create_info.minImageCount = image_count; - create_info.imageFormat = format.format; - create_info.imageColorSpace = format.colorSpace; - create_info.imageExtent = m_render_extent; - create_info.imageArrayLayers = 1; - create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - create_info.preTransform = capabilities.currentTransform; - create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - create_info.presentMode = present_mode; - create_info.clipped = VK_TRUE; - create_info.oldSwapchain = VK_NULL_HANDLE; /* TODO Window resize */ - - uint32_t queueFamilyIndices[] = {m_queue_family_graphic, m_queue_family_present}; - - if (m_queue_family_graphic != m_queue_family_present) { - create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - create_info.queueFamilyIndexCount = 2; - create_info.pQueueFamilyIndices = queueFamilyIndices; - } - else { - create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - create_info.queueFamilyIndexCount = 0; - create_info.pQueueFamilyIndices = NULL; - } - - VK_CHECK(vkCreateSwapchainKHR(m_device, &create_info, NULL, &m_swapchain)); - - create_render_pass(m_device, format.format, &m_render_pass); - - /* image_count may not be what we requested! Getter for final value. */ - vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, NULL); - m_swapchain_images.resize(image_count); - vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, m_swapchain_images.data()); - - m_in_flight_images.resize(image_count, VK_NULL_HANDLE); - m_swapchain_image_views.resize(image_count); - m_swapchain_framebuffers.resize(image_count); - for (int i = 0; i < image_count; i++) { - VkImageViewCreateInfo view_create_info = {}; - view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_create_info.image = m_swapchain_images[i]; - view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_create_info.format = format.format; - view_create_info.components = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - }; - view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_create_info.subresourceRange.baseMipLevel = 0; - view_create_info.subresourceRange.levelCount = 1; - view_create_info.subresourceRange.baseArrayLayer = 0; - view_create_info.subresourceRange.layerCount = 1; - - VK_CHECK(vkCreateImageView(m_device, &view_create_info, NULL, &m_swapchain_image_views[i])); - - VkImageView attachments[] = {m_swapchain_image_views[i]}; - - VkFramebufferCreateInfo fb_create_info = {}; - fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - fb_create_info.renderPass = m_render_pass; - fb_create_info.attachmentCount = 1; - fb_create_info.pAttachments = attachments; - fb_create_info.width = m_render_extent.width; - fb_create_info.height = m_render_extent.height; - fb_create_info.layers = 1; - - VK_CHECK(vkCreateFramebuffer(m_device, &fb_create_info, NULL, &m_swapchain_framebuffers[i])); - } - - m_image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT); - m_render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT); - m_in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT); - for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - - VkSemaphoreCreateInfo semaphore_info = {}; - semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_image_available_semaphores[i])); - VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_render_finished_semaphores[i])); - - VkFenceCreateInfo fence_info = {}; - fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - VK_CHECK(vkCreateFence(m_device, &fence_info, NULL, &m_in_flight_fences[i])); - } - - createGraphicsCommandBuffers(); - - return GHOST_kSuccess; -} - -const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const -{ -#ifdef _WIN32 - return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; -#elif defined(__APPLE__) - return VK_EXT_METAL_SURFACE_EXTENSION_NAME; -#else /* UNIX/Linux */ - switch (m_platform) { - case GHOST_kVulkanPlatformX11: - return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; - break; -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: - return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; - break; -# endif - } -#endif - return NULL; -} - -GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() -{ -#ifdef _WIN32 - const bool use_window_surface = (m_hwnd != NULL); -#elif defined(__APPLE__) - const bool use_window_surface = (m_metal_layer != NULL); -#else /* UNIX/Linux */ - bool use_window_surface = false; - switch (m_platform) { - case GHOST_kVulkanPlatformX11: - use_window_surface = (m_display != NULL) && (m_window != (Window)NULL); - break; -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: - use_window_surface = (m_wayland_display != NULL) && (m_wayland_surface != NULL); - break; -# endif - } -#endif - - auto layers_available = getLayersAvailable(); - auto extensions_available = getExtensionsAvailable(); - - vector layers_enabled; - if (m_debug) { - enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); - } - - vector extensions_device; - vector extensions_enabled; - - if (use_window_surface) { - const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension(); - - requireExtension(extensions_available, extensions_enabled, "VK_KHR_surface"); - requireExtension(extensions_available, extensions_enabled, native_surface_extension_name); - - extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - } - extensions_device.push_back("VK_KHR_dedicated_allocation"); - extensions_device.push_back("VK_KHR_get_memory_requirements2"); - /* Enable MoltenVK required instance extensions. */ -#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME - requireExtension( - extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2"); -#endif - - VkApplicationInfo app_info = {}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pApplicationName = "Blender"; - app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - app_info.pEngineName = "Blender"; - app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); - app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0); - - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.pApplicationInfo = &app_info; - create_info.enabledLayerCount = static_cast(layers_enabled.size()); - create_info.ppEnabledLayerNames = layers_enabled.data(); - create_info.enabledExtensionCount = static_cast(extensions_enabled.size()); - create_info.ppEnabledExtensionNames = extensions_enabled.data(); - - VK_CHECK(vkCreateInstance(&create_info, NULL, &m_instance)); - - if (use_window_surface) { -#ifdef _WIN32 - VkWin32SurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surface_create_info.hinstance = GetModuleHandle(NULL); - surface_create_info.hwnd = m_hwnd; - VK_CHECK(vkCreateWin32SurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); -#elif defined(__APPLE__) - VkMetalSurfaceCreateInfoEXT info = {}; - info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; - info.pNext = NULL; - info.flags = 0; - info.pLayer = m_metal_layer; - VK_CHECK(vkCreateMetalSurfaceEXT(m_instance, &info, nullptr, &m_surface)); -#else - switch (m_platform) { - case GHOST_kVulkanPlatformX11: { - VkXlibSurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - surface_create_info.dpy = m_display; - surface_create_info.window = m_window; - VK_CHECK(vkCreateXlibSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); - break; - } -# ifdef WITH_GHOST_WAYLAND - case GHOST_kVulkanPlatformWayland: { - VkWaylandSurfaceCreateInfoKHR surface_create_info = {}; - surface_create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - surface_create_info.display = m_wayland_display; - surface_create_info.surface = m_wayland_surface; - VK_CHECK(vkCreateWaylandSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); - break; - } -# endif - } - -#endif - } - - if (!pickPhysicalDevice(extensions_device)) { - return GHOST_kFailure; - } - -#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME - /* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be - * enabled. See - * https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/ - if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) { - extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - } -#endif - - vector queue_create_infos; - - { - /* A graphic queue is required to draw anything. */ - if (!getGraphicQueueFamily(m_physical_device, &m_queue_family_graphic)) { - return GHOST_kFailure; - } - - float queue_priorities[] = {1.0f}; - VkDeviceQueueCreateInfo graphic_queue_create_info = {}; - graphic_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - graphic_queue_create_info.queueFamilyIndex = m_queue_family_graphic; - graphic_queue_create_info.queueCount = 1; - graphic_queue_create_info.pQueuePriorities = queue_priorities; - queue_create_infos.push_back(graphic_queue_create_info); - } - - if (use_window_surface) { - /* A present queue is required only if we render to a window. */ - if (!getPresetQueueFamily(m_physical_device, m_surface, &m_queue_family_present)) { - return GHOST_kFailure; - } - - float queue_priorities[] = {1.0f}; - VkDeviceQueueCreateInfo present_queue_create_info = {}; - present_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - present_queue_create_info.queueFamilyIndex = m_queue_family_present; - present_queue_create_info.queueCount = 1; - present_queue_create_info.pQueuePriorities = queue_priorities; - - /* Each queue must be unique. */ - if (m_queue_family_graphic != m_queue_family_present) { - queue_create_infos.push_back(present_queue_create_info); - } - } - - VkPhysicalDeviceFeatures device_features = {}; -#if STRICT_REQUIREMENTS - device_features.geometryShader = VK_TRUE; - device_features.dualSrcBlend = VK_TRUE; - device_features.logicOp = VK_TRUE; -#endif - - VkDeviceCreateInfo device_create_info = {}; - device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - device_create_info.queueCreateInfoCount = static_cast(queue_create_infos.size()); - device_create_info.pQueueCreateInfos = queue_create_infos.data(); - /* layers_enabled are the same as instance extensions. - * This is only needed for 1.0 implementations. */ - device_create_info.enabledLayerCount = static_cast(layers_enabled.size()); - device_create_info.ppEnabledLayerNames = layers_enabled.data(); - device_create_info.enabledExtensionCount = static_cast(extensions_device.size()); - device_create_info.ppEnabledExtensionNames = extensions_device.data(); - device_create_info.pEnabledFeatures = &device_features; - - VK_CHECK(vkCreateDevice(m_physical_device, &device_create_info, NULL, &m_device)); - - vkGetDeviceQueue(m_device, m_queue_family_graphic, 0, &m_graphic_queue); - - createCommandPools(); - if (use_window_surface) { - vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); - createSwapchain(); - } - else { - createGraphicsCommandBuffer(); - } - return GHOST_kSuccess; -} - -GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles() -{ - return GHOST_kSuccess; -} +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#include "GHOST_ContextVK.h" + +#ifdef _WIN32 +# include +#elif defined(__APPLE__) +# include +#else /* X11 */ +# include +# ifdef WITH_GHOST_WAYLAND +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include + +/* Set to 0 to allow devices that do not have the required features. + * This allows development on OSX until we really needs these features. */ +#define STRICT_REQUIREMENTS 1 + +using namespace std; + +static const char *vulkan_error_as_string(VkResult result) +{ +#define FORMAT_ERROR(X) \ + case X: { \ + return "" #X; \ + } + + switch (result) { + FORMAT_ERROR(VK_NOT_READY); + FORMAT_ERROR(VK_TIMEOUT); + FORMAT_ERROR(VK_EVENT_SET); + FORMAT_ERROR(VK_EVENT_RESET); + FORMAT_ERROR(VK_INCOMPLETE); + FORMAT_ERROR(VK_ERROR_OUT_OF_HOST_MEMORY); + FORMAT_ERROR(VK_ERROR_OUT_OF_DEVICE_MEMORY); + FORMAT_ERROR(VK_ERROR_INITIALIZATION_FAILED); + FORMAT_ERROR(VK_ERROR_DEVICE_LOST); + FORMAT_ERROR(VK_ERROR_MEMORY_MAP_FAILED); + FORMAT_ERROR(VK_ERROR_LAYER_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_EXTENSION_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_FEATURE_NOT_PRESENT); + FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DRIVER); + FORMAT_ERROR(VK_ERROR_TOO_MANY_OBJECTS); + FORMAT_ERROR(VK_ERROR_FORMAT_NOT_SUPPORTED); + FORMAT_ERROR(VK_ERROR_FRAGMENTED_POOL); + FORMAT_ERROR(VK_ERROR_UNKNOWN); + FORMAT_ERROR(VK_ERROR_OUT_OF_POOL_MEMORY); + FORMAT_ERROR(VK_ERROR_INVALID_EXTERNAL_HANDLE); + FORMAT_ERROR(VK_ERROR_FRAGMENTATION); + FORMAT_ERROR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); + FORMAT_ERROR(VK_ERROR_SURFACE_LOST_KHR); + FORMAT_ERROR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + FORMAT_ERROR(VK_SUBOPTIMAL_KHR); + FORMAT_ERROR(VK_ERROR_OUT_OF_DATE_KHR); + FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + FORMAT_ERROR(VK_ERROR_VALIDATION_FAILED_EXT); + FORMAT_ERROR(VK_ERROR_INVALID_SHADER_NV); + FORMAT_ERROR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); + FORMAT_ERROR(VK_ERROR_NOT_PERMITTED_EXT); + FORMAT_ERROR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); + FORMAT_ERROR(VK_THREAD_IDLE_KHR); + FORMAT_ERROR(VK_THREAD_DONE_KHR); + FORMAT_ERROR(VK_OPERATION_DEFERRED_KHR); + FORMAT_ERROR(VK_OPERATION_NOT_DEFERRED_KHR); + FORMAT_ERROR(VK_PIPELINE_COMPILE_REQUIRED_EXT); + default: + return "Unknown Error"; + } +} + +static bool vklayer_config_exist(const char* vk_extension_config) +{ + const char *ev_val = getenv("VK_LAYER_PATH"); + if (ev_val == nullptr) { + return false; + } + std::stringstream filename; + filename << ev_val; + filename << "/" << vk_extension_config; + struct stat buffer; + return (stat(filename.str().c_str(), &buffer) == 0); +} + +#define __STR(A) "" #A +#define VK_CHECK(__expression) \ + do { \ + VkResult r = (__expression); \ + if (r != VK_SUCCESS) { \ + fprintf(stderr, \ + "Vulkan Error : %s:%d : %s failled with %s\n", \ + __FILE__, \ + __LINE__, \ + __STR(__expression), \ + vulkan_error_as_string(r)); \ + return GHOST_kFailure; \ + } \ + } while (0) + +#define DEBUG_PRINTF(...) \ + if (m_debug) { \ + printf(__VA_ARGS__); \ + } + +/* Triple buffering. */ +const int MAX_FRAMES_IN_FLIGHT = 2; + +GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual, +#ifdef _WIN32 + HWND hwnd, +#elif defined(__APPLE__) + CAMetalLayer *metal_layer, +#else + GHOST_TVulkanPlatformType platform, + /* X11 */ + Window window, + Display *display, + /* Wayland */ + wl_surface *wayland_surface, + wl_display *wayland_display, +#endif + int contextMajorVersion, + int contextMinorVersion, + int debug) + : GHOST_Context(stereoVisual), +#ifdef _WIN32 + m_hwnd(hwnd), +#elif defined(__APPLE__) + m_metal_layer(metal_layer), +#else + m_platform(platform), + /* X11 */ + m_display(display), + m_window(window), + /* Wayland */ + m_wayland_surface(wayland_surface), + m_wayland_display(wayland_display), +#endif + m_context_major_version(contextMajorVersion), + m_context_minor_version(contextMinorVersion), + m_debug(debug), + m_instance(VK_NULL_HANDLE), + m_physical_device(VK_NULL_HANDLE), + m_device(VK_NULL_HANDLE), + m_command_pool(VK_NULL_HANDLE), + m_surface(VK_NULL_HANDLE), + m_swapchain(VK_NULL_HANDLE), + m_render_pass(VK_NULL_HANDLE) +{ +} + +GHOST_ContextVK::~GHOST_ContextVK() +{ + if (m_device) { + vkDeviceWaitIdle(m_device); + } + + destroySwapchain(); + + if (m_command_pool != VK_NULL_HANDLE) { + vkDestroyCommandPool(m_device, m_command_pool, NULL); + } + if (m_device != VK_NULL_HANDLE) { + vkDestroyDevice(m_device, NULL); + } + if (m_surface != VK_NULL_HANDLE) { + vkDestroySurfaceKHR(m_instance, m_surface, NULL); + } + if (m_instance != VK_NULL_HANDLE) { + vkDestroyInstance(m_instance, NULL); + } +} + +GHOST_TSuccess GHOST_ContextVK::destroySwapchain() +{ + if (m_device != VK_NULL_HANDLE) { + vkDeviceWaitIdle(m_device); + } + + m_in_flight_images.resize(0); + + for (auto semaphore : m_image_available_semaphores) { + vkDestroySemaphore(m_device, semaphore, NULL); + } + for (auto semaphore : m_render_finished_semaphores) { + vkDestroySemaphore(m_device, semaphore, NULL); + } + for (auto fence : m_in_flight_fences) { + vkDestroyFence(m_device, fence, NULL); + } + for (auto framebuffer : m_swapchain_framebuffers) { + vkDestroyFramebuffer(m_device, framebuffer, NULL); + } + if (m_render_pass != VK_NULL_HANDLE) { + vkDestroyRenderPass(m_device, m_render_pass, NULL); + } + for (auto command_buffer : m_command_buffers) { + vkFreeCommandBuffers(m_device, m_command_pool, 1, &command_buffer); + } + for (auto imageView : m_swapchain_image_views) { + vkDestroyImageView(m_device, imageView, NULL); + } + if (m_swapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(m_device, m_swapchain, NULL); + } + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::swapBuffers() +{ + if (m_swapchain == VK_NULL_HANDLE) { + return GHOST_kFailure; + } + + vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, UINT64_MAX); + + VkResult result = vkAcquireNextImageKHR(m_device, + m_swapchain, + UINT64_MAX, + m_image_available_semaphores[m_currentFrame], + VK_NULL_HANDLE, + &m_currentImage); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ + destroySwapchain(); + createSwapchain(); + return GHOST_kSuccess; + } + else if (result != VK_SUCCESS) { + fprintf(stderr, + "Error: Failed to acquire swap chain image : %s\n", + vulkan_error_as_string(result)); + return GHOST_kFailure; + } + + /* Check if a previous frame is using this image (i.e. there is its fence to wait on) */ + if (m_in_flight_images[m_currentImage] != VK_NULL_HANDLE) { + vkWaitForFences(m_device, 1, &m_in_flight_images[m_currentImage], VK_TRUE, UINT64_MAX); + } + m_in_flight_images[m_currentImage] = m_in_flight_fences[m_currentFrame]; + + VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &m_image_available_semaphores[m_currentFrame]; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &m_command_buffers[m_currentImage]; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &m_render_finished_semaphores[m_currentFrame]; + + vkResetFences(m_device, 1, &m_in_flight_fences[m_currentFrame]); + + VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, m_in_flight_fences[m_currentFrame])); + do { + result = vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, 10000); + } while (result == VK_TIMEOUT); + + VK_CHECK(vkQueueWaitIdle(m_graphic_queue)); + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &m_render_finished_semaphores[m_currentFrame]; + present_info.swapchainCount = 1; + present_info.pSwapchains = &m_swapchain; + present_info.pImageIndices = &m_currentImage; + present_info.pResults = NULL; + + result = vkQueuePresentKHR(m_present_queue, &present_info); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */ + destroySwapchain(); + createSwapchain(); + return GHOST_kSuccess; + } + else if (result != VK_SUCCESS) { + fprintf(stderr, + "Error: Failed to present swap chain image : %s\n", + vulkan_error_as_string(result)); + return GHOST_kFailure; + } + + m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer( + void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) +{ + if (m_swapchain == VK_NULL_HANDLE) { + return GHOST_kFailure; + } + *((VkImage *)image) = m_swapchain_images[m_currentImage]; + *((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage]; + *((VkRenderPass *)render_pass) = m_render_pass; + *((VkExtent2D *)extent) = m_render_extent; + *fb_id = m_swapchain_id * 10 + m_currentFrame; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_family, + void *r_queue) +{ + *((VkInstance *)r_instance) = m_instance; + *((VkPhysicalDevice *)r_physical_device) = m_physical_device; + *((VkDevice *)r_device) = m_device; + *r_graphic_queue_family = m_queue_family_graphic; + *((VkQueue *)r_queue) = m_graphic_queue; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer) +{ + if (m_command_buffers.empty()) { + return GHOST_kFailure; + } + + if (m_swapchain == VK_NULL_HANDLE) { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0]; + } + else { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage]; + } + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::activateDrawingContext() +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::releaseDrawingContext() +{ + return GHOST_kSuccess; +} + +static vector getExtensionsAvailable() +{ + uint32_t extension_count = 0; + vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); + + vector extensions(extension_count); + vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions.data()); + + return extensions; +} + +static bool checkExtensionSupport(vector &extensions_available, + const char *extension_name) +{ + for (const auto &extension : extensions_available) { + if (strcmp(extension_name, extension.extensionName) == 0) { + return true; + } + } + return false; +} + +static void requireExtension(vector &extensions_available, + vector &extensions_enabled, + const char *extension_name) +{ + if (checkExtensionSupport(extensions_available, extension_name)) { + extensions_enabled.push_back(extension_name); + } + else { + fprintf(stderr, "Error: %s not found.\n", extension_name); + } +} + +static vector getLayersAvailable() +{ + uint32_t layer_count = 0; + vkEnumerateInstanceLayerProperties(&layer_count, NULL); + + vector layers(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, layers.data()); + + return layers; +} + +static bool checkLayerSupport(vector &layers_available, const char *layer_name) +{ + for (const auto &layer : layers_available) { + if (strcmp(layer_name, layer.layerName) == 0) { + return true; + } + } + return false; +} + +static void enableLayer(vector &layers_available, + vector &layers_enabled, + const char* layer_name, + const bool warning) +{ + + if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { + if (checkLayerSupport(layers_available, layer_name) && + vklayer_config_exist("VkLayer_khronos_validation.json")) { + layers_enabled.push_back(layer_name); + } + else if (warning) { + fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); + } + } + +} + +static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) +{ + uint32_t ext_count; + vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL); + + vector available_exts(ext_count); + vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, available_exts.data()); + + for (const auto &extension_needed : required_exts) { + bool found = false; + for (const auto &extension : available_exts) { + if (strcmp(extension_needed, extension.extensionName) == 0) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; +} + +GHOST_TSuccess GHOST_ContextVK::pickPhysicalDevice(vector required_exts) +{ + m_physical_device = VK_NULL_HANDLE; + + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(m_instance, &device_count, NULL); + + vector physical_devices(device_count); + vkEnumeratePhysicalDevices(m_instance, &device_count, physical_devices.data()); + + int best_device_score = -1; + for (const auto &physical_device : physical_devices) { + VkPhysicalDeviceProperties device_properties; + vkGetPhysicalDeviceProperties(physical_device, &device_properties); + + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physical_device, &features); + + DEBUG_PRINTF("%s : \n", device_properties.deviceName); + + if (!device_extensions_support(physical_device, required_exts)) { + DEBUG_PRINTF(" - Device does not support required device extensions.\n"); + continue; + } + + if (m_surface != VK_NULL_HANDLE) { + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, m_surface, &format_count, NULL); + + uint32_t present_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, m_surface, &present_count, NULL); + + /* For now anything will do. */ + if (format_count == 0 || present_count == 0) { + DEBUG_PRINTF(" - Device does not support presentation.\n"); + continue; + } + } + + if (!features.geometryShader) { + /* Needed for wide lines emulation and barycentric coords and a few others. */ + DEBUG_PRINTF(" - Device does not support geometryShader.\n"); + } + if (!features.dualSrcBlend) { + DEBUG_PRINTF(" - Device does not support dualSrcBlend.\n"); + } + if (!features.logicOp) { + /* Needed by UI. */ + DEBUG_PRINTF(" - Device does not support logicOp.\n"); + } + +#if STRICT_REQUIREMENTS + if (!features.geometryShader || !features.dualSrcBlend || !features.logicOp) { + continue; + } +#endif + + int device_score = 0; + switch (device_properties.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + device_score = 400; + break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + device_score = 300; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + device_score = 200; + break; + case VK_PHYSICAL_DEVICE_TYPE_CPU: + device_score = 100; + break; + default: + break; + } + if (device_score > best_device_score) { + m_physical_device = physical_device; + best_device_score = device_score; + } + DEBUG_PRINTF(" - Device suitable.\n"); + } + + if (m_physical_device == VK_NULL_HANDLE) { + fprintf(stderr, "Error: No suitable Vulkan Device found!\n"); + return GHOST_kFailure; + } + + return GHOST_kSuccess; +} + +static GHOST_TSuccess getGraphicQueueFamily(VkPhysicalDevice device, uint32_t *r_queue_index) +{ + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); + + vector queue_families(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + *r_queue_index = 0; + for (const auto &queue_family : queue_families) { + /* Every vulkan implementation by spec must have one queue family that support both graphics + * and compute pipelines. We select this one; compute only queue family hints at async compute + * implementations. */ + if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) && + (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) { + return GHOST_kSuccess; + } + (*r_queue_index)++; + } + + fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n"); + return GHOST_kFailure; +} + +static GHOST_TSuccess getPresetQueueFamily(VkPhysicalDevice device, + VkSurfaceKHR surface, + uint32_t *r_queue_index) +{ + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL); + + vector queue_families(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + *r_queue_index = 0; + for (int i = 0; i < queue_family_count; i++) { + VkBool32 present_support = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, *r_queue_index, surface, &present_support); + + if (present_support) { + return GHOST_kSuccess; + } + (*r_queue_index)++; + } + + fprintf(stderr, "Couldn't find any Present queue family on selected device\n"); + return GHOST_kFailure; +} + +static GHOST_TSuccess create_render_pass(VkDevice device, + VkFormat format, + VkRenderPass *r_renderPass) +{ + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = format; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, NULL, r_renderPass)); + + return GHOST_kSuccess; +} + +static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, + VkSurfaceKHR surface, + VkPresentModeKHR *r_presentMode) +{ + uint32_t present_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL); + vector presents(present_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data()); + /* MAILBOX is the lowest latency V-Sync enabled mode so use it if available */ + for (auto present_mode : presents) { + if (present_mode == VK_PRESENT_MODE_FIFO_KHR) { + *r_presentMode = present_mode; + return GHOST_kSuccess; + } + } + /* FIFO present mode is always available. */ + for (auto present_mode : presents) { + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { + *r_presentMode = present_mode; + return GHOST_kSuccess; + } + } + + fprintf(stderr, "Error: FIFO present mode is not supported by the swap chain!\n"); + + return GHOST_kFailure; +} + +GHOST_TSuccess GHOST_ContextVK::createCommandPools() +{ + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = m_queue_family_graphic; + + VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool)); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer() +{ + assert(m_command_pool != VK_NULL_HANDLE); + assert(m_command_buffers.size() == 0); + m_command_buffers.resize(1); + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = m_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); + + VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers() +{ + assert(m_command_pool != VK_NULL_HANDLE); + m_command_buffers.resize(m_swapchain_image_views.size()); + + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = m_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); + + VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createSwapchain() +{ + m_swapchain_id++; + + VkPhysicalDevice device = m_physical_device; + + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, NULL); + vector formats(format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_surface, &format_count, formats.data()); + + /* TODO choose appropriate format. */ + VkSurfaceFormatKHR format = formats[0]; + + VkPresentModeKHR present_mode; + if (!selectPresentMode(device, m_surface, &present_mode)) { + return GHOST_kFailure; + } + + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_surface, &capabilities); + + m_render_extent = capabilities.currentExtent; + if (m_render_extent.width == UINT32_MAX) { + /* Window Manager is going to set the surface size based on the given size. + * Choose something between minImageExtent and maxImageExtent. */ + m_render_extent.width = 1280; + m_render_extent.height = 720; + if (capabilities.minImageExtent.width > m_render_extent.width) { + m_render_extent.width = capabilities.minImageExtent.width; + } + if (capabilities.minImageExtent.height > m_render_extent.height) { + m_render_extent.height = capabilities.minImageExtent.height; + } + } + + /* Driver can stall if only using minimal image count. */ + uint32_t image_count = capabilities.minImageCount; + /* Note: maxImageCount == 0 means no limit. */ + if (image_count > capabilities.maxImageCount && capabilities.maxImageCount > 0) { + image_count = capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.surface = m_surface; + create_info.minImageCount = image_count; + create_info.imageFormat = format.format; + create_info.imageColorSpace = format.colorSpace; + create_info.imageExtent = m_render_extent; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + create_info.preTransform = capabilities.currentTransform; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = present_mode; + create_info.clipped = VK_TRUE; + create_info.oldSwapchain = VK_NULL_HANDLE; /* TODO Window resize */ + + uint32_t queueFamilyIndices[] = {m_queue_family_graphic, m_queue_family_present}; + + if (m_queue_family_graphic != m_queue_family_present) { + create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + create_info.queueFamilyIndexCount = 2; + create_info.pQueueFamilyIndices = queueFamilyIndices; + } + else { + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = NULL; + } + + VK_CHECK(vkCreateSwapchainKHR(m_device, &create_info, NULL, &m_swapchain)); + + create_render_pass(m_device, format.format, &m_render_pass); + + /* image_count may not be what we requested! Getter for final value. */ + vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, NULL); + m_swapchain_images.resize(image_count); + vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, m_swapchain_images.data()); + + m_in_flight_images.resize(image_count, VK_NULL_HANDLE); + m_swapchain_image_views.resize(image_count); + m_swapchain_framebuffers.resize(image_count); + for (int i = 0; i < image_count; i++) { + VkImageViewCreateInfo view_create_info = {}; + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.image = m_swapchain_images[i]; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = format.format; + view_create_info.components = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_create_info.subresourceRange.baseMipLevel = 0; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.baseArrayLayer = 0; + view_create_info.subresourceRange.layerCount = 1; + + VK_CHECK(vkCreateImageView(m_device, &view_create_info, NULL, &m_swapchain_image_views[i])); + + VkImageView attachments[] = {m_swapchain_image_views[i]}; + + VkFramebufferCreateInfo fb_create_info = {}; + fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_create_info.renderPass = m_render_pass; + fb_create_info.attachmentCount = 1; + fb_create_info.pAttachments = attachments; + fb_create_info.width = m_render_extent.width; + fb_create_info.height = m_render_extent.height; + fb_create_info.layers = 1; + + VK_CHECK(vkCreateFramebuffer(m_device, &fb_create_info, NULL, &m_swapchain_framebuffers[i])); + } + + m_image_available_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_render_finished_semaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT); + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + + VkSemaphoreCreateInfo semaphore_info = {}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_image_available_semaphores[i])); + VK_CHECK(vkCreateSemaphore(m_device, &semaphore_info, NULL, &m_render_finished_semaphores[i])); + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + VK_CHECK(vkCreateFence(m_device, &fence_info, NULL, &m_in_flight_fences[i])); + } + + createGraphicsCommandBuffers(); + + return GHOST_kSuccess; +} + +const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const +{ +#ifdef _WIN32 + return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; +#elif defined(__APPLE__) + return VK_EXT_METAL_SURFACE_EXTENSION_NAME; +#else /* UNIX/Linux */ + switch (m_platform) { + case GHOST_kVulkanPlatformX11: + return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; + break; +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: + return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; + break; +# endif + } +#endif + return NULL; +} + +GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() +{ +#ifdef _WIN32 + const bool use_window_surface = (m_hwnd != NULL); +#elif defined(__APPLE__) + const bool use_window_surface = (m_metal_layer != NULL); +#else /* UNIX/Linux */ + bool use_window_surface = false; + switch (m_platform) { + case GHOST_kVulkanPlatformX11: + use_window_surface = (m_display != NULL) && (m_window != (Window)NULL); + break; +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: + use_window_surface = (m_wayland_display != NULL) && (m_wayland_surface != NULL); + break; +# endif + } +#endif + + auto layers_available = getLayersAvailable(); + auto extensions_available = getExtensionsAvailable(); + + vector layers_enabled; + if (m_debug) { + enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); + } + + vector extensions_device; + vector extensions_enabled; + + if (use_window_surface) { + const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension(); + + requireExtension(extensions_available, extensions_enabled, "VK_KHR_surface"); + requireExtension(extensions_available, extensions_enabled, native_surface_extension_name); + + extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } + extensions_device.push_back("VK_KHR_dedicated_allocation"); + extensions_device.push_back("VK_KHR_get_memory_requirements2"); + /* Enable MoltenVK required instance extensions. */ +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + requireExtension( + extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2"); +#endif + + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "Blender"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "Blender"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0); + + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = static_cast(layers_enabled.size()); + create_info.ppEnabledLayerNames = layers_enabled.data(); + create_info.enabledExtensionCount = static_cast(extensions_enabled.size()); + create_info.ppEnabledExtensionNames = extensions_enabled.data(); + + VK_CHECK(vkCreateInstance(&create_info, NULL, &m_instance)); + + if (use_window_surface) { +#ifdef _WIN32 + VkWin32SurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surface_create_info.hinstance = GetModuleHandle(NULL); + surface_create_info.hwnd = m_hwnd; + VK_CHECK(vkCreateWin32SurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); +#elif defined(__APPLE__) + VkMetalSurfaceCreateInfoEXT info = {}; + info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + info.pNext = NULL; + info.flags = 0; + info.pLayer = m_metal_layer; + VK_CHECK(vkCreateMetalSurfaceEXT(m_instance, &info, nullptr, &m_surface)); +#else + switch (m_platform) { + case GHOST_kVulkanPlatformX11: { + VkXlibSurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surface_create_info.dpy = m_display; + surface_create_info.window = m_window; + VK_CHECK(vkCreateXlibSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); + break; + } +# ifdef WITH_GHOST_WAYLAND + case GHOST_kVulkanPlatformWayland: { + VkWaylandSurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + surface_create_info.display = m_wayland_display; + surface_create_info.surface = m_wayland_surface; + VK_CHECK(vkCreateWaylandSurfaceKHR(m_instance, &surface_create_info, NULL, &m_surface)); + break; + } +# endif + } + +#endif + } + + if (!pickPhysicalDevice(extensions_device)) { + return GHOST_kFailure; + } + +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + /* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be + * enabled. See + * https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/ + if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) { + extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + } +#endif + + vector queue_create_infos; + + { + /* A graphic queue is required to draw anything. */ + if (!getGraphicQueueFamily(m_physical_device, &m_queue_family_graphic)) { + return GHOST_kFailure; + } + + float queue_priorities[] = {1.0f}; + VkDeviceQueueCreateInfo graphic_queue_create_info = {}; + graphic_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + graphic_queue_create_info.queueFamilyIndex = m_queue_family_graphic; + graphic_queue_create_info.queueCount = 1; + graphic_queue_create_info.pQueuePriorities = queue_priorities; + queue_create_infos.push_back(graphic_queue_create_info); + } + + if (use_window_surface) { + /* A present queue is required only if we render to a window. */ + if (!getPresetQueueFamily(m_physical_device, m_surface, &m_queue_family_present)) { + return GHOST_kFailure; + } + + float queue_priorities[] = {1.0f}; + VkDeviceQueueCreateInfo present_queue_create_info = {}; + present_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + present_queue_create_info.queueFamilyIndex = m_queue_family_present; + present_queue_create_info.queueCount = 1; + present_queue_create_info.pQueuePriorities = queue_priorities; + + /* Each queue must be unique. */ + if (m_queue_family_graphic != m_queue_family_present) { + queue_create_infos.push_back(present_queue_create_info); + } + } + + VkPhysicalDeviceFeatures device_features = {}; +#if STRICT_REQUIREMENTS + device_features.geometryShader = VK_TRUE; + device_features.dualSrcBlend = VK_TRUE; + device_features.logicOp = VK_TRUE; +#endif + + VkDeviceCreateInfo device_create_info = {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.queueCreateInfoCount = static_cast(queue_create_infos.size()); + device_create_info.pQueueCreateInfos = queue_create_infos.data(); + /* layers_enabled are the same as instance extensions. + * This is only needed for 1.0 implementations. */ + device_create_info.enabledLayerCount = static_cast(layers_enabled.size()); + device_create_info.ppEnabledLayerNames = layers_enabled.data(); + device_create_info.enabledExtensionCount = static_cast(extensions_device.size()); + device_create_info.ppEnabledExtensionNames = extensions_device.data(); + device_create_info.pEnabledFeatures = &device_features; + + VK_CHECK(vkCreateDevice(m_physical_device, &device_create_info, NULL, &m_device)); + + vkGetDeviceQueue(m_device, m_queue_family_graphic, 0, &m_graphic_queue); + + createCommandPools(); + if (use_window_surface) { + vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); + createSwapchain(); + } + else { + createGraphicsCommandBuffer(); + } + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles() +{ + return GHOST_kSuccess; +} -- 2.30.2 From 69ac0203d43f3c509ee8ce6fd5e1a1761979aff1 Mon Sep 17 00:00:00 2001 From: AgAmemnno Date: Tue, 28 Mar 2023 02:48:02 +0900 Subject: [PATCH 13/13] FIX:Added VkLayerEnum --- intern/ghost/intern/GHOST_ContextVK.cpp | 49 ++++++++++++++++++------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 65a33a468db..b6d64b157eb 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -19,13 +19,14 @@ #include -#include #include #include #include #include #include +#include + /* Set to 0 to allow devices that do not have the required features. * This allows development on OSX until we really needs these features. */ #define STRICT_REQUIREMENTS 1 @@ -82,7 +83,9 @@ static const char *vulkan_error_as_string(VkResult result) } } -static bool vklayer_config_exist(const char* vk_extension_config) +enum class VkLayer : uint8_t { KHRONOS_validation }; + +static bool vklayer_config_exist(const char *vk_extension_config) { const char *ev_val = getenv("VK_LAYER_PATH"); if (ev_val == nullptr) { @@ -416,20 +419,38 @@ static bool checkLayerSupport(vector &layers_available, const static void enableLayer(vector &layers_available, vector &layers_enabled, - const char* layer_name, - const bool warning) + const VkLayer layer, + const bool display_warning) { - - if (strcmp(layer_name, "VK_LAYER_KHRONOS_validation") == 0) { - if (checkLayerSupport(layers_available, layer_name) && - vklayer_config_exist("VkLayer_khronos_validation.json")) { - layers_enabled.push_back(layer_name); - } - else if (warning) { - fprintf(stderr,"Warning: Layer requested, but not supported by the platform. [%s]\n",layer_name); - } +#define PUSH_VKLAYER(name, name2) \ + if (vklayer_config_exist("VkLayer_" #name ".json") && \ + checkLayerSupport(layers_available, "VK_LAYER_" #name2)) { \ + layers_enabled.push_back("VK_LAYER_" #name2); \ + enabled = true; \ + } \ + else { \ + warnings << "VK_LAYER_" #name2; \ } + bool enabled = false; + std::stringstream warnings; + + switch (layer) { + case VkLayer::KHRONOS_validation: + PUSH_VKLAYER(khronos_validation, KHRONOS_validation); + }; + + if (enabled) { + return; + } + + if (display_warning) { + fprintf(stderr, + "Warning: Layer requested, but not supported by the platform. [%s] \n", + warnings.str().c_str()); + } + +#undef PUSH_VKLAYER } static bool device_extensions_support(VkPhysicalDevice device, vector required_exts) @@ -883,7 +904,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vector layers_enabled; if (m_debug) { - enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug); + enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug); } vector extensions_device; -- 2.30.2