Skip to content

Commit 11ecd5c

Browse files
committed
Introduce StructureChainBuilder
1 parent 8f7fcb0 commit 11ecd5c

File tree

15 files changed

+351
-148
lines changed

15 files changed

+351
-148
lines changed

framework/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ set(FRAMEWORK_FILES
4949
hpp_resource_record.h
5050
hpp_resource_replay.h
5151
hpp_semaphore_pool.h
52+
structure_chain_builder.h
5253
# Source Files
5354
drawer.cpp
5455
spirv_reflection.cpp

framework/common/helpers.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,14 @@
4141

4242
namespace vkb
4343
{
44+
inline bool contains(uint32_t range_count, char const *const *range, char const *value)
45+
{
46+
return std::any_of(range, range + range_count, [value](char const *range_value) { return strcmp(range_value, value) == 0; });
47+
}
48+
4449
inline bool contains(std::vector<std::string> const &range, char const *value)
4550
{
46-
return std::ranges::find_if(range, [value](std::string const &range_value) { return range_value == value; }) != range.end();
51+
return std::ranges::any_of(range, [value](std::string const &range_value) { return range_value == value; });
4752
}
4853

4954
template <typename T>

framework/core/instance.h

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#pragma once
2020

2121
#include "common/helpers.h"
22+
#include "structure_chain_builder.h"
2223
#include <vulkan/vulkan.hpp>
2324

2425
namespace vkb
@@ -39,11 +40,6 @@ typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCrea
3940
return 0;
4041
}
4142
}
42-
43-
void const *get_default_pNext(std::vector<std::string> const &, std::vector<std::string> const &)
44-
{
45-
return nullptr;
46-
}
4743
} // namespace
4844

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

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

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

