Changeset View
Standalone View
intern/ghost/intern/GHOST_ContextVK.cpp
- This file was added.
| /* 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() | ||||||||||
| { | ||||||||||
fclem: Remove `void` as argument in C++ files. Check other occurrences. | ||||||||||
| 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); | ||||||||||
Done Inline ActionsI wrote a pipeline test, but the submit loop causes a bug. AgAmemnno: I wrote a pipeline test, but the submit loop causes a bug.
Shouldn't the fence work if this… | ||||||||||
Done Inline ActionsThis patch only contains some early development. Actual tests hasn't been performed yet. Would be awesome to have access to your pipeline test so we could already cover that part. jbakker: This patch only contains some early development. Actual tests hasn't been performed yet. Would… | ||||||||||
AgAmemnnoUnsubmitted Not Done Inline ActionsWhen using Shaderc, I ported NVPRO's auxiliary class, but it is an early development for testing. No problem on my computer. $make test 40/248 Test #40: bf_draw ......................................... ................... Passed 1.19 sec Please report any errors or crashes. AgAmemnno: When using Shaderc, I ported NVPRO's auxiliary class, but it is an early development for… | ||||||||||
| 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); | ||||||||||
Done Inline Actions
fclem: | ||||||||||
| 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; | ||||||||||
Done Inline ActionsGeometry shader should not be required. jbakker: Geometry shader should not be required. | ||||||||||
Done Inline ActionsYes, ideally. But we still don't have general purpose workaround for all of them. The metal backend reimplementation of the geometry shaders is using some tricks in the backend to bind VBOs as SSBOs, which I do not want in the Vulkan Backend. fclem: Yes, ideally. But we still don't have general purpose workaround for all of them. The metal… | ||||||||||
| } | ||||||||||
| } | ||||||||||
| 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"); | ||||||||||
Done Inline ActionsIs this still required? jbakker: Is this still required?
https://registry.khronos.org/vulkan/specs/1.3… | ||||||||||
Done Inline ActionsThis is only the dynamic state function. The baked pipeline option is here from vulkan 1.0 https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineColorBlendStateCreateInfo.html And yes it is used, it is wrapped by GPU_logic_op_xor_set which could be changed to not require it. But finding another way to draw and keep the same clarity might be tricky. We did change the text edit cursors which lost a bit of clarity. While it might be nice to remove, implementing it is not that difficult. fclem: This is only the dynamic state function. The baked pipeline option is here from vulkan 1.0… | ||||||||||
| } | ||||||||||
| 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()); | ||||||||||
Done Inline ActionsI think it's ambiguous whether to force reset the command buffer during the loop or conditionally reset it. Validation Error: [ VUID-vkBeginCommandBuffer-commandBuffer-00050 ] Object 0: handle = 0x20e874b0d10, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x2e2cd000000002b, type = VK_OBJECT_TYPE_COMMAND_POOL; | MessageID = 0xb24f00f5 | Call to vkBeginCommandBuffer() on VkCommandBuffer 0x20e874b0d10[] attempts to implicitly reset cmdBuffer created from VkCommandPool 0x2e2cd000000002b[] that does NOT have the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set. The Vulkan spec states: If commandBuffer was allocated from a VkCommandPool which did not have the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT flag set, commandBuffer must be in the initial state (https://vulkan.lunarg.com/doc/view/1.2.189.0/windows/1.2-extensions/vkspec.html#VUID-vkBeginCommandBuffer-commandBuffer-00050) AgAmemnno: I think it's ambiguous whether to force reset the command buffer during the loop or… | ||||||||||
| 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; | ||||||||||
| } | ||||||||||
Remove void as argument in C++ files. Check other occurrences.