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;
|
||||
}
|
||||
|
||||
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 {
|
||||
private:
|
||||
std::mutex init_mutex;
|
||||
@ -863,10 +911,68 @@ std::vector<std::string> check_device_extension_support(
|
||||
}
|
||||
|
||||
// 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,
|
||||
VkPhysicalDeviceFeatures requested,
|
||||
std::vector<GenericFeaturesPNextNode> const& extension_supported,
|
||||
std::vector<GenericFeaturesPNextNode> const& extension_requested) {
|
||||
GenericFeatureChain const& extension_supported,
|
||||
GenericFeatureChain const& extension_requested) {
|
||||
if (requested.robustBufferAccess && !supported.robustBufferAccess) return false;
|
||||
if (requested.fullDrawIndexUint32 && !supported.fullDrawIndexUint32) 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.inheritedQueries && !supported.inheritedQueries) return false;
|
||||
|
||||
// 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.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;
|
||||
return extension_supported.match(extension_requested);
|
||||
}
|
||||
// clang-format on
|
||||
// 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
|
||||
|
||||
PhysicalDevice PhysicalDeviceSelector::populate_device_details(VkPhysicalDevice vk_phys_device,
|
||||
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const {
|
||||
PhysicalDevice PhysicalDeviceSelector::populate_device_details(
|
||||
VkPhysicalDevice vk_phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const {
|
||||
PhysicalDevice physical_device{};
|
||||
physical_device.physical_device = vk_phys_device;
|
||||
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.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;
|
||||
|
||||
auto fill_chain = src_extended_features_chain;
|
||||
|
||||
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)) {
|
||||
|
||||
detail::GenericFeaturesPNextNode* prev = nullptr;
|
||||
for (auto& extension : fill_chain) {
|
||||
if (prev != nullptr) {
|
||||
prev->pNext = &extension;
|
||||
}
|
||||
prev = &extension;
|
||||
}
|
||||
|
||||
if (!fill_chain.nodes.empty() && (instance_is_1_1 || instance_info.properties2_ext_enabled)) {
|
||||
VkPhysicalDeviceFeatures2 local_features{};
|
||||
local_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; // KHR is same as core here
|
||||
local_features.pNext = &fill_chain.front();
|
||||
fill_chain.chain_up(local_features);
|
||||
// Use KHR function if not able to use the core function
|
||||
if (instance_is_1_1) {
|
||||
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 {
|
||||
#if !defined(NDEBUG)
|
||||
// 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) &&
|
||||
"Features struct sType must be filled with the struct's "
|
||||
"corresponding VkStructureType enum");
|
||||
@ -1336,7 +1420,7 @@ PhysicalDeviceSelector& PhysicalDeviceSelector::disable_portability_subset() {
|
||||
}
|
||||
|
||||
PhysicalDeviceSelector& PhysicalDeviceSelector::set_required_features(VkPhysicalDeviceFeatures const& features) {
|
||||
criteria.required_features = features;
|
||||
detail::combine_features(criteria.required_features, features);
|
||||
return *this;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
// ---- 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 };
|
||||
}
|
||||
|
||||
@ -1535,12 +1652,12 @@ Result<Device> DeviceBuilder::build() const {
|
||||
auto physical_device_extension_features_copy = physical_device.extended_features_chain;
|
||||
VkPhysicalDeviceFeatures2 local_features2{};
|
||||
local_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
local_features2.features = physical_device.features;
|
||||
|
||||
if (!user_defined_phys_dev_features_2) {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
|
@ -172,11 +172,35 @@ struct GenericFeaturesPNextNode {
|
||||
|
||||
static bool match(GenericFeaturesPNextNode const& requested, GenericFeaturesPNextNode const& supported) noexcept;
|
||||
|
||||
void combine(GenericFeaturesPNextNode const& right) noexcept;
|
||||
|
||||
VkStructureType sType = static_cast<VkStructureType>(0);
|
||||
void* pNext = nullptr;
|
||||
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
|
||||
|
||||
enum class InstanceError {
|
||||
@ -512,6 +536,16 @@ struct PhysicalDevice {
|
||||
// Returns true if all the extensions are present.
|
||||
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
|
||||
// in places where VkPhysicalDevice would have been used.
|
||||
operator VkPhysicalDevice() const;
|
||||
@ -521,8 +555,7 @@ struct PhysicalDevice {
|
||||
std::vector<std::string> extensions_to_enable;
|
||||
std::vector<std::string> available_extensions;
|
||||
std::vector<VkQueueFamilyProperties> queue_families;
|
||||
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
|
||||
VkPhysicalDeviceFeatures2 features2{};
|
||||
detail::GenericFeatureChain extended_features_chain;
|
||||
|
||||
bool defer_surface_initialization = false;
|
||||
bool properties2_ext_enabled = false;
|
||||
@ -530,6 +563,8 @@ struct PhysicalDevice {
|
||||
Suitable suitable = Suitable::yes;
|
||||
friend class PhysicalDeviceSelector;
|
||||
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 };
|
||||
@ -620,7 +655,7 @@ class PhysicalDeviceSelector {
|
||||
// If this function is used, the user should not put their own VkPhysicalDeviceFeatures2 in
|
||||
// the pNext chain of VkDeviceCreateInfo.
|
||||
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;
|
||||
}
|
||||
|
||||
@ -681,14 +716,14 @@ class PhysicalDeviceSelector {
|
||||
VkPhysicalDeviceFeatures required_features{};
|
||||
VkPhysicalDeviceFeatures2 required_features2{};
|
||||
|
||||
std::vector<detail::GenericFeaturesPNextNode> extended_features_chain;
|
||||
detail::GenericFeatureChain extended_features_chain;
|
||||
bool defer_surface_initialization = false;
|
||||
bool use_first_gpu_unconditionally = false;
|
||||
bool enable_portability_subset = true;
|
||||
} criteria;
|
||||
|
||||
PhysicalDevice populate_device_details(VkPhysicalDevice phys_device,
|
||||
std::vector<detail::GenericFeaturesPNextNode> const& src_extended_features_chain) const;
|
||||
PhysicalDevice populate_device_details(
|
||||
VkPhysicalDevice phys_device, detail::GenericFeatureChain const& src_extended_features_chain) const;
|
||||
|
||||
PhysicalDevice::Suitable is_device_suitable(PhysicalDevice const& phys_device) const;
|
||||
|
||||
|
@ -7,7 +7,7 @@ else()
|
||||
endif()
|
||||
target_link_libraries(VulkanMock
|
||||
PUBLIC
|
||||
Vulkan::Headers
|
||||
Vulkan::Headers vk-bootstrap
|
||||
PRIVATE
|
||||
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_features(descriptor_indexing_features)
|
||||
.select();
|
||||
// Ignore if hardware support isn't true
|
||||
REQUIRE(phys_dev_ret.has_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_features(descriptor_indexing_features)
|
||||
.select();
|
||||
// Ignore if hardware support isn't true
|
||||
REQUIRE(phys_dev_ret.has_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]") {
|
||||
VulkanMock& mock = get_and_setup_default();
|
||||
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_features(descriptor_indexing_features)
|
||||
.select();
|
||||
// Ignore if hardware support isn't true
|
||||
REQUIRE(phys_dev_ret.has_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)
|
||||
.add_required_extension(VK_KHR_MAINTENANCE3_EXTENSION_NAME)
|
||||
.select();
|
||||
// Ignore if hardware support isn't true
|
||||
REQUIRE(phys_dev_ret.has_value());
|
||||
|
||||
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_features(descriptor_indexing_features)
|
||||
.select();
|
||||
// Ignore if hardware support isn't true
|
||||
REQUIRE(phys_dev_ret.has_value());
|
||||
|
||||
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.multiview = true;
|
||||
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;
|
||||
|
||||
vkb::PhysicalDeviceSelector selector(instance);
|
||||
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());
|
||||
|
||||
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);
|
||||
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());
|
||||
}
|
||||
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);
|
||||
auto phys_dev_ret = selector.set_required_features_11(features_11).select();
|
||||
// Ignore if hardware support differs
|
||||
REQUIRE(!phys_dev_ret.has_value());
|
||||
}
|
||||
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) {
|
||||
pFeatures->features = get_physical_device_details(physicalDevice).features;
|
||||
const auto& phys_dev = get_physical_device_details(physicalDevice);
|
||||
VkBaseOutStructure* current = static_cast<VkBaseOutStructure*>(pFeatures->pNext);
|
||||
while (current) {
|
||||
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);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -179,27 +183,56 @@ VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateDevice(VkPhysicalDevice physicalDevi
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
*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;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyDevice(
|
||||
[[maybe_unused]] VkDevice device, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {
|
||||
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);
|
||||
if (it != std::end(physical_devices.created_devices)) {
|
||||
physical_devices.created_devices.erase(it);
|
||||
auto it = std::find(
|
||||
std::begin(physical_devices.created_device_handles), std::end(physical_devices.created_device_handles), device);
|
||||
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) {
|
||||
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);
|
||||
if (it != std::end(physical_devices.created_devices)) {
|
||||
auto it = std::find(
|
||||
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() &&
|
||||
queueIndex < physical_devices.queue_family_properties[queueFamilyIndex].queueCount) {
|
||||
*pQueue = get_handle<VkQueue>(0x0000CCEEU);
|
||||
*pQueue = get_handle<VkQueue>(0x0000CCEEU + queueIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,11 @@
|
||||
|
||||
#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.
|
||||
inline size_t get_pnext_chain_struct_size(VkStructureType type) {
|
||||
inline size_t check_if_features2_struct(VkStructureType type) {
|
||||
switch (type) {
|
||||
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES):
|
||||
return sizeof(VkPhysicalDeviceDescriptorIndexingFeatures);
|
||||
@ -21,10 +23,15 @@ inline size_t get_pnext_chain_struct_size(VkStructureType type) {
|
||||
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES):
|
||||
return sizeof(VkPhysicalDeviceVulkan12Features);
|
||||
default:
|
||||
assert(false && "Must update get_pnext_chain_struct_size(VkStructureType type) to add type!");
|
||||
}
|
||||
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); }
|
||||
|
||||
@ -65,21 +72,24 @@ struct VulkanMock {
|
||||
return surface_handles.back();
|
||||
}
|
||||
|
||||
struct CreatedDeviceDetails {
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
std::vector<const char*> extensions;
|
||||
std::vector<vkb::detail::GenericFeaturesPNextNode> features_pNextChain;
|
||||
};
|
||||
|
||||
struct PhysicalDeviceDetails {
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
VkPhysicalDeviceMemoryProperties memory_properties{};
|
||||
std::vector<VkExtensionProperties> extensions;
|
||||
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) {
|
||||
T* new_type = new T();
|
||||
*new_type = t;
|
||||
features_pNextChain.emplace_back(reinterpret_cast<VkBaseOutStructure*>(new_type));
|
||||
}
|
||||
template <typename T> void add_features_pNext_struct(T features) { features_pNextChain.push_back(features); }
|
||||
};
|
||||
|
||||
std::vector<VkPhysicalDevice> physical_device_handles;
|
||||
|
Loading…
Reference in New Issue
Block a user