Skip to content

Commit c9d9428

Browse files
Add enable_features_if_present to PhysicalDevice
Allows users to enable features if they are present, getting back a bool telling them whether the feature is supported and will be enabled on the device. Also: * Removes redundant VkPhysicalDeviceFeatures2 struct in vkb::PhysicalDevice. * Adds test copying of details when creating a VkDevice so that test can check what features were actually enabled on the device. * Creates GenericFeatureChain struct for managing pNext chains. * Allow multiple calls to set_require_features by combining the fields
1 parent a78a7f3 commit c9d9428

6 files changed

Lines changed: 435 additions & 72 deletions

File tree

src/VkBootstrap.cpp

Lines changed: 152 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,54 @@ bool GenericFeaturesPNextNode::match(GenericFeaturesPNextNode const& requested,
4747
return true;
4848
}
4949

50+
void GenericFeaturesPNextNode::combine(GenericFeaturesPNextNode const& right) noexcept {
51+
assert(sType == right.sType && "Non-matching sTypes in features nodes!");
52+
for (uint32_t i = 0; i < GenericFeaturesPNextNode::field_capacity; i++) {
53+
fields[i] = fields[i] || right.fields[i];
54+
}
55+
}
56+
57+
bool GenericFeatureChain::match(GenericFeatureChain const& extension_requested) const noexcept {
58+
// Should only be false if extension_supported was unable to be filled out, due to the
59+
// physical device not supporting vkGetPhysicalDeviceFeatures2 in any capacity.
60+
if (extension_requested.nodes.size() != nodes.size()) {
61+
return false;
62+
}
63+
64+
for (size_t i = 0; i < nodes.size() && i < nodes.size(); ++i) {
65+
if (!GenericFeaturesPNextNode::match(extension_requested.nodes[i], nodes[i])) return false;
66+
}
67+
return true;
68+
}
69+
70+
void GenericFeatureChain::chain_up(VkPhysicalDeviceFeatures2& feats2) noexcept {
71+
detail::GenericFeaturesPNextNode* prev = nullptr;
72+
for (auto& extension : nodes) {
73+
if (prev != nullptr) {
74+
prev->pNext = &extension;
75+
}
76+
prev = &extension;
77+
}
78+
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
79+
feats2.pNext = !nodes.empty() ? &nodes.at(0) : nullptr;
80+
}
81+
82+
void GenericFeatureChain::combine(GenericFeatureChain const& right) noexcept {
83+
for (const auto& right_node : right.nodes) {
84+
bool already_contained = false;
85+
for (auto& left_node : nodes) {
86+
if (left_node.sType == right_node.sType) {
87+
left_node.combine(right_node);
88+
already_contained = true;
89+
}
90+
}
91+
if (!already_contained) {
92+
nodes.push_back(right_node);
93+
}
94+
}
95+
}
96+
97+
5098
class VulkanFunctions {
5199
private:
52100
std::mutex init_mutex;
@@ -863,10 +911,68 @@ std::vector<std::string> check_device_extension_support(
863911
}
864912

865913
// clang-format off
914+
void combine_features(VkPhysicalDeviceFeatures& dest, VkPhysicalDeviceFeatures src){
915+
dest.robustBufferAccess = dest.robustBufferAccess || src.robustBufferAccess;
916+
dest.fullDrawIndexUint32 = dest.fullDrawIndexUint32 || src.fullDrawIndexUint32;
917+
dest.imageCubeArray = dest.imageCubeArray || src.imageCubeArray;
918+
dest.independentBlend = dest.independentBlend || src.independentBlend;
919+
dest.geometryShader = dest.geometryShader || src.geometryShader;
920+
dest.tessellationShader = dest.tessellationShader || src.tessellationShader;
921+
dest.sampleRateShading = dest.sampleRateShading || src.sampleRateShading;
922+
dest.dualSrcBlend = dest.dualSrcBlend || src.dualSrcBlend;
923+
dest.logicOp = dest.logicOp || src.logicOp;
924+
dest.multiDrawIndirect = dest.multiDrawIndirect || src.multiDrawIndirect;
925+
dest.drawIndirectFirstInstance = dest.drawIndirectFirstInstance || src.drawIndirectFirstInstance;
926+
dest.depthClamp = dest.depthClamp || src.depthClamp;
927+
dest.depthBiasClamp = dest.depthBiasClamp || src.depthBiasClamp;
928+
dest.fillModeNonSolid = dest.fillModeNonSolid || src.fillModeNonSolid;
929+
dest.depthBounds = dest.depthBounds || src.depthBounds;
930+
dest.wideLines = dest.wideLines || src.wideLines;
931+
dest.largePoints = dest.largePoints || src.largePoints;
932+
dest.alphaToOne = dest.alphaToOne || src.alphaToOne;
933+
dest.multiViewport = dest.multiViewport || src.multiViewport;
934+
dest.samplerAnisotropy = dest.samplerAnisotropy || src.samplerAnisotropy;
935+
dest.textureCompressionETC2 = dest.textureCompressionETC2 || src.textureCompressionETC2;
936+
dest.textureCompressionASTC_LDR = dest.textureCompressionASTC_LDR || src.textureCompressionASTC_LDR;
937+
dest.textureCompressionBC = dest.textureCompressionBC || src.textureCompressionBC;
938+
dest.occlusionQueryPrecise = dest.occlusionQueryPrecise || src.occlusionQueryPrecise;
939+
dest.pipelineStatisticsQuery = dest.pipelineStatisticsQuery || src.pipelineStatisticsQuery;
940+
dest.vertexPipelineStoresAndAtomics = dest.vertexPipelineStoresAndAtomics || src.vertexPipelineStoresAndAtomics;
941+
dest.fragmentStoresAndAtomics = dest.fragmentStoresAndAtomics || src.fragmentStoresAndAtomics;
942+
dest.shaderTessellationAndGeometryPointSize = dest.shaderTessellationAndGeometryPointSize || src.shaderTessellationAndGeometryPointSize;
943+
dest.shaderImageGatherExtended = dest.shaderImageGatherExtended || src.shaderImageGatherExtended;
944+
dest.shaderStorageImageExtendedFormats = dest.shaderStorageImageExtendedFormats || src.shaderStorageImageExtendedFormats;
945+
dest.shaderStorageImageMultisample = dest.shaderStorageImageMultisample || src.shaderStorageImageMultisample;
946+
dest.shaderStorageImageReadWithoutFormat = dest.shaderStorageImageReadWithoutFormat || src.shaderStorageImageReadWithoutFormat;
947+
dest.shaderStorageImageWriteWithoutFormat = dest.shaderStorageImageWriteWithoutFormat || src.shaderStorageImageWriteWithoutFormat;
948+
dest.shaderUniformBufferArrayDynamicIndexing = dest.shaderUniformBufferArrayDynamicIndexing || src.shaderUniformBufferArrayDynamicIndexing;
949+
dest.shaderSampledImageArrayDynamicIndexing = dest.shaderSampledImageArrayDynamicIndexing || src.shaderSampledImageArrayDynamicIndexing;
950+
dest.shaderStorageBufferArrayDynamicIndexing = dest.shaderStorageBufferArrayDynamicIndexing || src.shaderStorageBufferArrayDynamicIndexing;
951+
dest.shaderStorageImageArrayDynamicIndexing = dest.shaderStorageImageArrayDynamicIndexing || src.shaderStorageImageArrayDynamicIndexing;
952+
dest.shaderClipDistance = dest.shaderClipDistance || src.shaderClipDistance;
953+
dest.shaderCullDistance = dest.shaderCullDistance || src.shaderCullDistance;
954+
dest.shaderFloat64 = dest.shaderFloat64 || src.shaderFloat64;
955+
dest.shaderInt64 = dest.shaderInt64 || src.shaderInt64;
956+
dest.shaderInt16 = dest.shaderInt16 || src.shaderInt16;
957+
dest.shaderResourceResidency = dest.shaderResourceResidency || src.shaderResourceResidency;
958+
dest.shaderResourceMinLod = dest.shaderResourceMinLod || src.shaderResourceMinLod;
959+
dest.sparseBinding = dest.sparseBinding || src.sparseBinding;
960+
dest.sparseResidencyBuffer = dest.sparseResidencyBuffer || src.sparseResidencyBuffer;
961+
dest.sparseResidencyImage2D = dest.sparseResidencyImage2D || src.sparseResidencyImage2D;
962+
dest.sparseResidencyImage3D = dest.sparseResidencyImage3D || src.sparseResidencyImage3D;
963+
dest.sparseResidency2Samples = dest.sparseResidency2Samples || src.sparseResidency2Samples;
964+
dest.sparseResidency4Samples = dest.sparseResidency4Samples || src.sparseResidency4Samples;
965+
dest.sparseResidency8Samples = dest.sparseResidency8Samples || src.sparseResidency8Samples;
966+
dest.sparseResidency16Samples = dest.sparseResidency16Samples || src.sparseResidency16Samples;
967+
dest.sparseResidencyAliased = dest.sparseResidencyAliased || src.sparseResidencyAliased;
968+
dest.variableMultisampleRate = dest.variableMultisampleRate || src.variableMultisampleRate;
969+
dest.inheritedQueries = dest.inheritedQueries || src.inheritedQueries;
970+
}
971+
866972
bool supports_features(VkPhysicalDeviceFeatures supported,
867973
VkPhysicalDeviceFeatures requested,
868-
std::vector<GenericFeaturesPNextNode> const& extension_supported,
869-
std::vector<GenericFeaturesPNextNode> const& extension_requested) {
974+
GenericFeatureChain const& extension_supported,
975+
GenericFeatureChain const& extension_requested) {
870976
if (requested.robustBufferAccess && !supported.robustBufferAccess) return false;
871977
if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) return false;
872978
if (requested.imageCubeArray && !supported.imageCubeArray) return false;
@@ -923,18 +1029,7 @@ bool supports_features(VkPhysicalDeviceFeatures supported,
9231029
if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false;
9241030
if (requested.inheritedQueries && !supported.inheritedQueries) return false;
9251031

926-
// Should only be false if extension_supported was unable to be filled out, due to the
927-
// physical device not supporting vkGetPhysicalDeviceFeatures2 in any capacity.
928-
if (extension_requested.size() != extension_supported.size()) {
929-
return false;
930-
}
931-
932-
for(size_t i = 0; i < extension_requested.size() && i < extension_supported.size(); ++i) {
933-
auto res = GenericFeaturesPNextNode::match(extension_requested[i], extension_supported[i]);
934-
if(!res) return false;
935-
}
936-
937-
return true;
1032+
return extension_supported.match(extension_requested);
9381033
}
9391034
// clang-format on
9401035
// Finds the first queue which supports the desired operations. Returns QUEUE_INDEX_MAX_VALUE if none is found
@@ -988,8 +1083,8 @@ uint32_t get_present_queue_index(
9881083
}
9891084
} // namespace detail
9901085

