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
2023-10-19 11:34:40 +00:00
# include "gdkdebugprivate.h"
# include "gdkdmabufformatsbuilderprivate.h"
# include "gdkdmabuffourccprivate.h"
# include "gdkdmabuftextureprivate.h"
2016-11-21 13:18:43 +00:00
# include "gdkdisplayprivate.h"
2024-04-29 22:27:05 +00:00
# include "gdkprofilerprivate.h"
2022-09-24 03:33:42 +00:00
# include <glib/gi18n-lib.h>
2023-04-03 13:39:25 +00:00
# include <math.h>
2016-11-21 13:18:43 +00:00
2023-11-03 01:37:17 +00:00
# ifdef GDK_RENDERING_VULKAN
static const GdkDebugKey gsk_vulkan_feature_keys [ ] = {
{ " dmabuf " , GDK_VULKAN_FEATURE_DMABUF , " Never import Dmabufs " } ,
{ " ycbcr " , GDK_VULKAN_FEATURE_YCBCR , " Do not support Ycbcr textures " } ,
{ " descriptor-indexing " , GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING , " Force slow descriptor set layout codepath " } ,
2024-01-08 01:43:26 +00:00
{ " dynamic-indexing " , GDK_VULKAN_FEATURE_DYNAMIC_INDEXING , " Hardcode small number of buffer and texture arrays " } ,
2023-11-03 01:37:17 +00:00
{ " nonuniform-indexing " , GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING , " Split draw calls to ensure uniform texture accesses " } ,
2023-11-29 22:14:00 +00:00
{ " semaphore-export " , GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT , " Disable sync of exported dmabufs " } ,
{ " semaphore-import " , GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT , " Disable sync of imported dmabufs " } ,
2023-11-30 12:32:38 +00:00
{ " incremental-present " , GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT , " Do not send damage regions " } ,
2023-11-03 01:37:17 +00:00
} ;
# endif
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 .
2024-04-18 16:49:18 +00:00
*
* Deprecated : 4.14 : GTK does not expose any Vulkan internals . This
* struct is a leftover that was accidentally exposed .
2017-12-04 23:58:30 +00:00
*/
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 ;
2023-06-18 12:00:39 +00:00
struct {
VkSurfaceFormatKHR vk_format ;
GdkMemoryFormat gdk_format ;
} formats [ 4 ] ;
GdkMemoryDepth current_format ;
2023-06-20 14:35:55 +00:00
GdkMemoryFormat offscreen_formats [ 4 ] ;
2016-11-29 02:20:31 +00:00
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 ;
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
} ;
2024-05-25 17:20:43 +00:00
/**
* gdk_vulkan_error_quark :
*
* Registers an error quark for [ class @ Gdk . VulkanContext ] errors .
*
* Returns : the error quark
* */
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
2022-11-26 14:56:10 +00:00
* https : //github.com/KhronosGroup/Vulkan-Headers/blob/main/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
2022-11-26 14:56:10 +00:00
# if VK_HEADER_VERSION >= 213
case VK_ERROR_COMPRESSION_EXHAUSTED_EXT :
return " An image creation failed because internal resources required for compression are exhausted. (VK_ERROR_COMPRESSION_EXHAUSTED_EXT) " ;
# 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 :
2023-03-20 14:15:05 +00:00
# endif
2023-05-01 06:38:33 +00:00
# if VK_HEADER_VERSION >= 238
2023-03-20 14:15:05 +00:00
case VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR :
return " The requested VkImageUsageFlags are not supported. (VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR) " ;
case VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR :
return " The requested video picture layout is not supported. (VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR) " ;
case VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR :
return " A video profile operation specified via VkVideoProfileInfoKHR::videoCodecOperation is not supported. (VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR) " ;
case VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR :
return " Format parameters in a requested VkVideoProfileInfoKHR chain are not supported. (VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR) " ;
case VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR :
return " Codec-specific parameters in a requested (VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR) " ;
case VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR :
return " The specified video Std header version is not supported. (VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR) " ;
2023-07-20 01:10:28 +00:00
# endif
# if VK_HEADER_VERSION >= 246
case VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT :
2023-07-27 19:37:36 +00:00
return " The provided binary shader code is not compatible with this device. (VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT) " ;
2020-05-11 07:07:41 +00:00
# endif
2024-03-03 02:28:20 +00:00
# if VK_HEADER_VERSION >= 274
case VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR :
return " The specified Video Std parameters do not adhere to the syntactic or semantic requirements of the used video compression standard or implementation " ;
# endif
2024-03-03 01:17:33 +00:00
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. " ;
}
}
2023-04-13 15:15:53 +00:00
static const char *
surface_present_mode_to_string ( VkPresentModeKHR present_mode )
{
switch ( present_mode )
{
case VK_PRESENT_MODE_MAILBOX_KHR :
return " VK_PRESENT_MODE_MAILBOX_KHR " ;
case VK_PRESENT_MODE_IMMEDIATE_KHR :
return " VK_PRESENT_MODE_IMMEDIATE_KHR " ;
case VK_PRESENT_MODE_FIFO_KHR :
return " VK_PRESENT_MODE_FIFO_KHR " ;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR :
return " VK_PRESENT_MODE_FIFO_RELAXED_KHR " ;
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR :
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR :
case VK_PRESENT_MODE_MAX_ENUM_KHR :
default :
return " (invalid) " ;
}
return " (unknown) " ;
}
static const VkPresentModeKHR preferred_present_modes [ ] = {
VK_PRESENT_MODE_MAILBOX_KHR ,
VK_PRESENT_MODE_IMMEDIATE_KHR ,
} ;
static VkPresentModeKHR
find_best_surface_present_mode ( GdkVulkanContext * context )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
VkPresentModeKHR * available_present_modes ;
uint32_t n_present_modes ;
VkResult res ;
res = GDK_VK_CHECK ( vkGetPhysicalDeviceSurfacePresentModesKHR , gdk_vulkan_context_get_physical_device ( context ) ,
priv - > surface ,
& n_present_modes ,
NULL ) ;
if ( res ! = VK_SUCCESS )
goto fallback_present_mode ;
available_present_modes = g_alloca ( sizeof ( VkPresentModeKHR ) * n_present_modes ) ;
res = GDK_VK_CHECK ( vkGetPhysicalDeviceSurfacePresentModesKHR , gdk_vulkan_context_get_physical_device ( context ) ,
priv - > surface ,
& n_present_modes ,
available_present_modes ) ;
if ( res ! = VK_SUCCESS )
goto fallback_present_mode ;
for ( uint32_t i = 0 ; i < G_N_ELEMENTS ( preferred_present_modes ) ; i + + )
{
for ( uint32_t j = 0 ; j < n_present_modes ; j + + )
{
if ( preferred_present_modes [ i ] = = available_present_modes [ j ] )
return available_present_modes [ j ] ;
}
}
fallback_present_mode :
return VK_PRESENT_MODE_FIFO_KHR ;
}
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 ;
2023-04-13 15:15:53 +00:00
VkPresentModeKHR present_mode ;
2016-11-29 02:20:31 +00:00
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 ) ;
2023-07-12 20:26:27 +00:00
/*
* Wait for device to be idle because this function is also called in window resizes .
* And if we destroy old swapchain it also destroy the old VkImages , those images could
* be in use by a vulkan render .
*/
vkDeviceWaitIdle ( device ) ;
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 ;
}
2023-04-13 15:15:53 +00:00
present_mode = find_best_surface_present_mode ( context ) ;
GDK_DEBUG ( VULKAN , " Using surface present mode %s " ,
surface_present_mode_to_string ( present_mode ) ) ;
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 )
{
2023-04-03 13:39:25 +00:00
double scale = gdk_surface_get_scale ( surface ) ;
capabilities . currentExtent . width = MAX ( 1 , ( int ) ceil ( gdk_surface_get_width ( surface ) * scale ) ) ;
capabilities . currentExtent . height = MAX ( 1 , ( int ) ceil ( gdk_surface_get_height ( surface ) * scale ) ) ;
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 ) ,
2023-06-18 12:00:39 +00:00
. imageFormat = priv - > formats [ priv - > current_format ] . vk_format . format ,
. imageColorSpace = priv - > formats [ priv - > current_format ] . vk_format . colorSpace ,
2016-11-29 02:20:31 +00:00
. 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 ,
2023-04-13 15:15:53 +00:00
. presentMode = present_mode ,
2016-11-29 02:20:31 +00:00
. 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
2023-06-19 12:48:37 +00:00
physical_device_supports_extension ( VkPhysicalDevice device ,
const char * extension_name )
2019-10-04 21:53:10 +00:00
{
VkExtensionProperties * extensions ;
uint32_t n_device_extensions ;
2023-06-19 12:48:37 +00:00
GDK_VK_CHECK ( vkEnumerateDeviceExtensionProperties , device , NULL , & n_device_extensions , NULL ) ;
2019-10-04 21:53:10 +00:00
extensions = g_newa ( VkExtensionProperties , n_device_extensions ) ;
2023-06-19 12:48:37 +00:00
GDK_VK_CHECK ( vkEnumerateDeviceExtensionProperties , device , NULL , & n_device_extensions , extensions ) ;
2019-10-04 21:53:10 +00:00
for ( uint32_t i = 0 ; i < n_device_extensions ; i + + )
{
2023-06-19 12:48:37 +00:00
if ( g_str_equal ( extensions [ i ] . extensionName , extension_name ) )
2019-10-04 21:53:10 +00:00
return TRUE ;
}
return FALSE ;
}
2024-03-16 10:49:25 +00:00
static GdkVulkanFeatures
physical_device_check_features ( VkPhysicalDevice device )
2023-10-19 10:54:14 +00:00
{
2023-11-03 01:36:22 +00:00
VkPhysicalDeviceVulkan12Features v12_features = {
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES ,
} ;
2023-10-19 10:54:14 +00:00
VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcr_features = {
2023-11-03 01:36:22 +00:00
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES ,
. pNext = & v12_features
2023-10-19 10:54:14 +00:00
} ;
2024-03-16 10:49:25 +00:00
VkPhysicalDeviceFeatures2 v10_features = {
2023-10-19 10:54:14 +00:00
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 ,
. pNext = & ycbcr_features
} ;
2023-11-29 22:14:00 +00:00
VkExternalSemaphoreProperties semaphore_props = {
. sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES ,
} ;
2024-03-16 10:49:25 +00:00
GdkVulkanFeatures features ;
2023-10-19 10:54:14 +00:00
2024-03-16 10:49:25 +00:00
vkGetPhysicalDeviceFeatures2 ( device , & v10_features ) ;
2023-11-29 22:14:00 +00:00
vkGetPhysicalDeviceExternalSemaphoreProperties ( device ,
& ( VkPhysicalDeviceExternalSemaphoreInfo ) {
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO ,
. handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT ,
} ,
& semaphore_props ) ;
2023-10-19 10:54:14 +00:00
2024-03-16 10:49:25 +00:00
features = 0 ;
2023-10-19 10:54:14 +00:00
2024-03-16 10:49:25 +00:00
if ( v10_features . features . shaderUniformBufferArrayDynamicIndexing & &
v10_features . features . shaderSampledImageArrayDynamicIndexing )
features | = GDK_VULKAN_FEATURE_DYNAMIC_INDEXING ;
2023-11-03 01:36:22 +00:00
if ( v12_features . descriptorIndexing & &
v12_features . descriptorBindingPartiallyBound & &
v12_features . descriptorBindingVariableDescriptorCount & &
v12_features . descriptorBindingSampledImageUpdateAfterBind & &
v12_features . descriptorBindingStorageBufferUpdateAfterBind )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ;
2023-11-03 01:36:22 +00:00
else if ( physical_device_supports_extension ( device , VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME ) )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ;
2023-11-03 01:36:22 +00:00
if ( v12_features . shaderSampledImageArrayNonUniformIndexing & &
v12_features . shaderStorageBufferArrayNonUniformIndexing )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING ;
2023-10-19 10:54:14 +00:00
2024-02-09 00:59:01 +00:00
if ( ycbcr_features . samplerYcbcrConversion | |
physical_device_supports_extension ( device , VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME ) )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_YCBCR ;
2023-10-19 10:54:14 +00:00
if ( physical_device_supports_extension ( device , VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME ) & &
2024-05-08 13:45:35 +00:00
physical_device_supports_extension ( device , VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME ) & &
physical_device_supports_extension ( device , VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME ) )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_DMABUF ;
2023-10-19 10:54:14 +00:00
2023-11-29 22:14:00 +00:00
if ( physical_device_supports_extension ( device , VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME ) )
{
if ( semaphore_props . externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT ;
2023-11-29 22:14:00 +00:00
if ( semaphore_props . externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT ;
2023-11-29 22:14:00 +00:00
}
2023-11-30 12:32:38 +00:00
if ( physical_device_supports_extension ( device , VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) )
2024-03-16 10:49:25 +00:00
features | = GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT ;
2023-11-30 12:32:38 +00:00
2024-03-16 10:49:25 +00:00
return features ;
2023-10-19 10:54:14 +00:00
}
2016-12-01 03:07:20 +00:00
static void
gdk_vulkan_context_begin_frame ( GdkDrawContext * draw_context ,
2023-06-16 06:00:19 +00:00
GdkMemoryDepth 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 ) ;
2024-04-29 17:53:25 +00:00
VkResult acquire_result ;
2016-12-26 21:24:22 +00:00
guint i ;
2016-12-01 03:07:20 +00:00
2023-06-18 12:00:39 +00:00
if ( depth ! = priv - > current_format )
{
if ( priv - > formats [ depth ] . gdk_format ! = priv - > formats [ priv - > current_format ] . gdk_format )
{
GError * error = NULL ;
if ( ! gdk_vulkan_context_check_swapchain ( context , & error ) )
{
g_warning ( " %s " , error - > message ) ;
g_error_free ( error ) ;
return ;
}
}
priv - > current_format = depth ;
}
2016-12-26 21:24:22 +00:00
for ( i = 0 ; i < priv - > n_images ; i + + )
{
cairo_region_union ( priv - > regions [ i ] , region ) ;
}
2024-04-29 17:53:25 +00:00
acquire_next_image :
acquire_result = GDK_VK_CHECK ( vkAcquireNextImageKHR , gdk_vulkan_context_get_device ( context ) ,
priv - > swapchain ,
UINT64_MAX ,
priv - > draw_semaphore ,
VK_NULL_HANDLE ,
& priv - > draw_index ) ;
if ( ( acquire_result = = VK_ERROR_OUT_OF_DATE_KHR ) | |
( acquire_result = = VK_SUBOPTIMAL_KHR ) )
{
GError * error = NULL ;
GDK_DEBUG ( VULKAN , " Recreating the swapchain " ) ;
if ( ! gdk_vulkan_context_check_swapchain ( context , & error ) )
{
g_warning ( " %s " , error - > message ) ;
g_error_free ( error ) ;
return ;
}
goto acquire_next_image ;
}
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 ) ;
2023-11-30 12:32:38 +00:00
GdkDisplay * display = gdk_draw_context_get_display ( draw_context ) ;
2023-03-21 19:16:17 +00:00
VkRectLayerKHR * rectangles ;
int n_regions ;
2019-10-04 21:53:10 +00:00
2023-11-30 12:32:38 +00:00
if ( display - > vulkan_features & GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT )
2023-03-21 19:16:17 +00:00
{
2023-11-30 12:32:38 +00:00
double scale ;
2023-03-21 19:16:17 +00:00
2023-11-30 12:32:38 +00:00
scale = gdk_surface_get_scale ( surface ) ;
n_regions = cairo_region_num_rectangles ( painted ) ;
rectangles = g_alloca ( sizeof ( VkRectLayerKHR ) * n_regions ) ;
2023-03-21 19:16:17 +00:00
2023-11-30 12:32:38 +00:00
for ( int i = 0 ; i < n_regions ; i + + )
{
cairo_rectangle_int_t r ;
2019-10-04 21:53:10 +00:00
2023-11-30 12:32:38 +00:00
cairo_region_get_rectangle ( painted , i , & r ) ;
2019-10-04 21:53:10 +00:00
2023-11-30 12:32:38 +00:00
rectangles [ i ] = ( VkRectLayerKHR ) {
. layer = 0 ,
. offset . x = ( int ) floor ( r . x * scale ) ,
. offset . y = ( int ) floor ( r . y * scale ) ,
. extent . width = ( int ) ceil ( r . width * scale ) ,
. extent . height = ( int ) ceil ( r . height * scale ) ,
} ;
}
}
else
{
rectangles = NULL ;
n_regions = 0 ;
}
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 ,
2022-11-26 14:56:10 +00:00
. pSwapchains = ( VkSwapchainKHR [ ] ) {
2016-12-01 03:07:20 +00:00
priv - > swapchain
} ,
. pImageIndices = ( uint32_t [ ] ) {
priv - > draw_index
} ,
2023-11-30 12:32:38 +00:00
. pNext = rectangles = = NULL ? NULL : & ( VkPresentRegionsKHR ) {
. sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR ,
. swapchainCount = 1 ,
. pRegions = & ( VkPresentRegionKHR ) {
. rectangleCount = n_regions ,
. pRectangles = rectangles ,
} ,
}
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 ) ) ;
2023-06-14 08:18:47 +00:00
GdkSurface * surface = gdk_draw_context_get_surface ( 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
2023-11-09 04:42:58 +00:00
priv - > vulkan_ref = gdk_display_init_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 ;
2023-06-20 14:35:55 +00:00
priv - > offscreen_formats [ GDK_MEMORY_U8 ] = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED ;
priv - > offscreen_formats [ GDK_MEMORY_U16 ] = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED ;
priv - > offscreen_formats [ GDK_MEMORY_FLOAT16 ] = GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED ;
priv - > offscreen_formats [ GDK_MEMORY_FLOAT32 ] = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED ;
2023-06-14 08:18:47 +00:00
if ( surface = = NULL )
{
2023-06-18 12:00:39 +00:00
for ( i = 0 ; i < G_N_ELEMENTS ( priv - > formats ) ; i + + )
{
priv - > formats [ i ] . vk_format . format = VK_FORMAT_B8G8R8A8_UNORM ;
priv - > formats [ i ] . vk_format . colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ;
priv - > formats [ i ] . gdk_format = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED ;
}
2023-06-14 08:18:47 +00:00
return TRUE ;
}
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 + + )
{
2023-06-18 12:00:39 +00:00
if ( formats [ i ] . colorSpace ! = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR )
continue ;
switch ( ( int ) formats [ i ] . format )
{
case VK_FORMAT_B8G8R8A8_UNORM :
if ( priv - > formats [ GDK_MEMORY_U8 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
{
priv - > formats [ GDK_MEMORY_U8 ] . vk_format = formats [ i ] ;
priv - > formats [ GDK_MEMORY_U8 ] . gdk_format = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED ;
2023-06-20 14:35:55 +00:00
priv - > offscreen_formats [ GDK_MEMORY_U8 ] = GDK_MEMORY_B8G8R8A8_PREMULTIPLIED ;
2023-06-18 12:00:39 +00:00
} ;
break ;
case VK_FORMAT_R8G8B8A8_UNORM :
if ( priv - > formats [ GDK_MEMORY_U8 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
{
priv - > formats [ GDK_MEMORY_U8 ] . vk_format = formats [ i ] ;
priv - > formats [ GDK_MEMORY_U8 ] . gdk_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED ;
}
break ;
case VK_FORMAT_R16G16B16A16_UNORM :
priv - > formats [ GDK_MEMORY_U16 ] . vk_format = formats [ i ] ;
priv - > formats [ GDK_MEMORY_U16 ] . gdk_format = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED ;
break ;
case VK_FORMAT_R16G16B16A16_SFLOAT :
priv - > formats [ GDK_MEMORY_FLOAT16 ] . vk_format = formats [ i ] ;
priv - > formats [ GDK_MEMORY_FLOAT16 ] . gdk_format = GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED ;
break ;
case VK_FORMAT_R32G32B32A32_SFLOAT :
priv - > formats [ GDK_MEMORY_FLOAT32 ] . vk_format = formats [ i ] ;
priv - > formats [ GDK_MEMORY_FLOAT32 ] . gdk_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED ;
break ;
default :
break ;
}
2016-11-29 02:20:31 +00:00
}
2023-06-18 12:00:39 +00:00
if ( priv - > formats [ GDK_MEMORY_U8 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
2016-11-29 02:20:31 +00:00
{
g_set_error_literal ( error , GDK_VULKAN_ERROR , GDK_VULKAN_ERROR_NOT_AVAILABLE ,
" No supported image format found. " ) ;
2022-11-26 14:56:10 +00:00
goto out_surface ;
2016-11-29 02:20:31 +00:00
}
2023-06-18 12:00:39 +00:00
/* Ensure all the formats exist:
* - If a format was found , keep that one .
* - FLOAT32 chooses the best format we have .
* - FLOAT16 and U16 pick the format FLOAT32 uses
*/
if ( priv - > formats [ GDK_MEMORY_FLOAT32 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
{
if ( priv - > formats [ GDK_MEMORY_FLOAT16 ] . vk_format . format ! = VK_FORMAT_UNDEFINED )
priv - > formats [ GDK_MEMORY_FLOAT32 ] = priv - > formats [ GDK_MEMORY_FLOAT16 ] ;
else if ( priv - > formats [ GDK_MEMORY_U16 ] . vk_format . format ! = VK_FORMAT_UNDEFINED )
priv - > formats [ GDK_MEMORY_FLOAT32 ] = priv - > formats [ GDK_MEMORY_U16 ] ;
else
priv - > formats [ GDK_MEMORY_FLOAT32 ] = priv - > formats [ GDK_MEMORY_U8 ] ;
}
if ( priv - > formats [ GDK_MEMORY_FLOAT16 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
priv - > formats [ GDK_MEMORY_FLOAT16 ] = priv - > formats [ GDK_MEMORY_FLOAT32 ] ;
if ( priv - > formats [ GDK_MEMORY_U16 ] . vk_format . format = = VK_FORMAT_UNDEFINED )
priv - > formats [ GDK_MEMORY_U16 ] = priv - > formats [ GDK_MEMORY_FLOAT32 ] ;
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
}
2023-06-20 14:35:55 +00:00
GdkMemoryFormat
gdk_vulkan_context_get_offscreen_format ( GdkVulkanContext * context ,
GdkMemoryDepth depth )
{
GdkVulkanContextPrivate * priv = gdk_vulkan_context_get_instance_private ( context ) ;
return priv - > offscreen_formats [ depth ] ;
}
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 ;
}
2023-06-26 03:10:16 +00:00
static char *
gdk_vulkan_get_pipeline_cache_dirname ( void )
{
return g_build_filename ( g_get_user_cache_dir ( ) , " gtk-4.0 " , " vulkan-pipeline-cache " , NULL ) ;
}
static GFile *
gdk_vulkan_get_pipeline_cache_file ( GdkDisplay * display )
{
VkPhysicalDeviceProperties props ;
char * dirname , * basename , * path ;
GFile * result ;
vkGetPhysicalDeviceProperties ( display - > vk_physical_device , & props ) ;
dirname = gdk_vulkan_get_pipeline_cache_dirname ( ) ;
basename = g_strdup_printf ( " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x "
" -%02x%02x%02x%02x%02x%02x.%u " ,
props . pipelineCacheUUID [ 0 ] , props . pipelineCacheUUID [ 1 ] ,
props . pipelineCacheUUID [ 2 ] , props . pipelineCacheUUID [ 3 ] ,
props . pipelineCacheUUID [ 4 ] , props . pipelineCacheUUID [ 5 ] ,
props . pipelineCacheUUID [ 6 ] , props . pipelineCacheUUID [ 7 ] ,
props . pipelineCacheUUID [ 8 ] , props . pipelineCacheUUID [ 9 ] ,
props . pipelineCacheUUID [ 10 ] , props . pipelineCacheUUID [ 11 ] ,
props . pipelineCacheUUID [ 12 ] , props . pipelineCacheUUID [ 13 ] ,
props . pipelineCacheUUID [ 14 ] , props . pipelineCacheUUID [ 15 ] ,
props . driverVersion ) ;
path = g_build_filename ( dirname , basename , NULL ) ;
result = g_file_new_for_path ( path ) ;
g_free ( path ) ;
g_free ( basename ) ;
g_free ( dirname ) ;
return result ;
}
static VkPipelineCache
gdk_display_load_pipeline_cache ( GdkDisplay * display )
{
GError * error = NULL ;
VkPipelineCache result ;
GFile * cache_file ;
char * etag , * data ;
gsize size ;
cache_file = gdk_vulkan_get_pipeline_cache_file ( display ) ;
if ( ! g_file_load_contents ( cache_file , NULL , & data , & size , & etag , & error ) )
{
GDK_DEBUG ( VULKAN , " failed to load Vulkan pipeline cache file '%s': %s \n " ,
g_file_peek_path ( cache_file ) , error - > message ) ;
g_object_unref ( cache_file ) ;
g_clear_error ( & error ) ;
return VK_NULL_HANDLE ;
}
if ( GDK_VK_CHECK ( vkCreatePipelineCache , display - > vk_device ,
& ( VkPipelineCacheCreateInfo ) {
. sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO ,
. initialDataSize = size ,
. pInitialData = data ,
} ,
NULL ,
& result ) ! = VK_SUCCESS )
result = VK_NULL_HANDLE ;
2023-07-27 08:13:26 +00:00
g_object_unref ( cache_file ) ;
2023-06-26 03:10:16 +00:00
g_free ( data ) ;
g_free ( display - > vk_pipeline_cache_etag ) ;
display - > vk_pipeline_cache_etag = etag ;
display - > vk_pipeline_cache_size = size ;
return result ;
}
static gboolean
gdk_vulkan_save_pipeline_cache ( GdkDisplay * display )
{
2024-04-30 02:43:31 +00:00
G_GNUC_UNUSED gint64 begin_time = GDK_PROFILER_CURRENT_TIME ;
2023-06-26 03:10:16 +00:00
GError * error = NULL ;
VkDevice device ;
VkPipelineCache cache ;
GFile * file ;
char * path ;
size_t size ;
char * data , * etag ;
device = display - > vk_device ;
cache = display - > vk_pipeline_cache ;
GDK_VK_CHECK ( vkGetPipelineCacheData , device , cache , & size , NULL ) ;
if ( size = = 0 )
return TRUE ;
2024-04-30 12:26:21 +00:00
2023-06-26 03:10:16 +00:00
if ( size = = display - > vk_pipeline_cache_size )
{
GDK_DEBUG ( VULKAN , " pipeline cache size (%zu bytes) unchanged, skipping save " , size ) ;
return TRUE ;
}
data = g_malloc ( size ) ;
if ( GDK_VK_CHECK ( vkGetPipelineCacheData , device , cache , & size , data ) ! = VK_SUCCESS )
{
g_free ( data ) ;
return FALSE ;
}
path = gdk_vulkan_get_pipeline_cache_dirname ( ) ;
if ( g_mkdir_with_parents ( path , 0755 ) ! = 0 )
{
g_warning_once ( " Failed to create pipeline cache directory " ) ;
g_free ( path ) ;
2023-07-17 19:02:33 +00:00
g_free ( data ) ;
2023-06-26 03:10:16 +00:00
return FALSE ;
}
g_free ( path ) ;
file = gdk_vulkan_get_pipeline_cache_file ( display ) ;
2024-04-30 12:26:21 +00:00
GDK_DEBUG ( VULKAN , " Saving pipeline cache of size %lu to %s " , size , g_file_peek_path ( file ) ) ;
2023-06-26 03:10:16 +00:00
if ( ! g_file_replace_contents ( file ,
data ,
size ,
display - > vk_pipeline_cache_etag ,
FALSE ,
0 ,
& etag ,
NULL ,
& error ) )
{
if ( g_error_matches ( error , G_IO_ERROR , G_IO_ERROR_WRONG_ETAG ) )
{
VkPipelineCache new_cache ;
2024-04-30 02:43:31 +00:00
2023-06-26 03:10:16 +00:00
GDK_DEBUG ( VULKAN , " Pipeline cache file modified, merging into current " ) ;
new_cache = gdk_display_load_pipeline_cache ( display ) ;
if ( new_cache )
{
GDK_VK_CHECK ( vkMergePipelineCaches , device , cache , 1 , & new_cache ) ;
vkDestroyPipelineCache ( device , new_cache , NULL ) ;
}
else
{
g_clear_pointer ( & display - > vk_pipeline_cache_etag , g_free ) ;
}
g_clear_error ( & error ) ;
g_object_unref ( file ) ;
2023-07-17 19:02:33 +00:00
g_free ( data ) ;
2023-06-26 03:10:16 +00:00
/* try again */
return gdk_vulkan_save_pipeline_cache ( display ) ;
}
g_warning ( " Failed to save pipeline cache: %s " , error - > message ) ;
g_clear_error ( & error ) ;
g_object_unref ( file ) ;
2023-07-17 19:02:33 +00:00
g_free ( data ) ;
2023-06-26 03:10:16 +00:00
return FALSE ;
}
2024-04-30 02:43:31 +00:00
gdk_profiler_end_markf ( begin_time ,
2024-04-30 12:26:21 +00:00
" Save Vulkan pipeline cache " , " %s size %lu " ,
2024-04-30 02:43:31 +00:00
g_file_peek_path ( file ) , size ) ;
2023-06-26 03:10:16 +00:00
g_object_unref ( file ) ;
2023-07-17 19:02:33 +00:00
g_free ( data ) ;
2023-06-26 03:10:16 +00:00
g_free ( display - > vk_pipeline_cache_etag ) ;
display - > vk_pipeline_cache_etag = etag ;
2024-04-30 12:27:20 +00:00
display - > vk_pipeline_cache_size = size ;
2023-06-26 03:10:16 +00:00
return TRUE ;
}
static gboolean
gdk_vulkan_save_pipeline_cache_cb ( gpointer data )
{
GdkDisplay * display = data ;
gdk_vulkan_save_pipeline_cache ( display ) ;
display - > vk_save_pipeline_cache_source = 0 ;
return G_SOURCE_REMOVE ;
}
void
2023-11-20 03:54:26 +00:00
gdk_display_vulkan_pipeline_cache_updated ( GdkDisplay * display )
2023-06-26 03:10:16 +00:00
{
g_clear_handle_id ( & display - > vk_save_pipeline_cache_source , g_source_remove ) ;
display - > vk_save_pipeline_cache_source = g_timeout_add_seconds_full ( G_PRIORITY_DEFAULT_IDLE - 10 ,
10 , /* random choice that is not now */
gdk_vulkan_save_pipeline_cache_cb ,
display ,
NULL ) ;
}
static void
gdk_display_create_pipeline_cache ( GdkDisplay * display )
{
display - > vk_pipeline_cache = gdk_display_load_pipeline_cache ( display ) ;
2023-07-17 19:02:33 +00:00
if ( display - > vk_pipeline_cache = = VK_NULL_HANDLE )
{
GDK_VK_CHECK ( vkCreatePipelineCache , display - > vk_device ,
& ( VkPipelineCacheCreateInfo ) {
. sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO ,
} ,
NULL ,
& display - > vk_pipeline_cache ) ;
2024-04-30 12:26:21 +00:00
GDK_DEBUG ( VULKAN , " Creating empty pipeline cache " ) ;
}
else
{
GDK_DEBUG ( VULKAN , " Loading pipeline cache (%lu bytes) " , display - > vk_pipeline_cache_size ) ;
2023-07-17 19:02:33 +00:00
}
2023-06-26 03:10:16 +00:00
}
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 ) ;
2023-06-18 12:00:39 +00:00
return priv - > formats [ priv - > current_format ] . vk_format . format ;
2016-11-29 02:20:31 +00:00
}
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
{
2024-04-29 22:27:05 +00:00
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME ;
2018-01-10 01:33:04 +00:00
uint32_t i , j , k ;
const char * override ;
gboolean list_devices ;
int first , last ;
2023-11-03 01:37:17 +00:00
GdkVulkanFeatures skip_features ;
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 ;
2024-03-16 13:03:36 +00:00
skip_features = gdk_parse_debug_var ( " GDK_VULKAN_DISABLE " ,
2023-11-03 01:37:17 +00:00
gsk_vulkan_feature_keys ,
G_N_ELEMENTS ( gsk_vulkan_feature_keys ) ) ;
2018-01-10 01:33:04 +00:00
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 + + )
{
2023-11-03 01:37:17 +00:00
GdkVulkanFeatures features , device_features ;
2016-11-21 13:18:43 +00:00
uint32_t n_queue_props ;
2023-06-19 12:54:00 +00:00
2024-03-16 10:49:25 +00:00
device_features = physical_device_check_features ( devices [ i ] ) ;
2023-06-19 12:54:00 +00:00
2023-11-03 01:37:17 +00:00
features = device_features & ~ skip_features ;
2023-10-19 10:54:14 +00:00
2016-11-21 13:18:43 +00:00
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 ;
device_extensions = g_ptr_array_new ( ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_SWAPCHAIN_EXTENSION_NAME ) ;
2023-11-03 01:36:22 +00:00
if ( features & GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING )
g_ptr_array_add ( device_extensions , ( gpointer ) VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME ) ;
2024-02-09 00:59:01 +00:00
if ( features & GDK_VULKAN_FEATURE_YCBCR )
{
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_MAINTENANCE_1_EXTENSION_NAME ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_BIND_MEMORY_2_EXTENSION_NAME ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME ) ;
}
2023-10-19 10:54:14 +00:00
if ( features & GDK_VULKAN_FEATURE_DMABUF )
{
2024-02-09 00:59:01 +00:00
g_assert ( features & GDK_VULKAN_FEATURE_YCBCR ) ;
2023-10-19 10:54:14 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME ) ;
2024-02-09 00:59:01 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME ) ;
2024-05-08 13:45:35 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME ) ;
2024-02-09 00:59:01 +00:00
2023-10-19 10:54:14 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME ) ;
2024-02-09 00:59:01 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME ) ;
2023-10-19 10:54:14 +00:00
}
2023-11-29 22:14:00 +00:00
if ( features & ( GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT | GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT ) )
2024-02-09 00:59:01 +00:00
{
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME ) ;
}
2023-11-30 12:32:38 +00:00
if ( features & GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT )
2019-10-04 21:53:10 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) ;
2023-11-03 01:36:22 +00:00
# define ENABLE_IF(flag) ((features & (flag)) ? VK_TRUE : VK_FALSE)
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 ) {
2023-05-20 00:56:02 +00:00
. sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO ,
. queueCreateInfoCount = 1 ,
. pQueueCreateInfos = & ( VkDeviceQueueCreateInfo ) {
2016-11-21 13:18:43 +00:00
. sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO ,
. queueFamilyIndex = j ,
. queueCount = 1 ,
. pQueuePriorities = ( float [ ] ) { 1.0f } ,
} ,
2023-05-20 00:56:02 +00:00
. enabledExtensionCount = device_extensions - > len ,
. ppEnabledExtensionNames = ( const char * const * ) device_extensions - > pdata ,
2023-10-19 10:54:14 +00:00
. pNext = & ( VkPhysicalDeviceVulkan11Features ) {
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES ,
2023-11-03 01:36:22 +00:00
. samplerYcbcrConversion = ENABLE_IF ( GDK_VULKAN_FEATURE_YCBCR ) ,
2023-10-19 10:54:14 +00:00
. pNext = & ( VkPhysicalDeviceVulkan12Features ) {
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES ,
2023-11-03 01:36:22 +00:00
. shaderSampledImageArrayNonUniformIndexing = ENABLE_IF ( GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING ) ,
. shaderStorageBufferArrayNonUniformIndexing = ENABLE_IF ( GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING ) ,
. descriptorIndexing = ENABLE_IF ( GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ) ,
. descriptorBindingPartiallyBound = ENABLE_IF ( GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ) ,
. descriptorBindingVariableDescriptorCount = ENABLE_IF ( GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ) ,
. descriptorBindingSampledImageUpdateAfterBind = ENABLE_IF ( GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ) ,
. descriptorBindingStorageBufferUpdateAfterBind = ENABLE_IF ( GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING ) ,
2023-10-19 10:54:14 +00:00
}
2023-05-20 00:56:02 +00:00
}
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 ;
}
2023-11-03 01:36:22 +00:00
# undef ENABLE_IF
2019-10-04 21:53:10 +00:00
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 ;
2023-10-19 10:54:14 +00:00
display - > vulkan_features = features ;
2023-11-03 01:37:17 +00:00
2024-03-16 13:03:36 +00:00
GDK_DISPLAY_DEBUG ( display , VULKAN , " Enabled features (use GDK_VULKAN_DISABLE env var to disable): " ) ;
2023-11-03 01:37:17 +00:00
for ( i = 0 ; i < G_N_ELEMENTS ( gsk_vulkan_feature_keys ) ; i + + )
{
GDK_DISPLAY_DEBUG ( display , VULKAN , " %s: %s " ,
gsk_vulkan_feature_keys [ i ] . key ,
( features & gsk_vulkan_feature_keys [ i ] . value ) ? " YES " :
( ( skip_features & gsk_vulkan_feature_keys [ i ] . value ) ? " disabled via env var " :
( ( ( device_features & gsk_vulkan_feature_keys [ i ] . value ) = = 0 ) ? " not supported " :
" Hum, what? This should not happen. " ) ) ) ;
}
2024-04-29 22:27:05 +00:00
gdk_profiler_end_mark ( start_time , " Create Vulkan device " , NULL ) ;
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
{
2024-04-29 22:27:05 +00:00
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME ;
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
2023-10-20 07:30:01 +00:00
if ( gdk_display_get_debug_flags ( display ) & GDK_DEBUG_VULKAN_DISABLE )
{
g_set_error_literal ( error , GDK_VULKAN_ERROR , GDK_VULKAN_ERROR_NOT_AVAILABLE ,
_ ( " Vulkan support disabled via GDK_DEBUG " ) ) ;
return FALSE ;
}
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 ( ) ;
2022-11-26 14:56:10 +00:00
g_ptr_array_add ( used_extensions , ( gpointer ) VK_KHR_SURFACE_EXTENSION_NAME ) ;
2023-05-20 00:56:02 +00:00
g_ptr_array_add ( used_extensions , ( gpointer ) VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) ;
2022-11-26 14:56:10 +00:00
g_ptr_array_add ( used_extensions , ( gpointer ) GDK_DISPLAY_GET_CLASS ( display ) - > vk_extension_name ) ;
2016-11-29 15:29:19 +00:00
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 ;
}
2024-02-09 00:59:01 +00:00
if ( g_str_equal ( extensions [ i ] . extensionName , VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) )
g_ptr_array_add ( used_extensions , ( gpointer ) VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) ;
if ( g_str_equal ( extensions [ i ] . extensionName , VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME ) )
g_ptr_array_add ( used_extensions , ( gpointer ) VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME ) ;
if ( g_str_equal ( extensions [ i ] . extensionName , VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME ) )
g_ptr_array_add ( used_extensions , ( gpointer ) VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME ) ;
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 ) ;
2023-03-30 20:09:24 +00:00
if ( gdk_display_get_debug_flags ( display ) & GDK_DEBUG_VULKAN_VALIDATE )
2016-11-29 14:35:04 +00:00
{
2023-03-30 20:09:24 +00:00
const char * validation_layer_names [ ] = {
" VK_LAYER_LUNARG_standard_validation " ,
" VK_LAYER_KHRONOS_validation " ,
NULL ,
} ;
if ( g_strv_contains ( validation_layer_names , layers [ i ] . layerName ) )
{
g_ptr_array_add ( used_layers , layers [ i ] . layerName ) ;
validate = TRUE ;
}
2016-11-29 14:35:04 +00:00
}
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 ) ,
2023-12-21 14:01:38 +00:00
. apiVersion = VK_API_VERSION_1_3
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 ,
2023-05-20 00:56:02 +00:00
. 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 ;
2022-11-26 14:56:10 +00:00
2016-11-29 15:29:19 +00:00
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 ;
}
2023-06-26 03:10:16 +00:00
gdk_display_create_pipeline_cache ( display ) ;
2023-06-28 03:43:53 +00:00
display - > vk_shader_modules = g_hash_table_new ( g_str_hash , g_str_equal ) ;
2024-04-29 22:27:05 +00:00
gdk_profiler_end_mark ( start_time , " Create Vulkan instance " , NULL ) ;
2016-11-29 14:35:04 +00:00
return TRUE ;
2016-11-21 13:18:43 +00:00
}
2023-11-09 04:42:58 +00:00
/*
* gdk_display_init_vulkan :
* @ display : a display
* @ error : A potential error message
*
* Initializes Vulkan and returns an error on failure .
*
* If Vulkan is already initialized , this function returns
* % TRUE and increases the refcount of the existing instance .
*
* You need to gdk_display_unref_vulkan ( ) to close it again .
*
* Returns : % TRUE if Vulkan is initialized .
* */
2016-11-21 13:18:43 +00:00
gboolean
2023-11-09 04:42:58 +00:00
gdk_display_init_vulkan ( GdkDisplay * display ,
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 + + ;
2022-11-26 14:56:10 +00:00
2016-11-21 13:18:43 +00:00
return TRUE ;
}
2023-11-09 04:42:58 +00:00
/*
* gdk_display_ref_vulkan :
* @ display : a GdkDisplay
*
* Increases the refcount of an existing Vulkan instance .
*
* This function must not be called if Vulkan may not be initialized
* yet , call gdk_display_init_vulkan ( ) in that case .
* */
void
gdk_display_ref_vulkan ( GdkDisplay * display )
{
g_assert ( display - > vulkan_refcount > 0 ) ;
display - > vulkan_refcount + + ;
}
2016-11-21 13:18:43 +00:00
void
gdk_display_unref_vulkan ( GdkDisplay * display )
{
2023-06-28 03:43:53 +00:00
GHashTableIter iter ;
gpointer key , value ;
2016-11-21 13:18:43 +00:00
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 ;
2022-11-26 14:56:10 +00:00
2023-11-09 04:42:58 +00:00
GDK_DEBUG ( VULKAN , " Closing Vulkan instance " ) ;
2023-10-19 10:54:14 +00:00
display - > vulkan_features = 0 ;
2023-10-19 11:34:40 +00:00
g_clear_pointer ( & display - > vk_dmabuf_formats , gdk_dmabuf_formats_unref ) ;
2023-06-28 03:43:53 +00:00
g_hash_table_iter_init ( & iter , display - > vk_shader_modules ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
{
g_free ( key ) ;
vkDestroyShaderModule ( display - > vk_device ,
2024-05-25 02:53:41 +00:00
* ( ( VkShaderModule * ) value ) ,
2023-06-28 03:43:53 +00:00
NULL ) ;
2024-05-25 02:53:41 +00:00
g_free ( value ) ;
2023-06-28 03:43:53 +00:00
}
2023-07-27 08:13:26 +00:00
g_hash_table_unref ( display - > vk_shader_modules ) ;
2023-06-28 03:43:53 +00:00
2023-06-26 03:10:16 +00:00
if ( display - > vk_save_pipeline_cache_source )
{
2023-07-27 08:13:26 +00:00
g_clear_handle_id ( & display - > vk_save_pipeline_cache_source , g_source_remove ) ;
2023-06-26 03:10:16 +00:00
gdk_vulkan_save_pipeline_cache_cb ( display ) ;
g_assert ( display - > vk_save_pipeline_cache_source = = 0 ) ;
}
vkDestroyPipelineCache ( display - > vk_device , display - > vk_pipeline_cache , NULL ) ;
2023-07-17 19:02:33 +00:00
display - > vk_pipeline_cache = VK_NULL_HANDLE ;
2023-06-26 03:10:16 +00:00
g_clear_pointer ( & display - > vk_pipeline_cache_etag , g_free ) ;
display - > vk_pipeline_cache_size = 0 ;
2016-11-21 13:18:43 +00:00
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
}
2024-01-07 13:56:01 +00:00
# ifdef HAVE_DMABUF
2023-12-19 23:28:54 +00:00
/* Hack. We don't include gsk/gsk.h here to avoid a build order problem
* with the generated header gskenumtypes . h , so we need to hack around
* a bit to access the gsk api we need .
*/
typedef struct _GskRenderer GskRenderer ;
2024-01-07 09:03:44 +00:00
extern GskRenderer * gsk_vulkan_renderer_new ( void ) ;
extern gboolean gsk_renderer_realize_for_display ( GskRenderer * renderer ,
GdkDisplay * display ,
GError * * error ) ;
2023-12-19 23:28:54 +00:00
2023-10-19 11:34:40 +00:00
GdkDmabufDownloader *
gdk_vulkan_get_dmabuf_downloader ( GdkDisplay * display ,
GdkDmabufFormatsBuilder * builder )
{
GdkDmabufFormatsBuilder * vulkan_builder ;
2023-12-19 23:28:54 +00:00
GskRenderer * renderer ;
2023-10-19 11:34:40 +00:00
VkDrmFormatModifierPropertiesEXT modifier_list [ 100 ] ;
VkDrmFormatModifierPropertiesListEXT modifier_props = {
. sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT ,
. pNext = NULL ,
. pDrmFormatModifierProperties = modifier_list ,
} ;
VkFormatProperties2 props = {
. sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 ,
. pNext = & modifier_props ,
} ;
VkFormat vk_format ;
guint32 fourcc ;
2023-12-19 23:28:54 +00:00
GError * error = NULL ;
2023-10-19 11:34:40 +00:00
gsize i , j ;
g_assert ( display - > vk_dmabuf_formats = = NULL ) ;
if ( ! gdk_display_init_vulkan ( display , NULL ) )
return NULL ;
if ( ( display - > vulkan_features & GDK_VULKAN_FEATURE_DMABUF ) = = 0 )
return NULL ;
vulkan_builder = gdk_dmabuf_formats_builder_new ( ) ;
for ( i = 0 ; gdk_dmabuf_vk_get_nth ( i , & fourcc , & vk_format ) ; i + + )
{
if ( vk_format = = VK_FORMAT_UNDEFINED )
continue ;
modifier_props . drmFormatModifierCount = sizeof ( modifier_list ) ;
vkGetPhysicalDeviceFormatProperties2 ( display - > vk_physical_device ,
vk_format ,
& props ) ;
g_warn_if_fail ( modifier_props . drmFormatModifierCount < sizeof ( modifier_list ) ) ;
for ( j = 0 ; j < modifier_props . drmFormatModifierCount ; j + + )
{
GDK_DISPLAY_DEBUG ( display , DMABUF ,
" Vulkan supports dmabuf format %.4s::%016llx with %u planes and features 0x%x " ,
( char * ) & fourcc ,
( long long unsigned ) modifier_list [ j ] . drmFormatModifier ,
modifier_list [ j ] . drmFormatModifierPlaneCount ,
modifier_list [ j ] . drmFormatModifierTilingFeatures ) ;
if ( modifier_list [ j ] . drmFormatModifier = = DRM_FORMAT_MOD_LINEAR )
continue ;
gdk_dmabuf_formats_builder_add_format ( vulkan_builder ,
fourcc ,
modifier_list [ j ] . drmFormatModifier ) ;
}
}
display - > vk_dmabuf_formats = gdk_dmabuf_formats_builder_free_to_formats ( vulkan_builder ) ;
gdk_dmabuf_formats_builder_add_formats ( builder , display - > vk_dmabuf_formats ) ;
2023-12-19 23:28:54 +00:00
renderer = gsk_vulkan_renderer_new ( ) ;
2024-01-07 09:03:44 +00:00
if ( ! gsk_renderer_realize_for_display ( renderer , display , & error ) )
2023-12-19 23:28:54 +00:00
{
g_warning ( " Failed to realize GL renderer: %s " , error - > message ) ;
g_error_free ( error ) ;
g_object_unref ( renderer ) ;
return NULL ;
}
return GDK_DMABUF_DOWNLOADER ( renderer ) ;
2023-10-19 11:34:40 +00:00
}
2024-01-07 13:56:01 +00:00
# endif
2023-06-28 03:43:53 +00:00
VkShaderModule
gdk_display_get_vk_shader_module ( GdkDisplay * self ,
const char * resource_name )
{
2024-05-25 02:53:41 +00:00
VkShaderModule * shader ;
2023-06-28 03:43:53 +00:00
GError * error = NULL ;
GBytes * bytes ;
shader = g_hash_table_lookup ( self - > vk_shader_modules , resource_name ) ;
if ( shader )
2024-05-25 02:53:41 +00:00
return * shader ;
2023-06-28 03:43:53 +00:00
bytes = g_resources_lookup_data ( resource_name , 0 , & error ) ;
if ( bytes = = NULL )
{
GDK_DEBUG ( VULKAN , " Error loading shader data: %s " , error - > message ) ;
g_clear_error ( & error ) ;
return VK_NULL_HANDLE ;
}
2024-05-25 02:53:41 +00:00
shader = g_new0 ( VkShaderModule , 1 ) ;
2023-06-28 03:43:53 +00:00
if ( GDK_VK_CHECK ( vkCreateShaderModule , self - > vk_device ,
& ( VkShaderModuleCreateInfo ) {
. sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO ,
. codeSize = g_bytes_get_size ( bytes ) ,
. pCode = ( uint32_t * ) g_bytes_get_data ( bytes , NULL ) ,
} ,
NULL ,
2024-05-25 02:53:41 +00:00
shader ) = = VK_SUCCESS )
2023-06-28 03:43:53 +00:00
{
g_hash_table_insert ( self - > vk_shader_modules , g_strdup ( resource_name ) , shader ) ;
}
else
{
2024-05-25 02:53:41 +00:00
g_free ( shader ) ;
return VK_NULL_HANDLE ;
2023-06-28 03:43:53 +00:00
}
g_bytes_unref ( bytes ) ;
2024-05-25 02:53:41 +00:00
return * shader ;
2023-06-28 03:43:53 +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 */