From dd745dbc37266915f42226dba70856f9edd63ca3 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 31 May 2026 12:56:25 -0400 Subject: [PATCH 1/3] render/vulkan: use KHR variant for physical device properties The Vulkan renderer calls vkGetPhysicalDeviceProperties2 while the instance is created with API version 1.0. This violates the specification because the function was promoted to core in 1.1. The resulting validation error leads to undefined behavior and a crash on drivers that enforce strict API checking (e.g. AMD RADV). Replace the core function with the KHR extension variant, which is available when VK_KHR_get_physical_device_properties_2 is enabled, and maintains compatibility with Vulkan 1.0 instances. --- include/render/vulkan.h | 4 +++- render/vulkan/renderer.c | 2 +- render/vulkan/vulkan.c | 14 +++++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index de1195b8f..145395da5 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -24,6 +24,8 @@ struct wlr_vk_instance { PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessengerEXT; } api; + + PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; }; // Creates and initializes a vulkan instance. @@ -75,7 +77,7 @@ struct wlr_vk_device { // Tries to find the VkPhysicalDevice for the given drm fd. // Might find none and return VK_NULL_HANDLE. VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd); -int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev); +int vulkan_open_phdev_drm_fd(struct wlr_vk_instance *ini, VkPhysicalDevice phdev); // Creates a device for the given instance and physical device. struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index d342dee2c..cfad8ff58 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2818,7 +2818,7 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { // Do not use the drm_fd that was passed in: we should prefer the render // node even if a primary node was provided - dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); + dev->drm_fd = vulkan_open_phdev_drm_fd(ini, phdev); return vulkan_renderer_create_for_device(dev); diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 3877ec2a7..aa69733d2 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -195,6 +195,14 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { } } + ini->vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) + vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceProperties2KHR"); + if (!ini->vkGetPhysicalDeviceProperties2KHR) { + wlr_log(WLR_ERROR, "vkGetPhysicalDeviceProperties2KHR not found"); + goto error; + } + + return ini; error: @@ -324,7 +332,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) props.pNext = &driver_props; } - vkGetPhysicalDeviceProperties2(phdev, &props); + ini->vkGetPhysicalDeviceProperties2KHR(phdev, &props); if (has_driver_props) { wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); @@ -356,7 +364,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) return VK_NULL_HANDLE; } -int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) { +int vulkan_open_phdev_drm_fd(struct wlr_vk_instance *ini, VkPhysicalDevice phdev) { // vulkan_find_drm_phdev() already checks that VK_EXT_physical_device_drm // is supported VkPhysicalDeviceDrmPropertiesEXT drm_props = { @@ -366,7 +374,7 @@ int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &drm_props, }; - vkGetPhysicalDeviceProperties2(phdev, &props); + ini->vkGetPhysicalDeviceProperties2KHR(phdev, &props); dev_t devid; if (drm_props.hasRender) { From 280cc0742ae60fdb45526a283ce896694cf65b07 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 31 May 2026 23:07:57 -0400 Subject: [PATCH 2/3] Replace other 1.1 api device_features https://docs.vulkan.org/refpages/latest/refpages/source/vkGetPhysicalDeviceFeatures2.html --- include/render/vulkan.h | 1 + render/vulkan/vulkan.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 145395da5..77664e5fd 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -26,6 +26,7 @@ struct wlr_vk_instance { } api; PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; + PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; }; // Creates and initializes a vulkan instance. diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index aa69733d2..9a0a82bcc 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -202,6 +202,13 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { goto error; } + ini->vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR) + vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceFeatures2KHR"); + if (!ini->vkGetPhysicalDeviceFeatures2KHR) { + wlr_log(WLR_ERROR, "vkGetPhysicalDeviceFeatures2KHR not found"); + goto error; + } + return ini; @@ -555,7 +562,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = &phdev_sampler_ycbcr_features, }; - vkGetPhysicalDeviceFeatures2(phdev, &phdev_features); + ini->vkGetPhysicalDeviceFeatures2KHR(phdev, &phdev_features); dev->sampler_ycbcr_conversion = phdev_sampler_ycbcr_features.samplerYcbcrConversion; wlr_log(WLR_DEBUG, "Sampler YCbCr conversion %s", From c26c8ba315e81dafa9e964143d890065585c39bd Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 2 Jun 2026 17:57:55 -0400 Subject: [PATCH 3/3] render/vulkan: negotiate instance API version Use vkEnumerateInstanceVersion to pick the instance API version and load vkGetPhysicalDevice*2 entrypoints via core or KHR depending on that. This avoids API version validation errors when running with Vulkan 1.0. --- include/render/vulkan.h | 5 ++- render/vulkan/vulkan.c | 83 +++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 77664e5fd..6e2a5f783 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -19,14 +19,15 @@ struct wlr_vk_texture; struct wlr_vk_instance { VkInstance instance; VkDebugUtilsMessengerEXT messenger; + uint32_t api_version; struct { PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessengerEXT; } api; - PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; - PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; + PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2; + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2; }; // Creates and initializes a vulkan instance. diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 9a0a82bcc..9ad716ba9 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -81,6 +81,27 @@ static VKAPI_ATTR VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT } struct wlr_vk_instance *vulkan_instance_create(bool debug) { + uint32_t api_version = VK_API_VERSION_1_0; + PFN_vkEnumerateInstanceVersion enumerate_instance_version = + (PFN_vkEnumerateInstanceVersion)vkGetInstanceProcAddr( + VK_NULL_HANDLE, "vkEnumerateInstanceVersion"); + if (enumerate_instance_version) { + uint32_t supported_api_version = VK_API_VERSION_1_0; + if (enumerate_instance_version(&supported_api_version) == VK_SUCCESS) { + api_version = supported_api_version; + } else { + wlr_log(WLR_INFO, + "vkEnumerateInstanceVersion failed, assuming Vulkan 1.0"); + } + } + if (api_version >= VK_API_VERSION_1_1) { + api_version = VK_API_VERSION_1_1; + } else { + api_version = VK_API_VERSION_1_0; + } + wlr_log(WLR_DEBUG, "Using Vulkan instance API version %u.%u.%u", + VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version), + VK_VERSION_PATCH(api_version)); uint32_t avail_extc = 0; VkResult res; @@ -108,12 +129,15 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { wlr_log_errno(WLR_ERROR, "allocation failed"); return NULL; } + ini->api_version = api_version; size_t extensions_len = 0; const char *extensions[8] = {0}; - extensions[extensions_len++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; - extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME; - extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + if (api_version < VK_API_VERSION_1_1) { + extensions[extensions_len++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + } for (size_t i = 0; i < extensions_len; i++) { if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { @@ -136,7 +160,7 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pEngineName = "wlroots", .engineVersion = WLR_VERSION_NUM, - .apiVersion = VK_API_VERSION_1_0, + .apiVersion = api_version, }; VkInstanceCreateInfo instance_info = { @@ -195,18 +219,41 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { } } - ini->vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) - vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceProperties2KHR"); - if (!ini->vkGetPhysicalDeviceProperties2KHR) { - wlr_log(WLR_ERROR, "vkGetPhysicalDeviceProperties2KHR not found"); + if (ini->api_version < VK_API_VERSION_1_1) { + ini->vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) + vkGetInstanceProcAddr(ini->instance, + "vkGetPhysicalDeviceProperties2KHR"); + } else { + ini->vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) + vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceProperties2"); + if (!ini->vkGetPhysicalDeviceProperties2) { + ini->vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) + vkGetInstanceProcAddr(ini->instance, + "vkGetPhysicalDeviceProperties2KHR"); + } + } + if (!ini->vkGetPhysicalDeviceProperties2) { + wlr_log(WLR_ERROR, "vkGetPhysicalDeviceProperties2 not found"); goto error; } - ini->vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR) - vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceFeatures2KHR"); - if (!ini->vkGetPhysicalDeviceFeatures2KHR) { - wlr_log(WLR_ERROR, "vkGetPhysicalDeviceFeatures2KHR not found"); - goto error; + if (ini->api_version < VK_API_VERSION_1_1) { + ini->vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) + vkGetInstanceProcAddr(ini->instance, + "vkGetPhysicalDeviceFeatures2KHR"); + } else { + ini->vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) + vkGetInstanceProcAddr(ini->instance, "vkGetPhysicalDeviceFeatures2"); + if (!ini->vkGetPhysicalDeviceFeatures2) { + ini->vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) + vkGetInstanceProcAddr(ini->instance, + "vkGetPhysicalDeviceFeatures2KHR"); + } + } + if (!ini->vkGetPhysicalDeviceFeatures2) { + wlr_log(WLR_INFO, + "vkGetPhysicalDeviceFeatures2 not available, assuming no sampler " + "YCbCr conversion support"); } @@ -339,7 +386,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) props.pNext = &driver_props; } - ini->vkGetPhysicalDeviceProperties2KHR(phdev, &props); + ini->vkGetPhysicalDeviceProperties2(phdev, &props); if (has_driver_props) { wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); @@ -381,7 +428,7 @@ int vulkan_open_phdev_drm_fd(struct wlr_vk_instance *ini, VkPhysicalDevice phdev .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &drm_props, }; - ini->vkGetPhysicalDeviceProperties2KHR(phdev, &props); + ini->vkGetPhysicalDeviceProperties2(phdev, &props); dev_t devid; if (drm_props.hasRender) { @@ -562,7 +609,11 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = &phdev_sampler_ycbcr_features, }; - ini->vkGetPhysicalDeviceFeatures2KHR(phdev, &phdev_features); + if (ini->vkGetPhysicalDeviceFeatures2) { + ini->vkGetPhysicalDeviceFeatures2(phdev, &phdev_features); + } else { + phdev_sampler_ycbcr_features.samplerYcbcrConversion = VK_FALSE; + } dev->sampler_ycbcr_conversion = phdev_sampler_ycbcr_features.samplerYcbcrConversion; wlr_log(WLR_DEBUG, "Sampler YCbCr conversion %s",