mirror of
https://github.com/charles-lunarg/vk-bootstrap.git
synced 2024-11-22 07:24:34 +00:00
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
This commit is contained in:
parent
a78a7f38da
commit
c9d94287a5
@ -47,6 +47,54 @@ bool GenericFeaturesPNextNode::match(GenericFeaturesPNextNode const& requested,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenericFeaturesPNextNode::combine(GenericFeaturesPNextNode const& right) noexcept {
|
||||||
|
assert(sType == right.sType && "Non-matching sTypes in features nodes!");
|
||||||
|
for (uint32_t i = 0; i < GenericFeaturesPNextNode::field_capacity; i++) {
|
||||||
|
fields[i] = fields[i] || right.fields[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericFeatureChain::match(GenericFeatureChain const& extension_requested) const noexcept {
|
||||||
|
// Should only be false if extension_supported was unable to be filled out, due to the
|
||||||
|
// physical device not supporting vkGetPhysicalDeviceFeatures2 in any capacity.
|
||||||
|
if (extension_requested.nodes.size() != nodes.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nodes.size() && i < nodes.size(); ++i) {
|
||||||
|
if (!GenericFeaturesPNextNode::match(extension_requested.nodes[i], nodes[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericFeatureChain::chain_up(VkPhysicalDeviceFeatures2& feats2) noexcept {
|
||||||
|
detail::GenericFeaturesPNextNode* prev = nullptr;
|
||||||
|
for (auto& extension : nodes) {
|
||||||
|
if (prev != nullptr) {
|
||||||
|
prev->pNext = &extension;
|
||||||
|
}
|
||||||
|
prev = &extension;
|
||||||
|
}
|
||||||
|
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||||
|
feats2.pNext = !nodes.empty() ? &nodes.at(0) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericFeatureChain::combine(GenericFeatureChain const& right) noexcept {
|
||||||
|
for (const auto& right_node : right.nodes) {
|
||||||
|
bool already_contained = false;
|
||||||
|
for (auto& left_node : nodes) {
|
||||||
|
if (left_node.sType == right_node.sType) {
|
||||||
|
left_node.combine(right_node);
|
||||||
|
already_contained = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!already_contained) {
|
||||||
|
nodes.push_back(right_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class VulkanFunctions {
|
class VulkanFunctions {
|
||||||
private:
|
private:
|
||||||
std::mutex init_mutex;
|
std::mutex init_mutex;
|
||||||
@ -863,10 +911,68 @@ std::vector<std::string> check_device_extension_support(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
void combine_features(VkPhysicalDeviceFeatures& dest, VkPhysicalDeviceFeatures src){
|
||||||
|
dest.robustBufferAccess = dest.robustBufferAccess || src.robustBufferAccess;
|
||||||
|
dest.fullDrawIndexUint32 = dest.fullDrawIndexUint32 || src.fullDrawIndexUint32;
|
||||||
|
dest.imageCubeArray = dest.imageCubeArray || src.imageCubeArray;
|
||||||
|
dest.independentBlend = dest.independentBlend || src.independentBlend;
|
||||||
|
dest.geometryShader = dest.geometryShader || src.geometryShader;
|
||||||
|
dest.tessellationShader = dest.tessellationShader || src.tessellationShader;
|
||||||
|
dest.sampleRateShading = dest.sampleRateShading || src.sampleRateShading;
|
||||||
|
dest.dualSrcBlend = dest.dualSrcBlend || src.dualSrcBlend;
|
||||||
|
dest.logicOp = dest.logicOp || src.logicOp;
|
||||||
|
dest.multiDrawIndirect = dest.multiDrawIndirect || src.multiDrawIndirect;
|
||||||
|
dest.drawIndirectFirstInstance = dest.drawIndirectFirstInstance || src.drawIndirectFirstInstance;
|
||||||
|
dest.depthClamp = dest.depthClamp || src.depthClamp;
|
||||||
|
dest.depthBiasClamp = dest.depthBiasClamp || src.depthBiasClamp;
|
||||||
|
dest.fillModeNonSolid = dest.fillModeNonSolid || src.fillModeNonSolid;
|
||||||
|
dest.depthBounds = dest.depthBounds || src.depthBounds;
|
||||||
|
dest.wideLines = dest.wideLines || src.wideLines;
|
||||||
|
dest.largePoints = dest.largePoints || src.largePoints;
|
||||||
|
dest.alphaToOne = dest.alphaToOne || src.alphaToOne;
|
||||||
|
dest.multiViewport = dest.multiViewport || src.multiViewport;
|
||||||
|
dest.samplerAnisotropy = dest.samplerAnisotropy || src.samplerAnisotropy;
|
||||||
|
dest.textureCompressionETC2 = dest.textureCompressionETC2 || src.textureCompressionETC2;
|
||||||
|
dest.textureCompressionASTC_LDR = dest.textureCompressionASTC_LDR || src.textureCompressionASTC_LDR;
|
||||||
|
dest.textureCompressionBC = dest.textureCompressionBC || src.textureCompressionBC;
|
||||||
|
dest.occlusionQueryPrecise = dest.occlusionQueryPrecise || src.occlusionQueryPrecise;
|
||||||
|
dest.pipelineStatisticsQuery = dest.pipelineStatisticsQuery || src.pipelineStatisticsQuery;
|
||||||
|
dest.vertexPipelineStoresAndAtomics = dest.vertexPipelineStoresAndAtomics || src.vertexPipelineStoresAndAtomics;
|
||||||
|
dest.fragmentStoresAndAtomics = dest.fragmentStoresAndAtomics || src.fragmentStoresAndAtomics;
|
||||||
|
dest.shaderTessellationAndGeometryPointSize = dest.shaderTessellationAndGeometryPointSize || src.shaderTessellationAndGeometryPointSize;
|
||||||
|
dest.shaderImageGatherExtended = dest.shaderImageGatherExtended || src.shaderImageGatherExtended;
|
||||||
|
dest.shaderStorageImageExtendedFormats = dest.shaderStorageImageExtendedFormats || src.shaderStorageImageExtendedFormats;
|
||||||
|
dest.shaderStorageImageMultisample = dest.shaderStorageImageMultisample || src.shaderStorageImageMultisample;
|
||||||
|
dest.shaderStorageImageReadWithoutFormat = dest.shaderStorageImageReadWithoutFormat || src.shaderStorageImageReadWithoutFormat;
|
||||||
|
dest.shaderStorageImageWriteWithoutFormat = dest.shaderStorageImageWriteWithoutFormat || src.shaderStorageImageWriteWithoutFormat;
|
||||||
|
dest.shaderUniformBufferArrayDynamicIndexing = dest.shaderUniformBufferArrayDynamicIndexing || src.shaderUniformBufferArrayDynamicIndexing;
|
||||||
|
dest.shaderSampledImageArrayDynamicIndexing = dest.shaderSampledImageArrayDynamicIndexing || src.shaderSampledImageArrayDynamicIndexing;
|
||||||
|
dest.shaderStorageBufferArrayDynamicIndexing = dest.shaderStorageBufferArrayDynamicIndexing || src.shaderStorageBufferArrayDynamicIndexing;
|
||||||
|
dest.shaderStorageImageArrayDynamicIndexing = dest.shaderStorageImageArrayDynamicIndexing || src.shaderStorageImageArrayDynamicIndexing;
|
||||||
|
dest.shaderClipDistance = dest.shaderClipDistance || src.shaderClipDistance;
|
||||||
|
dest.shaderCullDistance = dest.shaderCullDistance || src.shaderCullDistance;
|
||||||
|
dest.shaderFloat64 = dest.shaderFloat64 || src.shaderFloat64;
|
||||||
|
dest.shaderInt64 = dest.shaderInt64 || src.shaderInt64;
|
||||||
|
dest.shaderInt16 = dest.shaderInt16 || src.shaderInt16;
|
||||||
|
dest.shaderResourceResidency = dest.shaderResourceResidency || src.shaderResourceResidency;
|
||||||
|
dest.shaderResourceMinLod = dest.shaderResourceMinLod || src.shaderResourceMinLod;
|
||||||
|
dest.sparseBinding = dest.sparseBinding || src.sparseBinding;
|
||||||
|
dest.sparseResidencyBuffer = dest.sparseResidencyBuffer || src.sparseResidencyBuffer;
|
||||||
|
dest.sparseResidencyImage2D = dest.sparseResidencyImage2D || src.sparseResidencyImage2D;
|
||||||
|
dest.sparseResidencyImage3D = dest.sparseResidencyImage3D || src.sparseResidencyImage3D;
|
||||||
|
dest.sparseResidency2Samples = dest.sparseResidency2Samples || src.sparseResidency2Samples;
|
||||||
|
dest.sparseResidency4Samples = dest.sparseResidency4Samples || src.sparseResidency4Samples;
|
||||||
|
dest.sparseResidency8Samples = dest.sparseResidency8Samples || src.sparseResidency8Samples;
|
||||||
|
dest.sparseResidency16Samples = dest.sparseResidency16Samples || src.sparseResidency16Samples;
|
||||||
|
dest.sparseResidencyAliased = dest.sparseResidencyAliased || src.sparseResidencyAliased;
|
||||||
|
dest.variableMultisampleRate = dest.variableMultisampleRate || src.variableMultisampleRate;
|
||||||
|
dest.inheritedQueries = dest.inheritedQueries || src.inheritedQueries;
|
||||||
|
}
|
||||||
|
|
||||||
bool supports_features(VkPhysicalDeviceFeatures supported,
|
bool supports_features(VkPhysicalDeviceFeatures supported,
|
||||||
VkPhysicalDeviceFeatures requested,
|
VkPhysicalDeviceFeatures requested,
|
||||||
std::vector<GenericFeaturesPNextNode> const& extension_supported,
|
GenericFeatureChain const& extension_supported,
|
||||||
std::vector<GenericFeaturesPNextNode> const& extension_requested) {
|
GenericFeatureChain const& extension_requested) {
|
||||||
if (requested.robustBufferAccess && !supported.robustBufferAccess) return false;
|
if (requested.robustBufferAccess && !supported.robustBufferAccess) return false;
|
||||||
if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) return false;
|
if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) return false;
|
||||||
if (requested.imageCubeArray && !supported.imageCubeArray) return false;
|
if (requested.imageCubeArray && !supported.imageCubeArray) return false;
|
||||||
@ -923,18 +1029,7 @@ bool supports_features(VkPhysicalDeviceFeatures supported,
|
|||||||
if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false;
|
if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false;
|
||||||
if (requested.inheritedQueries && !supported.inheritedQueries) return false;
|
if (requested.inheritedQueries && !supported.inheritedQueries) return false;
|
||||||
|
|
||||||
// Should only be false if extension_supported was unable to be filled out, due to the
|
return extension_supported.match(extension_requested);
|
||||||
// physical device not supporting vkGetPhysicalDeviceFeatures2 in any capacity.
|
|
||||||
if (extension_requested.size() != extension_supported.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < extension_requested.size() && i < extension_supported.size(); ++i) {
|
|
||||||
auto res = GenericFeaturesPNextNode::match(extension_requested[i], extension_supported[i]);
|
|
||||||
if(!res) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
// Finds the first queue which supports the desired operations. Returns QUEUE_INDEX_MAX_VALUE if none is found
|
// 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(
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice vk_phys_device,
|
PhysicalDevice PhysicalDeviceSelector::populate_device_details(
|
||||||
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const {
|
VkPhysicalDevice vk_phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const {
|
||||||
PhysicalDevice physical_device{};
|
PhysicalDevice physical_device{};
|
||||||
physical_device.physical_device = vk_phys_device;
|
physical_device.physical_device = vk_phys_device;
|
||||||
physical_device.surface = instance_info.surface;
|
physical_device.surface = instance_info.surface;
|
||||||
@ -1013,25 +1108,14 @@ PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice
|
|||||||
physical_device.available_extensions.push_back(&ext.extensionName[0]);
|
physical_device.available_extensions.push_back(&ext.extensionName[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
physical_device.features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; // same value as the non-KHR version
|
|
||||||
physical_device.properties2_ext_enabled = instance_info.properties2_ext_enabled;
|
physical_device.properties2_ext_enabled = instance_info.properties2_ext_enabled;
|
||||||
|
|
||||||
auto fill_chain = src_extended_features_chain;
|
auto fill_chain = src_extended_features_chain;
|
||||||
|
|
||||||
bool instance_is_1_1 = instance_info.version >= VKB_VK_API_VERSION_1_1;
|
bool instance_is_1_1 = instance_info.version >= VKB_VK_API_VERSION_1_1;
|
||||||
if (!fill_chain.empty() && (instance_is_1_1 || instance_info.properties2_ext_enabled)) {
|
if (!fill_chain.nodes.empty() && (instance_is_1_1 || instance_info.properties2_ext_enabled)) {
|
||||||
|
|
||||||
detail::GenericFeaturesPNextNode* prev = nullptr;
|
|
||||||
for (auto& extension : fill_chain) {
|
|
||||||
if (prev != nullptr) {
|
|
||||||
prev->pNext = &extension;
|
|
||||||
}
|
|
||||||
prev = &extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures2 local_features{};
|
VkPhysicalDeviceFeatures2 local_features{};
|
||||||
local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; // KHR is same as core here
|
fill_chain.chain_up(local_features);
|
||||||
local_features.pNext = &fill_chain.front();
|
|
||||||
// Use KHR function if not able to use the core function
|
// Use KHR function if not able to use the core function
|
||||||
if (instance_is_1_1) {
|
if (instance_is_1_1) {
|
||||||
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(vk_phys_device, &local_features);
|
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(vk_phys_device, &local_features);
|
||||||
@ -1135,7 +1219,7 @@ PhysicalDeviceSelector::PhysicalDeviceSelector(Instance const& instance, VkSurfa
|
|||||||
Result<std::vector<PhysicalDevice>> PhysicalDeviceSelector::select_impl(DeviceSelectionMode selection) const {
|
Result<std::vector<PhysicalDevice>> PhysicalDeviceSelector::select_impl(DeviceSelectionMode selection) const {
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
// Validation
|
// Validation
|
||||||
for (const auto& node : criteria.extended_features_chain) {
|
for (const auto& node : criteria.extended_features_chain.nodes) {
|
||||||
assert(node.sType != static_cast<VkStructureType>(0) &&
|
assert(node.sType != static_cast<VkStructureType>(0) &&
|
||||||
"Features struct sType must be filled with the struct's "
|
"Features struct sType must be filled with the struct's "
|
||||||
"corresponding VkStructureType enum");
|
"corresponding VkStructureType enum");
|
||||||
@ -1336,7 +1420,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::disable_portability_subset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysicalDeviceFeatures const& features) {
|
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysicalDeviceFeatures const& features) {
|
||||||
criteria.required_features = features;
|
detail::combine_features(criteria.required_features, features);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#if defined(VKB_VK_API_VERSION_1_2)
|
#if defined(VKB_VK_API_VERSION_1_2)
|
||||||
@ -1411,6 +1495,39 @@ bool PhysicalDevice::enable_extensions_if_present(const std::vector<const char*>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PhysicalDevice::enable_features_if_present(const VkPhysicalDeviceFeatures& features_to_enable) {
|
||||||
|
VkPhysicalDeviceFeatures actual_pdf{};
|
||||||
|
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures(physical_device, &actual_pdf);
|
||||||
|
|
||||||
|
bool required_features_supported = detail::supports_features(actual_pdf, features_to_enable, {}, {});
|
||||||
|
if (required_features_supported) {
|
||||||
|
detail::combine_features(features, features_to_enable);
|
||||||
|
}
|
||||||
|
return required_features_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PhysicalDevice::enable_features_node_if_present(detail::GenericFeaturesPNextNode const& node) {
|
||||||
|
VkPhysicalDeviceFeatures2 actual_pdf2{};
|
||||||
|
|
||||||
|
detail::GenericFeatureChain requested_features;
|
||||||
|
requested_features.nodes.push_back(node);
|
||||||
|
|
||||||
|
detail::GenericFeatureChain fill_chain = requested_features;
|
||||||
|
// Zero out supported features
|
||||||
|
memset(fill_chain.nodes.front().fields, UINT8_MAX, sizeof(VkBool32) * detail::GenericFeaturesPNextNode::field_capacity);
|
||||||
|
|
||||||
|
actual_pdf2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||||
|
fill_chain.chain_up(actual_pdf2);
|
||||||
|
|
||||||
|
detail::vulkan_functions().fp_vkGetPhysicalDeviceFeatures2(physical_device, &actual_pdf2);
|
||||||
|
bool required_features_supported = detail::supports_features({}, {}, fill_chain, requested_features);
|
||||||
|
if (required_features_supported) {
|
||||||
|
extended_features_chain.combine(requested_features);
|
||||||
|
}
|
||||||
|
return required_features_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; }
|
PhysicalDevice::operator VkPhysicalDevice() const { return this->physical_device; }
|
||||||
|
|
||||||
// ---- Queues ---- //
|
// ---- Queues ---- //
|
||||||
@ -1527,7 +1644,7 @@ Result<Device> DeviceBuilder::build() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_defined_phys_dev_features_2 && !physical_device.extended_features_chain.empty()) {
|
if (user_defined_phys_dev_features_2 && !physical_device.extended_features_chain.nodes.empty()) {
|
||||||
return { DeviceError::VkPhysicalDeviceFeatures2_in_pNext_chain_while_using_add_required_extension_features };
|
return { DeviceError::VkPhysicalDeviceFeatures2_in_pNext_chain_while_using_add_required_extension_features };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1535,12 +1652,12 @@ Result<Device> DeviceBuilder::build() const {
|
|||||||
auto physical_device_extension_features_copy = physical_device.extended_features_chain;
|
auto physical_device_extension_features_copy = physical_device.extended_features_chain;
|
||||||
VkPhysicalDeviceFeatures2 local_features2{};
|
VkPhysicalDeviceFeatures2 local_features2{};
|
||||||
local_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
local_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||||
|
local_features2.features = physical_device.features;
|
||||||
|
|
||||||
if (!user_defined_phys_dev_features_2) {
|
if (!user_defined_phys_dev_features_2) {
|
||||||
if (physical_device.instance_version >= VKB_VK_API_VERSION_1_1 || physical_device.properties2_ext_enabled) {
|
if (physical_device.instance_version >= VKB_VK_API_VERSION_1_1 || physical_device.properties2_ext_enabled) {
|
||||||
local_features2.features = physical_device.features;
|
|
||||||
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&local_features2));
|
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&local_features2));
|
||||||
for (auto& features_node : physical_device_extension_features_copy) {
|
for (auto& features_node : physical_device_extension_features_copy.nodes) {
|
||||||
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&features_node));
|
final_pnext_chain.push_back(reinterpret_cast<VkBaseOutStructure*>(&features_node));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -172,11 +172,35 @@ struct GenericFeaturesPNextNode {
|
|||||||
|
|
||||||
static bool match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept;
|
static bool match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept;
|
||||||
|
|
||||||
|
void combine(GenericFeaturesPNextNode const& right) noexcept;
|
||||||
|
|
||||||
VkStructureType sType = static_cast<VkStructureType>(0);
|
VkStructureType sType = static_cast<VkStructureType>(0);
|
||||||
void* pNext = nullptr;
|
void* pNext = nullptr;
|
||||||
VkBool32 fields[field_capacity];
|
VkBool32 fields[field_capacity];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GenericFeatureChain {
|
||||||
|
std::vector<GenericFeaturesPNextNode> nodes;
|
||||||
|
|
||||||
|
template <typename T> void add(T const& features) noexcept {
|
||||||
|
// If this struct is already in the list, combine it
|
||||||
|
for (auto& node : nodes) {
|
||||||
|
if (features.sType == node.sType) {
|
||||||
|
node.combine(features);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise append to the end
|
||||||
|
nodes.push_back(features);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match(GenericFeatureChain const& extension_requested) const noexcept;
|
||||||
|
|
||||||
|
void chain_up(VkPhysicalDeviceFeatures2& feats2) noexcept;
|
||||||
|
|
||||||
|
void combine(GenericFeatureChain const& right) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
enum class InstanceError {
|
enum class InstanceError {
|
||||||
@ -512,6 +536,16 @@ struct PhysicalDevice {
|
|||||||
// Returns true if all the extensions are present.
|
// Returns true if all the extensions are present.
|
||||||
bool enable_extensions_if_present(const std::vector<const char*>& extensions);
|
bool enable_extensions_if_present(const std::vector<const char*>& extensions);
|
||||||
|
|
||||||
|
// If the features from VkPhysicalDeviceFeatures are all present, make all of the features be enable on the device.
|
||||||
|
// Returns true all of the features are present.
|
||||||
|
bool enable_features_if_present(const VkPhysicalDeviceFeatures& features_to_enable);
|
||||||
|
|
||||||
|
// If the features from the provided features struct are all present, make all of the features be enable on the
|
||||||
|
// device. Returns true all of the features are present.
|
||||||
|
template <typename T> bool enable_extension_features_if_present(T const& features) {
|
||||||
|
return enable_features_node_if_present(detail::GenericFeaturesPNextNode(features));
|
||||||
|
}
|
||||||
|
|
||||||
// A conversion function which allows this PhysicalDevice to be used
|
// A conversion function which allows this PhysicalDevice to be used
|
||||||
// in places where VkPhysicalDevice would have been used.
|
// in places where VkPhysicalDevice would have been used.
|
||||||
operator VkPhysicalDevice() const;
|
operator VkPhysicalDevice() const;
|
||||||
@ -521,8 +555,7 @@ struct PhysicalDevice {
|
|||||||
std::vector<std::string> extensions_to_enable;
|
std::vector<std::string> extensions_to_enable;
|
||||||
std::vector<std::string> available_extensions;
|
std::vector<std::string> available_extensions;
|
||||||
std::vector<VkQueueFamilyProperties> queue_families;
|
std::vector<VkQueueFamilyProperties> queue_families;
|
||||||
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
|
detail::GenericFeatureChain extended_features_chain;
|
||||||
VkPhysicalDeviceFeatures2 features2{};
|
|
||||||
|
|
||||||
bool defer_surface_initialization = false;
|
bool defer_surface_initialization = false;
|
||||||
bool properties2_ext_enabled = false;
|
bool properties2_ext_enabled = false;
|
||||||
@ -530,6 +563,8 @@ struct PhysicalDevice {
|
|||||||
Suitable suitable = Suitable::yes;
|
Suitable suitable = Suitable::yes;
|
||||||
friend class PhysicalDeviceSelector;
|
friend class PhysicalDeviceSelector;
|
||||||
friend class DeviceBuilder;
|
friend class DeviceBuilder;
|
||||||
|
|
||||||
|
bool enable_features_node_if_present(detail::GenericFeaturesPNextNode const& node);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PreferredDeviceType { other = 0, integrated = 1, discrete = 2, virtual_gpu = 3, cpu = 4 };
|
enum class PreferredDeviceType { other = 0, integrated = 1, discrete = 2, virtual_gpu = 3, cpu = 4 };
|
||||||
@ -620,7 +655,7 @@ class PhysicalDeviceSelector {
|
|||||||
// If this function is used, the user should not put their own VkPhysicalDeviceFeatures2 in
|
// If this function is used, the user should not put their own VkPhysicalDeviceFeatures2 in
|
||||||
// the pNext chain of VkDeviceCreateInfo.
|
// the pNext chain of VkDeviceCreateInfo.
|
||||||
template <typename T> PhysicalDeviceSelector& add_required_extension_features(T const& features) {
|
template <typename T> PhysicalDeviceSelector& add_required_extension_features(T const& features) {
|
||||||
criteria.extended_features_chain.push_back(features);
|
criteria.extended_features_chain.add(features);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,14 +716,14 @@ class PhysicalDeviceSelector {
|
|||||||
VkPhysicalDeviceFeatures required_features{};
|
VkPhysicalDeviceFeatures required_features{};
|
||||||
VkPhysicalDeviceFeatures2 required_features2{};
|
VkPhysicalDeviceFeatures2 required_features2{};
|
||||||
|
|
||||||
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
|
detail::GenericFeatureChain extended_features_chain;
|
||||||
bool defer_surface_initialization = false;
|
bool defer_surface_initialization = false;
|
||||||
bool use_first_gpu_unconditionally = false;
|
bool use_first_gpu_unconditionally = false;
|
||||||
bool enable_portability_subset = true;
|
bool enable_portability_subset = true;
|
||||||
} criteria;
|
} criteria;
|
||||||
|
|
||||||
PhysicalDevice populate_device_details(VkPhysicalDevice phys_device,
|
PhysicalDevice populate_device_details(
|
||||||
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const;
|
VkPhysicalDevice phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const;
|
||||||
|
|
||||||
PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const;
|
PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
target_link_libraries(VulkanMock
|
target_link_libraries(VulkanMock
|
||||||
PUBLIC
|
PUBLIC
|
||||||
Vulkan::Headers
|
Vulkan::Headers vk-bootstrap
|
||||||
PRIVATE
|
PRIVATE
|
||||||
vk-bootstrap-compiler-warnings
|
vk-bootstrap-compiler-warnings
|
||||||
)
|
)
|
||||||
|
@ -599,7 +599,6 @@ TEST_CASE("Querying Required Extension Features but with 1.0", "[VkBootstrap.sel
|
|||||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||||
.add_required_extension_features(descriptor_indexing_features)
|
.add_required_extension_features(descriptor_indexing_features)
|
||||||
.select();
|
.select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
@ -631,7 +630,6 @@ TEST_CASE("Querying Required Extension Features", "[VkBootstrap.select_features]
|
|||||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||||
.add_required_extension_features(descriptor_indexing_features)
|
.add_required_extension_features(descriptor_indexing_features)
|
||||||
.select();
|
.select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
@ -643,6 +641,102 @@ TEST_CASE("Querying Required Extension Features", "[VkBootstrap.select_features]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Adding Optional Extension Features", "[VkBootstrap.enable_features_if_present]") {
|
||||||
|
VulkanMock& mock = get_and_setup_default();
|
||||||
|
mock.api_version = VK_API_VERSION_1_1;
|
||||||
|
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
|
||||||
|
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_get_physical_device_properties2"));
|
||||||
|
auto vulkan_10_features = VkPhysicalDeviceFeatures{};
|
||||||
|
vulkan_10_features.multiViewport = true;
|
||||||
|
mock.physical_devices_details[0].features = vulkan_10_features;
|
||||||
|
|
||||||
|
auto vulkan_11_features = VkPhysicalDeviceVulkan11Features{};
|
||||||
|
vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
vulkan_11_features.shaderDrawParameters = true;
|
||||||
|
mock.physical_devices_details[0].add_features_pNext_struct(vulkan_11_features);
|
||||||
|
|
||||||
|
auto vulkan_12_features = VkPhysicalDeviceVulkan12Features{};
|
||||||
|
vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||||
|
vulkan_12_features.bufferDeviceAddress = true;
|
||||||
|
mock.physical_devices_details[0].add_features_pNext_struct(vulkan_12_features);
|
||||||
|
|
||||||
|
|
||||||
|
GIVEN("A working instance and physical device which has a VkPhysicalDeviceVulkan12Features in its features pNext "
|
||||||
|
"chain") {
|
||||||
|
auto instance = get_headless_instance();
|
||||||
|
|
||||||
|
VkPhysicalDeviceVulkan12Features physical_device_features_12{};
|
||||||
|
physical_device_features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||||
|
physical_device_features_12.bufferDeviceAddress = true;
|
||||||
|
|
||||||
|
vkb::PhysicalDeviceSelector selector(instance);
|
||||||
|
selector.add_required_extension_features(physical_device_features_12);
|
||||||
|
{
|
||||||
|
|
||||||
|
SECTION("Require enable_features_if_present to work with an empty feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceFeatures phys_dev_features_empty{};
|
||||||
|
REQUIRE(phys_dev.enable_features_if_present(phys_dev_features_empty));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
SECTION("Require enable_features_if_present to fail with an unsupported feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceFeatures phys_dev_features_bad{};
|
||||||
|
phys_dev_features_bad.depthClamp = true;
|
||||||
|
REQUIRE(!phys_dev.enable_features_if_present(phys_dev_features_bad));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
REQUIRE(!mock.physical_devices_details.at(0).created_device_details.at(0).features.depthClamp);
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
SECTION("Require enable_features_if_present to work with a supported feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceFeatures phys_dev_features_good{};
|
||||||
|
phys_dev_features_good.multiViewport = true;
|
||||||
|
REQUIRE(phys_dev.enable_features_if_present(phys_dev_features_good));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
REQUIRE(mock.physical_devices_details.at(0).created_device_details.at(0).features.multiViewport);
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Require enable_extension_features_if_present to work with an empty 1.1 feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceVulkan11Features phys_dev_vulkan_11_features{};
|
||||||
|
phys_dev_vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
REQUIRE(phys_dev.enable_extension_features_if_present(phys_dev_vulkan_11_features));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
SECTION("Require enable_extension_features_if_present to fail with an unsupported 1.1 feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceVulkan11Features phys_dev_vulkan_11_features{};
|
||||||
|
phys_dev_vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
phys_dev_vulkan_11_features.multiview = true;
|
||||||
|
REQUIRE(!phys_dev.enable_extension_features_if_present(phys_dev_vulkan_11_features));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
auto* s = reinterpret_cast<VkPhysicalDeviceVulkan12Features*>(
|
||||||
|
&mock.physical_devices_details.at(0).created_device_details.at(0).features_pNextChain.at(0));
|
||||||
|
REQUIRE(s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES);
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
SECTION("Require enable_extension_features_if_present to work with a supported 1.1 feature struct") {
|
||||||
|
auto phys_dev = selector.select().value();
|
||||||
|
VkPhysicalDeviceVulkan11Features phys_dev_vulkan_11_features{};
|
||||||
|
phys_dev_vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
phys_dev_vulkan_11_features.shaderDrawParameters = true;
|
||||||
|
REQUIRE(phys_dev.enable_extension_features_if_present(phys_dev_vulkan_11_features));
|
||||||
|
auto device = vkb::DeviceBuilder(phys_dev).build().value();
|
||||||
|
auto* s = reinterpret_cast<VkPhysicalDeviceVulkan11Features*>(
|
||||||
|
&mock.physical_devices_details.at(0).created_device_details.at(0).features_pNextChain.at(1));
|
||||||
|
REQUIRE(s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES);
|
||||||
|
REQUIRE(s->shaderDrawParameters);
|
||||||
|
vkb::destroy_device(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vkb::destroy_instance(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_handle]") {
|
TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_handle]") {
|
||||||
VulkanMock& mock = get_and_setup_default();
|
VulkanMock& mock = get_and_setup_default();
|
||||||
auto surface = mock.get_new_surface(get_basic_surface_details());
|
auto surface = mock.get_new_surface(get_basic_surface_details());
|
||||||
@ -687,7 +781,6 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]"
|
|||||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||||
.add_required_extension_features(descriptor_indexing_features)
|
.add_required_extension_features(descriptor_indexing_features)
|
||||||
.select();
|
.select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
@ -705,7 +798,6 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]"
|
|||||||
auto phys_dev_ret = selector.add_required_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
|
auto phys_dev_ret = selector.add_required_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
|
||||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||||
.select();
|
.select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures2 phys_dev_feats2{};
|
VkPhysicalDeviceFeatures2 phys_dev_feats2{};
|
||||||
@ -728,7 +820,6 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]"
|
|||||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||||
.add_required_extension_features(descriptor_indexing_features)
|
.add_required_extension_features(descriptor_indexing_features)
|
||||||
.select();
|
.select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures2 phys_dev_feats2{};
|
VkPhysicalDeviceFeatures2 phys_dev_feats2{};
|
||||||
@ -765,17 +856,23 @@ TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") {
|
|||||||
features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
features_11.multiview = true;
|
features_11.multiview = true;
|
||||||
VkPhysicalDeviceVulkan12Features features_12{};
|
VkPhysicalDeviceVulkan12Features features_12{};
|
||||||
features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||||
features_12.bufferDeviceAddress = true;
|
features_12.bufferDeviceAddress = true;
|
||||||
|
|
||||||
vkb::PhysicalDeviceSelector selector(instance);
|
vkb::PhysicalDeviceSelector selector(instance);
|
||||||
auto phys_dev_ret = selector.set_required_features_11(features_11).set_required_features_12(features_12).select();
|
auto phys_dev_ret = selector.set_required_features_11(features_11).set_required_features_12(features_12).select();
|
||||||
// Ignore if hardware support isn't true
|
|
||||||
REQUIRE(phys_dev_ret.has_value());
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
auto device_ret = device_builder.build();
|
auto device_ret = device_builder.build();
|
||||||
REQUIRE(device_ret.has_value());
|
REQUIRE(device_ret.has_value());
|
||||||
|
auto* s1 = reinterpret_cast<VkPhysicalDeviceVulkan11Features*>(
|
||||||
|
&mock.physical_devices_details.at(0).created_device_details.at(0).features_pNextChain.at(0));
|
||||||
|
REQUIRE(s1->multiview);
|
||||||
|
auto* s2 = reinterpret_cast<VkPhysicalDeviceVulkan12Features*>(
|
||||||
|
&mock.physical_devices_details.at(0).created_device_details.at(0).features_pNextChain.at(1));
|
||||||
|
REQUIRE(s2->bufferDeviceAddress);
|
||||||
|
|
||||||
vkb::destroy_device(device_ret.value());
|
vkb::destroy_device(device_ret.value());
|
||||||
}
|
}
|
||||||
mock.api_version = VK_API_VERSION_1_1;
|
mock.api_version = VK_API_VERSION_1_1;
|
||||||
@ -787,9 +884,80 @@ TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") {
|
|||||||
|
|
||||||
vkb::PhysicalDeviceSelector selector(instance);
|
vkb::PhysicalDeviceSelector selector(instance);
|
||||||
auto phys_dev_ret = selector.set_required_features_11(features_11).select();
|
auto phys_dev_ret = selector.set_required_features_11(features_11).select();
|
||||||
// Ignore if hardware support differs
|
|
||||||
REQUIRE(!phys_dev_ret.has_value());
|
REQUIRE(!phys_dev_ret.has_value());
|
||||||
}
|
}
|
||||||
vkb::destroy_instance(instance);
|
vkb::destroy_instance(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Add required features in multiple calls", "[VkBootstrap.required_features]") {
|
||||||
|
VulkanMock& mock = get_and_setup_default();
|
||||||
|
mock.api_version = VK_API_VERSION_1_2;
|
||||||
|
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2;
|
||||||
|
mock.physical_devices_details[0].features.independentBlend = true;
|
||||||
|
mock.physical_devices_details[0].features.shaderInt64 = true;
|
||||||
|
|
||||||
|
GIVEN("A working instance") {
|
||||||
|
auto instance = get_headless_instance(1); // make sure we use 1.1
|
||||||
|
SECTION("Requires a device that supports independentBlend and shaderInt64") {
|
||||||
|
VkPhysicalDeviceFeatures features1{};
|
||||||
|
features1.independentBlend = true;
|
||||||
|
VkPhysicalDeviceFeatures features2{};
|
||||||
|
features2.shaderInt64 = true;
|
||||||
|
|
||||||
|
vkb::PhysicalDeviceSelector selector(instance);
|
||||||
|
auto phys_dev_ret = selector.set_required_features(features1).set_required_features(features2).select();
|
||||||
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
|
auto device_ret = device_builder.build();
|
||||||
|
REQUIRE(device_ret.has_value());
|
||||||
|
|
||||||
|
REQUIRE(mock.physical_devices_details.at(0).created_device_details.at(0).features.independentBlend);
|
||||||
|
REQUIRE(mock.physical_devices_details.at(0).created_device_details.at(0).features.shaderInt64);
|
||||||
|
|
||||||
|
vkb::destroy_device(device_ret.value());
|
||||||
|
}
|
||||||
|
vkb::destroy_instance(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Add required extension features in multiple calls", "[VkBootstrap.required_features]") {
|
||||||
|
VulkanMock& mock = get_and_setup_default();
|
||||||
|
mock.api_version = VK_API_VERSION_1_2;
|
||||||
|
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2;
|
||||||
|
|
||||||
|
auto mock_vulkan_11_features = VkPhysicalDeviceVulkan11Features{};
|
||||||
|
mock_vulkan_11_features.multiview = true;
|
||||||
|
mock_vulkan_11_features.samplerYcbcrConversion = true;
|
||||||
|
mock.physical_devices_details[0].add_features_pNext_struct(mock_vulkan_11_features);
|
||||||
|
|
||||||
|
GIVEN("A working instance") {
|
||||||
|
auto instance = get_headless_instance(1); // make sure we use 1.1
|
||||||
|
SECTION("Requires a device that supports multiview and samplerYcbcrConversion") {
|
||||||
|
VkPhysicalDeviceVulkan11Features features1{};
|
||||||
|
features1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
features1.multiview = true;
|
||||||
|
|
||||||
|
VkPhysicalDeviceVulkan11Features features2{};
|
||||||
|
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||||
|
features2.samplerYcbcrConversion = true;
|
||||||
|
|
||||||
|
|
||||||
|
vkb::PhysicalDeviceSelector selector(instance);
|
||||||
|
auto phys_dev_ret = selector.set_required_features_11(features1).set_required_features_11(features2).select();
|
||||||
|
REQUIRE(phys_dev_ret.has_value());
|
||||||
|
|
||||||
|
vkb::DeviceBuilder device_builder(phys_dev_ret.value());
|
||||||
|
auto device_ret = device_builder.build();
|
||||||
|
REQUIRE(device_ret.has_value());
|
||||||
|
auto* s1 = reinterpret_cast<VkPhysicalDeviceVulkan11Features*>(
|
||||||
|
&mock.physical_devices_details.at(0).created_device_details.at(0).features_pNextChain.at(0));
|
||||||
|
REQUIRE(s1->multiview);
|
||||||
|
REQUIRE(s1->samplerYcbcrConversion);
|
||||||
|
|
||||||
|
vkb::destroy_device(device_ret.value());
|
||||||
|
}
|
||||||
|
vkb::destroy_instance(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -138,13 +138,17 @@ VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceMemoryProperties(
|
|||||||
}
|
}
|
||||||
|
|
||||||
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
|
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
|
||||||
|
pFeatures->features = get_physical_device_details(physicalDevice).features;
|
||||||
const auto& phys_dev = get_physical_device_details(physicalDevice);
|
const auto& phys_dev = get_physical_device_details(physicalDevice);
|
||||||
VkBaseOutStructure* current = static_cast<VkBaseOutStructure*>(pFeatures->pNext);
|
VkBaseOutStructure* current = static_cast<VkBaseOutStructure*>(pFeatures->pNext);
|
||||||
while (current) {
|
while (current) {
|
||||||
for (const auto& features_pNext : phys_dev.features_pNextChain) {
|
for (const auto& features_pNext : phys_dev.features_pNextChain) {
|
||||||
if (features_pNext->sType == current->sType) {
|
if (features_pNext.sType == current->sType) {
|
||||||
VkBaseOutStructure* next = static_cast<VkBaseOutStructure*>(current->pNext);
|
VkBaseOutStructure* next = static_cast<VkBaseOutStructure*>(current->pNext);
|
||||||
std::memcpy(current, features_pNext.get(), get_pnext_chain_struct_size(features_pNext->sType));
|
std::memcpy(static_cast<void*>(current),
|
||||||
|
static_cast<const void*>(&features_pNext),
|
||||||
|
get_pnext_chain_struct_size(features_pNext.sType));
|
||||||
|
// Repair pNext void* since we clobbered it in the memcpy
|
||||||
current->pNext = next;
|
current->pNext = next;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -179,27 +183,56 @@ VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateDevice(VkPhysicalDevice physicalDevi
|
|||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
return VK_ERROR_INITIALIZATION_FAILED;
|
||||||
}
|
}
|
||||||
*pDevice = get_handle<VkDevice>(0x0000ABCDU);
|
*pDevice = get_handle<VkDevice>(0x0000ABCDU);
|
||||||
get_physical_device_details(physicalDevice).created_devices.push_back(*pDevice);
|
auto& physical_device_details = get_physical_device_details(physicalDevice);
|
||||||
|
physical_device_details.created_device_handles.push_back(*pDevice);
|
||||||
|
VkPhysicalDeviceFeatures new_feats{};
|
||||||
|
if (pCreateInfo->pEnabledFeatures) {
|
||||||
|
new_feats = *pCreateInfo->pEnabledFeatures;
|
||||||
|
}
|
||||||
|
std::vector<vkb::detail::GenericFeaturesPNextNode> new_chain;
|
||||||
|
std::vector<const char*> created_extensions;
|
||||||
|
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
|
||||||
|
created_extensions.push_back(pCreateInfo->ppEnabledExtensionNames[i]);
|
||||||
|
}
|
||||||
|
const void* pNext_chain = pCreateInfo->pNext;
|
||||||
|
while (pNext_chain) {
|
||||||
|
const auto* chain = static_cast<const VkBaseOutStructure*>(pNext_chain);
|
||||||
|
const void* next = chain->pNext;
|
||||||
|
if (check_if_features2_struct(chain->sType) > 0) {
|
||||||
|
vkb::detail::GenericFeaturesPNextNode node;
|
||||||
|
std::memcpy(static_cast<void*>(&node), pNext_chain, get_pnext_chain_struct_size(chain->sType));
|
||||||
|
new_chain.push_back(node);
|
||||||
|
}
|
||||||
|
if (chain->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {
|
||||||
|
new_feats = static_cast<const VkPhysicalDeviceFeatures2*>(pNext_chain)->features;
|
||||||
|
}
|
||||||
|
pNext_chain = next;
|
||||||
|
}
|
||||||
|
physical_device_details.created_device_details.push_back({ new_feats, created_extensions, new_chain });
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyDevice(
|
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyDevice(
|
||||||
[[maybe_unused]] VkDevice device, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {
|
[[maybe_unused]] VkDevice device, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {
|
||||||
for (auto& physical_devices : mock.physical_devices_details) {
|
for (auto& physical_devices : mock.physical_devices_details) {
|
||||||
auto it = std::find(std::begin(physical_devices.created_devices), std::end(physical_devices.created_devices), device);
|
auto it = std::find(
|
||||||
if (it != std::end(physical_devices.created_devices)) {
|
std::begin(physical_devices.created_device_handles), std::end(physical_devices.created_device_handles), device);
|
||||||
physical_devices.created_devices.erase(it);
|
if (it != std::end(physical_devices.created_device_handles)) {
|
||||||
|
auto index = it - physical_devices.created_device_handles.begin();
|
||||||
|
physical_devices.created_device_handles.erase(it);
|
||||||
|
physical_devices.created_device_details.erase(physical_devices.created_device_details.begin() + index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VKAPI_ATTR void VKAPI_CALL shim_vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) {
|
VKAPI_ATTR void VKAPI_CALL shim_vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) {
|
||||||
for (auto& physical_devices : mock.physical_devices_details) {
|
for (auto& physical_devices : mock.physical_devices_details) {
|
||||||
auto it = std::find(std::begin(physical_devices.created_devices), std::end(physical_devices.created_devices), device);
|
auto it = std::find(
|
||||||
if (it != std::end(physical_devices.created_devices)) {
|
std::begin(physical_devices.created_device_handles), std::end(physical_devices.created_device_handles), device);
|
||||||
|
if (it != std::end(physical_devices.created_device_handles)) {
|
||||||
if (queueFamilyIndex < physical_devices.queue_family_properties.size() &&
|
if (queueFamilyIndex < physical_devices.queue_family_properties.size() &&
|
||||||
queueIndex < physical_devices.queue_family_properties[queueFamilyIndex].queueCount) {
|
queueIndex < physical_devices.queue_family_properties[queueFamilyIndex].queueCount) {
|
||||||
*pQueue = get_handle<VkQueue>(0x0000CCEEU);
|
*pQueue = get_handle<VkQueue>(0x0000CCEEU + queueIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
// Helper function to get the size of a struct given a VkStructureType
|
#include <VkBootstrap.h>
|
||||||
|
|
||||||
|
// Helper function to return the size of the sType if it is a known features struct, otherwise return 0
|
||||||
// Hand written, must be updated to include any used struct.
|
// Hand written, must be updated to include any used struct.
|
||||||
inline size_t get_pnext_chain_struct_size(VkStructureType type) {
|
inline size_t check_if_features2_struct(VkStructureType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES):
|
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES):
|
||||||
return sizeof(VkPhysicalDeviceDescriptorIndexingFeatures);
|
return sizeof(VkPhysicalDeviceDescriptorIndexingFeatures);
|
||||||
@ -21,9 +23,14 @@ inline size_t get_pnext_chain_struct_size(VkStructureType type) {
|
|||||||
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES):
|
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES):
|
||||||
return sizeof(VkPhysicalDeviceVulkan12Features);
|
return sizeof(VkPhysicalDeviceVulkan12Features);
|
||||||
default:
|
default:
|
||||||
assert(false && "Must update get_pnext_chain_struct_size(VkStructureType type) to add type!");
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t get_pnext_chain_struct_size(VkStructureType type) {
|
||||||
|
auto size = check_if_features2_struct(type);
|
||||||
|
assert(size > 0 && "Must update get_pnext_chain_struct_size(VkStructureType type) to add type!");
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> T get_handle(size_t value) { return reinterpret_cast<T>(value); }
|
template <typename T> T get_handle(size_t value) { return reinterpret_cast<T>(value); }
|
||||||
@ -65,21 +72,24 @@ struct VulkanMock {
|
|||||||
return surface_handles.back();
|
return surface_handles.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CreatedDeviceDetails {
|
||||||
|
VkPhysicalDeviceFeatures features{};
|
||||||
|
std::vector<const char*> extensions;
|
||||||
|
std::vector<vkb::detail::GenericFeaturesPNextNode> features_pNextChain;
|
||||||
|
};
|
||||||
|
|
||||||
struct PhysicalDeviceDetails {
|
struct PhysicalDeviceDetails {
|
||||||
VkPhysicalDeviceProperties properties{};
|
VkPhysicalDeviceProperties properties{};
|
||||||
VkPhysicalDeviceFeatures features{};
|
VkPhysicalDeviceFeatures features{};
|
||||||
VkPhysicalDeviceMemoryProperties memory_properties{};
|
VkPhysicalDeviceMemoryProperties memory_properties{};
|
||||||
std::vector<VkExtensionProperties> extensions;
|
std::vector<VkExtensionProperties> extensions;
|
||||||
std::vector<VkQueueFamilyProperties> queue_family_properties;
|
std::vector<VkQueueFamilyProperties> queue_family_properties;
|
||||||
std::vector<std::unique_ptr<VkBaseOutStructure>> features_pNextChain;
|
std::vector<vkb::detail::GenericFeaturesPNextNode> features_pNextChain;
|
||||||
|
|
||||||
std::vector<VkDevice> created_devices;
|
std::vector<VkDevice> created_device_handles;
|
||||||
|
std::vector<CreatedDeviceDetails> created_device_details;
|
||||||
|
|
||||||
template <typename T> void add_features_pNext_struct(T t) {
|
template <typename T> void add_features_pNext_struct(T features) { features_pNextChain.push_back(features); }
|
||||||
T* new_type = new T();
|
|
||||||
*new_type = t;
|
|
||||||
features_pNextChain.emplace_back(reinterpret_cast<VkBaseOutStructure*>(new_type));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<VkPhysicalDevice> physical_device_handles;
|
std::vector<VkPhysicalDevice> physical_device_handles;
|
||||||
|
Loading…
Reference in New Issue
Block a user