@@ -273,26 +273,67 @@ static inline void clearVector(T& v, S func, bool downsize = true)
273273
274274// ---------------------------- VULKAN DEVICE MANAGEMENT ----------------------------
275275
276- static bool hasFeatures (const vk::PhysicalDeviceFeatures& available, const vk::PhysicalDeviceFeatures& required) // The struct consists only of VkBool32 members
276+ template <class T > // Feature structs: sType, pNext, then only VkBool32 members (possibly followed by padding)
277+ static bool hasFeatures (const T& available, const T& required)
277278{
278- static_assert (sizeof (vk::PhysicalDeviceFeatures) % sizeof (vk::Bool32) == 0 );
279- const vk::Bool32* a = reinterpret_cast <const vk::Bool32*>(&available);
280- const vk::Bool32* r = reinterpret_cast <const vk::Bool32*>(&required);
281- for (size_t i = 0 ; i < sizeof (vk::PhysicalDeviceFeatures) / sizeof (vk::Bool32); i++) {
279+ const vk::Bool32* a = reinterpret_cast <const vk::Bool32*>(reinterpret_cast <const char *>(&available) + sizeof (VkBaseOutStructure));
280+ const vk::Bool32* r = reinterpret_cast <const vk::Bool32*>(reinterpret_cast <const char *>(&required) + sizeof (VkBaseOutStructure));
281+ for (size_t i = 0 ; i < (sizeof (T) - sizeof (VkBaseOutStructure)) / sizeof (vk::Bool32); i++) {
282282 if (r[i] && !a[i]) {
283283 return false ;
284284 }
285285 }
286286 return true ;
287287}
288288
289- double GPUDisplayBackendVulkan::checkDevice (vk::PhysicalDevice device, const std::vector<const char *>& reqDeviceExtensions, const vk::PhysicalDeviceFeatures& reqDeviceFeatures, bool silent)
289+ template <class ... T>
290+ static bool hasFeatures (const vk::StructureChain<T...>& available, const vk::StructureChain<T...>& required)
291+ {
292+ return (hasFeatures (available.template get <T>(), required.template get <T>()) && ...);
293+ }
294+
295+ template <class ... T>
296+ static void clearFeatures (vk::StructureChain<T...>& chain) // Zero all feature flags including padding, which the member-wise comparison in hasFeatures reads
297+ {
298+ ((memset (reinterpret_cast <char *>(&chain.template get <T>()) + sizeof (VkBaseOutStructure), 0 , sizeof (T) - sizeof (VkBaseOutStructure))), ...);
299+ }
300+
301+ template <class ... T>
302+ static vk::StructureChain<T...> getFeatures (vk::PhysicalDevice device, const vk::StructureChain<T...>&)
303+ {
304+ return device.getFeatures2 <T...>();
305+ }
306+
307+ double GPUDisplayBackendVulkan::checkDevice (vk::PhysicalDevice device, const std::vector<const char *>& reqDeviceExtensions, const FeatureChain& reqDeviceFeatures, bool silent)
290308{
291309 double score = -1 .;
292310 vk::PhysicalDeviceProperties deviceProperties = device.getProperties ();
293- vk::PhysicalDeviceFeatures deviceFeatures = device.getFeatures ();
294311 vk::PhysicalDeviceMemoryProperties memoryProperties = device.getMemoryProperties ();
295- if (!hasFeatures (deviceFeatures, reqDeviceFeatures)) {
312+ if (deviceProperties.apiVersion < VK_API_VERSION_1_4 ) {
313+ if (!silent) {
314+ GPUInfo (" %s ignored, supports only Vulkan %u.%u" , &deviceProperties.deviceName [0 ], VK_API_VERSION_MAJOR (deviceProperties.apiVersion ), VK_API_VERSION_MINOR (deviceProperties.apiVersion ));
315+ }
316+ return (-1 );
317+ }
318+
319+ std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties (nullptr );
320+ uint32_t extensionsFound = 0 ;
321+ for (uint32_t i = 0 ; i < reqDeviceExtensions.size (); i++) {
322+ for (uint32_t j = 0 ; j < availableExtensions.size (); j++) {
323+ if (strcmp (reqDeviceExtensions[i], availableExtensions[j].extensionName ) == 0 ) {
324+ extensionsFound++;
325+ break ;
326+ }
327+ }
328+ }
329+ if (extensionsFound < reqDeviceExtensions.size ()) {
330+ if (!silent) {
331+ GPUInfo (" %s ignored due to missing extensions" , &deviceProperties.deviceName [0 ]);
332+ }
333+ return (-1 );
334+ }
335+
336+ if (!hasFeatures (getFeatures (device, reqDeviceFeatures), reqDeviceFeatures)) { // Query only after the version and extension checks, the chain contains 1.4 and extension structs
296337 if (!silent) {
297338 GPUInfo (" %s ignored due to missing features" , &deviceProperties.deviceName [0 ]);
298339 }
@@ -320,23 +361,6 @@ double GPUDisplayBackendVulkan::checkDevice(vk::PhysicalDevice device, const std
320361 return (-1 );
321362 }
322363
323- std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties (nullptr );
324- uint32_t extensionsFound = 0 ;
325- for (uint32_t i = 0 ; i < reqDeviceExtensions.size (); i++) {
326- for (uint32_t j = 0 ; j < availableExtensions.size (); j++) {
327- if (strcmp (reqDeviceExtensions[i], availableExtensions[j].extensionName ) == 0 ) {
328- extensionsFound++;
329- break ;
330- }
331- }
332- }
333- if (extensionsFound < reqDeviceExtensions.size ()) {
334- if (!silent) {
335- GPUInfo (" %s ignored due to missing extensions" , &deviceProperties.deviceName [0 ]);
336- }
337- return (-1 );
338- }
339-
340364 updateSwapChainDetails (device);
341365 if (mSwapChainDetails .formats .empty () || mSwapChainDetails .presentModes .empty ()) {
342366 if (!silent) {
@@ -369,14 +393,27 @@ void GPUDisplayBackendVulkan::createDevice()
369393 appInfo.applicationVersion = VK_MAKE_VERSION (1 , 0 , 0 );
370394 appInfo.pEngineName = " GPU CI Standalone Engine" ;
371395 appInfo.engineVersion = VK_MAKE_VERSION (1 , 0 , 0 );
372- appInfo.apiVersion = VK_API_VERSION_1_0 ;
396+ appInfo.apiVersion = VK_API_VERSION_1_4 ;
373397
374398 vk::InstanceCreateInfo instanceCreateInfo;
375399 instanceCreateInfo.pApplicationInfo = &appInfo;
376400
377401 const char ** frontendExtensions;
378402 uint32_t frontendExtensionCount = mDisplay ->frontend ()->getReqVulkanExtensions (frontendExtensions);
379403 std::vector<const char *> reqInstanceExtensions (frontendExtensions, frontendExtensions + frontendExtensionCount);
404+ reqInstanceExtensions.insert (reqInstanceExtensions.end (), {VK_EXT_DEBUG_UTILS_EXTENSION_NAME , VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME , VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME }); // The latter two are required by VK_KHR_swapchain_maintenance1
405+ std::vector<vk::ExtensionProperties> availableInstanceExtensions = vk::enumerateInstanceExtensionProperties (nullptr );
406+ if (mDisplay ->GetProcessingSettings ().debugLevel >= 3 ) {
407+ std::cout << " available instance extensions: " << availableInstanceExtensions.size () << " \n " ;
408+ for (const auto & extension : availableInstanceExtensions) {
409+ std::cout << ' \t ' << extension.extensionName << ' \n ' ;
410+ }
411+ }
412+ for (const char * ext : reqInstanceExtensions) {
413+ if (std::none_of (availableInstanceExtensions.begin (), availableInstanceExtensions.end (), [ext](const auto & e) { return strcmp (ext, e.extensionName ) == 0 ; })) {
414+ throw std::runtime_error (std::string (" Required Vulkan instance extension not available: " ) + ext);
415+ }
416+ }
380417
381418 const std::vector<const char *> reqValidationLayers = {" VK_LAYER_KHRONOS_validation" };
382419 auto debugCallback = [](vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType, const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData, void * pUserData) -> VkBool32 {
@@ -412,7 +449,6 @@ void GPUDisplayBackendVulkan::createDevice()
412449 if (checkVulkanLayersSupported (reqValidationLayers)) {
413450 throw std::runtime_error (" Requested validation layer support not available" );
414451 }
415- reqInstanceExtensions.push_back (VK_EXT_DEBUG_UTILS_EXTENSION_NAME );
416452 instanceCreateInfo.enabledLayerCount = static_cast <uint32_t >(reqValidationLayers.size ());
417453 instanceCreateInfo.ppEnabledLayerNames = reqValidationLayers.data ();
418454 instanceCreateInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
@@ -435,21 +471,19 @@ void GPUDisplayBackendVulkan::createDevice()
435471 GPUInfo (" Enabling Vulkan Validation Layers" );
436472 mDebugMessenger = mInstance .createDebugUtilsMessengerEXT (debugCreateInfo, nullptr );
437473 }
438- std::vector<vk::ExtensionProperties> extensions = vk::enumerateInstanceExtensionProperties (nullptr );
439- if (mDisplay ->GetProcessingSettings ().debugLevel >= 3 ) {
440- std::cout << " available instance extensions: " << extensions.size () << " \n " ;
441- for (const auto & extension : extensions) {
442- std::cout << ' \t ' << extension.extensionName << ' \n ' ;
443- }
444- }
445474
446475 if (mDisplay ->frontend ()->getVulkanSurface (&mInstance , &mSurface )) {
447476 throw std::runtime_error (" Frontend does not provide Vulkan surface" );
448477 }
449478
450- const std::vector<const char *> reqDeviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME };
451- vk::PhysicalDeviceFeatures reqDeviceFeatures{};
452- reqDeviceFeatures.geometryShader = reqDeviceFeatures.wideLines = reqDeviceFeatures.largePoints = VK_TRUE ;
479+ const std::vector<const char *> reqDeviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME , VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME , VK_KHR_PRESENT_ID_EXTENSION_NAME , VK_KHR_PRESENT_WAIT_EXTENSION_NAME };
480+ FeatureChain reqDeviceFeatures; // Exactly these features are required and enabled
481+ clearFeatures (reqDeviceFeatures);
482+ auto & reqFeatures10 = reqDeviceFeatures.get <vk::PhysicalDeviceFeatures2>().features ;
483+ reqFeatures10.geometryShader = reqFeatures10.wideLines = reqFeatures10.largePoints = VK_TRUE ;
484+ reqDeviceFeatures.get <vk::PhysicalDeviceSwapchainMaintenance1FeaturesKHR>().swapchainMaintenance1 = VK_TRUE ;
485+ reqDeviceFeatures.get <vk::PhysicalDevicePresentIdFeaturesKHR>().presentId = VK_TRUE ;
486+ reqDeviceFeatures.get <vk::PhysicalDevicePresentWaitFeaturesKHR>().presentWait = VK_TRUE ;
453487
454488 mPhysicalDevice = VkPhysicalDevice (VK_NULL_HANDLE );
455489 std::vector<vk::PhysicalDevice> devices = mInstance .enumeratePhysicalDevices ();
@@ -502,7 +536,8 @@ void GPUDisplayBackendVulkan::createDevice()
502536 vk::DeviceCreateInfo deviceCreateInfo{};
503537 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
504538 deviceCreateInfo.queueCreateInfoCount = 1 ;
505- deviceCreateInfo.pEnabledFeatures = &reqDeviceFeatures;
539+ deviceCreateInfo.pNext = &reqDeviceFeatures.get <vk::PhysicalDeviceFeatures2>();
540+ deviceCreateInfo.pEnabledFeatures = nullptr ;
506541 deviceCreateInfo.enabledExtensionCount = static_cast <uint32_t >(reqDeviceExtensions.size ());
507542 deviceCreateInfo.ppEnabledExtensionNames = reqDeviceExtensions.data ();
508543 mDevice = mPhysicalDevice .createDevice (deviceCreateInfo, nullptr );
0 commit comments