mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-09 18:30:08 +00:00
0c8e7d0ac0
We now hook Vulkan error reporting into glib's error reporting. I'm not sure on the severity handling yet, but we can fix that as we go along.
688 lines
28 KiB
C
688 lines
28 KiB
C
/* GDK - The GIMP Drawing Kit
|
||
*
|
||
* gdkvulkancontext-x11.c: X11 specific Vulkan wrappers
|
||
*
|
||
* Copyright © 2016 Benjamin Otte
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Library General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library 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
|
||
* Library General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Library General Public
|
||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include "gdkvulkancontextprivate.h"
|
||
|
||
#include <vulkan/vulkan.h>
|
||
|
||
#include "gdkdisplayprivate.h"
|
||
#include "gdkinternals.h"
|
||
#include "gdkintl.h"
|
||
|
||
typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate;
|
||
|
||
struct _GdkVulkanContextPrivate {
|
||
VkSurfaceKHR surface;
|
||
VkSurfaceFormatKHR image_format;
|
||
|
||
int swapchain_width, swapchain_height;
|
||
VkSwapchainKHR swapchain;
|
||
};
|
||
|
||
G_DEFINE_QUARK (gdk-vulkan-error-quark, gdk_vulkan_error)
|
||
|
||
static void gdk_vulkan_context_initable_init (GInitableIface *iface);
|
||
|
||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkVulkanContext, gdk_vulkan_context, GDK_TYPE_DRAW_CONTEXT,
|
||
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_vulkan_context_initable_init)
|
||
G_ADD_PRIVATE (GdkVulkanContext))
|
||
|
||
#ifdef GDK_WINDOWING_VULKAN
|
||
|
||
const char *
|
||
gdk_vulkan_strerror (VkResult result)
|
||
{
|
||
switch (result)
|
||
{
|
||
case VK_SUCCESS:
|
||
return "Command successfully completed.";
|
||
case VK_NOT_READY:
|
||
return "A fence or query has not yet completed.";
|
||
case VK_TIMEOUT:
|
||
return "A wait operation has not completed in the specified time.";
|
||
case VK_EVENT_SET:
|
||
return "An event is signaled.";
|
||
case VK_EVENT_RESET:
|
||
return "An event is unsignaled.";
|
||
case VK_INCOMPLETE:
|
||
return "A return array was too small for the result.";
|
||
case VK_SUBOPTIMAL_KHR:
|
||
return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully.";
|
||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||
return "A host memory allocation has failed.";
|
||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||
return "A device memory allocation has failed.";
|
||
case VK_ERROR_INITIALIZATION_FAILED:
|
||
return "Initialization of an object could not be completed for implementation-specific reasons.";
|
||
case VK_ERROR_DEVICE_LOST:
|
||
return "The logical or physical device has been lost.";
|
||
case VK_ERROR_MEMORY_MAP_FAILED:
|
||
return "Mapping of a memory object has failed.";
|
||
case VK_ERROR_LAYER_NOT_PRESENT:
|
||
return "A requested layer is not present or could not be loaded.";
|
||
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
||
return "A requested extension is not supported.";
|
||
case VK_ERROR_FEATURE_NOT_PRESENT:
|
||
return "A requested feature is not supported.";
|
||
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
||
return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons.";
|
||
case VK_ERROR_TOO_MANY_OBJECTS:
|
||
return "Too many objects of the type have already been created.";
|
||
case VK_ERROR_FORMAT_NOT_SUPPORTED:
|
||
return "A requested format is not supported on this device.";
|
||
case VK_ERROR_FRAGMENTED_POOL:
|
||
return "A requested pool allocation has failed due to fragmentation of the pool’s memory.";
|
||
case VK_ERROR_SURFACE_LOST_KHR:
|
||
return "A surface is no longer available.";
|
||
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
|
||
return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again.";
|
||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||
return "A surface has changed in such a way that it is no longer compatible with the swapchain.";
|
||
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
|
||
return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image.";
|
||
default:
|
||
return "Unknown Vulkan error.";
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_dispose (GObject *gobject)
|
||
{
|
||
GdkVulkanContext *context = GDK_VULKAN_CONTEXT (gobject);
|
||
GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
|
||
GdkDisplay *display;
|
||
|
||
if (priv->swapchain != VK_NULL_HANDLE)
|
||
{
|
||
vkDestroySwapchainKHR (gdk_vulkan_context_get_device (context),
|
||
priv->swapchain,
|
||
NULL);
|
||
priv->swapchain = VK_NULL_HANDLE;
|
||
}
|
||
|
||
if (priv->surface != VK_NULL_HANDLE)
|
||
{
|
||
vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context),
|
||
priv->surface,
|
||
NULL);
|
||
priv->surface = VK_NULL_HANDLE;
|
||
}
|
||
|
||
/* display will be unset in gdk_draw_context_dispose() */
|
||
display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
|
||
if (display)
|
||
gdk_display_unref_vulkan (display);
|
||
|
||
G_OBJECT_CLASS (gdk_vulkan_context_parent_class)->dispose (gobject);
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_class_init (GdkVulkanContextClass *klass)
|
||
{
|
||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||
|
||
gobject_class->dispose = gdk_vulkan_context_dispose;
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_init (GdkVulkanContext *self)
|
||
{
|
||
}
|
||
|
||
static gboolean
|
||
gdk_vulkan_context_check_swapchain (GdkVulkanContext *context,
|
||
GError **error)
|
||
{
|
||
GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
|
||
GdkWindow *window = gdk_draw_context_get_window (GDK_DRAW_CONTEXT (context));
|
||
VkSurfaceCapabilitiesKHR capabilities;
|
||
VkCompositeAlphaFlagBitsKHR composite_alpha;
|
||
VkSwapchainKHR new_swapchain;
|
||
VkResult res;
|
||
|
||
if (gdk_window_get_width (window) == priv->swapchain_width &&
|
||
gdk_window_get_height (window) == priv->swapchain_height)
|
||
return TRUE;
|
||
|
||
res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, gdk_vulkan_context_get_physical_device (context),
|
||
priv->surface,
|
||
&capabilities);
|
||
if (res != VK_SUCCESS)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"Could not query surface capabilities: %s", gdk_vulkan_strerror (res));
|
||
return FALSE;
|
||
}
|
||
|
||
if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
|
||
composite_alpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
|
||
else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
|
||
composite_alpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
|
||
else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
||
{
|
||
/* let's hope the backend knows what it's doing */
|
||
composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||
}
|
||
else
|
||
{
|
||
GDK_NOTE (VULKAN, g_warning ("Vulkan swapchain doesn't do transparency. Using opaque swapchain instead."));
|
||
composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||
}
|
||
|
||
res = GDK_VK_CHECK (vkCreateSwapchainKHR, gdk_vulkan_context_get_device (context),
|
||
&(VkSwapchainCreateInfoKHR) {
|
||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||
.pNext = NULL,
|
||
.flags = 0,
|
||
.surface = priv->surface,
|
||
.minImageCount = 2,
|
||
.imageFormat = priv->image_format.format,
|
||
.imageColorSpace = priv->image_format.colorSpace,
|
||
.imageExtent = capabilities.currentExtent,
|
||
.imageArrayLayers = 1,
|
||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 1,
|
||
.pQueueFamilyIndices = (uint32_t[1]) {
|
||
gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index
|
||
},
|
||
.preTransform = capabilities.currentTransform,
|
||
.compositeAlpha = composite_alpha,
|
||
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
|
||
.clipped = VK_FALSE,
|
||
.oldSwapchain = priv->swapchain
|
||
},
|
||
NULL,
|
||
&new_swapchain);
|
||
|
||
if (priv->swapchain != VK_NULL_HANDLE)
|
||
vkDestroySwapchainKHR (gdk_vulkan_context_get_device (context),
|
||
priv->swapchain,
|
||
NULL);
|
||
|
||
if (res == VK_SUCCESS)
|
||
{
|
||
priv->swapchain_width = gdk_window_get_width (window);
|
||
priv->swapchain_height = gdk_window_get_height (window);
|
||
priv->swapchain = new_swapchain;
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"Could not create swapchain for this window: %s", gdk_vulkan_strerror (res));
|
||
priv->swapchain = VK_NULL_HANDLE;
|
||
priv->swapchain_width = 0;
|
||
priv->swapchain_height = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_vulkan_context_real_init (GInitable *initable,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GdkVulkanContext *context = GDK_VULKAN_CONTEXT (initable);
|
||
GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
|
||
VkResult res;
|
||
VkBool32 supported;
|
||
uint32_t i;
|
||
|
||
if (!gdk_display_ref_vulkan (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), error))
|
||
return FALSE;
|
||
|
||
res = GDK_VULKAN_CONTEXT_GET_CLASS (context)->create_surface (context, &priv->surface);
|
||
if (res != VK_SUCCESS)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"Could not create surface for this window: %s", gdk_vulkan_strerror (res));
|
||
return FALSE;
|
||
}
|
||
|
||
res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceSupportKHR, gdk_vulkan_context_get_physical_device (context),
|
||
gdk_vulkan_context_get_queue_family_index (context),
|
||
priv->surface,
|
||
&supported);
|
||
if (res != VK_SUCCESS)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"Could not check if queue family supports this window: %s", gdk_vulkan_strerror (res));
|
||
}
|
||
else if (!supported)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"FIXME: Queue family does not support surface. Write code to try different queue family.");
|
||
}
|
||
else
|
||
{
|
||
uint32_t n_formats;
|
||
GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context),
|
||
priv->surface,
|
||
&n_formats, NULL);
|
||
VkSurfaceFormatKHR formats[n_formats];
|
||
GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context),
|
||
priv->surface,
|
||
&n_formats, formats);
|
||
for (i = 0; i < n_formats; i++)
|
||
{
|
||
if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB)
|
||
break;
|
||
}
|
||
if (i == n_formats)
|
||
{
|
||
g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"No supported image format found.");
|
||
goto out_surface;
|
||
}
|
||
priv->image_format = formats[i];
|
||
|
||
if (!gdk_vulkan_context_check_swapchain (context, error))
|
||
goto out_surface;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
out_surface:
|
||
vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context),
|
||
priv->surface,
|
||
NULL);
|
||
priv->surface = VK_NULL_HANDLE;
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_initable_init (GInitableIface *iface)
|
||
{
|
||
iface->init = gdk_vulkan_context_real_init;
|
||
}
|
||
|
||
VkInstance
|
||
gdk_vulkan_context_get_instance (GdkVulkanContext *context)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);
|
||
|
||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_instance;
|
||
}
|
||
|
||
VkPhysicalDevice
|
||
gdk_vulkan_context_get_physical_device (GdkVulkanContext *context)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);
|
||
|
||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_physical_device;
|
||
}
|
||
|
||
VkDevice
|
||
gdk_vulkan_context_get_device (GdkVulkanContext *context)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);
|
||
|
||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_device;
|
||
}
|
||
|
||
VkQueue
|
||
gdk_vulkan_context_get_queue (GdkVulkanContext *context)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);
|
||
|
||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue;
|
||
}
|
||
|
||
uint32_t
|
||
gdk_vulkan_context_get_queue_family_index (GdkVulkanContext *context)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0);
|
||
|
||
return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index;
|
||
}
|
||
|
||
VkFormat
|
||
gdk_vulkan_context_get_image_format (GdkVulkanContext *context)
|
||
{
|
||
GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
|
||
|
||
g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_FORMAT_UNDEFINED);
|
||
|
||
return priv->image_format.format;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_display_create_vulkan_device (GdkDisplay *display,
|
||
GError **error)
|
||
{
|
||
uint32_t i, j;
|
||
|
||
uint32_t n_devices;
|
||
GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, NULL);
|
||
VkPhysicalDevice devices[n_devices];
|
||
GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, devices);
|
||
|
||
if (n_devices == 0)
|
||
{
|
||
/* Give a different error for 0 devices so people know their drivers suck. */
|
||
g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"No Vulkan devices available.");
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < n_devices; i++)
|
||
{
|
||
VkPhysicalDeviceProperties props;
|
||
|
||
vkGetPhysicalDeviceProperties (devices[i], &props);
|
||
|
||
GDK_NOTE (VULKAN, g_print ("Vulkan Device %u:\n", i));
|
||
GDK_NOTE (VULKAN, g_print (" %s (%u)\n", props.deviceName, props.deviceType));
|
||
GDK_NOTE (VULKAN, g_print (" vendor ID: 0x%Xu\n", props.vendorID));
|
||
GDK_NOTE (VULKAN, g_print (" device ID: 0x%Xu\n", props.deviceID));
|
||
GDK_NOTE (VULKAN, g_print (" API version %u.%u.%u\n",
|
||
VK_VERSION_MAJOR (props.apiVersion),
|
||
VK_VERSION_MINOR (props.apiVersion),
|
||
VK_VERSION_PATCH (props.apiVersion)));
|
||
GDK_NOTE (VULKAN, g_print (" driver version %u.%u.%u\n",
|
||
VK_VERSION_MAJOR (props.driverVersion),
|
||
VK_VERSION_MINOR (props.driverVersion),
|
||
VK_VERSION_PATCH (props.driverVersion)));
|
||
|
||
uint32_t n_queue_props;
|
||
vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, NULL);
|
||
VkQueueFamilyProperties queue_props[n_queue_props];
|
||
vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, queue_props);
|
||
|
||
for (j = 0; j < n_queue_props; j++)
|
||
{
|
||
GDK_NOTE (VULKAN, g_print (" queue %u/%u: %s\n", j, n_queue_props, queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ? "graphics" : "no graphics"));
|
||
if (queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||
{
|
||
GDK_NOTE (VULKAN, g_print (" => trying this queue\n"));
|
||
if (GDK_VK_CHECK (vkCreateDevice, devices[i],
|
||
&(VkDeviceCreateInfo) {
|
||
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||
NULL,
|
||
0,
|
||
1,
|
||
&(VkDeviceQueueCreateInfo) {
|
||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||
.queueFamilyIndex = j,
|
||
.queueCount = 1,
|
||
.pQueuePriorities = (float []) { 1.0f },
|
||
},
|
||
0,
|
||
NULL,
|
||
1,
|
||
(const char * const []) {
|
||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||
},
|
||
},
|
||
NULL,
|
||
&display->vk_device) != VK_SUCCESS)
|
||
continue;
|
||
|
||
display->vk_physical_device = devices[i];
|
||
vkGetDeviceQueue(display->vk_device, j, 0, &display->vk_queue);
|
||
display->vk_queue_family_index = j;
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
|
||
"Could not find a Vulkan device with the required features.");
|
||
return FALSE;
|
||
}
|
||
|
||
static VkBool32
|
||
gdk_vulkan_debug_report (VkDebugReportFlagsEXT flags,
|
||
VkDebugReportObjectTypeEXT objectType,
|
||
uint64_t object,
|
||
size_t location,
|
||
int32_t messageCode,
|
||
const char* pLayerPrefix,
|
||
const char* pMessage,
|
||
void* pUserData)
|
||
{
|
||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
|
||
{
|
||
g_critical ("Vulkan: %s: %s\n", pLayerPrefix, pMessage);
|
||
}
|
||
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
||
{
|
||
g_critical ("Vulkan: %s: %s\n", pLayerPrefix, pMessage);
|
||
}
|
||
else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
|
||
{
|
||
g_warning ("Vulkan: %s: %s\n", pLayerPrefix, pMessage);
|
||
}
|
||
else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
|
||
{
|
||
g_debug ("Vulkan: %s: %s\n", pLayerPrefix, pMessage);
|
||
}
|
||
else
|
||
{
|
||
g_message ("Vulkan: %s: %s\n", pLayerPrefix, pMessage);
|
||
}
|
||
|
||
return VK_TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_display_create_vulkan_instance (GdkDisplay *display,
|
||
GError **error)
|
||
{
|
||
uint32_t i;
|
||
GPtrArray *used_extensions;
|
||
GPtrArray *used_layers;
|
||
gboolean validate = FALSE, have_debug_report = FALSE;
|
||
VkResult res;
|
||
|
||
if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
|
||
"The %s backend has no Vulkan support.", G_OBJECT_TYPE_NAME (display));
|
||
return FALSE;
|
||
}
|
||
|
||
uint32_t n_extensions;
|
||
GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, NULL);
|
||
VkExtensionProperties extensions[n_extensions];
|
||
GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, extensions);
|
||
|
||
used_extensions = g_ptr_array_new ();
|
||
g_ptr_array_add (used_extensions, (gpointer) VK_KHR_SURFACE_EXTENSION_NAME);
|
||
g_ptr_array_add (used_extensions, (gpointer) GDK_DISPLAY_GET_CLASS (display)->vk_extension_name);
|
||
|
||
for (i = 0; i < n_extensions; i++)
|
||
{
|
||
GDK_NOTE (VULKAN, g_print ("Extension available: %s v%u.%u.%u\n",
|
||
extensions[i].extensionName,
|
||
VK_VERSION_MAJOR (extensions[i].specVersion),
|
||
VK_VERSION_MINOR (extensions[i].specVersion),
|
||
VK_VERSION_PATCH (extensions[i].specVersion)));
|
||
|
||
if (g_str_equal (extensions[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
|
||
{
|
||
g_ptr_array_add (used_extensions, (gpointer) VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||
have_debug_report = TRUE;
|
||
}
|
||
}
|
||
|
||
uint32_t n_layers;
|
||
GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, NULL);
|
||
VkLayerProperties layers[n_layers];
|
||
GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, layers);
|
||
|
||
used_layers = g_ptr_array_new ();
|
||
|
||
for (i = 0; i < n_layers; i++)
|
||
{
|
||
GDK_NOTE (VULKAN, g_print ("Layer available: %s v%u.%u.%u (%s)\n",
|
||
layers[i].layerName,
|
||
VK_VERSION_MAJOR (layers[i].specVersion),
|
||
VK_VERSION_MINOR (layers[i].specVersion),
|
||
VK_VERSION_PATCH (layers[i].specVersion),
|
||
layers[i].description));
|
||
if ((_gdk_vulkan_flags & GDK_VULKAN_VALIDATE) &&
|
||
g_str_equal (layers[i].layerName, "VK_LAYER_LUNARG_standard_validation"))
|
||
{
|
||
g_ptr_array_add (used_layers, (gpointer) "VK_LAYER_LUNARG_standard_validation");
|
||
validate = TRUE;
|
||
}
|
||
}
|
||
|
||
if ((_gdk_vulkan_flags & GDK_VULKAN_VALIDATE) && !validate)
|
||
{
|
||
g_warning ("Vulkan validation layers were requested, but not found. Running without.");
|
||
}
|
||
|
||
res = GDK_VK_CHECK (vkCreateInstance, &(VkInstanceCreateInfo) {
|
||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||
.pNext = NULL,
|
||
.flags = 0,
|
||
.pApplicationInfo = &(VkApplicationInfo) {
|
||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||
.pNext = NULL,
|
||
.pApplicationName = g_get_application_name (),
|
||
.applicationVersion = 0,
|
||
.pEngineName = "GTK+",
|
||
.engineVersion = VK_MAKE_VERSION (GDK_MAJOR_VERSION, GDK_MINOR_VERSION, GDK_MICRO_VERSION),
|
||
.apiVersion = VK_API_VERSION_1_0
|
||
},
|
||
.enabledLayerCount = used_layers->len,
|
||
.ppEnabledLayerNames = (const char * const *) used_layers->pdata,
|
||
.enabledExtensionCount = used_extensions->len,
|
||
.ppEnabledExtensionNames = (const char * const *) used_extensions->pdata
|
||
},
|
||
NULL,
|
||
&display->vk_instance);
|
||
g_ptr_array_free (used_layers, TRUE);
|
||
g_ptr_array_free (used_extensions, TRUE);
|
||
|
||
if (res != VK_SUCCESS)
|
||
{
|
||
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
|
||
"Could not create a Vulkan instance: %s", gdk_vulkan_strerror (res));
|
||
return FALSE;
|
||
}
|
||
|
||
if (have_debug_report)
|
||
{
|
||
PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
|
||
|
||
vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkCreateDebugReportCallbackEXT" );
|
||
GDK_VK_CHECK (vkCreateDebugReportCallbackEXT, display->vk_instance,
|
||
&(VkDebugReportCallbackCreateInfoEXT) {
|
||
.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
|
||
.pNext = NULL,
|
||
.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,
|
||
.pfnCallback = gdk_vulkan_debug_report,
|
||
.pUserData = NULL
|
||
},
|
||
NULL,
|
||
&display->vk_debug_callback);
|
||
}
|
||
|
||
if (!gdk_display_create_vulkan_device (display, error))
|
||
{
|
||
if (display->vk_debug_callback != VK_NULL_HANDLE)
|
||
{
|
||
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
|
||
|
||
vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT");
|
||
vkDestroyDebugReportCallbackEXT (display->vk_instance,
|
||
display->vk_debug_callback,
|
||
NULL);
|
||
display->vk_debug_callback = VK_NULL_HANDLE;
|
||
}
|
||
vkDestroyInstance (display->vk_instance, NULL);
|
||
display->vk_instance = VK_NULL_HANDLE;
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
gboolean
|
||
gdk_display_ref_vulkan (GdkDisplay *display,
|
||
GError **error)
|
||
{
|
||
if (display->vulkan_refcount == 0)
|
||
{
|
||
if (!gdk_display_create_vulkan_instance (display, error))
|
||
return FALSE;
|
||
}
|
||
|
||
display->vulkan_refcount++;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void
|
||
gdk_display_unref_vulkan (GdkDisplay *display)
|
||
{
|
||
g_return_if_fail (GDK_IS_DISPLAY (display));
|
||
g_return_if_fail (display->vulkan_refcount > 0);
|
||
|
||
display->vulkan_refcount--;
|
||
if (display->vulkan_refcount > 0)
|
||
return;
|
||
|
||
vkDestroyDevice (display->vk_device, NULL);
|
||
display->vk_device = VK_NULL_HANDLE;
|
||
if (display->vk_debug_callback != VK_NULL_HANDLE)
|
||
{
|
||
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
|
||
|
||
vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT");
|
||
vkDestroyDebugReportCallbackEXT (display->vk_instance,
|
||
display->vk_debug_callback,
|
||
NULL);
|
||
display->vk_debug_callback = VK_NULL_HANDLE;
|
||
}
|
||
vkDestroyInstance (display->vk_instance, NULL);
|
||
display->vk_instance = VK_NULL_HANDLE;
|
||
}
|
||
|
||
#else /* GDK_WINDOWING_VULKAN */
|
||
|
||
static void
|
||
gdk_vulkan_context_class_init (GdkVulkanContextClass *klass)
|
||
{
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_init (GdkVulkanContext *self)
|
||
{
|
||
}
|
||
|
||
static void
|
||
gdk_vulkan_context_initable_init (GInitableIface *iface)
|
||
{
|
||
}
|
||
|
||
#endif /* GDK_WINDOWING_VULKAN */
|