1
1

Compare commits

...

24 Commits

Author SHA1 Message Date
9c4381216f Merge branch 'master' into temp-ghost-vulkan 2022-11-22 11:25:29 +01:00
041900ae95 GHOST: Command pool should be able to reset. 2022-11-18 11:45:15 +01:00
b271ed8ac9 Code style struct initialization. 2022-11-18 11:35:44 +01:00
6ccd38ea90 GHOST: Vulkan swapbuffer should wait for graphics queue idling. 2022-11-18 11:35:12 +01:00
7d691969e6 Remove (void) parameters from GHOST_ContextVK. 2022-11-18 11:28:14 +01:00
1a47f3ae17 CMAKE: Move WITH_VULKAN_BACKEND from global to specific modules. 2022-11-18 11:24:23 +01:00
7211f3ab5b Merge branch 'master' into temp-ghost-vulkan 2022-11-18 11:04:32 +01:00
fad06751a6 Merge branch 'master' into temp-ghost-vulkan 2022-11-15 11:12:27 +01:00
0fae43efb2 Apply formatting. 2022-11-08 14:32:27 +01:00
31ecc30283 Added support for Linux. Thanks to Qiang Yu for the patch! 2022-11-08 14:29:25 +01:00
d64d789174 Initialize vk backend. 2022-11-07 08:03:50 +01:00
975e9020cb Create VKBackend when selecting vulkan from the command line. 2022-11-01 14:52:33 +01:00
d2c6a27f58 Finding MOLTENVK. 2022-11-01 13:51:16 +01:00
6ca82bbf34 Fix missing import in GHOST_SystemWin32.cpp 2022-11-01 12:02:27 +01:00
86868a4bcc Changes to cmake to select vulkan from libs. 2022-11-01 12:00:28 +01:00
5db147c5be Merge branch 'master' into temp-ghost-vulkan 2022-11-01 09:26:54 +01:00
39db9b836b Removed debug code. 2022-11-01 08:42:49 +01:00
b0800197e6 Removed debug code. 2022-11-01 08:42:49 +01:00
16f5cda14a Removed obsolete comments. 2022-11-01 08:42:49 +01:00
50e0d346f1 Implemented newDrawingContext. 2022-11-01 08:42:49 +01:00
7cd24fb70a Implemented createOffscreenContext. 2022-11-01 08:42:49 +01:00
1b04b5cf08 Find MoltenVK (WIP). 2022-11-01 08:42:49 +01:00
18ba57ddb6 Copied from tmp-vulkan branch. 2022-11-01 08:42:49 +01:00
ed2b382490 Add vulkan changes to cmake files. 2022-11-01 08:42:49 +01:00
30 changed files with 1657 additions and 7 deletions

View File

@@ -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()
# -----------------------------------------------------------------------------

View 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)

View File

@@ -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()

View File

@@ -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})

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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.

View File

@@ -156,6 +156,9 @@ typedef enum {
#ifdef __APPLE__
GHOST_kDrawingContextTypeMetal,
#endif
#ifdef WITH_VULKAN_BACKEND
GHOST_kDrawingContextTypeVulkan,
#endif
} GHOST_TDrawingContextType;
typedef enum {

View File

@@ -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 */

View File

@@ -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;

View 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;
}

View 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();
};

View File

@@ -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;

View File

@@ -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);

View File

@@ -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",

View File

@@ -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. */

View File

@@ -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();

View File

@@ -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.

View File

@@ -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(

View File

@@ -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_,

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -267,6 +267,8 @@ endif()
if(WITH_VULKAN_BACKEND)
list(APPEND SRC ${VULKAN_SRC})
add_definitions(-DWITH_VULKAN_BACKEND)
endif()
if(WITH_METAL_BACKEND)

View File

@@ -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()
{
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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:

View File

@@ -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()