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
This commit is contained in:
parent
efa87ad5eb
commit
6dac345a64
Notes:
blender-bot
2025-02-14 01:29:06 +00:00
Referenced by issue #93031, GHOST Vulkan Backend API.
@ -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()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
59
build_files/cmake/Modules/FindMoltenVK.cmake
Normal file
59
build_files/cmake/Modules/FindMoltenVK.cmake
Normal file
@ -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)
|
@ -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()
|
||||
|
@ -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})
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
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.
|
||||
|
@ -156,6 +156,9 @@ typedef enum {
|
||||
#ifdef __APPLE__
|
||||
GHOST_kDrawingContextTypeMetal,
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
GHOST_kDrawingContextTypeVulkan,
|
||||
#endif
|
||||
} GHOST_TDrawingContextType;
|
||||
|
||||
typedef enum {
|
||||
|
@ -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 */
|
@ -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;
|
||||
|
||||
|
975
intern/ghost/intern/GHOST_ContextVK.cpp
Normal file
975
intern/ghost/intern/GHOST_ContextVK.cpp
Normal file
@ -0,0 +1,975 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextVK.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <vulkan/vulkan_win32.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <MoltenVK/vk_mvk_moltenvk.h>
|
||||
#else /* X11 */
|
||||
# include <vulkan/vulkan_xlib.h>
|
||||
# ifdef WITH_GHOST_WAYLAND
|
||||
# include <vulkan/vulkan_wayland.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
/* 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<VkExtensionProperties> getExtensionsAvailable()
|
||||
{
|
||||
uint32_t extension_count = 0;
|
||||
vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL);
|
||||
|
||||
vector<VkExtensionProperties> extensions(extension_count);
|
||||
vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions.data());
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
static bool checkExtensionSupport(vector<VkExtensionProperties> &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<VkExtensionProperties> &extensions_available,
|
||||
vector<const char *> &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<VkLayerProperties> getLayersAvailable()
|
||||
{
|
||||
uint32_t layer_count = 0;
|
||||
vkEnumerateInstanceLayerProperties(&layer_count, NULL);
|
||||
|
||||
vector<VkLayerProperties> layers(layer_count);
|
||||
vkEnumerateInstanceLayerProperties(&layer_count, layers.data());
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
static bool checkLayerSupport(vector<VkLayerProperties> &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<VkLayerProperties> &layers_available,
|
||||
vector<const char *> &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<const char *> required_exts)
|
||||
{
|
||||
uint32_t ext_count;
|
||||
vkEnumerateDeviceExtensionProperties(device, NULL, &ext_count, NULL);
|
||||
|
||||
vector<VkExtensionProperties> 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<const char *> required_exts)
|
||||
{
|
||||
m_physical_device = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t device_count = 0;
|
||||
vkEnumeratePhysicalDevices(m_instance, &device_count, NULL);
|
||||
|
||||
vector<VkPhysicalDevice> 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<VkQueueFamilyProperties> 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<VkQueueFamilyProperties> 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<VkPresentModeKHR> 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<uint32_t>(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<VkSurfaceFormatKHR> 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<const char *> layers_enabled;
|
||||
if (m_debug) {
|
||||
enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation");
|
||||
}
|
||||
|
||||
vector<const char *> extensions_device;
|
||||
vector<const char *> 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<uint32_t>(layers_enabled.size());
|
||||
create_info.ppEnabledLayerNames = layers_enabled.data();
|
||||
create_info.enabledExtensionCount = static_cast<uint32_t>(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<VkDeviceQueueCreateInfo> 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<uint32_t>(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<uint32_t>(layers_enabled.size());
|
||||
device_create_info.ppEnabledLayerNames = layers_enabled.data();
|
||||
device_create_info.enabledExtensionCount = static_cast<uint32_t>(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;
|
||||
}
|
205
intern/ghost/intern/GHOST_ContextVK.h
Normal file
205
intern/ghost/intern/GHOST_ContextVK.h
Normal file
@ -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 <vector>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <MoltenVK/vk_mvk_moltenvk.h>
|
||||
#else
|
||||
# include <vulkan/vulkan.h>
|
||||
#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<VkImage> m_swapchain_images;
|
||||
std::vector<VkImageView> m_swapchain_image_views;
|
||||
std::vector<VkFramebuffer> m_swapchain_framebuffers;
|
||||
std::vector<VkCommandBuffer> m_command_buffers;
|
||||
VkRenderPass m_render_pass;
|
||||
VkExtent2D m_render_extent;
|
||||
std::vector<VkSemaphore> m_image_available_semaphores;
|
||||
std::vector<VkSemaphore> m_render_finished_semaphores;
|
||||
std::vector<VkFence> m_in_flight_fences;
|
||||
std::vector<VkFence> 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<const char *> required_exts);
|
||||
GHOST_TSuccess createSwapchain();
|
||||
GHOST_TSuccess destroySwapchain();
|
||||
GHOST_TSuccess createCommandBuffers();
|
||||
GHOST_TSuccess recordCommandBuffers();
|
||||
};
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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 <X11/XF86keysym.h>
|
||||
#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. */
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -8,6 +8,10 @@
|
||||
|
||||
#include "GHOST_ContextCGL.h"
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.h"
|
||||
#endif
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
@ -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(
|
||||
|
@ -14,6 +14,9 @@
|
||||
|
||||
#include "GHOST_ContextEGL.h"
|
||||
#include "GHOST_ContextNone.h"
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.h"
|
||||
#endif
|
||||
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
@ -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_,
|
||||
|
@ -15,6 +15,9 @@
|
||||
#include "utfconv.h"
|
||||
|
||||
#include "GHOST_ContextWGL.h"
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.h"
|
||||
#endif
|
||||
|
||||
#include <Dwmapi.h>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -267,6 +267,8 @@ endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND SRC ${VULKAN_SRC})
|
||||
|
||||
add_definitions(-DWITH_VULKAN_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_METAL_BACKEND)
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
@ -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
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user