todo 队列和数据上传

This commit is contained in:
2026-01-07 20:43:34 +08:00
parent fd62137041
commit b94cee1b98
35 changed files with 1381 additions and 29 deletions

View File

@@ -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 {
<<mirai::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<vk::CommandBuffer>& command_buffers,
const std::vector<vk::Semaphore>& wait_semaphores = {},
const std::vector<vk::PipelineStageFlags>& wait_stages = {},
const std::vector<vk::Semaphore>& signal_semaphores = {},
vk::Fence fence = nullptr
);
// Vulkan 1.3 Synchronization2 提交接口 (建议支持)
void submit2(
const std::vector<vk::CommandBufferSubmitInfo>& command_buffers,
const std::vector<vk::SemaphoreSubmitInfo>& wait_semaphores = {},
const std::vector<vk::SemaphoreSubmitInfo>& 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<vulkan_graphics_queue> get_graphics_queue() const noexcept { return graphics_queue_; }
[[nodiscard]] std::shared_ptr<vulkan_compute_queue> get_compute_queue() const noexcept { return compute_queue_; }
[[nodiscard]] std::shared_ptr<vulkan_transfer_queue> get_transfer_queue() const noexcept { return transfer_queue_; }
private:
std::shared_ptr<vulkan_graphics_queue> graphics_queue_;
std::shared_ptr<vulkan_compute_queue> compute_queue_;
std::shared_ptr<vulkan_transfer_queue> 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<vulkan_graphics_queue>(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<vulkan_compute_queue>(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<vulkan_transfer_queue>(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<std::mutex> lock_; // 指向该硬件队列的锁
public:
void submit(...) {
std::lock_guard<std::mutex> lock(*lock_);
queue_.submit(...);
}
};
```
`vulkan_device` 初始化时:
1. 建立 `family_index -> shared_ptr<mutex>` 的映射。
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` 的调用逻辑,确保能获取到索引以便初始化。

8
src/core/thread_utils.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <thread>
#include <string>
namespace mirai {
void set_thread_name(std::jthread& t, const std::string& name);
}

View File

@@ -0,0 +1,18 @@
#include "core/thread_utils.h"
#include "core/logger.h"
#include <windows.h>
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);
}
}
}

View File

@@ -6,6 +6,8 @@
#include <vk_mem_alloc.hpp>
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;
/// 调试名称

View File

@@ -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;

View File

@@ -1,11 +1,12 @@
#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);
private:

View File

@@ -0,0 +1,4 @@
#include "gpu_resource.h"
namespace mirai {
}

View File

@@ -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<vulkan_queue> queue) : sharing_mode_(resource_sharing_mode::exclusive), queue_(queue) {}
virtual resource_async_task upload(std::vector<uint8_t> 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<vulkan_queue> queue_;
vk::PipelineStageFlags2 current_stages_{};
vk::AccessFlags2 current_access_{};
};
}

View File

@@ -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<u32>(size_.x()),
static_cast<u32>(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;
}
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
#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};
};
}

View File

@@ -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 {
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
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_{};
};
}

View File

@@ -0,0 +1,121 @@
#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"
namespace mirai {
struct timeline_awaiter {
std::shared_ptr<vulkan_device> 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, &current_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(vk::Device device) : device_(device) {
task_pool_.assign(128, {});
vk::SemaphoreTypeCreateInfo type_info{ vk::SemaphoreType::eTimeline, 0 };
vk::SemaphoreCreateInfo sem_info{};
sem_info.pNext = &type_info;
auto [result, timeline_semaphore] = device.createSemaphore(sem_info);
global_timeline_ = timeline_semaphore;
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<texture>& tex, const std::vector<uint8_t>& 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("上传完成,清理暂存缓冲区...");
}
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();
}
}
}
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <thread>
#include <coroutine>
#include <mutex>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "core/object.h"
namespace mirai {
class texture;
struct resource_upload_task {
std::coroutine_handle<> handle;
uint64_t target_value = 0;
std::atomic<bool> is_active{ false };
};
struct resource_async_task {
struct promise_type {
resource_async_task get_return_object() { return {std::coroutine_handle<promise_type>::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<promise_type> handle;
};
class resource_manager : public object {
MIRAI_OBJECT_TYPE_INFO(resource_manager, object)
public:
resource_manager(vk::Device device);
void add_tracking_task(vk::Semaphore sem, uint64_t value, std::coroutine_handle<> handle);
resource_async_task upload_resource(const std::shared_ptr<texture>& tex, const std::vector<uint8_t>& data);
uint64_t dispatch_immediate_transfer(std::shared_ptr<texture> tex, std::shared_ptr<staging_buffer> staging);
[[nodiscard]] auto get_semaphore() noexcept { return global_timeline_; }
private:
void upload_thread_func();
vk::Device device_;
vk::Semaphore global_timeline_;
std::atomic<uint64_t> last_value_{0};
// 池化存储
std::vector<resource_upload_task> task_pool_;
std::vector<size_t> active_indices_;
std::mutex active_mutex_;
std::mutex submission_mutex_; // 保护 GPU Queue 提交
std::jthread thread_;
std::atomic<bool> running_{true};
};
}

View File

@@ -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,

View File

@@ -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:

View File

@@ -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<vk::Image> 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;
}
}
}

View File

@@ -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_;
};

View File