991-
PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice vk_phys_device,
992-
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const {
1086+
PhysicalDevice PhysicalDeviceSelector::populate_device_details(
1087+
VkPhysicalDevice vk_phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const {
9931088
PhysicalDevice physical_device{};
9941089
physical_device.physical_device = vk_phys_device;
9951090
physical_device.surface = instance_info.surface;
@@ -1013,25 +1108,14 @@ PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice
10131108
physical_device.available_extensions.push_back(&ext.extensionName[0]);
10141109
}
10151110

1016-
physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; // same value as the non-KHR version
10171111
physical_device.properties2_ext_enabled = instance_info.properties2_ext_enabled;
10181112

10191113
auto fill_chain = src_extended_features_chain;
10201114

10211115
bool instance_is_1_1 = instance_info.version >= VKB_VK_API_VERSION_1_1;
1022-
if (!fill_chain.empty() && (instance_is_1_1 || instance_info.properties2_ext_enabled)) {
1023-
1024-
detail::GenericFeaturesPNextNode* prev = nullptr;
1025-
for (auto& extension : fill_chain) {
1026-
if (prev != nullptr) {
1027-
prev->pNext = &extension;
1028-
}
1029-
prev = &extension;
1030-
}
1031-
1116+
if (!fill_chain.nodes.empty() && (instance_is_1_1 || instance_info.properties2_ext_enabled)) {
10321117
VkPhysicalDeviceFeatures2 local_features{};
1033-
local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; // KHR is same as core here
1034-
local_features.pNext = &fill_chain.front();
1118+
fill_chain.chain_up(local_features);
10351119
// Use KHR function if not able to use the core function
10361120
if (instance_is_1_1) {
10371121
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(vk_phys_device, &local_features);
@@ -1135,7 +1219,7 @@ PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance, VkSurfa
11351219
Result<std::vector<PhysicalDevice>> PhysicalDeviceSelector::select_impl(DeviceSelectionMode selection) const {
11361220
#if !defined(NDEBUG)
11371221
// Validation
1138-
for (const auto& node : criteria.extended_features_chain) {
1222+
for (const auto& node : criteria.extended_features_chain.nodes) {
11391223
assert(node.sType != static_cast<VkStructureType>(0) &&
11401224
"Features struct sType must be filled with the struct's "
11411225
"corresponding VkStructureType enum");
@@ -1336,7 +1420,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::disable_portability_subset() {
13361420
}
13371421

13381422
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysicalDeviceFeatures const& features) {
1339-
criteria.required_features = features;
1423+
detail::combine_features(criteria.required_features, features);
13401424
return *this;
13411425
}
13421426
#if defined(VKB_VK_API_VERSION_1_2)
@@ -1399,7 +1483,7 @@ bool PhysicalDevice::enable_extension_if_present(const char* extension) {
13991483
}
14001484
return false;
14011485
}
1402-
bool PhysicalDevice::enable_extensions_if_present(const std::vector<const char*>& extensions) {
1486+
bool PhysicalDevice::enable_extensions_if_present(const std::vector<const char*>& extensions) {
14031487
for (const auto extension : extensions) {
14041488
auto it = std::find_if(std::begin(available_extensions),
14051489
std::end(available_extensions),
@@ -1411,6 +1495,39 @@ bool PhysicalDevice::enable_extensions_if_present(const std::vector<const char*>
14111495
return true;
14121496
}
14131497

1498+
bool PhysicalDevice::enable_features_if_present(const VkPhysicalDeviceFeatures& features_to_enable) {
1499+
VkPhysicalDeviceFeatures actual_pdf{};
1500+
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(physical_device, &actual_pdf);
1501+
1502+
bool required_features_supported = detail::supports_features(actual_pdf, features_to_enable, {}, {});
1503+
if (required_features_supported) {
1504+
detail::combine_features(features, features_to_enable);
1505+
}
1506+
return required_features_supported;
1507+
}
1508+
1509+
bool PhysicalDevice::enable_features_node_if_present(detail::GenericFeaturesPNextNode const& node) {
1510+
VkPhysicalDeviceFeatures2 actual_pdf2{};
1511+
1512+
detail::GenericFeatureChain requested_features;
1513+
requested_features.nodes.push_back(node);
1514+
1515+
detail::GenericFeatureChain fill_chain = requested_features;
1516+
// Zero out supported features
1517+
memset(fill_chain.nodes.front().fields, UINT8_MAX, sizeof(VkBool32) * detail::GenericFeaturesPNextNode::field_capacity);
1518+
1519+
actual_pdf2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1520+
fill_chain.chain_up(actual_pdf2);
1521+
1522+
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(physical_device, &actual_pdf2);
1523+
bool required_features_supported = detail::supports_features({}, {}, fill_chain, requested_features);
1524+
if (required_features_supported) {
1525+
extended_features_chain.combine(requested_features);
1526+
}
1527+
return required_features_supported;
1528+
}
1529+
1530+
14141531
PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; }
14151532

14161533
// ---- Queues ---- //
@@ -1527,20 +1644,20 @@ Result<Device> DeviceBuilder::build() const {
15271644
}
15281645
}
15291646

1530-
if (user_defined_phys_dev_features_2 && !physical_device.extended_features_chain.empty()) {
1647+
if (user_defined_phys_dev_features_2 && !physical_device.extended_features_chain.nodes.empty()) {
15311648
return { DeviceError::VkPhysicalDeviceFeatures2_in_pNext_chain_while_using_add_required_extension_features };
15321649
}
15331650

15341651
// These objects must be alive during the call to vkCreateDevice
15351652
auto physical_device_extension_features_copy = physical_device.extended_features_chain;
15361653
VkPhysicalDeviceFeatures2 local_features2{};
15371654
local_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1655+
local_features2.features = physical_device.features;
15381656

15391657
if (!user_defined_phys_dev_features_2) {
15401658
if (physical_device.instance_version >= VKB_VK_API_VERSION_1_1 || physical_device.properties2_ext_enabled) {
1541-
local_features2.features = physical_device.features;
15421659
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&local_features2));
1543-
for (auto& features_node : physical_device_extension_features_copy) {
1660+
for (auto& features_node : physical_device_extension_features_copy.nodes) {
15441661
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&features_node));
15451662
}
15461663
} else {

src/VkBootstrap.h

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,35 @@ struct GenericFeaturesPNextNode {
172172

173173
static bool match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept;
174174

175+
void combine(GenericFeaturesPNextNode const& right) noexcept;
176+
175177
VkStructureType sType = static_cast<VkStructureType>(0);
176178
void* pNext = nullptr;
177179
VkBool32 fields[field_capacity];
178180
};
179181

182+
struct GenericFeatureChain {
183+
std::vector<GenericFeaturesPNextNode> nodes;
184+
185+
template <typename T> void add(T const& features) noexcept {
186+
// If this struct is already in the list, combine it
187+
for (auto& node : nodes) {
188+
if (features.sType == node.sType) {
189+
node.combine(features);
190+
return;
191+
}
192+
}
193+
// Otherwise append to the end
194+
nodes.push_back(features);
195+
}
196+
197+
bool match(GenericFeatureChain const& extension_requested) const noexcept;
198+
199+
void chain_up(VkPhysicalDeviceFeatures2& feats2) noexcept;
200+
201+
void combine(GenericFeatureChain const& right) noexcept;
202+
};
203+
180204
} // namespace detail
181205

182206
enum class InstanceError {
@@ -512,6 +536,16 @@ struct PhysicalDevice {
512536
// Returns true if all the extensions are present.
513537
bool enable_extensions_if_present(const std::vector<const char*>& extensions);
514538

539+
// If the features from VkPhysicalDeviceFeatures are all present, make all of the features be enable on the device.
540+
// Returns true all of the features are present.
541+
bool enable_features_if_present(const VkPhysicalDeviceFeatures& features_to_enable);
542+
543+
// If the features from the provided features struct are all present, make all of the features be enable on the
544+
// device. Returns true all of the features are present.
545+
template <typename T> bool enable_extension_features_if_present(T const& features) {
546+
return enable_features_node_if_present(detail::GenericFeaturesPNextNode(features));
547+
}
548+
515549
// A conversion function which allows this PhysicalDevice to be used
516550
// in places where VkPhysicalDevice would have been used.
517551
operator VkPhysicalDevice() const;
@@ -521,15 +555,16 @@ struct PhysicalDevice {
521555
std::vector<std::string> extensions_to_enable;
522556
std::vector<std::string> available_extensions;
523557
std::vector<VkQueueFamilyProperties> queue_families;
524-
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
525-
VkPhysicalDeviceFeatures2 features2{};
558+
detail::GenericFeatureChain extended_features_chain;
526559

527560
bool defer_surface_initialization = false;
528561
bool properties2_ext_enabled = false;
529562
enum class Suitable { yes, partial, no };
530563
Suitable suitable = Suitable::yes;
531564
friend class PhysicalDeviceSelector;
532565
friend class DeviceBuilder;
566+
567+
bool enable_features_node_if_present(detail::GenericFeaturesPNextNode const& node);
533568
};
534569

535570
enum class PreferredDeviceType { other = 0, integrated = 1, discrete = 2, virtual_gpu = 3, cpu = 4 };
@@ -620,7 +655,7 @@ class PhysicalDeviceSelector {
620655
// If this function is used, the user should not put their own VkPhysicalDeviceFeatures2 in
621656
// the pNext chain of VkDeviceCreateInfo.
622657
template <typename T> PhysicalDeviceSelector& add_required_extension_features(T const& features) {
623-
criteria.extended_features_chain.push_back(features);
658+
criteria.extended_features_chain.add(features);
624659
return *this;
625660
}
626661

@@ -681,14 +716,14 @@ class PhysicalDeviceSelector {
681716
VkPhysicalDeviceFeatures required_features{};
682717
VkPhysicalDeviceFeatures2 required_features2{};
683718

684-
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
719+
detail::GenericFeatureChain extended_features_chain;
685720
bool defer_surface_initialization = false;
686721
bool use_first_gpu_unconditionally = false;
687722
bool enable_portability_subset = true;
688723
} criteria;
689724

690-
PhysicalDevice populate_device_details(VkPhysicalDevice phys_device,
691-
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const;
725+
PhysicalDevice populate_device_details(
726+
VkPhysicalDevice phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const;
692727

693728
PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const;
694729

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ else()
77
endif()
88
target_link_libraries(VulkanMock
99
PUBLIC
10-
Vulkan::Headers
10+
Vulkan::Headers vk-bootstrap
1111
PRIVATE
1212
vk-bootstrap-compiler-warnings
1313
)

0 commit comments

Comments
 (0)