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>
2023-04-03 13:39:25 +00:00
# include <math.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 ;
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 ;
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
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 :
return " The provided binary shader code is not compatible with this device. "
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. " ;
}
}
2023-04-13 15:15:53 +00:00
# ifdef G_ENABLE_DEBUG
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) " ;
}
# endif
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 ;
}
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 ) ;
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 ) ;
}
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 ;
2023-03-21 19:16:17 +00:00
VkRectLayerKHR * rectangles ;
2023-04-03 13:39:25 +00:00
double scale ;
2023-03-21 19:16:17 +00:00
int n_regions ;
2019-10-04 21:53:10 +00:00
2023-04-03 13:39:25 +00:00
scale = gdk_surface_get_scale ( surface ) ;
2023-03-21 19:16:17 +00:00
n_regions = cairo_region_num_rectangles ( painted ) ;
rectangles = g_alloca ( sizeof ( VkRectLayerKHR ) * n_regions ) ;
for ( int i = 0 ; i < n_regions ; i + + )
{
cairo_rectangle_int_t r ;
cairo_region_get_rectangle ( painted , i , & r ) ;
rectangles [ i ] = ( VkRectLayerKHR ) {
. layer = 0 ,
2023-04-03 13:39:25 +00:00
. 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 ) ,
2023-03-21 19:16:17 +00:00
} ;
}
2019-10-04 21:53:10 +00:00
regions = ( VkPresentRegionsKHR ) {
. sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR ,
. swapchainCount = 1 ,
. pRegions = & ( VkPresentRegionKHR ) {
2023-03-21 19:16:17 +00:00
. rectangleCount = n_regions ,
. pRectangles = rectangles ,
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 ,
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
} ,
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 ) ) ;
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
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 ;
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 ] ;
2023-06-19 12:48:37 +00:00
priv - > has_present_region = physical_device_supports_extension ( display - > vk_physical_device ,
VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) ;
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 ;
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 )
{
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 ;
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 ) ;
GDK_DEBUG ( VULKAN , " Saving pipeline cache to %s " , g_file_peek_path ( file ) ) ;
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 ;
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 ;
}
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 ;
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
gdk_vulkan_context_pipeline_cache_updated ( GdkVulkanContext * self )
{
GdkDisplay * display = gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( self ) ) ;
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 ) ;
}
2023-06-26 03:10:16 +00:00
}
VkPipelineCache
gdk_vulkan_context_get_pipeline_cache ( GdkVulkanContext * self )
{
g_return_val_if_fail ( GDK_IS_VULKAN_CONTEXT ( self ) , NULL ) ;
return gdk_draw_context_get_display ( GDK_DRAW_CONTEXT ( self ) ) - > vk_pipeline_cache ;
}
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
{
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 ;
2023-06-19 12:54:00 +00:00
if ( ! physical_device_supports_extension ( devices [ i ] , VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME ) )
continue ;
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 ;
gboolean has_incremental_present ;
2023-06-19 12:48:37 +00:00
has_incremental_present = physical_device_supports_extension ( devices [ i ] ,
VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME ) ;
2019-10-04 21:53:10 +00:00
device_extensions = g_ptr_array_new ( ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_SWAPCHAIN_EXTENSION_NAME ) ;
2023-05-20 00:56:02 +00:00
g_ptr_array_add ( device_extensions , ( gpointer ) VK_KHR_MAINTENANCE_3_EXTENSION_NAME ) ;
g_ptr_array_add ( device_extensions , ( gpointer ) VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME ) ;
2019-10-04 21:53:10 +00:00
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 ) {
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 ,
. pNext = & ( VkPhysicalDeviceDescriptorIndexingFeatures ) {
. sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES ,
. descriptorBindingPartiallyBound = VK_TRUE ,
. descriptorBindingVariableDescriptorCount = VK_TRUE ,
. descriptorBindingSampledImageUpdateAfterBind = VK_TRUE ,
2023-06-20 13:29:45 +00:00
. descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE ,
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 ;
}
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 ( ) ;
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 ;
}
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 ) ,
. 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 ,
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 ) ;
2016-11-29 14:35:04 +00:00
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 + + ;
2022-11-26 14:56:10 +00:00
2016-11-21 13:18:43 +00:00
return TRUE ;
}
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-06-28 03:43:53 +00:00
display - > vk_shader_modules = g_hash_table_new ( g_str_hash , g_str_equal ) ;
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 ,
value ,
NULL ) ;
}
2023-06-26 03:10:16 +00:00
if ( display - > vk_save_pipeline_cache_source )
{
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
}
2023-06-28 03:43:53 +00:00
VkShaderModule
gdk_display_get_vk_shader_module ( GdkDisplay * self ,
const char * resource_name )
{
VkShaderModule shader ;
GError * error = NULL ;
GBytes * bytes ;
shader = g_hash_table_lookup ( self - > vk_shader_modules , resource_name ) ;
if ( shader )
return shader ;
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 ;
}
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 ,
& shader ) = = VK_SUCCESS )
{
g_hash_table_insert ( self - > vk_shader_modules , g_strdup ( resource_name ) , shader ) ;
}
else
{
shader = VK_NULL_HANDLE ;
}
g_bytes_unref ( bytes ) ;
return shader ;
}
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 */