47VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
48 [[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
49 [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT message_type,
50 const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
51 [[maybe_unused]]
void* user_data)
54 if (VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT & message_severity) level = LogLevel::error;
55 else if (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT & message_severity) level = LogLevel::warning;
56 else if (VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT & message_severity) level = LogLevel::info;
57 else if (VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT & message_severity) level = LogLevel::verbose;
59 vulkan_log_msg(level,
"validation layer: "s + callback_data->pMessage);
61 if (message_type != VkDebugUtilsMessageTypeFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT){
68void check_validation_layers(
const std::vector<const char*>& required_layers) {
69 std::vector<vk::LayerProperties> layers = vk::enumerateInstanceLayerProperties();
72 for (
const auto& req_layer : required_layers) {
73 auto layer_equals = [req_layer](
auto const &layer) {
return strcmp(req_layer, layer.layerName) == 0; };
74 bool found = std::any_of(layers.begin(), layers.end(), layer_equals);
76 throw VulkanError{
"Layer "s + req_layer +
" is not supported."};
81void check_instance_extensions(
const std::vector<const char*>& required_extensions) {
82 std::vector<vk::ExtensionProperties> extensions = vk::enumerateInstanceExtensionProperties(
nullptr);
84 for (
const auto& req_exten : required_extensions) {
85 auto extension_equals = [req_exten](
auto const &exten) {
return strcmp(req_exten, exten.extensionName) == 0; };
86 bool found = std::any_of(extensions.begin(), extensions.end(), extension_equals);
88 throw VulkanError{
"Instance extension "s + req_exten +
" is not supported."};
93bool check_device_extensions(
bool propagate_error,
94 const std::vector<const char*>& required_extensions,
const vk::PhysicalDevice& device)
96 std::vector<vk::ExtensionProperties> extensions = device.enumerateDeviceExtensionProperties(
nullptr);
98 for (
const auto& req_exten : required_extensions) {
99 auto extension_equals = [req_exten](
auto const &exten) {
return strcmp(req_exten, exten.extensionName) == 0; };
100 bool found = std::any_of(extensions.begin(), extensions.end(), extension_equals);
102 if (propagate_error) {
103 throw VulkanError{
"Device extension "s + req_exten +
" is not supported."};
111uint32_t choose_queue_family_index(vk::PhysicalDevice gpu, vk::SurfaceKHR surface) {
114 std::vector<vk::QueueFamilyProperties> families = gpu.getQueueFamilyProperties();
116 for (uint32_t i = 0; i < families.size(); i++) {
117 VkBool32 surface_supported =
true;
119 surface_supported = gpu.getSurfaceSupportKHR(i, surface);
122 if (surface_supported &&
123 (families[i].queueFlags & vk::QueueFlagBits::eGraphics) &&
124 (families[i].queueFlags & vk::QueueFlagBits::eCompute))
132const std::vector required_gpu_extensions = {
"VK_KHR_swapchain" };
134bool is_gpu_suitable(
bool propagate_error, vk::PhysicalDevice gpu, vk::SurfaceKHR surface =
nullptr) {
135 bool result = check_device_extensions(propagate_error, required_gpu_extensions, gpu);
139 uint32_t index = choose_queue_family_index(gpu, surface);
143vk::PhysicalDevice choose_suitable_GPU(
const std::vector<vk::PhysicalDevice>& gpus, vk::SurfaceKHR surface, uint32_t req_device_type) {
145 for (
const auto& gpu : gpus) {
146 auto properties = gpu.getProperties();
147 if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
155 for (
const auto& gpu : gpus) {
156 auto properties = gpu.getProperties();
157 if (properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) {
165 for (
const auto& gpu : gpus) {
174vk::PhysicalDevice choose_gpu_by_index(
const std::vector<vk::PhysicalDevice>& gpus, uint32_t gpu_index) {
175 if (gpu_index >= gpus.size()) {
178 std::vector<std::pair<std::string, vk::PhysicalDevice>> gpu_names;
179 gpu_names.reserve(gpus.size());
181 auto get_gpu_name = [](
auto gpu) -> std::pair<std::string, vk::PhysicalDevice> {
182 return { gpu.getProperties().deviceName, gpu };
185 std::transform(gpus.begin(), gpus.end(), std::back_inserter(gpu_names), get_gpu_name);
187 std::sort(gpu_names.begin(), gpu_names.end());
188 return gpu_names[gpu_index].second;
191vk::CompositeAlphaFlagBitsKHR get_composite_alpha(vk::CompositeAlphaFlagsKHR capabilities) {
193 while (!(result &
static_cast<uint32_t
>(capabilities))) {
196 return static_cast<vk::CompositeAlphaFlagBitsKHR
>(result);
200bool contains(
const std::vector<T>& vec,
const T&
key){
201 return std::find(vec.begin(), vec.end(),
key) != vec.end();
204vk::SurfaceFormatKHR get_surface_format(vk::PhysicalDevice gpu, vk::SurfaceKHR surface) {
205 std::vector<vk::SurfaceFormatKHR> available_formats = gpu.getSurfaceFormatsKHR(surface);
207 std::array<vk::SurfaceFormatKHR, 5> preferred_formats {{
208 {vk::Format::eA2B10G10R10UnormPack32, vk::ColorSpaceKHR::eSrgbNonlinear},
209 {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear},
210 {vk::Format::eR8G8B8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear},
213 for(
auto& format: preferred_formats){
214 if (contains(available_formats, format)) {
219 return available_formats[0];
222vk::PresentModeKHR get_present_mode(vk::PhysicalDevice gpu, vk::SurfaceKHR surface, vk::PresentModeKHR preferred_present_mode) {
223 std::vector<vk::PresentModeKHR> modes = gpu.getSurfacePresentModesKHR(surface);
225 vk::PresentModeKHR preferred = preferred_present_mode;
226 if (contains(modes, preferred)) {
231 auto alternative = (preferred == vk::PresentModeKHR::eImmediate
232 ? vk::PresentModeKHR::eMailbox
233 : vk::PresentModeKHR::eFifo);
235 if (contains(modes, alternative)) {
242void log_gpu_info(vk::PhysicalDeviceProperties
const &gpu_properties, uint32_t vulkan_version){
243 vulkan_log_msg(LogLevel::info,
"Vulkan uses GPU called: "s + &gpu_properties.deviceName[0]);
245 "Used Vulkan API: "s,
246 std::to_string(VK_VERSION_MAJOR(vulkan_version)),
248 std::to_string(VK_VERSION_MINOR(vulkan_version))
253vk::PhysicalDevice create_physical_device(vk::Instance
instance, vk::SurfaceKHR surface, uint32_t gpu_index) {
256 std::vector<vk::PhysicalDevice> gpus =
instance.enumeratePhysicalDevices();
259 return choose_suitable_GPU(gpus, surface, gpu_index);
261 auto gpu = choose_gpu_by_index(gpus, gpu_index);
262 is_gpu_suitable(
true, gpu, surface);
267void create_swapchain_views(vk::Device device, vk::SwapchainKHR swapchain, vk::Format format, std::vector<SwapchainImage>& swapchain_images) {
268 std::vector<vk::Image> images = device.getSwapchainImagesKHR(swapchain);
269 auto image_count =
static_cast<uint32_t
>(images.size());
271 vk::ImageViewCreateInfo image_view_info =
274 swapchain_images.resize(image_count);
275 for (uint32_t i = 0; i < image_count; i++) {
277 swapchain_image.
image = images[i];
279 image_view_info.setImage(swapchain_image.
image);
280 swapchain_image.
view = device.createImageView(image_view_info);
289void VulkanInstance::init(std::vector<const char*>& required_extensions,
bool enable_validation, std::function<
void(
LogLevel, std::string_view sv)> logging_function) {
291 std::vector<const char*> validation_layers{};
292 if (enable_validation) {
293 validation_layers.push_back(
"VK_LAYER_KHRONOS_validation");
294 check_validation_layers(validation_layers);
296 const char* debug_extension = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
297 required_extensions.push_back(debug_extension);
300 check_instance_extensions(required_extensions);
302 vk::ApplicationInfo app_info{};
303 app_info.setApiVersion(VK_API_VERSION_1_1);
306 vk::InstanceCreateInfo instance_info{};
308 .setPApplicationInfo(&app_info)
309 .setEnabledLayerCount(
static_cast<uint32_t
>(validation_layers.size()))
310 .setPpEnabledLayerNames(validation_layers.data())
311 .setEnabledExtensionCount(
static_cast<uint32_t
>(required_extensions.size()))
312 .setPpEnabledExtensionNames(required_extensions.data());
313 auto result = vk::createInstance(&instance_info,
nullptr, &
instance);
316 case vk::Result::eSuccess:
318 case vk::Result::eErrorIncompatibleDriver:
319 app_info.apiVersion = VK_API_VERSION_1_0;
321 result = vk::createInstance(&instance_info,
nullptr, &
instance);
322 if(result != vk::Result::eSuccess){
323 throw VulkanError{
"Vulkan instance cannot be created: "s + vk::to_string(result)};
327 throw VulkanError{
"Vulkan instance cannot be created: "s + vk::to_string(result)};
330 if (enable_validation) {
337 vk::DebugUtilsMessengerCreateInfoEXT messenger_info{};
338 using Severity = vk::DebugUtilsMessageSeverityFlagBitsEXT;
339 using Type = vk::DebugUtilsMessageTypeFlagBitsEXT;
341 .setMessageSeverity(Severity::eError | Severity::eInfo | Severity::eWarning)
342 .setMessageType(Type::eGeneral | Type::ePerformance | Type::eValidation)
343 .setPfnUserCallback(debug_callback)
344 .setPUserData(
nullptr);
351 std::vector<vk::PhysicalDevice> physical_devices =
instance.enumeratePhysicalDevices();
353 gpus.reserve(physical_devices.size());
354 for (
const auto& gpu : physical_devices) {
355 auto properties = gpu.getProperties();
356 gpus.emplace_back(properties.deviceName,
true);
358 std::sort(gpus.begin(), gpus.end());
382 constexpr std::array priorities = { 1.0f };
383 vk::DeviceQueueCreateInfo queue_info{};
386 .setPQueuePriorities(priorities.data())
392 .setQueueCreateInfoCount(1)
393 .setPQueueCreateInfos(&queue_info)
394 .setEnabledExtensionCount(
static_cast<uint32_t
>(required_gpu_extensions.size()))
395 .setPpEnabledExtensionNames(required_gpu_extensions.data());
397 vk::PhysicalDeviceFeatures2 features2{};
398 vk::PhysicalDeviceSamplerYcbcrConversionFeatures yCbCr_feature{};
400 features2.setPNext(&yCbCr_feature);
401 gpu.getFeatures2(&features2);
402 if (yCbCr_feature.samplerYcbcrConversion) {
410 features2 = vk::PhysicalDeviceFeatures2{};
411 features2.setPNext(&yCbCr_feature);
417 constexpr int initialization_attempts = 3;
418 for (
int attempt = 0; attempt < initialization_attempts; attempt++) {
425 vk::Extent2D swapchain_image_size;
434 uint32_t image_count = std::max(uint32_t{2},
capabilities.minImageCount);
436 image_count = std::min(image_count,
capabilities.maxImageCount);
440 "Recreating swapchain, size: "s,
441 std::to_string(swapchain_image_size.width),
443 std::to_string(swapchain_image_size.height),
450 vk::SwapchainCreateInfoKHR swapchain_info{};
456 .setMinImageCount(image_count)
457 .setImageExtent(swapchain_image_size)
458 .setImageArrayLayers(1)
459 .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
460 .setImageSharingMode(vk::SharingMode::eExclusive)
462 .setCompositeAlpha(get_composite_alpha(
swapchain_atributes.capabilities.supportedCompositeAlpha))
464 .setOldSwapchain(old_swapchain);
467 device.destroy(old_swapchain);
468 old_swapchain =
nullptr;
471 catch(std::exception& err){
472 vulkan_log_msg(LogLevel::info,
"Recreation unsuccesful: "s + err.what());
473 device.destroy(old_swapchain);
474 old_swapchain =
nullptr;
475 if(attempt + 1 == initialization_attempts){
484 WindowParameters parameters, uint32_t gpu_index, vk::PresentModeKHR preferredMode)
486 assert(!this->instance);
490 this->vulkan_version =
instance.vulkan_version;
495 this->preferred_present_mode = preferredMode;
498 gpu = create_physical_device(this->instance,
surface, gpu_index);
504 auto properties =
gpu.getProperties();
505 if (properties.apiVersion < VK_API_VERSION_1_1) {
517 vk::FramebufferCreateInfo framebuffer_info;
519 .setRenderPass(render_pass)
526 .setAttachmentCount(1)
527 .setPAttachments(&swapchain_image.view);
528 swapchain_image.framebuffer =
device.createFramebuffer(framebuffer_info);
539 vk::SwapchainKHR old_swap_chain =
swapchain;
546 constexpr uint64_t timeout = 1'000'000'000;
547 uint32_t image_index;
548 auto acquired =
device.acquireNextImageKHR(
swapchain, timeout, acquire_semaphore,
nullptr, &image_index);
550 case vk::Result::eSuccess:
552 case vk::Result::eSuboptimalKHR: [[fallthrough]];
553 case vk::Result::eErrorOutOfDateKHR:
556 case vk::Result::eTimeout:
560 throw VulkanError{
"Next swapchain image cannot be acquired."s + vk::to_string(acquired)};
friend void vulkan_display_detail::VulkanContext::init(VulkanInstance &&, vk::SurfaceKHR, vulkan_display::WindowParameters, uint32_t, vk::PresentModeKHR)
void get_available_gpus(std::vector< std::pair< std::string, bool > > &gpus)
returns all available grafhics cards first parameter is gpu name, second parameter is true only if th...
vk::DebugUtilsMessengerEXT messenger
std::unique_ptr< vk::DispatchLoaderDynamic > dynamic_dispatcher
void init_validation_layers_error_messenger()
void create_logical_device()
void create_swap_chain(vk::SwapchainKHR &&old_swap_chain=vk::SwapchainKHR{})
struct vulkan_display_detail::VulkanContext::@94 swapchain_atributes
void recreate_swapchain(vulkan_display::WindowParameters parameters, vk::RenderPass render_pass)
vk::DebugUtilsMessengerEXT messenger
vk::SwapchainKHR swapchain
std::vector< SwapchainImage > swapchain_images
std::unique_ptr< vk::DispatchLoaderDynamic > dynamic_dispatcher
void destroy_framebuffers()
vk::PresentModeKHR preferred_present_mode
uint32_t acquire_next_swapchain_image(vk::Semaphore acquire_semaphore) const
void destroy_swapchain_views()
uint32_t queue_family_index
void create_framebuffers(vk::RenderPass render_pass)
void init(vulkan_display::VulkanInstance &&instance, vk::SurfaceKHR surface, vulkan_display::WindowParameters window_parameters, uint32_t gpu_index, vk::PresentModeKHR preferred_mode)
vulkan_display::WindowParameters window_parameters
vk::SurfaceCapabilitiesKHR capabilities
std::string concat(size_t expected_result_size, std::array< std::string, count > const &strings)
std::function< void(vulkan_display::LogLevel, std::string_view)> vulkan_log_msg
vk::ImageViewCreateInfo default_image_view_create_info(vk::Format format)
constexpr uint32_t swapchain_image_out_of_date
constexpr uint32_t no_queue_index_found
constexpr uint32_t swapchain_image_timeout
constexpr uint32_t gpu_macro_min
constexpr uint32_t gpu_integrated
constexpr uint32_t gpu_discrete
constexpr uint32_t no_gpu_selected
static class worker_pool instance