Compare commits
24 Commits
tracking_t
...
temp-ghost
Author | SHA1 | Date | |
---|---|---|---|
9c4381216f | |||
041900ae95 | |||
b271ed8ac9 | |||
6ccd38ea90 | |||
7d691969e6 | |||
1a47f3ae17 | |||
7211f3ab5b | |||
fad06751a6 | |||
0fae43efb2 | |||
31ecc30283 | |||
d64d789174 | |||
975e9020cb | |||
d2c6a27f58 | |||
6ca82bbf34 | |||
86868a4bcc | |||
5db147c5be | |||
39db9b836b | |||
b0800197e6 | |||
16f5cda14a | |||
50e0d346f1 | |||
7cd24fb70a | |||
1b04b5cf08 | |||
18ba57ddb6 | |||
ed2b382490 |
@@ -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()
|
||||
|
Reference in New Issue
Block a user