770 lines
30 KiB
C++
770 lines
30 KiB
C++
/* Copyright (c) 2018-2025, Arm Limited and Contributors
|
|
* Copyright (c) 2022-2025, 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 "common/vk_common.h"
|
|
#include <ranges>
|
|
#include <vulkan/vulkan.hpp>
|
|
|
|
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
|
|
# define USE_VALIDATION_LAYERS
|
|
#endif
|
|
|
|
#if defined(USE_VALIDATION_LAYERS) && \
|
|
(defined(VKB_VALIDATION_LAYERS_GPU_ASSISTED) || defined(VKB_VALIDATION_LAYERS_BEST_PRACTICES) || defined(VKB_VALIDATION_LAYERS_SYNCHRONIZATION))
|
|
# define USE_VALIDATION_LAYER_FEATURES
|
|
#endif
|
|
|
|
namespace vkb
|
|
{
|
|
class PhysicalDevice;
|
|
|
|
namespace core
|
|
{
|
|
class HPPPhysicalDevice;
|
|
|
|
/**
|
|
* @brief A wrapper class for InstanceType
|
|
*
|
|
* This class is responsible for initializing the dispatcher, enumerating over all available extensions and validation layers
|
|
* enabling them if they exist, setting up debug messaging and querying all the physical devices existing on the machine.
|
|
*/
|
|
template <vkb::BindingType bindingType>
|
|
class Instance
|
|
{
|
|
public:
|
|
using InstanceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Instance, VkInstance>::type;
|
|
using SurfaceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SurfaceKHR, VkSurfaceKHR>::type;
|
|
|
|
using PhysicalDeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPPhysicalDevice, vkb::PhysicalDevice>::type;
|
|
|
|
public:
|
|
/**
|
|
* @brief Can be set from the GPU selection plugin to explicitly select a GPU instead
|
|
*/
|
|
inline static uint32_t selected_gpu_index = ~0;
|
|
|
|
/**
|
|
* @brief Initializes the connection to Vulkan
|
|
* @param application_name The name of the application
|
|
* @param requested_extensions The extensions requested to be enabled
|
|
* @param requested_layers The validation layers to be enabled
|
|
* @param requested_layer_settings The layer settings to be enabled
|
|
* @param api_version The Vulkan API version that the instance will be using
|
|
* @throws runtime_error if the required extensions and validation layers are not found
|
|
*/
|
|
Instance(std::string const &application_name,
|
|
std::unordered_map<char const *, bool> const &requested_extensions = {},
|
|
std::unordered_map<char const *, bool> const &requested_layers = {},
|
|
const std::vector<vk::LayerSettingEXT> &requested_layer_settings = {},
|
|
uint32_t api_version = VK_API_VERSION_1_1);
|
|
Instance(std::string const &application_name,
|
|
std::unordered_map<char const *, bool> const &requested_extensions,
|
|
std::unordered_map<char const *, bool> const &requested_layers,
|
|
std::vector<VkLayerSettingEXT> const &requested_layer_settings,
|
|
uint32_t api_version = VK_API_VERSION_1_1);
|
|
|
|
/**
|
|
* @brief Queries the GPUs of a InstanceType that is already created
|
|
* @param instance A valid InstanceType
|
|
* @param externally_enabled_extensions List of extensions that have been enabled, used for following checks e.g. during device creation
|
|
* @param needsToInitializeDispatcher If the sample uses the C-bindings and some "non-standard" initialization, the dispatcher needs to be initialized
|
|
*/
|
|
Instance(vk::Instance instance, const std::vector<const char *> &externally_enabled_extensions = {}, bool needsToInitializeDispatcher = false);
|
|
Instance(VkInstance instance, const std::vector<const char *> &externally_enabled_extensions = {});
|
|
|
|
Instance(Instance const &) = delete;
|
|
Instance(Instance &&) = delete;
|
|
|
|
~Instance();
|
|
|
|
Instance &operator=(Instance const &) = delete;
|
|
Instance &operator=(Instance &&) = delete;
|
|
|
|
const std::vector<const char *> &get_extensions();
|
|
|
|
/**
|
|
* @brief Tries to find the first available discrete GPU
|
|
* @returns A valid physical device
|
|
*/
|
|
PhysicalDeviceType &get_first_gpu();
|
|
|
|
InstanceType get_handle() const;
|
|
|
|
/**
|
|
* @brief Tries to find the first available discrete GPU that can render to the given surface
|
|
* @param surface to test against
|
|
* @param headless_surface Is surface created with VK_EXT_headless_surface
|
|
* @returns A valid physical device
|
|
*/
|
|
PhysicalDeviceType &get_suitable_gpu(SurfaceType surface, bool headless_surface);
|
|
|
|
/**
|
|
* @brief Checks if the given extension is enabled in the InstanceType
|
|
* @param extension An extension to check
|
|
*/
|
|
bool is_enabled(char const *extension) const;
|
|
|
|
private:
|
|
/**
|
|
* @brief Queries the instance for the physical devices on the machine
|
|
*/
|
|
void query_gpus();
|
|
|
|
private:
|
|
std::vector<const char *> enabled_extensions; // The enabled extensions
|
|
std::vector<std::unique_ptr<vkb::core::HPPPhysicalDevice>> gpus; // The physical devices found on the machine
|
|
vk::Instance handle; // The Vulkan instance
|
|
|
|
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
|
|
vk::DebugReportCallbackEXT debug_report_callback; // The debug report callback
|
|
vk::DebugUtilsMessengerEXT debug_utils_messenger; // Debug utils messenger callback for VK_EXT_Debug_Utils
|
|
#endif
|
|
};
|
|
|
|
using InstanceC = Instance<vkb::BindingType::C>;
|
|
using InstanceCpp = Instance<vkb::BindingType::Cpp>;
|
|
} // namespace core
|
|
} // namespace vkb
|
|
|
|
#include "core/hpp_physical_device.h"
|
|
|
|
namespace vkb
|
|
{
|
|
namespace core
|
|
{
|
|
namespace
|
|
{
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
inline VKAPI_ATTR vk::Bool32 VKAPI_CALL debug_utils_messenger_callback(vk::DebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
|
vk::DebugUtilsMessageTypeFlagsEXT message_type,
|
|
vk::DebugUtilsMessengerCallbackDataEXT const *callback_data,
|
|
void *user_data)
|
|
{
|
|
// Log debug message
|
|
if (message_severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning)
|
|
{
|
|
LOGW("{} - {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage);
|
|
}
|
|
else if (message_severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError)
|
|
{
|
|
LOGE("{} - {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline VKAPI_ATTR vk::Bool32 VKAPI_CALL debug_callback(vk::DebugReportFlagsEXT flags,
|
|
vk::DebugReportObjectTypeEXT /*type*/,
|
|
uint64_t /*object*/,
|
|
size_t /*location*/,
|
|
int32_t /*message_code*/,
|
|
char const *layer_prefix,
|
|
char const *message,
|
|
void * /*user_data*/)
|
|
{
|
|
if (flags & vk::DebugReportFlagBitsEXT::eError)
|
|
{
|
|
LOGE("{}: {}", layer_prefix, message);
|
|
}
|
|
else if (flags & vk::DebugReportFlagBitsEXT::eWarning)
|
|
{
|
|
LOGW("{}: {}", layer_prefix, message);
|
|
}
|
|
else if (flags & vk::DebugReportFlagBitsEXT::ePerformanceWarning)
|
|
{
|
|
LOGW("{}: {}", layer_prefix, message);
|
|
}
|
|
else
|
|
{
|
|
LOGI("{}: {}", layer_prefix, message);
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
inline bool enable_extension(char const *requested_extension,
|
|
std::vector<vk::ExtensionProperties> const &available_extensions,
|
|
std::vector<char const *> &enabled_extensions)
|
|
{
|
|
bool is_available = std::ranges::any_of(available_extensions,
|
|
[&requested_extension](auto const &available_extension) { return strcmp(requested_extension, available_extension.extensionName) == 0; });
|
|
if (is_available)
|
|
{
|
|
bool is_already_enabled = std::ranges::any_of(
|
|
enabled_extensions, [&requested_extension](auto const &enabled_extension) { return strcmp(requested_extension, enabled_extension) == 0; });
|
|
if (!is_already_enabled)
|
|
{
|
|
LOGI("Extension {} available, enabling it", requested_extension);
|
|
enabled_extensions.emplace_back(requested_extension);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGI("Extension {} not available", requested_extension);
|
|
}
|
|
|
|
return is_available;
|
|
}
|
|
|
|
inline bool enable_layer(char const *requested_layer, std::vector<vk::LayerProperties> const &available_layers, std::vector<char const *> &enabled_layers)
|
|
{
|
|
bool is_available = std::ranges::any_of(
|
|
available_layers, [&requested_layer](auto const &available_layer) { return strcmp(requested_layer, available_layer.layerName) == 0; });
|
|
if (is_available)
|
|
{
|
|
bool is_already_enabled =
|
|
std::ranges::any_of(enabled_layers, [&requested_layer](auto const &enabled_layer) { return strcmp(requested_layer, enabled_layer) == 0; });
|
|
if (!is_already_enabled)
|
|
{
|
|
LOGI("Layer {} available, enabling it", requested_layer);
|
|
enabled_layers.emplace_back(requested_layer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGI("Layer {} not available", requested_layer);
|
|
}
|
|
|
|
return is_available;
|
|
}
|
|
|
|
inline bool enable_layer_setting(vk::LayerSettingEXT const &requested_layer_setting,
|
|
std::vector<char const *> const &enabled_layers,
|
|
std::vector<vk::LayerSettingEXT> &enabled_layer_settings)
|
|
{
|
|
// We are checking if the layer is available.
|
|
// Vulkan does not provide a reflection API for layer settings. Layer settings are described in each layer JSON manifest.
|
|
bool is_available = std::ranges::any_of(
|
|
enabled_layers, [&requested_layer_setting](auto const &available_layer) { return strcmp(available_layer, requested_layer_setting.pLayerName) == 0; });
|
|
#if defined(PLATFORM__MACOS)
|
|
// On Apple platforms the MoltenVK layer is implicitly enabled and available, and cannot be explicitly added or checked via enabled_layers.
|
|
is_available = is_available || strcmp(requested_layer_setting.pLayerName, "MoltenVK") == 0;
|
|
#endif
|
|
|
|
if (!is_available)
|
|
{
|
|
LOGW("Layer: {} not found. Disabling layer setting: {}", requested_layer_setting.pLayerName, requested_layer_setting.pSettingName);
|
|
return false;
|
|
}
|
|
|
|
bool is_already_enabled = std::ranges::any_of(enabled_layer_settings,
|
|
[&requested_layer_setting](VkLayerSettingEXT const &enabled_layer_setting) {
|
|
return (strcmp(requested_layer_setting.pLayerName, enabled_layer_setting.pLayerName) == 0) &&
|
|
(strcmp(requested_layer_setting.pSettingName, enabled_layer_setting.pSettingName) == 0);
|
|
});
|
|
|
|
if (is_already_enabled)
|
|
{
|
|
LOGW("Ignoring duplicated layer setting {} in layer {}.", requested_layer_setting.pSettingName, requested_layer_setting.pLayerName);
|
|
return false;
|
|
}
|
|
|
|
LOGI("Enabling layer setting {} in layer {}.", requested_layer_setting.pSettingName, requested_layer_setting.pLayerName);
|
|
enabled_layer_settings.push_back(requested_layer_setting);
|
|
return true;
|
|
}
|
|
|
|
inline bool enable_layer_setting(VkLayerSettingEXT const &requested_layer_setting,
|
|
std::vector<char const *> const &enabled_layers,
|
|
std::vector<vk::LayerSettingEXT> &enabled_layer_settings)
|
|
{
|
|
return enable_layer_setting(reinterpret_cast<vk::LayerSettingEXT const &>(requested_layer_setting), enabled_layers, enabled_layer_settings);
|
|
}
|
|
|
|
inline bool validate_layers(std::vector<char const *> const &required, std::vector<vk::LayerProperties> const &available)
|
|
{
|
|
for (auto layer : required)
|
|
{
|
|
bool found = false;
|
|
for (auto &available_layer : available)
|
|
{
|
|
if (strcmp(available_layer.layerName, layer) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
LOGE("Validation Layer {} not found", layer);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Instance<bindingType>::Instance(std::string const &application_name,
|
|
std::unordered_map<char const *, bool> const &requested_extensions,
|
|
std::unordered_map<char const *, bool> const &requested_layers,
|
|
std::vector<vk::LayerSettingEXT> const &requested_layer_settings,
|
|
uint32_t api_version)
|
|
{
|
|
std::vector<vk::ExtensionProperties> available_instance_extensions = vk::enumerateInstanceExtensionProperties();
|
|
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
// Check if VK_EXT_debug_utils is supported, which supersedes VK_EXT_Debug_Report
|
|
const bool has_debug_utils = enable_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
bool has_debug_report = false;
|
|
|
|
if (!has_debug_utils)
|
|
{
|
|
has_debug_report = enable_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
if (!has_debug_report)
|
|
{
|
|
LOGW("Neither of {} or {} are available; disabling debug reporting", VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if (defined(VKB_ENABLE_PORTABILITY))
|
|
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
bool portability_enumeration_available = enable_extension(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
#endif
|
|
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
const char *validation_layer_name = "VK_LAYER_KHRONOS_validation";
|
|
# ifdef USE_VALIDATION_LAYER_FEATURES
|
|
bool validation_features = false;
|
|
{
|
|
std::vector<vk::ExtensionProperties> available_layer_instance_extensions = vk::enumerateInstanceExtensionProperties(std::string(validation_layer_name));
|
|
validation_features = enable_extension(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, available_layer_instance_extensions, enabled_extensions);
|
|
}
|
|
# endif // USE_VALIDATION_LAYER_FEATURES
|
|
#endif // USE_VALIDATION_LAYERS
|
|
|
|
// Specific surface extensions are obtained from Window::get_required_surface_extensions
|
|
// They are already added to requested_extensions by VulkanSample::prepare
|
|
|
|
// Even for a headless surface a swapchain is still required
|
|
enable_extension(VK_KHR_SURFACE_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
|
|
// VK_KHR_get_physical_device_properties2 is a prerequisite of VK_KHR_performance_query
|
|
// which will be used for stats gathering where available.
|
|
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
|
|
|
for (auto requested_extension : requested_extensions)
|
|
{
|
|
auto const &extension_name = requested_extension.first;
|
|
auto extension_is_optional = requested_extension.second;
|
|
if (!enable_extension(extension_name, available_instance_extensions, enabled_extensions))
|
|
{
|
|
if (extension_is_optional)
|
|
{
|
|
LOGW("Optional instance extension {} not available, some features may be disabled", extension_name);
|
|
}
|
|
else
|
|
{
|
|
LOGE("Required instance extension {} not available, cannot run", extension_name);
|
|
throw std::runtime_error("Required instance extensions are missing.");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<vk::LayerProperties> supported_layers = vk::enumerateInstanceLayerProperties();
|
|
|
|
std::vector<char const *> enabled_layers;
|
|
|
|
auto layer_error = false;
|
|
for (auto const &requested_layer : requested_layers)
|
|
{
|
|
auto const &layer_name = requested_layer.first;
|
|
auto layer_is_optional = requested_layer.second;
|
|
if (!enable_layer(layer_name, supported_layers, enabled_layers))
|
|
{
|
|
if (layer_is_optional)
|
|
{
|
|
LOGW("Optional layer {} not available, some features may be disabled", layer_name);
|
|
}
|
|
else
|
|
{
|
|
LOGE("Required layer {} not available, cannot run", layer_name);
|
|
throw std::runtime_error("Required layers are missing.");
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
// NOTE: It's important to have the validation layer as the last one here!!!!
|
|
// Otherwise, device creation fails !?!
|
|
enable_layer(validation_layer_name, supported_layers, enabled_layers);
|
|
#endif
|
|
|
|
vk::ApplicationInfo app_info{.pApplicationName = application_name.c_str(), .pEngineName = "Vulkan Samples", .apiVersion = api_version};
|
|
|
|
vk::InstanceCreateInfo instance_info{.pApplicationInfo = &app_info,
|
|
.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size()),
|
|
.ppEnabledLayerNames = enabled_layers.data(),
|
|
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()),
|
|
.ppEnabledExtensionNames = enabled_extensions.data()};
|
|
|
|
std::vector<vk::LayerSettingEXT> enabled_layer_settings;
|
|
|
|
for (auto const &layer_setting : requested_layer_settings)
|
|
{
|
|
enable_layer_setting(layer_setting, enabled_layers, enabled_layer_settings);
|
|
}
|
|
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
vk::DebugUtilsMessengerCreateInfoEXT debug_utils_create_info;
|
|
vk::DebugReportCallbackCreateInfoEXT debug_report_create_info;
|
|
if (has_debug_utils)
|
|
{
|
|
debug_utils_create_info = vk::DebugUtilsMessengerCreateInfoEXT{
|
|
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning,
|
|
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
|
.pfnUserCallback = debug_utils_messenger_callback};
|
|
|
|
instance_info.pNext = &debug_utils_create_info;
|
|
}
|
|
else if (has_debug_report)
|
|
{
|
|
debug_report_create_info = {.flags =
|
|
vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning,
|
|
.pfnCallback = debug_callback};
|
|
|
|
instance_info.pNext = &debug_report_create_info;
|
|
}
|
|
#endif
|
|
|
|
#if (defined(VKB_ENABLE_PORTABILITY))
|
|
if (portability_enumeration_available)
|
|
{
|
|
instance_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
|
}
|
|
#endif
|
|
|
|
// Some of the specialized layers need to be enabled explicitly
|
|
// The validation layer does not need to be enabled in code and it can also be configured using the vulkan configurator.
|
|
#ifdef USE_VALIDATION_LAYER_FEATURES
|
|
|
|
# if defined(VKB_VALIDATION_LAYERS_GPU_ASSISTED)
|
|
const vk::Bool32 setting_validate_gpuav = true;
|
|
if (validation_features)
|
|
{
|
|
enable_layer_setting(vk::LayerSettingEXT(validation_layer_name, "gpuav_enable", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_gpuav), enabled_layers, enabled_layer_settings);
|
|
}
|
|
# endif
|
|
|
|
# if defined(VKB_VALIDATION_LAYERS_BEST_PRACTICES)
|
|
const vk::Bool32 setting_validate_best_practices = true;
|
|
const vk::Bool32 setting_validate_best_practices_arm = true;
|
|
const vk::Bool32 setting_validate_best_practices_amd = true;
|
|
const vk::Bool32 setting_validate_best_practices_img = true;
|
|
const vk::Bool32 setting_validate_best_practices_nvidia = true;
|
|
if (validation_features)
|
|
{
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_arm", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_arm),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_amd", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_amd),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_img", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_img),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(
|
|
validation_layer_name, "validate_best_practices_nvidia", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_nvidia),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
}
|
|
# endif
|
|
|
|
# if defined(VKB_VALIDATION_LAYERS_SYNCHRONIZATION)
|
|
const vk::Bool32 setting_validate_sync = true;
|
|
const vk::Bool32 setting_validate_sync_heuristics = true;
|
|
if (validation_features)
|
|
{
|
|
enable_layer_setting(vk::LayerSettingEXT(validation_layer_name, "validate_sync", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_sync),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
enable_layer_setting(
|
|
vk::LayerSettingEXT(
|
|
validation_layer_name, "syncval_shader_accesses_heuristic", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_sync_heuristics),
|
|
enabled_layers,
|
|
enabled_layer_settings);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
vk::LayerSettingsCreateInfoEXT layerSettingsCreateInfo;
|
|
|
|
// If layer settings are defined, then activate the sample's required layer settings during instance creation
|
|
if (enabled_layer_settings.size() > 0)
|
|
{
|
|
layerSettingsCreateInfo.settingCount = static_cast<uint32_t>(enabled_layer_settings.size());
|
|
layerSettingsCreateInfo.pSettings = enabled_layer_settings.data();
|
|
layerSettingsCreateInfo.pNext = instance_info.pNext;
|
|
instance_info.pNext = &layerSettingsCreateInfo;
|
|
}
|
|
|
|
// Create the Vulkan instance
|
|
handle = vk::createInstance(instance_info);
|
|
|
|
// initialize the Vulkan-Hpp default dispatcher on the instance
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(handle);
|
|
|
|
// Need to load volk for all the not-yet Vulkan-Hpp calls
|
|
volkLoadInstance(handle);
|
|
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
if (has_debug_utils)
|
|
{
|
|
debug_utils_messenger = handle.createDebugUtilsMessengerEXT(debug_utils_create_info);
|
|
}
|
|
else if (has_debug_report)
|
|
{
|
|
debug_report_callback = handle.createDebugReportCallbackEXT(debug_report_create_info);
|
|
}
|
|
#endif
|
|
|
|
query_gpus();
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Instance<bindingType>::Instance(std::string const &application_name,
|
|
std::unordered_map<char const *, bool> const &requested_extensions,
|
|
std::unordered_map<char const *, bool> const &requested_layers,
|
|
std::vector<VkLayerSettingEXT> const &requested_layer_settings,
|
|
uint32_t api_version) :
|
|
Instance<bindingType>(application_name,
|
|
requested_extensions,
|
|
requested_layers,
|
|
reinterpret_cast<std::vector<vk::LayerSettingEXT> const &>(requested_layer_settings),
|
|
api_version)
|
|
{}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Instance<bindingType>::Instance(vk::Instance instance, const std::vector<const char *> &externally_enabled_extensions, bool needsToInitializeDispatcher) :
|
|
handle{instance}
|
|
{
|
|
if (needsToInitializeDispatcher)
|
|
{
|
|
#if defined(_HPP_VULKAN_LIBRARY)
|
|
static vk::detail::DynamicLoader dl(_HPP_VULKAN_LIBRARY);
|
|
#else
|
|
static vk::detail::DynamicLoader dl;
|
|
#endif
|
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
|
}
|
|
|
|
// Some parts of the framework will check for certain extensions to be enabled
|
|
// To make those work we need to copy over externally enabled extensions into this class
|
|
for (auto extension : externally_enabled_extensions)
|
|
{
|
|
enabled_extensions.push_back(extension);
|
|
}
|
|
|
|
if (handle)
|
|
{
|
|
query_gpus();
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Instance not valid");
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Instance<bindingType>::Instance(VkInstance instance, const std::vector<const char *> &externally_enabled_extensions) :
|
|
Instance<bindingType>(static_cast<vk::Instance>(instance), externally_enabled_extensions, true)
|
|
{}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline Instance<bindingType>::~Instance()
|
|
{
|
|
#ifdef USE_VALIDATION_LAYERS
|
|
if (debug_utils_messenger)
|
|
{
|
|
handle.destroyDebugUtilsMessengerEXT(debug_utils_messenger);
|
|
}
|
|
if (debug_report_callback)
|
|
{
|
|
handle.destroyDebugReportCallbackEXT(debug_report_callback);
|
|
}
|
|
#endif
|
|
|
|
if (handle)
|
|
{
|
|
handle.destroy();
|
|
}
|
|
}
|
|
|
|
#if defined(USE_VALIDATION_LAYERS)
|
|
# undef USE_VALIDATION_LAYERS
|
|
#endif
|
|
|
|
#if defined(USE_VALIDATION_LAYER_FEATURES)
|
|
# undef USE_VALIDATION_LAYER_FEATURES
|
|
#endif
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename Instance<bindingType>::PhysicalDeviceType &Instance<bindingType>::get_first_gpu()
|
|
{
|
|
assert(!gpus.empty() && "No physical devices were found on the system.");
|
|
|
|
// Find a discrete GPU
|
|
auto gpuIt = std::ranges::find_if(gpus, [](auto const &gpu) { return gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu; });
|
|
if (gpuIt == gpus.end())
|
|
{
|
|
LOGW("Couldn't find a discrete physical device, picking default GPU");
|
|
gpuIt = gpus.begin();
|
|
}
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return **gpuIt;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::PhysicalDevice &>(**gpuIt);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename Instance<bindingType>::InstanceType Instance<bindingType>::get_handle() const
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return handle;
|
|
}
|
|
else
|
|
{
|
|
return static_cast<VkInstance>(handle);
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline typename Instance<bindingType>::PhysicalDeviceType &Instance<bindingType>::get_suitable_gpu(SurfaceType surface, bool headless_surface)
|
|
{
|
|
assert(!gpus.empty() && "No physical devices were found on the system.");
|
|
|
|
// A GPU can be explicitly selected via the command line (see plugins/gpu_selection.cpp), this overrides the below GPU selection algorithm
|
|
auto gpuIt = gpus.begin();
|
|
if (selected_gpu_index != ~0)
|
|
{
|
|
LOGI("Explicitly selecting GPU {}", selected_gpu_index);
|
|
if (selected_gpu_index > gpus.size() - 1)
|
|
{
|
|
throw std::runtime_error("Selected GPU index is not within no. of available GPUs");
|
|
}
|
|
gpuIt = gpus.begin() + selected_gpu_index;
|
|
}
|
|
else
|
|
{
|
|
if (headless_surface)
|
|
{
|
|
LOGW("Using headless surface with multiple GPUs. Considered explicitly selecting the target GPU.")
|
|
}
|
|
|
|
// Find a discrete GPU
|
|
#if 0
|
|
gpuIt = std::ranges::find_if(gpus,
|
|
[&surface](auto const &gpu) {
|
|
return (gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu) &&
|
|
std::ranges::any_of(std::views::iota(size_t(0), gpu->get_queue_family_properties().size()),
|
|
[&gpu, &surface](auto const &queue_idx) {
|
|
return gpu->get_handle().getSurfaceSupportKHR(static_cast<uint32_t>(queue_idx), surface);
|
|
});
|
|
});
|
|
#else
|
|
gpuIt = std::ranges::find_if(gpus,
|
|
[&surface](auto const &gpu) {
|
|
auto gpu_supports_surface = [&gpu, &surface]() {
|
|
for (uint32_t queue_idx = 0; queue_idx < gpu->get_queue_family_properties().size(); ++queue_idx)
|
|
{
|
|
if (gpu->get_handle().getSurfaceSupportKHR(queue_idx, surface))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
return (gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu) && gpu_supports_surface();
|
|
});
|
|
#endif
|
|
if (gpuIt == gpus.end())
|
|
{
|
|
LOGW("Couldn't find a discrete physical device that supports the surface, picking default GPU");
|
|
gpuIt = gpus.begin();
|
|
}
|
|
}
|
|
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
return **gpuIt;
|
|
}
|
|
else
|
|
{
|
|
return reinterpret_cast<vkb::PhysicalDevice &>(**gpuIt);
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline bool Instance<bindingType>::is_enabled(char const *extension) const
|
|
{
|
|
return std::ranges::find_if(enabled_extensions, [extension](char const *enabled_extension) { return strcmp(extension, enabled_extension) == 0; }) !=
|
|
enabled_extensions.end();
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline void Instance<bindingType>::query_gpus()
|
|
{
|
|
// Querying valid physical devices on the machine
|
|
std::vector<vk::PhysicalDevice> physical_devices = handle.enumeratePhysicalDevices();
|
|
if (physical_devices.empty())
|
|
{
|
|
throw std::runtime_error("Couldn't find a physical device that supports Vulkan.");
|
|
}
|
|
|
|
// Create gpus wrapper objects from the vk::PhysicalDevice's
|
|
for (auto &physical_device : physical_devices)
|
|
{
|
|
if constexpr (bindingType == BindingType::Cpp)
|
|
{
|
|
gpus.push_back(std::make_unique<vkb::core::HPPPhysicalDevice>(*this, physical_device));
|
|
}
|
|
else
|
|
{
|
|
gpus.push_back(std::make_unique<vkb::core::HPPPhysicalDevice>(*reinterpret_cast<vkb::core::InstanceCpp *>(this), physical_device));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <vkb::BindingType bindingType>
|
|
inline std::vector<char const *> const &Instance<bindingType>::get_extensions()
|
|
{
|
|
return enabled_extensions;
|
|
}
|
|
} // namespace core
|
|
} // namespace vkb
|