2016-11-21 13:18:43 +00:00
/* GDK - The GIMP Drawing Kit
*
2016-12-09 19:05:26 +00:00
* gdkvulkancontext . c : Vulkan wrappers
2016-11-21 13:18:43 +00:00
*
* 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"
2016-12-09 19:21:18 +00:00
# include "gdkvulkancontext.h"
2016-11-21 13:18:43 +00:00
2016-12-09 19:21:18 +00:00
# include "gdkvulkancontextprivate.h"
2016-11-21 13:18:43 +00:00
# include "gdkdisplayprivate.h"
2022-09-24 03:33:42 +00:00
# include <glib/gi18n-lib.h>
2016-11-21 13:18:43 +00:00
2017-12-04 23:58:30 +00:00
/**
2021-02-21 05:13:57 +00:00
* GdkVulkanContext :
2017-12-04 23:58:30 +00:00
*
2021-02-21 05:13:57 +00:00
* ` GdkVulkanContext ` is an object representing the platform - specific
2017-12-26 19:10:54 +00:00
* Vulkan draw context .
2017-12-04 23:58:30 +00:00
*
2021-02-21 05:13:57 +00:00
* ` GdkVulkanContext ` s are created for a surface using
* [ method @ Gdk . Surface . create_vulkan_context ] , and the context will match
2021-10-17 20:05:35 +00:00
* the characteristics of the surface .
2017-12-04 23:58:30 +00:00
*
2021-02-21 05:13:57 +00:00
* Support for ` GdkVulkanContext ` is platform - specific and context creation
2017-12-04 23:58:30 +00:00
* can fail , returning % NULL context .
*/
2016-11-21 13:18:43 +00:00
typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate ;
struct _GdkVulkanContextPrivate {
2016-12-09 19:21:18 +00:00
# ifdef GDK_RENDERING_VULKAN
2016-11-28 15:34:01 +00:00
VkSurfaceKHR surface ;
2016-11-29 02:20:31 +00:00
VkSurfaceFormatKHR image_format ;
VkSwapchainKHR swapchain ;
2016-12-01 03:07:20 +00:00
VkSemaphore draw_semaphore ;
2016-11-29 23:54:48 +00:00
guint n_images ;
VkImage * images ;
2016-12-26 21:24:22 +00:00
cairo_region_t * * regions ;
2019-10-04 21:53:10 +00:00
gboolean has_present_region ;
2016-12-09 19:21:18 +00:00
# endif
2016-12-01 03:07:20 +00:00
2016-12-09 19:21:18 +00:00
guint32 draw_index ;
2017-01-04 17:26:14 +00:00
guint vulkan_ref : 1 ;
2016-11-29 23:54:48 +00:00
} ;
enum {
IMAGES_UPDATED ,
LAST_SIGNAL
2016-11-21 13:18:43 +00:00
} ;
G_DEFINE_QUARK ( gdk - vulkan - error - quark , gdk_vulkan_error )
2016-11-29 23:54:48 +00:00
static guint signals [ LAST_SIGNAL ] = { 0 } ;
2016-11-28 15:34:01 +00:00
static void gdk_vulkan_context_initable_init ( GInitableIface * iface ) ;
2016-11-28 16:55:46 +00:00
G_DEFINE_ABSTRACT_TYPE_WITH_CODE ( GdkVulkanContext , gdk_vulkan_context , GDK_TYPE_DRAW_CONTEXT ,
2016-11-28 15:34:01 +00:00
G_IMPLEMENT_INTERFACE ( G_TYPE_INITABLE , gdk_vulkan_context_initable_init )
G_ADD_PRIVATE ( GdkVulkanContext ) )
2016-11-21 13:18:43 +00:00
2016-12-09 19:11:37 +00:00
# ifdef GDK_RENDERING_VULKAN
2016-11-29 02:20:31 +00:00
const char *
gdk_vulkan_strerror ( VkResult result )
{
2018-02-23 12:49:17 +00:00
/* If your compiler brought you here with a warning about missing
* enumeration values , you ' re running a newer Vulkan version than
2020-04-26 17:40:23 +00:00
* the GTK developers ( or you are a GTK developer ) and have
2018-02-23 12:49:17 +00:00
* encountered a newly added Vulkan error message .
* You want to add it to this enum now .
*
2020-05-28 08:00:03 +00:00
* Because the Vulkan people don ' t make adding this too easy , here ' s
2018-02-23 12:49:17 +00:00
* the process to manage it :
* 1. go to
2019-11-01 13:49:41 +00:00
* https : //github.com/KhronosGroup/Vulkan-Headers/blob/master/include/vulkan/vulkan_core.h
2018-02-23 12:49:17 +00:00
* 2. Find the line where this enum value was added .
* 3. Click the commit that added this line .
* 4. The commit you ' re looking at now should also change
* VK_HEADER_VERSION , find that number .
* 5. Use that number in the # ifdef when adding the enum value to
* this enum .
* 6. For the error message , look at the specification ( the one
* that includes all extensions ) at
* https : //www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VkResult
* 7. If this value has not been added to the specification yet ,
* search for the error message in the text of specification .
* Often it will have a description that can be used as an error
* message .
* 8. If that didn ' t lead to one ( or you are lazy ) , just use the
* literal string of the enum value as the error message . A
* GTK developer will add the correct one once it ' s added to the
* specification .
*/
switch ( result )
2016-11-29 02:20:31 +00:00
{
case VK_SUCCESS :
2021-08-12 12:18:46 +00:00
return " Command successfully completed. (VK_SUCCESS) " ;
2016-11-29 02:20:31 +00:00
case VK_NOT_READY :
2021-08-12 12:18:46 +00:00
return " A fence or query has not yet completed. (VK_NOT_READY) " ;
2016-11-29 02:20:31 +00:00
case VK_TIMEOUT :
2021-08-12 12:18:46 +00:00
return " A wait operation has not completed in the specified time. (VK_TIMEOUT) " ;
2016-11-29 02:20:31 +00:00
case VK_EVENT_SET :
2021-08-12 12:18:46 +00:00
return " An event is signaled. (VK_EVENT_SET) " ;
2016-11-29 02:20:31 +00:00
case VK_EVENT_RESET :
2021-08-12 12:18:46 +00:00
return " An event is unsignaled. (VK_EVENT_RESET) " ;
2016-11-29 02:20:31 +00:00
case VK_INCOMPLETE :
2021-08-12 12:18:46 +00:00
return " A return array was too small for the result. (VK_INCOMPLETE) " ;
2016-11-29 02:20:31 +00:00
case VK_SUBOPTIMAL_KHR :
2021-08-12 12:18:46 +00:00
return " A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_OUT_OF_HOST_MEMORY :
2021-08-12 12:18:46 +00:00
return " A host memory allocation has failed. (VK_ERROR_OUT_OF_HOST_MEMORY) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_OUT_OF_DEVICE_MEMORY :
2021-08-12 12:18:46 +00:00
return " A device memory allocation has failed. (VK_ERROR_OUT_OF_DEVICE_MEMORY) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_INITIALIZATION_FAILED :
2021-08-12 12:18:46 +00:00
return " Initialization of an object could not be completed for implementation-specific reasons. (VK_ERROR_INITIALIZATION_FAILED) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_DEVICE_LOST :
2021-08-12 12:18:46 +00:00
return " The logical or physical device has been lost. (VK_ERROR_DEVICE_LOST) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_MEMORY_MAP_FAILED :
2021-08-12 12:18:46 +00:00
return " Mapping of a memory object has failed. (VK_ERROR_MEMORY_MAP_FAILED) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_LAYER_NOT_PRESENT :
2021-08-12 12:18:46 +00:00
return " A requested layer is not present or could not be loaded. (VK_ERROR_LAYER_NOT_PRESENT) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_EXTENSION_NOT_PRESENT :
2021-08-12 12:18:46 +00:00
return " A requested extension is not supported. (VK_ERROR_EXTENSION_NOT_PRESENT) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_FEATURE_NOT_PRESENT :
2021-08-12 12:18:46 +00:00
return " A requested feature is not supported. (VK_ERROR_FEATURE_NOT_PRESENT) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_INCOMPATIBLE_DRIVER :
2021-08-12 12:18:46 +00:00
return " The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons. (VK_ERROR_INCOMPATIBLE_DRIVER) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_TOO_MANY_OBJECTS :
2021-08-12 12:18:46 +00:00
return " Too many objects of the type have already been created. (VK_ERROR_TOO_MANY_OBJECTS) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_FORMAT_NOT_SUPPORTED :
2021-08-12 12:18:46 +00:00
return " A requested format is not supported on this device. (VK_ERROR_FORMAT_NOT_SUPPORTED) " ;
2018-02-23 12:48:20 +00:00
# if VK_HEADER_VERSION >= 24
2016-11-29 02:20:31 +00:00
case VK_ERROR_FRAGMENTED_POOL :
2021-08-12 12:18:46 +00:00
return " A requested pool allocation has failed due to fragmentation of the pool’ s memory. (VK_ERROR_FRAGMENTED_POOL) " ;
2018-02-23 12:48:20 +00:00
# endif
2016-11-29 02:20:31 +00:00
case VK_ERROR_SURFACE_LOST_KHR :
2021-08-12 12:18:46 +00:00
return " A surface is no longer available. (VK_ERROR_SURFACE_LOST_KHR) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR :
2021-08-12 12:18:46 +00:00
return " The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again. (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_OUT_OF_DATE_KHR :
2021-08-12 12:18:46 +00:00
return " A surface has changed in such a way that it is no longer compatible with the swapchain. (VK_ERROR_OUT_OF_DATE_KHR) " ;
2016-11-29 02:20:31 +00:00
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR :
2021-08-12 12:18:46 +00:00
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. (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR) " ;
2017-10-06 19:19:42 +00:00
case VK_ERROR_VALIDATION_FAILED_EXT :
2021-08-12 12:18:46 +00:00
return " The application caused the validation layer to fail. (VK_ERROR_VALIDATION_FAILED_EXT) " ;
2017-10-06 19:19:42 +00:00
case VK_ERROR_INVALID_SHADER_NV :
2021-08-12 12:18:46 +00:00
return " One or more shaders failed to compile or link. (VK_ERROR_INVALID_SHADER_NV) " ;
2018-02-23 12:48:20 +00:00
# if VK_HEADER_VERSION >= 39
2017-10-06 22:29:00 +00:00
case VK_ERROR_OUT_OF_POOL_MEMORY_KHR :
2021-08-12 12:18:46 +00:00
return " A pool memory allocation has failed. (VK_ERROR_OUT_OF_POOL_MEMORY_KHR) " ;
2018-02-23 12:48:20 +00:00
# endif
# if VK_HEADER_VERSION >= 54
2017-10-06 22:29:00 +00:00
case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR :
2021-08-12 12:18:46 +00:00
return " An external handle is not a valid handle of the specified type. (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR) " ;
2018-02-23 12:48:20 +00:00
# endif
# if VK_HEADER_VERSION >= 64
case VK_ERROR_NOT_PERMITTED_EXT :
2021-08-12 12:18:46 +00:00
return " The caller does not have sufficient privileges. (VK_ERROR_NOT_PERMITTED_EXT) " ;
2018-04-10 07:53:48 +00:00
# endif
# if VK_HEADER_VERSION >= 72
case VK_ERROR_FRAGMENTATION_EXT :
2021-08-12 12:18:46 +00:00
return " A descriptor pool creation has failed due to fragmentation. (VK_ERROR_FRAGMENTATION_EXT) " ;
2018-02-23 12:48:20 +00:00
# endif
2018-11-13 14:28:28 +00:00
# if VK_HEADER_VERSION >= 89
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT :
2021-08-12 12:18:46 +00:00
return " Invalid DRM format modifier plane layout (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT) " ;
2018-11-13 14:28:28 +00:00
# endif
2019-04-12 17:03:22 +00:00
# if VK_HEADER_VERSION >= 97
case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT :
2021-08-12 12:18:46 +00:00
return " Invalid device address (VK_ERROR_INVALID_DEVICE_ADDRESS_EXT) " ;
2019-04-12 17:03:22 +00:00
# endif
2019-11-01 13:49:41 +00:00
# if VK_HEADER_VERSION >= 105
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT :
2021-08-12 12:18:46 +00:00
return " An operation on a swapchain created with VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT failed as it did not have exclusive full-screen access. (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) " ;
2019-11-01 13:49:41 +00:00
# endif
2020-02-11 14:07:43 +00:00
# if VK_HEADER_VERSION >= 131
case VK_ERROR_UNKNOWN :
2021-08-12 12:18:46 +00:00
return " An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred. (VK_ERROR_UNKNOWN) " ;
2020-04-26 17:40:23 +00:00
# endif
# if VK_HEADER_VERSION >= 135
2020-12-21 23:04:16 +00:00
# if VK_HEADER_VERSION < 162
case VK_ERROR_INCOMPATIBLE_VERSION_KHR :
2021-08-12 12:18:46 +00:00
return " This error was removed by the Vulkan gods. (VK_ERROR_INCOMPATIBLE_VERSION_KHR) " ;
2020-12-21 23:04:16 +00:00
# endif
2020-04-26 17:40:23 +00:00
case VK_THREAD_IDLE_KHR :
2021-08-12 12:18:46 +00:00
return " A deferred operation is not complete but there is currently no work for this thread to do at the time of this call. (VK_THREAD_IDLE_KHR) " ;
2020-04-26 17:40:23 +00:00
case VK_THREAD_DONE_KHR :
2021-08-12 12:18:46 +00:00
return " A deferred operation is not complete but there is no work remaining to assign to additional threads. (VK_THREAD_DONE_KHR) " ;
2020-04-26 17:40:23 +00:00
case VK_OPERATION_DEFERRED_KHR :
2021-08-12 12:18:46 +00:00
return " A deferred operation was requested and at least some of the work was deferred. (VK_OPERATION_DEFERRED_KHR) " ;
2020-04-26 17:40:23 +00:00
case VK_OPERATION_NOT_DEFERRED_KHR :
2021-08-12 12:18:46 +00:00
return " A deferred operation was requested and no operations were deferred. (VK_OPERATION_NOT_DEFERRED_KHR) " ;
2020-04-26 17:40:23 +00:00
case VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT :
2021-08-12 12:18:46 +00:00
return " A requested pipeline creation would have required compilation, but the application requested compilation to not be performed. (VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT) " ;
2020-02-11 14:07:43 +00:00
# endif
2020-12-28 18:41:18 +00:00
# if VK_HEADER_VERSION < 140
2017-10-06 19:19:42 +00:00
case VK_RESULT_RANGE_SIZE :
2020-05-11 07:07:41 +00:00
# endif
2017-10-06 19:19:42 +00:00
case VK_RESULT_MAX_ENUM :
2016-11-29 02:20:31 +00:00
default :
return " Unknown Vulkan error. " ;
}
}
2016-11-21 13:18:43 +00:00
static void
gdk_vulkan_context_dispose ( GObject * gobject )
{
GdkVulkanContext * context = GDK_VULKAN_CONTEXT ( gobject ) ;
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
2016-11-28 16:55:46 +00:00
GdkDisplay * display ;
2016-12-01 03:07:20 +00:00
VkDevice device ;
2016-12-26 21:24:22 +00:00
guint i ;
2016-11-21 13:18:43 +00:00
2016-12-26 21:24:22 +00:00
for ( i = 0 ; i < priv - > n_images ; i + + )
{
cairo_region_destroy ( priv - > regions [ i ] ) ;
}
g_clear_pointer ( & priv - > regions , g_free ) ;
2016-11-29 23:54:48 +00:00
g_clear_pointer ( & priv - > images , g_free ) ;
priv - > n_images = 0 ;
2016-12-01 03:07:20 +00:00
device = gdk_vulkan_context_get_device ( context ) ;
if ( priv - > draw_semaphore ! = VK_NULL_HANDLE )
{
vkDestroySemaphore ( device ,
priv - > draw_semaphore ,
NULL ) ;
priv - > draw_semaphore = VK_NULL_HANDLE ;
}
2016-11-29 02:20:31 +00:00
if ( priv - > swapchain ! = VK_NULL_HANDLE )
{
2016-12-01 03:07:20 +00:00
vkDestroySwapchainKHR ( device ,
2016-11-29 02:20:31 +00:00
priv - > swapchain ,
NULL ) ;
priv - > swapchain = VK_NULL_HANDLE ;
}
2016-11-28 16:55:46 +00:00
if ( priv - > surface ! = VK_NULL_HANDLE )
2016-11-21 13:18:43 +00:00
{
2016-11-28 16:55:46 +00:00
vkDestroySurfaceKHR ( gdk_vulkan_context_get_instance ( context ) ,
priv - > surface ,
NULL ) ;
priv - > surface = VK_NULL_HANDLE ;
2016-11-21 13:18:43 +00:00
}
2016-11-28 16:55:46 +00:00
/* display will be unset in gdk_draw_context_dispose() */
display = gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( context ) ) ;
2017-01-04 17:26:14 +00:00
if ( display & & priv - > vulkan_ref )
2016-11-28 16:55:46 +00:00
gdk_display_unref_vulkan ( display ) ;
2016-11-21 13:18:43 +00:00
2016-11-28 16:55:46 +00:00
G_OBJECT_CLASS ( gdk_vulkan_context_parent_class ) - > dispose ( gobject ) ;
2016-11-21 13:18:43 +00:00
}
2016-11-29 02:20:31 +00:00
static gboolean
gdk_vulkan_context_check_swapchain ( GdkVulkanContext * context ,
GError * * error )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
2018-03-20 14:14:10 +00:00
GdkSurface * surface = gdk_draw_context_get_surface ( GDK_DRAW_CONTEXT ( context ) ) ;
2016-11-29 02:20:31 +00:00
VkSurfaceCapabilitiesKHR capabilities ;
VkCompositeAlphaFlagBitsKHR composite_alpha ;
VkSwapchainKHR new_swapchain ;
VkResult res ;
2016-11-29 23:54:48 +00:00
VkDevice device ;
2016-12-26 21:24:22 +00:00
guint i ;
2016-11-29 02:20:31 +00:00
2016-11-29 23:54:48 +00:00
device = gdk_vulkan_context_get_device ( context ) ;
2016-11-29 02:20:31 +00:00
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_INHERIT_BIT_KHR )
{
/* let's hope the backend knows what it's doing */
composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR ;
}
else
{
2022-09-23 00:48:58 +00:00
GDK_DISPLAY_DEBUG ( gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( context ) ) , VULKAN ,
" Vulkan swapchain doesn't do transparency. Using opaque swapchain instead. " ) ;
2016-11-29 02:20:31 +00:00
composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ;
}
2017-01-03 23:11:57 +00:00
/*
* Per https : //www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#VkSurfaceCapabilitiesKHR
2020-09-10 17:55:16 +00:00
* the current extent may assume a special value , meaning that the extent should assume whatever
2018-03-20 14:14:10 +00:00
* value the surface has .
2017-01-03 23:11:57 +00:00
*/
if ( capabilities . currentExtent . width = = - 1 | | capabilities . currentExtent . height = = - 1 )
{
2020-09-10 17:55:16 +00:00
capabilities . currentExtent . width = MAX ( 1 , gdk_surface_get_width ( surface ) * gdk_surface_get_scale_factor ( surface ) ) ;
capabilities . currentExtent . height = MAX ( 1 , gdk_surface_get_height ( surface ) * gdk_surface_get_scale_factor ( surface ) ) ;
2017-01-03 23:11:57 +00:00
}
2016-11-29 23:54:48 +00:00
res = GDK_VK_CHECK ( vkCreateSwapchainKHR , device ,
2016-11-29 02:20:31 +00:00
& ( VkSwapchainCreateInfoKHR ) {
. sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR ,
. pNext = NULL ,
. flags = 0 ,
. surface = priv - > surface ,
2018-03-29 02:56:07 +00:00
. minImageCount = CLAMP ( 4 ,
2017-01-04 17:38:40 +00:00
capabilities . minImageCount ,
capabilities . maxImageCount ? capabilities . maxImageCount : G_MAXUINT32 ) ,
2016-11-29 02:20:31 +00:00
. 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 )
2016-11-29 23:54:48 +00:00
{
vkDestroySwapchainKHR ( device ,
priv - > swapchain ,
NULL ) ;
2016-12-26 21:24:22 +00:00
for ( i = 0 ; i < priv - > n_images ; i + + )
{
cairo_region_destroy ( priv - > regions [ i ] ) ;
}
g_clear_pointer ( & priv - > regions , g_free ) ;
2016-11-29 23:54:48 +00:00
g_clear_pointer ( & priv - > images , g_free ) ;
priv - > n_images = 0 ;
}
2016-11-29 02:20:31 +00:00
if ( res = = VK_SUCCESS )
{
priv - > swapchain = new_swapchain ;
2016-11-29 23:54:48 +00:00
GDK_VK_CHECK ( vkGetSwapchainImagesKHR , device ,
priv - > swapchain ,
& priv - > n_images ,
NULL ) ;
priv - > images = g_new ( VkImage , priv - > n_images ) ;
GDK_VK_CHECK ( vkGetSwapchainImagesKHR , device ,
priv - > swapchain ,
& priv - > n_images ,
priv - > images ) ;
2016-12-26 21:24:22 +00:00
priv - > regions = g_new ( cairo_region_t * , priv - > n_images ) ;
for ( i = 0 ; i < priv - > n_images ; i + + )
{
priv - > regions [ i ] = cairo_region_create_rectangle ( & ( cairo_rectangle_int_t ) {
0 , 0 ,
2018-03-20 14:14:10 +00:00
gdk_surface_get_width ( surface ) ,
gdk_surface_get_height ( surface ) ,
2016-12-26 21:24:22 +00:00
} ) ;
}
2016-11-29 02:20:31 +00:00
}
else
{
g_set_error ( error , GDK_VULKAN_ERROR , GDK_VULKAN_ERROR_NOT_AVAILABLE ,
2018-03-20 14:14:10 +00:00
" Could not create swapchain for this surface: %s " , gdk_vulkan_strerror ( res ) ) ;
2016-11-29 02:20:31 +00:00
priv - > swapchain = VK_NULL_HANDLE ;
return FALSE ;
}
2016-11-29 23:54:48 +00:00
g_signal_emit ( context , signals [ IMAGES_UPDATED ] , 0 ) ;
return res = = VK_SUCCESS ;
2016-11-29 02:20:31 +00:00
}
2019-10-04 21:53:10 +00:00
static gboolean
device_supports_incremental_present ( VkPhysicalDevice device )
{
VkExtensionProperties * extensions ;
uint32_t n_device_extensions ;
vkEnumerateDeviceExtensionProperties ( device , NULL , & n_device_extensions , NULL ) ;
extensions = g_newa ( VkExtensionProperties , n_device_extensions ) ;
vkEnumerateDeviceExtensionProperties ( device , NULL , & n_device_extensions , extensions ) ;
for ( uint32_t i = 0 ; i < n_device_extensions ; i + + )
{
if ( g_str_equal ( extensions [ i ] . extensionName , VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) )
return TRUE ;
}
return FALSE ;
}
2016-12-01 03:07:20 +00:00
static void
gdk_vulkan_context_begin_frame ( GdkDrawContext * draw_context ,
2021-10-06 16:25:30 +00:00
gboolean prefers_high_depth ,
2016-12-01 03:07:20 +00:00
cairo_region_t * region )
{
GdkVulkanContext * context = GDK_VULKAN_CONTEXT ( draw_context ) ;
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
2016-12-26 21:24:22 +00:00
guint i ;
2016-12-01 03:07:20 +00:00
2016-12-26 21:24:22 +00:00
for ( i = 0 ; i < priv - > n_images ; i + + )
{
cairo_region_union ( priv - > regions [ i ] , region ) ;
}
2016-12-01 03:07:20 +00:00
GDK_VK_CHECK ( vkAcquireNextImageKHR , gdk_vulkan_context_get_device ( context ) ,
priv - > swapchain ,
UINT64_MAX ,
priv - > draw_semaphore ,
VK_NULL_HANDLE ,
& priv - > draw_index ) ;
2016-12-26 21:24:22 +00:00
cairo_region_union ( region , priv - > regions [ priv - > draw_index ] ) ;
2016-12-01 03:07:20 +00:00
}
static void
gdk_vulkan_context_end_frame ( GdkDrawContext * draw_context ,
2018-04-23 21:26:14 +00:00
cairo_region_t * painted )
2016-12-01 03:07:20 +00:00
{
GdkVulkanContext * context = GDK_VULKAN_CONTEXT ( draw_context ) ;
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
2019-12-03 15:42:03 +00:00
GdkSurface * surface = gdk_draw_context_get_surface ( draw_context ) ;
2019-10-04 21:53:10 +00:00
VkPresentRegionsKHR * regionsptr = VK_NULL_HANDLE ;
VkPresentRegionsKHR regions ;
cairo_rectangle_int_t extents ;
2019-12-03 15:42:03 +00:00
int scale ;
2019-10-04 21:53:10 +00:00
cairo_region_get_extents ( painted , & extents ) ;
2019-12-03 15:42:03 +00:00
scale = gdk_surface_get_scale_factor ( surface ) ;
2019-10-04 21:53:10 +00:00
regions = ( VkPresentRegionsKHR ) {
. sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR ,
. swapchainCount = 1 ,
. pRegions = & ( VkPresentRegionKHR ) {
. rectangleCount = 1 ,
. pRectangles = & ( VkRectLayerKHR ) {
. layer = 0 ,
2019-12-03 15:42:03 +00:00
. offset . x = extents . x * scale ,
. offset . y = extents . y * scale ,
. extent . width = extents . width * scale ,
. extent . height = extents . height * scale ,
2019-10-04 21:53:10 +00:00
}
} ,
} ;
if ( priv - > has_present_region )
regionsptr = & regions ;
2016-12-01 03:07:20 +00:00
GDK_VK_CHECK ( vkQueuePresentKHR , gdk_vulkan_context_get_queue ( context ) ,
& ( VkPresentInfoKHR ) {
. sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR ,
2016-12-08 16:40:07 +00:00
. waitSemaphoreCount = 1 ,
. pWaitSemaphores = ( VkSemaphore [ ] ) {
priv - > draw_semaphore
} ,
2016-12-01 03:07:20 +00:00
. swapchainCount = 1 ,
. pSwapchains = ( VkSwapchainKHR [ ] ) {
priv - > swapchain
} ,
. pImageIndices = ( uint32_t [ ] ) {
priv - > draw_index
} ,
2019-10-04 21:53:10 +00:00
. pNext = regionsptr ,
2016-12-01 03:07:20 +00:00
} ) ;
2016-12-26 21:24:22 +00:00
cairo_region_destroy ( priv - > regions [ priv - > draw_index ] ) ;
priv - > regions [ priv - > draw_index ] = cairo_region_create ( ) ;
2016-12-01 03:07:20 +00:00
}
2018-04-08 22:40:47 +00:00
static void
gdk_vulkan_context_surface_resized ( GdkDrawContext * draw_context )
{
GdkVulkanContext * context = GDK_VULKAN_CONTEXT ( draw_context ) ;
GError * error = NULL ;
if ( ! gdk_vulkan_context_check_swapchain ( context , & error ) )
{
g_warning ( " %s " , error - > message ) ;
g_error_free ( error ) ;
return ;
}
}
2016-12-01 03:07:20 +00:00
static void
gdk_vulkan_context_class_init ( GdkVulkanContextClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
GdkDrawContextClass * draw_context_class = GDK_DRAW_CONTEXT_CLASS ( klass ) ;
gobject_class - > dispose = gdk_vulkan_context_dispose ;
draw_context_class - > begin_frame = gdk_vulkan_context_begin_frame ;
draw_context_class - > end_frame = gdk_vulkan_context_end_frame ;
2018-04-08 22:40:47 +00:00
draw_context_class - > surface_resized = gdk_vulkan_context_surface_resized ;
2016-12-01 03:07:20 +00:00
/**
* GdkVulkanContext : : images - updated :
* @ context : the object on which the signal is emitted
*
2021-02-21 05:13:57 +00:00
* Emitted when the images managed by this context have changed .
*
* Usually this means that the swapchain had to be recreated ,
2018-03-20 14:14:10 +00:00
* for example in response to a change of the surface size .
2016-12-01 03:07:20 +00:00
*/
signals [ IMAGES_UPDATED ] =
g_signal_new ( g_intern_static_string ( " images-updated " ) ,
G_OBJECT_CLASS_TYPE ( gobject_class ) ,
G_SIGNAL_RUN_LAST ,
0 ,
NULL , NULL ,
2019-05-29 20:05:19 +00:00
NULL ,
2016-12-01 03:07:20 +00:00
G_TYPE_NONE , 0 ) ;
}
static void
gdk_vulkan_context_init ( GdkVulkanContext * self )
{
}
2016-11-28 15:34:01 +00:00
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 ) ;
2019-10-04 21:53:10 +00:00
GdkDisplay * display = gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( context ) ) ;
2016-11-29 02:20:31 +00:00
VkResult res ;
VkBool32 supported ;
uint32_t i ;
2016-11-28 15:34:01 +00:00
2019-10-04 21:53:10 +00:00
priv - > vulkan_ref = gdk_display_ref_vulkan ( display , error ) ;
2017-01-04 17:26:14 +00:00
if ( ! priv - > vulkan_ref )
2016-11-28 15:34:01 +00:00
return FALSE ;
2016-11-29 02:20:31 +00:00
res = GDK_VULKAN_CONTEXT_GET_CLASS ( context ) - > create_surface ( context , & priv - > surface ) ;
if ( res ! = VK_SUCCESS )
2016-11-28 15:34:01 +00:00
{
2016-11-29 02:20:31 +00:00
g_set_error ( error , GDK_VULKAN_ERROR , GDK_VULKAN_ERROR_NOT_AVAILABLE ,
2018-03-20 14:14:10 +00:00
" Could not create surface for this surface: %s " , gdk_vulkan_strerror ( res ) ) ;
2016-11-28 15:34:01 +00:00
return FALSE ;
}
2016-11-29 02:20:31 +00:00
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 ,
2018-03-20 14:14:10 +00:00
" Could not check if queue family supports this surface: %s " , gdk_vulkan_strerror ( res ) ) ;
2016-11-29 02:20:31 +00:00
}
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 ) ;
2016-12-28 14:34:04 +00:00
VkSurfaceFormatKHR * formats = g_newa ( VkSurfaceFormatKHR , n_formats ) ;
2016-11-29 02:20:31 +00:00
GDK_VK_CHECK ( vkGetPhysicalDeviceSurfaceFormatsKHR , gdk_vulkan_context_get_physical_device ( context ) ,
priv - > surface ,
& n_formats , formats ) ;
for ( i = 0 ; i < n_formats ; i + + )
{
2016-12-25 04:57:16 +00:00
if ( formats [ i ] . format = = VK_FORMAT_B8G8R8A8_UNORM )
2016-11-29 02:20:31 +00:00
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 ] ;
2019-10-04 21:53:10 +00:00
priv - > has_present_region = device_supports_incremental_present ( display - > vk_physical_device ) ;
2016-11-29 02:20:31 +00:00
if ( ! gdk_vulkan_context_check_swapchain ( context , error ) )
goto out_surface ;
2016-12-01 03:07:20 +00:00
GDK_VK_CHECK ( vkCreateSemaphore , gdk_vulkan_context_get_device ( context ) ,
& ( VkSemaphoreCreateInfo ) {
. sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO ,
} ,
NULL ,
& priv - > draw_semaphore ) ;
2016-11-29 02:20:31 +00:00
return TRUE ;
}
out_surface :
vkDestroySurfaceKHR ( gdk_vulkan_context_get_instance ( context ) ,
priv - > surface ,
NULL ) ;
priv - > surface = VK_NULL_HANDLE ;
return FALSE ;
2016-11-28 15:34:01 +00:00
}
static void
gdk_vulkan_context_initable_init ( GInitableIface * iface )
{
iface - > init = gdk_vulkan_context_real_init ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_instance :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the Vulkan instance that is associated with @ context .
*
* Returns : ( transfer none ) : the VkInstance
*/
2016-11-28 15:34:01 +00:00
VkInstance
gdk_vulkan_context_get_instance ( GdkVulkanContext * context )
{
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( context ) , NULL ) ;
2016-11-28 16:55:46 +00:00
return gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( context ) ) - > vk_instance ;
2016-11-28 15:34:01 +00:00
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_physical_device :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the Vulkan physical device that this context is using .
*
* Returns : ( transfer none ) : the VkPhysicalDevice
*/
2016-11-29 02:20:31 +00:00
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 ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_device :
2021-05-20 03:39:18 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the Vulkan device that this context is using .
*
* Returns : ( transfer none ) : the VkDevice
*/
2016-11-29 02:20:31 +00:00
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 ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_queue :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the Vulkan queue that this context is using .
*
* Returns : ( transfer none ) : the VkQueue
*/
2016-11-29 02:20:31 +00:00
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 ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_queue_family_index :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the family index for the queue that this context is using .
2021-02-21 05:13:57 +00:00
*
2017-12-26 17:46:46 +00:00
* See vkGetPhysicalDeviceQueueFamilyProperties ( ) .
*
* Returns : the index
*/
2016-11-29 02:20:31 +00:00
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 ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_image_format :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the image format that this context is using .
*
* Returns : ( transfer none ) : the VkFormat
*/
2016-11-29 02:20:31 +00:00
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 ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_n_images :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the number of images that this context is using in its swap chain .
*
* Returns : the number of images
*/
2016-12-01 03:07:20 +00:00
uint32_t
2016-11-29 23:54:48 +00:00
gdk_vulkan_context_get_n_images ( GdkVulkanContext * context )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( context ) , 0 ) ;
return priv - > n_images ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_image :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
* @ id : the index of the image to return
*
* Gets the image with index @ id that this context is using .
*
* Returns : ( transfer none ) : the VkImage
*/
2016-11-29 23:54:48 +00:00
VkImage
gdk_vulkan_context_get_image ( GdkVulkanContext * context ,
guint id )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( context ) , VK_NULL_HANDLE ) ;
g_return_val_if_fail ( id < priv - > n_images , VK_NULL_HANDLE ) ;
return priv - > images [ id ] ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_draw_index :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the index of the image that is currently being drawn .
*
2021-02-21 05:13:57 +00:00
* This function can only be used between [ method @ Gdk . DrawContext . begin_frame ]
* and [ method @ Gdk . DrawContext . end_frame ] calls .
2017-12-26 17:46:46 +00:00
*
* Returns : the index of the images that is being drawn
*/
2016-12-01 03:07:20 +00:00
uint32_t
gdk_vulkan_context_get_draw_index ( GdkVulkanContext * context )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( context ) , 0 ) ;
2018-04-23 16:24:29 +00:00
g_return_val_if_fail ( gdk_draw_context_is_in_frame ( GDK_DRAW_CONTEXT ( context ) ) , 0 ) ;
2016-12-01 03:07:20 +00:00
return priv - > draw_index ;
}
2017-12-26 17:46:46 +00:00
/**
* gdk_vulkan_context_get_draw_semaphore :
2021-02-21 05:13:57 +00:00
* @ context : a ` GdkVulkanContext `
2017-12-26 17:46:46 +00:00
*
* Gets the Vulkan semaphore that protects access to the image that is
* currently being drawn .
*
2021-02-21 05:13:57 +00:00
* This function can only be used between [ method @ Gdk . DrawContext . begin_frame ]
* and [ method @ Gdk . DrawContext . end_frame ] calls .
2017-12-26 17:46:46 +00:00
*
* Returns : ( transfer none ) : the VkSemaphore
*/
2016-12-01 03:07:20 +00:00
VkSemaphore
gdk_vulkan_context_get_draw_semaphore ( GdkVulkanContext * context )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( context ) , VK_NULL_HANDLE ) ;
2018-04-23 16:24:29 +00:00
g_return_val_if_fail ( gdk_draw_context_is_in_frame ( GDK_DRAW_CONTEXT ( context ) ) , VK_NULL_HANDLE ) ;
2016-12-01 03:07:20 +00:00
return priv - > draw_semaphore ;
}
2016-11-21 13:18:43 +00:00
static gboolean
2016-11-28 15:34:01 +00:00
gdk_display_create_vulkan_device ( GdkDisplay * display ,
GError * * error )
2016-11-21 13:18:43 +00:00
{
2018-01-10 01:33:04 +00:00
uint32_t i , j , k ;
const char * override ;
gboolean list_devices ;
int first , last ;
2016-11-21 13:18:43 +00:00
2017-11-30 13:48:15 +00:00
uint32_t n_devices = 0 ;
2016-11-21 13:18:43 +00:00
GDK_VK_CHECK ( vkEnumeratePhysicalDevices , display - > vk_instance , & n_devices , NULL ) ;
2017-11-30 13:48:15 +00:00
VkPhysicalDevice * devices ;
2016-11-21 13:18:43 +00:00
2016-11-28 15:34:01 +00:00
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 ;
}
2017-11-30 13:48:15 +00:00
devices = g_newa ( VkPhysicalDevice , n_devices ) ;
GDK_VK_CHECK ( vkEnumeratePhysicalDevices , display - > vk_instance , & n_devices , devices ) ;
2018-01-10 01:33:04 +00:00
first = 0 ;
last = n_devices ;
override = g_getenv ( " GDK_VULKAN_DEVICE " ) ;
list_devices = FALSE ;
if ( override )
{
if ( g_strcmp0 ( override , " list " ) = = 0 )
list_devices = TRUE ;
else
{
gint64 device_idx ;
2019-01-22 03:30:47 +00:00
GError * error2 = NULL ;
2018-01-10 01:33:04 +00:00
2019-01-22 03:30:47 +00:00
if ( ! g_ascii_string_to_signed ( override , 10 , 0 , G_MAXINT , & device_idx , & error2 ) )
2018-01-10 01:33:04 +00:00
{
2019-01-22 03:30:47 +00:00
g_warning ( " Failed to parse %s: %s " , " GDK_VULKAN_DEVICE " , error2 - > message ) ;
g_error_free ( error2 ) ;
2018-01-10 01:33:04 +00:00
device_idx = - 1 ;
}
if ( device_idx < 0 | | device_idx > = n_devices )
g_warning ( " %s value out of range, ignoring " , " GDK_VULKAN_DEVICE " ) ;
else
{
first = device_idx ;
last = first + 1 ;
}
}
}
2018-01-12 00:48:27 +00:00
if ( list_devices | | GDK_DISPLAY_DEBUG_CHECK ( display , VULKAN ) )
2016-11-21 13:18:43 +00:00
{
2018-01-10 01:33:04 +00:00
for ( i = 0 ; i < n_devices ; i + + )
{
VkPhysicalDeviceProperties props ;
VkQueueFamilyProperties * queue_props ;
uint32_t n_queue_props ;
const char * device_type [ ] = {
" Other " , " Integrated GPU " , " Discrete GPU " , " Virtual GPU " , " CPU "
} ;
struct {
int bit ;
const char * name ;
} queue_caps [ ] = {
{ VK_QUEUE_GRAPHICS_BIT , " graphics " } ,
{ VK_QUEUE_COMPUTE_BIT , " compute " } ,
{ VK_QUEUE_TRANSFER_BIT , " transfer " } ,
{ VK_QUEUE_SPARSE_BINDING_BIT , " sparse binding " }
} ;
vkGetPhysicalDeviceProperties ( devices [ i ] , & props ) ;
vkGetPhysicalDeviceQueueFamilyProperties ( devices [ i ] , & n_queue_props , NULL ) ;
queue_props = g_newa ( VkQueueFamilyProperties , n_queue_props ) ;
vkGetPhysicalDeviceQueueFamilyProperties ( devices [ i ] , & n_queue_props , queue_props ) ;
g_print ( " Vulkan Device %u: \n " , i ) ;
g_print ( " %s (%s) \n " , props . deviceName , device_type [ props . deviceType ] ) ;
g_print ( " Vendor ID: 0x%Xu \n " , props . vendorID ) ;
g_print ( " Device ID: 0x%Xu \n " , props . deviceID ) ;
g_print ( " API version %u.%u.%u \n " ,
VK_VERSION_MAJOR ( props . apiVersion ) ,
VK_VERSION_MINOR ( props . apiVersion ) ,
VK_VERSION_PATCH ( props . apiVersion ) ) ;
for ( j = 0 ; j < n_queue_props ; j + + )
{
const char * sep = " " ;
g_print ( " Queue %d: " , j ) ;
for ( k = 0 ; k < G_N_ELEMENTS ( queue_caps ) ; k + + )
{
if ( queue_props [ j ] . queueFlags & queue_caps [ k ] . bit )
{
g_print ( " %s%s " , sep , queue_caps [ k ] . name ) ;
sep = " / " ;
}
}
g_print ( " \n " ) ;
}
}
}
2016-11-21 13:18:43 +00:00
2018-01-10 01:33:04 +00:00
for ( i = first ; i < last ; i + + )
{
2016-11-21 13:18:43 +00:00
uint32_t n_queue_props ;
vkGetPhysicalDeviceQueueFamilyProperties ( devices [ i ] , & n_queue_props , NULL ) ;
2016-12-28 14:34:04 +00:00
VkQueueFamilyProperties * queue_props = g_newa ( VkQueueFamilyProperties , n_queue_props ) ;
2016-11-21 13:18:43 +00:00
vkGetPhysicalDeviceQueueFamilyProperties ( devices [ i ] , & n_queue_props , queue_props ) ;
for ( j = 0 ; j < n_queue_props ; j + + )
{
if ( queue_props [ j ] . queueFlags & VK_QUEUE_GRAPHICS_BIT )
{
2019-10-04 21:53:10 +00:00
GPtrArray * device_extensions ;
gboolean has_incremental_present ;
has_incremental_present = device_supports_incremental_present ( devices [ i ] ) ;
device_extensions = g_ptr_array_new ( ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_SWAPCHAIN_EXTENSION_NAME ) ;
if ( has_incremental_present )
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) ;
2022-09-23 00:48:58 +00:00
GDK_DISPLAY_DEBUG ( display , VULKAN , " Using Vulkan device %u, queue %u " , i , j ) ;
2016-11-21 13:18:43 +00:00
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 ,
2019-10-04 21:53:10 +00:00
device_extensions - > len ,
2020-07-24 18:40:36 +00:00
( const char * const * ) device_extensions - > pdata
2016-11-21 13:18:43 +00:00
} ,
NULL ,
& display - > vk_device ) ! = VK_SUCCESS )
2019-10-04 21:53:10 +00:00
{
g_ptr_array_unref ( device_extensions ) ;
continue ;
}
g_ptr_array_unref ( device_extensions ) ;
2016-11-21 13:18:43 +00:00
display - > vk_physical_device = devices [ i ] ;
vkGetDeviceQueue ( display - > vk_device , j , 0 , & display - > vk_queue ) ;
2016-11-29 02:20:31 +00:00
display - > vk_queue_family_index = j ;
2016-11-21 13:18:43 +00:00
return TRUE ;
}
}
}
2016-11-28 15:34:01 +00:00
g_set_error_literal ( error , GDK_VULKAN_ERROR , GDK_VULKAN_ERROR_NOT_AVAILABLE ,
" Could not find a Vulkan device with the required features. " ) ;
2016-11-21 13:18:43 +00:00
return FALSE ;
}
2017-06-05 14:17:12 +00:00
static VkBool32 VKAPI_CALL
2016-11-29 15:29:19 +00:00
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 )
2017-09-26 22:17:27 +00:00
g_critical ( " Vulkan: %s: %s " , pLayerPrefix , pMessage ) ;
2016-11-29 15:29:19 +00:00
else if ( flags & VK_DEBUG_REPORT_WARNING_BIT_EXT )
2017-09-26 22:17:27 +00:00
g_critical ( " Vulkan: %s: %s " , pLayerPrefix , pMessage ) ;
2016-11-29 15:29:19 +00:00
else if ( flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT )
2017-09-26 22:17:27 +00:00
g_warning ( " Vulkan: %s: %s " , pLayerPrefix , pMessage ) ;
2016-11-29 15:29:19 +00:00
else if ( flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT )
2017-09-26 22:17:27 +00:00
g_debug ( " Vulkan: %s: %s " , pLayerPrefix , pMessage ) ;
2016-11-29 15:29:19 +00:00
else
2017-09-26 22:17:27 +00:00
g_info ( " Vulkan: %s: %s " , pLayerPrefix , pMessage ) ;
2017-01-06 03:35:19 +00:00
return VK_FALSE ;
2016-11-29 15:29:19 +00:00
}
2016-11-21 13:18:43 +00:00
static gboolean
2016-11-28 15:34:01 +00:00
gdk_display_create_vulkan_instance ( GdkDisplay * display ,
GError * * error )
2016-11-21 13:18:43 +00:00
{
uint32_t i ;
2016-11-29 15:29:19 +00:00
GPtrArray * used_extensions ;
2016-11-29 14:35:04 +00:00
GPtrArray * used_layers ;
2016-11-29 15:29:19 +00:00
gboolean validate = FALSE , have_debug_report = FALSE ;
2016-11-29 14:35:04 +00:00
VkResult res ;
2016-11-21 13:18:43 +00:00
2016-11-28 15:34:01 +00:00
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 ;
}
2016-11-21 13:18:43 +00:00
uint32_t n_extensions ;
GDK_VK_CHECK ( vkEnumerateInstanceExtensionProperties , NULL , & n_extensions , NULL ) ;
2016-12-28 14:34:04 +00:00
VkExtensionProperties * extensions = g_newa ( VkExtensionProperties , n_extensions ) ;
2016-11-21 13:18:43 +00:00
GDK_VK_CHECK ( vkEnumerateInstanceExtensionProperties , NULL , & n_extensions , extensions ) ;
2016-11-29 15:29:19 +00:00
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 ) ;
2016-11-21 13:18:43 +00:00
for ( i = 0 ; i < n_extensions ; i + + )
{
2018-01-12 00:48:27 +00:00
if ( GDK_DISPLAY_DEBUG_CHECK ( display , VULKAN ) )
g_print ( " Extension available: %s v%u.%u.%u \n " ,
2016-11-21 13:18:43 +00:00
extensions [ i ] . extensionName ,
VK_VERSION_MAJOR ( extensions [ i ] . specVersion ) ,
VK_VERSION_MINOR ( extensions [ i ] . specVersion ) ,
2018-01-12 00:48:27 +00:00
VK_VERSION_PATCH ( extensions [ i ] . specVersion ) ) ;
2016-11-29 15:29:19 +00:00
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 ;
}
2016-11-21 13:18:43 +00:00
}
uint32_t n_layers ;
GDK_VK_CHECK ( vkEnumerateInstanceLayerProperties , & n_layers , NULL ) ;
2016-12-28 14:34:04 +00:00
VkLayerProperties * layers = g_newa ( VkLayerProperties , n_layers ) ;
2016-11-21 13:18:43 +00:00
GDK_VK_CHECK ( vkEnumerateInstanceLayerProperties , & n_layers , layers ) ;
2016-11-29 14:35:04 +00:00
used_layers = g_ptr_array_new ( ) ;
2016-11-21 13:18:43 +00:00
for ( i = 0 ; i < n_layers ; i + + )
{
2018-01-12 00:48:27 +00:00
if ( GDK_DISPLAY_DEBUG_CHECK ( display , VULKAN ) )
g_print ( " Layer available: %s v%u.%u.%u (%s) \n " ,
2016-11-21 13:18:43 +00:00
layers [ i ] . layerName ,
VK_VERSION_MAJOR ( layers [ i ] . specVersion ) ,
VK_VERSION_MINOR ( layers [ i ] . specVersion ) ,
VK_VERSION_PATCH ( layers [ i ] . specVersion ) ,
2018-01-12 00:48:27 +00:00
layers [ i ] . description ) ;
2022-09-22 00:49:53 +00:00
if ( ( gdk_display_get_debug_flags ( display ) & GDK_DEBUG_VULKAN_VALIDATE ) & &
2016-11-29 14:35:04 +00:00
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 ;
}
2016-11-21 13:18:43 +00:00
}
2022-09-22 00:49:53 +00:00
if ( ( gdk_display_get_debug_flags ( display ) & GDK_DEBUG_VULKAN_VALIDATE ) & & ! validate )
2016-11-28 15:34:01 +00:00
{
2016-11-29 14:35:04 +00:00
g_warning ( " Vulkan validation layers were requested, but not found. Running without. " ) ;
}
res = GDK_VK_CHECK ( vkCreateInstance , & ( VkInstanceCreateInfo ) {
2016-11-29 15:29:19 +00:00
. 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 ,
2019-02-24 15:53:23 +00:00
. pEngineName = " GTK " ,
2016-11-29 15:29:19 +00:00
. engineVersion = VK_MAKE_VERSION ( GDK_MAJOR_VERSION , GDK_MINOR_VERSION , GDK_MICRO_VERSION ) ,
. apiVersion = VK_API_VERSION_1_0
2016-11-29 14:35:04 +00:00
} ,
2016-11-29 15:29:19 +00:00
. enabledLayerCount = used_layers - > len ,
. ppEnabledLayerNames = ( const char * const * ) used_layers - > pdata ,
. enabledExtensionCount = used_extensions - > len ,
. ppEnabledExtensionNames = ( const char * const * ) used_extensions - > pdata
2016-11-29 14:35:04 +00:00
} ,
NULL ,
& display - > vk_instance ) ;
g_ptr_array_free ( used_layers , TRUE ) ;
2016-11-29 15:29:19 +00:00
g_ptr_array_free ( used_extensions , TRUE ) ;
2016-11-29 14:35:04 +00:00
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 ) ) ;
2016-11-28 15:34:01 +00:00
return FALSE ;
}
2016-11-21 13:18:43 +00:00
2016-11-29 15:29:19 +00:00
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 ) ;
}
2016-11-29 14:35:04 +00:00
if ( ! gdk_display_create_vulkan_device ( display , error ) )
{
2016-11-29 15:29:19 +00:00
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 ;
}
2016-11-29 14:35:04 +00:00
vkDestroyInstance ( display - > vk_instance , NULL ) ;
display - > vk_instance = VK_NULL_HANDLE ;
return FALSE ;
}
return TRUE ;
2016-11-21 13:18:43 +00:00
}
gboolean
gdk_display_ref_vulkan ( GdkDisplay * display ,
2016-11-28 15:34:01 +00:00
GError * * error )
2016-11-21 13:18:43 +00:00
{
if ( display - > vulkan_refcount = = 0 )
{
2016-11-28 15:34:01 +00:00
if ( ! gdk_display_create_vulkan_instance ( display , error ) )
2016-11-21 13:18:43 +00:00
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 ) ;
2016-11-29 15:29:19 +00:00
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 ;
}
2016-11-21 13:18:43 +00:00
vkDestroyInstance ( display - > vk_instance , NULL ) ;
2016-11-29 15:29:19 +00:00
display - > vk_instance = VK_NULL_HANDLE ;
2016-11-21 13:18:43 +00:00
}
2016-12-09 19:11:37 +00:00
# else /* GDK_RENDERING_VULKAN */
2016-11-29 02:20:31 +00:00
static void
gdk_vulkan_context_class_init ( GdkVulkanContextClass * klass )
{
2016-11-29 23:54:48 +00:00
signals [ IMAGES_UPDATED ] =
g_signal_new ( g_intern_static_string ( " images-updated " ) ,
G_OBJECT_CLASS_TYPE ( klass ) ,
G_SIGNAL_RUN_LAST ,
0 ,
NULL , NULL ,
2019-05-29 20:05:19 +00:00
NULL ,
2016-11-29 23:54:48 +00:00
G_TYPE_NONE , 0 ) ;
2016-11-29 02:20:31 +00:00
}
static void
gdk_vulkan_context_init ( GdkVulkanContext * self )
{
}
static void
gdk_vulkan_context_initable_init ( GInitableIface * iface )
{
}
2016-12-09 19:11:37 +00:00
# endif /* GDK_RENDERING_VULKAN */