From 6dac345a64b1ab6d58ce838e9acd7f09af43a77f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 22 Nov 2022 11:28:37 +0100 Subject: [PATCH] GHOST: Vulkan Backend. This adds a vulkan backend to GHOST. The code was extracted from the tmp-vulkan branch. The main difference with the original code is that GHOST isn't responsible for fallback. For Metal backend there is already an idea that the GPU module is responsible for the fallback, not the system. For Blender we target Vulkan 1.2 at the time of this patch. MoltenVK (needed to convert Vulkan calls to Metal) has been added as a separate package. This patch isn't useful for end-users, currently when starting blender with `--gpu-backend vulkan` it would crash as the `VBBackend` doesn't initialize the expected global structs in the GPU module. Validated to be working on Windows and Apple. Linux still needs to be tested. Reviewed By: fclem Differential Revision: https://developer.blender.org/D13155 --- CMakeLists.txt | 8 +- build_files/cmake/Modules/FindMoltenVK.cmake | 59 ++ .../cmake/platform/platform_apple.cmake | 5 + .../cmake/platform/platform_unix.cmake | 4 + .../cmake/platform/platform_win32.cmake | 14 + intern/ghost/CMakeLists.txt | 20 + intern/ghost/GHOST_C-api.h | 24 + intern/ghost/GHOST_IContext.h | 14 + intern/ghost/GHOST_IWindow.h | 20 + intern/ghost/GHOST_Types.h | 3 + intern/ghost/intern/GHOST_C-api.cpp | 38 + intern/ghost/intern/GHOST_Context.h | 27 + intern/ghost/intern/GHOST_ContextVK.cpp | 975 ++++++++++++++++++ intern/ghost/intern/GHOST_ContextVK.h | 205 ++++ intern/ghost/intern/GHOST_SystemCocoa.mm | 16 + intern/ghost/intern/GHOST_SystemWayland.cpp | 31 +- intern/ghost/intern/GHOST_SystemWin32.cpp | 18 +- intern/ghost/intern/GHOST_SystemX11.cpp | 18 +- intern/ghost/intern/GHOST_Window.cpp | 16 + intern/ghost/intern/GHOST_Window.h | 18 + intern/ghost/intern/GHOST_WindowCocoa.mm | 17 + intern/ghost/intern/GHOST_WindowWayland.cpp | 18 + intern/ghost/intern/GHOST_WindowWin32.cpp | 16 + intern/ghost/intern/GHOST_WindowX11.cpp | 23 + source/blender/gpu/CMakeLists.txt | 2 + source/blender/gpu/vulkan/vk_backend.cc | 30 +- source/blender/gpu/vulkan/vk_backend.hh | 14 + source/blender/windowmanager/CMakeLists.txt | 4 + .../blender/windowmanager/intern/wm_window.c | 3 + source/creator/CMakeLists.txt | 4 + 30 files changed, 1657 insertions(+), 7 deletions(-) create mode 100644 build_files/cmake/Modules/FindMoltenVK.cmake create mode 100644 intern/ghost/intern/GHOST_ContextVK.cpp create mode 100644 intern/ghost/intern/GHOST_ContextVK.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a621b859e7..855f6aba44f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1239,12 +1239,14 @@ if(WITH_OPENGL) add_definitions(-DWITH_OPENGL) endif() - -# ----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- # Configure Vulkan. if(WITH_VULKAN_BACKEND) - add_definitions(-DWITH_VULKAN_BACKEND) + list(APPEND BLENDER_GL_LIBRARIES ${VULKAN_LIBRARIES}) + if(APPLE) + list(APPEND BLENDER_GL_LIBRARIES ${MOLTENVK_LIBRARIES}) + endif() endif() # ----------------------------------------------------------------------------- diff --git a/build_files/cmake/Modules/FindMoltenVK.cmake b/build_files/cmake/Modules/FindMoltenVK.cmake new file mode 100644 index 00000000000..07584e51ae5 --- /dev/null +++ b/build_files/cmake/Modules/FindMoltenVK.cmake @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2022 Blender Foundation. + +# - Find MoltenVK libraries +# Find the MoltenVK includes and libraries +# This module defines +# MOLTENVK_INCLUDE_DIRS, where to find MoltenVK headers, Set when +# MOLTENVK_INCLUDE_DIR is found. +# MOLTENVK_LIBRARIES, libraries to link against to use MoltenVK. +# MOLTENVK_ROOT_DIR, The base directory to search for MoltenVK. +# This can also be an environment variable. +# MOLTENVK_FOUND, If false, do not try to use MoltenVK. +# + +# If MOLTENVK_ROOT_DIR was defined in the environment, use it. +IF(NOT MOLTENVK_ROOT_DIR AND NOT $ENV{MOLTENVK_ROOT_DIR} STREQUAL "") + SET(MOLTENVK_ROOT_DIR $ENV{MOLTENVK_ROOT_DIR}) +ENDIF() + +SET(_moltenvk_SEARCH_DIRS + ${MOLTENVK_ROOT_DIR} + ${LIBDIR}/vulkan/MoltenVK +) + + +FIND_PATH(MOLTENVK_INCLUDE_DIR + NAMES + MoltenVK/vk_mvk_moltenvk.h + HINTS + ${_moltenvk_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +FIND_LIBRARY(MOLTENVK_LIBRARY + NAMES + MoltenVK + HINTS + ${_moltenvk_SEARCH_DIRS} + PATH_SUFFIXES + dylib/macOS +) + +# handle the QUIETLY and REQUIRED arguments and set MOLTENVK_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MoltenVK DEFAULT_MSG MOLTENVK_LIBRARY MOLTENVK_INCLUDE_DIR) + +IF(MOLTENVK_FOUND) + SET(MOLTENVK_LIBRARIES ${MOLTENVK_LIBRARY}) + SET(MOLTENVK_INCLUDE_DIRS ${MOLTENVK_INCLUDE_DIR}) +ENDIF() + +MARK_AS_ADVANCED( + MOLTENVK_INCLUDE_DIR + MOLTENVK_LIBRARY +) + +UNSET(_moltenvk_SEARCH_DIRS) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index c5fe3c908de..b923a076792 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -100,6 +100,11 @@ if(WITH_USD) find_package(USD REQUIRED) endif() +if(WITH_VULKAN_BACKEND) + find_package(Vulkan REQUIRED) + find_package(MoltenVK REQUIRED) +endif() + if(WITH_OPENSUBDIV) find_package(OpenSubdiv) endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index f1ce3221440..10be375ee0f 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -108,6 +108,10 @@ find_package_wrapper(ZLIB REQUIRED) find_package_wrapper(Zstd REQUIRED) find_package_wrapper(Epoxy REQUIRED) +if(WITH_VULKAN_BACKEND) + find_package_wrapper(Vulkan REQUIRED) +endif() + function(check_freetype_for_brotli) include(CheckSymbolExists) set(CMAKE_REQUIRED_INCLUDES ${FREETYPE_INCLUDE_DIRS}) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 47673794652..0439b9b2fb6 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -926,6 +926,20 @@ if(WITH_HARU) set(HARU_LIBRARIES ${HARU_ROOT_DIR}/lib/libhpdfs.lib) endif() +if(WITH_VULKAN_BACKEND) + if(EXISTS ${LIBDIR}/vulkan) + set(VULKAN_FOUND On) + set(VULKAN_ROOT_DIR ${LIBDIR}/vulkan) + set(VULKAN_INCLUDE_DIR ${VULKAN_ROOT_DIR}/include) + set(VULKAN_INCLUDE_DIRS ${VULKAN_INCLUDE_DIR}) + set(VULKAN_LIBRARY ${VULKAN_ROOT_DIR}/lib/vulkan-1.lib) + set(VULKAN_LIBRARIES ${VULKAN_LIBRARY}) + else() + message(WARNING "Vulkan was not found, disabling WITH_VULKAN_BACKEND") + set(WITH_VULKAN_BACKEND OFF) + endif() +endif() + if(WITH_CYCLES AND WITH_CYCLES_PATH_GUIDING) find_package(openpgl QUIET) if(openpgl_FOUND) diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 599d5304cac..512ce5300c6 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -76,6 +76,26 @@ set(LIB ${Epoxy_LIBRARIES} ) +if(WITH_VULKAN_BACKEND) + list(APPEND SRC + intern/GHOST_ContextVK.cpp + + intern/GHOST_ContextVK.h + ) + + list(APPEND INC_SYS + ${VULKAN_INCLUDE_DIRS} + ${MOLTENVK_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${VULKAN_LIBRARIES} + ${MOLTENVK_LIBRARIES} + ) + + add_definitions(-DWITH_VULKAN_BACKEND) +endif() + if(WITH_GHOST_DEBUG) list(APPEND SRC intern/GHOST_EventPrinter.cpp diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 62984c762c1..a8dae232d8b 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1185,6 +1185,30 @@ int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_context, #endif /* WITH_XR_OPENXR */ +#ifdef WITH_VULKAN_BACKEND + +/** + * Return vulkan handles for the given context. + */ +void GHOST_GetVulkanHandles(GHOST_ContextHandle context, + void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_familly); + +/** + * Return vulkan backbuffer resources handles for the given window. + */ +void GHOST_GetVulkanBackbuffer(GHOST_WindowHandle windowhandle, + void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id); + +#endif + #ifdef __cplusplus } diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h index 3d4a034f7f3..52863e8c061 100644 --- a/intern/ghost/GHOST_IContext.h +++ b/intern/ghost/GHOST_IContext.h @@ -40,6 +40,20 @@ class GHOST_IContext { virtual unsigned int getDefaultFramebuffer() = 0; + virtual GHOST_TSuccess getVulkanHandles(void *, void *, void *, uint32_t *) = 0; + + /** + * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap events as the framebuffer will change. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id) = 0; + virtual GHOST_TSuccess swapBuffers() = 0; #ifdef WITH_CXX_GUARDEDALLOC diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index f712d9bd9f0..33b9d160f0f 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -14,6 +14,8 @@ #include #include +class GHOST_IContext; + /** * Interface for GHOST windows. * @@ -62,6 +64,12 @@ class GHOST_IWindow { */ virtual GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type) = 0; + /** + * Returns the drawing context used in this window. + * \return The current drawing context. + */ + virtual GHOST_IContext *getDrawingContext() = 0; + /** * Sets the title displayed in the title bar. * \param title: The title to display in the title bar. @@ -202,6 +210,18 @@ class GHOST_IWindow { */ virtual unsigned int getDefaultFramebuffer() = 0; + /** + * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap events as the framebuffer will change. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id) = 0; + /** * Invalidates the contents of this window. * \return Indication of success. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index db4eeff3122..3932bc76af0 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -156,6 +156,9 @@ typedef enum { #ifdef __APPLE__ GHOST_kDrawingContextTypeMetal, #endif +#ifdef WITH_VULKAN_BACKEND + GHOST_kDrawingContextTypeVulkan, +#endif } GHOST_TDrawingContextType; typedef enum { diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 0c595b27148..0430dc8602c 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -565,6 +565,12 @@ GHOST_TSuccess GHOST_SetDrawingContextType(GHOST_WindowHandle windowhandle, return window->setDrawingContextType(type); } +GHOST_ContextHandle GHOST_GetDrawingContext(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + return (GHOST_ContextHandle)window->getDrawingContext(); +} + void GHOST_SetTitle(GHOST_WindowHandle windowhandle, const char *title) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; @@ -1190,3 +1196,35 @@ int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_contexthandle, } #endif /* WITH_XR_OPENXR */ + +#ifdef WITH_VULKAN_BACKEND + +/** + * Return vulkan handles for the given context. + */ +void GHOST_GetVulkanHandles(GHOST_ContextHandle contexthandle, + void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_familly) +{ + GHOST_IContext *context = (GHOST_IContext *)contexthandle; + context->getVulkanHandles(r_instance, r_physical_device, r_device, r_graphic_queue_familly); +} + +/** + * Return vulkan backbuffer resources handles for the given window. + */ +void GHOST_GetVulkanBackbuffer(GHOST_WindowHandle windowhandle, + void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id) +{ + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + window->getVulkanBackbuffer(image, framebuffer, command_buffer, render_pass, extent, fb_id); +} + +#endif /* WITH_VULKAN */ \ No newline at end of file diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h index 04d445e7f85..366781f8134 100644 --- a/intern/ghost/intern/GHOST_Context.h +++ b/intern/ghost/intern/GHOST_Context.h @@ -135,6 +135,33 @@ class GHOST_Context : public GHOST_IContext { return 0; } + /** + * Gets the Vulkan context related resource handles. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess getVulkanHandles(void * /*r_instance*/, + void * /*r_physical_device*/, + void * /*r_device*/, + uint32_t * /*r_graphic_queue_familly*/) override + { + return GHOST_kFailure; + }; + + /** + * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap events as the framebuffer will change. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void * /*image*/, + void * /*framebuffer*/, + void * /*command_buffer*/, + void * /*render_pass*/, + void * /*extent*/, + uint32_t * /*fb_id*/) override + { + return GHOST_kFailure; + } + protected: bool m_stereoVisual; diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp new file mode 100644 index 00000000000..e446d9057a7 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -0,0 +1,975 @@ +/* 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 + +/* 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"; + } +} + +#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__); \ + } + +/* Tripple 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) { + /* Swapchain is out of date. Recreate swapchain 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) { + /* Swapchain is out of date. Recreate swapchain 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 *command_buffer, + 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]; + *((VkCommandBuffer *)command_buffer) = m_command_buffers[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_familly) +{ + *((VkInstance *)r_instance) = m_instance; + *((VkPhysicalDevice *)r_physical_device) = m_physical_device; + *((VkDevice *)r_device) = m_device; + *r_graphic_queue_familly = m_queue_family_graphic; + + 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) +{ + if (checkLayerSupport(layers_available, layer_name)) { + layers_enabled.push_back(layer_name); + } + else { + fprintf(stderr, "Error: %s not supported.\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) { + if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + return GHOST_kSuccess; + } + (*r_queue_index)++; + } + + fprintf(stderr, "Couldn't find any Graphic queue familly 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 familly 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::createCommandBuffers() +{ + m_command_buffers.resize(m_swapchain_image_views.size()); + + 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)); + + 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])); + } + + createCommandBuffers(); + + 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"); + } + + 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); + } + + 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; + } + + 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; + + /* Eash 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); + + if (use_window_surface) { + vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); + + createSwapchain(); + } + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles() +{ + return GHOST_kSuccess; +} diff --git a/intern/ghost/intern/GHOST_ContextVK.h b/intern/ghost/intern/GHOST_ContextVK.h new file mode 100644 index 00000000000..e26808cc317 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextVK.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#pragma once + +#include "GHOST_Context.h" + +#ifdef _WIN32 +# include "GHOST_SystemWin32.h" +#elif defined(__APPLE__) +# include "GHOST_SystemCocoa.h" +#else +# include "GHOST_SystemX11.h" +# ifdef WITH_GHOST_WAYLAND +# include "GHOST_SystemWayland.h" +# else +# define wl_surface void +# define wl_display void +# endif +#endif + +#include + +#ifdef __APPLE__ +# include +#else +# include +#endif + +#ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS +/* leave as convenience define for the future */ +# define GHOST_OPENGL_VK_CONTEXT_FLAGS 0 +#endif + +#ifndef GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY +# define GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY 0 +#endif + +typedef enum { + GHOST_kVulkanPlatformX11 = 0, +#ifdef WITH_GHOST_WAYLAND + GHOST_kVulkanPlatformWayland, +#endif +} GHOST_TVulkanPlatformType; + +class GHOST_ContextVK : public GHOST_Context { + public: + /** + * Constructor. + */ + GHOST_ContextVK(bool stereoVisual, +#ifdef _WIN32 + HWND hwnd, +#elif defined(__APPLE__) + /* FIXME CAMetalLayer but have issue with linking. */ + void *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 m_debug); + + /** + * Destructor. + */ + ~GHOST_ContextVK(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Release the drawing context of the calling thread. + * \return A boolean success indicator. + */ + GHOST_TSuccess releaseDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Gets the Vulkan context related resource handles. + * \return A boolean success indicator. + */ + GHOST_TSuccess getVulkanHandles(void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_familly); + /** + * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap events as the framebuffer will change. + * \return A boolean success indicator. + */ + GHOST_TSuccess getVulkanBackbuffer(void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int /* interval */) + { + return GHOST_kFailure; + } + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int &) + { + return GHOST_kFailure; + }; + + private: +#ifdef _WIN32 + HWND m_hwnd; +#elif defined(__APPLE__) + CAMetalLayer *m_metal_layer; +#else /* Linux */ + GHOST_TVulkanPlatformType m_platform; + /* X11 */ + Display *m_display; + Window m_window; + /* Wayland */ + wl_surface *m_wayland_surface; + wl_display *m_wayland_display; +#endif + + const int m_context_major_version; + const int m_context_minor_version; + const int m_debug; + + VkInstance m_instance; + VkPhysicalDevice m_physical_device; + VkDevice m_device; + VkCommandPool m_command_pool; + + uint32_t m_queue_family_graphic; + uint32_t m_queue_family_present; + + VkQueue m_graphic_queue; + VkQueue m_present_queue; + + /* For display only. */ + VkSurfaceKHR m_surface; + VkSwapchainKHR m_swapchain; + std::vector m_swapchain_images; + std::vector m_swapchain_image_views; + std::vector m_swapchain_framebuffers; + std::vector m_command_buffers; + VkRenderPass m_render_pass; + VkExtent2D m_render_extent; + std::vector m_image_available_semaphores; + std::vector m_render_finished_semaphores; + std::vector m_in_flight_fences; + std::vector m_in_flight_images; + /** frame modulo swapchain_len. Used as index for sync objects. */ + int m_currentFrame = 0; + /** Image index in the swapchain. Used as index for render objects. */ + uint32_t m_currentImage = 0; + /** Used to unique framebuffer ids to return when swapchain is recreated. */ + uint32_t m_swapchain_id = 0; + + const char *getPlatformSpecificSurfaceExtension() const; + GHOST_TSuccess pickPhysicalDevice(std::vector required_exts); + GHOST_TSuccess createSwapchain(); + GHOST_TSuccess destroySwapchain(); + GHOST_TSuccess createCommandBuffers(); + GHOST_TSuccess recordCommandBuffers(); +}; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index a1016dd4843..b8463286eab 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -18,6 +18,10 @@ #include "GHOST_ContextCGL.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif + #ifdef WITH_INPUT_NDOF # include "GHOST_NDOFManagerCocoa.h" #endif @@ -750,6 +754,18 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, */ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSettings) { +#ifdef WITH_VULKAN_BACKEND + if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) { + const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; + GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 0, debug_context); + if (!context->initializeDrawingContext()) { + delete context; + return NULL; + } + return context; + } +#endif + GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL, glSettings.context_type); if (context->initializeDrawingContext()) return context; diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 285fbbd3b6c..4f6627310ca 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -20,6 +20,10 @@ #include "GHOST_ContextEGL.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif + #ifdef WITH_INPUT_NDOF # include "GHOST_NDOFManagerUnix.h" #endif @@ -6014,7 +6018,7 @@ static GHOST_Context *createOffscreenContext_impl(GHOST_SystemWayland *system, return nullptr; } -GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/) +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings) { #ifdef USE_EVENT_BACKGROUND_THREAD std::lock_guard lock_server_guard{*server_mutex}; @@ -6022,6 +6026,31 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g /* Create new off-screen window. */ wl_surface *wl_surface = wl_compositor_create_surface(wl_compositor()); + +#ifdef WITH_VULKAN_BACKEND + const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; + + if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) { + GHOST_Context *context = new GHOST_ContextVK(false, + GHOST_kVulkanPlatformWayland, + 0, + NULL, + wl_surface, + display_->wl_display, + 1, + 0, + debug_context); + + if (!context->initializeDrawingContext()) { + delete context; + return nullptr; + } + return context; + } +#else + (void)glSettings; +#endif + wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr; GHOST_Context *context = createOffscreenContext_impl(this, display_->wl_display, egl_window); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 4d016373fc6..3cc425d0ff3 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -37,6 +37,9 @@ #include "GHOST_WindowWin32.h" #include "GHOST_ContextWGL.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif #ifdef WITH_INPUT_NDOF # include "GHOST_NDOFManagerWin32.h" @@ -256,7 +259,20 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet { const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; - GHOST_Context *context; + GHOST_Context *context = nullptr; + +#ifdef WITH_VULKAN_BACKEND + /* Vulkan does not need a window. */ + if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) { + context = new GHOST_ContextVK(false, (HWND)0, 1, 0, debug_context); + + if (!context->initializeDrawingContext()) { + delete context; + return nullptr; + } + return context; + } +#endif HWND wnd = CreateWindowA("STATIC", "BlenderGLEW", diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 1462433277f..4a723435806 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -36,6 +36,10 @@ #include "GHOST_ContextEGL.h" #include "GHOST_ContextGLX.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif + #ifdef WITH_XF86KEYSYM # include #endif @@ -431,8 +435,20 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti * no fall-backs. */ const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; + GHOST_Context *context = nullptr; - GHOST_Context *context; +#ifdef WITH_VULKAN_BACKEND + if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) { + context = new GHOST_ContextVK( + false, GHOST_kVulkanPlatformX11, 0, m_display, NULL, NULL, 1, 0, debug_context); + + if (!context->initializeDrawingContext()) { + delete context; + return nullptr; + } + return context; + } +#endif #ifdef USE_EGL /* Try to initialize an EGL context. */ diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp index eaa4cdcffac..144bfeab373 100644 --- a/intern/ghost/intern/GHOST_Window.cpp +++ b/intern/ghost/intern/GHOST_Window.cpp @@ -77,6 +77,11 @@ GHOST_TSuccess GHOST_Window::setDrawingContextType(GHOST_TDrawingContextType typ return GHOST_kSuccess; } +GHOST_IContext *GHOST_Window::getDrawingContext() +{ + return m_context; +} + GHOST_TSuccess GHOST_Window::swapBuffers() { return m_context->swapBuffers(); @@ -102,6 +107,17 @@ uint GHOST_Window::getDefaultFramebuffer() return (m_context) ? m_context->getDefaultFramebuffer() : 0; } +GHOST_TSuccess GHOST_Window::getVulkanBackbuffer(void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id) +{ + return m_context->getVulkanBackbuffer( + image, framebuffer, command_buffer, render_pass, extent, fb_id); +} + GHOST_TSuccess GHOST_Window::activateDrawingContext() { return m_context->activateDrawingContext(); diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 396691fa161..741ce786859 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -232,6 +232,12 @@ class GHOST_Window : public GHOST_IWindow { */ GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type) override; + /** + * Returns the drawing context used in this window. + * \return The current drawing context. + */ + virtual GHOST_IContext *getDrawingContext() override; + /** * Swaps front and back buffers of a window. * \return A boolean success indicator. @@ -263,6 +269,18 @@ class GHOST_Window : public GHOST_IWindow { */ virtual unsigned int getDefaultFramebuffer() override; + /** + * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap events as the framebuffer will change. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void *image, + void *framebuffer, + void *command_buffer, + void *render_pass, + void *extent, + uint32_t *fb_id) override; + /** * Returns the window user data. * \return The window user data. diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index bc1f1e99a3a..8748bbaeb35 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -8,6 +8,10 @@ #include "GHOST_ContextCGL.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif + #include #include #include @@ -803,6 +807,19 @@ GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order) GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType type) { +#ifdef WITH_VULKAN_BACKEND + if (type == GHOST_kDrawingContextTypeVulkan) { + GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 0, true); + + if (!context->initializeDrawingContext()) { + delete context; + return NULL; + } + + return context; + } +#endif + if (type == GHOST_kDrawingContextTypeOpenGL || type == GHOST_kDrawingContextTypeMetal) { GHOST_Context *context = new GHOST_ContextCGL( diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 928e442abd6..239693e0e5b 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -14,6 +14,9 @@ #include "GHOST_ContextEGL.h" #include "GHOST_ContextNone.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif #include @@ -1211,6 +1214,21 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType case GHOST_kDrawingContextTypeNone: context = new GHOST_ContextNone(m_wantStereoVisual); break; + +#ifdef WITH_VULKAN_BACKEND + case GHOST_kDrawingContextTypeVulkan: + context = new GHOST_ContextVK(m_wantStereoVisual, + GHOST_kVulkanPlatformWayland, + 0, + NULL, + window_->wl_surface, + system_->wl_display(), + 1, + 0, + true); + break; +#endif + case GHOST_kDrawingContextTypeOpenGL: for (int minor = 6; minor >= 0; --minor) { context = new GHOST_ContextEGL(system_, diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index e2d143ee5e6..5086920be43 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -15,6 +15,9 @@ #include "utfconv.h" #include "GHOST_ContextWGL.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif #include @@ -632,6 +635,19 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty return context; } +#ifdef WITH_VULKAN_BACKEND + else if (type == GHOST_kDrawingContextTypeVulkan) { + GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 0, m_debug_context); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; + } + } +#endif + return NULL; } diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 8d8ce3643f4..9f77bdc4dd0 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -24,6 +24,9 @@ #include "GHOST_ContextEGL.h" #include "GHOST_ContextGLX.h" +#ifdef WITH_VULKAN_BACKEND +# include "GHOST_ContextVK.h" +#endif /* For #XIWarpPointer. */ #ifdef WITH_X11_XINPUT @@ -1228,6 +1231,26 @@ static GHOST_Context *create_glx_context(Window window, GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type) { +#if defined(WITH_VULKAN) + if (type == GHOST_kDrawingContextTypeVulkan) { + GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, + GHOST_kVulkanPlatformX11, + m_window, + m_display, + NULL, + NULL, + 1, + 0, + m_is_debug_context); + + if (!context->initializeDrawingContext()) { + delete context; + return nullptr; + } + return context; + } +#endif + if (type == GHOST_kDrawingContextTypeOpenGL) { /* During development: diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4aa3a3b0b7d..08aa60321e5 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -267,6 +267,8 @@ endif() if(WITH_VULKAN_BACKEND) list(APPEND SRC ${VULKAN_SRC}) + + add_definitions(-DWITH_VULKAN_BACKEND) endif() if(WITH_METAL_BACKEND) diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc index 00bc43333d6..d22275cebef 100644 --- a/source/blender/gpu/vulkan/vk_backend.cc +++ b/source/blender/gpu/vulkan/vk_backend.cc @@ -5,7 +5,7 @@ * \ingroup gpu */ -#include "vk_backend.hh" +#include "gpu_platform_private.hh" #include "vk_batch.hh" #include "vk_context.hh" @@ -19,8 +19,36 @@ #include "vk_uniform_buffer.hh" #include "vk_vertex_buffer.hh" +#include "vk_backend.hh" + namespace blender::gpu { +void VKBackend::init_platform() +{ + BLI_assert(!GPG.initialized); + + eGPUDeviceType device = GPU_DEVICE_ANY; + eGPUOSType os = GPU_OS_ANY; + eGPUDriverType driver = GPU_DRIVER_ANY; + eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED; + +#ifdef _WIN32 + os = GPU_OS_WIN; +#elif defined(__APPLE__) + os = GPU_OS_MAC; +#else + os = GPU_OS_UNIX; +#endif + + GPG.init(device, os, driver, support_level, GPU_BACKEND_VULKAN, "", "", ""); +} + +void VKBackend::platform_exit() +{ + BLI_assert(GPG.initialized); + GPG.clear(); +} + void VKBackend::delete_resources() { } diff --git a/source/blender/gpu/vulkan/vk_backend.hh b/source/blender/gpu/vulkan/vk_backend.hh index 549478586e8..55b528bfa00 100644 --- a/source/blender/gpu/vulkan/vk_backend.hh +++ b/source/blender/gpu/vulkan/vk_backend.hh @@ -13,6 +13,16 @@ namespace blender::gpu { class VKBackend : public GPUBackend { public: + VKBackend() + { + VKBackend::init_platform(); + } + + virtual ~VKBackend() + { + VKBackend::platform_exit(); + } + void delete_resources() override; void samplers_update() override; @@ -37,6 +47,10 @@ class VKBackend : public GPUBackend { void render_begin() override; void render_end() override; void render_step() override; + + private: + static void init_platform(); + static void platform_exit(); }; } // namespace blender::gpu \ No newline at end of file diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 3ab024841e8..16f69dac1bf 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -121,6 +121,10 @@ if(WITH_CYCLES) add_definitions(-DWITH_CYCLES) endif() +if(WITH_VULKAN_BACKEND) + add_definitions(-DWITH_VULKAN_BACKEND) +endif() + if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 265aa08a6b1..f5dc18bb16e 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1626,6 +1626,9 @@ GHOST_TDrawingContextType wm_ghost_drawing_context_type(const eGPUBackendType gp case GPU_BACKEND_OPENGL: return GHOST_kDrawingContextTypeOpenGL; case GPU_BACKEND_VULKAN: +#ifdef WITH_VULKAN_BACKEND + return GHOST_kDrawingContextTypeVulkan; +#endif BLI_assert_unreachable(); return GHOST_kDrawingContextTypeNone; case GPU_BACKEND_METAL: diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index bb9e73c0895..20d538991df 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -50,6 +50,10 @@ if(WITH_CYCLES) endif() endif() +if(WITH_VULKAN_BACKEND) + add_definitions(-DWITH_VULKAN_BACKEND) +endif() + if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif()