401 lines
11 KiB
C++
401 lines
11 KiB
C++
#pragma once
|
||
#include "types/types.h"
|
||
|
||
#include <vulkan/vulkan.hpp>
|
||
|
||
namespace mirai {
|
||
/**
|
||
* @brief 交换链支持详情
|
||
*
|
||
* 存储物理设备的交换链支持信息
|
||
*/
|
||
struct swapchain_support_details {
|
||
/// 表面能力
|
||
vk::SurfaceCapabilitiesKHR capabilities{};
|
||
|
||
/// 支持的表面格式列表
|
||
std::vector<vk::SurfaceFormatKHR> formats;
|
||
|
||
/// 支持的呈现模式列表
|
||
std::vector<vk::PresentModeKHR> present_modes;
|
||
|
||
/**
|
||
* @brief 检查交换链支持是否足够
|
||
* @return 如果至少有一个格式和一个呈现模式返回 true
|
||
*/
|
||
[[nodiscard]] bool is_adequate() const noexcept {
|
||
return !formats.empty() && !present_modes.empty();
|
||
}
|
||
|
||
/**
|
||
* @brief 选择最佳的表面格式
|
||
* @param preferred_format 首选格式
|
||
* @param preferred_color_space 首选颜色空间
|
||
* @return 选择的表面格式
|
||
*/
|
||
[[nodiscard]] vk::SurfaceFormatKHR choose_surface_format(
|
||
vk::Format preferred_format = vk::Format::eB8G8R8A8Srgb,
|
||
vk::ColorSpaceKHR preferred_color_space = vk::ColorSpaceKHR::eSrgbNonlinear
|
||
) const {
|
||
// 首先尝试找到首选格式
|
||
for (const auto& format : formats) {
|
||
if (format.format == preferred_format &&
|
||
format.colorSpace == preferred_color_space) {
|
||
return format;
|
||
}
|
||
}
|
||
|
||
// 如果找不到首选格式,返回第一个可用格式
|
||
return formats.empty()
|
||
? vk::SurfaceFormatKHR{
|
||
vk::Format::eB8G8R8A8Unorm,
|
||
vk::ColorSpaceKHR::eSrgbNonlinear
|
||
}
|
||
: formats[0];
|
||
}
|
||
|
||
/**
|
||
* @brief 选择最佳的呈现模式
|
||
* @param preferred_mode 首选模式
|
||
* @return 选择的呈现模式
|
||
*/
|
||
[[nodiscard]] vk::PresentModeKHR choose_present_mode(
|
||
vk::PresentModeKHR preferred_mode = vk::PresentModeKHR::eMailbox
|
||
) const {
|
||
// 首先尝试找到首选模式
|
||
for (const auto& mode : present_modes) {
|
||
if (mode == preferred_mode) {
|
||
return mode;
|
||
}
|
||
}
|
||
|
||
// 如果找不到首选模式,返回 FIFO(总是可用)
|
||
return vk::PresentModeKHR::eFifo;
|
||
}
|
||
|
||
/**
|
||
* @brief 选择交换链范围
|
||
* @param window_width 窗口宽度
|
||
* @param window_height 窗口高度
|
||
* @return 选择的范围
|
||
*/
|
||
[[nodiscard]] vk::Extent2D choose_extent(u32 window_width, u32 window_height) const {
|
||
if (capabilities.currentExtent.width != std::numeric_limits<u32>::max()) {
|
||
return capabilities.currentExtent;
|
||
}
|
||
|
||
vk::Extent2D actual_extent = {window_width, window_height};
|
||
|
||
actual_extent.width = std::clamp(actual_extent.width,
|
||
capabilities.minImageExtent.width,
|
||
capabilities.maxImageExtent.width);
|
||
actual_extent.height = std::clamp(actual_extent.height,
|
||
capabilities.minImageExtent.height,
|
||
capabilities.maxImageExtent.height);
|
||
|
||
return actual_extent;
|
||
}
|
||
|
||
/**
|
||
* @brief 选择图像数量
|
||
* @param preferred_count 首选数量
|
||
* @return 选择的图像数量
|
||
*/
|
||
[[nodiscard]] u32 choose_image_count(u32 preferred_count = 3) const {
|
||
u32 image_count = std::max(preferred_count, capabilities.minImageCount);
|
||
|
||
if (capabilities.maxImageCount > 0) {
|
||
image_count = std::min(image_count, capabilities.maxImageCount);
|
||
}
|
||
|
||
return image_count;
|
||
}
|
||
};
|
||
|
||
swapchain_support_details query_swapchain_support(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface);
|
||
|
||
/// 无效的队列族索引
|
||
constexpr u32 invalid_queue_family = std::numeric_limits<u32>::max();
|
||
/**
|
||
* @brief 队列族索引
|
||
*
|
||
* 存储 Vulkan 设备的各种队列族索引
|
||
*/
|
||
struct queue_family_indices {
|
||
/// 图形队列族索引
|
||
std::optional<u32> graphics_family;
|
||
|
||
/// 呈现队列族索引
|
||
std::optional<u32> present_family;
|
||
|
||
/// 计算队列族索引
|
||
std::optional<u32> compute_family;
|
||
|
||
/// 传输队列族索引
|
||
std::optional<u32> transfer_family;
|
||
|
||
/**
|
||
* @brief 检查是否所有必需的队列族都已找到
|
||
* @return 如果图形和呈现队列族都存在返回 true
|
||
*/
|
||
[[nodiscard]] bool is_complete() const noexcept {
|
||
return graphics_family.has_value() && present_family.has_value();
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否有独立的计算队列族
|
||
* @return 如果有独立的计算队列族返回 true
|
||
*/
|
||
[[nodiscard]] bool has_dedicated_compute() const noexcept {
|
||
return compute_family.has_value() &&
|
||
compute_family.value() != graphics_family.value();
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否有独立的传输队列族
|
||
* @return 如果有独立的传输队列族返回 true
|
||
*/
|
||
[[nodiscard]] bool has_dedicated_transfer() const noexcept {
|
||
return transfer_family.has_value() &&
|
||
transfer_family.value() != graphics_family.value() &&
|
||
transfer_family.value() != compute_family.value_or(invalid_queue_family);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取唯一的队列族索引列表
|
||
* @return 唯一队列族索引的向量
|
||
*/
|
||
[[nodiscard]] std::vector<u32> get_unique_families() const {
|
||
std::vector<u32> unique_families;
|
||
|
||
if (graphics_family.has_value()) {
|
||
unique_families.push_back(graphics_family.value());
|
||
}
|
||
|
||
if (present_family.has_value() &&
|
||
present_family.value() != graphics_family.value_or(invalid_queue_family)) {
|
||
unique_families.push_back(present_family.value());
|
||
}
|
||
|
||
if (compute_family.has_value() &&
|
||
compute_family.value() != graphics_family.value_or(invalid_queue_family) &&
|
||
compute_family.value() != present_family.value_or(invalid_queue_family)) {
|
||
unique_families.push_back(compute_family.value());
|
||
}
|
||
|
||
if (transfer_family.has_value() &&
|
||
transfer_family.value() != graphics_family.value_or(invalid_queue_family) &&
|
||
transfer_family.value() != present_family.value_or(invalid_queue_family) &&
|
||
transfer_family.value() != compute_family.value_or(invalid_queue_family)) {
|
||
unique_families.push_back(transfer_family.value());
|
||
}
|
||
|
||
return unique_families;
|
||
}
|
||
};
|
||
|
||
queue_family_indices find_queue_families(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface);
|
||
|
||
/**
|
||
* @brief 物理设备信息
|
||
*
|
||
* 存储物理设备的详细信息
|
||
*/
|
||
struct physical_device_info {
|
||
/// 物理设备句柄
|
||
vk::PhysicalDevice physical_device;
|
||
|
||
/// 设备属性
|
||
vk::PhysicalDeviceProperties properties{};
|
||
|
||
/// 设备特性
|
||
vk::PhysicalDeviceFeatures features{};
|
||
|
||
/// Vulkan 1.1 特性
|
||
vk::PhysicalDeviceVulkan11Features features_11;
|
||
|
||
/// Vulkan 1.2 特性
|
||
vk::PhysicalDeviceVulkan12Features features_12;
|
||
|
||
/// Vulkan 1.3 特性
|
||
vk::PhysicalDeviceVulkan13Features features_13;
|
||
|
||
/// 设备内存属性
|
||
vk::PhysicalDeviceMemoryProperties memory_properties{};
|
||
|
||
/// 队列族索引
|
||
queue_family_indices queue_families;
|
||
|
||
/// 交换链支持详情
|
||
swapchain_support_details swapchain_support;
|
||
|
||
/// 设备评分(用于选择最佳设备)
|
||
i32 score = 0;
|
||
|
||
/**
|
||
* @brief 获取设备名称
|
||
* @return 设备名称字符串
|
||
*/
|
||
[[nodiscard]] std::string get_device_name() const {
|
||
return std::string(properties.deviceName.data());
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否为独立 GPU
|
||
* @return 如果是独立 GPU 返回 true
|
||
*/
|
||
[[nodiscard]] bool is_discrete_gpu() const noexcept {
|
||
return properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否为集成 GPU
|
||
* @return 如果是集成 GPU 返回 true
|
||
*/
|
||
[[nodiscard]] bool is_integrated_gpu() const noexcept {
|
||
return properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持 Vulkan 1.3
|
||
* @return 如果支持 Vulkan 1.3 返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_vulkan_1_3() const noexcept {
|
||
return properties.apiVersion >= VK_API_VERSION_1_3;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持 Dynamic Rendering
|
||
* @return 如果支持 Dynamic Rendering 返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_dynamic_rendering() const noexcept {
|
||
return features_13.dynamicRendering == vk::True;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持 Synchronization2
|
||
* @return 如果支持 Synchronization2 返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_synchronization2() const noexcept {
|
||
return features_13.synchronization2 == vk::True;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持描述符索引
|
||
* @return 如果支持描述符索引返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_descriptor_indexing() const noexcept {
|
||
return features_12.descriptorIndexing == vk::True;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持 Timeline Semaphores
|
||
* @return 如果支持 Timeline Semaphores 返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_timeline_semaphores() const noexcept {
|
||
return features_12.timelineSemaphore == vk::True;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否支持 Buffer Device Address
|
||
* @return 如果支持 Buffer Device Address 返回 true
|
||
*/
|
||
[[nodiscard]] bool supports_buffer_device_address() const noexcept {
|
||
return features_12.bufferDeviceAddress == vk::True;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查设备是否适合使用
|
||
* @return 如果设备满足最低要求返回 true
|
||
*/
|
||
[[nodiscard]] bool is_suitable() const noexcept {
|
||
return physical_device &&
|
||
queue_families.is_complete() &&
|
||
swapchain_support.is_adequate() &&
|
||
supports_vulkan_1_3() &&
|
||
supports_dynamic_rendering() &&
|
||
supports_synchronization2();
|
||
}
|
||
|
||
/**
|
||
* @brief 计算设备评分
|
||
* @return 设备评分
|
||
*/
|
||
[[nodiscard]] i32 calculate_score() const {
|
||
if (!is_suitable()) {
|
||
return 0;
|
||
}
|
||
|
||
i32 device_score = 0;
|
||
|
||
// 独立 GPU 加分
|
||
if (is_discrete_gpu()) {
|
||
device_score += 1000;
|
||
}
|
||
else if (is_integrated_gpu()) {
|
||
device_score += 100;
|
||
}
|
||
|
||
// 最大纹理尺寸加分
|
||
device_score += static_cast<i32>(properties.limits.maxImageDimension2D / 1000);
|
||
|
||
// 支持可选特性加分
|
||
if (supports_descriptor_indexing()) {
|
||
device_score += 100;
|
||
}
|
||
if (supports_timeline_semaphores()) {
|
||
device_score += 50;
|
||
}
|
||
if (supports_buffer_device_address()) {
|
||
device_score += 50;
|
||
}
|
||
|
||
// 有独立计算队列加分
|
||
if (queue_families.has_dedicated_compute()) {
|
||
device_score += 50;
|
||
}
|
||
|
||
// 有独立传输队列加分
|
||
if (queue_families.has_dedicated_transfer()) {
|
||
device_score += 50;
|
||
}
|
||
|
||
return device_score;
|
||
}
|
||
};
|
||
|
||
physical_device_info populate_physical_device(const vk::PhysicalDevice& physical_device, vk::SurfaceKHR surface);
|
||
|
||
/**
|
||
* @brief 检查是否支持指定的实例扩展
|
||
* @param extension_name 扩展名称
|
||
* @return 如果支持返回 true
|
||
*/
|
||
[[nodiscard]] static bool is_extension_supported(const char* extension_name);
|
||
|
||
/**
|
||
* @brief 检查是否支持指定的验证层
|
||
* @param layer_name 层名称
|
||
* @return 如果支持返回 true
|
||
*/
|
||
[[nodiscard]] static bool is_layer_supported(const char* layer_name);
|
||
|
||
/**
|
||
* @brief 获取所有可用的实例扩展
|
||
* @return 扩展属性列表
|
||
*/
|
||
[[nodiscard]] static std::vector<vk::ExtensionProperties> get_available_extensions();
|
||
|
||
/**
|
||
* @brief 获取所有可用的验证层
|
||
* @return 层属性列表
|
||
*/
|
||
[[nodiscard]] static std::vector<vk::LayerProperties> get_available_layers();
|
||
|
||
/**
|
||
* @brief 获取实例支持的最高 API 版本
|
||
* @return 支持的 API 版本
|
||
*/
|
||
[[nodiscard]] static u32 get_supported_api_version();
|
||
}
|