添加 Vulkan 实例和窗口管理功能,支持窗口创建与更新
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
#include "app/mirai_app.h"
|
||||
#include "core/object.h"
|
||||
#include "render/vulkan_instance.h"
|
||||
#include "window/window_manager.h"
|
||||
|
||||
using namespace mirai;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
mirai_app_config config;
|
||||
config.window_mgr_config.main_window.size = vec2i{800, 600};
|
||||
mirai::mirai_app app;
|
||||
|
||||
app.setup(config);
|
||||
app.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ find_package(Stb REQUIRED)
|
||||
|
||||
simple_library(STATIC)
|
||||
|
||||
# 验证VulkanAPI版本
|
||||
if (Vulkan_VERSION VERSION_LESS "1.3.0")
|
||||
message(FATAL_ERROR "Vulkan version 1.3 or higher is required.")
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
Vulkan::Vulkan
|
||||
SDL3::SDL3
|
||||
@@ -23,3 +28,4 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
fmt::fmt
|
||||
)
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
|
||||
|
||||
27
src/app/mirai_app.cpp
Normal file
27
src/app/mirai_app.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "mirai_app.h"
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "core/time_util.h"
|
||||
|
||||
namespace mirai {
|
||||
bool mirai_app::setup(const mirai_app_config& config) {
|
||||
window_mgr = make_obj<window_manager>(config.window_mgr_config);
|
||||
vulkan_instance_ = make_obj<vulkan_instance>(config.vulkan_instance_cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
int mirai_app::run() {
|
||||
update_time();
|
||||
|
||||
while (!window_mgr->quit() && !g_quit_requested) {
|
||||
auto delta_time = get_delta_time();
|
||||
|
||||
window_mgr->update(delta_time);
|
||||
|
||||
update_time();
|
||||
}
|
||||
|
||||
shutdown();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
27
src/app/mirai_app.h
Normal file
27
src/app/mirai_app.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
#include "render/vulkan_instance.h"
|
||||
#include "window/window_manager.h"
|
||||
|
||||
namespace mirai {
|
||||
struct mirai_app_config {
|
||||
window_manager_config window_mgr_config{};
|
||||
vulkan_instance_config vulkan_instance_cfg{};
|
||||
};
|
||||
|
||||
class mirai_app {
|
||||
public:
|
||||
bool setup(const mirai_app_config& config);
|
||||
|
||||
void shutdown() {
|
||||
vulkan_instance_.reset();
|
||||
window_mgr.reset();
|
||||
}
|
||||
|
||||
int run();
|
||||
private:
|
||||
std::shared_ptr<window_manager> window_mgr;
|
||||
std::shared_ptr<vulkan_instance> vulkan_instance_;
|
||||
};
|
||||
}
|
||||
@@ -223,6 +223,9 @@ namespace mirai {
|
||||
friend struct mirai::object_deleter; \
|
||||
friend class object_registry;
|
||||
|
||||
template<typename T>
|
||||
concept is_object_type = std::derived_from<typename T::parent_t, object>;
|
||||
|
||||
struct object_factory {
|
||||
template<typename T, typename ...Args> requires std::derived_from<T, object>
|
||||
static auto create(Args&&... args) {
|
||||
@@ -232,7 +235,7 @@ namespace mirai {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename ...Args> requires std::derived_from<T, object>
|
||||
template<is_object_type T, typename ...Args>
|
||||
auto make_obj(Args&&... args) {
|
||||
return object_factory::create<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
28
src/core/time_util.h
Normal file
28
src/core/time_util.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "types/types.h"
|
||||
|
||||
namespace mirai {
|
||||
inline time_point last_time_point;
|
||||
|
||||
template<typename T = duration_ns>
|
||||
auto epoch_time() {
|
||||
return std::chrono::time_point_cast<T>(std::chrono::high_resolution_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
inline auto get_current_time() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
inline auto update_time() {
|
||||
auto now = get_current_time();
|
||||
last_time_point = now;
|
||||
return now;
|
||||
}
|
||||
|
||||
inline auto get_delta_time() {
|
||||
auto now = get_current_time();
|
||||
auto delta = now - last_time_point;
|
||||
last_time_point = now;
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
1
src/render/allocator.cpp
Normal file
1
src/render/allocator.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "allocator.h"
|
||||
125
src/render/allocator.h
Normal file
125
src/render/allocator.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
#include "types/types.h"
|
||||
#include "resource_types.h"
|
||||
#include "core/object.h"
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vma/vk_mem_alloc.h>
|
||||
|
||||
namespace mirai {
|
||||
struct allocation_info {
|
||||
// 分配的大小(字节)
|
||||
u64 size = 0;
|
||||
// 偏移量(字节)
|
||||
u64 offset = 0;
|
||||
// 设备内存句柄
|
||||
vk::DeviceMemory device_memory{};
|
||||
// 映射的内存指针
|
||||
void* mapped_ptr = nullptr;
|
||||
// 内存类型索引
|
||||
u32 memory_type_index = 0;
|
||||
// 分配标志
|
||||
vk::MemoryPropertyFlags property_flags{};
|
||||
};
|
||||
struct buffer_allocation_info {
|
||||
/// Buffer 大小(字节)
|
||||
u64 size = 0;
|
||||
/// Buffer 用途
|
||||
buffer_usage usage = buffer_usage::vertex;
|
||||
/// 内存用途
|
||||
memory_usage mem_usage = memory_usage::gpu_only;
|
||||
/// 是否需要持久映射
|
||||
bool persistent_mapped = false;
|
||||
/// 调试名称
|
||||
std::string_view debug_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Buffer 分配结果
|
||||
*/
|
||||
struct buffer_allocation {
|
||||
/// Vulkan Buffer 句柄
|
||||
vk::Buffer buffer{};
|
||||
/// VMA 分配句柄
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
/// 分配信息
|
||||
allocation_info info;
|
||||
/// 是否有效
|
||||
[[nodiscard]] bool is_valid() const noexcept {
|
||||
return buffer && allocation != VK_NULL_HANDLE;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @brief Image 分配创建信息
|
||||
*/
|
||||
struct image_allocation_info {
|
||||
/// 图像类型
|
||||
vk::ImageType image_type = vk::ImageType::e2D;
|
||||
/// 图像格式
|
||||
vk::Format format = vk::Format::eR8G8B8A8Unorm;
|
||||
/// 图像范围
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
/// Mip 层级数
|
||||
u32 mip_levels = 1;
|
||||
/// 数组层数
|
||||
u32 array_layers = 1;
|
||||
/// 采样数
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
|
||||
/// 平铺方式
|
||||
vk::ImageTiling tiling = vk::ImageTiling::eOptimal;
|
||||
/// 图像用途
|
||||
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eSampled;
|
||||
/// 初始布局
|
||||
vk::ImageLayout initial_layout = vk::ImageLayout::eUndefined;
|
||||
/// 内存用途
|
||||
memory_usage mem_usage = memory_usage::gpu_only;
|
||||
/// 是否为立方体贴图
|
||||
bool is_cube = false;
|
||||
/// 调试名称
|
||||
std::string_view debug_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Image 分配结果
|
||||
*/
|
||||
struct image_allocation {
|
||||
/// Vulkan Image 句柄
|
||||
vk::Image image{};
|
||||
/// VMA 分配句柄
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
/// 分配信息
|
||||
allocation_info info;
|
||||
/// 是否有效
|
||||
[[nodiscard]] bool is_valid() const noexcept {
|
||||
return image && allocation != VK_NULL_HANDLE;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief GPU 分配器配置
|
||||
*/
|
||||
struct allocator_config {
|
||||
/// Vulkan 实例
|
||||
vk::Instance instance{};
|
||||
/// 物理设备
|
||||
vk::PhysicalDevice physical_device{};
|
||||
/// 逻辑设备
|
||||
vk::Device device{};
|
||||
/// Vulkan API 版本
|
||||
u32 vulkan_api_version = VK_API_VERSION_1_3;
|
||||
/// 是否启用 Buffer Device Address
|
||||
bool enable_buffer_device_address = true;
|
||||
/// 是否启用内存预算跟踪
|
||||
bool enable_memory_budget = true;
|
||||
/// 首选的大块分配大小(字节)
|
||||
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
|
||||
};
|
||||
|
||||
class vma_allocator : public object {
|
||||
MIRAI_OBJECT_TYPE_INFO(vma_allocator, object)
|
||||
|
||||
explicit vma_allocator(const allocator_config& config);
|
||||
~vma_allocator() override;
|
||||
|
||||
};
|
||||
}
|
||||
348
src/render/error.h
Normal file
348
src/render/error.h
Normal file
@@ -0,0 +1,348 @@
|
||||
#pragma once
|
||||
|
||||
#include "types/types.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <source_location>
|
||||
#include <expected>
|
||||
#include <fmt/format.h>
|
||||
|
||||
/**
|
||||
* @brief 错误码枚举
|
||||
*
|
||||
* 定义了 MIRAI 框架中所有可能的错误类型
|
||||
*/
|
||||
enum class error_code : u32 {
|
||||
/// 成功,无错误
|
||||
success = 0,
|
||||
|
||||
// ---- 通用错误 (1-99) ----
|
||||
/// 未知错误
|
||||
unknown = 1,
|
||||
/// 无效参数
|
||||
invalid_argument = 2,
|
||||
/// 空指针
|
||||
null_pointer = 3,
|
||||
/// 越界访问
|
||||
out_of_bounds = 4,
|
||||
/// 操作超时
|
||||
timeout = 5,
|
||||
/// 操作被取消
|
||||
cancelled = 6,
|
||||
/// 资源不可用
|
||||
unavailable = 7,
|
||||
/// 操作不支持
|
||||
not_supported = 8,
|
||||
/// 操作已完成
|
||||
already_done = 9,
|
||||
/// 状态无效
|
||||
invalid_state = 10,
|
||||
|
||||
// ---- 内存错误 (100-199) ----
|
||||
/// 内存分配失败
|
||||
out_of_memory = 100,
|
||||
/// 内存对齐错误
|
||||
alignment_error = 101,
|
||||
/// 内存泄漏检测
|
||||
memory_leak = 102,
|
||||
/// 双重释放
|
||||
double_free = 103,
|
||||
/// 使用已释放内存
|
||||
use_after_free = 104,
|
||||
|
||||
// ---- 文件/IO 错误 (200-299) ----
|
||||
/// 文件未找到
|
||||
file_not_found = 200,
|
||||
/// 文件访问被拒绝
|
||||
access_denied = 201,
|
||||
/// 文件已存在
|
||||
file_exists = 202,
|
||||
/// 文件读取错误
|
||||
read_error = 203,
|
||||
/// 文件写入错误
|
||||
write_error = 204,
|
||||
/// 文件格式错误
|
||||
invalid_format = 205,
|
||||
/// 文件损坏
|
||||
corrupted_file = 206,
|
||||
|
||||
// ---- 图形/渲染错误 (300-399) ----
|
||||
/// Vulkan 初始化失败
|
||||
vulkan_init_failed = 300,
|
||||
/// 设备不支持
|
||||
device_not_supported = 301,
|
||||
/// 着色器编译失败
|
||||
shader_compilation_failed = 302,
|
||||
/// 管线创建失败
|
||||
pipeline_creation_failed = 303,
|
||||
/// 交换链创建失败
|
||||
swapchain_creation_failed = 304,
|
||||
/// 帧缓冲创建失败
|
||||
framebuffer_creation_failed = 305,
|
||||
/// 命令缓冲错误
|
||||
command_buffer_error = 306,
|
||||
/// 同步错误
|
||||
synchronization_error = 307,
|
||||
/// 资源绑定错误
|
||||
resource_binding_error = 308,
|
||||
|
||||
// ---- 窗口/输入错误 (400-499) ----
|
||||
/// 窗口创建失败
|
||||
window_creation_failed = 400,
|
||||
/// 窗口已关闭
|
||||
window_closed = 401,
|
||||
/// 输入设备错误
|
||||
input_device_error = 402,
|
||||
/// 事件处理错误
|
||||
event_error = 403,
|
||||
|
||||
// ---- 文本/字体错误 (500-599) ----
|
||||
/// 字体加载失败
|
||||
font_load_failed = 500,
|
||||
/// 字形渲染失败
|
||||
glyph_render_failed = 501,
|
||||
/// 文本整形失败
|
||||
text_shaping_failed = 502,
|
||||
/// 编码转换失败
|
||||
encoding_error = 503,
|
||||
|
||||
// ---- 资源错误 (600-699) ----
|
||||
/// 资源未找到
|
||||
resource_not_found = 600,
|
||||
/// 资源加载失败
|
||||
resource_load_failed = 601,
|
||||
/// 资源已存在
|
||||
resource_exists = 602,
|
||||
/// 资源类型不匹配
|
||||
resource_type_mismatch = 603,
|
||||
|
||||
// ---- 线程/并发错误 (700-799) ----
|
||||
/// 线程创建失败
|
||||
thread_creation_failed = 700,
|
||||
/// 死锁检测
|
||||
deadlock_detected = 701,
|
||||
/// 竞态条件
|
||||
race_condition = 702,
|
||||
/// 任务执行失败
|
||||
task_execution_failed = 703,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取错误码的字符串描述
|
||||
* @param code 错误码
|
||||
* @return 错误描述字符串
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view error_code_to_string(error_code code) noexcept {
|
||||
switch (code) {
|
||||
case error_code::success:
|
||||
return "Success";
|
||||
case error_code::unknown:
|
||||
return "Unknown error";
|
||||
case error_code::invalid_argument:
|
||||
return "Invalid argument";
|
||||
case error_code::null_pointer:
|
||||
return "Null pointer";
|
||||
case error_code::out_of_bounds:
|
||||
return "Out of bounds";
|
||||
case error_code::timeout:
|
||||
return "Operation timeout";
|
||||
case error_code::cancelled:
|
||||
return "Operation cancelled";
|
||||
case error_code::unavailable:
|
||||
return "Resource unavailable";
|
||||
case error_code::not_supported:
|
||||
return "Operation not supported";
|
||||
case error_code::already_done:
|
||||
return "Operation already done";
|
||||
case error_code::invalid_state:
|
||||
return "Invalid state";
|
||||
case error_code::out_of_memory:
|
||||
return "Out of memory";
|
||||
case error_code::alignment_error:
|
||||
return "Memory alignment error";
|
||||
case error_code::memory_leak:
|
||||
return "Memory leak detected";
|
||||
case error_code::double_free:
|
||||
return "Double free detected";
|
||||
case error_code::use_after_free:
|
||||
return "Use after free detected";
|
||||
case error_code::file_not_found:
|
||||
return "File not found";
|
||||
case error_code::access_denied:
|
||||
return "Access denied";
|
||||
case error_code::file_exists:
|
||||
return "File already exists";
|
||||
case error_code::read_error:
|
||||
return "Read error";
|
||||
case error_code::write_error:
|
||||
return "Write error";
|
||||
case error_code::invalid_format:
|
||||
return "Invalid format";
|
||||
case error_code::corrupted_file:
|
||||
return "Corrupted file";
|
||||
case error_code::vulkan_init_failed:
|
||||
return "Vulkan initialization failed";
|
||||
case error_code::device_not_supported:
|
||||
return "Device not supported";
|
||||
case error_code::shader_compilation_failed:
|
||||
return "Shader compilation failed";
|
||||
case error_code::pipeline_creation_failed:
|
||||
return "Pipeline creation failed";
|
||||
case error_code::swapchain_creation_failed:
|
||||
return "Swapchain creation failed";
|
||||
case error_code::framebuffer_creation_failed:
|
||||
return "Framebuffer creation failed";
|
||||
case error_code::command_buffer_error:
|
||||
return "Command buffer error";
|
||||
case error_code::synchronization_error:
|
||||
return "Synchronization error";
|
||||
case error_code::resource_binding_error:
|
||||
return "Resource binding error";
|
||||
case error_code::window_creation_failed:
|
||||
return "Window creation failed";
|
||||
case error_code::window_closed:
|
||||
return "Window closed";
|
||||
case error_code::input_device_error:
|
||||
return "Input device error";
|
||||
case error_code::event_error:
|
||||
return "Event error";
|
||||
case error_code::font_load_failed:
|
||||
return "Font load failed";
|
||||
case error_code::glyph_render_failed:
|
||||
return "Glyph render failed";
|
||||
case error_code::text_shaping_failed:
|
||||
return "Text shaping failed";
|
||||
case error_code::encoding_error:
|
||||
return "Encoding error";
|
||||
case error_code::resource_not_found:
|
||||
return "Resource not found";
|
||||
case error_code::resource_load_failed:
|
||||
return "Resource load failed";
|
||||
case error_code::resource_exists:
|
||||
return "Resource already exists";
|
||||
case error_code::resource_type_mismatch:
|
||||
return "Resource type mismatch";
|
||||
case error_code::thread_creation_failed:
|
||||
return "Thread creation failed";
|
||||
case error_code::deadlock_detected:
|
||||
return "Deadlock detected";
|
||||
case error_code::race_condition:
|
||||
return "Race condition detected";
|
||||
case error_code::task_execution_failed:
|
||||
return "Task execution failed";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 错误信息结构
|
||||
*
|
||||
* 包含错误码和可选的详细错误消息
|
||||
*/
|
||||
struct error_info {
|
||||
/// 错误码
|
||||
error_code code{error_code::unknown};
|
||||
|
||||
/// 详细错误消息
|
||||
std::string message{};
|
||||
|
||||
/// 源文件名
|
||||
std::string_view file{};
|
||||
|
||||
/// 行号
|
||||
u32 line{0};
|
||||
|
||||
/// 函数名
|
||||
std::string_view function{};
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
constexpr error_info() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief 从错误码构造
|
||||
* @param ec 错误码
|
||||
*/
|
||||
constexpr explicit error_info(error_code ec) noexcept : code(ec), message(std::string(error_code_to_string(ec))) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从错误码和消息构造
|
||||
* @param ec 错误码
|
||||
* @param msg 错误消息
|
||||
*/
|
||||
error_info(error_code ec, std::string msg) noexcept : code(ec), message(std::move(msg)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 完整构造函数
|
||||
* @param ec 错误码
|
||||
* @param msg 错误消息
|
||||
* @param f 源文件
|
||||
* @param l 行号
|
||||
* @param fn 函数名
|
||||
*/
|
||||
error_info(error_code ec, std::string msg,
|
||||
std::string_view f, u32 l, std::string_view fn) noexcept : code(ec), message(std::move(msg)), file(f),
|
||||
line(l), function(fn) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否为成功状态
|
||||
* @return 如果错误码为 success 返回 true
|
||||
*/
|
||||
[[nodiscard]] constexpr bool is_success() const noexcept {
|
||||
return code == error_code::success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否为错误状态
|
||||
* @return 如果错误码不为 success 返回 true
|
||||
*/
|
||||
[[nodiscard]] constexpr bool is_error() const noexcept {
|
||||
return code != error_code::success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取完整的错误描述
|
||||
* @return 包含位置信息的完整错误描述
|
||||
*/
|
||||
[[nodiscard]] std::string full_description() const {
|
||||
std::string desc = message;
|
||||
if (!file.empty()) {
|
||||
desc += " [at ";
|
||||
desc += file;
|
||||
desc += ":";
|
||||
desc += std::to_string(line);
|
||||
if (!function.empty()) {
|
||||
desc += " in ";
|
||||
desc += function;
|
||||
}
|
||||
desc += "]";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename E = error_info>
|
||||
using result_t = std::expected<T, E>;
|
||||
|
||||
using void_result_t = result_t<void, error_info>;
|
||||
|
||||
#define MAKE_ERROR_INFO(ec, msg, ...) std::unexpected(error_info( \
|
||||
ec, fmt::format(msg, __VA_ARGS__), \
|
||||
std::source_location::current().file_name(), \
|
||||
std::source_location::current().line(), \
|
||||
std::source_location::current().function_name() \
|
||||
))
|
||||
|
||||
#define MAKE_SUCCESS_INFO() error_info( \
|
||||
error_code::success, \
|
||||
"", \
|
||||
std::source_location::current().file_name(), \
|
||||
std::source_location::current().line(), \
|
||||
std::source_location::current().function_name() \
|
||||
)
|
||||
538
src/render/resource_types.h
Normal file
538
src/render/resource_types.h
Normal file
@@ -0,0 +1,538 @@
|
||||
#pragma once
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "core/flag_enum.h"
|
||||
#include "types/types.h"
|
||||
|
||||
namespace mirai {
|
||||
enum class buffer_usage : u32 {
|
||||
// 无用途
|
||||
none = 0,
|
||||
// 顶点缓冲区
|
||||
vertex = 1 << 0,
|
||||
// 索引缓冲区
|
||||
index = 1 << 1,
|
||||
// 统一缓冲区
|
||||
uniform = 1 << 2,
|
||||
// 存储缓冲区
|
||||
storage = 1 << 3,
|
||||
// 暂存缓冲区(用于数据传输)
|
||||
staging = 1 << 4,
|
||||
// 间接缓冲区
|
||||
indirect = 1 << 5,
|
||||
// 传输源缓冲区
|
||||
transfer_src = 1 << 6,
|
||||
// 传输目标缓冲区
|
||||
transfer_dst = 1 << 7,
|
||||
// 着色器设备地址缓冲区
|
||||
shader_device_address = 1 << 8,
|
||||
};
|
||||
|
||||
MIRAI_FLAG_ENUM(buffer_usage)
|
||||
|
||||
enum class memory_usage : u32 {
|
||||
// GPU专用内存(不可映射)
|
||||
gpu_only = 0,
|
||||
// 仅CPU可见内存(可映射)
|
||||
cpu_only = 1,
|
||||
// CPU写入, GPU读取(上传)
|
||||
cpu_to_gpu = 2,
|
||||
// GPU写入, CPU读取(下载)
|
||||
gpu_to_cpu = 3,
|
||||
// 自动选择
|
||||
auto_prefer_device = 4,
|
||||
// 自动选择,优先主机内存
|
||||
auto_prefer_host = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理格式枚举
|
||||
*
|
||||
* 命名规则说明:
|
||||
* - [components][bits]: 如 r8 (R通道8位), rgba32 (RGBA各32位)
|
||||
* - [type]:
|
||||
* - unorm: 无符号归一化整数 (Unsigned Normalized). 内存存整数,采样时映射到 [0.0, 1.0]
|
||||
* - snorm: 有符号归一化整数 (Signed Normalized). 内存存整数,采样时映射到 [-1.0, 1.0]
|
||||
* - uint: 无符号纯整数 (Unsigned Integer). 采样结果为 uint 类型 (不可线性过滤)
|
||||
* - sint: 有符号纯整数 (Signed Integer). 采样结果为 int 类型 (不可线性过滤)
|
||||
* - srgb: 非线性 sRGB 颜色空间. 读取时硬件自动执行 (val^2.2) 转换到线性空间
|
||||
* - float: 浮点数. 支持超过 1.0 的值 (HDR)
|
||||
*/
|
||||
enum class texture_format : u32 {
|
||||
/// @brief 未定义/无效格式
|
||||
undefined = 0,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 8位格式 (每个分量8位) ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 单通道 8位 无符号归一化 [0, 1]
|
||||
/// @usage 字体图集 (Font Atlas)、环境光遮蔽 (AO)、高度图、粗糙度/金属度贴图
|
||||
r8_unorm,
|
||||
/// @brief 单通道 8位 有符号归一化 [-1, 1]
|
||||
r8_snorm,
|
||||
/// @brief 单通道 8位 无符号整数 [0, 255]
|
||||
/// @usage 索引纹理、物体ID标记 (Object ID)
|
||||
r8_uint,
|
||||
/// @brief 单通道 8位 有符号整数 [-128, 127]
|
||||
r8_sint,
|
||||
|
||||
/// @brief 双通道 8位 无符号归一化
|
||||
/// @usage 2D 距离场 (SDF)、简单的查找表 (LUT)
|
||||
rg8_unorm,
|
||||
/// @brief 双通道 8位 有符号归一化
|
||||
/// @usage 简单的低精度法线贴图 (仅存XY,Z重建)
|
||||
rg8_snorm,
|
||||
/// @brief 双通道 8位 无符号整数
|
||||
rg8_uint,
|
||||
/// @brief 双通道 8位 有符号整数
|
||||
rg8_sint,
|
||||
|
||||
/// @brief 四通道 8位 无符号归一化 (红绿蓝透)
|
||||
/// @usage 最通用的颜色纹理、漫反射贴图 (Diffuse/Albedo)、UI 纹理
|
||||
rgba8_unorm,
|
||||
/// @brief 四通道 8位 有符号归一化
|
||||
rgba8_snorm,
|
||||
/// @brief 四通道 8位 无符号整数
|
||||
rgba8_uint,
|
||||
/// @brief 四通道 8位 有符号整数
|
||||
rgba8_sint,
|
||||
|
||||
/// @brief 四通道 8位 无符号归一化 (蓝绿红透)
|
||||
/// @usage Windows 平台的 Swapchain (交换链) 默认首选格式,因为历史原因显存通常以 BGR 顺序存储
|
||||
bgra8_unorm,
|
||||
/// @brief 四通道 8位 sRGB 非线性空间
|
||||
/// @usage 需要进行伽马校正的 Swapchain 呈现图像
|
||||
bgra8_srgb,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 16位格式 (每个分量16位) ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 单通道 16位 无符号归一化 [0, 1]
|
||||
/// @usage 高精度高度图 (地形渲染),避免梯田效应
|
||||
r16_unorm,
|
||||
/// @brief 单通道 16位 有符号归一化 [-1, 1]
|
||||
r16_snorm,
|
||||
/// @brief 单通道 16位 无符号整数
|
||||
r16_uint,
|
||||
/// @brief 单通道 16位 有符号整数
|
||||
r16_sint,
|
||||
/// @brief 单通道 16位 浮点数 (Half Float)
|
||||
/// @usage 节省显存的 HDR 单通道数据
|
||||
r16_float,
|
||||
|
||||
/// @brief 双通道 16位 无符号归一化
|
||||
/// @usage 高精度 UV 坐标存储
|
||||
rg16_unorm,
|
||||
/// @brief 双通道 16位 有符号归一化
|
||||
/// @usage 高品质法线贴图 (避免 8-bit 法线的波纹瑕疵)
|
||||
rg16_snorm,
|
||||
/// @brief 双通道 16位 无符号整数
|
||||
rg16_uint,
|
||||
/// @brief 双通道 16位 有符号整数
|
||||
rg16_sint,
|
||||
/// @brief 双通道 16位 浮点数
|
||||
/// @usage 速度向量 (Velocity Buffer) 用于动态模糊或 TAA
|
||||
rg16_float,
|
||||
|
||||
/// @brief 四通道 16位 无符号归一化
|
||||
rgba16_unorm,
|
||||
/// @brief 四通道 16位 有符号归一化
|
||||
rgba16_snorm,
|
||||
/// @brief 四通道 16位 无符号整数
|
||||
rgba16_uint,
|
||||
/// @brief 四通道 16位 有符号整数
|
||||
rgba16_sint,
|
||||
/// @brief 四通道 16位 浮点数 (Half Float)
|
||||
/// @usage 标准的 HDR 场景渲染目标 (Scene Color),兼顾精度与带宽
|
||||
rgba16_float,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 32位格式 (每个分量32位) ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 单通道 32位 无符号整数
|
||||
/// @usage 原子计数器、大范围索引、GPGPU 数据
|
||||
r32_uint,
|
||||
/// @brief 单通道 32位 有符号整数
|
||||
r32_sint,
|
||||
/// @brief 单通道 32位 浮点数
|
||||
/// @usage 深度纹理 (线性深度)、阴影贴图 (VSM)、科学计算数据
|
||||
r32_float,
|
||||
|
||||
/// @brief 双通道 32位 无符号整数
|
||||
rg32_uint,
|
||||
/// @brief 双通道 32位 有符号整数
|
||||
rg32_sint,
|
||||
/// @brief 双通道 32位 浮点数
|
||||
/// @usage 高精度复杂 UV 或 2D 坐标数据
|
||||
rg32_float,
|
||||
|
||||
/// @brief 三通道 32位 无符号整数
|
||||
/// @note 显卡对 3通道 (RGB) 格式支持较差,通常会有对齐填充,尽量使用 RGBA
|
||||
rgb32_uint,
|
||||
rgb32_sint,
|
||||
rgb32_float,
|
||||
|
||||
/// @brief 四通道 32位 无符号整数
|
||||
rgba32_uint,
|
||||
/// @brief 四通道 32位 有符号整数
|
||||
rgba32_sint,
|
||||
/// @brief 四通道 32位 浮点数
|
||||
/// @usage G-Buffer 中的世界坐标 (Position Buffer),或需要极高精度的物理模拟数据
|
||||
rgba32_float,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 深度/模板格式 ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 16位 深度归一化 [0, 1]
|
||||
/// @usage 节省显存的阴影贴图 (Shadow Map)
|
||||
depth16_unorm,
|
||||
/// @brief 24位 深度归一化 (通常打包在32位中)
|
||||
depth24_unorm,
|
||||
/// @brief 32位 浮点深度
|
||||
/// @usage 现代桌面端游戏的标准深度缓冲格式 (Reverse Z 友好)
|
||||
depth32_float,
|
||||
/// @brief 24位 深度 + 8位 模板
|
||||
/// @usage 经典的深度模板组合,适用于大多数需要模板测试的渲染
|
||||
depth24_unorm_stencil8_uint,
|
||||
/// @brief 32位 浮点深度 + 8位 模板
|
||||
depth32_float_stencil8_uint,
|
||||
/// @brief 仅 8位 模板
|
||||
stencil8_uint,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 压缩格式 (Block Compression) ----
|
||||
// BC 格式通过压缩 4x4 像素块来大幅减少显存占用 (通常减少 4-6 倍)
|
||||
// =================================================================================
|
||||
|
||||
/// @brief BC1 (DXT1) RGB, 无 Alpha 或 1位 Alpha
|
||||
/// @usage 不透明的颜色贴图 (Albedo),如石头、墙壁
|
||||
bc1_rgb_unorm,
|
||||
/// @brief BC1 (DXT1) sRGB
|
||||
/// @usage 需要 sRGB 校正的不透明颜色贴图
|
||||
bc1_rgb_srgb,
|
||||
/// @brief BC1 (DXT1) RGBA
|
||||
bc1_rgba_unorm,
|
||||
/// @brief BC1 (DXT1) sRGBA
|
||||
bc1_rgba_srgb,
|
||||
|
||||
/// @brief BC2 (DXT3) 显式 Alpha (4位清晰度)
|
||||
/// @usage 具有急剧边缘 Alpha 的纹理 (如铁丝网),现代引擎较少使用
|
||||
bc2_unorm,
|
||||
/// @brief BC2 (DXT3) sRGB
|
||||
bc2_srgb,
|
||||
|
||||
/// @brief BC3 (DXT5) 插值 Alpha
|
||||
/// @usage 最常用的透明贴图格式 (如植被叶子、半透明玻璃)。Alpha 渐变平滑
|
||||
bc3_unorm,
|
||||
/// @brief BC3 (DXT5) sRGB
|
||||
bc3_srgb,
|
||||
|
||||
/// @brief BC4 (ATI1) 单通道
|
||||
/// @usage 压缩的高度图、高光强度图 (Gloss)、环境光遮蔽 (AO)
|
||||
bc4_unorm,
|
||||
/// @brief BC4 (ATI1) 单通道 有符号
|
||||
bc4_snorm,
|
||||
|
||||
/// @brief BC5 (ATI2/3Dc) 双通道
|
||||
/// @usage **法线贴图的标准压缩格式** (存储 X, Y,在 Shader 中重建 Z)
|
||||
bc5_unorm,
|
||||
/// @brief BC5 双通道 有符号
|
||||
bc5_snorm,
|
||||
|
||||
/// @brief BC6H 无符号浮点 (HDR)
|
||||
/// @usage HDR 环境贴图 (Skybox)、光照探针 (Light Probe)。不带 Alpha。
|
||||
bc6h_ufloat,
|
||||
/// @brief BC6H 有符号浮点 (HDR)
|
||||
bc6h_sfloat,
|
||||
|
||||
/// @brief BC7 高质量 RGBA
|
||||
/// @usage 现代 GPU 的首选压缩格式。比 BC1/3 质量更好,伪影更少。适用于所有类型的颜色/Alpha 贴图。
|
||||
bc7_unorm,
|
||||
/// @brief BC7 高质量 sRGBA
|
||||
bc7_srgb,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 特殊/组合格式 ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief R11 G11 B10 浮点数 (无符号,无 Alpha)
|
||||
/// @details 总共32位 (11+11+10)。只能存储正数。
|
||||
/// @usage 极高效率的 HDR 颜色缓冲。常用于后期处理链 (Post-processing) 或无需 Alpha 的场景渲染。
|
||||
/// 比 rgba16_float 节省一半显存带宽。
|
||||
r11g11b10_float,
|
||||
|
||||
/// @brief RGB9 E5 共享指数浮点数
|
||||
/// @details 3个通道共享 5位指数。
|
||||
/// @usage 预计算的 HDR 数据,如天空盒或全局光照体积。不支持渲染目标 (Render Target) 输出。
|
||||
rgb9e5_float,
|
||||
|
||||
/// @brief RGB 10位 + Alpha 2位 无符号归一化
|
||||
/// @details 总共32位 (10+10+10+2)。
|
||||
/// @usage G-Buffer 法线存储 (比 8位精度高,比 16位省空间),或广色域 (HDR10) 输出。
|
||||
rgb10a2_unorm,
|
||||
|
||||
/// @brief RGB 10位 + Alpha 2位 无符号整数
|
||||
/// @usage 紧凑的数据打包 (如将 Mesh ID 和 Material ID 压入同一个 buffer)
|
||||
rgb10a2_uint,
|
||||
};
|
||||
|
||||
enum class texture_type : u32 {
|
||||
texture_1d = 0,
|
||||
texture_2d = 1,
|
||||
texture_3d = 2,
|
||||
texture_cube = 3,
|
||||
texture_1d_array = 4,
|
||||
texture_2d_array = 5,
|
||||
texture_cube_array = 6,
|
||||
};
|
||||
|
||||
enum class texture_usage : u32 {
|
||||
// 采样器读取
|
||||
sampled = 1 << 0,
|
||||
// 存储图像
|
||||
storage = 1 << 1,
|
||||
// 渲染目标 (颜色附件)
|
||||
color_attachment = 1 << 2,
|
||||
// 深度/模板附件
|
||||
depth_stencil_attachment = 1 << 3,
|
||||
// 输入附件
|
||||
input_attachment = 1 << 4,
|
||||
// 传输源
|
||||
transfer_src = 1 << 5,
|
||||
// 传输目标
|
||||
transfer_dst = 1 << 6,
|
||||
// 瞬态附件
|
||||
transient_attachment = 1 << 7,
|
||||
};
|
||||
|
||||
MIRAI_FLAG_ENUM(texture_usage)
|
||||
|
||||
enum class sampler_filter : u32 {
|
||||
nearest = 0,
|
||||
linear = 1,
|
||||
};
|
||||
|
||||
enum class sampler_address_mode : u32 {
|
||||
repeat = 0,
|
||||
mirrored_repeat = 1,
|
||||
clamp_to_edge = 2,
|
||||
clamp_to_border = 3,
|
||||
mirror_clamp_to_edge = 4,
|
||||
};
|
||||
|
||||
enum class sampler_mipmap_mode : u32 {
|
||||
nearest = 0,
|
||||
linear = 1,
|
||||
};
|
||||
|
||||
enum class sampler_preset : u32 {
|
||||
// 线性过滤,重复模式
|
||||
linear_repeat = 0,
|
||||
// 线性过滤,夹取模式
|
||||
linear_clamp = 1,
|
||||
// 最近点过滤,重复模式
|
||||
nearest_repeat = 2,
|
||||
// 最近点过滤,夹取模式
|
||||
nearest_clamp = 3,
|
||||
// 各向异性过滤,重复模式
|
||||
anisotropic_repeat = 4,
|
||||
// 各向异性过滤,夹取模式
|
||||
anisotropic_clamp = 5,
|
||||
// 阴影采样器 (比较采样器)
|
||||
shadow_sampler = 6,
|
||||
};
|
||||
|
||||
enum class border_color : u32 {
|
||||
// 黑色透明(浮点数)
|
||||
transparent_black_float = 0,
|
||||
// 黑色透明(整数)
|
||||
transparent_black_int = 1,
|
||||
// 不透明黑色(浮点数)
|
||||
opaque_black_float = 2,
|
||||
// 不透明黑色(整数)
|
||||
opaque_black_int = 3,
|
||||
// 不透明白色(浮点数)
|
||||
opaque_white_float = 4,
|
||||
// 不透明白色(整数)
|
||||
opaque_white_int = 5,
|
||||
};
|
||||
|
||||
enum class compare_op : u32 {
|
||||
never = 0,
|
||||
less = 1,
|
||||
equal = 2,
|
||||
less_equal = 3,
|
||||
greater = 4,
|
||||
not_equal = 5,
|
||||
greater_equal = 6,
|
||||
always = 7,
|
||||
};
|
||||
|
||||
enum class image_aspect : u32 {
|
||||
color = 1 << 0,
|
||||
depth = 1 << 1,
|
||||
stencil = 1 << 2,
|
||||
metadata = 1 << 3,
|
||||
};
|
||||
MIRAI_FLAG_ENUM(image_aspect)
|
||||
|
||||
enum class image_layout : u32 {
|
||||
/// 未定义
|
||||
undefined = 0,
|
||||
/// 通用布局
|
||||
general = 1,
|
||||
/// 颜色附件最优
|
||||
color_attachment_optimal = 2,
|
||||
/// 深度模板附件最优
|
||||
depth_stencil_attachment_optimal = 3,
|
||||
/// 深度模板只读最优
|
||||
depth_stencil_read_only_optimal = 4,
|
||||
/// 着色器只读最优
|
||||
shader_read_only_optimal = 5,
|
||||
/// 传输源最优
|
||||
transfer_src_optimal = 6,
|
||||
/// 传输目标最优
|
||||
transfer_dst_optimal = 7,
|
||||
/// 预初始化
|
||||
preinitialized = 8,
|
||||
/// 深度只读模板附件最优
|
||||
depth_read_only_stencil_attachment_optimal = 9,
|
||||
/// 深度附件模板只读最优
|
||||
depth_attachment_stencil_read_only_optimal = 10,
|
||||
/// 深度附件最优
|
||||
depth_attachment_optimal = 11,
|
||||
/// 深度只读最优
|
||||
depth_read_only_optimal = 12,
|
||||
/// 模板附件最优
|
||||
stencil_attachment_optimal = 13,
|
||||
/// 模板只读最优
|
||||
stencil_read_only_optimal = 14,
|
||||
/// 只读最优
|
||||
read_only_optimal = 15,
|
||||
/// 附件最优
|
||||
attachment_optimal = 16,
|
||||
/// 呈现源
|
||||
present_src = 17,
|
||||
};
|
||||
|
||||
enum class sample_count : u32 {
|
||||
count_1 = 1,
|
||||
count_2 = 2,
|
||||
count_4 = 4,
|
||||
count_8 = 8,
|
||||
count_16 = 16,
|
||||
count_32 = 32,
|
||||
count_64 = 64,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 描述符类型
|
||||
*/
|
||||
enum class descriptor_type : u32 {
|
||||
/// 采样器
|
||||
sampler = 0,
|
||||
/// 组合图像采样器
|
||||
combined_image_sampler = 1,
|
||||
/// 采样图像
|
||||
sampled_image = 2,
|
||||
/// 存储图像
|
||||
storage_image = 3,
|
||||
/// Uniform texel buffer
|
||||
uniform_texel_buffer = 4,
|
||||
/// Storage texel buffer
|
||||
storage_texel_buffer = 5,
|
||||
/// Uniform buffer
|
||||
uniform_buffer = 6,
|
||||
/// Storage buffer
|
||||
storage_buffer = 7,
|
||||
/// Uniform buffer dynamic
|
||||
uniform_buffer_dynamic = 8,
|
||||
/// Storage buffer dynamic
|
||||
storage_buffer_dynamic = 9,
|
||||
/// 输入附件
|
||||
input_attachment = 10,
|
||||
/// 加速结构
|
||||
acceleration_structure = 11,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 资源状态
|
||||
*
|
||||
* 用于跟踪资源的生命周期状态
|
||||
*/
|
||||
enum class resource_state : u32 {
|
||||
/// 未初始化
|
||||
uninitialized = 0,
|
||||
/// 已创建但未上传数据
|
||||
created = 1,
|
||||
/// 正在上传数据
|
||||
uploading = 2,
|
||||
/// 就绪可用
|
||||
ready = 3,
|
||||
/// 正在销毁
|
||||
destroying = 4,
|
||||
/// 已销毁
|
||||
destroyed = 5,
|
||||
/// 错误状态
|
||||
error = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 内存预算信息
|
||||
*/
|
||||
struct memory_budget {
|
||||
/// 预算字节数(设备建议的最大使用量)
|
||||
u64 budget_bytes = 0;
|
||||
/// 当前使用字节数
|
||||
u64 usage_bytes = 0;
|
||||
/// 堆索引
|
||||
u32 heap_index = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取剩余可用字节数
|
||||
* @return 剩余字节数
|
||||
*/
|
||||
[[nodiscard]] constexpr u64 available_bytes() const noexcept {
|
||||
return budget_bytes > usage_bytes ? budget_bytes - usage_bytes : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取使用百分比
|
||||
* @return 使用百分比 (0.0 - 1.0)
|
||||
*/
|
||||
[[nodiscard]] constexpr f64 usage_percentage() const noexcept {
|
||||
return budget_bytes > 0 ? static_cast<f64>(usage_bytes) / static_cast<f64>(budget_bytes) : 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 分配器统计信息
|
||||
*/
|
||||
struct allocator_statistics {
|
||||
/// 分配次数
|
||||
u64 allocation_count = 0;
|
||||
/// 总分配字节数
|
||||
u64 total_allocated_bytes = 0;
|
||||
/// 当前使用字节数
|
||||
u64 used_bytes = 0;
|
||||
/// 未使用字节数(碎片)
|
||||
u64 unused_bytes = 0;
|
||||
/// 块数量
|
||||
u32 block_count = 0;
|
||||
/// 堆预算数组
|
||||
std::vector<memory_budget> heap_budgets;
|
||||
|
||||
/**
|
||||
* @brief 获取碎片率
|
||||
* @return 碎片率 (0.0 - 1.0)
|
||||
*/
|
||||
[[nodiscard]] f64 fragmentation_ratio() const noexcept {
|
||||
u64 total = used_bytes + unused_bytes;
|
||||
return total > 0 ? static_cast<f64>(unused_bytes) / static_cast<f64>(total) : 0.0;
|
||||
}
|
||||
};
|
||||
}
|
||||
780
src/render/resource_types_vulkan.h
Normal file
780
src/render/resource_types_vulkan.h
Normal file
@@ -0,0 +1,780 @@
|
||||
#pragma once
|
||||
#include "resource_types.h"
|
||||
|
||||
namespace mirai {
|
||||
[[nodiscard]] constexpr auto to_vulkan_buffer_usage(buffer_usage usage) noexcept {
|
||||
vk::BufferUsageFlags2 flags{};
|
||||
|
||||
if (has_flag(usage, buffer_usage::vertex)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eVertexBuffer;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::index)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eIndexBuffer;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::uniform)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eUniformBuffer;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::storage)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eStorageBuffer;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::staging)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eTransferSrc | vk::BufferUsageFlagBits2::eTransferDst;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::indirect)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eIndirectBuffer;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::transfer_src)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eTransferSrc;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::transfer_dst)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eTransferDst;
|
||||
}
|
||||
if (has_flag(usage, buffer_usage::shader_device_address)) {
|
||||
flags |= vk::BufferUsageFlagBits2::eShaderDeviceAddress;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_format(texture_format format) noexcept {
|
||||
switch (format) {
|
||||
case texture_format::undefined:
|
||||
return vk::Format::eUndefined;
|
||||
|
||||
case texture_format::r8_unorm:
|
||||
return vk::Format::eR8Unorm;
|
||||
case texture_format::r8_snorm:
|
||||
return vk::Format::eR8Snorm;
|
||||
case texture_format::r8_uint:
|
||||
return vk::Format::eR8Uint;
|
||||
case texture_format::r8_sint:
|
||||
return vk::Format::eR8Sint;
|
||||
|
||||
case texture_format::rg8_unorm:
|
||||
return vk::Format::eR8G8Unorm;
|
||||
case texture_format::rg8_snorm:
|
||||
return vk::Format::eR8G8Snorm;
|
||||
case texture_format::rg8_uint:
|
||||
return vk::Format::eR8G8Uint;
|
||||
case texture_format::rg8_sint:
|
||||
return vk::Format::eR8G8Sint;
|
||||
|
||||
case texture_format::rgba8_unorm:
|
||||
return vk::Format::eR8G8B8A8Unorm;
|
||||
case texture_format::rgba8_snorm:
|
||||
return vk::Format::eR8G8B8A8Snorm;
|
||||
case texture_format::rgba8_uint:
|
||||
return vk::Format::eR8G8B8A8Uint;
|
||||
case texture_format::rgba8_sint:
|
||||
return vk::Format::eR8G8B8A8Sint;
|
||||
|
||||
case texture_format::bgra8_unorm:
|
||||
return vk::Format::eB8G8R8A8Unorm;
|
||||
case texture_format::bgra8_srgb:
|
||||
return vk::Format::eB8G8R8A8Srgb;
|
||||
|
||||
case texture_format::r16_unorm:
|
||||
return vk::Format::eR16Unorm;
|
||||
case texture_format::r16_snorm:
|
||||
return vk::Format::eR16Snorm;
|
||||
case texture_format::r16_uint:
|
||||
return vk::Format::eR16Uint;
|
||||
case texture_format::r16_sint:
|
||||
return vk::Format::eR16Sint;
|
||||
case texture_format::r16_float:
|
||||
return vk::Format::eR16Sfloat;
|
||||
|
||||
case texture_format::rg16_unorm:
|
||||
return vk::Format::eR16G16Unorm;
|
||||
case texture_format::rg16_snorm:
|
||||
return vk::Format::eR16G16Snorm;
|
||||
case texture_format::rg16_uint:
|
||||
return vk::Format::eR16G16Uint;
|
||||
case texture_format::rg16_sint:
|
||||
return vk::Format::eR16G16Sint;
|
||||
case texture_format::rg16_float:
|
||||
return vk::Format::eR16G16Sfloat;
|
||||
|
||||
case texture_format::rgba16_unorm:
|
||||
return vk::Format::eR16G16B16A16Unorm;
|
||||
case texture_format::rgba16_snorm:
|
||||
return vk::Format::eR16G16B16A16Snorm;
|
||||
case texture_format::rgba16_uint:
|
||||
return vk::Format::eR16G16B16A16Uint;
|
||||
case texture_format::rgba16_sint:
|
||||
return vk::Format::eR16G16B16A16Sint;
|
||||
case texture_format::rgba16_float:
|
||||
return vk::Format::eR16G16B16A16Sfloat;
|
||||
|
||||
case texture_format::r32_uint:
|
||||
return vk::Format::eR32Uint;
|
||||
case texture_format::r32_sint:
|
||||
return vk::Format::eR32Sint;
|
||||
case texture_format::r32_float:
|
||||
return vk::Format::eR32Sfloat;
|
||||
|
||||
case texture_format::rg32_uint:
|
||||
return vk::Format::eR32G32Uint;
|
||||
case texture_format::rg32_sint:
|
||||
return vk::Format::eR32G32Sint;
|
||||
case texture_format::rg32_float:
|
||||
return vk::Format::eR32G32Sfloat;
|
||||
|
||||
case texture_format::rgb32_uint:
|
||||
return vk::Format::eR32G32B32Uint;
|
||||
case texture_format::rgb32_sint:
|
||||
return vk::Format::eR32G32B32Sint;
|
||||
case texture_format::rgb32_float:
|
||||
return vk::Format::eR32G32B32Sfloat;
|
||||
|
||||
case texture_format::rgba32_uint:
|
||||
return vk::Format::eR32G32B32A32Uint;
|
||||
case texture_format::rgba32_sint:
|
||||
return vk::Format::eR32G32B32A32Sint;
|
||||
case texture_format::rgba32_float:
|
||||
return vk::Format::eR32G32B32A32Sfloat;
|
||||
|
||||
case texture_format::depth16_unorm:
|
||||
return vk::Format::eD16Unorm;
|
||||
case texture_format::depth24_unorm:
|
||||
return vk::Format::eX8D24UnormPack32;
|
||||
case texture_format::depth32_float:
|
||||
return vk::Format::eD32Sfloat;
|
||||
case texture_format::depth24_unorm_stencil8_uint:
|
||||
return vk::Format::eD24UnormS8Uint;
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
return vk::Format::eD32SfloatS8Uint;
|
||||
case texture_format::stencil8_uint:
|
||||
return vk::Format::eS8Uint;
|
||||
|
||||
case texture_format::bc1_rgb_unorm:
|
||||
return vk::Format::eBc1RgbUnormBlock;
|
||||
case texture_format::bc1_rgb_srgb:
|
||||
return vk::Format::eBc1RgbSrgbBlock;
|
||||
case texture_format::bc1_rgba_unorm:
|
||||
return vk::Format::eBc1RgbaUnormBlock;
|
||||
case texture_format::bc1_rgba_srgb:
|
||||
return vk::Format::eBc1RgbaSrgbBlock;
|
||||
case texture_format::bc2_unorm:
|
||||
return vk::Format::eBc2UnormBlock;
|
||||
case texture_format::bc2_srgb:
|
||||
return vk::Format::eBc2SrgbBlock;
|
||||
case texture_format::bc3_unorm:
|
||||
return vk::Format::eBc3UnormBlock;
|
||||
case texture_format::bc3_srgb:
|
||||
return vk::Format::eBc3SrgbBlock;
|
||||
case texture_format::bc4_unorm:
|
||||
return vk::Format::eBc4UnormBlock;
|
||||
case texture_format::bc4_snorm:
|
||||
return vk::Format::eBc4SnormBlock;
|
||||
case texture_format::bc5_unorm:
|
||||
return vk::Format::eBc5UnormBlock;
|
||||
case texture_format::bc5_snorm:
|
||||
return vk::Format::eBc5SnormBlock;
|
||||
case texture_format::bc6h_ufloat:
|
||||
return vk::Format::eBc6HUfloatBlock;
|
||||
case texture_format::bc6h_sfloat:
|
||||
return vk::Format::eBc6HSfloatBlock;
|
||||
case texture_format::bc7_unorm:
|
||||
return vk::Format::eBc7UnormBlock;
|
||||
case texture_format::bc7_srgb:
|
||||
return vk::Format::eBc7SrgbBlock;
|
||||
|
||||
case texture_format::r11g11b10_float:
|
||||
return vk::Format::eB10G11R11UfloatPack32;
|
||||
case texture_format::rgb9e5_float:
|
||||
return vk::Format::eE5B9G9R9UfloatPack32;
|
||||
case texture_format::rgb10a2_unorm:
|
||||
return vk::Format::eA2R10G10B10UnormPack32;
|
||||
case texture_format::rgb10a2_uint:
|
||||
return vk::Format::eA2R10G10B10UintPack32;
|
||||
|
||||
default:
|
||||
return vk::Format::eUndefined; // 未实现的格式返回未定义
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_depth_format(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::depth16_unorm:
|
||||
case texture_format::depth24_unorm:
|
||||
case texture_format::depth32_float:
|
||||
case texture_format::depth24_unorm_stencil8_uint:
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_stencil_format(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::depth24_unorm_stencil8_uint:
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
case texture_format::stencil8_uint:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_depth_stencil_format(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::depth24_unorm_stencil8_uint:
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_compressed_format(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::bc1_rgb_unorm:
|
||||
case texture_format::bc1_rgb_srgb:
|
||||
case texture_format::bc1_rgba_unorm:
|
||||
case texture_format::bc1_rgba_srgb:
|
||||
case texture_format::bc2_unorm:
|
||||
case texture_format::bc2_srgb:
|
||||
case texture_format::bc3_unorm:
|
||||
case texture_format::bc3_srgb:
|
||||
case texture_format::bc4_unorm:
|
||||
case texture_format::bc4_snorm:
|
||||
case texture_format::bc5_unorm:
|
||||
case texture_format::bc5_snorm:
|
||||
case texture_format::bc6h_ufloat:
|
||||
case texture_format::bc6h_sfloat:
|
||||
case texture_format::bc7_unorm:
|
||||
case texture_format::bc7_srgb:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_srgb_format(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::bgra8_srgb:
|
||||
case texture_format::bc1_rgb_srgb:
|
||||
case texture_format::bc1_rgba_srgb:
|
||||
case texture_format::bc2_srgb:
|
||||
case texture_format::bc3_srgb:
|
||||
case texture_format::bc7_srgb:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 get_format_byte_size(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::undefined:
|
||||
return 0;
|
||||
|
||||
// ====================================================
|
||||
// 1 Byte / Pixel (8 bits)
|
||||
// ====================================================
|
||||
case texture_format::r8_unorm:
|
||||
case texture_format::r8_snorm:
|
||||
case texture_format::r8_uint:
|
||||
case texture_format::r8_sint:
|
||||
case texture_format::stencil8_uint:
|
||||
return 1;
|
||||
|
||||
// ====================================================
|
||||
// 2 Bytes / Pixel (16 bits)
|
||||
// ====================================================
|
||||
case texture_format::rg8_unorm:
|
||||
case texture_format::rg8_snorm:
|
||||
case texture_format::rg8_uint:
|
||||
case texture_format::rg8_sint:
|
||||
// ----
|
||||
case texture_format::r16_unorm:
|
||||
case texture_format::r16_snorm:
|
||||
case texture_format::r16_uint:
|
||||
case texture_format::r16_sint:
|
||||
case texture_format::r16_float:
|
||||
// ----
|
||||
case texture_format::depth16_unorm:
|
||||
return 2;
|
||||
|
||||
// ====================================================
|
||||
// 4 Bytes / Pixel (32 bits)
|
||||
// ====================================================
|
||||
case texture_format::rgba8_unorm:
|
||||
case texture_format::rgba8_snorm:
|
||||
case texture_format::rgba8_uint:
|
||||
case texture_format::rgba8_sint:
|
||||
case texture_format::bgra8_unorm:
|
||||
case texture_format::bgra8_srgb:
|
||||
// ----
|
||||
case texture_format::rg16_unorm:
|
||||
case texture_format::rg16_snorm:
|
||||
case texture_format::rg16_uint:
|
||||
case texture_format::rg16_sint:
|
||||
case texture_format::rg16_float:
|
||||
// ----
|
||||
case texture_format::r32_uint:
|
||||
case texture_format::r32_sint:
|
||||
case texture_format::r32_float:
|
||||
// ----
|
||||
case texture_format::depth24_unorm: // 通常作为 X8_D24 pack32 存储
|
||||
case texture_format::depth32_float:
|
||||
case texture_format::depth24_unorm_stencil8_uint: // 24+8 = 32 bits
|
||||
// ----
|
||||
case texture_format::r11g11b10_float:
|
||||
case texture_format::rgb9e5_float:
|
||||
case texture_format::rgb10a2_unorm:
|
||||
case texture_format::rgb10a2_uint:
|
||||
return 4;
|
||||
|
||||
// ====================================================
|
||||
// 8 Bytes / Pixel or Block (64 bits)
|
||||
// ====================================================
|
||||
// Uncompressed (Pixel size)
|
||||
case texture_format::rgba16_unorm:
|
||||
case texture_format::rgba16_snorm:
|
||||
case texture_format::rgba16_uint:
|
||||
case texture_format::rgba16_sint:
|
||||
case texture_format::rgba16_float:
|
||||
// ----
|
||||
case texture_format::rg32_uint:
|
||||
case texture_format::rg32_sint:
|
||||
case texture_format::rg32_float:
|
||||
// ----
|
||||
// D32_S8 在 Vulkan 中通常需要 64位对齐 (4字节深度 + 4字节模板/填充)
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
return 8;
|
||||
|
||||
// Compressed (Block size - 4x4 pixels)
|
||||
// BC1 (DXT1) & BC4 (ATI1) use 64 bits per block
|
||||
case texture_format::bc1_rgb_unorm:
|
||||
case texture_format::bc1_rgb_srgb:
|
||||
case texture_format::bc1_rgba_unorm:
|
||||
case texture_format::bc1_rgba_srgb:
|
||||
case texture_format::bc4_unorm:
|
||||
case texture_format::bc4_snorm:
|
||||
return 8;
|
||||
|
||||
// ====================================================
|
||||
// 12 Bytes / Pixel (96 bits)
|
||||
// ====================================================
|
||||
case texture_format::rgb32_uint:
|
||||
case texture_format::rgb32_sint:
|
||||
case texture_format::rgb32_float:
|
||||
return 12;
|
||||
|
||||
// ====================================================
|
||||
// 16 Bytes / Pixel or Block (128 bits)
|
||||
// ====================================================
|
||||
// Uncompressed (Pixel size)
|
||||
case texture_format::rgba32_uint:
|
||||
case texture_format::rgba32_sint:
|
||||
case texture_format::rgba32_float:
|
||||
return 16;
|
||||
|
||||
// Compressed (Block size - 4x4 pixels)
|
||||
// BC2, BC3, BC5, BC6H, BC7 use 128 bits per block
|
||||
case texture_format::bc2_unorm:
|
||||
case texture_format::bc2_srgb:
|
||||
case texture_format::bc3_unorm:
|
||||
case texture_format::bc3_srgb:
|
||||
case texture_format::bc5_unorm:
|
||||
case texture_format::bc5_snorm:
|
||||
case texture_format::bc6h_ufloat:
|
||||
case texture_format::bc6h_sfloat:
|
||||
case texture_format::bc7_unorm:
|
||||
case texture_format::bc7_srgb:
|
||||
return 16;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 get_format_channel_count(texture_format format) {
|
||||
switch (format) {
|
||||
case texture_format::undefined:
|
||||
return 0;
|
||||
|
||||
// ====================================================
|
||||
// 1 Channel (R, Depth, Stencil, Alpha-only, etc.)
|
||||
// ====================================================
|
||||
case texture_format::r8_unorm:
|
||||
case texture_format::r8_snorm:
|
||||
case texture_format::r8_uint:
|
||||
case texture_format::r8_sint:
|
||||
// ----
|
||||
case texture_format::r16_unorm:
|
||||
case texture_format::r16_snorm:
|
||||
case texture_format::r16_uint:
|
||||
case texture_format::r16_sint:
|
||||
case texture_format::r16_float:
|
||||
// ----
|
||||
case texture_format::r32_uint:
|
||||
case texture_format::r32_sint:
|
||||
case texture_format::r32_float:
|
||||
// ----
|
||||
case texture_format::depth16_unorm:
|
||||
case texture_format::depth24_unorm:
|
||||
case texture_format::depth32_float:
|
||||
case texture_format::stencil8_uint:
|
||||
// ----
|
||||
case texture_format::bc4_unorm:
|
||||
case texture_format::bc4_snorm:
|
||||
return 1;
|
||||
|
||||
// ====================================================
|
||||
// 2 Channels (RG, Depth+Stencil)
|
||||
// ====================================================
|
||||
case texture_format::rg8_unorm:
|
||||
case texture_format::rg8_snorm:
|
||||
case texture_format::rg8_uint:
|
||||
case texture_format::rg8_sint:
|
||||
// ----
|
||||
case texture_format::rg16_unorm:
|
||||
case texture_format::rg16_snorm:
|
||||
case texture_format::rg16_uint:
|
||||
case texture_format::rg16_sint:
|
||||
case texture_format::rg16_float:
|
||||
// ----
|
||||
case texture_format::rg32_uint:
|
||||
case texture_format::rg32_sint:
|
||||
case texture_format::rg32_float:
|
||||
// ----
|
||||
// Depth + Stencil 被视为 2 个独立的数据分量
|
||||
case texture_format::depth24_unorm_stencil8_uint:
|
||||
case texture_format::depth32_float_stencil8_uint:
|
||||
// ----
|
||||
case texture_format::bc5_unorm:
|
||||
case texture_format::bc5_snorm:
|
||||
return 2;
|
||||
|
||||
// ====================================================
|
||||
// 3 Channels (RGB)
|
||||
// ====================================================
|
||||
case texture_format::rgb32_uint:
|
||||
case texture_format::rgb32_sint:
|
||||
case texture_format::rgb32_float:
|
||||
// ----
|
||||
case texture_format::bc1_rgb_unorm:
|
||||
case texture_format::bc1_rgb_srgb:
|
||||
// ----
|
||||
// BC6H 仅支持 HDR RGB,无 Alpha
|
||||
case texture_format::bc6h_ufloat:
|
||||
case texture_format::bc6h_sfloat:
|
||||
// ----
|
||||
case texture_format::r11g11b10_float:
|
||||
case texture_format::rgb9e5_float:
|
||||
return 3;
|
||||
|
||||
// ====================================================
|
||||
// 4 Channels (RGBA, BGRA)
|
||||
// ====================================================
|
||||
case texture_format::rgba8_unorm:
|
||||
case texture_format::rgba8_snorm:
|
||||
case texture_format::rgba8_uint:
|
||||
case texture_format::rgba8_sint:
|
||||
case texture_format::bgra8_unorm:
|
||||
case texture_format::bgra8_srgb:
|
||||
// ----
|
||||
case texture_format::rgba16_unorm:
|
||||
case texture_format::rgba16_snorm:
|
||||
case texture_format::rgba16_uint:
|
||||
case texture_format::rgba16_sint:
|
||||
case texture_format::rgba16_float:
|
||||
// ----
|
||||
case texture_format::rgba32_uint:
|
||||
case texture_format::rgba32_sint:
|
||||
case texture_format::rgba32_float:
|
||||
// ----
|
||||
case texture_format::bc1_rgba_unorm:
|
||||
case texture_format::bc1_rgba_srgb:
|
||||
case texture_format::bc2_unorm:
|
||||
case texture_format::bc2_srgb:
|
||||
case texture_format::bc3_unorm:
|
||||
case texture_format::bc3_srgb:
|
||||
case texture_format::bc7_unorm:
|
||||
case texture_format::bc7_srgb:
|
||||
// ----
|
||||
case texture_format::rgb10a2_unorm:
|
||||
case texture_format::rgb10a2_uint:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_image_type(texture_type type) noexcept {
|
||||
switch (type) {
|
||||
case texture_type::texture_1d:
|
||||
case texture_type::texture_1d_array:
|
||||
return vk::ImageType::e1D;
|
||||
case texture_type::texture_2d:
|
||||
case texture_type::texture_2d_array:
|
||||
case texture_type::texture_cube:
|
||||
case texture_type::texture_cube_array:
|
||||
return vk::ImageType::e2D;
|
||||
case texture_type::texture_3d:
|
||||
return vk::ImageType::e3D;
|
||||
default:
|
||||
return vk::ImageType::e2D; // 默认返回 2D
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_image_view_type(texture_type type) noexcept {
|
||||
switch (type) {
|
||||
case texture_type::texture_1d:
|
||||
return vk::ImageViewType::e1D;
|
||||
case texture_type::texture_2d:
|
||||
return vk::ImageViewType::e2D;
|
||||
case texture_type::texture_3d:
|
||||
return vk::ImageViewType::e3D;
|
||||
case texture_type::texture_cube:
|
||||
return vk::ImageViewType::eCube;
|
||||
case texture_type::texture_1d_array:
|
||||
return vk::ImageViewType::e1DArray;
|
||||
case texture_type::texture_2d_array:
|
||||
return vk::ImageViewType::e2DArray;
|
||||
case texture_type::texture_cube_array:
|
||||
return vk::ImageViewType::eCubeArray;
|
||||
default:
|
||||
return vk::ImageViewType::e2D; // 默认返回 2D
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_image_usage(texture_usage usage) noexcept {
|
||||
vk::ImageUsageFlags flags;
|
||||
|
||||
if (usage & texture_usage::sampled) {
|
||||
flags |= vk::ImageUsageFlagBits::eSampled;
|
||||
}
|
||||
if (usage & texture_usage::storage) {
|
||||
flags |= vk::ImageUsageFlagBits::eStorage;
|
||||
}
|
||||
if (usage & texture_usage::color_attachment) {
|
||||
flags |= vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
if (usage & texture_usage::depth_stencil_attachment) {
|
||||
flags |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
}
|
||||
if (usage & texture_usage::input_attachment) {
|
||||
flags |= vk::ImageUsageFlagBits::eInputAttachment;
|
||||
}
|
||||
if (usage & texture_usage::transfer_src) {
|
||||
flags |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||
}
|
||||
if (usage & texture_usage::transfer_dst) {
|
||||
flags |= vk::ImageUsageFlagBits::eTransferDst;
|
||||
}
|
||||
if (usage & texture_usage::transient_attachment) {
|
||||
flags |= vk::ImageUsageFlagBits::eTransientAttachment;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_filter(sampler_filter filter) noexcept {
|
||||
switch (filter) {
|
||||
case sampler_filter::nearest:
|
||||
return vk::Filter::eNearest;
|
||||
case sampler_filter::linear:
|
||||
return vk::Filter::eLinear;
|
||||
default:
|
||||
return vk::Filter::eNearest; // 默认返回最近点过滤
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_address_mode(sampler_address_mode mode) noexcept {
|
||||
switch (mode) {
|
||||
case sampler_address_mode::repeat:
|
||||
return vk::SamplerAddressMode::eRepeat;
|
||||
case sampler_address_mode::mirrored_repeat:
|
||||
return vk::SamplerAddressMode::eMirroredRepeat;
|
||||
case sampler_address_mode::clamp_to_edge:
|
||||
return vk::SamplerAddressMode::eClampToEdge;
|
||||
case sampler_address_mode::clamp_to_border:
|
||||
return vk::SamplerAddressMode::eClampToBorder;
|
||||
case sampler_address_mode::mirror_clamp_to_edge:
|
||||
return vk::SamplerAddressMode::eMirrorClampToEdge;
|
||||
default:
|
||||
return vk::SamplerAddressMode::eRepeat; // 默认返回重复模式
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_mipmap_mode(sampler_mipmap_mode mode) noexcept {
|
||||
switch (mode) {
|
||||
case sampler_mipmap_mode::nearest:
|
||||
return vk::SamplerMipmapMode::eNearest;
|
||||
case sampler_mipmap_mode::linear:
|
||||
return vk::SamplerMipmapMode::eLinear;
|
||||
default:
|
||||
return vk::SamplerMipmapMode::eNearest; // 默认返回最近点过滤
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_border_color(border_color color) noexcept {
|
||||
switch (color) {
|
||||
case border_color::transparent_black_float:
|
||||
return vk::BorderColor::eFloatTransparentBlack;
|
||||
case border_color::transparent_black_int:
|
||||
return vk::BorderColor::eIntTransparentBlack;
|
||||
case border_color::opaque_black_float:
|
||||
return vk::BorderColor::eFloatOpaqueBlack;
|
||||
case border_color::opaque_black_int:
|
||||
return vk::BorderColor::eIntOpaqueBlack;
|
||||
case border_color::opaque_white_float:
|
||||
return vk::BorderColor::eFloatOpaqueWhite;
|
||||
case border_color::opaque_white_int:
|
||||
return vk::BorderColor::eIntOpaqueWhite;
|
||||
default:
|
||||
return vk::BorderColor::eFloatTransparentBlack; // 默认返回透明黑色
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_compare_op(compare_op op) noexcept {
|
||||
switch (op) {
|
||||
case compare_op::never:
|
||||
return vk::CompareOp::eNever;
|
||||
case compare_op::less:
|
||||
return vk::CompareOp::eLess;
|
||||
case compare_op::equal:
|
||||
return vk::CompareOp::eEqual;
|
||||
case compare_op::less_equal:
|
||||
return vk::CompareOp::eLessOrEqual;
|
||||
case compare_op::greater:
|
||||
return vk::CompareOp::eGreater;
|
||||
case compare_op::not_equal:
|
||||
return vk::CompareOp::eNotEqual;
|
||||
case compare_op::greater_equal:
|
||||
return vk::CompareOp::eGreaterOrEqual;
|
||||
case compare_op::always:
|
||||
return vk::CompareOp::eAlways;
|
||||
default:
|
||||
return vk::CompareOp::eAlways; // 默认返回总是通过
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_image_aspect(image_aspect aspect) noexcept {
|
||||
switch (aspect) {
|
||||
case image_aspect::color:
|
||||
return vk::ImageAspectFlagBits::eColor;
|
||||
case image_aspect::depth:
|
||||
return vk::ImageAspectFlagBits::eDepth;
|
||||
case image_aspect::stencil:
|
||||
return vk::ImageAspectFlagBits::eStencil;
|
||||
case image_aspect::metadata:
|
||||
return vk::ImageAspectFlagBits::eMetadata;
|
||||
default:
|
||||
return vk::ImageAspectFlagBits::eColor; // 默认返回颜色方面
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto get_default_aspect(texture_format format) noexcept {
|
||||
if (is_depth_stencil_format(format)) {
|
||||
return image_aspect::depth | image_aspect::stencil;
|
||||
}
|
||||
if (is_depth_format(format)) {
|
||||
return image_aspect::depth;
|
||||
}
|
||||
if (is_stencil_format(format)) {
|
||||
return image_aspect::stencil;
|
||||
}
|
||||
return image_aspect::color;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_image_layout(image_layout layout) noexcept {
|
||||
switch (layout) {
|
||||
case image_layout::undefined:
|
||||
return vk::ImageLayout::eUndefined;
|
||||
case image_layout::general:
|
||||
return vk::ImageLayout::eGeneral;
|
||||
case image_layout::color_attachment_optimal:
|
||||
return vk::ImageLayout::eColorAttachmentOptimal;
|
||||
case image_layout::depth_stencil_attachment_optimal:
|
||||
return vk::ImageLayout::eDepthStencilAttachmentOptimal;
|
||||
case image_layout::depth_stencil_read_only_optimal:
|
||||
return vk::ImageLayout::eDepthStencilReadOnlyOptimal;
|
||||
case image_layout::shader_read_only_optimal:
|
||||
return vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
case image_layout::transfer_src_optimal:
|
||||
return vk::ImageLayout::eTransferSrcOptimal;
|
||||
case image_layout::transfer_dst_optimal:
|
||||
return vk::ImageLayout::eTransferDstOptimal;
|
||||
case image_layout::preinitialized:
|
||||
return vk::ImageLayout::ePreinitialized;
|
||||
case image_layout::depth_read_only_stencil_attachment_optimal:
|
||||
return vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal;
|
||||
case image_layout::depth_attachment_stencil_read_only_optimal:
|
||||
return vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal;
|
||||
case image_layout::depth_attachment_optimal:
|
||||
return vk::ImageLayout::eDepthAttachmentOptimal;
|
||||
case image_layout::depth_read_only_optimal:
|
||||
return vk::ImageLayout::eDepthReadOnlyOptimal;
|
||||
case image_layout::stencil_attachment_optimal:
|
||||
return vk::ImageLayout::eStencilAttachmentOptimal;
|
||||
case image_layout::stencil_read_only_optimal:
|
||||
return vk::ImageLayout::eStencilReadOnlyOptimal;
|
||||
case image_layout::read_only_optimal:
|
||||
return vk::ImageLayout::eReadOnlyOptimal;
|
||||
case image_layout::attachment_optimal:
|
||||
return vk::ImageLayout::eAttachmentOptimal;
|
||||
case image_layout::present_src:
|
||||
return vk::ImageLayout::ePresentSrcKHR;
|
||||
default:
|
||||
return vk::ImageLayout::eUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_sample_count(sample_count count) noexcept {
|
||||
switch (count) {
|
||||
case sample_count::count_1:
|
||||
return vk::SampleCountFlagBits::e1;
|
||||
case sample_count::count_2:
|
||||
return vk::SampleCountFlagBits::e2;
|
||||
case sample_count::count_4:
|
||||
return vk::SampleCountFlagBits::e4;
|
||||
case sample_count::count_8:
|
||||
return vk::SampleCountFlagBits::e8;
|
||||
case sample_count::count_16:
|
||||
return vk::SampleCountFlagBits::e16;
|
||||
case sample_count::count_32:
|
||||
return vk::SampleCountFlagBits::e32;
|
||||
case sample_count::count_64:
|
||||
return vk::SampleCountFlagBits::e64;
|
||||
default:
|
||||
return vk::SampleCountFlagBits::e1; // 默认返回 1 样本
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto to_vulkan_descriptor_type(descriptor_type type) noexcept {
|
||||
switch (type) {
|
||||
case descriptor_type::sampler:
|
||||
return vk::DescriptorType::eSampler;
|
||||
case descriptor_type::combined_image_sampler:
|
||||
return vk::DescriptorType::eCombinedImageSampler;
|
||||
case descriptor_type::sampled_image:
|
||||
return vk::DescriptorType::eSampledImage;
|
||||
case descriptor_type::storage_image:
|
||||
return vk::DescriptorType::eStorageImage;
|
||||
case descriptor_type::uniform_texel_buffer:
|
||||
return vk::DescriptorType::eUniformTexelBuffer;
|
||||
case descriptor_type::storage_texel_buffer:
|
||||
return vk::DescriptorType::eStorageTexelBuffer;
|
||||
case descriptor_type::uniform_buffer:
|
||||
return vk::DescriptorType::eUniformBuffer;
|
||||
case descriptor_type::storage_buffer:
|
||||
return vk::DescriptorType::eStorageBuffer;
|
||||
case descriptor_type::uniform_buffer_dynamic:
|
||||
return vk::DescriptorType::eUniformBufferDynamic;
|
||||
case descriptor_type::storage_buffer_dynamic:
|
||||
return vk::DescriptorType::eStorageBufferDynamic;
|
||||
case descriptor_type::input_attachment:
|
||||
return vk::DescriptorType::eInputAttachment;
|
||||
case descriptor_type::acceleration_structure:
|
||||
return vk::DescriptorType::eAccelerationStructureKHR;
|
||||
default:
|
||||
return vk::DescriptorType::eSampler;
|
||||
}
|
||||
}
|
||||
}
|
||||
359
src/render/vulkan_instance.cpp
Normal file
359
src/render/vulkan_instance.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
#include "vulkan_instance.h"
|
||||
|
||||
#include "vulkan_types.h"
|
||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
||||
#include <set>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
#include "core/logger.h"
|
||||
|
||||
namespace mirai {
|
||||
#if MIRAI_DEBUG
|
||||
/// Vulkan 验证层名称
|
||||
constexpr const char* validation_layer_name = "VK_LAYER_KHRONOS_validation";
|
||||
|
||||
/// 验证层列表
|
||||
constexpr std::array<const char*, 1> validation_layers = {
|
||||
validation_layer_name
|
||||
};
|
||||
#endif
|
||||
|
||||
/// 必需的设备扩展
|
||||
constexpr std::array<const char*, 1> required_device_extensions = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
||||
};
|
||||
|
||||
/// 可选的设备扩展(用于额外功能)
|
||||
constexpr std::array<const char*, 3> optional_device_extensions = {
|
||||
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
|
||||
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
|
||||
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME
|
||||
};
|
||||
|
||||
vulkan_instance::vulkan_instance(const vulkan_instance_config& config) {
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
bool vulkan_instance::is_extension_supported(const char* extension_name) {
|
||||
const auto& extensions = get_available_extensions();
|
||||
return std::ranges::any_of(extensions, [extension_name](const vk::ExtensionProperties& ext) {
|
||||
return std::strcmp(ext.extensionName, extension_name) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool vulkan_instance::is_layer_supported(const char* layer_name) {
|
||||
const auto& layers = get_available_layers();
|
||||
return std::ranges::any_of(layers, [layer_name](const vk::LayerProperties& layer) {
|
||||
return std::strcmp(layer.layerName, layer_name) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<vk::ExtensionProperties> vulkan_instance::get_available_extensions() {
|
||||
auto [result, extensions] = vk::enumerateInstanceExtensionProperties();
|
||||
if (result != vk::Result::eSuccess) {
|
||||
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance extensions: {}", vk::to_string(result));
|
||||
return {};
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
std::vector<vk::LayerProperties> vulkan_instance::get_available_layers() {
|
||||
auto [result, layers] = vk::enumerateInstanceLayerProperties();
|
||||
if (result != vk::Result::eSuccess) {
|
||||
MIRAI_LOG_ERROR("Failed to enumerate Vulkan instance layers: {}", vk::to_string(result));
|
||||
return {};
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
u32 vulkan_instance::get_supported_api_version() {
|
||||
auto [result, version] = vk::enumerateInstanceVersion();
|
||||
if (result != vk::Result::eSuccess) {
|
||||
MIRAI_LOG_ERROR("Failed to get supported Vulkan API version: {}", vk::to_string(result));
|
||||
return VK_API_VERSION_1_0;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
void vulkan_instance::on_created() {
|
||||
object::on_created();
|
||||
{
|
||||
const vk_dynamic_loader dl;
|
||||
auto proc_addr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(proc_addr);
|
||||
}
|
||||
|
||||
auto result = create_instance(config_);
|
||||
if (!result.has_value()) {
|
||||
MIRAI_LOG_ERROR("Failed to create Vulkan instance: {}", result.error().full_description());
|
||||
return;
|
||||
}
|
||||
|
||||
#if MIRAI_DEBUG
|
||||
if (validation_enabled_) {
|
||||
result = setup_debug_messenger();
|
||||
if (!result.has_value()) {
|
||||
MIRAI_LOG_WARN("Failed to setup debug messenger: {}", result.error().full_description());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MIRAI_LOG_INFO("Vulkan instance object created");
|
||||
}
|
||||
|
||||
void vulkan_instance::on_destroying() {
|
||||
object::on_destroying();
|
||||
MIRAI_LOG_INFO("Vulkan instance object destroying");
|
||||
#if MIRAI_DEBUG
|
||||
if (debug_messenger_) {
|
||||
destroy_debug_messenger();
|
||||
}
|
||||
#endif
|
||||
if (instance_) {
|
||||
instance_.destroy();
|
||||
instance_ = nullptr;
|
||||
}
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void_result_t vulkan_instance::create_instance(const vulkan_instance_config& config) {
|
||||
#if MIRAI_DEBUG
|
||||
validation_enabled_ = config.enable_validation;
|
||||
if (validation_enabled_ && !is_layer_supported(validation_layer_name)) {
|
||||
MIRAI_LOG_WARN("Vulkan validation layer not supported, disabling validation");
|
||||
validation_enabled_ = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
api_version_ = config.api_version;
|
||||
u32 supported_version = get_supported_api_version();
|
||||
if (supported_version < api_version_) {
|
||||
return MAKE_ERROR_INFO(error_code::vulkan_init_failed,
|
||||
"Requested Vulkan API version not supported");
|
||||
}
|
||||
|
||||
// 应用信息
|
||||
vk::ApplicationInfo app_info{};
|
||||
app_info.setPApplicationName(config.app_name.c_str())
|
||||
.setApplicationVersion(config.app_version)
|
||||
.setPEngineName(config.engine_name.c_str())
|
||||
.setEngineVersion(config.engine_version)
|
||||
.setApiVersion(api_version_);
|
||||
|
||||
// 获取必需的扩展
|
||||
enabled_extensions_ = get_required_extensions(config);
|
||||
|
||||
// 验证所有扩展都可用
|
||||
for (const char* ext : enabled_extensions_) {
|
||||
if (!is_extension_supported(ext)) {
|
||||
return MAKE_ERROR_INFO(error_code::vulkan_init_failed,
|
||||
"Required extension not supported: {}", ext);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置层
|
||||
enabled_layers_.clear();
|
||||
if (validation_enabled_) {
|
||||
enabled_layers_.push_back(validation_layer_name);
|
||||
}
|
||||
for (const char* layer : config.extra_layers) {
|
||||
if (is_layer_supported(layer)) {
|
||||
enabled_layers_.push_back(layer);
|
||||
}
|
||||
}
|
||||
|
||||
// 实例创建信息
|
||||
vk::InstanceCreateInfo create_info{};
|
||||
create_info.setPApplicationInfo(&app_info)
|
||||
.setEnabledExtensionCount(static_cast<u32>(enabled_extensions_.size()))
|
||||
.setPpEnabledExtensionNames(enabled_extensions_.data())
|
||||
.setEnabledLayerCount(static_cast<u32>(enabled_layers_.size()))
|
||||
.setPpEnabledLayerNames(enabled_layers_.data());
|
||||
|
||||
#if MIRAI_DEBUG
|
||||
// 调试信使创建信息(用于实例创建/销毁阶段的调试)
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_create_info{};
|
||||
debug_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
if (validation_enabled_) {
|
||||
debug_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
debug_create_info.pfnUserCallback = debug_callback;
|
||||
debug_create_info.pUserData = this;
|
||||
|
||||
create_info.setPNext(&debug_create_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 创建实例
|
||||
auto [result, instance] = vk::createInstance(create_info);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
return MAKE_ERROR_INFO(error_code::vulkan_init_failed,
|
||||
"Failed to create Vulkan instance: {}", vk::to_string(result));
|
||||
}
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||
instance_ = instance;
|
||||
|
||||
MIRAI_LOG_INFO("Vulkan instance created with API version {}.{}.{}",
|
||||
VK_VERSION_MAJOR(api_version_),
|
||||
VK_VERSION_MINOR(api_version_),
|
||||
VK_VERSION_PATCH(api_version_));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto vulkan_instance::get_required_extensions(
|
||||
const vulkan_instance_config& config) const -> std::vector<const char*> {
|
||||
std::vector<const char*> extensions;
|
||||
|
||||
// 获取 SDL 需要的扩展
|
||||
u32 sdl_extension_count = 0;
|
||||
const char* const* sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count);
|
||||
|
||||
if (sdl_extensions != nullptr) {
|
||||
for (u32 i = 0; i < sdl_extension_count; ++i) {
|
||||
extensions.push_back(sdl_extensions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#if MIRAI_DEBUG
|
||||
// 如果启用验证层,添加调试扩展
|
||||
if (config.enable_validation) {
|
||||
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 添加额外的扩展
|
||||
for (const char* ext : config.extra_extensions) {
|
||||
extensions.push_back(ext);
|
||||
}
|
||||
|
||||
// 去重
|
||||
std::set<std::string_view> unique_extensions;
|
||||
std::vector<const char*> result;
|
||||
for (const char* ext : extensions) {
|
||||
if (unique_extensions.insert(ext).second) {
|
||||
result.push_back(ext);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if MIRAI_DEBUG
|
||||
VkBool32 vulkan_instance::debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||
void* user_data) {
|
||||
auto* instance = static_cast<vulkan_instance*>(user_data);
|
||||
|
||||
// 转换严重级别
|
||||
debug_severity severity;
|
||||
if (message_severity & (VkDebugUtilsMessageSeverityFlagBitsEXT)
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) {
|
||||
severity = debug_severity::verbose;
|
||||
}
|
||||
else if (message_severity & (VkDebugUtilsMessageSeverityFlagBitsEXT)
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) {
|
||||
severity = debug_severity::info;
|
||||
}
|
||||
else if (message_severity & (VkDebugUtilsMessageSeverityFlagBitsEXT)
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
|
||||
severity = debug_severity::warning;
|
||||
}
|
||||
else {
|
||||
severity = debug_severity::error;
|
||||
}
|
||||
|
||||
// 转换消息类型
|
||||
debug_type type;
|
||||
if (message_type & (VkDebugUtilsMessageTypeFlagsEXT)vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation) {
|
||||
type = debug_type::validation;
|
||||
}
|
||||
else if (message_type & (VkDebugUtilsMessageTypeFlagsEXT)vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance) {
|
||||
type = debug_type::performance;
|
||||
}
|
||||
else {
|
||||
type = debug_type::general;
|
||||
}
|
||||
|
||||
// 调用用户回调
|
||||
if (instance != nullptr && instance->user_debug_callback_) {
|
||||
instance->user_debug_callback_(severity, type, callback_data->pMessage);
|
||||
}
|
||||
|
||||
// 使用日志系统记录
|
||||
switch (severity) {
|
||||
case debug_severity::verbose:
|
||||
MIRAI_LOG_TRACE("[Vulkan] {}", callback_data->pMessage);
|
||||
break;
|
||||
case debug_severity::info:
|
||||
MIRAI_LOG_DEBUG("[Vulkan] {}", callback_data->pMessage);
|
||||
break;
|
||||
case debug_severity::warning:
|
||||
MIRAI_LOG_WARN("[Vulkan] {}", callback_data->pMessage);
|
||||
break;
|
||||
case debug_severity::error:
|
||||
MIRAI_LOG_ERROR("[Vulkan] {}", callback_data->pMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
void_result_t vulkan_instance::setup_debug_messenger() {
|
||||
if (!validation_enabled_ || !instance_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// 使用 C API 创建调试信使
|
||||
auto func = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
|
||||
vkGetInstanceProcAddr(instance_, "vkCreateDebugUtilsMessengerEXT"));
|
||||
|
||||
if (!func) {
|
||||
return MAKE_ERROR_INFO(error_code::vulkan_init_failed, "Could not load vkCreateDebugUtilsMessengerEXT");
|
||||
}
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT create_info{};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
create_info.pfnUserCallback = debug_callback;
|
||||
create_info.pUserData = this;
|
||||
|
||||
VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
|
||||
auto result = static_cast<vk::Result>(func(static_cast<VkInstance>(instance_), &create_info, nullptr,
|
||||
&messenger));
|
||||
if (result != vk::Result::eSuccess) {
|
||||
return MAKE_ERROR_INFO(error_code::vulkan_init_failed, "Failed to create debug messenger: {}", vk::to_string(result));
|
||||
}
|
||||
debug_messenger_ = messenger;
|
||||
|
||||
MIRAI_LOG_DEBUG("Vulkan debug messenger created");
|
||||
return {};
|
||||
}
|
||||
|
||||
void vulkan_instance::destroy_debug_messenger() {
|
||||
if (!debug_messenger_ || !instance_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto func = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
|
||||
vkGetInstanceProcAddr(instance_, "vkDestroyDebugUtilsMessengerEXT"));
|
||||
|
||||
if (func) {
|
||||
func(static_cast<VkInstance>(instance_), static_cast<VkDebugUtilsMessengerEXT>(debug_messenger_), nullptr);
|
||||
}
|
||||
debug_messenger_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
158
src/render/vulkan_instance.h
Normal file
158
src/render/vulkan_instance.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include "core/object.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace mirai {
|
||||
/**
|
||||
* @brief 调试消息严重级别
|
||||
*/
|
||||
enum class debug_severity : u8 {
|
||||
verbose = 0,
|
||||
info = 1,
|
||||
warning = 2,
|
||||
error = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 调试消息类型
|
||||
*/
|
||||
enum class debug_type : u8 {
|
||||
general = 0,
|
||||
validation = 1,
|
||||
performance = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 调试消息回调函数类型
|
||||
* @param severity 严重级别
|
||||
* @param type 消息类型
|
||||
* @param message 消息内容
|
||||
*/
|
||||
using debug_callback_fn = std::function<void(debug_severity severity,
|
||||
debug_type type,
|
||||
std::string_view message)>;
|
||||
|
||||
/**
|
||||
* @brief Vulkan 实例配置
|
||||
*/
|
||||
struct vulkan_instance_config {
|
||||
/// 应用程序名称
|
||||
std::string app_name = "MIRAI Application";
|
||||
|
||||
/// 应用程序版本
|
||||
u32 app_version = VK_MAKE_VERSION(1, 0, 0);
|
||||
|
||||
/// 引擎名称
|
||||
std::string engine_name = "MIRAI Engine";
|
||||
|
||||
/// 引擎版本
|
||||
u32 engine_version = VK_MAKE_VERSION(0, 1, 0);
|
||||
|
||||
/// 要求的 Vulkan API 版本
|
||||
u32 api_version = VK_API_VERSION_1_3;
|
||||
|
||||
/// 是否启用验证层
|
||||
bool enable_validation = MIRAI_DEBUG;
|
||||
|
||||
/// 额外的实例扩展
|
||||
std::vector<const char*> extra_extensions;
|
||||
|
||||
/// 额外的验证层
|
||||
std::vector<const char*> extra_layers;
|
||||
};
|
||||
|
||||
class vulkan_instance : public object {
|
||||
MIRAI_OBJECT_TYPE_INFO(vulkan_instance, object)
|
||||
|
||||
explicit vulkan_instance(const vulkan_instance_config& config = {});
|
||||
|
||||
|
||||
// ============================================================================================
|
||||
// 扩展和层查询
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取已启用的实例扩展列表
|
||||
* @return 扩展名称列表
|
||||
*/
|
||||
[[nodiscard]] const std::vector<const char*>& get_enabled_extensions() const noexcept {
|
||||
return enabled_extensions_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取已启用的验证层列表
|
||||
* @return 层名称列表
|
||||
*/
|
||||
[[nodiscard]] const std::vector<const char*>& get_enabled_layers() const noexcept {
|
||||
return enabled_layers_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定的实例扩展
|
||||
* @param extension_name 扩展名称
|
||||
* @return 如果支持返回 true
|
||||
*/
|
||||
[[nodiscard]] static bool is_extension_supported(const char* extension_name);
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定的验证层
|
||||
* @param layer_name 层名称
|
||||
* @return 如果支持返回 true
|
||||
*/
|
||||
[[nodiscard]] static bool is_layer_supported(const char* layer_name);
|
||||
|
||||
/**
|
||||
* @brief 获取所有可用的实例扩展
|
||||
* @return 扩展属性列表
|
||||
*/
|
||||
[[nodiscard]] static std::vector<vk::ExtensionProperties> get_available_extensions();
|
||||
|
||||
/**
|
||||
* @brief 获取所有可用的验证层
|
||||
* @return 层属性列表
|
||||
*/
|
||||
[[nodiscard]] static std::vector<vk::LayerProperties> get_available_layers();
|
||||
|
||||
/**
|
||||
* @brief 获取要求的 API 版本
|
||||
* @return API 版本
|
||||
*/
|
||||
[[nodiscard]] u32 get_api_version() const noexcept {
|
||||
return api_version_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取实例支持的最高 API 版本
|
||||
* @return 支持的 API 版本
|
||||
*/
|
||||
[[nodiscard]] static u32 get_supported_api_version();
|
||||
protected:
|
||||
void on_created() override;
|
||||
void on_destroying() override;
|
||||
private:
|
||||
void_result_t create_instance(const vulkan_instance_config& config);
|
||||
[[nodiscard]] auto get_required_extensions(const vulkan_instance_config& config) const -> std::vector<const char*>;
|
||||
|
||||
vk::Instance instance_;
|
||||
bool validation_enabled_{MIRAI_DEBUG};
|
||||
u32 api_version_{VK_API_VERSION_1_3};
|
||||
std::vector<const char*> enabled_extensions_;
|
||||
std::vector<const char*> enabled_layers_;
|
||||
vulkan_instance_config config_;
|
||||
|
||||
#if MIRAI_DEBUG
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||
void* user_data);
|
||||
void_result_t setup_debug_messenger();
|
||||
void destroy_debug_messenger();
|
||||
vk::DebugUtilsMessengerEXT debug_messenger_;
|
||||
debug_callback_fn user_debug_callback_;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
9
src/render/vulkan_types.h
Normal file
9
src/render/vulkan_types.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#if VK_HEADER_VERSION >= 304
|
||||
using vk_dynamic_loader = vk::detail::DynamicLoader;
|
||||
#else
|
||||
using vk_dynamic_loader = vk::DynamicLoader;
|
||||
#endif
|
||||
@@ -3,6 +3,9 @@
|
||||
#include <cstdint>
|
||||
#include <cwchar>
|
||||
#include <span>
|
||||
#include <chrono>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
@@ -23,3 +26,22 @@ using intptr_type = std::intptr_t;
|
||||
using byte_type = std::byte;
|
||||
using byte_span = std::span<byte_type>;
|
||||
using const_byte_span = std::span<const byte_type>;
|
||||
|
||||
using duration_ns = std::chrono::duration<f64, std::nano>;
|
||||
using duration_ms = std::chrono::duration<f64, std::milli>;
|
||||
using duration_s = std::chrono::duration<f64, std::ratio<1>>;
|
||||
using time_point = std::chrono::steady_clock::time_point;
|
||||
|
||||
using vec2 = Eigen::Vector2f;
|
||||
using vec3 = Eigen::Vector3f;
|
||||
using vec4 = Eigen::Vector4f;
|
||||
using vec2i = Eigen::Vector2i;
|
||||
using vec3i = Eigen::Vector3i;
|
||||
using vec4i = Eigen::Vector4i;
|
||||
using mat3 = Eigen::Matrix3f;
|
||||
using mat4 = Eigen::Matrix4f;
|
||||
using quat = Eigen::Quaternionf;
|
||||
using transform = Eigen::Transform<f32, 3, Eigen::Affine>;
|
||||
using rect2d = Eigen::AlignedBox2f;
|
||||
|
||||
inline bool g_quit_requested = false;
|
||||
|
||||
61
src/window/window.cpp
Normal file
61
src/window/window.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "window.h"
|
||||
|
||||
#include "core/logger.h"
|
||||
|
||||
namespace mirai {
|
||||
void_result_t window::make_window(window_config config) {
|
||||
|
||||
if (config.vulkan_window) {
|
||||
config.flags |= SDL_WINDOW_VULKAN;
|
||||
}
|
||||
else {
|
||||
config.flags &= ~SDL_WINDOW_VULKAN;
|
||||
}
|
||||
|
||||
auto win_ptr = SDL_CreateWindow(config.title.c_str(), config.size.x(), config.size.y(), config.flags);
|
||||
if (!win_ptr) {
|
||||
return MAKE_ERROR_INFO(error_code::window_creation_failed,
|
||||
"SDL_CreateWindow 失败: {}", SDL_GetError());
|
||||
}
|
||||
window_ = win_ptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
void window::update(duration_ms delta_time) {
|
||||
|
||||
}
|
||||
|
||||
void window::show_window(bool show) {
|
||||
if (show) {
|
||||
SDL_ShowWindow(window_);
|
||||
}
|
||||
else {
|
||||
SDL_HideWindow(window_);
|
||||
}
|
||||
}
|
||||
|
||||
void window::move_window(vec2i pos) {
|
||||
SDL_SetWindowPosition(window_, pos.x(), pos.y());
|
||||
}
|
||||
|
||||
vec2i window::get_pos() const {
|
||||
int x, y;
|
||||
if (!SDL_GetWindowPosition(window_, &x, &y)) {
|
||||
MIRAI_LOG_ERROR("SDL_GetWindowPosition failed: %s", SDL_GetError());
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
return vec2i{x, y};
|
||||
}
|
||||
|
||||
void window::on_created() {
|
||||
object::on_created();
|
||||
|
||||
}
|
||||
|
||||
void window::on_destroying() {
|
||||
object::on_destroying();
|
||||
SDL_DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
}
|
||||
44
src/window/window.h
Normal file
44
src/window/window.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "core/object.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "render/error.h"
|
||||
|
||||
namespace mirai {
|
||||
struct window_config {
|
||||
vec2i size;
|
||||
// 窗口位置, 空表示默认位置(SDL_WINDOWPOS_CENTERED)
|
||||
std::optional<vec2i> pos;
|
||||
std::string title;
|
||||
SDL_WindowFlags flags{};
|
||||
bool vulkan_window = true;
|
||||
bool visible_on_create = true;
|
||||
};
|
||||
|
||||
class window : public object {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(window, object)
|
||||
|
||||
window() = default;
|
||||
void_result_t make_window(window_config config);
|
||||
void update(duration_ms delta_time);
|
||||
|
||||
auto get_window_id() const {
|
||||
return SDL_GetWindowID(window_);
|
||||
}
|
||||
|
||||
void show_window(bool show);
|
||||
void move_window(vec2i pos);
|
||||
|
||||
vec2i get_pos() const;
|
||||
auto is_closing() const {
|
||||
return closing_;
|
||||
}
|
||||
protected:
|
||||
void on_created() override;
|
||||
void on_destroying() override;
|
||||
private:
|
||||
SDL_Window* window_{};
|
||||
bool closing_{false};
|
||||
};
|
||||
}
|
||||
88
src/window/window_manager.cpp
Normal file
88
src/window/window_manager.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "window_manager.h"
|
||||
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
#include "core/logger.h"
|
||||
|
||||
namespace mirai {
|
||||
window_manager::window_manager(const window_manager_config& config) {
|
||||
auto result = SDL_Init(SDL_INIT_VIDEO);
|
||||
if (!result) {
|
||||
MIRAI_LOG_ERROR("初始化SDL失败: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
// 检查是否已经加载了 Vulkan 库
|
||||
if (!SDL_Vulkan_LoadLibrary(nullptr)) {
|
||||
MIRAI_LOG_ERROR("加载 Vulkan 库失败: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
create_main_window(config.main_window);
|
||||
}
|
||||
|
||||
bool window_manager::quit() const {
|
||||
return main_window_->is_closing();
|
||||
}
|
||||
|
||||
auto window_manager::get_window_by_id(SDL_WindowID id) const -> std::weak_ptr<window> {
|
||||
const auto result = std::ranges::find_if(windows_, [&](const auto& w) {
|
||||
return w->get_window_id() == id;
|
||||
});
|
||||
if (result != windows_.end()) {
|
||||
return *result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
result_t<std::weak_ptr<window>> window_manager::create_window(window_config config) {
|
||||
auto win = make_obj<window>();
|
||||
auto result = win->make_window(config);
|
||||
if (!result) {
|
||||
return MAKE_ERROR_INFO(error_code::window_creation_failed, "创建窗口失败: {}", result.error().full_description());
|
||||
}
|
||||
windows_.push_back(win);
|
||||
|
||||
win->show_window(config.visible_on_create);
|
||||
win->move_window(config.pos.value_or(vec2i{SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED}));
|
||||
return win;
|
||||
}
|
||||
|
||||
void window_manager::destroy_window(std::shared_ptr<window> win) {
|
||||
std::erase(windows_, win);
|
||||
if (win == main_window_) {
|
||||
main_window_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void window_manager::update(duration_ms delta_time) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
g_quit_requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto w : windows_) {
|
||||
w->update(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
void window_manager::on_created() {
|
||||
object::on_created();
|
||||
|
||||
}
|
||||
|
||||
void window_manager::on_destroying() {
|
||||
object::on_destroying();
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void window_manager::create_main_window(const window_config& config) {
|
||||
auto result = create_window(config);
|
||||
if (!result) {
|
||||
MIRAI_LOG_ERROR("无法创建主窗口: {}", result.error().full_description());
|
||||
return;
|
||||
}
|
||||
main_window_ = result.value().lock();
|
||||
}
|
||||
}
|
||||
42
src/window/window_manager.h
Normal file
42
src/window/window_manager.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "core/object.h"
|
||||
#include "render/error.h"
|
||||
|
||||
namespace mirai {
|
||||
struct window_manager_config {
|
||||
window_config main_window;
|
||||
};
|
||||
|
||||
class window_manager : public object {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(window_manager, object)
|
||||
|
||||
window_manager(const window_manager_config& config = {});
|
||||
bool quit() const;
|
||||
|
||||
auto get_window_by_id(SDL_WindowID id) const -> std::weak_ptr<window>;
|
||||
result_t<std::weak_ptr<window>> create_window(window_config config);
|
||||
void destroy_window(std::shared_ptr<window> win);
|
||||
|
||||
auto get_main_window() const {
|
||||
return main_window_;
|
||||
}
|
||||
auto get_windows() const {
|
||||
return windows_;
|
||||
}
|
||||
|
||||
void update(duration_ms delta_time);
|
||||
protected:
|
||||
void on_created() override;
|
||||
void on_destroying() override;
|
||||
|
||||
void create_main_window(const window_config& config);
|
||||
private:
|
||||
std::vector<std::shared_ptr<window>> windows_;
|
||||
std::shared_ptr<window> main_window_;
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,8 @@
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "sdl3",
|
||||
"version>=": "3.2.28"
|
||||
"version>=": "3.2.28",
|
||||
"features": [ "vulkan" ]
|
||||
},
|
||||
{
|
||||
"name": "spirv-cross",
|
||||
|
||||
Reference in New Issue
Block a user