From ff5cfb64b5834e542c530bf37ed439cc794cb4ba Mon Sep 17 00:00:00 2001 From: nanako <469449812@qq.com> Date: Sun, 4 Jan 2026 19:40:00 +0800 Subject: [PATCH] =?UTF-8?q?TODO=20=E8=AE=BE=E5=A4=87=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CMakeLists.txt | 2 + .../allocation_types.h} | 77 ++-- src/gpu_resource/allocator.cpp | 155 +++++++ src/gpu_resource/allocator.h | 64 +++ src/gpu_resource/gpu_buffer.cpp | 21 + src/gpu_resource/gpu_buffer.h | 29 ++ src/{render => gpu_resource}/resource_types.h | 0 .../resource_types_vulkan.h | 20 +- src/render/allocator.cpp | 1 - src/render/device_utils.cpp | 151 +++++++ src/render/device_utils.h | 400 ++++++++++++++++++ src/render/vulkan_context.cpp | 23 + src/render/vulkan_context.h | 29 ++ src/render/vulkan_device.cpp | 23 + src/render/vulkan_device.h | 41 ++ src/render/vulkan_instance.cpp | 63 +-- src/render/vulkan_instance.h | 49 +-- src/render/vulkan_types.h | 2 + vcpkg.json | 6 + 19 files changed, 1022 insertions(+), 134 deletions(-) rename src/{render/allocator.h => gpu_resource/allocation_types.h} (61%) create mode 100644 src/gpu_resource/allocator.cpp create mode 100644 src/gpu_resource/allocator.h create mode 100644 src/gpu_resource/gpu_buffer.cpp create mode 100644 src/gpu_resource/gpu_buffer.h rename src/{render => gpu_resource}/resource_types.h (100%) rename src/{render => gpu_resource}/resource_types_vulkan.h (97%) delete mode 100644 src/render/allocator.cpp create mode 100644 src/render/device_utils.cpp create mode 100644 src/render/device_utils.h create mode 100644 src/render/vulkan_context.cpp create mode 100644 src/render/vulkan_context.h create mode 100644 src/render/vulkan_device.cpp create mode 100644 src/render/vulkan_device.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 49b392c..12d51fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/render/allocator.h b/src/gpu_resource/allocation_types.h similarity index 61% rename from src/render/allocator.h rename to src/gpu_resource/allocation_types.h index 8237767..473b34a 100644 --- a/src/render/allocator.h +++ b/src/gpu_resource/allocation_types.h @@ -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 -#include +#include 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; - - }; } diff --git a/src/gpu_resource/allocator.cpp b/src/gpu_resource/allocator.cpp new file mode 100644 index 0000000..f1e88b9 --- /dev/null +++ b/src/gpu_resource/allocator.cpp @@ -0,0 +1,155 @@ +#include "allocator.h" + +#include "resource_types_vulkan.h" +#include "core/logger.h" + +#define VMA_IMPLEMENTATION +#include +#include + +namespace mirai { + result_t 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 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(&vk_buffer), + // reinterpret_cast(&alloc.allocation), &vma_alloc_info); + } + } +} diff --git a/src/gpu_resource/allocator.h b/src/gpu_resource/allocator.h new file mode 100644 index 0000000..922b2a6 --- /dev/null +++ b/src/gpu_resource/allocator.h @@ -0,0 +1,64 @@ +#pragma once +#include + +#include "types/types.h" +#include "resource_types.h" +#include "core/object.h" + +#include + +#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 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 total_allocated_bytes_{0}; + std::atomic allocation_count_{0}; + mutable std::mutex mutex_; + }; +} diff --git a/src/gpu_resource/gpu_buffer.cpp b/src/gpu_resource/gpu_buffer.cpp new file mode 100644 index 0000000..78a0efc --- /dev/null +++ b/src/gpu_resource/gpu_buffer.cpp @@ -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; + } + } +} diff --git a/src/gpu_resource/gpu_buffer.h b/src/gpu_resource/gpu_buffer.h new file mode 100644 index 0000000..68167d0 --- /dev/null +++ b/src/gpu_resource/gpu_buffer.h @@ -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_; + }; +} diff --git a/src/render/resource_types.h b/src/gpu_resource/resource_types.h similarity index 100% rename from src/render/resource_types.h rename to src/gpu_resource/resource_types.h diff --git a/src/render/resource_types_vulkan.h b/src/gpu_resource/resource_types_vulkan.h similarity index 97% rename from src/render/resource_types_vulkan.h rename to src/gpu_resource/resource_types_vulkan.h index 2f19c02..fe5584d 100644 --- a/src/render/resource_types_vulkan.h +++ b/src/gpu_resource/resource_types_vulkan.h @@ -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; } diff --git a/src/render/allocator.cpp b/src/render/allocator.cpp deleted file mode 100644 index 30c4efa..0000000 --- a/src/render/allocator.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "allocator.h" diff --git a/src/render/device_utils.cpp b/src/render/device_utils.cpp new file mode 100644 index 0000000..72c7e37 --- /dev/null +++ b/src/render/device_utils.cpp @@ -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 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 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 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; + } +} diff --git a/src/render/device_utils.h b/src/render/device_utils.h new file mode 100644 index 0000000..522a140 --- /dev/null +++ b/src/render/device_utils.h @@ -0,0 +1,400 @@ +#pragma once +#include "types/types.h" + +#include + +namespace mirai { + /** + * @brief 交换链支持详情 + * + * 存储物理设备的交换链支持信息 + */ + struct swapchain_support_details { + /// 表面能力 + vk::SurfaceCapabilitiesKHR capabilities{}; + + /// 支持的表面格式列表 + std::vector formats; + + /// 支持的呈现模式列表 + std::vector 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::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::max(); + /** + * @brief 队列族索引 + * + * 存储 Vulkan 设备的各种队列族索引 + */ + struct queue_family_indices { + /// 图形队列族索引 + std::optional graphics_family; + + /// 呈现队列族索引 + std::optional present_family; + + /// 计算队列族索引 + std::optional compute_family; + + /// 传输队列族索引 + std::optional 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 get_unique_families() const { + std::vector 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(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 get_available_extensions(); + + /** + * @brief 获取所有可用的验证层 + * @return 层属性列表 + */ + [[nodiscard]] static std::vector get_available_layers(); + + /** + * @brief 获取实例支持的最高 API 版本 + * @return 支持的 API 版本 + */ + [[nodiscard]] static u32 get_supported_api_version(); +} diff --git a/src/render/vulkan_context.cpp b/src/render/vulkan_context.cpp new file mode 100644 index 0000000..3b2ab74 --- /dev/null +++ b/src/render/vulkan_context.cpp @@ -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(config); + } +} diff --git a/src/render/vulkan_context.h b/src/render/vulkan_context.h new file mode 100644 index 0000000..5ca31e7 --- /dev/null +++ b/src/render/vulkan_context.h @@ -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 instance_; + std::vector> devices_; + }; +} diff --git a/src/render/vulkan_device.cpp b/src/render/vulkan_device.cpp new file mode 100644 index 0000000..e2ccc9b --- /dev/null +++ b/src/render/vulkan_device.cpp @@ -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("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); +} diff --git a/src/render/vulkan_device.h b/src/render/vulkan_device.h new file mode 100644 index 0000000..f311bc5 --- /dev/null +++ b/src/render/vulkan_device.h @@ -0,0 +1,41 @@ +#pragma once +#include "vulkan_types.h" +#include "core/object.h" + +#include + +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 allocator_; // 先使用全局分配器,这里保存一个变量用于未来多GPU支持 + + // 保存物理设备映射 + vk::PhysicalDevice physical_device_; + }; +} diff --git a/src/render/vulkan_instance.cpp b/src/render/vulkan_instance.cpp index 98d2d68..7e233ba 100644 --- a/src/render/vulkan_instance.cpp +++ b/src/render/vulkan_instance.cpp @@ -1,8 +1,5 @@ #include "vulkan_instance.h" -#include "vulkan_types.h" -VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE - #include #include #include @@ -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 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 vulkan_instance::enumerate_physical_devices() const { + if (!instance_) { return {}; } - return extensions; - } - std::vector 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 vulkan_instance::get_physical_devices_info(vk::SurfaceKHR surface) const { + auto devices = enumerate_physical_devices(); + std::vector 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("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 {}; } diff --git a/src/render/vulkan_instance.h b/src/render/vulkan_instance.h index dc9a9e2..208e062 100644 --- a/src/render/vulkan_instance.h +++ b/src/render/vulkan_instance.h @@ -5,6 +5,9 @@ #include #include +#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& get_enabled_extensions() const noexcept { - return enabled_extensions_; - } + [[nodiscard]] const std::vector& get_enabled_extensions() const noexcept { return enabled_extensions_; } /** * @brief 获取已启用的验证层列表 * @return 层名称列表 */ - [[nodiscard]] const std::vector& 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 get_available_extensions(); - - /** - * @brief 获取所有可用的验证层 - * @return 层属性列表 - */ - [[nodiscard]] static std::vector get_available_layers(); + [[nodiscard]] const std::vector& 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 enumerate_physical_devices() const; + [[nodiscard]] std::vector get_physical_devices_info(vk::SurfaceKHR surface = {}) const; protected: void on_created() override; void on_destroying() override; diff --git a/src/render/vulkan_types.h b/src/render/vulkan_types.h index 3670ccb..b88342c 100644 --- a/src/render/vulkan_types.h +++ b/src/render/vulkan_types.h @@ -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 diff --git a/vcpkg.json b/vcpkg.json index 9e259ee..9d76f08 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -35,6 +35,12 @@ "version>=": "12.2.0", "features": ["freetype"] }, + { + "name": "vulkan-hpp" + }, + { + "name": "vulkan-memory-allocator-hpp" + }, { "name": "yoga", "version>=": "3.2.1"