TODO 设备创建

This commit is contained in:
2026-01-04 19:40:00 +08:00
parent faca69caea
commit ff5cfb64b5
19 changed files with 1022 additions and 134 deletions

View File

@@ -1,6 +1,7 @@
project(mirai)
find_package(Vulkan REQUIRED)
find_package(unofficial-vulkan-memory-allocator-hpp CONFIG REQUIRED)
find_package(SDL3 CONFIG REQUIRED)
find_package(harfbuzz CONFIG REQUIRED)
find_package(Freetype CONFIG REQUIRED)
@@ -26,6 +27,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
Eigen3::Eigen
spdlog::spdlog
fmt::fmt
unofficial::VulkanMemoryAllocator-Hpp::VulkanMemoryAllocator-Hpp
)
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)

View File

@@ -1,25 +1,45 @@
#pragma once
#include "types/types.h"
#include "resource_types.h"
#include "core/object.h"
#include "gpu_resource/resource_types.h"
#include <vulkan/vulkan.hpp>
#include <vma/vk_mem_alloc.h>
#include <vulkan-memory-allocator-hpp/vk_mem_alloc.hpp>
namespace mirai {
/**
* @brief GPU
*
*
*/
struct allocation_info {
// 分配的大小(字节)
/// 分配的大小字节
u64 size = 0;
// 偏移量(字节)
/// 分配的偏移量
u64 offset = 0;
// 设备内存句柄
/// 设备内存句柄
vk::DeviceMemory device_memory{};
// 映射的内存指针
void* mapped_ptr = nullptr;
// 内存类型索引
/// 映射的指针(如果已映射)
void* mapped_data = nullptr;
/// 内存类型索引
u32 memory_type_index = 0;
// 分配标志
vk::MemoryPropertyFlags property_flags{};
/// 是否为专用分配
bool is_dedicated = false;
};
/**
* @brief Buffer
*/
struct buffer_create_info {
/// Buffer 大小(字节)
u64 size = 0;
/// Buffer 用途
buffer_usage usage = buffer_usage::vertex;
/// 内存用途
memory_usage mem_usage = memory_usage::gpu_only;
/// 是否持久映射
bool persistent_mapped = false;
/// 调试名称
std::string debug_name;
};
struct buffer_allocation_info {
/// Buffer 大小(字节)
@@ -41,12 +61,12 @@ namespace mirai {
/// Vulkan Buffer 句柄
vk::Buffer buffer{};
/// VMA 分配句柄
VmaAllocation allocation = VK_NULL_HANDLE;
vma::Allocation allocation = VK_NULL_HANDLE;
/// 分配信息
allocation_info info;
/// 是否有效
[[nodiscard]] bool is_valid() const noexcept {
return buffer && allocation != VK_NULL_HANDLE;
return buffer && allocation;
}
};
/**
@@ -86,40 +106,13 @@ namespace mirai {
/// Vulkan Image 句柄
vk::Image image{};
/// VMA 分配句柄
VmaAllocation allocation = VK_NULL_HANDLE;
vma::Allocation allocation = VK_NULL_HANDLE;
/// 分配信息
allocation_info info;
/// 是否有效
[[nodiscard]] bool is_valid() const noexcept {
return image && allocation != VK_NULL_HANDLE;
return image && allocation;
}
};
/**
* @brief GPU
*/
struct allocator_config {
/// Vulkan 实例
vk::Instance instance{};
/// 物理设备
vk::PhysicalDevice physical_device{};
/// 逻辑设备
vk::Device device{};
/// Vulkan API 版本
u32 vulkan_api_version = VK_API_VERSION_1_3;
/// 是否启用 Buffer Device Address
bool enable_buffer_device_address = true;
/// 是否启用内存预算跟踪
bool enable_memory_budget = true;
/// 首选的大块分配大小(字节)
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
};
class vma_allocator : public object {
MIRAI_OBJECT_TYPE_INFO(vma_allocator, object)
explicit vma_allocator(const allocator_config& config);
~vma_allocator() override;
};
}

View File

@@ -0,0 +1,155 @@
#include "allocator.h"
#include "resource_types_vulkan.h"
#include "core/logger.h"
#define VMA_IMPLEMENTATION
#include <vma/vk_mem_alloc.h>
#include <vulkan-memory-allocator-hpp/vk_mem_alloc.hpp>
namespace mirai {
result_t<vma::Allocator> create_vma_allocator(const allocator_config& config) {
// 假设你已经有了 instance, physicalDevice, device 和 dl (调度器)
auto& d = config.disp;
// 1. 填充 VMA 需要的函数指针结构体
vma::VulkanFunctions functions{};
functions.vkGetInstanceProcAddr = d.vkGetInstanceProcAddr;
functions.vkGetDeviceProcAddr = d.vkGetDeviceProcAddr;
// 还可以填充其他具体函数,但在 Dynamic 模式下,主要给这两个入口,
// VMA 会尝试自己获取,或者你需要把 d 里面的函数逐个填进去(视 VMA 版本而定)
// 比较稳妥的做法是把 VMA 需要的核心函数都填上:
functions.vkGetPhysicalDeviceProperties = d.vkGetPhysicalDeviceProperties;
functions.vkGetPhysicalDeviceMemoryProperties = d.vkGetPhysicalDeviceMemoryProperties;
functions.vkAllocateMemory = d.vkAllocateMemory;
functions.vkFreeMemory = d.vkFreeMemory;
functions.vkMapMemory = d.vkMapMemory;
functions.vkUnmapMemory = d.vkUnmapMemory;
functions.vkFlushMappedMemoryRanges = d.vkFlushMappedMemoryRanges;
functions.vkInvalidateMappedMemoryRanges = d.vkInvalidateMappedMemoryRanges;
functions.vkBindBufferMemory = d.vkBindBufferMemory;
functions.vkBindImageMemory = d.vkBindImageMemory;
functions.vkGetBufferMemoryRequirements = d.vkGetBufferMemoryRequirements;
functions.vkGetImageMemoryRequirements = d.vkGetImageMemoryRequirements;
functions.vkCreateBuffer = d.vkCreateBuffer;
functions.vkDestroyBuffer = d.vkDestroyBuffer;
functions.vkCreateImage = d.vkCreateImage;
functions.vkDestroyImage = d.vkDestroyImage;
functions.vkCmdCopyBuffer = d.vkCmdCopyBuffer;
// 如果开启了 bufferDeviceAddress还需要相关的函数...
// 2. 创建 Allocator
vma::AllocatorCreateInfo allocator_info{};
allocator_info.vulkanApiVersion = VK_API_VERSION_1_3;
allocator_info.physicalDevice = config.physical_device;
allocator_info.device = config.device;
allocator_info.instance = config.instance;
allocator_info.pVulkanFunctions = &functions; // <--- 关键:告诉 VMA 用动态加载的函数
allocator_info.preferredLargeHeapBlockSize = config.preferred_large_heap_block_size;
// allocatorInfo.flags = vma::AllocatorCreateFlagBits::eBufferDeviceAddress; // 如果你要用 BDA
// 启用功能标志
vma::AllocatorCreateFlags flags;
if (config.enable_buffer_device_address) {
flags |= vma::AllocatorCreateFlagBits::eBufferDeviceAddress;
}
if (config.enable_memory_budget) {
flags |= vma::AllocatorCreateFlagBits::eExtMemoryBudget;
}
allocator_info.flags = flags;
// 创建 RAII 风格的 Allocator
auto [result, allocator] = vma::createAllocator(allocator_info);
if (result != vk::Result::eSuccess) {
return MAKE_ERROR_INFO(error_code::vulkan_init_failed, "Failed to create VMA Allocator: {}", to_string(result));
}
return allocator;
}
void vma_allocator::setup(const allocator_config& config) {
instance_ = config.instance;
physical_device_ = config.physical_device;
device_ = config.device;
enable_buffer_device_address_ = config.enable_buffer_device_address;
auto result = create_vma_allocator(config);
if (!result) {
MIRAI_LOG_ERROR("VMA 分配器初始化失败: {}", result.error().full_description());
throw std::runtime_error("VMA Allocator initialization failed");
}
vma_allocator_ = result.value();
}
vma::MemoryUsage vma_allocator::to_vma_memory_usage(memory_usage usage) noexcept {
switch (usage) {
case memory_usage::gpu_only:
return vma::MemoryUsage::eGpuOnly;
case memory_usage::cpu_only:
return vma::MemoryUsage::eCpuOnly;
case memory_usage::cpu_to_gpu:
return vma::MemoryUsage::eCpuToGpu;
case memory_usage::gpu_to_cpu:
return vma::MemoryUsage::eGpuToCpu;
case memory_usage::auto_prefer_device:
return vma::MemoryUsage::eAutoPreferDevice;
case memory_usage::auto_prefer_host:
return vma::MemoryUsage::eAutoPreferHost;
default:
return vma::MemoryUsage::eAuto;
}
}
vma::AllocationCreateFlags
vma_allocator::to_vma_allocation_flags(memory_usage usage, bool persistent_mapped) noexcept {
vma::AllocationCreateFlags flags;
// 持久映射
if (persistent_mapped) {
flags |= vma::AllocationCreateFlagBits::eMapped;
}
// 根据内存用途设置访问模式
switch (usage) {
case memory_usage::cpu_only:
case memory_usage::cpu_to_gpu:
flags |= vma::AllocationCreateFlagBits::eHostAccessSequentialWrite;
break;
case memory_usage::gpu_to_cpu:
flags |= vma::AllocationCreateFlagBits::eHostAccessRandom;
break;
default:
break;
}
return flags;
}
result_t<buffer_allocation> vma_allocator::alloc_buffer(const buffer_create_info& info) {
buffer_allocation alloc;
vk::BufferCreateInfo buffer_info{};
buffer_info.size = info.size;
buffer_info.usage = to_vulkan_buffer_usage(info.usage);
buffer_info.sharingMode = vk::SharingMode::eExclusive;
if (enable_buffer_device_address_ && has_flag(info.usage, buffer_usage::shader_device_address)) {
buffer_info.usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
}
vma::AllocationCreateInfo alloc_info{};
alloc_info.usage = to_vma_memory_usage(info.mem_usage);
alloc_info.flags = to_vma_allocation_flags(info.mem_usage, info.persistent_mapped);
vma::AllocationInfo vma_alloc_info{};
vk::Result vk_result;
vk::Buffer vk_buffer;
{
std::lock_guard lock(mutex_);
// vk_result = vma::UniqueBuffer(vma_allocator_, &buffer_info, &alloc_info, reinterpret_cast<VkBuffer*>(&vk_buffer),
// reinterpret_cast<VmaAllocation*>(&alloc.allocation), &vma_alloc_info);
}
}
}

View File

@@ -0,0 +1,64 @@
#pragma once
#include <mutex>
#include "types/types.h"
#include "resource_types.h"
#include "core/object.h"
#include <vulkan/vulkan.hpp>
#include "allocation_types.h"
#include "render/vulkan_types.h"
#include "types/error.h"
namespace mirai {
/**
* @brief GPU 分配器配置
*/
struct allocator_config {
/// Vulkan 实例
vk::Instance instance{};
/// 物理设备
vk::PhysicalDevice physical_device{};
/// 逻辑设备
vk::Device device{};
/// Vulkan API 版本
u32 vulkan_api_version = VK_API_VERSION_1_3;
/// 是否启用 Buffer Device Address
bool enable_buffer_device_address = true;
/// 是否启用内存预算跟踪
bool enable_memory_budget = true;
/// 首选的大块分配大小(字节)
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
vk_dispatch_loader& disp;
};
class vma_allocator : public object {
MIRAI_OBJECT_TYPE_INFO(vma_allocator, object)
static auto& get() noexcept {
static vma_allocator instance;
return instance;
}
explicit vma_allocator() = default;
~vma_allocator() override;
void setup(const allocator_config& config);
vma::MemoryUsage to_vma_memory_usage(memory_usage usage) noexcept;
vma::AllocationCreateFlags to_vma_allocation_flags(memory_usage usage, bool persistent_mapped) noexcept;
result_t<buffer_allocation> alloc_buffer(const buffer_create_info& info);
private:
vk::Instance instance_{};
vk::PhysicalDevice physical_device_;
vk::Device device_;
vma::Allocator vma_allocator_;
vma::DefragmentationContext defrag_context_;
bool enable_buffer_device_address_ = true;
std::atomic<u64> total_allocated_bytes_{0};
std::atomic<u64> allocation_count_{0};
mutable std::mutex mutex_;
};
}

View File

@@ -0,0 +1,21 @@
#include "gpu_buffer.h"
#include "allocator.h"
namespace mirai {
gpu_buffer::gpu_buffer(const buffer_create_info& info) {
vma_allocator::get();
}
bool gpu_buffer::is_host_visible() const noexcept {
switch (mem_usage_) {
case memory_usage::cpu_only:
case memory_usage::cpu_to_gpu:
case memory_usage::gpu_to_cpu:
case memory_usage::auto_prefer_host:
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "resource_types.h"
#include "core/object.h"
#include "gpu_resource/allocation_types.h"
namespace mirai {
class gpu_buffer : public object {
MIRAI_OBJECT_TYPE_INFO(gpu_buffer, object)
gpu_buffer(const buffer_create_info& info);
private:
[[nodiscard]] bool is_host_visible() const noexcept;
buffer_allocation allocation_;
// buffer申请大小
u64 size_ = 0;
// buffer用途
buffer_usage usage_ = buffer_usage::vertex;
// 内存用途
memory_usage mem_usage_ = memory_usage::gpu_only;
// 是否持久映射
bool persistent_mapped_ = false;
// 映射的指针
void* mapped_data_ = nullptr;
// 调试名称
std::string debug_name_;
};
}

View File

@@ -3,34 +3,34 @@
namespace mirai {
[[nodiscard]] constexpr auto to_vulkan_buffer_usage(buffer_usage usage) noexcept {
vk::BufferUsageFlags2 flags{};
vk::BufferUsageFlags flags{};
if (has_flag(usage, buffer_usage::vertex)) {
flags |= vk::BufferUsageFlagBits2::eVertexBuffer;
flags |= vk::BufferUsageFlagBits::eVertexBuffer;
}
if (has_flag(usage, buffer_usage::index)) {
flags |= vk::BufferUsageFlagBits2::eIndexBuffer;
flags |= vk::BufferUsageFlagBits::eIndexBuffer;
}
if (has_flag(usage, buffer_usage::uniform)) {
flags |= vk::BufferUsageFlagBits2::eUniformBuffer;
flags |= vk::BufferUsageFlagBits::eUniformBuffer;
}
if (has_flag(usage, buffer_usage::storage)) {
flags |= vk::BufferUsageFlagBits2::eStorageBuffer;
flags |= vk::BufferUsageFlagBits::eStorageBuffer;
}
if (has_flag(usage, buffer_usage::staging)) {
flags |= vk::BufferUsageFlagBits2::eTransferSrc | vk::BufferUsageFlagBits2::eTransferDst;
flags |= vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
}
if (has_flag(usage, buffer_usage::indirect)) {
flags |= vk::BufferUsageFlagBits2::eIndirectBuffer;
flags |= vk::BufferUsageFlagBits::eIndirectBuffer;
}
if (has_flag(usage, buffer_usage::transfer_src)) {
flags |= vk::BufferUsageFlagBits2::eTransferSrc;
flags |= vk::BufferUsageFlagBits::eTransferSrc;
}
if (has_flag(usage, buffer_usage::transfer_dst)) {
flags |= vk::BufferUsageFlagBits2::eTransferDst;
flags |= vk::BufferUsageFlagBits::eTransferDst;
}
if (has_flag(usage, buffer_usage::shader_device_address)) {
flags |= vk::BufferUsageFlagBits2::eShaderDeviceAddress;
flags |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
}
return flags;
}

View File

@@ -1 +0,0 @@
#include "allocator.h"

151
src/render/device_utils.cpp Normal file
View File

@@ -0,0 +1,151 @@
#include "device_utils.h"
#include "core/logger.h"
namespace mirai {
swapchain_support_details query_swapchain_support(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface) {
swapchain_support_details details;
if (!surface) {
return details;
}
// 获取表面能力
auto [result_caps, capabilities] = physical_device.getSurfaceCapabilitiesKHR(surface);
if (result_caps == vk::Result::eSuccess) {
details.capabilities = capabilities;
}
// 获取表面格式
auto [result_formats, formats] = physical_device.getSurfaceFormatsKHR(surface);
if (result_formats == vk::Result::eSuccess) {
details.formats = formats;
}
// 获取呈现模式
auto [result_modes, modes] = physical_device.getSurfacePresentModesKHR(surface);
if (result_modes == vk::Result::eSuccess) {
details.present_modes = modes;
}
return details;
}
queue_family_indices find_queue_families(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface) {
queue_family_indices indices;
std::vector<vk::QueueFamilyProperties> queue_families = physical_device.getQueueFamilyProperties();
for (u32 i = 0; i < queue_families.size(); ++i) {
const auto& queue_family = queue_families[i];
// 检查图形支持
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
indices.graphics_family = i;
}
// 检查呈现支持
if (surface) {
auto [result, present_support] = physical_device.getSurfaceSupportKHR(i, surface);
if (result == vk::Result::eSuccess && present_support) {
indices.present_family = i;
}
}
// 检查独立计算队列
if ((queue_family.queueFlags & vk::QueueFlagBits::eCompute) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
indices.compute_family = i;
}
else if (queue_family.queueFlags & vk::QueueFlagBits::eCompute) {
if (!indices.compute_family.has_value()) {
indices.compute_family = i;
}
}
// 检查独立传输队列
if ((queue_family.queueFlags & vk::QueueFlagBits::eTransfer) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eGraphics) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eCompute)) {
indices.transfer_family = i;
}
else if (queue_family.queueFlags & vk::QueueFlagBits::eTransfer) {
if (!indices.transfer_family.has_value()) {
indices.transfer_family = i;
}
}
}
return indices;
}
physical_device_info populate_physical_device(const vk::PhysicalDevice& physical_device, vk::SurfaceKHR surface) {
physical_device_info info;
info.physical_device = physical_device;
// 获取基本属性和特性
info.properties = physical_device.getProperties();
info.features = physical_device.getFeatures();
info.memory_properties = physical_device.getMemoryProperties();
// 获取 Vulkan 1.1/1.2/1.3 特性
vk::PhysicalDeviceFeatures2 features2{};
features2.setPNext(&info.features_11);
info.features_11.setPNext(&info.features_12);
info.features_12.setPNext(&info.features_13);
physical_device.getFeatures2(&features2);
// 获取队列族
info.queue_families = find_queue_families(physical_device, surface);
// 获取交换链支持
if (surface) {
info.swapchain_support = query_swapchain_support(physical_device, surface);
}
return info;
}
bool is_extension_supported(const char* extension_name) {
const auto& extensions = get_available_extensions();
return std::ranges::any_of(extensions, [extension_name](const vk::ExtensionProperties& ext) {
return std::strcmp(ext.extensionName, extension_name) == 0;
});
}
bool is_layer_supported(const char* layer_name) {
const auto& layers = get_available_layers();
return std::ranges::any_of(layers, [layer_name](const vk::LayerProperties& layer) {
return std::strcmp(layer.layerName, layer_name) == 0;
});
}
std::vector<vk::ExtensionProperties> get_available_extensions() {
auto [result, extensions] = vk::enumerateInstanceExtensionProperties();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance extensions: {}", vk::to_string(result));
return {};
}
return extensions;
}
std::vector<vk::LayerProperties> get_available_layers() {
auto [result, layers] = vk::enumerateInstanceLayerProperties();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance layers: {}", vk::to_string(result));
return {};
}
return layers;
}
u32 get_supported_api_version() {
auto [result, version] = vk::enumerateInstanceVersion();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to get supported Vulkan API version: {}", vk::to_string(result));
return VK_API_VERSION_1_0;
}
return version;
}
}

400
src/render/device_utils.h Normal file
View File

@@ -0,0 +1,400 @@
#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();
}

View File

@@ -0,0 +1,23 @@
#include "vulkan_context.h"
#include "core/logger.h"
namespace mirai {
void vulkan_context::setup(const vulkan_context_config& config) {
}
void vulkan_context::create_default_device(const vk::SurfaceKHR& surface) {
auto infos = instance_->get_physical_devices_info(surface);
for (const auto& info : infos) {
if (info.is_suitable()) {
MIRAI_LOG_INFO("Selected GPU: {} (score: {})", info.get_device_name(), info.score);
break;
}
}
vulkan_device_config config{
};
auto device = make_obj<vulkan_device>(config);
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "vulkan_device.h"
#include "vulkan_instance.h"
#include "core/object.h"
namespace mirai {
struct vulkan_context_config {
/// Vulkan 实例配置
vulkan_instance_config instance_config;
};
class vulkan_context : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_context, object)
public:
static auto& get() noexcept {
static vulkan_context instance;
return instance;
}
void setup(const vulkan_context_config& config);
void create_default_device(const vk::SurfaceKHR& surface);
private:
vk_dynamic_loader dl;
std::shared_ptr<vulkan_instance> instance_;
std::vector<std::shared_ptr<vulkan_device>> devices_;
};
}

