From a180ff81b9decf8455a18e3cb577853445f320ce Mon Sep 17 00:00:00 2001 From: St0fF-NPL-ToM Date: Wed, 16 Sep 2026 22:41:18 +0200 Subject: [PATCH 1/4] Fix vsg::DeviceFeatures.cpp to return a VkPhysicalDeviceFeatures2 to pass into VkDeviceCreateInfo.pNext. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Map-internal comparison does not assure VkPhysicalDeviceFeatures2 being "first", as e.g. VkPhysicalDeviceMultiviewFeatures gets sorted up front ( 1000059000 vs. 1000053001 → see vulkan_core.h) --- src/vsg/vk/DeviceFeatures.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/vsg/vk/DeviceFeatures.cpp b/src/vsg/vk/DeviceFeatures.cpp index 2ed841044d..33db54e055 100644 --- a/src/vsg/vk/DeviceFeatures.cpp +++ b/src/vsg/vk/DeviceFeatures.cpp @@ -16,6 +16,10 @@ using namespace vsg; DeviceFeatures::DeviceFeatures() { + // make sure to have a default VkPhysicalDeviceFeatures2 created + get(); + // Specs state: + // "For all features, including the Core 1.0 Features, use VkPhysicalDeviceFeatures2 to pass into VkDeviceCreateInfo.pNext" } DeviceFeatures::~DeviceFeatures() @@ -40,16 +44,22 @@ void DeviceFeatures::clear() void* DeviceFeatures::data() const { - if (_features.empty()) return nullptr; - // chain the Feature pNext pointers together - FeatureHeader* previous = nullptr; - for (auto itr = _features.rbegin(); itr != _features.rend(); ++itr) - { - itr->second.first->pNext = previous; - previous = itr->second.first; - } + // IF NOTHING WAS SETUP - this will return nullptr as well. + FeatureHeader *previous = nullptr, *first = nullptr; + for (auto it : _features) + if (it.second.first->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) + first = it.second.first; + else + { + it.second.first->pNext = previous; + previous = it.second.first; + } + if (first) + first->pNext = previous; + else // Fallback path: try to simply hand over, what we have, if any … + first = previous; // return head of the chain - return const_cast(reinterpret_cast(_features.begin()->second.first)); + return const_cast(reinterpret_cast(first)); } From 19f92dde54d655931171b8ab15fd02e5bfc4309c Mon Sep 17 00:00:00 2001 From: St0fF-NPL-ToM Date: Thu, 17 Sep 2026 14:19:29 +0200 Subject: [PATCH 2/4] see VulkanSceneGraph#1744: really fix DeviceFeatures - change storage container type to simple std::list - rewritten get() template function to: - directly return already created structs - behaviour as before - insert and return the struct get<>() was called for - insert the required PhysicalDeviceFeatures2-Struct at the beginning in case there is none as soon as any other struct is added - rewritten data() function to: - short-circuit undeclared features (as before) - use the list to forward-chain all structs together and return the first. --- include/vsg/vk/DeviceFeatures.h | 28 +++++++++++++++++----------- src/vsg/vk/DeviceFeatures.cpp | 33 ++++++++++----------------------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/include/vsg/vk/DeviceFeatures.h b/include/vsg/vk/DeviceFeatures.h index c9aa381fc0..fe67435bda 100644 --- a/include/vsg/vk/DeviceFeatures.h +++ b/include/vsg/vk/DeviceFeatures.h @@ -2,7 +2,7 @@ /* -Copyright(c) 2021 Robert Osfield +Copyright(c) 2021 Robert Osfield, 2026 Stefan Kaps Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -12,7 +12,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ -#include +#include +#include #include #include @@ -37,15 +38,20 @@ namespace vsg template FeatureStruct& get() { - if (auto itr = _features.find(type); itr != _features.end()) return *reinterpret_cast(itr->second.first); + auto it = std::find_if(_features.begin(), _features.end(), [](const auto& f) { return f.sType == type; }); + if (it != _features.end()) return *reinterpret_cast(it->first); - FeatureStruct* feature = new FeatureStruct{}; + if constexpr (type == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) + it = _features.begin(); + else + { // as soon as we WILL create another type, we also need the base + if (_features.front().first->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) + get(); + it = _features.end(); + } - feature->sType = type; - feature->pNext = nullptr; - - _features[type].first = reinterpret_cast(feature); - _features[type].second = [](FeatureHeader* ptr) { delete reinterpret_cast(ptr); }; + FeatureStruct* feature = new FeatureStruct{type, nullptr}; + _features.emplace(it, {feature, [](FeatureHeader* ptr) { delete reinterpret_cast(ptr); }}); return *feature; } @@ -61,7 +67,7 @@ namespace vsg /// data() is used as the VkCreateDeviceInfo.pNext setting /// automatically chains the pNext pointers of the used feature structures /// return nullptr when no features structures have been used. - void* data() const; + void* data(); protected: ~DeviceFeatures() override; @@ -74,7 +80,7 @@ namespace vsg using DeleteHandler = void (*)(FeatureHeader* ptr); - std::map> _features; + std::list> _features; }; VSG_type_name(vsg::DeviceFeatures); diff --git a/src/vsg/vk/DeviceFeatures.cpp b/src/vsg/vk/DeviceFeatures.cpp index 33db54e055..877952053d 100644 --- a/src/vsg/vk/DeviceFeatures.cpp +++ b/src/vsg/vk/DeviceFeatures.cpp @@ -14,13 +14,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; -DeviceFeatures::DeviceFeatures() -{ - // make sure to have a default VkPhysicalDeviceFeatures2 created - get(); - // Specs state: - // "For all features, including the Core 1.0 Features, use VkPhysicalDeviceFeatures2 to pass into VkDeviceCreateInfo.pNext" -} +DeviceFeatures::DeviceFeatures() {} DeviceFeatures::~DeviceFeatures() { @@ -36,7 +30,7 @@ void DeviceFeatures::clear() { for (auto& feature : _features) { - feature.second.second(feature.second.first); + feature.second(feature.first); } _features.clear(); @@ -44,21 +38,14 @@ void DeviceFeatures::clear() void* DeviceFeatures::data() const { - // chain the Feature pNext pointers together - // IF NOTHING WAS SETUP - this will return nullptr as well. - FeatureHeader *previous = nullptr, *first = nullptr; - for (auto it : _features) - if (it.second.first->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) - first = it.second.first; - else - { - it.second.first->pNext = previous; - previous = it.second.first; - } - if (first) - first->pNext = previous; - else // Fallback path: try to simply hand over, what we have, if any … - first = previous; + if (_features.empty()) return nullptr; + + // chain the Feature pNext pointers together - make sure: first must ne VkPhysicalDeviceFeatures2 + if (_features.front().first->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) get(); + FeatureHeader* first{ nullptr }, *prev{ nullptr }; + for ( auto it : _features ) + if ( first ) prev->pNext = it.first, prev = it.first; + else first = prev = it.first; // return head of the chain return const_cast(reinterpret_cast(first)); From 9d6230b954e96286f6bcef4ee2a636635dd4312f Mon Sep 17 00:00:00 2001 From: St0fF-NPL-ToM Date: Thu, 17 Sep 2026 14:31:44 +0200 Subject: [PATCH 3/4] And some more fixing my own shortcomings ;) --- include/vsg/vk/DeviceFeatures.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/vsg/vk/DeviceFeatures.h b/include/vsg/vk/DeviceFeatures.h index fe67435bda..b0bc606021 100644 --- a/include/vsg/vk/DeviceFeatures.h +++ b/include/vsg/vk/DeviceFeatures.h @@ -38,7 +38,8 @@ namespace vsg template FeatureStruct& get() { - auto it = std::find_if(_features.begin(), _features.end(), [](const auto& f) { return f.sType == type; }); + auto it = std::find_if(_features.begin(), _features.end(), + [](const auto& p) { return p.first->sType == type; }); if (it != _features.end()) return *reinterpret_cast(it->first); if constexpr (type == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) @@ -46,12 +47,12 @@ namespace vsg else { // as soon as we WILL create another type, we also need the base if (_features.front().first->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) - get(); + get(); it = _features.end(); } FeatureStruct* feature = new FeatureStruct{type, nullptr}; - _features.emplace(it, {feature, [](FeatureHeader* ptr) { delete reinterpret_cast(ptr); }}); + _features.emplace(it, (FeatureHeader*)feature, [](FeatureHeader* ptr) { delete reinterpret_cast(ptr); }); return *feature; } @@ -67,7 +68,7 @@ namespace vsg /// data() is used as the VkCreateDeviceInfo.pNext setting /// automatically chains the pNext pointers of the used feature structures /// return nullptr when no features structures have been used. - void* data(); + void* data() const; protected: ~DeviceFeatures() override; From da2bad64652fb92d7594dc0c27a92f37a1f01530 Mon Sep 17 00:00:00 2001 From: St0fF-NPL-ToM Date: Thu, 17 Sep 2026 14:41:39 +0200 Subject: [PATCH 4/4] And again, I did not catch all relevant changes in a commit. --- src/vsg/vk/DeviceFeatures.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vsg/vk/DeviceFeatures.cpp b/src/vsg/vk/DeviceFeatures.cpp index 877952053d..a0ed5a0593 100644 --- a/src/vsg/vk/DeviceFeatures.cpp +++ b/src/vsg/vk/DeviceFeatures.cpp @@ -40,12 +40,13 @@ void* DeviceFeatures::data() const { if (_features.empty()) return nullptr; - // chain the Feature pNext pointers together - make sure: first must ne VkPhysicalDeviceFeatures2 - if (_features.front().first->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) get(); - FeatureHeader* first{ nullptr }, *prev{ nullptr }; - for ( auto it : _features ) - if ( first ) prev->pNext = it.first, prev = it.first; - else first = prev = it.first; + // chain the Feature pNext pointers together + FeatureHeader *first{nullptr}, *prev{nullptr}; + for (auto it : _features) + if (first) + prev->pNext = it.first, prev = it.first; + else + first = prev = it.first; // return head of the chain return const_cast(reinterpret_cast(first));