This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/ghost/intern/GHOST_ContextVK.cpp
T
Jeroen Bakker d21b9a4bb6 Vulkan: Warn Developer When Layer Not Found.
Currently a developer that starts blender with `--debug-gpu` or
runs the GPU test cases can receive an error when not the full
VulkanSDK is installed.

The VulkanSDK isn't required for normal developement and
therefore it is better to show it as a warning.

NOTE: VulkanSDK is adviced to use when developing in the Vulkan
backend as it contains tools that helps/speed up the development
and validation during development.

Pull Request: blender/blender#105599
2023-03-09 14:55:40 +01:00

1033 lines
35 KiB
C++

/* 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__); \
}
/* Triple buffering. */
const int MAX_FRAMES_IN_FLIGHT = 2;
GHOST_ContextVK::GHOST_ContextVK(bool stereoVisual,
#ifdef _WIN32
HWND hwnd,
#elif defined(__APPLE__)
CAMetalLayer *metal_layer,
#else
GHOST_TVulkanPlatformType platform,
/* X11 */
Window window,
Display *display,
/* Wayland */
wl_surface *wayland_surface,
wl_display *wayland_display,
#endif
int contextMajorVersion,
int contextMinorVersion,
int debug)
: GHOST_Context(stereoVisual),
#ifdef _WIN32
m_hwnd(hwnd),
#elif defined(__APPLE__)
m_metal_layer(metal_layer),
#else
m_platform(platform),
/* X11 */
m_display(display),
m_window(window),
/* Wayland */
m_wayland_surface(wayland_surface),
m_wayland_display(wayland_display),
#endif
m_context_major_version(contextMajorVersion),
m_context_minor_version(contextMinorVersion),
m_debug(debug),
m_instance(VK_NULL_HANDLE),
m_physical_device(VK_NULL_HANDLE),
m_device(VK_NULL_HANDLE),
m_command_pool(VK_NULL_HANDLE),
m_surface(VK_NULL_HANDLE),
m_swapchain(VK_NULL_HANDLE),
m_render_pass(VK_NULL_HANDLE)
{
}
GHOST_ContextVK::~GHOST_ContextVK()
{
if (m_device) {
vkDeviceWaitIdle(m_device);
}
destroySwapchain();
if (m_command_pool != VK_NULL_HANDLE) {
vkDestroyCommandPool(m_device, m_command_pool, NULL);
}
if (m_device != VK_NULL_HANDLE) {
vkDestroyDevice(m_device, NULL);
}
if (m_surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(m_instance, m_surface, NULL);
}
if (m_instance != VK_NULL_HANDLE) {
vkDestroyInstance(m_instance, NULL);
}
}
GHOST_TSuccess GHOST_ContextVK::destroySwapchain()
{
if (m_device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(m_device);
}
m_in_flight_images.resize(0);
for (auto semaphore : m_image_available_semaphores) {
vkDestroySemaphore(m_device, semaphore, NULL);
}
for (auto semaphore : m_render_finished_semaphores) {
vkDestroySemaphore(m_device, semaphore, NULL);
}
for (auto fence : m_in_flight_fences) {
vkDestroyFence(m_device, fence, NULL);
}
for (auto framebuffer : m_swapchain_framebuffers) {
vkDestroyFramebuffer(m_device, framebuffer, NULL);
}
if (m_render_pass != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, m_render_pass, NULL);
}
for (auto command_buffer : m_command_buffers) {
vkFreeCommandBuffers(m_device, m_command_pool, 1, &command_buffer);
}
for (auto imageView : m_swapchain_image_views) {
vkDestroyImageView(m_device, imageView, NULL);
}
if (m_swapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(m_device, m_swapchain, NULL);
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::swapBuffers()
{
if (m_swapchain == VK_NULL_HANDLE) {
return GHOST_kFailure;
}
vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, UINT64_MAX);
VkResult result = vkAcquireNextImageKHR(m_device,
m_swapchain,
UINT64_MAX,
m_image_available_semaphores[m_currentFrame],
VK_NULL_HANDLE,
&m_currentImage);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
/* Swap-chain is out of date. Recreate swap-chain and skip this frame. */
destroySwapchain();
createSwapchain();
return GHOST_kSuccess;
}
else if (result != VK_SUCCESS) {
fprintf(stderr,
"Error: Failed to acquire swap chain image : %s\n",
vulkan_error_as_string(result));
return GHOST_kFailure;
}
/* Check if a previous frame is using this image (i.e. there is its fence to wait on) */
if (m_in_flight_images[m_currentImage] != VK_NULL_HANDLE) {
vkWaitForFences(m_device, 1, &m_in_flight_images[m_currentImage], VK_TRUE, UINT64_MAX);
}
m_in_flight_images[m_currentImage] = m_in_flight_fences[m_currentFrame];
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &m_image_available_semaphores[m_currentFrame];
submit_info.pWaitDstStageMask = wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &m_command_buffers[m_currentImage];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &m_render_finished_semaphores[m_currentFrame];
vkResetFences(m_device, 1, &m_in_flight_fences[m_currentFrame]);
VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, m_in_flight_fences[m_currentFrame]));
do {
result = vkWaitForFences(m_device, 1, &m_in_flight_fences[m_currentFrame], VK_TRUE, 10000);
} while (result == VK_TIMEOUT);
VK_CHECK(vkQueueWaitIdle(m_graphic_queue));
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &m_render_finished_semaphores[m_currentFrame];
present_info.swapchainCount = 1;
present_info.pSwapchains = &m_swapchain;
present_info.pImageIndices = &m_currentImage;
present_info.pResults = NULL;
result = vkQueuePresentKHR(m_present_queue, &present_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
/* Swap-chain is out of date. Recreate swap-chain and skip this frame. */
destroySwapchain();
createSwapchain();
return GHOST_kSuccess;
}
else if (result != VK_SUCCESS) {
fprintf(stderr,
"Error: Failed to present swap chain image : %s\n",
vulkan_error_as_string(result));
return GHOST_kFailure;
}
m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer(
void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id)
{
if (m_swapchain == VK_NULL_HANDLE) {
return GHOST_kFailure;
}
*((VkImage *)image) = m_swapchain_images[m_currentImage];
*((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage];
*((VkRenderPass *)render_pass) = m_render_pass;
*((VkExtent2D *)extent) = m_render_extent;
*fb_id = m_swapchain_id * 10 + m_currentFrame;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance,
void *r_physical_device,
void *r_device,
uint32_t *r_graphic_queue_family,
void *r_queue)
{
*((VkInstance *)r_instance) = m_instance;
*((VkPhysicalDevice *)r_physical_device) = m_physical_device;
*((VkDevice *)r_device) = m_device;
*r_graphic_queue_family = m_queue_family_graphic;
*((VkQueue *)r_queue) = m_graphic_queue;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer)
{
if (m_command_buffers.empty()) {
return GHOST_kFailure;
}
if (m_swapchain == VK_NULL_HANDLE) {
*((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0];
}
else {
*((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage];
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::activateDrawingContext()
{
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::releaseDrawingContext()
{
return GHOST_kSuccess;
}
static vector<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,
const bool debug)
{
if (checkLayerSupport(layers_available, layer_name)) {
layers_enabled.push_back(layer_name);
}
else if (debug) {
fprintf(
stderr, "Warning: Layer requested, but not supported by the platform. [%s]\n", layer_name);
}
}
static bool device_extensions_support(VkPhysicalDevice device, vector<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) {
/* Every vulkan implementation by spec must have one queue family that support both graphics
* and compute pipelines. We select this one; compute only queue family hints at async compute
* implementations. */
if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
(queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) {
return GHOST_kSuccess;
}
(*r_queue_index)++;
}
fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n");
return GHOST_kFailure;
}
static GHOST_TSuccess getPresetQueueFamily(VkPhysicalDevice device,
VkSurfaceKHR surface,
uint32_t *r_queue_index)
{
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, NULL);
vector<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 family on selected device\n");
return GHOST_kFailure;
}
static GHOST_TSuccess create_render_pass(VkDevice device,
VkFormat format,
VkRenderPass *r_renderPass)
{
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
VK_CHECK(vkCreateRenderPass(device, &renderPassInfo, NULL, r_renderPass));
return GHOST_kSuccess;
}
static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device,
VkSurfaceKHR surface,
VkPresentModeKHR *r_presentMode)
{
uint32_t present_count;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, NULL);
vector<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::createCommandPools()
{
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = m_queue_family_graphic;
VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool));
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer()
{
assert(m_command_pool != VK_NULL_HANDLE);
assert(m_command_buffers.size() == 0);
m_command_buffers.resize(1);
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = m_command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = static_cast<uint32_t>(m_command_buffers.size());
VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data()));
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers()
{
assert(m_command_pool != VK_NULL_HANDLE);
m_command_buffers.resize(m_swapchain_image_views.size());
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = m_command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = static_cast<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]));
}
createGraphicsCommandBuffers();
return GHOST_kSuccess;
}
const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const
{
#ifdef _WIN32
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
#elif defined(__APPLE__)
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
#else /* UNIX/Linux */
switch (m_platform) {
case GHOST_kVulkanPlatformX11:
return VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
break;
# ifdef WITH_GHOST_WAYLAND
case GHOST_kVulkanPlatformWayland:
return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
break;
# endif
}
#endif
return NULL;
}
GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
{
#ifdef _WIN32
const bool use_window_surface = (m_hwnd != NULL);
#elif defined(__APPLE__)
const bool use_window_surface = (m_metal_layer != NULL);
#else /* UNIX/Linux */
bool use_window_surface = false;
switch (m_platform) {
case GHOST_kVulkanPlatformX11:
use_window_surface = (m_display != NULL) && (m_window != (Window)NULL);
break;
# ifdef WITH_GHOST_WAYLAND
case GHOST_kVulkanPlatformWayland:
use_window_surface = (m_wayland_display != NULL) && (m_wayland_surface != NULL);
break;
# endif
}
#endif
auto layers_available = getLayersAvailable();
auto extensions_available = getExtensionsAvailable();
vector<const char *> layers_enabled;
if (m_debug) {
enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug);
}
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);
}
extensions_device.push_back("VK_KHR_dedicated_allocation");
extensions_device.push_back("VK_KHR_get_memory_requirements2");
/* Enable MoltenVK required instance extensions. */
#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME
requireExtension(
extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2");
#endif
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pApplicationName = "Blender";
app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.pEngineName = "Blender";
app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0);
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
create_info.enabledLayerCount = static_cast<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;
}
#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME
/* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be
* enabled. See
* https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/
if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) {
extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
}
#endif
vector<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;
/* Each queue must be unique. */
if (m_queue_family_graphic != m_queue_family_present) {
queue_create_infos.push_back(present_queue_create_info);
}
}
VkPhysicalDeviceFeatures device_features = {};
#if STRICT_REQUIREMENTS
device_features.geometryShader = VK_TRUE;
device_features.dualSrcBlend = VK_TRUE;
device_features.logicOp = VK_TRUE;
#endif
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_create_info.queueCreateInfoCount = static_cast<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);
createCommandPools();
if (use_window_surface) {
vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue);
createSwapchain();
}
else {
createGraphicsCommandBuffer();
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextVK::releaseNativeHandles()
{
return GHOST_kSuccess;
}