View File

@@ -0,0 +1,23 @@
#include "vulkan_device.h"
#include "gpu_resource/allocator.h"
mirai::vulkan_device::vulkan_device(const vulkan_device_config& config) {
physical_device_ = config.physical_device;
auto proc_addr = config.dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
disp_.init(proc_addr);
disp_.init(device_);
allocator_config alloc_config{
config.instance,
physical_device_,
device_,
config.vulkan_api_version,
config.enable_buffer_device_address,
config.enable_memory_budget,
config.preferred_large_heap_block_size,
disp_
};
vma_allocator::get().setup(alloc_config);
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "vulkan_types.h"
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
class vma_allocator;
}
namespace mirai {
struct vulkan_device_config {
vk::Instance instance;
vk::PhysicalDevice physical_device;
/// Vulkan API 版本
u32 vulkan_api_version = VK_API_VERSION_1_3;
/// 是否启用 Buffer Device Address
bool enable_buffer_device_address = true;
/// 是否启用内存预算跟踪
bool enable_memory_budget = true;
/// 首选的大块分配大小(字节)
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
/// Vulkan 动态加载器引用
vk_dynamic_loader& dl;
};
class vulkan_device : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_device, object)
public:
vulkan_device(const vulkan_device_config& config);
private:
// 逻辑设备
vk::Device device_;
vk_dispatch_loader disp_;
// std::shared_ptr<vma_allocator> allocator_; // 先使用全局分配器这里保存一个变量用于未来多GPU支持
// 保存物理设备映射
vk::PhysicalDevice physical_device_;
};
}

