/* * Copyright (C) 2017-2019 Alberts Muktupāvels * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #define VK_USE_PLATFORM_XLIB_KHR #include #include "display-private.h" #include "meta-compositor-vulkan.h" #include "meta-enum-types.h" #include "meta-surface-vulkan.h" #include "prefs.h" #include "screen.h" #include "util.h" struct _MetaCompositorVulkan { MetaCompositor parent; gboolean lunarg_validation_layer; gboolean debug_report_extension; VkInstance instance; VkDebugReportCallbackEXT debug_callback; VkSurfaceKHR surface; VkSurfaceFormatKHR surface_format; VkExtent2D surface_extent; VkPhysicalDevice physical_device; uint32_t graphics_family_index; uint32_t present_family_index; VkDevice device; VkQueue graphics_queue; VkQueue present_queue; VkCommandPool command_pool; VkSemaphore semaphore; VkSwapchainKHR swapchain; uint32_t n_images; VkImage *images; uint32_t n_image_views; VkImageView *image_views; VkRenderPass render_pass; uint32_t n_framebuffers; VkFramebuffer *framebuffers; uint32_t n_command_buffers; VkCommandBuffer *command_buffers; }; G_DEFINE_TYPE (MetaCompositorVulkan, meta_compositor_vulkan, META_TYPE_COMPOSITOR) static void destroy_command_buffers (MetaCompositorVulkan *vulkan) { if (vulkan->command_buffers == NULL) return; vkFreeCommandBuffers (vulkan->device, vulkan->command_pool, vulkan->n_command_buffers, vulkan->command_buffers); g_clear_pointer (&vulkan->command_buffers, g_free); vulkan->n_command_buffers = 0; } static gboolean create_command_buffers (MetaCompositorVulkan *vulkan, GError **error) { VkCommandBufferAllocateInfo allocate_info; VkResult result; destroy_command_buffers (vulkan); vulkan->n_command_buffers = vulkan->n_images; vulkan->command_buffers = g_new0 (VkCommandBuffer, vulkan->n_images); allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocate_info.pNext = NULL; allocate_info.commandPool = vulkan->command_pool; allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocate_info.commandBufferCount = vulkan->n_command_buffers; result = vkAllocateCommandBuffers (vulkan->device, &allocate_info, vulkan->command_buffers); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to allocate command buffers"); return FALSE; } return TRUE; } static void destroy_framebuffers (MetaCompositorVulkan *vulkan) { uint32_t i; if (vulkan->framebuffers == NULL) return; for (i = 0; i < vulkan->n_framebuffers; i++) vkDestroyFramebuffer (vulkan->device, vulkan->framebuffers[i], NULL); g_clear_pointer (&vulkan->framebuffers, g_free); vulkan->n_framebuffers = 0; } static gboolean create_framebuffers (MetaCompositorVulkan *vulkan, GError **error) { uint32_t i; VkFramebufferCreateInfo info; VkResult result; destroy_framebuffers (vulkan); vulkan->framebuffers = g_new0 (VkFramebuffer, vulkan->n_images); for (i = 0; i < vulkan->n_images; i++) { VkImageView attachments[1]; attachments[0] = vulkan->image_views[i]; info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; info.pNext = NULL; info.flags = 0; info.renderPass = vulkan->render_pass; info.attachmentCount = 1; info.pAttachments = attachments; info.width = vulkan->surface_extent.width; info.height = vulkan->surface_extent.height; info.layers = 1; result = vkCreateFramebuffer (vulkan->device, &info, NULL, &vulkan->framebuffers[i]); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create framebuffer"); return FALSE; } vulkan->n_framebuffers++; } return TRUE; } static void destroy_render_pass (MetaCompositorVulkan *vulkan) { if (vulkan->render_pass != VK_NULL_HANDLE) { vkDestroyRenderPass (vulkan->device, vulkan->render_pass, NULL); vulkan->render_pass = VK_NULL_HANDLE; } } static gboolean create_render_pass (MetaCompositorVulkan *vulkan, GError **error) { VkAttachmentDescription color_attachment; VkAttachmentReference color_attachment_ref; VkSubpassDescription subpass; VkRenderPassCreateInfo info; VkResult result; color_attachment.flags = 0; color_attachment.format = vulkan->surface_format.format; color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; color_attachment_ref.attachment = 0; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.inputAttachmentCount = 0; subpass.pInputAttachments = NULL; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; subpass.pResolveAttachments = NULL; subpass.pDepthStencilAttachment = NULL; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = NULL; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; info.pNext = NULL; info.flags = 0; info.attachmentCount = 1; info.pAttachments = &color_attachment; info.subpassCount = 1; info.pSubpasses = &subpass; info.dependencyCount = 0; info.pDependencies = NULL; destroy_render_pass (vulkan); result = vkCreateRenderPass (vulkan->device, &info, NULL, &vulkan->render_pass); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create render pass"); return FALSE; } return TRUE; } static void destroy_image_views (MetaCompositorVulkan *vulkan) { uint32_t i; if (vulkan->image_views == NULL) return; for (i = 0; i < vulkan->n_image_views; i++) vkDestroyImageView (vulkan->device, vulkan->image_views[i], NULL); g_clear_pointer (&vulkan->image_views, g_free); vulkan->n_image_views = 0; } static gboolean create_image_views (MetaCompositorVulkan *vulkan, GError **error) { VkComponentMapping components; VkImageSubresourceRange subresource_range; uint32_t i; VkImageViewCreateInfo info; VkResult result; destroy_image_views (vulkan); vulkan->image_views = g_new0 (VkImageView, vulkan->n_images); components.r = VK_COMPONENT_SWIZZLE_R; components.g = VK_COMPONENT_SWIZZLE_G; components.b = VK_COMPONENT_SWIZZLE_B; components.a = VK_COMPONENT_SWIZZLE_A; subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresource_range.baseMipLevel = 0; subresource_range.levelCount = 1; subresource_range.baseArrayLayer = 0; subresource_range.layerCount = 1; for (i = 0; i < vulkan->n_images; i++) { info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.pNext = NULL; info.flags = 0; info.image = vulkan->images[i]; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = vulkan->surface_format.format; info.components = components; info.subresourceRange = subresource_range; result = vkCreateImageView (vulkan->device, &info, NULL, &vulkan->image_views[i]); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create image view"); return FALSE; } vulkan->n_image_views++; } return TRUE; } static void destroy_swapchain (MetaCompositorVulkan *vulkan) { if (vulkan->swapchain != VK_NULL_HANDLE) { vkDestroySwapchainKHR (vulkan->device, vulkan->swapchain, NULL); vulkan->swapchain = VK_NULL_HANDLE; } g_clear_pointer (&vulkan->images, g_free); vulkan->n_images = 0; } static gboolean create_swapchain (MetaCompositorVulkan *vulkan, GError **error) { VkSurfaceCapabilitiesKHR capabilities; VkSwapchainCreateInfoKHR info; VkSwapchainKHR swapchain; VkResult result; result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR (vulkan->physical_device, vulkan->surface, &capabilities); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not query surface capabilities"); return FALSE; } vulkan->surface_extent = capabilities.currentExtent; if (vulkan->surface_extent.width == 0xffffffff && vulkan->surface_extent.height == 0xffffffff) { MetaDisplay *display; gint width; gint height; display = meta_compositor_get_display (META_COMPOSITOR (vulkan)); meta_screen_get_size (display->screen, &width, &height); vulkan->surface_extent.width = width; vulkan->surface_extent.height = height; } info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.pNext = NULL; info.flags = 0; info.surface = vulkan->surface; info.minImageCount = capabilities.minImageCount; info.imageFormat = vulkan->surface_format.format; info.imageColorSpace = vulkan->surface_format.colorSpace; info.imageExtent = vulkan->surface_extent; info.imageArrayLayers = 1; info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; info.preTransform = capabilities.currentTransform; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; info.presentMode = VK_PRESENT_MODE_FIFO_KHR; info.clipped = VK_FALSE; info.oldSwapchain = vulkan->swapchain; if (vulkan->graphics_family_index != vulkan->present_family_index) { uint32_t indices[2]; indices[0] = vulkan->graphics_family_index; indices[1] = vulkan->present_family_index; info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; info.queueFamilyIndexCount = 2; info.pQueueFamilyIndices = indices; } else { info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; info.queueFamilyIndexCount = 0; info.pQueueFamilyIndices = NULL; } result = vkCreateSwapchainKHR (vulkan->device, &info, NULL, &swapchain); destroy_swapchain (vulkan); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create swapchain"); return FALSE; } vulkan->swapchain = swapchain; result = vkGetSwapchainImagesKHR (vulkan->device, vulkan->swapchain, &vulkan->n_images, NULL); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get swapchain images"); return FALSE; } vulkan->images = g_new0 (VkImage, vulkan->n_images); result = vkGetSwapchainImagesKHR (vulkan->device, vulkan->swapchain, &vulkan->n_images, vulkan->images); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get swapchain images"); destroy_swapchain (vulkan); return FALSE; } return TRUE; } static void enumerate_instance_layers (MetaCompositorVulkan *vulkan) { uint32_t n_layers; VkLayerProperties *layers; VkResult result; uint32_t i; if (!meta_check_debug_flags (META_DEBUG_VULKAN) && g_getenv ("META_VULKAN_VALIDATE") == NULL) return; result = vkEnumerateInstanceLayerProperties (&n_layers, NULL); if (result != VK_SUCCESS) { meta_topic (META_DEBUG_VULKAN, "Failed to enumerate instance layer properties\n"); return; } layers = g_new0 (VkLayerProperties, n_layers); result = vkEnumerateInstanceLayerProperties (&n_layers, layers); if (result != VK_SUCCESS) { meta_topic (META_DEBUG_VULKAN, "Failed to enumerate instance layer properties\n"); g_free (layers); return; } meta_topic (META_DEBUG_VULKAN, "Available instance layers:\n"); meta_push_no_msg_prefix (); for (i = 0; i < n_layers; i++) { const gchar *layer_name; layer_name = layers[i].layerName; meta_topic (META_DEBUG_VULKAN, " %s v%u.%u.%u (%s)\n", layer_name, VK_VERSION_MAJOR (layers[i].specVersion), VK_VERSION_MINOR (layers[i].specVersion), VK_VERSION_PATCH (layers[i].specVersion), layers[i].description); if (g_strcmp0 (layer_name, "VK_LAYER_LUNARG_standard_validation") == 0) { vulkan->lunarg_validation_layer = TRUE; } } meta_pop_no_msg_prefix (); g_free (layers); } static void enumerate_instance_extensions (MetaCompositorVulkan *vulkan) { uint32_t n_extensions; VkExtensionProperties *extensions; VkResult result; uint32_t i; if (!meta_check_debug_flags (META_DEBUG_VULKAN) && g_getenv ("META_VULKAN_VALIDATE") == NULL) return; result = vkEnumerateInstanceExtensionProperties (NULL, &n_extensions, NULL); if (result != VK_SUCCESS) { meta_topic (META_DEBUG_VULKAN, "Failed to enumerate instance extension properties\n"); return; } extensions = g_new0 (VkExtensionProperties, n_extensions); result = vkEnumerateInstanceExtensionProperties (NULL, &n_extensions, extensions); if (result != VK_SUCCESS) { meta_topic (META_DEBUG_VULKAN, "Failed to enumerate instance extension properties\n"); g_free (extensions); return; } meta_topic (META_DEBUG_VULKAN, "Available instance extensions:\n"); meta_push_no_msg_prefix (); for (i = 0; i < n_extensions; i++) { const gchar *extension_name; extension_name = extensions[i].extensionName; meta_topic (META_DEBUG_VULKAN, " %s v%u.%u.%u\n", extension_name, VK_VERSION_MAJOR (extensions[i].specVersion), VK_VERSION_MINOR (extensions[i].specVersion), VK_VERSION_PATCH (extensions[i].specVersion)); if (g_strcmp0 (extension_name, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { vulkan->debug_report_extension = TRUE; } } meta_pop_no_msg_prefix (); g_free (extensions); } static gboolean create_instance (MetaCompositorVulkan *vulkan, GError **error) { GPtrArray *layers; GPtrArray *extensions; VkApplicationInfo app_info; VkInstanceCreateInfo instance_info; VkResult result; layers = g_ptr_array_new (); extensions = g_ptr_array_new (); if (vulkan->lunarg_validation_layer) g_ptr_array_add (layers, (gpointer) "VK_LAYER_LUNARG_standard_validation"); g_ptr_array_add (extensions, (gpointer) VK_KHR_SURFACE_EXTENSION_NAME); g_ptr_array_add (extensions, (gpointer) VK_KHR_XLIB_SURFACE_EXTENSION_NAME); if (vulkan->debug_report_extension) g_ptr_array_add (extensions, (gpointer) VK_EXT_DEBUG_REPORT_EXTENSION_NAME); app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.pNext = NULL; app_info.pApplicationName = "Metacity"; app_info.applicationVersion = VK_MAKE_VERSION (METACITY_MAJOR_VERSION, METACITY_MINOR_VERSION, METACITY_MICRO_VERSION); app_info.pEngineName = NULL; app_info.engineVersion = 0; app_info.apiVersion = VK_API_VERSION_1_0; instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_info.pNext = NULL; instance_info.flags = 0; instance_info.pApplicationInfo = &app_info; instance_info.enabledLayerCount = layers->len; instance_info.ppEnabledLayerNames = (const char * const *) layers->pdata; instance_info.enabledExtensionCount = extensions->len; instance_info.ppEnabledExtensionNames = (const char * const *) extensions->pdata; result = vkCreateInstance (&instance_info, NULL, &vulkan->instance); g_ptr_array_free (layers, TRUE); g_ptr_array_free (extensions, TRUE); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create Vulkan instance"); return FALSE; } return TRUE; } static VkBool32 debug_report_cb (VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage, void *pUserData) { if (!meta_check_debug_flags (META_DEBUG_VULKAN)) { if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) g_critical ("%s: %s", pLayerPrefix, pMessage); else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) g_critical ("%s: %s", pLayerPrefix, pMessage); else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) g_warning ("%s: %s", pLayerPrefix, pMessage); else meta_topic (META_DEBUG_VULKAN, "%s: %s\n", pLayerPrefix, pMessage); } else { meta_topic (META_DEBUG_VULKAN, "%s: %s\n", pLayerPrefix, pMessage); } return VK_FALSE; } static void setup_debug_callback (MetaCompositorVulkan *vulkan) { PFN_vkVoidFunction f; VkDebugReportFlagsEXT flags; VkDebugReportCallbackCreateInfoEXT info; VkResult result; if (!vulkan->lunarg_validation_layer || !vulkan->debug_report_extension) return; f = vkGetInstanceProcAddr (vulkan->instance, "vkCreateDebugReportCallbackEXT"); if (f == VK_NULL_HANDLE) { if (!meta_check_debug_flags (META_DEBUG_VULKAN)) g_warning ("VK_EXT_debug_report not found"); else meta_topic (META_DEBUG_VULKAN, "VK_EXT_debug_report not found"); return; } flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT; info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; info.pNext = NULL; info.flags = flags; info.pfnCallback = debug_report_cb; info.pUserData = NULL; result = ((PFN_vkCreateDebugReportCallbackEXT) f) (vulkan->instance, &info, NULL, &vulkan->debug_callback); if (result != VK_SUCCESS) { if (!meta_check_debug_flags (META_DEBUG_VULKAN)) g_warning ("Failed to set up debug callback"); else meta_topic (META_DEBUG_VULKAN, "Failed to set up debug callback"); } } static gboolean find_surface_format (MetaCompositorVulkan *vulkan, GError **error) { uint32_t n_formats; VkSurfaceFormatKHR *formats; VkResult result; uint32_t i; result = vkGetPhysicalDeviceSurfaceFormatsKHR (vulkan->physical_device, vulkan->surface, &n_formats, NULL); if (result != VK_SUCCESS || n_formats == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get surface formats"); return FALSE; } formats = g_new0 (VkSurfaceFormatKHR, n_formats); result = vkGetPhysicalDeviceSurfaceFormatsKHR (vulkan->physical_device, vulkan->surface, &n_formats, formats); for (i = 0; i < n_formats; i++) { if (formats[i].format == VK_FORMAT_B8G8R8A8_UNORM) { vulkan->surface_format = formats[i]; break; } } g_free (formats); if (i == n_formats) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find valid surface format"); return FALSE; } return TRUE; } static gboolean create_overlay_surface (MetaCompositorVulkan *vulkan, GError **error) { MetaCompositor *compositor; MetaDisplay *display; Window overlay; VkXlibSurfaceCreateInfoKHR info; VkResult result; compositor = META_COMPOSITOR (vulkan); display = meta_compositor_get_display (compositor); overlay = meta_compositor_get_overlay_window (compositor); info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; info.pNext = NULL; info.flags = 0; info.dpy = display->xdisplay; info.window = overlay; result = vkCreateXlibSurfaceKHR (vulkan->instance, &info, NULL, &vulkan->surface); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create Vulkan surface for overlay window"); return FALSE; } return TRUE; } static const gchar * device_type_to_string (VkPhysicalDeviceType type) { switch (type) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: return "other"; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return "integrated"; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return "discrete"; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return "virtual"; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: return "cpu"; break; #if VK_HEADER_VERSION < 140 case VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE: #endif case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM: default: break; } return "unknown"; } static gchar * queue_flags_to_string (VkQueueFlags flags) { GPtrArray *operations; gchar *result; operations = g_ptr_array_new (); if (flags & VK_QUEUE_GRAPHICS_BIT) g_ptr_array_add (operations, (gpointer) "graphics"); if (flags & VK_QUEUE_COMPUTE_BIT) g_ptr_array_add (operations, (gpointer) "compute"); if (flags & VK_QUEUE_TRANSFER_BIT) g_ptr_array_add (operations, (gpointer) "transfer"); if (flags & VK_QUEUE_SPARSE_BINDING_BIT) g_ptr_array_add (operations, (gpointer) "sparse binding"); g_ptr_array_add (operations, NULL); result = g_strjoinv (", ", (char **) operations->pdata); g_ptr_array_free (operations, TRUE); return result; } static gboolean enumerate_physical_devices (MetaCompositorVulkan *vulkan, GError **error) { uint32_t n_devices; VkPhysicalDevice *devices; VkResult result; uint32_t i; result = vkEnumeratePhysicalDevices (vulkan->instance, &n_devices, NULL); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to query the number of physical devices presents"); return FALSE; } if (n_devices == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find GPUs with Vulkan support"); return FALSE; } devices = g_new0 (VkPhysicalDevice, n_devices); result = vkEnumeratePhysicalDevices (vulkan->instance, &n_devices, devices); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to enumerate physical devices present"); g_free (devices); return FALSE; } meta_topic (META_DEBUG_VULKAN, "Available physical devices:\n"); meta_push_no_msg_prefix (); for (i = 0; i < n_devices; i++) { uint32_t n_family_properties; VkQueueFamilyProperties *family_properties; uint32_t graphics_family_index; uint32_t present_family_index; uint32_t j; if (meta_check_debug_flags (META_DEBUG_VULKAN)) { VkPhysicalDeviceProperties device_properties; vkGetPhysicalDeviceProperties (devices[i], &device_properties); meta_topic (META_DEBUG_VULKAN, " %s (type - %s, driver - v%d.%d.%d, " "api - v%d.%d.%d)\n", device_properties.deviceName, device_type_to_string (device_properties.deviceType), VK_VERSION_MAJOR (device_properties.driverVersion), VK_VERSION_MINOR (device_properties.driverVersion), VK_VERSION_PATCH (device_properties.driverVersion), VK_VERSION_MAJOR (device_properties.apiVersion), VK_VERSION_MINOR (device_properties.apiVersion), VK_VERSION_PATCH (device_properties.apiVersion)); } vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_family_properties, NULL); family_properties = g_new0 (VkQueueFamilyProperties, n_family_properties); vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_family_properties, family_properties); graphics_family_index = n_family_properties; present_family_index = n_family_properties; for (j = 0; j < n_family_properties; j++) { if (meta_check_debug_flags (META_DEBUG_VULKAN)) { gchar *operations; operations = queue_flags_to_string (family_properties[j].queueFlags); meta_topic (META_DEBUG_VULKAN, " queues: %d; operations: %s\n", family_properties[j].queueCount, operations); g_free (operations); } if (family_properties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT && graphics_family_index == n_family_properties) { graphics_family_index = j; } if (present_family_index == n_family_properties) { VkBool32 supported; result = vkGetPhysicalDeviceSurfaceSupportKHR (devices[i], j, vulkan->surface, &supported); if (result == VK_SUCCESS && supported) present_family_index = j; } } if (graphics_family_index != n_family_properties && present_family_index != n_family_properties && vulkan->physical_device == VK_NULL_HANDLE) { vulkan->physical_device = devices[i]; vulkan->graphics_family_index = graphics_family_index; vulkan->present_family_index = present_family_index; } g_free (family_properties); } meta_pop_no_msg_prefix (); g_free (devices); if (vulkan->physical_device == VK_NULL_HANDLE) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find a suitable GPU"); return FALSE; } return TRUE; } static gboolean create_logical_device (MetaCompositorVulkan *vulkan, GError **error) { GPtrArray *layers; GPtrArray *extensions; VkDeviceCreateInfo create_info; VkResult result; layers = g_ptr_array_new (); extensions = g_ptr_array_new (); if (vulkan->lunarg_validation_layer) g_ptr_array_add (layers, (gpointer) "VK_LAYER_LUNARG_standard_validation"); g_ptr_array_add (extensions, (gpointer) VK_KHR_SWAPCHAIN_EXTENSION_NAME); create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; create_info.pNext = NULL; create_info.flags = 0; create_info.enabledLayerCount = layers->len; create_info.ppEnabledLayerNames = (const char * const *) layers->pdata; create_info.enabledExtensionCount = extensions->len; create_info.ppEnabledExtensionNames = (const char * const *) extensions->pdata; create_info.pEnabledFeatures = NULL; if (vulkan->graphics_family_index != vulkan->present_family_index) { VkDeviceQueueCreateInfo queue_create_info[2]; queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info[0].pNext = NULL; queue_create_info[0].flags = 0; queue_create_info[0].queueFamilyIndex = vulkan->graphics_family_index; queue_create_info[0].queueCount = 1; queue_create_info[0].pQueuePriorities = (float []) { 1.0f }; queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info[1].pNext = NULL; queue_create_info[1].flags = 0; queue_create_info[1].queueFamilyIndex = vulkan->present_family_index; queue_create_info[1].queueCount = 1; queue_create_info[1].pQueuePriorities = (float []) { 1.0f }; create_info.queueCreateInfoCount = G_N_ELEMENTS (queue_create_info); create_info.pQueueCreateInfos = queue_create_info; } else { VkDeviceQueueCreateInfo queue_create_info; queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info.pNext = NULL; queue_create_info.flags = 0; queue_create_info.queueFamilyIndex = vulkan->graphics_family_index; queue_create_info.queueCount = 1; queue_create_info.pQueuePriorities = (float []) { 1.0f }; create_info.queueCreateInfoCount = 1; create_info.pQueueCreateInfos = &queue_create_info; } result = vkCreateDevice (vulkan->physical_device, &create_info, NULL, &vulkan->device); g_ptr_array_free (layers, TRUE); g_ptr_array_free (extensions, TRUE); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create logical device"); return FALSE; } vkGetDeviceQueue (vulkan->device, vulkan->graphics_family_index, 0, &vulkan->graphics_queue); vkGetDeviceQueue (vulkan->device, vulkan->present_family_index, 0, &vulkan->present_queue); return TRUE; } static gboolean create_command_pool (MetaCompositorVulkan *vulkan, GError **error) { VkCommandPoolCreateInfo info; VkResult result; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.pNext = NULL; info.flags = 0; info.queueFamilyIndex = vulkan->graphics_family_index; result = vkCreateCommandPool (vulkan->device, &info, NULL, &vulkan->command_pool); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create command pool"); return FALSE; } return TRUE; } static gboolean create_semaphore (MetaCompositorVulkan *vulkan, GError **error) { VkSemaphoreCreateInfo info; VkResult result; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; info.pNext = NULL; info.flags = 0; result = vkCreateSemaphore (vulkan->device, &info, NULL, &vulkan->semaphore); if (result != VK_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create semaphore"); return FALSE; } return TRUE; } static void meta_compositor_vulkan_finalize (GObject *object) { MetaCompositorVulkan *vulkan; vulkan = META_COMPOSITOR_VULKAN (object); destroy_command_buffers (vulkan); destroy_framebuffers (vulkan); destroy_render_pass (vulkan); destroy_image_views (vulkan); destroy_swapchain (vulkan); if (vulkan->semaphore != VK_NULL_HANDLE) { vkDestroySemaphore (vulkan->device, vulkan->semaphore, NULL); vulkan->semaphore = VK_NULL_HANDLE; } if (vulkan->command_pool != VK_NULL_HANDLE) { vkDestroyCommandPool (vulkan->device, vulkan->command_pool, NULL); vulkan->command_pool = VK_NULL_HANDLE; } if (vulkan->device != VK_NULL_HANDLE) { vkDestroyDevice (vulkan->device, NULL); vulkan->device = VK_NULL_HANDLE; } if (vulkan->surface != VK_NULL_HANDLE) { vkDestroySurfaceKHR (vulkan->instance, vulkan->surface, NULL); vulkan->surface = VK_NULL_HANDLE; } if (vulkan->debug_callback != VK_NULL_HANDLE) { PFN_vkVoidFunction f; f = vkGetInstanceProcAddr (vulkan->instance, "vkDestroyDebugReportCallbackEXT"); ((PFN_vkDestroyDebugReportCallbackEXT) f) (vulkan->instance, vulkan->debug_callback, NULL); vulkan->debug_callback = VK_NULL_HANDLE; } if (vulkan->instance != VK_NULL_HANDLE) { vkDestroyInstance (vulkan->instance, NULL); vulkan->instance = VK_NULL_HANDLE; } G_OBJECT_CLASS (meta_compositor_vulkan_parent_class)->finalize (object); } static gboolean not_implemented_cb (MetaCompositorVulkan *vulkan) { MetaDisplay *display; MetaCompositorType type; GEnumClass *enum_class; GEnumValue *enum_value; display = meta_compositor_get_display (META_COMPOSITOR (vulkan)); type = meta_prefs_get_compositor (); enum_class = g_type_class_ref (META_TYPE_COMPOSITOR_TYPE); enum_value = g_enum_get_value (enum_class, type); g_assert_nonnull (enum_value); g_warning ("“vulkan” compositor is not implemented, switching to “%s”...", enum_value->value_nick); g_type_class_unref (enum_class); g_unsetenv ("META_COMPOSITOR"); meta_display_update_compositor (display); return G_SOURCE_REMOVE; } static gboolean meta_compositor_vulkan_manage (MetaCompositor *compositor, GError **error) { MetaCompositorVulkan *vulkan; vulkan = META_COMPOSITOR_VULKAN (compositor); if (!meta_compositor_check_common_extensions (compositor, error)) return FALSE; enumerate_instance_layers (vulkan); enumerate_instance_extensions (vulkan); if (!create_instance (vulkan, error)) return FALSE; setup_debug_callback (vulkan); if (!meta_compositor_set_selection (compositor, error)) return FALSE; if (!meta_compositor_redirect_windows (compositor, error)) return FALSE; if (!create_overlay_surface (vulkan, error)) return FALSE; if (!enumerate_physical_devices (vulkan, error)) return FALSE; if (!find_surface_format (vulkan, error)) return FALSE; if (!create_logical_device (vulkan, error)) return FALSE; if (!create_command_pool (vulkan, error)) return FALSE; if (!create_semaphore (vulkan, error)) return FALSE; if (!create_swapchain (vulkan, error)) return FALSE; if (!create_image_views (vulkan, error)) return FALSE; if (!create_render_pass (vulkan, error)) return FALSE; if (!create_framebuffers (vulkan, error)) return FALSE; if (!create_command_buffers (vulkan, error)) return FALSE; g_timeout_add (10000, (GSourceFunc) not_implemented_cb, vulkan); return TRUE; } static MetaSurface * meta_compositor_vulkan_add_window (MetaCompositor *compositor, MetaWindow *window) { MetaSurface *surface; surface = g_object_new (META_TYPE_SURFACE_VULKAN, "compositor", compositor, "window", window, NULL); return surface; } static void meta_compositor_vulkan_process_event (MetaCompositor *compositor, XEvent *event, MetaWindow *window) { } static void meta_compositor_vulkan_sync_screen_size (MetaCompositor *compositor) { } static void meta_compositor_vulkan_pre_paint (MetaCompositor *compositor) { META_COMPOSITOR_CLASS (meta_compositor_vulkan_parent_class)->pre_paint (compositor); } static void meta_compositor_vulkan_redraw (MetaCompositor *compositor, XserverRegion all_damage) { } static void meta_compositor_vulkan_class_init (MetaCompositorVulkanClass *vulkan_class) { GObjectClass *object_class; MetaCompositorClass *compositor_class; object_class = G_OBJECT_CLASS (vulkan_class); compositor_class = META_COMPOSITOR_CLASS (vulkan_class); object_class->finalize = meta_compositor_vulkan_finalize; compositor_class->manage = meta_compositor_vulkan_manage; compositor_class->add_window = meta_compositor_vulkan_add_window; compositor_class->process_event = meta_compositor_vulkan_process_event; compositor_class->sync_screen_size = meta_compositor_vulkan_sync_screen_size; compositor_class->pre_paint = meta_compositor_vulkan_pre_paint; compositor_class->redraw = meta_compositor_vulkan_redraw; } static void meta_compositor_vulkan_init (MetaCompositorVulkan *vulkan) { meta_compositor_set_composited (META_COMPOSITOR (vulkan), TRUE); } MetaCompositor * meta_compositor_vulkan_new (MetaDisplay *display, GError **error) { return g_initable_new (META_TYPE_COMPOSITOR_VULKAN, NULL, error, "display", display, NULL); }