Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ set(FRAMEWORK_FILES
hpp_resource_record.h
hpp_resource_replay.h
hpp_semaphore_pool.h
structure_chain_builder.h
# Source Files
drawer.cpp
spirv_reflection.cpp
Expand Down
7 changes: 6 additions & 1 deletion framework/common/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@

namespace vkb
{
inline bool contains(uint32_t range_count, char const *const *range, char const *value)
{
return std::any_of(range, range + range_count, [value](char const *range_value) { return strcmp(range_value, value) == 0; });
}

inline bool contains(std::vector<std::string> const &range, char const *value)
{
return std::ranges::find_if(range, [value](std::string const &range_value) { return range_value == value; }) != range.end();
return std::ranges::any_of(range, [value](std::string const &range_value) { return range_value == value; });
}

template <typename T>
Expand Down
49 changes: 28 additions & 21 deletions framework/core/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "common/helpers.h"
#include "structure_chain_builder.h"
#include <vulkan/vulkan.hpp>

namespace vkb
Expand All @@ -39,11 +40,6 @@ typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCrea
return 0;
}
}

void const *get_default_pNext(std::vector<std::string> const &, std::vector<std::string> const &)
{
return nullptr;
}
} // namespace

/**
Expand All @@ -57,6 +53,7 @@ class Instance
{
public:
using InstanceCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCreateFlags, VkInstanceCreateFlags>::type;
using InstanceCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCreateInfo, VkInstanceCreateInfo>::type;
using InstanceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Instance, VkInstance>::type;

public:
Expand All @@ -66,17 +63,17 @@ class Instance
* @param api_version The Vulkan API version that the instance will be using
* @param requested_layers The requested layers to be enabled
* @param requested_extensions The requested extensions to be enabled
* @param get_pNext A function pointer returning the pNext pointer for the InstanceCreateInfo
* @param get_create_flags A function pointer returning the InstanceCreateFlags for the InstanceCreateInfo
* @param extend_instance_create_info A function pointer to extend the InstanceCreateInfo with additional structures in the pNext chain
* @throws runtime_error if a required layer or extension is not available
*/
Instance(
std::string const &application_name,
uint32_t api_version = VK_API_VERSION_1_1,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers = {},
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions = {},
std::function<void const *(std::vector<std::string> const &, std::vector<std::string> const &)> const &get_pNext = get_default_pNext,
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags = get_default_create_flags);
std::string const &application_name,
uint32_t api_version = VK_API_VERSION_1_1,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers = {},
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions = {},
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags = get_default_create_flags,
std::function<void(vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> &)> const &extend_instance_create_info = [](vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> const &) {});

Instance(vk::Instance instance, std::vector<char const *> const &externally_enabled_extensions = {}, bool needsToInitializeDispatcher = false);
Instance(VkInstance instance, std::vector<char const *> const &externally_enabled_extensions = {});
Expand Down Expand Up @@ -158,12 +155,12 @@ inline bool
} // namespace

template <vkb::BindingType bindingType>
inline Instance<bindingType>::Instance(std::string const &application_name,
uint32_t api_version,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers,
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions,
std::function<void const *(std::vector<std::string> const &, std::vector<std::string> const &)> const &get_pNext,
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags)
inline Instance<bindingType>::Instance(std::string const &application_name,
uint32_t api_version,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers,
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions,
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags,
std::function<void(vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> &)> const &extend_instance_create_info)
{
// check API version
LOGI("Requesting Vulkan API version {}.{}", VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version));
Expand Down Expand Up @@ -243,16 +240,26 @@ inline Instance<bindingType>::Instance(std::string const

vk::ApplicationInfo app_info{.pApplicationName = application_name.c_str(), .pEngineName = "Vulkan Samples", .apiVersion = api_version};

vk::InstanceCreateInfo create_info{.pNext = get_pNext(enabled_layers, enabled_extensions),
.flags = static_cast<vk::InstanceCreateFlags>(get_create_flags(enabled_extensions)),
vk::InstanceCreateInfo create_info{.flags = static_cast<vk::InstanceCreateFlags>(get_create_flags(enabled_extensions)),
.pApplicationInfo = &app_info,
.enabledLayerCount = static_cast<uint32_t>(enabled_layers_cstr.size()),
.ppEnabledLayerNames = enabled_layers_cstr.data(),
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size()),
.ppEnabledExtensionNames = enabled_extensions_cstr.data()};

vkb::StructureChainBuilder<vkb::BindingType::Cpp, vk::InstanceCreateInfo> scb;
scb.set_anchor_struct(create_info);
if constexpr (bindingType == vkb::BindingType::Cpp)
{
extend_instance_create_info(scb);
}
else
{
extend_instance_create_info(reinterpret_cast<vkb::StructureChainBuilder<vkb::BindingType::C, VkInstanceCreateInfo> &>(scb));
}

// Create the Vulkan instance
handle = vk::createInstance(create_info);
handle = vk::createInstance(*scb.get_struct<vk::InstanceCreateInfo>());

// initialize the Vulkan-Hpp default dispatcher on the instance
VULKAN_HPP_DEFAULT_DISPATCHER.init(handle);
Expand Down
162 changes: 162 additions & 0 deletions framework/structure_chain_builder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <any>
#include <vulkan/vulkan.hpp>

namespace vkb
{
template <vkb::BindingType bindingType, typename AnchorStructType>
class StructureChainBuilder
{
public:
StructureChainBuilder();

template <typename T>
T &add_chain_data(T const &data_to_add = {}); // Adds data to the structure chain builder that is not part of the structure chain itself, but is used by the
// structures in the chain (e.g. pointed to by a member of a struct in the structure chain)

template <typename StructType>
StructType &add_struct(StructType const &struct_to_add = {});

template <typename StructType>
StructType const *get_struct(size_t skip = 0) const;

void set_anchor_struct(AnchorStructType const &anchor_struct);

private:
template <typename StructType>
StructType &add_struct_impl(StructType const &struct_to_add);
template <typename StructType>
StructType const *get_struct_impl(size_t skip) const;
void set_anchor_struct_impl(AnchorStructType const &anchor_struct);

private:
std::vector<std::unique_ptr<std::any>> structure_chain;
std::vector<std::unique_ptr<std::any>> chain_data; // data used with the structure chain, stored separately to ensure correct destruction order (the structs
// in the structure chain may contain pointers to data owned by the chain_data)
};

template <typename AnchorStructType>
using StructureChainBuilderC = StructureChainBuilder<vkb::BindingType::C, AnchorStructType>;
template <typename AnchorStructType>
using StructureChainBuilderCpp = StructureChainBuilder<vkb::BindingType::Cpp, AnchorStructType>;

template <vkb::BindingType bindingType, typename AnchorStructType>
template <typename T>
T &StructureChainBuilder<bindingType, AnchorStructType>::add_chain_data(T const &data_to_add)
{
chain_data.push_back(std::make_unique<std::any>(std::make_any<T>(data_to_add)));
return *std::any_cast<T>(chain_data.back().get());
}

template <vkb::BindingType bindingType, typename AnchorStructType>
inline StructureChainBuilder<bindingType, AnchorStructType>::StructureChainBuilder()
{
static_assert((offsetof(AnchorStructType, sType) == 0) && (offsetof(AnchorStructType, pNext) == sizeof(void *)));
structure_chain.push_back(std::make_unique<std::any>(std::make_any<AnchorStructType>()));
}

template <vkb::BindingType bindingType, typename AnchorStructType>
template <typename StructType>
inline StructType &StructureChainBuilder<bindingType, AnchorStructType>::add_struct(StructType const &struct_to_add)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return add_struct_impl(struct_to_add);
}
else
{
return reinterpret_cast<StructureChainBuilder<vkb::BindingType::Cpp, typename vk::CppType<AnchorStructType>::Type> *>(this)->add_struct(
reinterpret_cast<typename vk::CppType<StructType>::Type const &>(struct_to_add));
}
}

