todo 队列和数据上传
This commit is contained in:
242
plans/design_vulkan_queues.md
Normal file
242
plans/design_vulkan_queues.md
Normal 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
8
src/core/thread_utils.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
namespace mirai {
|
||||
void set_thread_name(std::jthread& t, const std::string& name);
|
||||
}
|
||||
18
src/core/windows/thread_utils.cpp
Normal file
18
src/core/windows/thread_utils.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
/// 调试名称
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
4
src/gpu_resource/gpu_resource.cpp
Normal file
4
src/gpu_resource/gpu_resource.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "gpu_resource.h"
|
||||
|
||||
namespace mirai {
|
||||
}
|
||||
32
src/gpu_resource/gpu_resource.h
Normal file
32
src/gpu_resource/gpu_resource.h
Normal 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_{};
|
||||
};
|
||||
}
|
||||
125
src/gpu_resource/image_block.cpp
Normal file
125
src/gpu_resource/image_block.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
62
src/gpu_resource/image_block.h
Normal file
62
src/gpu_resource/image_block.h
Normal 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};
|
||||
};
|
||||
}
|
||||
26
src/gpu_resource/render_target.cpp
Normal file
26
src/gpu_resource/render_target.cpp
Normal 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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
27
src/gpu_resource/render_target.h
Normal file
27
src/gpu_resource/render_target.h
Normal 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_{};
|
||||
};
|
||||
}
|
||||
121
src/gpu_resource/resource_manager.cpp
Normal file
121
src/gpu_resource/resource_manager.cpp
Normal 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, ¤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(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/gpu_resource/resource_manager.h
Normal file
55
src/gpu_resource/resource_manager.h
Normal 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};
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
91
src/gpu_resource/texture/texture.cpp
Normal file
91
src/gpu_resource/texture/texture.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
43
src/gpu_resource/texture/texture.h
Normal file
43
src/gpu_resource/texture/texture.h
Normal 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};
|
||||
};
|
||||
}
|
||||
41
src/gpu_resource/texture/texture1d.cpp
Normal file
41
src/gpu_resource/texture/texture1d.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
18
src/gpu_resource/texture/texture1d.h
Normal file
18
src/gpu_resource/texture/texture1d.h
Normal 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};
|
||||
};
|
||||
}
|
||||
61
src/gpu_resource/texture/texture2d.cpp
Normal file
61
src/gpu_resource/texture/texture2d.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
18
src/gpu_resource/texture/texture2d.h
Normal file
18
src/gpu_resource/texture/texture2d.h
Normal 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};
|
||||
};
|
||||
}
|
||||
1
src/gpu_resource/texture/texture3d.cpp
Normal file
1
src/gpu_resource/texture/texture3d.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "texture3d.h"
|
||||
4
src/gpu_resource/texture/texture3d.h
Normal file
4
src/gpu_resource/texture/texture3d.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
class texture3d {
|
||||
};
|
||||
11
src/gpu_resource/transfer_task.cpp
Normal file
11
src/gpu_resource/transfer_task.cpp
Normal 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
68
src/gpu_resource/transfer_task.h
Normal file
68
src/gpu_resource/transfer_task.h
Normal 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();
|
||||
};
|
||||
}
|
||||
@@ -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()};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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支持
|
||||
|
||||
// 保存物理设备映射
|
||||
|
||||
38
src/render/vulkan_queue.cpp
Normal file
38
src/render/vulkan_queue.cpp
Normal 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
80
src/render/vulkan_queue.h
Normal 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
|
||||
Reference in New Issue
Block a user