diff --git a/include/SDL3/SDL_vulkan.h b/include/SDL3/SDL_vulkan.h index 736162129..ca4bf7161 100644 --- a/include/SDL3/SDL_vulkan.h +++ b/include/SDL3/SDL_vulkan.h @@ -51,6 +51,7 @@ extern "C" { #endif VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_HANDLE(VkPhysicalDevice) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) struct VkAllocationCallbacks; @@ -211,6 +212,25 @@ extern SDL_DECLSPEC void SDLCALL SDL_Vulkan_DestroySurface(VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator); +/** + * Query support for presentation via a given physical device and queue family. + * + * The `instance` must have been created with extensions returned by + * SDL_Vulkan_GetInstanceExtensions() enabled. + * + * \param instance the Vulkan instance handle. + * \param physicalDevice a valid Vulkan physical device handle. + * \param queueFamilyIndex a valid queue family index for the given physical device. + * \returns SDL_TRUE if supported, SDL_FALSE if unsupported or an error occurred. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_Vulkan_GetInstanceExtensions + */ +extern SDL_DECLSPEC SDL_bool SDLCALL SDL_Vulkan_GetPresentationSupport(VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex); + /* @} *//* Vulkan support functions */ /* Ends C function definitions when using C++ */ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 7d23d8902..5339837ac 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1045,6 +1045,7 @@ SDL3_0.0.0 { SDL_wcsnstr; SDL_wcsstr; SDL_wcstol; + SDL_Vulkan_GetPresentationSupport; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index ebd709cb2..6b940e079 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1070,3 +1070,4 @@ #define SDL_wcsnstr SDL_wcsnstr_REAL #define SDL_wcsstr SDL_wcsstr_REAL #define SDL_wcstol SDL_wcstol_REAL +#define SDL_Vulkan_GetPresentationSupport SDL_Vulkan_GetPresentationSupport_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 2399bfc4f..acef1b981 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1076,3 +1076,4 @@ SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return) SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t c),(a,b,c),return) SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),return) SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_Vulkan_GetPresentationSupport,(VkInstance a, VkPhysicalDevice b, Uint32 c),(a,b,c),return) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index f648fd0e2..4a3d03908 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -304,6 +304,7 @@ struct SDL_VideoDevice char const* const* (*Vulkan_GetInstanceExtensions)(SDL_VideoDevice *_this, Uint32 *count); int (*Vulkan_CreateSurface)(SDL_VideoDevice *_this, SDL_Window *window, VkInstance instance, const struct VkAllocationCallbacks *allocator, VkSurfaceKHR *surface); void (*Vulkan_DestroySurface)(SDL_VideoDevice *_this, VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator); + SDL_bool (*Vulkan_GetPresentationSupport)(SDL_VideoDevice *_this, VkInstance instance, VkPhysicalDevice physicalDevice, Uint32 queueFamilyIndex); /* * * */ /* diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 516ac5369..9e7026cb3 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -5526,6 +5526,24 @@ void SDL_Vulkan_DestroySurface(VkInstance instance, } } +SDL_bool SDL_Vulkan_GetPresentationSupport(VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex) +{ + if (_this && instance && physicalDevice) { + if (_this->Vulkan_GetPresentationSupport) { + return _this->Vulkan_GetPresentationSupport(_this, instance, physicalDevice, queueFamilyIndex); + } + + /* If the backend does not have this function then it does not have a + * WSI function to query it; in other words it's not necessary to check + * as it is always supported. + */ + return SDL_TRUE; + } + return SDL_FALSE; +} + SDL_MetalView SDL_Metal_CreateView(SDL_Window *window) { CHECK_WINDOW_MAGIC(window, NULL); diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 6f07959e2..dc7232b09 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -519,6 +519,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void) device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions; device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface; device->Vulkan_DestroySurface = Wayland_Vulkan_DestroySurface; + device->Vulkan_GetPresentationSupport = Wayland_Vulkan_GetPresentationSupport; #endif device->free = Wayland_DeleteDevice; diff --git a/src/video/wayland/SDL_waylandvulkan.c b/src/video/wayland/SDL_waylandvulkan.c index 1bcb262dc..e6d78aa1a 100644 --- a/src/video/wayland/SDL_waylandvulkan.c +++ b/src/video/wayland/SDL_waylandvulkan.c @@ -175,4 +175,32 @@ void Wayland_Vulkan_DestroySurface(SDL_VideoDevice *_this, } } +SDL_bool Wayland_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex) +{ + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = + (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)vkGetInstanceProcAddr( + instance, + "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); + + if (!_this->vulkan_config.loader_handle) { + SDL_SetError("Vulkan is not loaded"); + return SDL_FALSE; + } + + if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) { + SDL_SetError(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME + " extension is not enabled in the Vulkan instance."); + return SDL_FALSE; + } + + return vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, + queueFamilyIndex, + _this->driverdata->display); +} + #endif diff --git a/src/video/wayland/SDL_waylandvulkan.h b/src/video/wayland/SDL_waylandvulkan.h index 85d2adaee..b71433c40 100644 --- a/src/video/wayland/SDL_waylandvulkan.h +++ b/src/video/wayland/SDL_waylandvulkan.h @@ -46,6 +46,10 @@ void Wayland_Vulkan_DestroySurface(SDL_VideoDevice *_this, VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator); +SDL_bool Wayland_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex); #endif diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 688bc04b6..757a3f6bd 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -263,6 +263,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void) device->Vulkan_GetInstanceExtensions = WIN_Vulkan_GetInstanceExtensions; device->Vulkan_CreateSurface = WIN_Vulkan_CreateSurface; device->Vulkan_DestroySurface = WIN_Vulkan_DestroySurface; + device->Vulkan_GetPresentationSupport = WIN_Vulkan_GetPresentationSupport; #endif #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) diff --git a/src/video/windows/SDL_windowsvulkan.c b/src/video/windows/SDL_windowsvulkan.c index 1584c526b..bd774df5f 100644 --- a/src/video/windows/SDL_windowsvulkan.c +++ b/src/video/windows/SDL_windowsvulkan.c @@ -166,4 +166,31 @@ void WIN_Vulkan_DestroySurface(SDL_VideoDevice *_this, } } +SDL_bool WIN_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex) +{ + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = + (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)vkGetInstanceProcAddr( + instance, + "vkGetPhysicalDeviceWin32PresentationSupportKHR"); + + if (!_this->vulkan_config.loader_handle) { + SDL_SetError("Vulkan is not loaded"); + return SDL_FALSE; + } + + if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) { + SDL_SetError(VK_KHR_WIN32_SURFACE_EXTENSION_NAME + " extension is not enabled in the Vulkan instance."); + return SDL_FALSE; + } + + return vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, + queueFamilyIndex); +} + #endif diff --git a/src/video/windows/SDL_windowsvulkan.h b/src/video/windows/SDL_windowsvulkan.h index 474a6fce4..ea74856cb 100644 --- a/src/video/windows/SDL_windowsvulkan.h +++ b/src/video/windows/SDL_windowsvulkan.h @@ -46,6 +46,10 @@ void WIN_Vulkan_DestroySurface(SDL_VideoDevice *_this, VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator); +SDL_bool WIN_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex); #endif diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 2f87933f1..afba879ff 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -280,6 +280,7 @@ static SDL_VideoDevice *X11_CreateDevice(void) device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions; device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface; device->Vulkan_DestroySurface = X11_Vulkan_DestroySurface; + device->Vulkan_GetPresentationSupport = X11_Vulkan_GetPresentationSupport; #endif #ifdef SDL_USE_LIBDBUS diff --git a/src/video/x11/SDL_x11vulkan.c b/src/video/x11/SDL_x11vulkan.c index 25d4f90f7..8aa07221d 100644 --- a/src/video/x11/SDL_x11vulkan.c +++ b/src/video/x11/SDL_x11vulkan.c @@ -232,4 +232,59 @@ void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this, } } +SDL_bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex) +{ + SDL_VideoData *videoData = _this->driverdata; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + const char *forced_visual_id; + VisualID visualid; + + if (!_this->vulkan_config.loader_handle) { + return SDL_SetError("Vulkan is not loaded"); + } + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; + + forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID); + if (forced_visual_id) { + visualid = SDL_strtol(forced_visual_id, NULL, 0); + } else { + visualid = X11_XVisualIDFromVisual(DefaultVisual(videoData->display, DefaultScreen(videoData->display))); + } + + if (videoData->vulkan_xlib_xcb_library) { + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)vkGetInstanceProcAddr( + instance, + "vkGetPhysicalDeviceXcbPresentationSupportKHR"); + + if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) { + SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); + return SDL_FALSE; + } + + return vkGetPhysicalDeviceXcbPresentationSupportKHR(physicalDevice, + queueFamilyIndex, + videoData->vulkan_XGetXCBConnection(videoData->display), + visualid); + } else { + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)vkGetInstanceProcAddr( + instance, + "vkGetPhysicalDeviceXlibPresentationSupportKHR"); + + if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) { + SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); + return SDL_FALSE; + } + + return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, + queueFamilyIndex, + videoData->display, + visualid); + } +} + #endif diff --git a/src/video/x11/SDL_x11vulkan.h b/src/video/x11/SDL_x11vulkan.h index 217ea726d..23d916160 100644 --- a/src/video/x11/SDL_x11vulkan.h +++ b/src/video/x11/SDL_x11vulkan.h @@ -43,6 +43,10 @@ void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this, VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator); +SDL_bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, + VkInstance instance, + VkPhysicalDevice physicalDevice, + Uint32 queueFamilyIndex); #endif