Files
Vulkan-Samples/framework/core/device.h
T
2025-09-04 10:54:47 +08:00

774 lines
32 KiB
C++

/* Copyright (c) 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/hpp_vk_common.h"
#include "common/vk_common.h"
#include "core/buffer.h"
#include "core/hpp_physical_device.h"
#include "hpp_debug.h"
#include "hpp_fence_pool.h"
#include "hpp_queue.h"
#include "hpp_resource_cache.h"
#include "queue.h"
#include <utility>
#include <vulkan/vulkan.hpp>
namespace vkb
{
class FencePool;
class DebugUtils;
class PhysicalDevice;
class ResourceCache;
namespace core
{
template <vkb::BindingType bindingType>
class CommandPool;
using CommandPoolC = CommandPool<vkb::BindingType::C>;
using CommandPoolCpp = CommandPool<vkb::BindingType::Cpp>;
template <vkb::BindingType bindingType>
class Device
: public vkb::core::VulkanResource<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Device, VkDevice>::type>
{
public:
using Bool32Type = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Bool32, VkBool32>::type;
using BufferCopyType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCopy, VkBufferCopy>::type;
using CommandBufferLevelType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBufferLevel, VkCommandBufferLevel>::type;
using CommandBufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBuffer, VkCommandBuffer>::type;
using CommandPoolCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandPoolCreateFlags, VkCommandPoolCreateFlags>::type;
using CommandPoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandPool, VkCommandPool>::type;
using DeviceMemoryType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceMemory, VkDeviceMemory>::type;
using DeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Device, VkDevice>::type;
using Extent2DType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Extent2D, VkExtent2D>::type;
using FenceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Fence, VkFence>::type;
using FormatType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Format, VkFormat>::type;
using ImageType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Image, VkImage>::type;
using ImageUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageUsageFlags, VkImageUsageFlags>::type;
using MemoryPropertyFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::MemoryPropertyFlags, VkMemoryPropertyFlags>::type;
using QueueFamilyPropertiesType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFamilyProperties, VkQueueFamilyProperties>::type;
using QueueFlagBitsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFlagBits, VkQueueFlagBits>::type;
using QueueFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFlags, VkQueueFlags>::type;
using QueueType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Queue, VkQueue>::type;
using ResultType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Result, VkResult>::type;
using SemaphoreType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Semaphore, VkSemaphore>::type;
using SurfaceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SurfaceKHR, VkSurfaceKHR>::type;
using DebugUtilsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPDebugUtils, vkb::DebugUtils>::type;
using FencePoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::HPPFencePool, vkb::FencePool>::type;
using PhysicalDeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPPhysicalDevice, vkb::PhysicalDevice>::type;
using CoreQueueType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPQueue, vkb::Queue>::type;
using ResourceCacheType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::HPPResourceCache, vkb::ResourceCache>::type;
public:
/**
* @brief Device constructor
* @param gpu A valid Vulkan physical device and the requested gpu features
* @param surface The surface
* @param debug_utils The debug utils to be associated to this device
* @param requested_extensions (Optional) List of required device extensions and whether support is optional or not
*/
Device(PhysicalDeviceType &gpu,
SurfaceType surface,
std::unique_ptr<DebugUtilsType> &&debug_utils,
std::unordered_map<const char *, bool> const &requested_extensions = {});
/**
* @brief Device constructor
* @param gpu A valid Vulkan physical device and the requested gpu features
* @param vulkan_device A valid Vulkan device
* @param surface The surface
*/
Device(PhysicalDeviceType &gpu, DeviceType &vulkan_device, SurfaceType surface);
Device(const Device &) = delete;
Device(Device &&) = delete;
~Device();
Device &operator=(const Device &) = delete;
Device &operator=(Device &&) = delete;
void add_queue(size_t global_index, uint32_t family_index, QueueFamilyPropertiesType const &properties, Bool32Type can_present);
void copy_buffer(
vkb::core::Buffer<bindingType> const &src, vkb::core::Buffer<bindingType> &dst, QueueType queue, BufferCopyType const *copy_region = nullptr);
CommandBufferType create_command_buffer(CommandBufferLevelType level, bool begin = false) const;
CommandPoolType create_command_pool(uint32_t queue_index, CommandPoolCreateFlagsType flags = 0);
std::pair<ImageType, DeviceMemoryType> create_image(
FormatType format, Extent2DType const &extent, uint32_t mip_levels, ImageUsageFlagsType usage, MemoryPropertyFlagsType properties) const;
void create_internal_command_pool();
void create_internal_fence_pool();
void flush_command_buffer(CommandBufferType command_buffer, QueueType queue, bool free = true, SemaphoreType signal_semaphore = VK_NULL_HANDLE) const;
vkb::core::CommandPool<bindingType> &get_command_pool() const;
DebugUtilsType const &get_debug_utils() const;
FencePoolType &get_fence_pool() const;
PhysicalDeviceType const &get_gpu() const;
CoreQueueType const &get_queue(uint32_t queue_family_index, uint32_t queue_index) const;
CoreQueueType const &get_queue_by_flags(QueueFlagsType queue_flags, uint32_t queue_index) const;
CoreQueueType const &get_queue_by_present(uint32_t queue_index) const;
ResourceCacheType &get_resource_cache();
bool is_extension_enabled(const char *extension) const;
bool is_image_format_supported(FormatType format) const;
void wait_idle() const;
private:
void copy_buffer_impl(vk::Device device, vkb::core::BufferCpp const &src, vkb::core::BufferCpp &dst, vk::Queue queue, vk::BufferCopy const *copy_region);
vk::CommandBuffer create_command_buffer_impl(vk::Device device, vk::CommandBufferLevel level, bool begin) const;
std::pair<vk::Image, vk::DeviceMemory> create_image_impl(
vk::Device device, vk::Format format, vk::Extent2D const &extent, uint32_t mip_levels, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties)
const;
void flush_command_buffer_impl(
vk::Device device, vk::CommandBuffer command_buffer, vk::Queue queue, bool free = true, vk::Semaphore signal_semaphore = nullptr) const;
vkb::core::HPPQueue const &get_queue_by_flags_impl(vk::QueueFlags queue_flags, uint32_t queue_index) const;
void init(std::unordered_map<const char *, bool> const &requested_extensions);
private:
std::unique_ptr<vkb::core::CommandPoolCpp> command_pool;
std::unique_ptr<vkb::core::HPPDebugUtils> debug_utils;
std::vector<const char *> enabled_extensions{};
std::unique_ptr<vkb::HPPFencePool> fence_pool;
vkb::core::HPPPhysicalDevice &gpu;
std::vector<std::vector<vkb::core::HPPQueue>> queues;
vkb::HPPResourceCache resource_cache;
vk::SurfaceKHR surface = nullptr;
};
using DeviceC = Device<vkb::BindingType::C>;
using DeviceCpp = Device<vkb::BindingType::Cpp>;
} // namespace core
} // namespace vkb
#include "core/command_pool.h"
namespace vkb
{
namespace core
{
template <>
inline Device<vkb::BindingType::Cpp>::Device(vkb::core::HPPPhysicalDevice &gpu,
vk::SurfaceKHR surface,
std::unique_ptr<vkb::core::HPPDebugUtils> &&debug_utils,
std::unordered_map<const char *, bool> const &requested_extensions) :
vkb::core::VulkanResourceCpp<vk::Device>{nullptr, this}, debug_utils{std::move(debug_utils)}, gpu{gpu}, resource_cache{*this}, surface(surface)
{
init(requested_extensions);
}
template <>
inline Device<vkb::BindingType::C>::Device(vkb::PhysicalDevice &gpu,
VkSurfaceKHR surface,
std::unique_ptr<vkb::DebugUtils> &&debug_utils,
std::unordered_map<const char *, bool> const &requested_extensions) :
vkb::core::VulkanResourceC<VkDevice>{VK_NULL_HANDLE, this}, debug_utils{reinterpret_cast<vkb::core::HPPDebugUtils *>(debug_utils.release())}, gpu{reinterpret_cast<vkb::core::HPPPhysicalDevice &>(gpu)}, resource_cache{*reinterpret_cast<vkb::core::DeviceCpp *>(this)}, surface(static_cast<vk::SurfaceKHR>(surface))
{
init(requested_extensions);
}
template <>
inline Device<vkb::BindingType::Cpp>::Device(vkb::core::HPPPhysicalDevice &gpu, vk::Device &vulkan_device, vk::SurfaceKHR surface) :
VulkanResource{vulkan_device}, gpu{gpu}, surface{surface}, resource_cache{*this}
{
debug_utils = std::make_unique<HPPDummyDebugUtils>();
}
template <>
inline Device<vkb::BindingType::C>::Device(vkb::PhysicalDevice &gpu, VkDevice &vulkan_device, VkSurfaceKHR surface) :
VulkanResource{vulkan_device}, gpu{reinterpret_cast<vkb::core::HPPPhysicalDevice &>(gpu)}, resource_cache{*reinterpret_cast<vkb::core::DeviceCpp *>(this)}, surface{static_cast<vk::SurfaceKHR>(surface)}
{
debug_utils = std::make_unique<HPPDummyDebugUtils>();
}
template <vkb::BindingType bindingType>
inline Device<bindingType>::~Device()
{
resource_cache.clear();
command_pool.reset();
fence_pool.reset();
vkb::allocated::shutdown();
if (this->get_handle())
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_handle().destroy();
}
else
{
static_cast<vk::Device>(this->get_handle()).destroy();
}
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::add_queue(size_t global_index, uint32_t family_index, QueueFamilyPropertiesType const &properties, Bool32Type can_present)
{
if (queues.size() <= global_index)
{
queues.resize(global_index + 1);
}
if constexpr (bindingType == vkb::BindingType::Cpp)
{
queues[global_index].emplace_back(*this, family_index, properties, can_present, 0);
}
else
{
queues[global_index].emplace_back(*reinterpret_cast<vkb::core::DeviceCpp *>(this),
family_index,
reinterpret_cast<vk::QueueFamilyProperties const &>(properties),
static_cast<vk::Bool32>(can_present),
0);
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::copy_buffer(vkb::core::Buffer<bindingType> const &src,
vkb::core::Buffer<bindingType> &dst,
QueueType queue,
BufferCopyType const *copy_region)
{
assert(dst.get_size() <= src.get_size());
assert(src.get_handle());
if constexpr (bindingType == vkb::BindingType::Cpp)
{
copy_buffer_impl(this->get_handle(), src, dst, queue, copy_region);
}
else
{
copy_buffer_impl(static_cast<vk::Device>(this->get_handle()), reinterpret_cast<vkb::core::BufferCpp const &>(src),
reinterpret_cast<vkb::core::BufferCpp &>(dst),
static_cast<vk::Queue>(queue),
reinterpret_cast<vk::BufferCopy const *>(copy_region));
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::CommandBufferType Device<bindingType>::create_command_buffer(CommandBufferLevelType level, bool begin) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return create_command_buffer_impl(this->get_handle(), level, begin);
}
else
{
return static_cast<VkCommandBuffer>(
create_command_buffer_impl(static_cast<vk::Device>(this->get_handle()), static_cast<vk::CommandBufferLevel>(level), begin));
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::CommandPoolType Device<bindingType>::create_command_pool(uint32_t queue_index, CommandPoolCreateFlagsType flags)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
vk::CommandPoolCreateInfo command_pool_info{.flags = flags, .queueFamilyIndex = queue_index};
return this->get_handle().createCommandPool(command_pool_info);
}
else
{
vk::CommandPoolCreateInfo command_pool_info{.flags = static_cast<vk::CommandPoolCreateFlags>(flags), .queueFamilyIndex = queue_index};
return static_cast<vk::Device>(this->get_handle()).createCommandPool(command_pool_info);
}
}
template <vkb::BindingType bindingType>
inline std::pair<typename Device<bindingType>::ImageType, typename Device<bindingType>::DeviceMemoryType> Device<bindingType>::create_image(
FormatType format, Extent2DType const &extent, uint32_t mip_levels, ImageUsageFlagsType usage, MemoryPropertyFlagsType properties) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return create_image_impl(this->get_handle(), format, extent, mip_levels, usage, properties);
}
else
{
return static_cast<std::pair<VkImage, VkDeviceMemory>>(create_image_impl(static_cast<vk::Device>(this->get_handle()),
static_cast<VkFormat>(format),
static_cast<VkExtent2D>(extent),
mip_levels,
static_cast<VkImageUsageFlags>(usage),
static_cast<VkMemoryPropertyFlags>(properties)));
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::create_internal_command_pool()
{
uint32_t familyIndex = get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index();
if constexpr (bindingType == vkb::BindingType::Cpp)
{
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(*this, familyIndex);
}
else
{
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(*reinterpret_cast<vkb::core::DeviceCpp *>(this), familyIndex);
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::create_internal_fence_pool()
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
fence_pool = std::make_unique<vkb::HPPFencePool>(*this);
}
else
{
fence_pool = std::make_unique<vkb::HPPFencePool>(*reinterpret_cast<vkb::core::DeviceCpp *>(this));
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::flush_command_buffer(CommandBufferType command_buffer, QueueType queue, bool free, SemaphoreType signal_semaphore) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
flush_command_buffer_impl(this->get_handle(), command_buffer, queue, free, signal_semaphore);
}
else
{
flush_command_buffer_impl(static_cast<vk::Device>(this->get_handle()),
static_cast<vk::CommandBuffer>(command_buffer),
static_cast<vk::Queue>(queue),
free,
static_cast<vk::Semaphore>(signal_semaphore));
}
}
template <vkb::BindingType bindingType>
inline vkb::core::CommandPool<bindingType> &Device<bindingType>::get_command_pool() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return *command_pool;
}
else
{
return reinterpret_cast<vkb::core::CommandPoolC &>(*command_pool);
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::DebugUtilsType const &Device<bindingType>::get_debug_utils() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return *debug_utils;
}
else
{
return reinterpret_cast<vkb::DebugUtils const &>(*debug_utils);
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::FencePoolType &Device<bindingType>::get_fence_pool() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return *fence_pool;
}
else
{
return reinterpret_cast<vkb::FencePool &>(*fence_pool);
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::PhysicalDeviceType const &Device<bindingType>::get_gpu() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return gpu;
}
else
{
return reinterpret_cast<vkb::PhysicalDevice const &>(gpu);
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue(uint32_t queue_family_index, uint32_t queue_index) const
{
assert(queue_family_index < queues.size() && "Queue family index out of bounds");
assert(queue_index < queues[queue_family_index].size() && "Queue index out of bounds");
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return queues[queue_family_index][queue_index];
}
else
{
return reinterpret_cast<vkb::Queue const &>(queues[queue_family_index][queue_index]);
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue_by_flags(QueueFlagsType required_queue_flags,
uint32_t queue_index) const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return get_queue_by_flags_impl(required_queue_flags, queue_index);
}
else
{
return reinterpret_cast<vkb::Queue const &>(get_queue_by_flags_impl(static_cast<vk::QueueFlags>(required_queue_flags), queue_index));
}
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue_by_present(uint32_t queue_index) const
{
auto queueIt =
std::ranges::find_if(queues,
[queue_index](const std::vector<vkb::core::HPPQueue> &queue_family) { return !queue_family.empty() && queue_index < queue_family[0].get_properties().queueCount && queue_family[0].support_present(); });
if (queueIt != queues.end())
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return (*queueIt)[queue_index];
}
else
{
return reinterpret_cast<vkb::Queue const &>((*queueIt)[queue_index]);
}
}
throw std::runtime_error("Queue not found");
}
template <vkb::BindingType bindingType>
inline typename Device<bindingType>::ResourceCacheType &Device<bindingType>::get_resource_cache()
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return resource_cache;
}
else
{
return reinterpret_cast<vkb::ResourceCache &>(resource_cache);
}
}
template <vkb::BindingType bindingType>
inline bool Device<bindingType>::is_extension_enabled(const char *extension) const
{
return std::ranges::find_if(enabled_extensions, [extension](const char *enabled_extension) { return strcmp(extension, enabled_extension) == 0; }) !=
enabled_extensions.end();
}
template <vkb::BindingType bindingType>
inline bool Device<bindingType>::is_image_format_supported(FormatType format) const
{
// as we want to check for an error (vk::Result::eErrorFormatNotSupported) we use the non-throwing version of getImageFormatProperties here
vk::ImageFormatProperties format_properties;
return vk::Result::eErrorFormatNotSupported !=
gpu.get_handle().getImageFormatProperties(
static_cast<vk::Format>(format), vk::ImageType::e2D, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled, {}, &format_properties);
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::wait_idle() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
this->get_handle().waitIdle();
}
else
{
static_cast<vk::Device>(this->get_handle()).waitIdle();
}
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::copy_buffer_impl(
vk::Device device, vkb::core::BufferCpp const &src, vkb::core::BufferCpp &dst, vk::Queue queue, vk::BufferCopy const *copy_region)
{
vk::CommandBuffer command_buffer = create_command_buffer_impl(device, vk::CommandBufferLevel::ePrimary, true);
vk::BufferCopy buffer_copy;
if (copy_region == nullptr)
{
buffer_copy.size = src.get_size();
}
else
{
buffer_copy = *copy_region;
}
command_buffer.copyBuffer(src.get_handle(), dst.get_handle(), buffer_copy);
flush_command_buffer_impl(device, command_buffer, queue);
}
template <vkb::BindingType bindingType>
inline vk::CommandBuffer Device<bindingType>::create_command_buffer_impl(vk::Device device, vk::CommandBufferLevel level, bool begin) const
{
assert(command_pool && "No command pool exists in the device");
vk::CommandBufferAllocateInfo command_buffer_allocate_info{.commandPool = command_pool->get_handle(), .level = level, .commandBufferCount = 1};
vk::CommandBuffer command_buffer = device.allocateCommandBuffers(command_buffer_allocate_info).front();
// If requested, also start recording for the new command buffer
if (begin)
{
command_buffer.begin(vk::CommandBufferBeginInfo());
}
return command_buffer;
}
template <vkb::BindingType bindingType>
inline std::pair<vk::Image, vk::DeviceMemory> Device<bindingType>::create_image_impl(
vk::Device device, vk::Format format, vk::Extent2D const &extent, uint32_t mip_levels, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties) const
{
vk::ImageCreateInfo image_create_info{.imageType = vk::ImageType::e2D,
.format = format,
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
.mipLevels = mip_levels,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = usage};
vk::Image image = device.createImage(image_create_info);
vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image);
vk::MemoryAllocateInfo memory_allocation{.allocationSize = memory_requirements.size,
.memoryTypeIndex = gpu.get_memory_type(memory_requirements.memoryTypeBits, properties)};
vk::DeviceMemory memory = device.allocateMemory(memory_allocation);
device.bindImageMemory(image, memory, 0);
return std::make_pair(image, memory);
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::flush_command_buffer_impl(
vk::Device device, vk::CommandBuffer command_buffer, vk::Queue queue, bool free, vk::Semaphore signal_semaphore) const
{
if (command_buffer)
{
command_buffer.end();
vk::SubmitInfo submit_info{.commandBufferCount = 1, .pCommandBuffers = &command_buffer};
if (signal_semaphore)
{
submit_info.setSignalSemaphores(signal_semaphore);
}
// Create fence to ensure that the command buffer has finished executing
vk::Fence fence = device.createFence({});
// Submit to the queue
queue.submit(submit_info, fence);
// Wait for the fence to signal that command buffer has finished executing
vk::Result result = device.waitForFences(fence, true, DEFAULT_FENCE_TIMEOUT);
if (result != vk::Result::eSuccess)
{
LOGE("Detected Vulkan error: {}", vkb::to_string(result));
abort();
}
device.destroyFence(fence);
if (command_pool && free)
{
device.freeCommandBuffers(command_pool->get_handle(), command_buffer);
}
}
}
template <vkb::BindingType bindingType>
vkb::core::HPPQueue const &Device<bindingType>::get_queue_by_flags_impl(vk::QueueFlags required_queue_flags, uint32_t queue_index) const
{
auto queueIt =
std::ranges::find_if(queues,
[required_queue_flags, queue_index](const std::vector<vkb::core::HPPQueue> &queue) {
assert(!queue.empty());
vk::QueueFamilyProperties const &properties = queue[0].get_properties();
return ((properties.queueFlags & required_queue_flags) == required_queue_flags) && (queue_index < properties.queueCount);
});
if (queueIt == queues.end())
{
throw std::runtime_error("Queue not found");
}
return (*queueIt)[queue_index];
}
template <vkb::BindingType bindingType>
inline void Device<bindingType>::init(std::unordered_map<const char *, bool> const &requested_extensions)
{
LOGI("Selected GPU: {}", *gpu.get_properties().deviceName);
// Prepare the device queues
std::vector<vk::QueueFamilyProperties> queue_family_properties = gpu.get_queue_family_properties();
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
std::vector<std::vector<float>> queue_priorities;
queue_create_infos.reserve(queue_family_properties.size());
queue_priorities.reserve(queue_family_properties.size());
for (uint32_t queue_family_index = 0U; queue_family_index < queue_family_properties.size(); ++queue_family_index)
{
auto const &queue_family_property = queue_family_properties[queue_family_index];
queue_priorities.push_back(std::vector<float>(queue_family_property.queueCount, 0.5f));
if (gpu.has_high_priority_graphics_queue() &&
(vkb::common::get_queue_family_index(queue_family_properties, vk::QueueFlagBits::eGraphics) == queue_family_index))
{
queue_priorities.back()[0] = 1.0f;
}
queue_create_infos.push_back({.queueFamilyIndex = queue_family_index,
.queueCount = queue_family_property.queueCount,
.pQueuePriorities = queue_priorities[queue_family_index].data()});
}
// Check extensions to enable Vma Dedicated Allocation
bool can_get_memory_requirements = gpu.is_extension_supported("VK_KHR_get_memory_requirements2");
bool has_dedicated_allocation = gpu.is_extension_supported("VK_KHR_dedicated_allocation");
if (can_get_memory_requirements && has_dedicated_allocation)
{
enabled_extensions.push_back("VK_KHR_get_memory_requirements2");
enabled_extensions.push_back("VK_KHR_dedicated_allocation");
LOGI("Dedicated Allocation enabled");
}
// For performance queries, we also use host query reset since queryPool resets cannot
// live in the same command buffer as beginQuery
if (gpu.is_extension_supported("VK_KHR_performance_query") &&
gpu.is_extension_supported("VK_EXT_host_query_reset"))
{
auto perf_counter_features = gpu.get_extension_features<vk::PhysicalDevicePerformanceQueryFeaturesKHR>();
auto host_query_reset_features = gpu.get_extension_features<vk::PhysicalDeviceHostQueryResetFeatures>();
if (perf_counter_features.performanceCounterQueryPools && host_query_reset_features.hostQueryReset)
{
gpu.add_extension_features<vk::PhysicalDevicePerformanceQueryFeaturesKHR>().performanceCounterQueryPools = VK_TRUE;
gpu.add_extension_features<vk::PhysicalDeviceHostQueryResetFeatures>().hostQueryReset = VK_TRUE;
enabled_extensions.push_back("VK_KHR_performance_query");
enabled_extensions.push_back("VK_EXT_host_query_reset");
LOGI("Performance query enabled");
}
}
// Check that extensions are supported before trying to create the device
std::vector<const char *> unsupported_extensions{};
for (auto &extension : requested_extensions)
{
if (gpu.is_extension_supported(extension.first))
{
enabled_extensions.emplace_back(extension.first);
}
else
{
unsupported_extensions.emplace_back(extension.first);
}
}
if (enabled_extensions.size() > 0)
{
LOGI("Device supports the following requested extensions:");
for (auto &extension : enabled_extensions)
{
LOGI(" \t{}", extension);
}
}
if (unsupported_extensions.size() > 0)
{
auto error = false;
for (auto &extension : unsupported_extensions)
{
auto extIt = requested_extensions.find(extension);
assert(extIt != requested_extensions.end());
if (extIt->second)
{
LOGW("Optional device extension {} not available, some features may be disabled", extension);
}
else
{
LOGE("Required device extension {} not available, cannot run", extension);
error = true;
}
}
if (error)
{
throw VulkanException(VK_ERROR_EXTENSION_NOT_PRESENT, "Extensions not present");
}
}
// Latest requested feature will have the pNext's all set up for device creation.
vk::DeviceCreateInfo create_info{.pNext = gpu.get_extension_feature_chain(),
.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size()),
.pQueueCreateInfos = queue_create_infos.data(),
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()),
.ppEnabledExtensionNames = enabled_extensions.data(),
.pEnabledFeatures = &gpu.get_requested_features()};
this->set_handle(gpu.get_handle().createDevice(create_info));
queues.resize(queue_family_properties.size());
for (uint32_t queue_family_index = 0U; queue_family_index < queue_family_properties.size(); ++queue_family_index)
{
const vk::QueueFamilyProperties &queue_family_property = gpu.get_queue_family_properties()[queue_family_index];
vk::Bool32 present_supported = gpu.get_handle().getSurfaceSupportKHR(queue_family_index, surface);
for (uint32_t queue_index = 0U; queue_index < queue_family_property.queueCount; ++queue_index)
{
if constexpr (bindingType == BindingType::Cpp)
{
queues[queue_family_index].emplace_back(*this, queue_family_index, queue_family_property, present_supported, queue_index);
}
else
{
queues[queue_family_index].emplace_back(
*reinterpret_cast<vkb::core::DeviceCpp *>(this), queue_family_index, queue_family_property, present_supported, queue_index);
}
}
}
vkb::allocated::init(*this);
if constexpr (bindingType == BindingType::Cpp)
{
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(
*this, get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index());
fence_pool = std::make_unique<vkb::HPPFencePool>(*this);
}
else
{
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(
*reinterpret_cast<vkb::core::DeviceCpp *>(this),
get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index());
fence_pool = std::make_unique<vkb::HPPFencePool>(*reinterpret_cast<vkb::core::DeviceCpp *>(this));
}
}
} // namespace core
} // namespace vkb