template <vkb::BindingType bindingType, typename AnchorStructType>
template <typename StructType>
inline StructType &StructureChainBuilder<bindingType, AnchorStructType>::add_struct_impl(StructType const &struct_to_add)
{
#if !defined(NDEBUG)
auto it = std::ranges::find_if(structure_chain, [](auto const &chain_element) {
return std::any_cast<StructType>(chain_element.get()) != nullptr;
});
assert(it == structure_chain.end() || StructType::allowDuplicate); // If the struct type already exists in the structure chain, it must allow duplicates
#endif

structure_chain.push_back(std::make_unique<std::any>(std::make_any<StructType>(struct_to_add)));
std::any_cast<StructType>(structure_chain.back().get())->pNext = std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext;
std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext = std::any_cast<StructType>(structure_chain.back().get());
return *std::any_cast<StructType>(structure_chain.back().get());
}

template <vkb::BindingType bindingType, typename AnchorStructType>
template <typename StructType>
inline StructType const *StructureChainBuilder<bindingType, AnchorStructType>::get_struct(size_t skip) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return get_struct_impl<StructType>(skip);
}
else
{
return reinterpret_cast<StructType const *>(get_struct_impl<vk::CppType<StructType>::Type>(skip));
}
}

template <vkb::BindingType bindingType, typename AnchorStructType>
template <typename StructType>
inline StructType const *StructureChainBuilder<bindingType, AnchorStructType>::get_struct_impl(size_t skip) const
{
assert(!skip || StructType::allowDuplicate); // If skip is non-zero, the struct type must allow duplicates in the structure chain

auto it = std::ranges::find_if(structure_chain, [](auto const &chain_element) {
return std::any_cast<StructType>(chain_element.get()) != nullptr;
});
for (size_t i = 0; (i < skip) && (it != structure_chain.end()); ++i)
{
it = std::find_if(std::next(it), structure_chain.end(), [](auto const &chain_element) {
return std::any_cast<StructType>(chain_element.get()) != nullptr;
});
}
return (it != structure_chain.end()) ? std::any_cast<StructType>(it->get()) : nullptr;
}

template <vkb::BindingType bindingType, typename AnchorStructType>
inline void StructureChainBuilder<bindingType, AnchorStructType>::set_anchor_struct(AnchorStructType const &anchor_struct)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
set_anchor_struct_impl(anchor_struct);
}
else
{
return reinterpret_cast<StructureChainBuilder<vkb::BindingType::Cpp, typename vk::CppType<AnchorStructType>::Type> *>(this)->add_anchor_struct(
reinterpret_cast<typename vk::CppType<AnchorStructType>::Type const &>(anchor_struct));
}
}

template <vkb::BindingType bindingType, typename AnchorStructType>
inline void StructureChainBuilder<bindingType, AnchorStructType>::set_anchor_struct_impl(AnchorStructType const &anchor_struct)
{
void const *pNext = std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext;
*std::any_cast<AnchorStructType>(structure_chain.front().get()) = anchor_struct;
std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext = pNext;
}

} // namespace vkb
Loading
Loading