@@ -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<vulkan_queue> 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<vulkan_queue>& 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;
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
#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<vulkan_queue> queue);
[[nodiscard]] virtual texture_type get_texture_type() const noexcept = 0;
[[nodiscard]] auto image() const { return image_; }
[[nodiscard]] auto image_view() const { return image_view_; }
[[nodiscard]] virtual vk::ImageSubresourceRange get_full_range() const noexcept = 0;
void record_transfer_barrier(
vk::CommandBuffer cmd,
const std::shared_ptr<vulkan_queue>& 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};
};
}

View File

@@ -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;
}
}

View File

@@ -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};
};
}

View File

@@ -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<uint8_t> 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<u32>(size_.x()),
static_cast<u32>(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;
}
}

View File

@@ -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<uint8_t> 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};
};
}

View File

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

View File

@@ -0,0 +1,4 @@
#pragma once
class texture3d {
};

View File

@@ -0,0 +1,11 @@
#include "transfer_task.h"
#include "render/vulkan_context.h"
namespace mirai {
void transfer_task::run(std::shared_ptr<image_block> block) {
task_ = [block] {
auto default_device = vulkan_context::get().get_default_device();
};
}
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <condition_variable>
#include <queue>
#include <thread>
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
class image_block;
class transfer_task : public object {
MIRAI_OBJECT_TYPE_INFO(transfer_task, object)
public:
void run(std::shared_ptr<image_block> block);
void set_data(std::span<std::byte> 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<bool> complete_{false};
std::span<std::byte> data_;
std::function<void()> task_;
};
class transfer_task_copy : public object {
MIRAI_OBJECT_TYPE_INFO(transfer_task_copy, object)
public:
void set_data(const std::vector<std::byte>& 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<bool> complete_{false};
std::vector<std::byte> data_;
std::function<void()> 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<std::shared_ptr<transfer_task>> task_queue_;
std::queue<std::shared_ptr<transfer_task_copy>> task_copy_queue_;
bool running_ = true;
vk::Queue transfer_queue_;
void process_tasks();
};
}

View File

@@ -1,4 +1,6 @@
#pragma once
#include <set>
#include "types/types.h"
#include <vulkan/vulkan.hpp>
@@ -170,31 +172,25 @@ namespace mirai {
* @return 唯一队列族索引的向量
*/
[[nodiscard]] std::vector<u32> get_unique_families() const {
std::vector<u32> unique_families;
std::set<u32> 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()};
}
};

View File

@@ -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<vulkan_queue>& 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];
}
}

View File

@@ -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<vulkan_instance> instance_;
std::vector<std::shared_ptr<vulkan_device>> 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<vulkan_queue>& queue);
private:
std::unordered_map<u32, vk::CommandPool> pools_;
};
}

View File

@@ -15,7 +15,7 @@ mirai::vulkan_device::vulkan_device(const vulkan_device_config& config) {
// 创建设备队列创建信息
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
const float queue_priority = 1.0f;
const float queue_priority = 1.0f;
for (const auto& family : unique_queue_families) {
vk::DeviceQueueCreateInfo queue_create_info{};
@@ -110,7 +110,31 @@ mirai::vulkan_device::vulkan_device(const vulkan_device_config& config) {
};
vma_allocator::get().setup(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<vulkan_graphics_queue>(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<vulkan_present_queue>(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<vulkan_compute_queue>(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<vulkan_transfer_queue>(device_, device_.getQueue(family_index, queue_index), family_index, queue_index);
}
MIRAI_LOG_INFO("VMA 分配器初始化完成");
resource_manager_ = make_obj<resource_manager>(device_);
}
void mirai::vulkan_device::on_destroying() {

View File

@@ -4,7 +4,11 @@
#include <vulkan/vulkan.hpp>
#include "vulkan_queue.h"
#include "types/error.h"
namespace mirai {
class resource_manager;
class vma_allocator;
}
@@ -35,6 +39,12 @@ 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]] 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_; }
protected:
void on_destroying() override;
@@ -42,6 +52,12 @@ namespace mirai {
// 逻辑设备
vk::Device device_;
vk_dispatch_loader disp_;
std::shared_ptr<vulkan_graphics_queue> graphics_queue_;
std::shared_ptr<vulkan_present_queue> present_queue_;
std::shared_ptr<vulkan_compute_queue> compute_queue_;
std::shared_ptr<vulkan_transfer_queue> transfer_queue_;
std::shared_ptr<resource_manager> resource_manager_;
// std::shared_ptr<vma_allocator> allocator_; // 先使用全局分配器这里保存一个变量用于未来多GPU支持
// 保存物理设备映射

View File

@@ -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<vk::CommandBufferSubmitInfo>& command_buffers,
const std::vector<vk::SemaphoreSubmitInfo>& wait_semaphores,
const std::vector<vk::SemaphoreSubmitInfo>& 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<vk::CommandBufferSubmitInfo>& 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

80
src/render/vulkan_queue.h Normal file
View File

@@ -0,0 +1,80 @@
#pragma once
#include "core/object.h"
#include "types/types.h"
#include <vulkan/vulkan.hpp>
#include <memory>
#include <mutex>
#include <vector>
#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<vk::CommandBufferSubmitInfo>& command_buffers,
const std::vector<vk::SemaphoreSubmitInfo>& wait_semaphores = {},
const std::vector<vk::SemaphoreSubmitInfo>& 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<VkQueue, std::mutex> 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<vk::CommandBufferSubmitInfo>& 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