160157
template <vkb::BindingType bindingType>
161-
inline Instance<bindingType>::Instance(std::string const &application_name,
162-
uint32_t api_version,
163-
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers,
164-
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions,
165-
std::function<void const *(std::vector<std::string> const &, std::vector<std::string> const &)> const &get_pNext,
166-
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags)
158+
inline Instance<bindingType>::Instance(std::string const &application_name,
159+
uint32_t api_version,
160+
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers,
161+
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions,
162+
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags,
163+
std::function<void(vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> &)> const &extend_instance_create_info)
167164
{
168165
// check API version
169166
LOGI("Requesting Vulkan API version {}.{}", VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version));
@@ -243,16 +240,26 @@ inline Instance<bindingType>::Instance(std::string const
243240

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

246-
vk::InstanceCreateInfo create_info{.pNext = get_pNext(enabled_layers, enabled_extensions),
247-
.flags = static_cast<vk::InstanceCreateFlags>(get_create_flags(enabled_extensions)),
243+
vk::InstanceCreateInfo create_info{.flags = static_cast<vk::InstanceCreateFlags>(get_create_flags(enabled_extensions)),
248244
.pApplicationInfo = &app_info,
249245
.enabledLayerCount = static_cast<uint32_t>(enabled_layers_cstr.size()),
250246
.ppEnabledLayerNames = enabled_layers_cstr.data(),
251247
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size()),
252248
.ppEnabledExtensionNames = enabled_extensions_cstr.data()};
253249

250+
vkb::StructureChainBuilder<vkb::BindingType::Cpp, vk::InstanceCreateInfo> scb;
251+
scb.set_anchor_struct(create_info);
252+
if constexpr (bindingType == vkb::BindingType::Cpp)
253+
{
254+
extend_instance_create_info(scb);
255+
}
256+
else
257+
{
258+
extend_instance_create_info(reinterpret_cast<vkb::StructureChainBuilder<vkb::BindingType::C, VkInstanceCreateInfo> &>(scb));
259+
}
260+
254261
// Create the Vulkan instance
255-
handle = vk::createInstance(create_info);
262+
handle = vk::createInstance(*scb.get_struct<vk::InstanceCreateInfo>());
256263

257264
// initialize the Vulkan-Hpp default dispatcher on the instance
258265
VULKAN_HPP_DEFAULT_DISPATCHER.init(handle);
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 the "License";
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include <any>
21+
#include <vulkan/vulkan.hpp>
22+
23+
namespace vkb
24+
{
25+
template <vkb::BindingType bindingType, typename AnchorStructType>
26+
class StructureChainBuilder
27+
{
28+
public:
29+
StructureChainBuilder();
30+
31+
template <typename T>
32+
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
33+
// structures in the chain (e.g. pointed to by a member of a struct in the structure chain)
34+
35+
template <typename StructType>
36+
StructType &add_struct(StructType const &struct_to_add = {});
37+
38+
template <typename StructType>
39+
StructType const *get_struct(size_t skip = 0) const;
40+
41+
void set_anchor_struct(AnchorStructType const &anchor_struct);
42+
43+
private:
44+
template <typename StructType>
45+
StructType &add_struct_impl(StructType const &struct_to_add);
46+
template <typename StructType>
47+
StructType const *get_struct_impl(size_t skip) const;
48+
void set_anchor_struct_impl(AnchorStructType const &anchor_struct);
49+
50+
private:
51+
std::vector<std::unique_ptr<std::any>> structure_chain;
52+
std::vector<std::unique_ptr<std::any>> chain_data; // data used with the structure chain, stored separately to ensure correct destruction order (the structs
53+
// in the structure chain may contain pointers to data owned by the chain_data)
54+
};
55+
56+
template <typename AnchorStructType>
57+
using StructureChainBuilderC = StructureChainBuilder<vkb::BindingType::C, AnchorStructType>;
58+
template <typename AnchorStructType>
59+
using StructureChainBuilderCpp = StructureChainBuilder<vkb::BindingType::Cpp, AnchorStructType>;
60+
61+
template <vkb::BindingType bindingType, typename AnchorStructType>
62+
template <typename T>
63+
T &StructureChainBuilder<bindingType, AnchorStructType>::add_chain_data(T const &data_to_add)
64+
{
65+
chain_data.push_back(std::make_unique<std::any>(std::make_any<T>(data_to_add)));
66+
return *std::any_cast<T>(chain_data.back().get());
67+
}
68+
69+
template <vkb::BindingType bindingType, typename AnchorStructType>
70+
inline StructureChainBuilder<bindingType, AnchorStructType>::StructureChainBuilder()
71+
{
72+
static_assert((offsetof(AnchorStructType, sType) == 0) && (offsetof(AnchorStructType, pNext) == sizeof(void *)));
73+
structure_chain.push_back(std::make_unique<std::any>(std::make_any<AnchorStructType>()));
74+
}
75+
76+
template <vkb::BindingType bindingType, typename AnchorStructType>
77+
template <typename StructType>
78+
inline StructType &StructureChainBuilder<bindingType, AnchorStructType>::add_struct(StructType const &struct_to_add)
79+
{
80+
if constexpr (bindingType == vkb::BindingType::Cpp)
81+
{
82+
return add_struct_impl(struct_to_add);
83+
}
84+
else
85+
{
86+
return reinterpret_cast<StructureChainBuilder<vkb::BindingType::Cpp, typename vk::CppType<AnchorStructType>::Type> *>(this)->add_struct(
87+
reinterpret_cast<typename vk::CppType<StructType>::Type const &>(struct_to_add));
88+
}
89+
}
90+
91+
template <vkb::BindingType bindingType, typename AnchorStructType>
92+
template <typename StructType>
93+
inline StructType &StructureChainBuilder<bindingType, AnchorStructType>::add_struct_impl(StructType const &struct_to_add)
94+
{
95+
#if !defined(NDEBUG)
96+
auto it = std::ranges::find_if(structure_chain, [](auto const &chain_element) {
97+
return std::any_cast<StructType>(chain_element.get()) != nullptr;
98+
});
99+
assert(it == structure_chain.end() || StructType::allowDuplicate); // If the struct type already exists in the structure chain, it must allow duplicates
100+
#endif
101+
102+
structure_chain.push_back(std::make_unique<std::any>(std::make_any<StructType>(struct_to_add)));
103+
std::any_cast<StructType>(structure_chain.back().get())->pNext = std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext;
104+
std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext = std::any_cast<StructType>(structure_chain.back().get());
105+
return *std::any_cast<StructType>(structure_chain.back().get());
106+
}
107+
108+
template <vkb::BindingType bindingType, typename AnchorStructType>
109+
template <typename StructType>
110+
inline StructType const *StructureChainBuilder<bindingType, AnchorStructType>::get_struct(size_t skip) const
111+
{
112+
if constexpr (bindingType == vkb::BindingType::Cpp)
113+
{
114+
return get_struct_impl<StructType>(skip);
115+
}
116+
else
117+
{
118+
return reinterpret_cast<StructType const *>(get_struct_impl<vk::CppType<StructType>::Type>(skip));
119+
}
120+
}
121+
122+
template <vkb::BindingType bindingType, typename AnchorStructType>
123+
template <typename StructType>
124+
inline StructType const *StructureChainBuilder<bindingType, AnchorStructType>::get_struct_impl(size_t skip) const
125+
{
126+
assert(!skip || StructType::allowDuplicate); // If skip is non-zero, the struct type must allow duplicates in the structure chain
127+
128+
auto it = std::ranges::find_if(structure_chain, [](auto const &chain_element) {
129+
return std::any_cast<StructType>(chain_element.get()) != nullptr;
130+
});
131+
for (size_t i = 0; (i < skip) && (it != structure_chain.end()); ++i)
132+
{
133+
it = std::find_if(std::next(it), structure_chain.end(), [](auto const &chain_element) {
134+
return std::any_cast<StructType>(chain_element.get()) != nullptr;
135+
});
136+
}
137+
return (it != structure_chain.end()) ? std::any_cast<StructType>(it->get()) : nullptr;
138+
}
139+
140+
template <vkb::BindingType bindingType, typename AnchorStructType>
141+
inline void StructureChainBuilder<bindingType, AnchorStructType>::set_anchor_struct(AnchorStructType const &anchor_struct)
142+
{
143+
if constexpr (bindingType == vkb::BindingType::Cpp)
144+
{
145+
set_anchor_struct_impl(anchor_struct);
146+
}
147+
else
148+
{
149+
return reinterpret_cast<StructureChainBuilder<vkb::BindingType::Cpp, typename vk::CppType<AnchorStructType>::Type> *>(this)->add_anchor_struct(
150+
reinterpret_cast<typename vk::CppType<AnchorStructType>::Type const &>(anchor_struct));
151+
}
152+
}
153+
154+
template <vkb::BindingType bindingType, typename AnchorStructType>
155+
inline void StructureChainBuilder<bindingType, AnchorStructType>::set_anchor_struct_impl(AnchorStructType const &anchor_struct)
156+
{
157+
void const *pNext = std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext;
158+
*std::any_cast<AnchorStructType>(structure_chain.front().get()) = anchor_struct;
159+
std::any_cast<AnchorStructType>(structure_chain.front().get())->pNext = pNext;
160+
}
161+
162+
} // namespace vkb

0 commit comments

Comments
 (0)