Skip to content

Commit da8ee80

Browse files
authored
Refactor instance extension and layer handling (#1256)
* Refactor instance extension and layer handling * Adjust hello_triangle samples to warn instead of throw when there's no validation layer
1 parent da0fbd3 commit da8ee80

File tree

17 files changed

+287
-544
lines changed

17 files changed

+287
-544
lines changed

framework/core/hpp_instance.cpp

Lines changed: 84 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
2-
* Copyright (c) 2024, Arm Limited and Contributors
1+
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2024-2025, Arm Limited and Contributors
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*
@@ -103,109 +103,86 @@ bool validate_layers(const std::vector<const char *> &required,
103103

104104
namespace core
105105
{
106-
std::vector<const char *> get_optimal_validation_layers(const std::vector<vk::LayerProperties> &supported_instance_layers)
107-
{
108-
std::vector<std::vector<const char *>> validation_layer_priority_list =
109-
{
110-
// The preferred validation layer is "VK_LAYER_KHRONOS_validation"
111-
{"VK_LAYER_KHRONOS_validation"},
112-
113-
// Otherwise we fallback to using the LunarG meta layer
114-
{"VK_LAYER_LUNARG_standard_validation"},
115-
116-
// Otherwise we attempt to enable the individual layers that compose the LunarG meta layer since it doesn't exist
117-
{
118-
"VK_LAYER_GOOGLE_threading",
119-
"VK_LAYER_LUNARG_parameter_validation",
120-
"VK_LAYER_LUNARG_object_tracker",
121-
"VK_LAYER_LUNARG_core_validation",
122-
"VK_LAYER_GOOGLE_unique_objects",
123-
},
124-
125-
// Otherwise as a last resort we fallback to attempting to enable the LunarG core layer
126-
{"VK_LAYER_LUNARG_core_validation"}};
127-
128-
for (auto &validation_layers : validation_layer_priority_list)
129-
{
130-
if (validate_layers(validation_layers, supported_instance_layers))
131-
{
132-
return validation_layers;
133-
}
134-
135-
LOGW("Couldn't enable validation layers (see log for error) - falling back");
136-
}
137-
138-
// Else return nothing
139-
return {};
140-
}
141-
142106
Optional<uint32_t> HPPInstance::selected_gpu_index;
143107

144108
namespace
145109
{
146-
bool enable_extension(const char *required_ext_name,
147-
const std::vector<vk::ExtensionProperties> &available_exts,
110+
bool enable_extension(const char *requested_extension,
111+
const std::vector<vk::ExtensionProperties> &available_extensions,
148112
std::vector<const char *> &enabled_extensions)
149113
{
150-
for (auto &avail_ext_it : available_exts)
151-
{
152-
if (strcmp(avail_ext_it.extensionName, required_ext_name) == 0)
114+
bool is_available =
115+
std::any_of(available_extensions.begin(),
116+
available_extensions.end(),
117+
[&requested_extension](auto const &available_extension) { return strcmp(requested_extension, available_extension.extensionName) == 0; });
118+
if (is_available)
119+
{
120+
bool is_already_enabled =
121+
std::any_of(enabled_extensions.begin(),
122+
enabled_extensions.end(),
123+
[&requested_extension](auto const &enabled_extension) { return strcmp(requested_extension, enabled_extension) == 0; });
124+
if (!is_already_enabled)
153125
{
154-
auto it = std::find_if(enabled_extensions.begin(), enabled_extensions.end(),
155-
[required_ext_name](const char *enabled_ext_name) {
156-
return strcmp(enabled_ext_name, required_ext_name) == 0;
157-
});
158-
if (it != enabled_extensions.end())
159-
{
160-
// Extension is already enabled
161-
}
162-
else
163-
{
164-
LOGI("Extension {} found, enabling it", required_ext_name);
165-
enabled_extensions.emplace_back(required_ext_name);
166-
}
167-
return true;
126+
LOGI("Extension {} available, enabling it", requested_extension);
127+
enabled_extensions.emplace_back(requested_extension);
168128
}
169129
}
130+
else
131+
{
132+
LOGI("Extension {} not available", requested_extension);
133+
}
170134

171-
LOGI("Extension {} not found", required_ext_name);
172-
return false;
135+
return is_available;
173136
}
174137

175-
bool enable_all_extensions(const std::vector<const char *> required_ext_names,
176-
const std::vector<vk::ExtensionProperties> &available_exts,
177-
std::vector<const char *> &enabled_extensions)
138+
bool enable_layer(const char *requested_layer,
139+
const std::vector<vk::LayerProperties> &available_layers,
140+
std::vector<const char *> &enabled_layers)
178141
{
179-
using std::placeholders::_1;
142+
bool is_available =
143+
std::any_of(available_layers.begin(),
144+
available_layers.end(),
145+
[&requested_layer](auto const &available_layer) { return strcmp(requested_layer, available_layer.layerName) == 0; });
146+
if (is_available)
147+
{
148+
bool is_already_enabled =
149+
std::any_of(enabled_layers.begin(),
150+
enabled_layers.end(),
151+
[&requested_layer](auto const &enabled_layer) { return strcmp(requested_layer, enabled_layer) == 0; });
152+
if (!is_already_enabled)
153+
{
154+
LOGI("Layer {} available, enabling it", requested_layer);
155+
enabled_layers.emplace_back(requested_layer);
156+
}
157+
}
158+
else
159+
{
160+
LOGI("Layer {} not available", requested_layer);
161+
}
180162

181-
return std::all_of(required_ext_names.begin(), required_ext_names.end(),
182-
std::bind(enable_extension, _1, available_exts, enabled_extensions));
163+
return is_available;
183164
}
184-
185165
} // namespace
186166

187167
HPPInstance::HPPInstance(const std::string &application_name,
188-
const std::unordered_map<const char *, bool> &required_extensions,
189-
const std::vector<const char *> &required_validation_layers,
168+
const std::unordered_map<const char *, bool> &requested_extensions,
169+
const std::unordered_map<const char *, bool> &requested_layers,
190170
const std::vector<vk::LayerSettingEXT> &required_layer_settings,
191171
uint32_t api_version)
192172
{
193173
std::vector<vk::ExtensionProperties> available_instance_extensions = vk::enumerateInstanceExtensionProperties();
194174

195175
#ifdef USE_VALIDATION_LAYERS
196176
// Check if VK_EXT_debug_utils is supported, which supersedes VK_EXT_Debug_Report
197-
const bool has_debug_utils = enable_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
198-
available_instance_extensions, enabled_extensions);
177+
const bool has_debug_utils = enable_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
199178
bool has_debug_report = false;
200179

201180
if (!has_debug_utils)
202181
{
203-
has_debug_report = enable_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
204-
available_instance_extensions, enabled_extensions);
182+
has_debug_report = enable_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
205183
if (!has_debug_report)
206184
{
207-
LOGW("Neither of {} or {} are available; disabling debug reporting",
208-
VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
185+
LOGW("Neither of {} or {} are available; disabling debug reporting", VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
209186
}
210187
}
211188
#endif
@@ -220,34 +197,24 @@ HPPInstance::HPPInstance(const std::string &applicati
220197
{
221198
std::vector<vk::ExtensionProperties> available_layer_instance_extensions = vk::enumerateInstanceExtensionProperties(std::string("VK_LAYER_KHRONOS_validation"));
222199

223-
for (auto &available_extension : available_layer_instance_extensions)
224-
{
225-
if (strcmp(available_extension.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0)
226-
{
227-
validation_features = true;
228-
LOGI("{} is available, enabling it", VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
229-
enabled_extensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
230-
}
231-
}
200+
enable_extension(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME, available_layer_instance_extensions, enabled_extensions);
232201
}
233202
#endif
234203

235204
// Specific surface extensions are obtained from Window::get_required_surface_extensions
236-
// They are already added to required_extensions by VulkanSample::prepare
205+
// They are already added to requested_extensions by VulkanSample::prepare
237206

238-
// If using VK_EXT_headless_surface, we still create and use a surface
239-
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
207+
// Even for a headless surface a swapchain is still required
208+
enable_extension(VK_KHR_SURFACE_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
240209

241210
// VK_KHR_get_physical_device_properties2 is a prerequisite of VK_KHR_performance_query
242211
// which will be used for stats gathering where available.
243-
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
244-
available_instance_extensions, enabled_extensions);
212+
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
245213

246-
auto extension_error = false;
247-
for (auto extension : required_extensions)
214+
for (auto requested_extension : requested_extensions)
248215
{
249-
auto extension_name = extension.first;
250-
auto extension_is_optional = extension.second;
216+
auto const &extension_name = requested_extension.first;
217+
auto extension_is_optional = requested_extension.second;
251218
if (!enable_extension(extension_name, available_instance_extensions, enabled_extensions))
252219
{
253220
if (extension_is_optional)
@@ -257,43 +224,43 @@ HPPInstance::HPPInstance(const std::string &applicati
257224
else
258225
{
259226
LOGE("Required instance extension {} not available, cannot run", extension_name);
260-
extension_error = true;
227+
throw std::runtime_error("Required instance extensions are missing.");
261228
}
262-
extension_error = extension_error || !extension_is_optional;
263229
}
264230
}
265231

266-
if (extension_error)
267-
{
268-
throw std::runtime_error("Required instance extensions are missing.");
269-
}
270-
271-
std::vector<vk::LayerProperties> supported_validation_layers = vk::enumerateInstanceLayerProperties();
232+
std::vector<vk::LayerProperties> supported_layers = vk::enumerateInstanceLayerProperties();
272233

273-
std::vector<const char *> requested_validation_layers(required_validation_layers);
234+
std::vector<const char *> enabled_layers;
274235

275-
#ifdef USE_VALIDATION_LAYERS
276-
// Determine the optimal validation layers to enable that are necessary for useful debugging
277-
std::vector<const char *> optimal_validation_layers = get_optimal_validation_layers(supported_validation_layers);
278-
requested_validation_layers.insert(requested_validation_layers.end(), optimal_validation_layers.begin(), optimal_validation_layers.end());
279-
#endif
280-
281-
if (validate_layers(requested_validation_layers, supported_validation_layers))
236+
auto layer_error = false;
237+
for (auto const &requested_layer : requested_layers)
282238
{
283-
LOGI("Enabled Validation Layers:")
284-
for (const auto &layer : requested_validation_layers)
239+
auto const &layer_name = requested_layer.first;
240+
auto layer_is_optional = requested_layer.second;
241+
if (!enable_layer(layer_name, supported_layers, enabled_layers))
285242
{
286-
LOGI(" \t{}", layer);
243+
if (layer_is_optional)
244+
{
245+
LOGW("Optional layer {} not available, some features may be disabled", layer_name);
246+
}
247+
else
248+
{
249+
LOGE("Required layer {} not available, cannot run", layer_name);
250+
throw std::runtime_error("Required layers are missing.");
251+
}
287252
}
288253
}
289-
else
290-
{
291-
throw std::runtime_error("Required validation layers are missing.");
292-
}
254+
255+
#ifdef USE_VALIDATION_LAYERS
256+
// NOTE: It's important to have the validation layer as the last one here!!!!
257+
// Otherwise, device creation fails !?!
258+
enable_layer("VK_LAYER_KHRONOS_validation", supported_layers, enabled_layers);
259+
#endif
293260

294261
vk::ApplicationInfo app_info(application_name.c_str(), 0, "Vulkan Samples", 0, api_version);
295262

296-
vk::InstanceCreateInfo instance_info({}, &app_info, requested_validation_layers, enabled_extensions);
263+
vk::InstanceCreateInfo instance_info({}, &app_info, enabled_layers, enabled_extensions);
297264

298265
#ifdef USE_VALIDATION_LAYERS
299266
vk::DebugUtilsMessengerCreateInfoEXT debug_utils_create_info;
@@ -450,7 +417,7 @@ vkb::core::HPPPhysicalDevice &HPPInstance::get_suitable_gpu(vk::SurfaceKHR surfa
450417
}
451418
return *gpus[selected_gpu_index.value()];
452419
}
453-
if ( headless_surface )
420+
if (headless_surface)
454421
{
455422
LOGW("Using headless surface with multiple GPUs. Considered explicitly selecting the target GPU.")
456423
}

framework/core/hpp_instance.h

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
2-
* Copyright (c) 2024, Arm Limited and Contributors
1+
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
2+
* Copyright (c) 2024-2025, Arm Limited and Contributors
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*
@@ -28,12 +28,6 @@ namespace vkb
2828
namespace core
2929
{
3030
class HPPPhysicalDevice;
31-
/**
32-
* @brief Returns a list of Khronos/LunarG supported validation layers
33-
* Attempting to enable them in order of preference, starting with later Vulkan SDK versions
34-
* @param supported_instance_layers A list of validation layers to check against
35-
*/
36-
std::vector<const char *> get_optimal_validation_layers(const std::vector<vk::LayerProperties> &supported_instance_layers);
3731

3832
/**
3933
* @brief A wrapper class for vk::Instance
@@ -52,17 +46,17 @@ class HPPInstance
5246
/**
5347
* @brief Initializes the connection to Vulkan
5448
* @param application_name The name of the application
55-
* @param required_extensions The extensions requested to be enabled
56-
* @param required_validation_layers The validation layers to be enabled
49+
* @param requested_extensions The extensions requested to be enabled
50+
* @param requested_layers The validation layers to be enabled
5751
* @param required_layer_settings The layer settings to be enabled
5852
* @param api_version The Vulkan API version that the instance will be using
5953
* @throws runtime_error if the required extensions and validation layers are not found
6054
*/
6155
HPPInstance(const std::string &application_name,
62-
const std::unordered_map<const char *, bool> &required_extensions = {},
63-
const std::vector<const char *> &required_validation_layers = {},
64-
const std::vector<vk::LayerSettingEXT> &required_layer_settings = {},
65-
uint32_t api_version = VK_API_VERSION_1_0);
56+
const std::unordered_map<const char *, bool> &requested_extensions = {},
57+
const std::unordered_map<const char *, bool> &requested_layers = {},
58+
const std::vector<vk::LayerSettingEXT> &required_layer_settings = {},
59+
uint32_t api_version = VK_API_VERSION_1_0);
6660

6761
/**
6862
* @brief Queries the GPUs of a vk::Instance that is already created

0 commit comments

Comments
 (0)