View File

@@ -1,8 +1,5 @@
#include "vulkan_instance.h"
#include "vulkan_types.h"
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
#include <set>
#include <SDL3/SDL.h>
#include <SDL3/SDL_vulkan.h>
@@ -36,54 +33,40 @@ namespace mirai {
config_ = config;
}
bool vulkan_instance::is_extension_supported(const char* extension_name) {
const auto& extensions = get_available_extensions();
return std::ranges::any_of(extensions, [extension_name](const vk::ExtensionProperties& ext) {
return std::strcmp(ext.extensionName, extension_name) == 0;
});
}
bool vulkan_instance::is_layer_supported(const char* layer_name) {
const auto& layers = get_available_layers();
return std::ranges::any_of(layers, [layer_name](const vk::LayerProperties& layer) {
return std::strcmp(layer.layerName, layer_name) == 0;
});
}
std::vector<vk::ExtensionProperties> vulkan_instance::get_available_extensions() {
auto [result, extensions] = vk::enumerateInstanceExtensionProperties();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance extensions: {}", vk::to_string(result));
std::vector<vk::PhysicalDevice> vulkan_instance::enumerate_physical_devices() const {
if (!instance_) {
return {};
}
return extensions;
}
std::vector<vk::LayerProperties> vulkan_instance::get_available_layers() {
auto [result, layers] = vk::enumerateInstanceLayerProperties();
auto [result, devices] = instance_.enumeratePhysicalDevices();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance layers: {}", vk::to_string(result));
return {};
}
return layers;
return devices;
}
u32 vulkan_instance::get_supported_api_version() {
auto [result, version] = vk::enumerateInstanceVersion();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("Failed to get supported Vulkan API version: {}", vk::to_string(result));
return VK_API_VERSION_1_0;
std::vector<physical_device_info> vulkan_instance::get_physical_devices_info(vk::SurfaceKHR surface) const {
auto devices = enumerate_physical_devices();
std::vector<physical_device_info> infos;
infos.reserve(devices.size());
for (const auto& device : devices) {
auto info = populate_physical_device(device, surface);
info.score = info.calculate_score();
infos.push_back(std::move(info));
}
return version;
auto pred = [](const physical_device_info& a, const physical_device_info& b) {
return a.score > b.score;
};
// 按评分排序(降序)
std::ranges::sort(infos, pred);
return infos;
}
void vulkan_instance::on_created() {
void vulkan_instance::on_created() {
object::on_created();
{
const vk_dynamic_loader dl;
auto proc_addr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(proc_addr);
}
auto result = create_instance(config_);
if (!result.has_value()) {
@@ -196,13 +179,13 @@ namespace mirai {
return MAKE_ERROR_INFO(error_code::vulkan_init_failed,
"Failed to create Vulkan instance: {}", vk::to_string(result));
}
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
instance_ = instance;
MIRAI_LOG_INFO("Vulkan instance created with API version {}.{}.{}",
VK_VERSION_MAJOR(api_version_),
VK_VERSION_MINOR(api_version_),
VK_VERSION_PATCH(api_version_));
return {};
}

View File

@@ -5,6 +5,9 @@
#include <vulkan/vulkan.hpp>
#include <functional>
#include "device_utils.h"
#include "vulkan_types.h"
namespace mirai {
/**
* @brief 调试消息严重级别
@@ -69,7 +72,6 @@ namespace mirai {
explicit vulkan_instance(const vulkan_instance_config& config = {});
// ============================================================================================
// 扩展和层查询
// ============================================================================================
@@ -78,57 +80,22 @@ namespace mirai {
* @brief 获取已启用的实例扩展列表
* @return 扩展名称列表
*/
[[nodiscard]] const std::vector<const char*>& get_enabled_extensions() const noexcept {
return enabled_extensions_;
}
[[nodiscard]] const std::vector<const char*>& get_enabled_extensions() const noexcept { return enabled_extensions_; }
/**
* @brief 获取已启用的验证层列表
* @return 层名称列表
*/
[[nodiscard]] const std::vector<const char*>& get_enabled_layers() const noexcept {
return enabled_layers_;
}
/**
* @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();
[[nodiscard]] const std::vector<const char*>& get_enabled_layers() const noexcept { return enabled_layers_; }
/**
* @brief 获取要求的 API 版本
* @return API 版本
*/
[[nodiscard]] u32 get_api_version() const noexcept {
return api_version_;
}
[[nodiscard]] u32 get_api_version() const noexcept { return api_version_; }
/**
* @brief 获取实例支持的最高 API 版本
* @return 支持的 API 版本
*/
[[nodiscard]] static u32 get_supported_api_version();
[[nodiscard]] std::vector<vk::PhysicalDevice> enumerate_physical_devices() const;
[[nodiscard]] std::vector<physical_device_info> get_physical_devices_info(vk::SurfaceKHR surface = {}) const;
protected:
void on_created() override;
void on_destroying() override;

View File

@@ -4,6 +4,8 @@
#if VK_HEADER_VERSION >= 304
using vk_dynamic_loader = vk::detail::DynamicLoader;
using vk_dispatch_loader = vk::detail::DispatchLoaderDynamic;
#else
using vk_dynamic_loader = vk::DynamicLoader;
using vk_dispatch_loader = vk::DispatchLoaderDynamic;
#endif

View File

@@ -35,6 +35,12 @@
"version>=": "12.2.0",
"features": ["freetype"]
},
{
"name": "vulkan-hpp"
},
{
"name": "vulkan-memory-allocator-hpp"
},
{
"name": "yoga",
"version>=": "3.2.1"