diff --git a/plans/design_vulkan_queues.md b/plans/design_vulkan_queues.md new file mode 100644 index 0000000..8f7e6c2 --- /dev/null +++ b/plans/design_vulkan_queues.md @@ -0,0 +1,242 @@ +# Vulkan 队列封装设计方案 + +## 1. 背景与目标 + +当前 `src/render/vulkan_device` 在初始化时虽然创建了必要的 Vulkan 队列(图形、计算、传输),但并未将这些队列句柄保存或封装暴露给上层使用。目前的实现中,`transfer_task` 等组件需要使用队列时无法方便地获取。 + +本设计旨在封装 `vulkan_graphics_queue`、`vulkan_compute_queue` 和 `vulkan_transfer_queue`,明确它们的职责,提供统一的提交和同步接口,并集成到 `vulkan_device` 的初始化流程中。 + +## 2. 类结构设计 + +### 2.1 类图概览 + +```mermaid +classDiagram + class object { + <> + } + + class vulkan_queue { + -vk::Queue queue + -u32 family_index + -u32 queue_index + -vk::Device device + +submit(...) + +wait_idle() + +get_handle() + +get_family_index() + } + + class vulkan_graphics_queue { + +submit(command_buffers, wait_semaphores, signal_semaphores, fence) + } + + class vulkan_compute_queue { + +submit(command_buffers, wait_semaphores, signal_semaphores, fence) + } + + class vulkan_transfer_queue { + +submit(command_buffers, wait_semaphores, signal_semaphores, fence) + } + + class vulkan_device { + -graphics_queue: shared_ptr~vulkan_graphics_queue~ + -compute_queue: shared_ptr~vulkan_compute_queue~ + -transfer_queue: shared_ptr~vulkan_transfer_queue~ + +get_graphics_queue() + +get_compute_queue() + +get_transfer_queue() + } + + object <|-- vulkan_queue + vulkan_queue <|-- vulkan_graphics_queue + vulkan_queue <|-- vulkan_compute_queue + vulkan_queue <|-- vulkan_transfer_queue + vulkan_device *-- vulkan_graphics_queue + vulkan_device *-- vulkan_compute_queue + vulkan_device *-- vulkan_transfer_queue +``` + +### 2.2 核心类定义 + +建议在 `src/render/vulkan_queue.h` 中定义。 + +#### 基类 `vulkan_queue` + +作为所有队列的基类,继承自 `mirai::object`。 + +```cpp +namespace mirai { + class vulkan_queue : public object { + MIRAI_OBJECT_TYPE_INFO(vulkan_queue, object) + public: + vulkan_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index); + + // 基础提交接口 + void submit( + const std::vector& command_buffers, + const std::vector& wait_semaphores = {}, + const std::vector& wait_stages = {}, + const std::vector& signal_semaphores = {}, + vk::Fence fence = nullptr + ); + + // Vulkan 1.3 Synchronization2 提交接口 (建议支持) + void submit2( + const std::vector& command_buffers, + const std::vector& wait_semaphores = {}, + const std::vector& signal_semaphores = {}, + vk::Fence fence = nullptr + ); + + void wait_idle(); + + [[nodiscard]] vk::Queue get_handle() const noexcept { return queue_; } + [[nodiscard]] u32 get_family_index() const noexcept { return family_index_; } + [[nodiscard]] u32 get_queue_index() const noexcept { return queue_index_; } + + protected: + vk::Device device_; + vk::Queue queue_; + u32 family_index_; + u32 queue_index_; + }; +} +``` + +#### 子类实现 + +子类主要用于类型区分,未来可以针对不同类型的队列添加特定的辅助方法(例如图形队列的呈现操作)。 + +* **`vulkan_graphics_queue`**: 处理图形渲染命令。 +* **`vulkan_compute_queue`**: 处理计算着色器命令。 +* **`vulkan_transfer_queue`**: 处理缓冲/图像拷贝命令。 + +```cpp +namespace mirai { + class vulkan_graphics_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_graphics_queue, vulkan_queue) + public: + using vulkan_queue::vulkan_queue; + // 未来可添加 present 相关接口 + }; + + class vulkan_compute_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_compute_queue, vulkan_queue) + public: + using vulkan_queue::vulkan_queue; + }; + + class vulkan_transfer_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_transfer_queue, vulkan_queue) + public: + using vulkan_queue::vulkan_queue; + }; +} +``` + +## 3. 集成与初始化 + +### 3.1 修改 `vulkan_device` + +在 `src/render/vulkan_device.h` 中添加成员变量和访问器。 + +```cpp +class vulkan_device : public object { + // ... 现有代码 ... +public: + // ... + [[nodiscard]] std::shared_ptr get_graphics_queue() const noexcept { return graphics_queue_; } + [[nodiscard]] std::shared_ptr get_compute_queue() const noexcept { return compute_queue_; } + [[nodiscard]] std::shared_ptr get_transfer_queue() const noexcept { return transfer_queue_; } + +private: + std::shared_ptr graphics_queue_; + std::shared_ptr compute_queue_; + std::shared_ptr transfer_queue_; + // ... +}; +``` + +### 3.2 初始化逻辑 + +在 `src/render/vulkan_device.cpp` 的构造函数中,在逻辑设备创建成功后,初始化这些队列对象。 + +**注意:** `find_queue_families` 可能会返回相同的索引给不同类型的队列(例如图形队列通常也支持计算和传输)。如果是相同的 `family_index`,且我们只创建了一个队列 (`queueCount = 1`),那么这些封装对象将共享同一个底层的 `vk::Queue` 句柄。这在 Vulkan 中是完全合法的,但需要注意线程安全(`vkQueueSubmit` 需要外部同步)。 + +建议的初始化流程: + +1. 获取 `unique_queue_families` 并创建逻辑设备(现有逻辑)。 +2. 从 `device_` 获取各个 queue handle。 +3. 创建 `vulkan_*_queue` 实例。 + +```cpp +// 在 vulkan_device 构造函数中: + +// ... 逻辑设备创建之后 ... + +const auto& indices = config.physical_device_info.queue_families; // 假设 config 传入了 indices 或者重新查询 + +// 获取 Graphics Queue +if (indices.graphics_family.has_value()) { + u32 family_index = indices.graphics_family.value(); + vk::Queue queue = device_.getQueue(family_index, 0); + graphics_queue_ = make_obj(device_, queue, family_index, 0); +} + +// 获取 Compute Queue +if (indices.compute_family.has_value()) { + u32 family_index = indices.compute_family.value(); + // 注意:如果 compute family 和 graphics family 相同,这里获得的 queue handle 也是相同的 + vk::Queue queue = device_.getQueue(family_index, 0); + compute_queue_ = make_obj(device_, queue, family_index, 0); +} + +// 获取 Transfer Queue +if (indices.transfer_family.has_value()) { + u32 family_index = indices.transfer_family.value(); + vk::Queue queue = device_.getQueue(family_index, 0); + transfer_queue_ = make_obj(device_, queue, family_index, 0); +} +``` + +*注意:目前的 `vulkan_device_config` 并没有包含 `queue_family_indices`,需要从 `physical_device` 重新获取或者修改 `config` 结构体传入。考虑到 `find_queue_families` 已经在 `vulkan_device` 构造函数中调用了一次,可以直接复用其结果。* + +## 4. 同步与线程安全 + +* **队列提交同步**:`vkQueueSubmit` 是非线程安全的。如果多个 `vulkan_*_queue` 共享同一个底层 `vk::Queue`,则它们在不同线程并发调用 `submit` 时会发生冲突。 +* **解决方案**:可以在 `vulkan_queue` 中添加一个 `std::mutex`。但由于多个 `vulkan_queue` 对象可能共享同一个底层句柄,互斥锁需要是针对底层句柄的。 + * **方案 A (简化版)**:假设应用层逻辑保证不会多线程并发访问同一个底层队列(例如只在渲染线程提交)。 + * **方案 B (共享锁)**:由于我们设计上允许 `graphics` 和 `compute` 是不同的对象但可能指向同一硬件队列,最稳妥的方式是在 `vulkan_device` 中维护一个 `mutex` map,或者简单地如果 family index 相同,则应当共享锁。 + * **方案 C (即时获取)**:`vulkan_device` 内部维护 `vk::Queue` 到 `std::mutex` 的映射。 + +鉴于当前项目处于早期阶段,且 Mirai 引擎可能有特定的线程模型,建议**方案 A** 作为起点,并在 `vulkan_queue` 的文档中明确注明:**如果多个逻辑队列映射到同一个硬件队列,多线程提交需要外部同步。** 或者,如果 `vulkan_queue` 是轻量级封装,我们可以在 `vulkan_device` 这一层控制,确保对于同一个 family index 创建的 `vk::Queue`,只创建一个 `vulkan_queue` 实例并在内部复用,但这样 `get_graphics_queue` 和 `get_compute_queue` 可能会返回同一个对象,这会破坏类型系统(不能同时是 `graphics_queue` 和 `compute_queue`)。 + +**修正方案**: +保持 `vulkan_graphics_queue` 等类型区分。如果底层队列相同,它们就是指向同一资源的各类“视图”。为了线程安全,可以在 `vulkan_device` 中为每个 unique queue family 创建一个 `std::mutex`,并在构造 `vulkan_queue` 时传入这个 mutex 的指针/引用。 + +```cpp +class vulkan_queue : public object { + // ... + std::shared_ptr lock_; // 指向该硬件队列的锁 +public: + void submit(...) { + std::lock_guard lock(*lock_); + queue_.submit(...); + } +}; +``` + +在 `vulkan_device` 初始化时: +1. 建立 `family_index -> shared_ptr` 的映射。 +2. 创建 `vulkan_*_queue` 时,根据其 `family_index` 传入对应的 mutex。 + +这样即使 `graphics_queue` 和 `compute_queue` 是不同的对象,只要它们对应同一个硬件队列,就会争夺同一个锁,从而保证线程安全。 + +## 5. 实现计划 + +1. 创建 `src/render/vulkan_queue.h` 和 `src/render/vulkan_queue.cpp`。 +2. 实现 `vulkan_queue` 及其子类。 +3. 在 `vulkan_device.h` 中引入头文件并添加成员。 +4. 修改 `vulkan_device.cpp` 的构造函数,实现队列的获取和封装对象的创建,并处理 mutex 共享逻辑。 +5. 修改 `find_queue_families` 的调用逻辑,确保能获取到索引以便初始化。 diff --git a/src/core/object.h b/src/core/object.h index 6d844ad..0d21114 100644 --- a/src/core/object.h +++ b/src/core/object.h @@ -218,6 +218,12 @@ namespace mirai { [[nodiscard]] constexpr virtual const mirai::type_info* get_parent_type() const noexcept override { \ return &mirai::get_type_info(); \ } \ + [[nodiscard]] constexpr auto shared_this() noexcept -> std::shared_ptr { \ + return std::static_pointer_cast(mirai::object::shared_from_this()); \ + } \ + [[nodiscard]] constexpr auto shared_this() const noexcept -> std::shared_ptr { \ + return std::static_pointer_cast(mirai::object::shared_from_this()); \ + } \ friend struct mirai::object_factory; \ template \ friend struct mirai::object_deleter; \ diff --git a/src/core/thread_utils.h b/src/core/thread_utils.h new file mode 100644 index 0000000..8400015 --- /dev/null +++ b/src/core/thread_utils.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +namespace mirai { + void set_thread_name(std::jthread& t, const std::string& name); +} diff --git a/src/core/windows/thread_utils.cpp b/src/core/windows/thread_utils.cpp new file mode 100644 index 0000000..a6df3f1 --- /dev/null +++ b/src/core/windows/thread_utils.cpp @@ -0,0 +1,18 @@ +#include "core/thread_utils.h" + +#include "core/logger.h" + +#include + +namespace mirai { + void set_thread_name(std::jthread& t, const std::string& name) { + // 将std::string转换为std::wstring + auto wname = std::wstring(name.begin(), name.end()); + + // Windows 10 1607 及更高版本支持 SetThreadDescription API + const auto hr = SetThreadDescription(t.native_handle(), wname.c_str()); + if (FAILED(hr)) { + MIRAI_LOG_WARN("设置线程名称失败: {}", hr); + } + } +} diff --git a/src/gpu_resource/allocation_types.h b/src/gpu_resource/allocation_types.h index d20ff2e..95f3483 100644 --- a/src/gpu_resource/allocation_types.h +++ b/src/gpu_resource/allocation_types.h @@ -6,6 +6,8 @@ #include namespace mirai { + class vulkan_queue; + /** * @brief GPU 分配信息 * @@ -36,6 +38,8 @@ namespace mirai { buffer_usage usage = buffer_usage::vertex; /// 内存用途 memory_usage mem_usage = memory_usage::gpu_only; + /// 资源共享模式 + resource_sharing_mode sharing_mode = resource_sharing_mode::exclusive; /// 是否持久映射 bool persistent_mapped = false; /// 调试名称 diff --git a/src/gpu_resource/allocator.cpp b/src/gpu_resource/allocator.cpp index 1905691..cdfe444 100644 --- a/src/gpu_resource/allocator.cpp +++ b/src/gpu_resource/allocator.cpp @@ -132,7 +132,7 @@ namespace mirai { vk::BufferCreateInfo buffer_info{}; buffer_info.size = info.size; buffer_info.usage = to_vulkan_buffer_usage(info.usage); - buffer_info.sharingMode = vk::SharingMode::eExclusive; + buffer_info.sharingMode = to_vulkan_sharing_mode(info.sharing_mode); if (enable_buffer_device_address_ && has_flag(info.usage, buffer_usage::shader_device_address)) { buffer_info.usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress; diff --git a/src/gpu_resource/allocator.h b/src/gpu_resource/allocator.h index 19900a7..2d6ade6 100644 --- a/src/gpu_resource/allocator.h +++ b/src/gpu_resource/allocator.h @@ -36,13 +36,10 @@ namespace mirai { 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(const allocator_config& config) { + setup(config); } - explicit vma_allocator() = default; - void setup(const allocator_config& config); vma::MemoryUsage to_vma_memory_usage(memory_usage usage) noexcept; diff --git a/src/gpu_resource/gpu_buffer.h b/src/gpu_resource/gpu_buffer.h index 68167d0..7652c0a 100644 --- a/src/gpu_resource/gpu_buffer.h +++ b/src/gpu_resource/gpu_buffer.h @@ -1,13 +1,21 @@ #pragma once +#include "gpu_resource.h" #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) + class gpu_buffer : public gpu_resource { + MIRAI_OBJECT_TYPE_INFO(gpu_buffer, gpu_resource) gpu_buffer(const buffer_create_info& info); + + [[nodiscard]] auto get_buffer() const noexcept { return allocation_.buffer; } + [[nodiscard]] u64 get_size() const noexcept { return size_; } + [[nodiscard]] buffer_usage get_usage() const noexcept { return usage_; } + [[nodiscard]] memory_usage get_memory_usage() const noexcept { return mem_usage_; } + [[nodiscard]] bool is_persistent_mapped() const noexcept { return persistent_mapped_; } + [[nodiscard]] void* get_mapped_data() const noexcept { return mapped_data_; } private: [[nodiscard]] bool is_host_visible() const noexcept; diff --git a/src/gpu_resource/gpu_resource.cpp b/src/gpu_resource/gpu_resource.cpp new file mode 100644 index 0000000..c9062cb --- /dev/null +++ b/src/gpu_resource/gpu_resource.cpp @@ -0,0 +1,4 @@ +#include "gpu_resource.h" + +namespace mirai { +} diff --git a/src/gpu_resource/gpu_resource.h b/src/gpu_resource/gpu_resource.h new file mode 100644 index 0000000..4422aee --- /dev/null +++ b/src/gpu_resource/gpu_resource.h @@ -0,0 +1,32 @@ +#pragma once +#include "resource_manager.h" +#include "resource_types.h" +#include "core/object.h" + +namespace mirai { + class vulkan_queue; + + class gpu_resource : public object { + MIRAI_OBJECT_TYPE_INFO(gpu_resource, object) + public: + // 如果没有传入任何参数,则代表资源使用并发(concurrent)模式 + gpu_resource() : sharing_mode_(resource_sharing_mode::concurrent) {} + // 如果传入队列,则代表资源使用独占(exclusive)模式 + gpu_resource(std::shared_ptr queue) : sharing_mode_(resource_sharing_mode::exclusive), queue_(queue) {} + + virtual resource_async_task upload(std::vector data) = 0; + + [[nodiscard]] auto get_sharing_mode() const noexcept { return sharing_mode_; } + [[nodiscard]] auto get_queue() const noexcept { return queue_.lock(); } + [[nodiscard]] auto get_current_stages() const noexcept { return current_stages_; } + [[nodiscard]] auto get_current_access() const noexcept { return current_access_; } + protected: + /// 资源共享模式 + resource_sharing_mode sharing_mode_ = resource_sharing_mode::exclusive; + /// 当前所处队列(当exclusive共享时有效) + std::weak_ptr queue_; + + vk::PipelineStageFlags2 current_stages_{}; + vk::AccessFlags2 current_access_{}; + }; +} \ No newline at end of file diff --git a/src/gpu_resource/image_block.cpp b/src/gpu_resource/image_block.cpp new file mode 100644 index 0000000..401ad34 --- /dev/null +++ b/src/gpu_resource/image_block.cpp @@ -0,0 +1,125 @@ +#include "image_block.h" + +#include "resource_types_vulkan.h" +#include "core/logger.h" +#include "render/vulkan_context.h" + +namespace mirai { + image_block::image_block(image_block_config config) { + if (config.outer_image) { + is_owner_ = false; + image_ = config.outer_image; + } else { + is_owner_ = true; + image_ = nullptr; + } + + image_count_ = config.image_count; + size_ = config.size; + texture_type_ = config.texture_type; + texture_format_ = config.texture_format; + + // 参数校验 + #if MIRAI_DEBUG + if (texture_format_ == texture_format::undefined) { + MIRAI_LOG_ERROR("创建图像块时使用了未定义的纹理格式。"); + } + if (size_.x() <= 0 || size_.y() <= 0) { + MIRAI_LOG_ERROR("创建图像块时使用了无效的尺寸: ({}, {})。", size_.x(), size_.y()); + } + if (image_count_ == 0) { + MIRAI_LOG_ERROR("创建图像块时使用了无效的图像数量: 0。"); + } + if (!is_owner_ && !image_) { + MIRAI_LOG_ERROR("创建图像块时未提供有效的外部图像。"); + } + if (texture_type_ == texture_type::texture_cube || texture_type_ == texture_type::texture_cube_array) { + if (image_count_ % 6 != 0) { + MIRAI_LOG_ERROR("立方体纹理图像块的图像数量必须是6的倍数。"); + } + } + + bool is_array = (texture_type_ == texture_type::texture_1d_array || + texture_type_ == texture_type::texture_2d_array || + texture_type_ == texture_type::texture_cube_array); + if (is_array && image_count_ > 0) { + MIRAI_LOG_ERROR("纹理数组图像块的图像数量必须大于0。"); + } else { + if (image_count_ == 1) { + MIRAI_LOG_ERROR("非数组纹理图像块的图像数量必须等于1。"); + } + } + #endif + } + + bool image_block::rebuild_image(void* data) { + if (!is_owner_) { + MIRAI_LOG_ERROR("尝试重建不拥有的图像块的图像。"); + return false; + } + if (!destroy_image()) + return false; + + auto default_device = vulkan_context::get().get_default_device(); + // 创建图像 + vk::ImageCreateInfo image_info{}; + image_info.setImageType(to_vulkan_image_type(get_texture_type())); + image_info.setFormat(to_vulkan_format(get_texture_format())); + image_info.setExtent( + vk::Extent3D{ + static_cast(size_.x()), + static_cast(size_.y()), + 1 + } + ); + image_info.setMipLevels(1); + image_info.setArrayLayers(get_image_count()); + image_info.setSamples(to_vulkan_sample_count(get_sample_count())); + image_info.setTiling(to_vulkan_image_tiling(get_texture_tiling())); + image_info.setUsage(to_vulkan_image_usage(get_texture_usage())); + image_info.setSharingMode(to_vulkan_sharing_mode(get_texture_sharing_mode())); + image_info.setInitialLayout(to_vulkan_image_layout(get_texture_layout())); + + auto [result, image] = default_device->get_device().createImage(image_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("图像块图像创建失败:{}", vk::to_string(result)); + return false; + } + image_ = image; + + if (data) { + return set_data(data); + } + + return true; + } + + bool image_block::set_data(void* data) { + if (!data) { + MIRAI_LOG_ERROR("尝试为图像块设置空数据。"); + return false; + } + if (!image_) { + MIRAI_LOG_ERROR("尝试为未创建的图像块设置数据。"); + return false; + } + auto default_device = vulkan_context::get().get_default_device(); + + return true; + } + + bool image_block::destroy_image() { + if (!is_owner_) { + MIRAI_LOG_ERROR("尝试销毁不拥有的图像块的图像。"); + return false; + } + + auto default_device = vulkan_context::get().get_default_device(); + if (image_) { + default_device->get_device().destroyImage(image_); + image_ = nullptr; + } + + return true; + } +} diff --git a/src/gpu_resource/image_block.h b/src/gpu_resource/image_block.h new file mode 100644 index 0000000..1702efb --- /dev/null +++ b/src/gpu_resource/image_block.h @@ -0,0 +1,62 @@ +#pragma once +#include "core/object.h" + +#include + +#include "resource_types.h" + +namespace mirai { + struct image_block_config { + vk::Image outer_image{}; + u32 image_count{1}; + vec2i size{0}; + texture_type texture_type{texture_type::texture_2d}; + texture_format texture_format{texture_format::undefined}; + sample_count sample_count{1}; + texture_tiling tiling{texture_tiling::optimal}; + texture_usage usage{texture_usage::sampled | texture_usage::transfer_dst}; + resource_sharing_mode sharing_mode{resource_sharing_mode::exclusive}; + texture_layout layout{texture_layout::undefined}; + }; + + class image_block : public object { + MIRAI_OBJECT_TYPE_INFO(image_block, object) + + image_block(image_block_config config); + + void set_size(vec2i size) noexcept { size_ = size; } + void set_image_count(u32 count) noexcept { image_count_ = count; } + void set_texture_type(texture_type type) noexcept { texture_type_ = type; } + void set_texture_format(texture_format format) noexcept { texture_format_ = format; } + void set_sample_count(sample_count count) noexcept { sample_count_ = count; } + + [[nodiscard]] auto get_image_count() const noexcept { return image_count_; } + [[nodiscard]] auto get_texture_type() const noexcept { return texture_type_; } + [[nodiscard]] auto get_texture_format() const noexcept { return texture_format_; } + [[nodiscard]] auto get_sample_count() const noexcept { return sample_count_; } + [[nodiscard]] auto get_texture_tiling() const noexcept { return titling_; } + [[nodiscard]] auto get_texture_usage() const noexcept { return usage_; } + [[nodiscard]] auto get_texture_sharing_mode() const noexcept { return sharing_mode_; } + [[nodiscard]] auto get_texture_layout() const noexcept { return layout_; } + [[nodiscard]] auto get_image() const noexcept { return image_; } + [[nodiscard]] auto get_size() const noexcept { return size_; } + [[nodiscard]] auto is_owner() const noexcept { return is_owner_; } + + bool rebuild_image(void* data = nullptr); + bool set_data(void* data); + bool destroy_image(); + private: + bool is_owner_{true}; + vk::Image image_{}; + + u32 image_count_{1}; + vec2i size_{0}; + texture_type texture_type_{texture_type::texture_2d}; + texture_format texture_format_{texture_format::undefined}; + sample_count sample_count_{1}; + texture_tiling titling_{texture_tiling::optimal}; + texture_usage usage_{texture_usage::sampled | texture_usage::transfer_dst}; + resource_sharing_mode sharing_mode_{resource_sharing_mode::exclusive}; + texture_layout layout_{texture_layout::undefined}; + }; +} diff --git a/src/gpu_resource/render_target.cpp b/src/gpu_resource/render_target.cpp new file mode 100644 index 0000000..e67b255 --- /dev/null +++ b/src/gpu_resource/render_target.cpp @@ -0,0 +1,26 @@ +#include "render_target.h" + +#include "render/vulkan_context.h" + +namespace mirai { + render_target::render_target(vk::Image image) { + image_ref_ = image; + + auto default_device = vulkan_context::get().get_default_device(); + // 创建图像视图 + vk::ImageViewCreateInfo view_info{}; + view_info.setImage(image_ref_); + view_info.setViewType(vk::ImageViewType::e2D); + view_info.setFormat(vk::Format::eB8G8R8A8Srgb); + } + + void render_target::resize(vec2i size) { + auto default_device = vulkan_context::get().get_default_device(); + default_device->get_device().waitIdle(); + } + + vec2i render_target::get_size() const noexcept { + + } + +} diff --git a/src/gpu_resource/render_target.h b/src/gpu_resource/render_target.h new file mode 100644 index 0000000..fd3d808 --- /dev/null +++ b/src/gpu_resource/render_target.h @@ -0,0 +1,27 @@ +#pragma once +#include "core/object.h" +#include + +namespace mirai { + struct render_target_config { + // 引用的外部图像,不负责销毁 + vec2i size{800, 600}; + }; + + class render_target : public object { + MIRAI_OBJECT_TYPE_INFO(render_target, object) + + public: + render_target(vk::Image image); + + void resize(vec2i size); + vec2i get_size() const noexcept; + + [[nodiscard]] vk::ImageView get_image_view() const noexcept { return image_view_; } + [[nodiscard]] vk::Framebuffer get_framebuffer() const noexcept { return framebuffer_; } + protected: + void on_destroying() override; + private: + vk::Framebuffer framebuffer_{}; + }; +} diff --git a/src/gpu_resource/resource_manager.cpp b/src/gpu_resource/resource_manager.cpp new file mode 100644 index 0000000..aec6828 --- /dev/null +++ b/src/gpu_resource/resource_manager.cpp @@ -0,0 +1,188 @@ +#include "resource_manager.h" + + +#include "core/logger.h" +#include "core/thread_utils.h" +#include "render/vulkan_context.h" +#include "types/types.h" +#include "render/vulkan_device.h" +#include "gpu_resource/texture/texture.h" +#include "gpu_resource/gpu_buffer.h" +#include "render/vulkan_fence.h" +#include "render/vulkan_time_semaphore.h" + +namespace mirai { + struct timeline_awaiter { + std::shared_ptr device; + vk::Semaphore semaphore; + uint64_t target_value; + + // 1. 检查是否需要挂起 + // 如果 GPU 跑得比 CPU 录制还快(极少见但可能),则不挂起直接执行 + bool await_ready() const { + uint64_t current_val = 0; + // 获取当前时间戳信号量的进度 + const auto result = device->get_device().getSemaphoreCounterValue(semaphore, ¤t_val); + return (result == vk::Result::eSuccess) && (current_val >= target_value); + } + + // 2. 挂起时的逻辑 + // 当 await_ready 返回 false 时调用,handle 是编译器生成的协程句柄 + void await_suspend(std::coroutine_handle<> handle) noexcept { + // 将此协程注册到管理器的任务池中 + // 管理器线程会轮询这个信号量,并在满足 target_value 时调用 handle.resume() + device->get_resource_manager()->add_tracking_task(semaphore, target_value, handle); + } + + // 3. 恢复时的逻辑 + // 当协程被 resume() 唤醒后,这里执行的代码是回到原函数后的第一行 + void await_resume() const noexcept { + // 这里可以返回结果,比如一个错误码,但对于上传任务,通常返回 void + } + }; + + + resource_manager::resource_manager(const std::shared_ptr& device) : device_(device) { + task_pool_.assign(128, {}); + + global_timeline_ = make_obj(device, 0); + thread_ = std::jthread(&resource_manager::upload_thread_func, this); + set_thread_name(thread_, "upload_thread"); + } + + void resource_manager::add_tracking_task(vk::Semaphore sem, uint64_t value, std::coroutine_handle<> handle) { + // 从池中找一个空位 + for (auto& task : task_pool_) { + bool expected = false; + if (task.is_active.compare_exchange_strong(expected, true)) { + task.handle = handle; + task.target_value = value; + + // 将索引推入活跃队列(需要加锁保护活跃列表本身) + std::lock_guard lock(active_mutex_); + active_indices_.push_back(&task - &task_pool_[0]); + return; + } + } + // 如果池满了,实际生产中这里应处理扩容或阻塞 + MIRAI_LOG_ERROR("资源上传任务池已满,无法添加新的跟踪任务!"); + throw std::runtime_error("资源上传任务池已满"); + } + + resource_async_task resource_manager::upload_resource(const std::shared_ptr& tex, const std::vector& data) { + // 1. 这里的逻辑在管理器线程执行,立即派发 Submit + uint64_t completion_value = dispatch_immediate_transfer(tex, data); + + // 2. 挂起并等待 GPU + // 这里的 co_await 会调用 TimelineAwaiter + co_await timeline_awaiter{get_semaphore(), completion_value}; + + // 3. 当管理器轮询到完成,会自动调用 handle.resume() + // 代码会在这里“奇迹般”恢复,执行清理工作 + MIRAI_LOG_DEBUG("上传完成,清理暂存缓冲区..."); + } + + uint64_t resource_manager::dispatch_immediate_transfer(std::shared_ptr tex, std::shared_ptr staging) { + auto queue = device_->get_transfer_queue(); + auto cmd = device_->create_command_buffer(vk::CommandBufferLevel::ePrimary); + + cmd.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit}); + + vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}; + vk::ImageMemoryBarrier2 barrier{vk::PipelineStageFlagBits2::eTopOfPipe, vk::AccessFlagBits2::eNone, + vk::PipelineStageFlagBits2::eCopy, vk::AccessFlagBits2::eTransferWrite, + vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + tex->get_image(), range}; + + cmd.pipelineBarrier2(vk::DependencyInfo{0, nullptr, 0, nullptr, 1, &barrier}); + + vk::BufferImageCopy copy_region{0, 0, 0, + vk::ImageAspectFlagBits::eColor, 0, {0, 0, 1}, + tex->get_extent()}; + cmd.copyBufferToImage(staging->buffer, tex->get_image(), vk::ImageLayout::eTransferDstOptimal, 1, ©_region); + + vk::ImageMemoryBarrier2 barrier2{vk::PipelineStageFlagBits2::eCopy, vk::AccessFlagBits2::eTransferWrite, + vk::PipelineStageFlagBits2::eBottomOfPipe, vk::AccessFlagBits2::eNone, + vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + tex->get_image(), range}; + cmd.pipelineBarrier2(vk::DependencyInfo{0, nullptr, 0, nullptr, 1, &barrier2}); + + cmd.end(); + + auto fence = device_->create_fence(); + vk::SubmitInfo submit_info{0, nullptr, nullptr, 1, &cmd, 0, nullptr}; + queue.submit(1, &submit_info, fence); + + auto res = fence->device_wait(); + if (res == vk::Result::eSuccess) { + fence.reset(); + } + device_->destroy_command_buffer(cmd); + + return ++last_value_; + } + + uint64_t resource_manager::dispatch_immediate_transfer(std::shared_ptr buffer, std::shared_ptr staging) { + auto& ctx = vulkan_context::get(); + auto device = ctx.get_default_device(); + auto queue = ctx.get_transfer_queue(); + + auto cmd = device->create_command_buffer(vk::CommandBufferLevel::ePrimary); + + cmd.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit}); + + vk::BufferCopy copy_region{0, 0, buffer->get_size()}; + cmd.copyBuffer(staging->buffer, buffer->get_buffer(), 1, ©_region); + + cmd.end(); + + auto fence = device->create_fence(); + vk::SubmitInfo submit_info{0, nullptr, nullptr, 1, &cmd, 0, nullptr}; + queue.submit(1, &submit_info, fence); + + auto result = device->wait_for_fence(fence); + if (result == file_result::success) { + device->destroy_fence(fence); + } + device->destroy_command_buffer(cmd); + + return ++last_value_; + } + + void resource_manager::upload_thread_func() { + while (running_) { + uint64_t gpu_value = 0; + auto res = device_.getSemaphoreCounterValue(global_timeline_, &gpu_value); + + if (res == vk::Result::eSuccess) { + std::lock_guard lock(active_mutex_); + + for (auto it = active_indices_.begin(); it != active_indices_.end(); ) { + size_t idx = *it; + if (gpu_value >= task_pool_[idx].target_value) { + // 唤醒协程 + auto handle = task_pool_[idx].handle; + + // 先标记为不活跃,再恢复协程(防止重入问题) + task_pool_[idx].is_active.store(false); + it = active_indices_.erase(it); + + // 恢复执行协程后续的清理代码 + if (handle) handle.resume(); + } else { + ++it; + } + } + } + + // 避免 100% CPU 占用 + if (active_indices_.empty()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + std::this_thread::yield(); + } + } + } +} diff --git a/src/gpu_resource/resource_manager.h b/src/gpu_resource/resource_manager.h new file mode 100644 index 0000000..80ac53f --- /dev/null +++ b/src/gpu_resource/resource_manager.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include + +#include + +#include "core/object.h" + +namespace mirai { + class vulkan_device; + class vulkan_time_semaphore; + class texture; + class gpu_buffer; + class staging_buffer; + + struct resource_upload_task { + std::coroutine_handle<> handle; + uint64_t target_value = 0; + std::atomic is_active{ false }; + }; + + struct resource_async_task { + struct promise_type { + resource_async_task get_return_object() { return {std::coroutine_handle::from_promise(*this)}; } + std::suspend_never initial_suspend() { return {}; } // 创建即开始执行 + std::suspend_always final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() { std::terminate(); } + }; + std::coroutine_handle handle; + }; + + class resource_manager : public object { + MIRAI_OBJECT_TYPE_INFO(resource_manager, object) + public: + resource_manager(const std::shared_ptr& device); + + void add_tracking_task(vk::Semaphore sem, uint64_t value, std::coroutine_handle<> handle); + resource_async_task upload_resource(const std::shared_ptr& tex, const std::vector& data); + uint64_t dispatch_immediate_transfer(std::shared_ptr tex, std::shared_ptr staging); + uint64_t dispatch_immediate_transfer(std::shared_ptr buffer, std::shared_ptr staging); + [[nodiscard]] auto get_semaphore() noexcept { return global_timeline_; } + private: + void upload_thread_func(); + + std::shared_ptr device_; + std::shared_ptr global_timeline_; + std::atomic last_value_{0}; + + // 池化存储 + std::vector task_pool_; + std::vector active_indices_; + std::mutex active_mutex_; + std::mutex submission_mutex_; // 保护 GPU Queue 提交 + std::jthread thread_; + std::atomic running_{true}; + }; +} diff --git a/src/gpu_resource/resource_types.h b/src/gpu_resource/resource_types.h index 8ba535b..6c5e6a3 100644 --- a/src/gpu_resource/resource_types.h +++ b/src/gpu_resource/resource_types.h @@ -290,6 +290,29 @@ namespace mirai { texture_cube_array = 6, }; + enum class texture_tiling { + optimal = 0, + linear = 1, + drm_format_modifier_extension = 2, + }; + + enum class resource_sharing_mode { + exclusive = 0, + concurrent = 1, + }; + + enum class texture_layout { + undefined = 0, + preinitialized = 1, + }; + + enum class queue_type { + graphics = 0, + present = 1, + compute = 2, + transfer = 3, + }; + enum class texture_usage : u32 { // 采样器读取 sampled = 1 << 0, @@ -308,7 +331,6 @@ namespace mirai { // 瞬态附件 transient_attachment = 1 << 7, }; - MIRAI_FLAG_ENUM(texture_usage) enum class sampler_filter : u32 { @@ -430,8 +452,8 @@ namespace mirai { }; /** - * @brief 描述符类型 - */ + * @brief 描述符类型 + */ enum class descriptor_type : u32 { /// 采样器 sampler = 0, diff --git a/src/gpu_resource/resource_types_vulkan.h b/src/gpu_resource/resource_types_vulkan.h index 807f9d8..99a6834 100644 --- a/src/gpu_resource/resource_types_vulkan.h +++ b/src/gpu_resource/resource_types_vulkan.h @@ -523,6 +523,41 @@ namespace mirai { } } + [[nodiscard]] constexpr auto to_vulkan_image_tiling(texture_tiling tiling) noexcept { + switch (tiling) { + case texture_tiling::optimal: + return vk::ImageTiling::eOptimal; + case texture_tiling::linear: + return vk::ImageTiling::eLinear; + case texture_tiling::drm_format_modifier_extension: + return vk::ImageTiling::eDrmFormatModifierEXT; + default: + return vk::ImageTiling::eOptimal; // 默认返回最优布局 + } + } + + [[nodiscard]] constexpr auto to_vulkan_sharing_mode(resource_sharing_mode mode) noexcept { + switch (mode) { + case resource_sharing_mode::exclusive: + return vk::SharingMode::eExclusive; + case resource_sharing_mode::concurrent: + return vk::SharingMode::eConcurrent; + default: + return vk::SharingMode::eExclusive; // 默认返回独占模式 + } + } + + [[nodiscard]] constexpr auto to_vulkan_image_layout(texture_layout layout) noexcept { + switch (layout) { + case texture_layout::undefined: + return vk::ImageLayout::eUndefined; + case texture_layout::preinitialized: + return vk::ImageLayout::ePreinitialized; + default: + return vk::ImageLayout::eUndefined; // 默认返回未定义布局 + } + } + [[nodiscard]] constexpr auto to_vulkan_image_view_type(texture_type type) noexcept { switch (type) { case texture_type::texture_1d: diff --git a/src/gpu_resource/swapchain.cpp b/src/gpu_resource/swapchain.cpp index e380708..0bc1383 100644 --- a/src/gpu_resource/swapchain.cpp +++ b/src/gpu_resource/swapchain.cpp @@ -6,6 +6,21 @@ namespace mirai { swapchain::swapchain(const swapchain_create_info& info) { + create_swapchain(info); + create_image_views(); + } + + void swapchain::on_destroying() { + object::on_destroying(); + if (swapchain_) { + MIRAI_LOG_INFO("销毁交换链。"); + auto default_device = vulkan_context::get().get_default_device(); + default_device->get_device().destroySwapchainKHR(swapchain_); + swapchain_ = nullptr; + } + } + + void swapchain::create_swapchain(const swapchain_create_info& info) { auto default_device = vulkan_context::get().get_default_device(); if (!default_device) { MIRAI_LOG_ERROR("无有效Vulkan设备,无法创建交换链。"); @@ -68,13 +83,21 @@ namespace mirai { swapchain_ = swapchain; } - void swapchain::on_destroying() { - object::on_destroying(); - if (swapchain_) { - MIRAI_LOG_INFO("销毁交换链。"); - auto default_device = vulkan_context::get().get_default_device(); - default_device->get_device().destroySwapchainKHR(swapchain_); - swapchain_ = nullptr; + void swapchain::create_image_views() { + auto default_device = vulkan_context::get().get_default_device(); + u32 image_count = 0; + auto result = default_device->get_device().getSwapchainImagesKHR(swapchain_, &image_count, images); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("获取交换链图像数量失败:{}", vk::to_string(result)); + return; } + std::vector swapchain_images(image_count); + result = default_device->get_device().getSwapchainImagesKHR(swapchain_, &image_count, swapchain_images.data()); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("获取交换链图像失败:{}", vk::to_string(result)); + return; + } + + } } diff --git a/src/gpu_resource/swapchain.h b/src/gpu_resource/swapchain.h index 1d7b3fc..ad2bfe6 100644 --- a/src/gpu_resource/swapchain.h +++ b/src/gpu_resource/swapchain.h @@ -17,6 +17,9 @@ namespace mirai { auto get_swapchain() const noexcept { return swapchain_; } protected: void on_destroying() override; + + void create_swapchain(const swapchain_create_info& info); + void create_image_views(); private: vk::SwapchainKHR swapchain_; }; diff --git a/src/gpu_resource/texture/texture.cpp b/src/gpu_resource/texture/texture.cpp new file mode 100644 index 0000000..332e411 --- /dev/null +++ b/src/gpu_resource/texture/texture.cpp @@ -0,0 +1,91 @@ +#include "texture.h" + +#include "gpu_resource/resource_types_vulkan.h" +#include "core/logger.h" +#include "render/vulkan_context.h" + +namespace mirai { + texture::texture(vk::Image outer_image, texture_format format) { + is_owner_ = !outer_image; + image_ = outer_image; + format_ = format; + } + + texture::texture(vk::Image outer_image, texture_format format, std::shared_ptr queue) : gpu_resource(queue) { + is_owner_ = !outer_image; + image_ = outer_image; + format_ = format; + } + + void texture::record_transfer_barrier(vk::CommandBuffer cmd, const std::shared_ptr& dst_queue, + vk::ImageLayout new_layout, vk::PipelineStageFlags2 dst_stage, vk::AccessFlags2 dst_access) { + if (get_sharing_mode() != resource_sharing_mode::exclusive) { + MIRAI_LOG_DEBUG("仅支持独占模式的纹理资源需要进行布局转换。"); + return; + } + auto src_queue = queue_.lock(); + + vk::ImageMemoryBarrier2 barrier; + barrier.srcStageMask = get_current_stages(); // 资源自己记录的状态 + barrier.srcAccessMask = get_current_access(); + barrier.dstStageMask = dst_stage; + barrier.dstAccessMask = dst_access; + + barrier.oldLayout = current_layout_; + barrier.newLayout = new_layout; + + // 关键:所有权转移索引 + barrier.srcQueueFamilyIndex = src_queue ? src_queue->get_family_index() : VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = dst_queue->get_family_index(); + + barrier.image = image_; + barrier.subresourceRange = get_full_range(); // 只有资源知道自己的 range + + vk::DependencyInfo dep; + dep.setImageMemoryBarriers(barrier); + cmd.pipelineBarrier2(dep); + + queue_ = dst_queue; + current_layout_ = new_layout; + current_stages_ = dst_stage; + current_access_ = dst_access; + } + + void texture::on_created() { + object::on_created(); + if (is_owner_) { + image_ = create_image(); + } + image_view_ = create_image_view(); + } + + void texture::on_destroying() { + object::on_destroying(); + auto default_device = vulkan_context::get().get_default_device(); + if (image_view_) { + default_device->get_device().destroyImageView(image_view_); + image_view_ = nullptr; + } + + if (is_owner_ && image_) { + default_device->get_device().destroyImage(image_); + image_ = nullptr; + } + } + + vk::ImageView texture::create_image_view() { + auto default_device = vulkan_context::get().get_default_device(); + // 创建图像视图 + vk::ImageViewCreateInfo view_info{}; + view_info.setImage(image_); + view_info.setViewType(to_vulkan_image_view_type(get_texture_type())); + view_info.setFormat(to_vulkan_format(format_)); + view_info.setSubresourceRange(get_full_range()); + auto [result, image_view] = default_device->get_device().createImageView(view_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建纹理图像视图失败: {}", vk::to_string(result)); + return {}; + } + return image_view; + } +} diff --git a/src/gpu_resource/texture/texture.h b/src/gpu_resource/texture/texture.h new file mode 100644 index 0000000..9496689 --- /dev/null +++ b/src/gpu_resource/texture/texture.h @@ -0,0 +1,46 @@ +#pragma once +#include "core/object.h" +#include + +#include "gpu_resource/gpu_resource.h" +#include "gpu_resource/resource_types.h" +#include "types/error.h" + +namespace mirai { + class vulkan_queue; + + class texture : public gpu_resource { + MIRAI_OBJECT_TYPE_INFO(texture, gpu_resource); + public: + texture(vk::Image outer_image, texture_format format); + texture(vk::Image outer_image, texture_format format, std::shared_ptr queue); + + [[nodiscard]] virtual texture_type get_texture_type() const noexcept = 0; + [[nodiscard]] auto get_image() const { return image_; } + [[nodiscard]] auto get_image_view() const { return image_view_; } + [[nodiscard]] virtual vk::ImageSubresourceRange get_full_range() const noexcept = 0; + [[nodiscard]] virtual vk::Extent3D get_extent() const noexcept = 0; + [[nodiscard]] auto get_format() const noexcept { return format_; } + [[nodiscard]] auto get_current_layout() const noexcept { return current_layout_; } + + void record_transfer_barrier( + vk::CommandBuffer cmd, + const std::shared_ptr& dst_queue, + vk::ImageLayout new_layout, + vk::PipelineStageFlags2 dst_stage, + vk::AccessFlags2 dst_access + ); + protected: + void on_created() override; + void on_destroying() override; + virtual vk::Image create_image() = 0; + virtual vk::ImageView create_image_view(); + protected: + // 是否拥有图像资源的所有权 + bool is_owner_{true}; + vk::Image image_{}; + vk::ImageView image_view_{}; + vk::ImageLayout current_layout_{vk::ImageLayout::eUndefined}; + texture_format format_{texture_format::undefined}; + }; +} diff --git a/src/gpu_resource/texture/texture1d.cpp b/src/gpu_resource/texture/texture1d.cpp new file mode 100644 index 0000000..29d23e4 --- /dev/null +++ b/src/gpu_resource/texture/texture1d.cpp @@ -0,0 +1,41 @@ +#include "texture1d.h" + +#include "core/logger.h" +#include "gpu_resource/resource_types_vulkan.h" +#include "render/vulkan_context.h" + +namespace mirai { + vk::ImageSubresourceRange texture1d::get_full_range() const noexcept { + return vk::ImageSubresourceRange{ + vk::ImageAspectFlagBits::eColor, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS + }; + } + + vk::Image texture1d::create_image() { + vk::ImageCreateInfo image_info{}; + image_info.imageType = vk::ImageType::e2D; + image_info.format = to_vulkan_format(format_); + image_info.extent = vk::Extent3D{ + size_, + 1, + 1 + }; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = vk::SampleCountFlagBits::e1; + image_info.tiling = vk::ImageTiling::eOptimal; + image_info.usage = to_vulkan_image_usage(texture_usage::sampled | texture_usage::transfer_dst); + image_info.sharingMode = vk::SharingMode::eExclusive; + image_info.initialLayout = vk::ImageLayout::eUndefined; + + auto default_device = vulkan_context::get().get_default_device(); + auto [result, image] = default_device->get_device().createImage(image_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建 2D 纹理图像失败: {}", vk::to_string(result)); + return {}; + } + return image; + } +} diff --git a/src/gpu_resource/texture/texture1d.h b/src/gpu_resource/texture/texture1d.h new file mode 100644 index 0000000..9090951 --- /dev/null +++ b/src/gpu_resource/texture/texture1d.h @@ -0,0 +1,18 @@ +#pragma once +#include "texture.h" + +namespace mirai { + class texture1d : public texture { + MIRAI_OBJECT_TYPE_INFO(texture1d, texture); + public: + texture_type get_texture_type() const noexcept override { return texture_type::texture_1d; } + + [[nodiscard]] auto size() const { return size_; } + vk::ImageSubresourceRange get_full_range() const noexcept override; + protected: + vk::Image create_image() override; + + private: + u32 size_{0}; + }; +} diff --git a/src/gpu_resource/texture/texture2d.cpp b/src/gpu_resource/texture/texture2d.cpp new file mode 100644 index 0000000..818582a --- /dev/null +++ b/src/gpu_resource/texture/texture2d.cpp @@ -0,0 +1,61 @@ +#include "texture2d.h" + +#include "core/logger.h" +#include "gpu_resource/resource_types_vulkan.h" +#include "render/vulkan_context.h" +#include "types/error.h" + +namespace mirai { + resource_async_task texture2d::upload(std::vector data) { + auto& manager = resource_manager::get(); + // A. 准备工作 (Staging Buffer 分配等) + auto staging = manager.allocate_staging(data.size()); + memcpy(staging->map(), data.data(), data.size()); + + // B. 立即派发 (Immediate Dispatch) + // 内部调用两次 submit,并返回本次任务的全局 target_value + uint64_t wait_val = manager.dispatch_immediate_transfer(shared_from_this(), staging); + + // C. 异步等待 + // 线程会在这里返回,直到 GPU 完成任务后被管理器唤醒 + co_await TimelineAwaiter{ manager.get_global_semaphore(), wait_val, manager }; + + // D. 恢复执行 (清理工作) + manager.free_staging(staging); + MIRAI_LOG_DEBUG("纹理上传及所有权转移完成"); + } + + vk::ImageSubresourceRange texture2d::get_full_range() const noexcept { + return vk::ImageSubresourceRange{ + vk::ImageAspectFlagBits::eColor, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS + }; + } + + vk::Image texture2d::create_image() { + vk::ImageCreateInfo image_info{}; + image_info.imageType = vk::ImageType::e2D; + image_info.format = to_vulkan_format(format_); + image_info.extent = vk::Extent3D{ + static_cast(size_.x()), + static_cast(size_.y()), + 1 + }; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = vk::SampleCountFlagBits::e1; + image_info.tiling = vk::ImageTiling::eOptimal; + image_info.usage = to_vulkan_image_usage(texture_usage::sampled | texture_usage::transfer_dst); + image_info.sharingMode = vk::SharingMode::eExclusive; + image_info.initialLayout = vk::ImageLayout::eUndefined; + + auto default_device = vulkan_context::get().get_default_device(); + auto [result, image] = default_device->get_device().createImage(image_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建 2D 纹理图像失败: {}", vk::to_string(result)); + return {}; + } + return image; + } +} diff --git a/src/gpu_resource/texture/texture2d.h b/src/gpu_resource/texture/texture2d.h new file mode 100644 index 0000000..afdacf8 --- /dev/null +++ b/src/gpu_resource/texture/texture2d.h @@ -0,0 +1,18 @@ +#pragma once +#include "texture.h" + +namespace mirai { + class texture2d : public texture { + MIRAI_OBJECT_TYPE_INFO(texture2d, texture); + + resource_async_task upload(std::vector data) override; + texture_type get_texture_type() const noexcept override { return texture_type::texture_2d; } + + [[nodiscard]] auto size() const { return size_; } + vk::ImageSubresourceRange get_full_range() const noexcept override; + protected: + vk::Image create_image() override; + private: + vec2i size_{0}; + }; +} diff --git a/src/gpu_resource/texture/texture3d.cpp b/src/gpu_resource/texture/texture3d.cpp new file mode 100644 index 0000000..19446e4 --- /dev/null +++ b/src/gpu_resource/texture/texture3d.cpp @@ -0,0 +1 @@ +#include "texture3d.h" \ No newline at end of file diff --git a/src/gpu_resource/texture/texture3d.h b/src/gpu_resource/texture/texture3d.h new file mode 100644 index 0000000..96ff0a0 --- /dev/null +++ b/src/gpu_resource/texture/texture3d.h @@ -0,0 +1,4 @@ +#pragma once + +class texture3d { +}; \ No newline at end of file diff --git a/src/gpu_resource/transfer_task.cpp b/src/gpu_resource/transfer_task.cpp new file mode 100644 index 0000000..4b388dc --- /dev/null +++ b/src/gpu_resource/transfer_task.cpp @@ -0,0 +1,11 @@ +#include "transfer_task.h" + +#include "render/vulkan_context.h" + +namespace mirai { + void transfer_task::run(std::shared_ptr block) { + task_ = [block] { + auto default_device = vulkan_context::get().get_default_device(); + }; + } +} diff --git a/src/gpu_resource/transfer_task.h b/src/gpu_resource/transfer_task.h new file mode 100644 index 0000000..2a7c53e --- /dev/null +++ b/src/gpu_resource/transfer_task.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include "core/object.h" + +#include + +namespace mirai { + class image_block; + + class transfer_task : public object { + MIRAI_OBJECT_TYPE_INFO(transfer_task, object) + public: + void run(std::shared_ptr block); + void set_data(std::span data) noexcept { + data_ = data; + complete_.store(true, std::memory_order_release); + } + auto get_data() const noexcept { + return data_; + } + bool is_complete() const noexcept { + return complete_; + } + private: + std::atomic complete_{false}; + std::span data_; + std::function task_; + }; + + class transfer_task_copy : public object { + MIRAI_OBJECT_TYPE_INFO(transfer_task_copy, object) + public: + void set_data(const std::vector& data) noexcept { + data_ = data; + complete_.store(true, std::memory_order_release); + } + auto get_data() const noexcept { + return data_; + } + bool is_complete() const noexcept { + return complete_; + } + private: + std::atomic complete_{false}; + std::vector data_; + std::function task_; + }; + + class transfer_processor : public object { + MIRAI_OBJECT_TYPE_INFO(transfer_processor, object) + + + private: + std::jthread thread_; + std::mutex mutex_; + std::condition_variable cv_; + std::queue> task_queue_; + std::queue> task_copy_queue_; + bool running_ = true; + vk::Queue transfer_queue_; + + void process_tasks(); + }; +} \ No newline at end of file diff --git a/src/render/device_utils.h b/src/render/device_utils.h index 9468724..12245ab 100644 --- a/src/render/device_utils.h +++ b/src/render/device_utils.h @@ -1,4 +1,6 @@ #pragma once +#include + #include "types/types.h" #include @@ -170,31 +172,25 @@ namespace mirai { * @return 唯一队列族索引的向量 */ [[nodiscard]] std::vector get_unique_families() const { - std::vector unique_families; + std::set unique_families; if (graphics_family.has_value()) { - unique_families.push_back(graphics_family.value()); + unique_families.insert(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 (present_family.has_value()) { + unique_families.insert(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 (compute_family.has_value()) { + unique_families.insert(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()); + if (transfer_family.has_value()) { + unique_families.insert(transfer_family.value()); } - return unique_families; + return {unique_families.begin(), unique_families.end()}; } }; diff --git a/src/render/vulkan_context.cpp b/src/render/vulkan_context.cpp index 1689510..b2d2cc2 100644 --- a/src/render/vulkan_context.cpp +++ b/src/render/vulkan_context.cpp @@ -1,5 +1,6 @@ #include "vulkan_context.h" +#include "vulkan_queue.h" #include "window/window_manager.h" #include "core/logger.h" @@ -50,4 +51,23 @@ namespace mirai { }; create_device(config); } + + vk::CommandPool vulkan_thread_context::get_pool(const std::shared_ptr& queue) { + const auto family_index = queue->get_family_index(); + const auto device = queue->get_device(); + if (!pools_.contains(family_index)) { + // 创建新池子,允许重置单个 CommandBuffer + vk::CommandPoolCreateInfo info( + vk::CommandPoolCreateFlagBits::eResetCommandBuffer, + family_index + ); + auto [result, pool] = device.createCommandPool(info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建命令池失败: {}", vk::to_string(result)); + throw std::runtime_error("创建命令池失败"); + } + pools_[family_index] = pool; + } + return pools_[family_index]; + } } diff --git a/src/render/vulkan_context.h b/src/render/vulkan_context.h index 6c963dc..503c115 100644 --- a/src/render/vulkan_context.h +++ b/src/render/vulkan_context.h @@ -5,6 +5,8 @@ #include "core/object.h" namespace mirai { + class vulkan_queue; + struct vulkan_context_init_config { /// Vulkan 实例配置 vulkan_instance_config instance_config; @@ -43,4 +45,16 @@ namespace mirai { std::shared_ptr instance_; std::vector> devices_; }; + + class vulkan_thread_context { + static thread_local vulkan_thread_context instance; + public: + static auto& get() noexcept { + return instance; + } + + vk::CommandPool get_pool(const std::shared_ptr& queue); + private: + std::unordered_map pools_; + }; } diff --git a/src/render/vulkan_device.cpp b/src/render/vulkan_device.cpp index deacc81..7589d94 100644 --- a/src/render/vulkan_device.cpp +++ b/src/render/vulkan_device.cpp @@ -3,124 +3,171 @@ #include "device_utils.h" #include "core/logger.h" #include "gpu_resource/allocator.h" +#include "gpu_resource/resource_manager.h" +#include "vulkan_fence.h" +#include "vulkan_semaphore.h" +#include "vulkan_time_semaphore.h" -mirai::vulkan_device::vulkan_device(const vulkan_device_config& config) { - physical_device_ = config.physical_device; +namespace mirai { + vulkan_device::vulkan_device(const vulkan_device_config& config) { + physical_device_ = config.physical_device; - // 获取物理设备队列族信息(不需要 surface,因为 vulkan_device 不依赖窗口) - auto queue_families = find_queue_families(physical_device_, {}); + // 获取物理设备队列族信息(不需要 surface,因为 vulkan_device 不依赖窗口) + auto queue_families = find_queue_families(physical_device_, {}); - // 收集唯一的队列族索引 - auto unique_queue_families = queue_families.get_unique_families(); + // 收集唯一的队列族索引 + auto unique_queue_families = queue_families.get_unique_families(); - // 创建设备队列创建信息 - std::vector queue_create_infos; - const float queue_priority = 1.0f; + // 创建设备队列创建信息 + std::vector queue_create_infos; + const float queue_priority = 1.0f; - for (const auto& family : unique_queue_families) { - vk::DeviceQueueCreateInfo queue_create_info{}; - queue_create_info.setQueueFamilyIndex(family) - .setQueueCount(1) - .setPQueuePriorities(&queue_priority); - queue_create_infos.push_back(queue_create_info); + for (const auto& family : unique_queue_families) { + vk::DeviceQueueCreateInfo queue_create_info{}; + queue_create_info.setQueueFamilyIndex(family) + .setQueueCount(1) + .setPQueuePriorities(&queue_priority); + queue_create_infos.push_back(queue_create_info); + } + + // 启用设备扩展 + std::vector enabled_extensions; + + // 必需的交换链扩展 + enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + // 可选扩展(根据设备支持情况) + enabled_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + enabled_extensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); + enabled_extensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + + // 获取物理设备特性以检查支持的扩展 + auto properties = physical_device_.getProperties(); + auto features = physical_device_.getFeatures(); + + // 检查 Vulkan 1.3 特性支持 + vk::PhysicalDeviceFeatures2 features2{}; + vk::PhysicalDeviceVulkan11Features features_11{}; + vk::PhysicalDeviceVulkan12Features features_12{}; + vk::PhysicalDeviceVulkan13Features features_13{}; + + features_12.setPNext(&features_13); + features_11.setPNext(&features_12); + features2.setPNext(&features_11); + physical_device_.getFeatures2(&features2); + + // 创建设备特性结构 + vk::PhysicalDeviceVulkan12Features device_features_12{}; + vk::PhysicalDeviceVulkan13Features device_features_13{}; + + // 配置 Vulkan 1.2 特性 + device_features_12.setDescriptorIndexing(features_12.descriptorIndexing) + .setDescriptorBindingPartiallyBound(features_12.descriptorBindingPartiallyBound) + .setDescriptorBindingUpdateUnusedWhilePending( + features_12.descriptorBindingUpdateUnusedWhilePending) + .setDescriptorBindingVariableDescriptorCount(features_12.descriptorBindingVariableDescriptorCount) + .setRuntimeDescriptorArray(features_12.runtimeDescriptorArray) + .setBufferDeviceAddress(config.enable_buffer_device_address && features_12.bufferDeviceAddress) + .setTimelineSemaphore(features_12.timelineSemaphore); + + // 配置 Vulkan 1.3 特性 + device_features_13.setDynamicRendering(features_13.dynamicRendering) + .setSynchronization2(features_13.synchronization2) + .setInlineUniformBlock(features_13.inlineUniformBlock) + .setDescriptorBindingInlineUniformBlockUpdateAfterBind( + features_13.descriptorBindingInlineUniformBlockUpdateAfterBind) + .setMaintenance4(features_13.maintenance4); + + // 设置 pNext 链 + device_features_12.setPNext(&device_features_13); + + // 创建设备创建信息 + vk::DeviceCreateInfo device_create_info{}; + device_create_info.setQueueCreateInfos(queue_create_infos) + .setPEnabledExtensionNames(enabled_extensions) + .setPNext(&device_features_12); + + // 创建逻辑设备 + auto [result, device] = physical_device_.createDevice(device_create_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建逻辑设备失败: {}", vk::to_string(result)); + return; + } + device_ = device; + + MIRAI_LOG_INFO("逻辑设备创建成功: {}", properties.deviceName.data()); + + // 初始化 dispatch loader + auto proc_addr = config.dl.getProcAddress("vkGetInstanceProcAddr"); + disp_.init(proc_addr); + disp_.init(config.instance); + disp_.init(device_); + + // 配置并初始化 VMA 分配器 + 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_ + }; + allocator_ = make_obj(alloc_config); + + // 获取并保存队列句柄 + if (queue_families.graphics_family.has_value()) { + auto family_index = queue_families.graphics_family.value(); + u32 queue_index = 0; + graphics_queue_ = make_obj(device_, device_.getQueue(family_index, queue_index), + family_index, queue_index); + } + if (queue_families.present_family.has_value()) { + auto family_index = queue_families.present_family.value(); + u32 queue_index = 0; + present_queue_ = make_obj(device_, device_.getQueue(family_index, queue_index), + family_index, queue_index); + } + if (queue_families.compute_family.has_value()) { + auto family_index = queue_families.compute_family.value(); + u32 queue_index = 0; + compute_queue_ = make_obj(device_, device_.getQueue(family_index, queue_index), + family_index, queue_index); + } + if (queue_families.transfer_family.has_value()) { + auto family_index = queue_families.transfer_family.value(); + u32 queue_index = 0; + transfer_queue_ = make_obj(device_, device_.getQueue(family_index, queue_index), + family_index, queue_index); + } + + MIRAI_LOG_INFO("VMA 分配器初始化完成"); + + resource_manager_ = make_obj(device_); } - // 启用设备扩展 - std::vector enabled_extensions; - - // 必需的交换链扩展 - enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - - // 可选扩展(根据设备支持情况) - enabled_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); - enabled_extensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); - enabled_extensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); - - // 获取物理设备特性以检查支持的扩展 - auto properties = physical_device_.getProperties(); - auto features = physical_device_.getFeatures(); - - // 检查 Vulkan 1.3 特性支持 - vk::PhysicalDeviceFeatures2 features2{}; - vk::PhysicalDeviceVulkan11Features features_11{}; - vk::PhysicalDeviceVulkan12Features features_12{}; - vk::PhysicalDeviceVulkan13Features features_13{}; - - features_12.setPNext(&features_13); - features_11.setPNext(&features_12); - features2.setPNext(&features_11); - physical_device_.getFeatures2(&features2); - - // 创建设备特性结构 - vk::PhysicalDeviceVulkan12Features device_features_12{}; - vk::PhysicalDeviceVulkan13Features device_features_13{}; - - // 配置 Vulkan 1.2 特性 - device_features_12.setDescriptorIndexing(features_12.descriptorIndexing) - .setDescriptorBindingPartiallyBound(features_12.descriptorBindingPartiallyBound) - .setDescriptorBindingUpdateUnusedWhilePending(features_12.descriptorBindingUpdateUnusedWhilePending) - .setDescriptorBindingVariableDescriptorCount(features_12.descriptorBindingVariableDescriptorCount) - .setRuntimeDescriptorArray(features_12.runtimeDescriptorArray) - .setBufferDeviceAddress(config.enable_buffer_device_address && features_12.bufferDeviceAddress) - .setTimelineSemaphore(features_12.timelineSemaphore); - - // 配置 Vulkan 1.3 特性 - device_features_13.setDynamicRendering(features_13.dynamicRendering) - .setSynchronization2(features_13.synchronization2) - .setInlineUniformBlock(features_13.inlineUniformBlock) - .setDescriptorBindingInlineUniformBlockUpdateAfterBind( - features_13.descriptorBindingInlineUniformBlockUpdateAfterBind) - .setMaintenance4(features_13.maintenance4); - - // 设置 pNext 链 - device_features_12.setPNext(&device_features_13); - - // 创建设备创建信息 - vk::DeviceCreateInfo device_create_info{}; - device_create_info.setQueueCreateInfos(queue_create_infos) - .setPEnabledExtensionNames(enabled_extensions) - .setPNext(&device_features_12); - - // 创建逻辑设备 - auto [result, device] = physical_device_.createDevice(device_create_info); - if (result != vk::Result::eSuccess) { - MIRAI_LOG_ERROR("创建逻辑设备失败: {}", vk::to_string(result)); - return; + std::shared_ptr vulkan_device::create_fence() { + return make_obj(shared_this()); } - device_ = device; - MIRAI_LOG_INFO("逻辑设备创建成功: {}", properties.deviceName.data()); - - // 初始化 dispatch loader - auto proc_addr = config.dl.getProcAddress("vkGetInstanceProcAddr"); - disp_.init(proc_addr); - disp_.init(config.instance); - disp_.init(device_); - - // 配置并初始化 VMA 分配器 - 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); - - MIRAI_LOG_INFO("VMA 分配器初始化完成"); -} - -void mirai::vulkan_device::on_destroying() { - object::on_destroying(); - - MIRAI_LOG_INFO("Vulkan 设备 {} 正在销毁", get_device_name()); - - // 销毁逻辑设备 - if (device_) { - device_.destroy(); - device_ = nullptr; + std::shared_ptr vulkan_device::create_semaphore() { + return make_obj(shared_this()); } -} + + std::shared_ptr vulkan_device::create_timeline_semaphore(u32 initial_value) { + return make_obj(shared_this(), initial_value); + } + + void vulkan_device::on_destroying() { + object::on_destroying(); + + MIRAI_LOG_INFO("Vulkan 设备 {} 正在销毁", get_device_name()); + + // 销毁逻辑设备 + if (device_) { + device_.destroy(); + device_ = nullptr; + } + } +} \ No newline at end of file diff --git a/src/render/vulkan_device.h b/src/render/vulkan_device.h index 39144b6..b0cbb0a 100644 --- a/src/render/vulkan_device.h +++ b/src/render/vulkan_device.h @@ -1,10 +1,18 @@ #pragma once #include "vulkan_types.h" #include "core/object.h" +#include "vulkan_queue.h" #include +#include "gpu_resource/allocator.h" +#include "types/error.h" + namespace mirai { + class vulkan_time_semaphore; + class vulkan_semaphore; + class vulkan_fence; + class resource_manager; class vma_allocator; } @@ -35,6 +43,17 @@ namespace mirai { [[nodiscard]] auto& get_dispatch_loader() noexcept { return disp_; } [[nodiscard]] const auto& get_dispatch_loader() const noexcept { return disp_; } [[nodiscard]] std::string get_device_name() const noexcept { return physical_device_.getProperties().deviceName.data(); } + [[nodiscard]] const auto& get_allocator() const noexcept { return allocator_; } + + [[nodiscard]] auto get_graphics_queue() const noexcept { return graphics_queue_; } + [[nodiscard]] auto get_present_queue() const noexcept { return present_queue_; } + [[nodiscard]] auto get_compute_queue() const noexcept { return compute_queue_; } + [[nodiscard]] auto get_transfer_queue() const noexcept { return transfer_queue_; } + [[nodiscard]] auto get_resource_manager() const noexcept { return resource_manager_; } + + [[nodiscard]] std::shared_ptr create_fence(); + [[nodiscard]] std::shared_ptr create_semaphore(); + [[nodiscard]] std::shared_ptr create_timeline_semaphore(u32 initial_value = 0); protected: void on_destroying() override; @@ -42,7 +61,13 @@ namespace mirai { // 逻辑设备 vk::Device device_; vk_dispatch_loader disp_; - // std::shared_ptr allocator_; // 先使用全局分配器,这里保存一个变量用于未来多GPU支持 + std::shared_ptr graphics_queue_; + std::shared_ptr present_queue_; + std::shared_ptr compute_queue_; + std::shared_ptr transfer_queue_; + std::shared_ptr resource_manager_; + + std::shared_ptr allocator_; // 保存物理设备映射 vk::PhysicalDevice physical_device_; diff --git a/src/render/vulkan_fence.cpp b/src/render/vulkan_fence.cpp new file mode 100644 index 0000000..f24ef4e --- /dev/null +++ b/src/render/vulkan_fence.cpp @@ -0,0 +1,30 @@ +#include "vulkan_fence.h" + +#include "vulkan_device.h" +#include "core/logger.h" + +namespace mirai { + vulkan_fence::vulkan_fence(const std::shared_ptr& device) { + vk::FenceCreateInfo info{}; + + auto [result, fence] = device->get_device().createFence(info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建 Vulkan 栅栏失败: {}", to_string(result)); + return; + } + fence_ = fence; + } + + vk::Result vulkan_fence::device_wait(bool wait_all, u64 timeout) const { + return device_->get_device().waitForFences(fence_, wait_all, timeout); + } + + void vulkan_fence::on_destroying() { + object::on_destroying(); + + if (fence_) { + // 销毁 Vulkan 栅栏 + device_->get_device().destroyFence(fence_); + } + } +} diff --git a/src/render/vulkan_fence.h b/src/render/vulkan_fence.h new file mode 100644 index 0000000..9db2364 --- /dev/null +++ b/src/render/vulkan_fence.h @@ -0,0 +1,22 @@ +#pragma once +#include + +#include "core/object.h" + +namespace mirai { + class vulkan_device; + + class vulkan_fence : public object { + MIRAI_OBJECT_TYPE_INFO(vulkan_fence, object) + public: + vulkan_fence(const std::shared_ptr& device); + + [[nodiscard]] auto get_fence() const noexcept { return fence_; } + vk::Result device_wait(bool wait_all = true, u64 timeout = UINT64_MAX) const; + protected: + void on_destroying() override; + private: + vk::Fence fence_; + std::shared_ptr device_; + }; +} diff --git a/src/render/vulkan_queue.cpp b/src/render/vulkan_queue.cpp new file mode 100644 index 0000000..c510fd8 --- /dev/null +++ b/src/render/vulkan_queue.cpp @@ -0,0 +1,38 @@ +#include "render/vulkan_queue.h" + +namespace mirai { + vulkan_queue::vulkan_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : device_(device) + , queue_(queue) + , family_index_(family_index), + queue_index_(queue_index) { + } + + vk::Result vulkan_queue::submit( + const std::vector& command_buffers, + const std::vector& wait_semaphores, + const std::vector& signal_semaphores, + vk::Fence fence) { + std::lock_guard lock(get_lock()); + + vk::SubmitInfo2 submit_info; + submit_info.setCommandBufferInfos(command_buffers); + submit_info.setWaitSemaphoreInfos(wait_semaphores); + submit_info.setSignalSemaphoreInfos(signal_semaphores); + + return queue_.submit2(submit_info, fence); + } + + vk::Result vulkan_queue::wait_idle() { + std::lock_guard lock(get_lock()); + return queue_.waitIdle(); + } + + void_result_t vulkan_graphics_queue::submit(const std::vector& command_buffers) { + vk::PipelineStageFlags waitStages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput}; + submitInfo.setWaitSemaphores(imageAvailableSemaphore); + submitInfo.setPWaitDstStageMask(waitStages); + } + + void_result_t vulkan_present_queue::present(const vk::PresentInfoKHR& present_info) { + } +} // namespace mirai diff --git a/src/render/vulkan_queue.h b/src/render/vulkan_queue.h new file mode 100644 index 0000000..561a901 --- /dev/null +++ b/src/render/vulkan_queue.h @@ -0,0 +1,80 @@ +#pragma once +#include "core/object.h" +#include "types/types.h" + +#include + +#include +#include +#include + +#include "gpu_resource/resource_types.h" +#include "types/error.h" + +namespace mirai { + + class vulkan_queue : public object { + MIRAI_OBJECT_TYPE_INFO(vulkan_queue, object) + public: + vulkan_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index); + + // Vulkan 1.3 Synchronization2 提交接口 + vk::Result submit( + const std::vector& command_buffers, + const std::vector& wait_semaphores = {}, + const std::vector& signal_semaphores = {}, + vk::Fence fence = nullptr + ); + + vk::Result wait_idle(); + + [[nodiscard]] auto get_device() const noexcept { return device_; } + [[nodiscard]] auto get_handle() const noexcept { return queue_; } + [[nodiscard]] auto get_family_index() const noexcept { return family_index_; } + [[nodiscard]] auto get_queue_index() const noexcept { return queue_index_; } + [[nodiscard]] virtual queue_type get_queue_type() const noexcept = 0; + protected: + [[nodiscard]] auto& get_lock() const noexcept { + return lock_map_[queue_]; + } + vk::Device device_; + vk::Queue queue_; + u32 family_index_; + u32 queue_index_; + static std::unordered_map lock_map_; + }; + + class vulkan_graphics_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_graphics_queue, vulkan_queue) + public: + vulkan_graphics_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : vulkan_queue(device, queue, family_index, queue_index) {} + queue_type get_queue_type() const noexcept override { return queue_type::graphics; } + // 未来可添加 present 相关接口 + + void_result_t submit(const std::vector& command_buffers); + }; + + class vulkan_present_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_present_queue, vulkan_queue) + public: + vulkan_present_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : vulkan_queue(device, queue, family_index, queue_index) {} + queue_type get_queue_type() const noexcept override { return queue_type::present; } + + void_result_t present(const vk::PresentInfoKHR& present_info); + }; + + class vulkan_compute_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_compute_queue, vulkan_queue) + public: + vulkan_compute_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : vulkan_queue(device, queue, family_index, queue_index) {} + queue_type get_queue_type() const noexcept override { return queue_type::compute; } + }; + + class vulkan_transfer_queue : public vulkan_queue { + MIRAI_OBJECT_TYPE_INFO(vulkan_transfer_queue, vulkan_queue) + public: + vulkan_transfer_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : vulkan_queue(device, queue, family_index, queue_index) {} + queue_type get_queue_type() const noexcept override { return queue_type::transfer; } + }; + +} // namespace mirai \ No newline at end of file diff --git a/src/render/vulkan_semaphore.cpp b/src/render/vulkan_semaphore.cpp new file mode 100644 index 0000000..ddcddbe --- /dev/null +++ b/src/render/vulkan_semaphore.cpp @@ -0,0 +1,25 @@ +#include "vulkan_semaphore.h" + +#include "vulkan_device.h" +#include "core/logger.h" + +namespace mirai { + vulkan_semaphore::vulkan_semaphore(const std::shared_ptr& device) : device_(device) { + vk::SemaphoreCreateInfo info{}; + + auto [result, semaphore] = device_->get_device().createSemaphore(info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建 Vulkan 信号量失败: {}", to_string(result)); + return; + } + semaphore_ = semaphore; + } + + void vulkan_semaphore::on_destroying() { + object::on_destroying(); + if (semaphore_) { + // 销毁 Vulkan 信号量 + device_->get_device().destroySemaphore(semaphore_); + } + } +} diff --git a/src/render/vulkan_semaphore.h b/src/render/vulkan_semaphore.h new file mode 100644 index 0000000..b7d8009 --- /dev/null +++ b/src/render/vulkan_semaphore.h @@ -0,0 +1,21 @@ +#pragma once +#include + +#include "core/object.h" + +namespace mirai { + class vulkan_device; + + class vulkan_semaphore : public object { + MIRAI_OBJECT_TYPE_INFO(vulkan_semaphore, object) + public: + vulkan_semaphore(const std::shared_ptr& device); + + [[nodiscard]] auto get_semaphore() const noexcept { return semaphore_; } + protected: + void on_destroying() override; + private: + vk::Semaphore semaphore_; + std::shared_ptr device_; + }; +} diff --git a/src/render/vulkan_time_semaphore.cpp b/src/render/vulkan_time_semaphore.cpp new file mode 100644 index 0000000..4f9a332 --- /dev/null +++ b/src/render/vulkan_time_semaphore.cpp @@ -0,0 +1,28 @@ +#include "vulkan_time_semaphore.h" + +#include "vulkan_device.h" +#include "core/logger.h" + +namespace mirai { + vulkan_time_semaphore::vulkan_time_semaphore(const std::shared_ptr& device, u32 initial_value) { + device_ = device; + + vk::SemaphoreTypeCreateInfo type_info{ vk::SemaphoreType::eTimeline, initial_value }; + vk::SemaphoreCreateInfo sem_info{}; + sem_info.setPNext(&type_info); + auto [result, timeline_semaphore] = device_->get_device().createSemaphore(sem_info); + if (result != vk::Result::eSuccess) { + MIRAI_LOG_ERROR("创建 Vulkan 时间信号量失败: {}", to_string(result)); + return; + } + semaphore_ = timeline_semaphore; + } + + void vulkan_time_semaphore::on_destroying() { + object::on_destroying(); + + if (semaphore_) { + device_->get_device().destroySemaphore(semaphore_); + } + } +} diff --git a/src/render/vulkan_time_semaphore.h b/src/render/vulkan_time_semaphore.h new file mode 100644 index 0000000..f45fc70 --- /dev/null +++ b/src/render/vulkan_time_semaphore.h @@ -0,0 +1,20 @@ +#pragma once +#include "core/object.h" +#include + +namespace mirai { + class vulkan_device; + + class vulkan_time_semaphore : public object { + MIRAI_OBJECT_TYPE_INFO(vulkan_time_semaphore, object) + public: + vulkan_time_semaphore(const std::shared_ptr& device, u32 initial_value = 0); + + [[nodiscard]] auto get_semaphore() const noexcept { return semaphore_; } + protected: + void on_destroying() override; + private: + std::shared_ptr device_; + vk::Semaphore semaphore_; + }; +}