去他妈的Vulkan,这东西就不是人用的

This commit is contained in:
2026-01-18 20:08:16 +08:00
parent d45ae1d436
commit 7d3d474ab2
77 changed files with 434 additions and 5079 deletions

27
.gitmodules vendored
View File

@@ -1,3 +1,30 @@
[submodule "third_party/vma_hpp"]
path = third_party/vma_hpp
url = https://github.com/YaaZ/VulkanMemoryAllocator-Hpp.git
[submodule "third_party/yoga"]
path = third_party/yoga
url = https://github.com/facebook/yoga.git
[submodule "third_party/freetype"]
path = third_party/freetype
url = https://github.com/freetype/freetype.git
[submodule "third_party/SDL"]
path = third_party/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "third_party/json"]
path = third_party/json
url = https://github.com/nlohmann/json.git
[submodule "third_party/stb/stb"]
path = third_party/stb/stb
url = https://github.com/nothings/stb.git
[submodule "third_party/harfbuzz"]
path = third_party/harfbuzz
url = https://github.com/harfbuzz/harfbuzz.git
[submodule "third_party/eigen"]
path = third_party/eigen
url = https://gitlab.com/libeigen/eigen.git
[submodule "third_party/spdlog"]
path = third_party/spdlog
url = https://github.com/gabime/spdlog.git
[submodule "third_party/fmt"]
path = third_party/fmt
url = https://github.com/fmtlib/fmt.git

View File

@@ -1,14 +1,5 @@
cmake_minimum_required(VERSION 3.25)
# ================================================================================================
# vcpkg 工具链集成
# 必须在 project() 之前设置
# ================================================================================================
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain file")
endif()
project(mirai_project)
include(cmake/retrieve_files.cmake)
@@ -16,14 +7,35 @@ include(cmake/detect_os.cmake)
include(cmake/config_macos.cmake)
include(cmake/mirai_utils.cmake)
include(cmake/project_cpp_standard.cmake)
include(cmake/webgpu.cmake)
setup_project_options(
STANDARD 23
INTERFACE_TARGET mirai_project_options
)
set(SPDLOG_FMT_EXTERNAL OFF)
set(SPDLOG_FMT_EXTERNAL_HO ON)
add_subdirectory(third_party/vma_hpp/include)
add_subdirectory(third_party/vma_hpp/VulkanMemoryAllocator)
add_subdirectory(third_party/fmt)
add_subdirectory(third_party/eigen)
add_subdirectory(third_party/freetype)
add_subdirectory(third_party/harfbuzz)
add_subdirectory(third_party/json)
add_subdirectory(third_party/SDL)
add_subdirectory(third_party/spdlog)
add_subdirectory(third_party/stb)
add_subdirectory(third_party/yoga/yoga)
add_subdirectory(src)
add_subdirectory(example)
add_subdirectory(tests)
# add_subdirectory(tests)
# 检查编译器是否是 MSVC
if(MSVC)
# 为所有目标添加 /utf-8 编译选项
add_compile_options(/utf-8)
add_compile_options(/source-charset:utf-8)
endif()

View File

@@ -75,7 +75,6 @@ function(setup_project_options)
-Wall
-Wextra
-Wpedantic # 更加严格的警告
-Werror # 将所有警告视为错误 (可选,但推荐)
-Wno-unused-parameter
)
@@ -101,14 +100,16 @@ function(setup_project_options)
message(STATUS "为 GCC/Clang 添加特定编译选项")
endif()
# --- MinGW 特定设置 ---
if(MINGW)
# 为 C++17 及以上的 <filesystem> 支持添加链接库
if(${ARG_STANDARD} GREATER_EQUAL 17)
# 使用 target_link_libraries这才是正确的方式
target_link_libraries(${ARG_INTERFACE_TARGET} INTERFACE -lstdc++fs)
message(STATUS "为 MinGW C++${ARG_STANDARD} 添加 libstdc++fs 依赖 (用于 <filesystem>)")
endif()
# 检查编译器是否为 GCC (GNU Compiler Collection) 并且版本低于 9.0。
# 这是需要手动链接 stdc++fs 的主要情况。
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0")
# 如果是旧版 GCC则显式链接 stdc++fs
message(STATUS "检测到旧的 GCC 版本。链接 stdc++fs。")
target_link_libraries(my_app PRIVATE stdc++fs)
else()
# 对于所有其他情况 (GCC 9+, Clang, MSVC 等)
# filesystem 库已经集成在标准库中,无需额外链接。
message(STATUS "检测到现代编译器,无需链接 stdc++fs。")
endif()
# --- 将 INTERFACE 库的名称返回给调用者 ---

93
cmake/webgpu.cmake Normal file
View File

@@ -0,0 +1,93 @@
# This file is part of the "Learn WebGPU for C++" book.
# https://eliemichel.github.io/LearnWebGPU
#
# MIT License
# Copyright (c) 2022-2024 Elie Michel and the wgpu-native authors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
include(FetchContent)
set(WEBGPU_BACKEND "WGPU_STATIC" CACHE STRING "Backend implementation of WebGPU. Possible values are EMSCRIPTEN, WGPU, WGPU_STATIC and DAWN (it does not matter when using emcmake)")
# FetchContent's GIT_SHALLOW option is buggy and does not actually do a shallow
# clone. This macro takes care of it.
macro(FetchContent_DeclareShallowGit Name GIT_REPOSITORY GitRepository GIT_TAG GitTag)
FetchContent_Declare(
"${Name}"
# This is what it'd look line if GIT_SHALLOW was indeed working:
#GIT_REPOSITORY "${GitRepository}"
#GIT_TAG "${GitTag}"
#GIT_SHALLOW ON
# Manual download mode instead:
DOWNLOAD_COMMAND
cd "${FETCHCONTENT_BASE_DIR}/${Name}-src" &&
git init &&
git fetch --depth=1 "${GitRepository}" "${GitTag}" &&
git reset --hard FETCH_HEAD
)
endmacro()
if (NOT TARGET webgpu)
string(TOUPPER ${WEBGPU_BACKEND} WEBGPU_BACKEND_U)
if (EMSCRIPTEN OR WEBGPU_BACKEND_U STREQUAL "EMSCRIPTEN")
FetchContent_DeclareShallowGit(
webgpu-backend-emscripten
GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution
GIT_TAG e0038567ca1e35ffa9a848760eb6c49450264305 # emscripten-v3.1.61 + fix
)
FetchContent_MakeAvailable(webgpu-backend-emscripten)
elseif (WEBGPU_BACKEND_U STREQUAL "WGPU")
FetchContent_DeclareShallowGit(
webgpu-backend-wgpu
GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution
GIT_TAG ac055bef9d8c37f28e7706e4bd32153cbc0c210f # wgpu-v0.19.4.1 + fix
)
FetchContent_MakeAvailable(webgpu-backend-wgpu)
elseif (WEBGPU_BACKEND_U STREQUAL "WGPU_STATIC")
FetchContent_DeclareShallowGit(
webgpu-backend-wgpu-static
GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution
GIT_TAG 3612311d4df171027513a3ad5dcd8a77d93c447f # wgpu-static-v0.19.4.1 + fix
)
FetchContent_MakeAvailable(webgpu-backend-wgpu-static)
elseif (WEBGPU_BACKEND_U STREQUAL "DAWN")
FetchContent_DeclareShallowGit(
webgpu-backend-dawn
GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution
GIT_TAG b3082d247df326c2e2dd1c64d5a148bc3cf1167f # dawn-6512 + emscripten-v3.1.61 + fix
)
FetchContent_MakeAvailable(webgpu-backend-dawn)
else()
message(FATAL_ERROR "Invalid value for WEBGPU_BACKEND: possible values are EMSCRIPTEN, WGPU, WGPU_STATIC and DAWN, but '${WEBGPU_BACKEND_U}' was provided.")
endif()
endif()

View File

@@ -1,6 +1,5 @@
#include "app/mirai_app.h"
#include "core/object.h"
#include "render/vulkan_instance.h"
#include "window/window_manager.h"
using namespace mirai;

View File

@@ -1,28 +1,36 @@
project(mirai)
find_package(Vulkan REQUIRED)
find_package(SDL3 CONFIG REQUIRED)
find_package(harfbuzz CONFIG REQUIRED)
find_package(Freetype CONFIG REQUIRED)
find_package(yoga CONFIG REQUIRED)
find_package(Eigen3 CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(Stb REQUIRED)
simple_library(STATIC)
target_link_libraries(${PROJECT_NAME} PUBLIC
Vulkan::Vulkan
SDL3::SDL3
Freetype::Freetype
harfbuzz::harfbuzz
yoga::yogacore
Eigen3::Eigen
spdlog::spdlog
fmt::fmt
freetype
harfbuzz
yogacore
eigen
spdlog
VulkanMemoryAllocator
VulkanMemoryAllocator-Hpp
stb
fmt::fmt-header-only
webgpu
)
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS)
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS VMA_NOT_NULL= VMA_NULLABLE=)
target_copy_webgpu_binaries(${PROJECT_NAME})
if (WIN32)
target_link_libraries(${PROJECT_NAME} PUBLIC
opengl32
ntdll
bcrypt
d3dcompiler # 用于 D3DCompile
ws2_32 # 用于 Windows Sockets (网络)
userenv # 用于 GetUserProfileDirectoryW
ole32 # Windows OLE API, wgpu 可能间接依赖
oleaut32 # Windows OLE Automation API
)
endif ()

View File

@@ -2,33 +2,46 @@
#include "core/logger.h"
#include "core/time_util.h"
#include "gpu_resource/gpu_buffer.h"
#include "render/webgpu-raii.hpp"
namespace mirai {
void test_func() {
buffer_create_info info;
info.debug_name = "测试Buffer";
info.device = vulkan_context::get().get_default_device();
info.size = 64;
info.usage = buffer_usage::vertex;
auto buf = make_obj<gpu_buffer>(info);
std::array<uint8_t, 64> test_data{};
buf->upload(nullptr, std::span(test_data));
}
bool mirai_app::setup(const mirai_app_config& config) {
// We create a descriptor
wgpu::InstanceDescriptor desc{};
// We create the instance using this descriptor
wgpu::raii::Instance instance = wgpu::createInstance(desc);
wgpu::raii::Device device;
{
wgpu::RequestAdapterOptions options{};
options.powerPreference = wgpu::PowerPreference::HighPerformance;
wgpu::raii::Adapter adapter = instance->requestAdapter(options);
wgpu::DeviceDescriptor device_desc{};
device_desc.label = "Test Device";
device = adapter->requestDevice(device_desc);
}
wgpu::BufferDescriptor buffer_desc{};
buffer_desc.label = "Test Buffer";
buffer_desc.size = 64;
buffer_desc.usage = wgpu::BufferUsage::Uniform;
buffer_desc.mappedAtCreation = false;
wgpu::raii::Buffer test_buffer = device->createBuffer(buffer_desc);
test_buffer->mapAsync(wgpu::MapMode::Write, 0, buffer_desc.size, [&](wgpu::BufferMapAsyncStatus status) {
if (status != wgpu::BufferMapAsyncStatus::Success) {
MIRAI_LOG_ERROR("Failed to map buffer asynchronously");
return;
}
auto buf = test_buffer->getMappedRange(0, buffer_desc.size);
memset(buf, 0, buffer_desc.size);
test_buffer->unmap();
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
window_mgr_ = make_obj<window_manager>();
vulkan_context_init_config context_init_config{config.vulkan_instance_cfg};
vulkan_context::get().init(context_init_config);
window_mgr_->setup(config.window_mgr_config);
vulkan_context_config context_config{window_mgr_->get_main_window()->get_vk_surface()};
vulkan_context::get().setup(context_config);
test_func();
return true;
}

View File

@@ -1,13 +1,11 @@
#pragma once
#include <memory>
#include "render/vulkan_context.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 {
@@ -16,7 +14,6 @@ namespace mirai {
void shutdown() {
window_mgr_.reset();
vulkan_context::get().shutdown();
}
int run();

View File

@@ -14,7 +14,7 @@ inline constexpr bool is_flag_enum_v = is_flag_enum<E>::value;
template <typename E>
concept flag_enum = std::is_enum_v<E> && is_flag_enum_v<E>;
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr E operator|(E lhs, E rhs) noexcept {
using underlying = std::underlying_type_t<E>;
return static_cast<E>(
@@ -22,7 +22,7 @@ template <typename E>
);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr E operator&(E lhs, E rhs) noexcept {
using underlying = std::underlying_type_t<E>;
return static_cast<E>(
@@ -30,7 +30,7 @@ template <typename E>
);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr E operator^(E lhs, E rhs) noexcept {
using underlying = std::underlying_type_t<E>;
return static_cast<E>(
@@ -38,7 +38,7 @@ template <typename E>
);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr E operator~(E value) noexcept {
using underlying = std::underlying_type_t<E>;
return static_cast<E>(
@@ -46,53 +46,53 @@ template <typename E>
);
}
template <typename E>
template <flag_enum E>
constexpr E& operator|=(E& lhs, E rhs) noexcept {
lhs = lhs | rhs;
return lhs;
}
template <typename E>
template <flag_enum E>
constexpr E& operator&=(E& lhs, E rhs) noexcept {
lhs = lhs & rhs;
return lhs;
}
template <typename E>
template <flag_enum E>
constexpr E& operator^=(E& lhs, E rhs) noexcept {
lhs = lhs ^ rhs;
return lhs;
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr bool has_flag(E flags, E check) noexcept {
using underlying = std::underlying_type_t<E>;
return (static_cast<underlying>(flags) & static_cast<underlying>(check))
== static_cast<underlying>(check);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr bool has_any_flag(E flags, E check) noexcept {
using underlying = std::underlying_type_t<E>;
return (static_cast<underlying>(flags) & static_cast<underlying>(check)) != underlying{0};
}
template <typename E>
template <flag_enum E>
constexpr void set_flag(E& flags, E to_set) noexcept {
flags |= to_set;
}
template <typename E>
template <flag_enum E>
constexpr void clear_flag(E& flags, E to_clear) noexcept {
flags &= ~to_clear;
}
template <typename E>
template <flag_enum E>
constexpr void toggle_flag(E& flags, E to_toggle) noexcept {
flags ^= to_toggle;
}
template <typename E>
template <flag_enum E>
constexpr bool set_flag_if(E& flags, E flag, bool condition) noexcept {
if (condition) {
set_flag(flags, flag);
@@ -102,26 +102,26 @@ constexpr bool set_flag_if(E& flags, E flag, bool condition) noexcept {
return false;
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr bool is_empty(E flags) noexcept {
using underlying = std::underlying_type_t<E>;
return (static_cast<underlying>(flags) == underlying{0});
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr auto to_underlying(E flags) noexcept {
return static_cast<std::underlying_type_t<E>>(flags);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr auto from_underlying(std::underlying_type_t<E> value) noexcept {
return static_cast<E>(value);
}
template <typename E>
template <flag_enum E>
[[nodiscard]] constexpr auto count_flags(E flags) noexcept {
using underlying = std::underlying_type_t<E>;
underlying value = static_cast<underlying>(flags);
auto value = static_cast<underlying>(flags);
int count = 0;
while (value) {
count += (value & 1);

View File

@@ -77,36 +77,6 @@ namespace mirai {
[[nodiscard]] bool should_log(log_level level) const noexcept;
void flush();
template <typename... Args>
void trace(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::trace, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void debug(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::debug, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void info(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::info, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::warn, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::error, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void critical(fmt::format_string<Args...> fmt, Args&&... args) {
log(log_level::critical, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void log(log_level level, fmt::format_string<Args...> fmt, Args&&... args) {
if (should_log(level)) {
@@ -183,169 +153,62 @@ namespace mirai {
logger->flush();
}
}
namespace detail {
/**
* @brief Concept 检查类型 T 是否有 get_debug_name() const -> std::string_view
*/
template<typename T>
concept has_get_debug_name = requires(const std::decay_t<T>& t) {
{ t.get_debug_name() } -> std::convertible_to<std::string_view>;
};
/**
* @brief 记录 trace 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
* @brief 实际执行日志记录的底层函数
*/
template<typename... Args>
void log_trace(fmt::format_string<Args...> fmt, Args&&... args) {
void do_log(log_level level, fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->trace(fmt, std::forward<Args>(args)...);
if (logger->should_log(level)) {
logger->log(level, fmt, std::forward<Args>(args)...);
}
}
}
/**
* @brief 记录 debug 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_debug(fmt::format_string<Args...> fmt, Args&&... args) {
void do_log_with_prefix(log_level level, std::string_view prefix, fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->debug(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 info 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_info(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->info(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 warn 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_warn(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->warn(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 error 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_error(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->error(fmt, std::forward<Args>(args)...);
}
}
/**
* @brief 记录 critical 级别日志
* @tparam Args 参数类型
* @param fmt 格式字符串
* @param args 格式参数
*/
template<typename... Args>
void log_critical(fmt::format_string<Args...> fmt, Args&&... args) {
if (auto logger = get_logger()) {
logger->critical(fmt, std::forward<Args>(args)...);
if (logger->should_log(level)) {
auto user_message = fmt::format(fmt, std::forward<Args>(args)...);
logger->log(level, "[{}] {}", prefix, user_message);
}
}
}
} // namespace detail
}
/**
* @def MIRAI_LOG_TRACE(...)
* @brief 记录 trace 级别日志
* @brief 核心日志宏实现
* @note __VA_ARGS__ 包含了格式化字符串和所有参数
*/
#define MIRAI_LOG_TRACE(...) ::mirai::log_trace(__VA_ARGS__)
#define MIRAI_LOG_IMPL(level, ...) \
{ \
::mirai::detail::do_log(level, __VA_ARGS__); \
}
/**
* @def MIRAI_LOG_DEBUG(...)
* @brief 记录 debug 级别日志
*/
#define MIRAI_LOG_DEBUG(...) ::mirai::log_debug(__VA_ARGS__)
/**
* @def MIRAI_LOG_INFO(...)
* @brief 记录 info 级别日志
*/
#define MIRAI_LOG_INFO(...) ::mirai::log_info(__VA_ARGS__)
/**
* @def MIRAI_LOG_WARN(...)
* @brief 记录 warn 级别日志
*/
#define MIRAI_LOG_WARN(...) ::mirai::log_warn(__VA_ARGS__)
/**
* @def MIRAI_LOG_ERROR(...)
* @brief 记录 error 级别日志
*/
#define MIRAI_LOG_ERROR(...) ::mirai::log_error(__VA_ARGS__)
/**
* @def MIRAI_LOG_CRITICAL(...)
* @brief 记录 critical 级别日志
*/
#define MIRAI_LOG_CRITICAL(...) ::mirai::log_critical(__VA_ARGS__)
/**
* @def MIRAI_LOG(level, ...)
* @brief 记录指定级别的日志
*/
#define MIRAI_LOG(level, ...) \
do { \
if (auto _logger = ::mirai::get_logger()) { \
_logger->log(level, __VA_ARGS__); \
} \
} while (false)
/**
* @def MIRAI_LOG_IF(condition, level, ...)
* @brief 条件日志记录
*/
#define MIRAI_LOG_IF(condition, level, ...) \
do { \
if (condition) { \
MIRAI_LOG(level, __VA_ARGS__); \
} \
} while (false)
#define MIRAI_LOG_WARN(...) MIRAI_LOG_IMPL(::mirai::log_level::warn, __VA_ARGS__)
#define MIRAI_LOG_ERROR(...) MIRAI_LOG_IMPL(::mirai::log_level::error, __VA_ARGS__)
#define MIRAI_LOG_CRITICAL(...) MIRAI_LOG_IMPL(::mirai::log_level::critical, __VA_ARGS__)
#define MIRAI_LOG_INFO(...) MIRAI_LOG_IMPL(::mirai::log_level::info, __VA_ARGS__)
// Debug 模式专用日志宏
#if MIRAI_DEBUG
/**
* @def MIRAI_DLOG_TRACE(...)
* @brief Debug 模式 trace 日志
*/
#define MIRAI_DLOG_TRACE(...) MIRAI_LOG_TRACE(__VA_ARGS__)
/**
* @def MIRAI_DLOG_DEBUG(...)
* @brief Debug 模式 debug 日志
*/
#define MIRAI_DLOG_DEBUG(...) MIRAI_LOG_DEBUG(__VA_ARGS__)
/**
* @def MIRAI_DLOG_INFO(...)
* @brief Debug 模式 info 日志
*/
#define MIRAI_DLOG_INFO(...) MIRAI_LOG_INFO(__VA_ARGS__)
#define MIRAI_LOG_TRACE(...) MIRAI_LOG_IMPL(::mirai::log_level::trace, __VA_ARGS__)
#define MIRAI_LOG_DEBUG(...) MIRAI_LOG_IMPL(::mirai::log_level::debug, __VA_ARGS__)
#else
#define MIRAI_DLOG_TRACE(...) ((void)0)
#define MIRAI_DLOG_DEBUG(...) ((void)0)
#define MIRAI_DLOG_INFO(...) ((void)0)
#define MIRAI_LOG_TRACE(...) ((void)0)
#define MIRAI_LOG_DEBUG(...) ((void)0)
#endif

View File

@@ -146,7 +146,7 @@ namespace mirai {
debug_name_ = std::move(name);
}
[[nodiscard]] const std::string& get_debug_name() const noexcept {
[[nodiscard]] std::string_view get_debug_name() const noexcept {
return debug_name_;
}
private:

View File

@@ -22,7 +22,6 @@ enum class property_flags : u32 {
required = 1 << 5,
deprecated = 1 << 6,
};
MIRAI_FLAG_ENUM(property_flags)
struct property_change_event {

View File

@@ -4,6 +4,8 @@
#include <windows.h>
namespace mirai {
void set_thread_name(std::jthread& t, const std::string& name) {
// 将std::string转换为std::wstring

View File

@@ -1,126 +0,0 @@
#pragma once
#include "types/types.h"
#include "gpu_resource/resource_types.h"
#include <vulkan/vulkan.hpp>
#include <vk_mem_alloc.hpp>
namespace mirai {
class vulkan_device;
class vulkan_queue;
/**
* @brief GPU 分配信息
*
* 包含单个内存分配的详细信息
*/
struct allocation_info {
/// 分配的大小(字节)
u64 size = 0;
/// 分配的偏移量
u64 offset = 0;
/// 设备内存句柄
vk::DeviceMemory device_memory{};
/// 映射的指针(如果已映射)
void* mapped_data = nullptr;
/// 是否为直接访问内存
bool is_direct_access = false;
/// 内存类型索引
u32 memory_type_index = 0;
};
/**
* @brief Buffer 创建信息
*/
struct buffer_create_info {
/// Buffer 大小(字节)
u64 size = 0;
/// Buffer 用途
buffer_usage usage = buffer_usage::vertex;
/// 资源共享模式
resource_sharing_mode sharing_mode = resource_sharing_mode::exclusive;
/// 设备
std::shared_ptr<vulkan_device> device{};
#if MIRAI_DEBUG
/// 调试名称
std::string debug_name;
#endif
};
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 分配句柄
vma::Allocation allocation = VK_NULL_HANDLE;
/// 分配信息
allocation_info info;
#if MIRAI_DEBUG
std::string debug_name;
#endif
/// 是否有效
[[nodiscard]] bool is_valid() const noexcept {
return buffer && allocation;
}
};
/**
* @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 分配句柄
vma::Allocation allocation = VK_NULL_HANDLE;
/// 分配信息
allocation_info info;
/// 是否有效
[[nodiscard]] bool is_valid() const noexcept {
return image && allocation;
}
};
}

View File

@@ -1,199 +0,0 @@
#include "allocator.h"
#include "resource_types_vulkan.h"
#include "core/logger.h"
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
#include <vk_mem_alloc.hpp>
namespace mirai {
result_t<vma::Allocator> create_vma_allocator(const allocator_config& config) {
// 假设你已经有了 instance, physicalDevice, device 和 dl (调度器)
auto& d = config.disp;
// 1. 填充 VMA 需要的函数指针结构体
vma::VulkanFunctions functions{};
functions.vkGetInstanceProcAddr = d.vkGetInstanceProcAddr;
functions.vkGetDeviceProcAddr = d.vkGetDeviceProcAddr;
// 还可以填充其他具体函数,但在 Dynamic 模式下,主要给这两个入口,
// VMA 会尝试自己获取,或者你需要把 d 里面的函数逐个填进去(视 VMA 版本而定)
// 比较稳妥的做法是把 VMA 需要的核心函数都填上:
functions.vkGetPhysicalDeviceProperties = d.vkGetPhysicalDeviceProperties;
functions.vkGetPhysicalDeviceMemoryProperties = d.vkGetPhysicalDeviceMemoryProperties;
functions.vkAllocateMemory = d.vkAllocateMemory;
functions.vkFreeMemory = d.vkFreeMemory;
functions.vkMapMemory = d.vkMapMemory;
functions.vkUnmapMemory = d.vkUnmapMemory;
functions.vkFlushMappedMemoryRanges = d.vkFlushMappedMemoryRanges;
functions.vkInvalidateMappedMemoryRanges = d.vkInvalidateMappedMemoryRanges;
functions.vkBindBufferMemory = d.vkBindBufferMemory;
functions.vkBindImageMemory = d.vkBindImageMemory;
functions.vkGetBufferMemoryRequirements = d.vkGetBufferMemoryRequirements;
functions.vkGetImageMemoryRequirements = d.vkGetImageMemoryRequirements;
functions.vkCreateBuffer = d.vkCreateBuffer;
functions.vkDestroyBuffer = d.vkDestroyBuffer;
functions.vkCreateImage = d.vkCreateImage;
functions.vkDestroyImage = d.vkDestroyImage;
functions.vkCmdCopyBuffer = d.vkCmdCopyBuffer;
// 如果开启了 bufferDeviceAddress还需要相关的函数...
// 2. 创建 Allocator
vma::AllocatorCreateInfo allocator_info{};
allocator_info.vulkanApiVersion = VK_API_VERSION_1_3;
allocator_info.physicalDevice = config.physical_device;
allocator_info.device = config.device;
allocator_info.instance = config.instance;
allocator_info.pVulkanFunctions = &functions; // <--- 关键:告诉 VMA 用动态加载的函数
allocator_info.preferredLargeHeapBlockSize = config.preferred_large_heap_block_size;
// allocatorInfo.flags = vma::AllocatorCreateFlagBits::eBufferDeviceAddress; // 如果你要用 BDA
// 启用功能标志
vma::AllocatorCreateFlags flags;
if (config.enable_buffer_device_address) {
flags |= vma::AllocatorCreateFlagBits::eBufferDeviceAddress;
}
if (config.enable_memory_budget) {
flags |= vma::AllocatorCreateFlagBits::eExtMemoryBudget;
}
allocator_info.flags = flags;
// 创建 RAII 风格的 Allocator
auto [result, allocator] = vma::createAllocator(allocator_info);
if (result != vk::Result::eSuccess) {
return MAKE_ERROR_INFO(error_code::vulkan_init_failed, "VMA分配器创建失败: {}", to_string(result));
}
return allocator;
}
void vma_allocator::setup(const allocator_config& config) {
instance_ = config.instance;
physical_device_ = config.physical_device;
device_ = config.device;
enable_buffer_device_address_ = config.enable_buffer_device_address;
auto result = create_vma_allocator(config);
if (!result) {
MIRAI_LOG_ERROR("VMA 分配器初始化失败: {}", result.error().full_description());
throw std::runtime_error("VMA 分配器初始化失败");
}
vma_allocator_ = result.value();
}
result_t<buffer_allocation> vma_allocator::alloc_buffer(const buffer_create_info& info) {
buffer_allocation alloc;
#if MIRAI_DEBUG
alloc.debug_name = info.debug_name;
#endif
vk::BufferCreateInfo buffer_info{};
buffer_info.size = info.size;
buffer_info.usage = to_vulkan_buffer_usage(info.usage);
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;
}
vma::AllocationCreateInfo alloc_info{};
alloc_info.usage = vma::MemoryUsage::eAuto;
alloc_info.flags = vma::AllocationCreateFlagBits::eHostAccessSequentialWrite;
alloc_info.flags |= vma::AllocationCreateFlagBits::eMapped;
alloc_info.preferredFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
{
std::lock_guard lock(mutex_);
auto [result, pair] = vma_allocator_.createBuffer(buffer_info, alloc_info);
if (result != vk::Result::eSuccess) {
return MAKE_ERROR_INFO(error_code::vulkan_allocation_failed,
"VMA 分配 Buffer 失败: {}",
vk::to_string(result));
}
const auto& [allocation, buffer] = pair;
alloc.buffer = buffer;
alloc.allocation = allocation;
}
auto res_info = vma_allocator_.getAllocationInfo(alloc.allocation);
auto mem_flags = vma_allocator_.getAllocationMemoryProperties(alloc.allocation);
auto is_direct_access = static_cast<bool>(mem_flags & vk::MemoryPropertyFlagBits::eDeviceLocal);
// 获取映射好的指针(因为加了 eMapped 标志)
const auto mapped_ptr = vma_allocator_.getAllocationInfo(alloc.allocation).pMappedData;
alloc.info.size = res_info.size;
alloc.info.offset = res_info.offset;
alloc.info.device_memory = res_info.deviceMemory;
alloc.info.mapped_data = mapped_ptr;
alloc.info.is_direct_access = is_direct_access;
alloc.info.memory_type_index = res_info.memoryType;
return alloc;
}
result_t<buffer_allocation> vma_allocator::alloc_staging_buffer(const buffer_create_info& info) {
buffer_allocation alloc;
vma::AllocationCreateInfo alloc_info{};
alloc_info.usage = vma::MemoryUsage::eAuto;
alloc_info.flags = vma::AllocationCreateFlagBits::eHostAccessSequentialWrite;
alloc_info.flags |= vma::AllocationCreateFlagBits::eMapped;
alloc_info.preferredFlags = vk::MemoryPropertyFlagBits::eHostVisible;
vk::BufferCreateInfo buffer_create_info{};
buffer_create_info.setSize(info.size);
buffer_create_info.setUsage(vk::BufferUsageFlagBits::eTransferSrc);
{
auto [result, pair] = vma_allocator_.createBuffer(buffer_create_info, alloc_info);
if (result != vk::Result::eSuccess) {
return MAKE_ERROR_INFO(error_code::vulkan_allocation_failed,
"VMA 分配暂存 Buffer 失败: {}",
vk::to_string(result));
}
const auto& [allocation, buffer] = pair;
alloc.buffer = buffer;
alloc.allocation = allocation;
}
auto res_info = vma_allocator_.getAllocationInfo(alloc.allocation);
auto mem_flags = vma_allocator_.getAllocationMemoryProperties(alloc.allocation);
auto is_direct_access = static_cast<bool>(mem_flags & vk::MemoryPropertyFlagBits::eHostVisible);
// 获取映射好的指针(因为加了 eMapped 标志)
const auto mapped_ptr = vma_allocator_.getAllocationInfo(alloc.allocation).pMappedData;
alloc.info.size = res_info.size;
alloc.info.offset = res_info.offset;
alloc.info.device_memory = res_info.deviceMemory;
alloc.info.mapped_data = mapped_ptr;
alloc.info.is_direct_access = is_direct_access;
alloc.info.memory_type_index = res_info.memoryType;
return alloc;
}
void_result_t vma_allocator::free_buffer(const buffer_allocation& buffer) {
{
std::lock_guard lock(mutex_);
vma_allocator_.destroyBuffer(buffer.buffer, buffer.allocation);
}
return {};
}
void_result_t vma_allocator::free_staging_buffer(const buffer_allocation& buffer) {
{
std::lock_guard lock(mutex_);
vma_allocator_.destroyBuffer(buffer.buffer, buffer.allocation);
}
return {};
}
void vma_allocator::on_destroying() {
object::on_destroying();
MIRAI_LOG_INFO("VMA 分配器正在销毁");
if (vma_allocator_) {
vma_allocator_.destroy();
vma_allocator_ = nullptr;
}
}
}

View File

@@ -1,62 +0,0 @@
#pragma once
#include <mutex>
#include "types/types.h"
#include "resource_types.h"
#include "core/object.h"
#include <vulkan/vulkan.hpp>
#include "allocation_types.h"
#include "render/vulkan_types.h"
#include "types/error.h"
namespace mirai {
/**
* @brief GPU 分配器配置
*/
struct allocator_config {
/// Vulkan 实例
vk::Instance instance{};
/// 物理设备
vk::PhysicalDevice physical_device{};
/// 逻辑设备
vk::Device device{};
/// Vulkan API 版本
u32 vulkan_api_version = VK_API_VERSION_1_3;
/// 是否启用 Buffer Device Address
bool enable_buffer_device_address = true;
/// 是否启用内存预算跟踪
bool enable_memory_budget = true;
/// 首选的大块分配大小(字节)
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
vk_dispatch_loader& disp;
};
class vma_allocator : public object {
MIRAI_OBJECT_TYPE_INFO(vma_allocator, object)
explicit vma_allocator(const allocator_config& config) {
setup(config);
}
void setup(const allocator_config& config);
result_t<buffer_allocation> alloc_buffer(const buffer_create_info& info);
result_t<buffer_allocation> alloc_staging_buffer(const buffer_create_info& info);
void_result_t free_buffer(const buffer_allocation& buffer);
void_result_t free_staging_buffer(const buffer_allocation& buffer);
protected:
void on_destroying() override;
private:
vk::Instance instance_{};
vk::PhysicalDevice physical_device_;
vk::Device device_;
vma::Allocator vma_allocator_;
vma::DefragmentationContext defrag_context_;
bool enable_buffer_device_address_ = true;
std::atomic<u64> total_allocated_bytes_{0};
std::atomic<u64> allocation_count_{0};
mutable std::mutex mutex_;
};
}

View File

@@ -1,85 +0,0 @@
#include "gpu_buffer.h"
#include "allocator.h"
#include "core/logger.h"
#include "render/vulkan_command_buffer.h"
#include "render/vulkan_device.h"
#include "resource_types_to_string.h"
namespace mirai {
gpu_buffer::gpu_buffer(const buffer_create_info& info) {
device_ = info.device;
auto allocator = device_->get_allocator();
auto buffer = allocator->alloc_buffer(info);
if (!buffer) {
MIRAI_LOG_ERROR("分配 GPU Buffer 失败: {}", buffer.error().full_description());
return;
}
allocation_ = buffer.value();
#if MIRAI_DEBUG
MIRAI_LOG_DEBUG("创建 GPU Buffer {}: 大小={} 字节, 用途={}, 共享模式={}",
info.debug_name,
allocation_.info.size,
to_string(info.usage),
to_string(info.sharing_mode)
);
#endif
}
gpu_buffer::~gpu_buffer() {
if (allocation_.is_valid()) {
auto allocator = device_->get_allocator();
auto result = allocator->free_buffer(allocation_);
if (!result) {
MIRAI_LOG_ERROR("释放 GPU Buffer 失败: {}", result.error().full_description());
}
#if MIRAI_DEBUG
MIRAI_LOG_DEBUG("销毁 GPU Buffer {},大小={} 字节", allocation_.debug_name, allocation_.info.size);
#endif
}
}
void gpu_buffer::upload(std::shared_ptr<vulkan_command_buffer> cmd, std::span<uint8_t> data) {
// 如果开启了Resizable Bar则可以直接访问显存
if (allocation_.info.is_direct_access) {
auto mapped_ptr = static_cast<uint8_t*>(allocation_.info.mapped_data);
memcpy(mapped_ptr, data.data(), std::min<u64>(allocation_.info.size, data.size()));
} else {
// 使用暂存缓冲区上传数据
auto staging_buffer_info = buffer_create_info{};
staging_buffer_info.size = allocation_.info.size;
staging_buffer_info.usage = buffer_usage::transfer_src;
staging_buffer_info.sharing_mode = resource_sharing_mode::concurrent;
staging_buffer_info.device = device_;
auto allocator = device_->get_allocator();
auto staging_buffer_alloc = allocator->alloc_staging_buffer(staging_buffer_info);
if (!staging_buffer_alloc) {
MIRAI_LOG_ERROR("分配暂存 Buffer 失败: {}", staging_buffer_alloc.error().full_description());
return;
}
const auto& staging_buffer = staging_buffer_alloc.value();
// 拷贝数据到暂存缓冲区
auto mapped_ptr = static_cast<uint8_t*>(staging_buffer.info.mapped_data);
memcpy(mapped_ptr, data.data(), std::min<u64>(staging_buffer.info.size, data.size()));
// 记录命令将数据从暂存缓冲区复制到目标缓冲区
cmd->copy_buffer(staging_buffer.buffer, allocation_.buffer, allocation_.info.size);
vulkan_cmd_buffer_cleanup_task cleanup_task{};
#if MIRAI_DEBUG
cleanup_task.debug_name = "GPU Buffer Upload任务释放Staging Buffer ";
#endif
cleanup_task.task = [allocator, staging_buffer] {
// 释放暂存缓冲区
auto free_result = allocator->free_staging_buffer(staging_buffer);
if (!free_result) {
MIRAI_LOG_ERROR("释放暂存 Buffer 失败: {}", free_result.error().full_description());
}
};
cmd->add_cleanup_task(cleanup_task);
}
}
}

View File

@@ -1,24 +0,0 @@
#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 gpu_resource {
MIRAI_OBJECT_TYPE_INFO(gpu_buffer, gpu_resource)
gpu_buffer(const buffer_create_info& info);
~gpu_buffer() override;
void upload(std::shared_ptr<vulkan_command_buffer> cmd, std::span<uint8_t> data) override;
[[nodiscard]] auto get_allocation_info() const noexcept { return allocation_.info; }
[[nodiscard]] auto get_buffer() const noexcept { return allocation_.buffer; }
[[nodiscard]] auto get_size() const noexcept { return allocation_.info.size; }
private:
buffer_allocation allocation_;
std::shared_ptr<vulkan_device> device_;
};
}

View File

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

View File

@@ -1,37 +0,0 @@
#pragma once
#include "resource_manager.h"
#include "resource_types.h"
#include "core/object.h"
#include <future>
namespace mirai {
class vulkan_command_buffer;
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(const std::shared_ptr<vulkan_queue>& queue) : sharing_mode_(resource_sharing_mode::exclusive),
queue_(queue) {
}
virtual void upload(std::shared_ptr<vulkan_command_buffer> cmd, std::span<uint8_t> data) = 0;
[[nodiscard]] auto get_sharing_mode() const noexcept { return sharing_mode_; }
[[nodiscard]] auto get_queue() const noexcept { return queue_.lock(); }
[[nodiscard]] auto get_current_stages() const noexcept { return current_stages_; }
[[nodiscard]] auto get_current_access() const noexcept { return current_access_; }
protected:
/// 资源共享模式
resource_sharing_mode sharing_mode_ = resource_sharing_mode::exclusive;
/// 当前所处队列当exclusive共享时有效
std::weak_ptr<vulkan_queue> queue_;
vk::PipelineStageFlags2 current_stages_{};
vk::AccessFlags2 current_access_{};
};
}

View File

@@ -1,125 +0,0 @@
#include "image_block.h"
#include "resource_types_vulkan.h"
#include "core/logger.h"
#include "render/vulkan_context.h"
namespace mirai {
image_block::image_block(image_block_config config) {
if (config.outer_image) {
is_owner_ = false;
image_ = config.outer_image;
} else {
is_owner_ = true;
image_ = nullptr;
}
image_count_ = config.image_count;
size_ = config.size;
texture_type_ = config.texture_type;
texture_format_ = config.texture_format;
// 参数校验
#if MIRAI_DEBUG
if (texture_format_ == texture_format::undefined) {
MIRAI_LOG_ERROR("创建图像块时使用了未定义的纹理格式。");
}
if (size_.x() <= 0 || size_.y() <= 0) {
MIRAI_LOG_ERROR("创建图像块时使用了无效的尺寸: ({}, {})。", size_.x(), size_.y());
}
if (image_count_ == 0) {
MIRAI_LOG_ERROR("创建图像块时使用了无效的图像数量: 0。");
}
if (!is_owner_ && !image_) {
MIRAI_LOG_ERROR("创建图像块时未提供有效的外部图像。");
}
if (texture_type_ == texture_type::texture_cube || texture_type_ == texture_type::texture_cube_array) {
if (image_count_ % 6 != 0) {
MIRAI_LOG_ERROR("立方体纹理图像块的图像数量必须是6的倍数。");
}
}
bool is_array = (texture_type_ == texture_type::texture_1d_array ||
texture_type_ == texture_type::texture_2d_array ||
texture_type_ == texture_type::texture_cube_array);
if (is_array && image_count_ > 0) {
MIRAI_LOG_ERROR("纹理数组图像块的图像数量必须大于0。");
} else {
if (image_count_ == 1) {
MIRAI_LOG_ERROR("非数组纹理图像块的图像数量必须等于1。");
}
}
#endif
}
bool image_block::rebuild_image(void* data) {
if (!is_owner_) {
MIRAI_LOG_ERROR("尝试重建不拥有的图像块的图像。");
return false;
}
if (!destroy_image())
return false;
auto default_device = vulkan_context::get().get_default_device();
// 创建图像
vk::ImageCreateInfo image_info{};
image_info.setImageType(to_vulkan_image_type(get_texture_type()));
image_info.setFormat(to_vulkan_format(get_texture_format()));
image_info.setExtent(
vk::Extent3D{
static_cast<u32>(size_.x()),
static_cast<u32>(size_.y()),
1
}
);
image_info.setMipLevels(1);
image_info.setArrayLayers(get_image_count());
image_info.setSamples(to_vulkan_sample_count(get_sample_count()));
image_info.setTiling(to_vulkan_image_tiling(get_texture_tiling()));
image_info.setUsage(to_vulkan_image_usage(get_texture_usage()));
image_info.setSharingMode(to_vulkan_sharing_mode(get_texture_sharing_mode()));
image_info.setInitialLayout(to_vulkan_image_layout(get_texture_layout()));
auto [result, image] = default_device->get_device().createImage(image_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("图像块图像创建失败:{}", vk::to_string(result));
return false;
}
image_ = image;
if (data) {
return set_data(data);
}
return true;
}
bool image_block::set_data(void* data) {
if (!data) {
MIRAI_LOG_ERROR("尝试为图像块设置空数据。");
return false;
}
if (!image_) {
MIRAI_LOG_ERROR("尝试为未创建的图像块设置数据。");
return false;
}
auto default_device = vulkan_context::get().get_default_device();
return true;
}
bool image_block::destroy_image() {
if (!is_owner_) {
MIRAI_LOG_ERROR("尝试销毁不拥有的图像块的图像。");
return false;
}
auto default_device = vulkan_context::get().get_default_device();
if (image_) {
default_device->get_device().destroyImage(image_);
image_ = nullptr;
}
return true;
}
}

View File

@@ -1,62 +0,0 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
#include "resource_types.h"
namespace mirai {
struct image_block_config {
vk::Image outer_image{};
u32 image_count{1};
vec2i size{0};
texture_type texture_type{texture_type::texture_2d};
texture_format texture_format{texture_format::undefined};
sample_count sample_count{1};
texture_tiling tiling{texture_tiling::optimal};
texture_usage usage{texture_usage::sampled | texture_usage::transfer_dst};
resource_sharing_mode sharing_mode{resource_sharing_mode::exclusive};
texture_layout layout{texture_layout::undefined};
};
class image_block : public object {
MIRAI_OBJECT_TYPE_INFO(image_block, object)
image_block(image_block_config config);
void set_size(vec2i size) noexcept { size_ = size; }
void set_image_count(u32 count) noexcept { image_count_ = count; }
void set_texture_type(texture_type type) noexcept { texture_type_ = type; }
void set_texture_format(texture_format format) noexcept { texture_format_ = format; }
void set_sample_count(sample_count count) noexcept { sample_count_ = count; }
[[nodiscard]] auto get_image_count() const noexcept { return image_count_; }
[[nodiscard]] auto get_texture_type() const noexcept { return texture_type_; }
[[nodiscard]] auto get_texture_format() const noexcept { return texture_format_; }
[[nodiscard]] auto get_sample_count() const noexcept { return sample_count_; }
[[nodiscard]] auto get_texture_tiling() const noexcept { return titling_; }
[[nodiscard]] auto get_texture_usage() const noexcept { return usage_; }
[[nodiscard]] auto get_texture_sharing_mode() const noexcept { return sharing_mode_; }
[[nodiscard]] auto get_texture_layout() const noexcept { return layout_; }
[[nodiscard]] auto get_image() const noexcept { return image_; }
[[nodiscard]] auto get_size() const noexcept { return size_; }
[[nodiscard]] auto is_owner() const noexcept { return is_owner_; }
bool rebuild_image(void* data = nullptr);
bool set_data(void* data);
bool destroy_image();
private:
bool is_owner_{true};
vk::Image image_{};
u32 image_count_{1};
vec2i size_{0};
texture_type texture_type_{texture_type::texture_2d};
texture_format texture_format_{texture_format::undefined};
sample_count sample_count_{1};
texture_tiling titling_{texture_tiling::optimal};
texture_usage usage_{texture_usage::sampled | texture_usage::transfer_dst};
resource_sharing_mode sharing_mode_{resource_sharing_mode::exclusive};
texture_layout layout_{texture_layout::undefined};
};
}

View File

@@ -1,26 +0,0 @@
#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 {
return vec2i{800, 600};
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
struct render_target_config {
// 引用的外部图像,不负责销毁
vec2i size{800, 600};
};
class render_target : public object {
MIRAI_OBJECT_TYPE_INFO(render_target, object)
public:
render_target(vk::Image image);
void resize(vec2i size);
vec2i get_size() const noexcept;
// [[nodiscard]] vk::ImageView get_image_view() const noexcept { return image_view_; }
[[nodiscard]] vk::Framebuffer get_framebuffer() const noexcept { return framebuffer_; }
protected:
void on_destroying() override;
private:
vk::Framebuffer framebuffer_{};
};
}

View File

@@ -1,190 +0,0 @@
#include "resource_manager.h"
#include "core/logger.h"
#include "core/thread_utils.h"
#include "render/vulkan_context.h"
#include "types/types.h"
#include "render/vulkan_device.h"
#include "gpu_resource/texture/texture.h"
#include "gpu_resource/gpu_buffer.h"
#include "render/vulkan_command_buffer.h"
#include "render/vulkan_fence.h"
#include "render/vulkan_time_semaphore.h"
namespace mirai {
struct timeline_awaiter {
std::shared_ptr<vulkan_device> device;
vk::Semaphore semaphore;
uint64_t target_value;
// 1. 检查是否需要挂起
// 如果 GPU 跑得比 CPU 录制还快(极少见但可能),则不挂起直接执行
bool await_ready() const {
uint64_t current_val = 0;
// 获取当前时间戳信号量的进度
const auto result = device->get_device().getSemaphoreCounterValue(semaphore, &current_val);
return (result == vk::Result::eSuccess) && (current_val >= target_value);
}
// 2. 挂起时的逻辑
// 当 await_ready 返回 false 时调用handle 是编译器生成的协程句柄
void await_suspend(std::coroutine_handle<> handle) noexcept {
// 将此协程注册到管理器的任务池中
// 管理器线程会轮询这个信号量,并在满足 target_value 时调用 handle.resume()
device->get_resource_manager()->add_tracking_task(semaphore, target_value, handle);
}
// 3. 恢复时的逻辑
// 当协程被 resume() 唤醒后,这里执行的代码是回到原函数后的第一行
void await_resume() const noexcept {
// 这里可以返回结果,比如一个错误码,但对于上传任务,通常返回 void
}
};
resource_manager::resource_manager(const std::shared_ptr<vulkan_device>& device) : device_(device) {
task_pool_.assign(128, {});
global_timeline_ = make_obj<vulkan_time_semaphore>(device, 0);
thread_ = std::jthread(&resource_manager::upload_thread_func, this);
set_thread_name(thread_, "upload_thread");
}
void resource_manager::add_tracking_task(vk::Semaphore sem, uint64_t value, std::coroutine_handle<> handle) {
// 从池中找一个空位
for (auto& task : task_pool_) {
bool expected = false;
if (task.is_active.compare_exchange_strong(expected, true)) {
task.handle = handle;
task.target_value = value;
// 将索引推入活跃队列(需要加锁保护活跃列表本身)
std::lock_guard lock(active_mutex_);
active_indices_.push_back(&task - &task_pool_[0]);
return;
}
}
// 如果池满了,实际生产中这里应处理扩容或阻塞
MIRAI_LOG_ERROR("资源上传任务池已满,无法添加新的跟踪任务!");
throw std::runtime_error("资源上传任务池已满");
}
resource_async_task resource_manager::upload_resource(const std::shared_ptr<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("上传完成,清理暂存缓冲区...");
return {};
}
uint64_t resource_manager::dispatch_immediate_transfer(std::shared_ptr<texture> tex, std::shared_ptr<staging_buffer> staging) {
auto queue = device_->get_transfer_queue();
auto cmd = make_obj<vulkan_command_buffer>(queue);
auto result = cmd->begin();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("开始命令缓冲区失败: {}", vk::to_string(result));
throw std::runtime_error("开始命令缓冲区失败");
}
vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
vk::ImageMemoryBarrier2 barrier{vk::PipelineStageFlagBits2::eTopOfPipe, vk::AccessFlagBits2::eNone,
vk::PipelineStageFlagBits2::eCopy, vk::AccessFlagBits2::eTransferWrite,
vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
tex->get_image(), range};
vk::DependencyInfo begin_barrier_info{};
begin_barrier_info.setImageMemoryBarriers({barrier});
cmd->pipeline_barrier(begin_barrier_info);
vk::BufferImageCopy copy_region{};
copy_region.setImageExtent(tex->get_extent());
copy_region.setImageSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1});
cmd->copy_buffer_to_image(copy_region, tex->get_image(), vk::ImageLayout::eTransferDstOptimal);
vk::ImageMemoryBarrier2 barrier2{vk::PipelineStageFlagBits2::eCopy, vk::AccessFlagBits2::eTransferWrite,
vk::PipelineStageFlagBits2::eBottomOfPipe, vk::AccessFlagBits2::eNone,
vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
tex->get_image(), range};
vk::DependencyInfo end_barrier_info{};
end_barrier_info.setImageMemoryBarriers({barrier2});
cmd->pipeline_barrier(end_barrier_info);
result = cmd->end();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("结束命令缓冲区失败: {}", vk::to_string(result));
throw std::runtime_error("结束命令缓冲区失败");
}
auto fence = device_->create_fence();
queue->submit({cmd->get_command_buffer()}, {}, {});
auto res = fence->device_wait();
if (res == vk::Result::eSuccess) {
fence.reset();
}
return ++last_value_;
}
uint64_t resource_manager::dispatch_immediate_transfer(std::shared_ptr<gpu_buffer> buffer, std::shared_ptr<staging_buffer> staging) {
// auto queue = device_->get_transfer_queue();
// auto cmd = make_obj<vulkan_command_buffer>(queue);
//
// auto result = cmd->begin();
// vk::BufferCopy copy_region{0, 0, buffer->get_size()};
// cmd->copy_buffer(staging->buffer, buffer->get_buffer(), copy_region);
// result = cmd->end();
//
// auto fence = make_obj<vulkan_fence>(device_);
// vk::SubmitInfo submit_info{0, nullptr, nullptr, 1, &cmd, 0, nullptr};
// queue->submit(1, &submit_info, fence);
//
// result = fence->device_wait();
return ++last_value_;
}
void resource_manager::upload_thread_func() {
// while (running_) {
// uint64_t gpu_value = 0;
// auto res = device_.getSemaphoreCounterValue(global_timeline_, &gpu_value);
//
// if (res == vk::Result::eSuccess) {
// std::lock_guard lock(active_mutex_);
//
// for (auto it = active_indices_.begin(); it != active_indices_.end(); ) {
// size_t idx = *it;
// if (gpu_value >= task_pool_[idx].target_value) {
// // 唤醒协程
// auto handle = task_pool_[idx].handle;
//
// // 先标记为不活跃,再恢复协程(防止重入问题)
// task_pool_[idx].is_active.store(false);
// it = active_indices_.erase(it);
//
// // 恢复执行协程后续的清理代码
// if (handle) handle.resume();
// } else {
// ++it;
// }
// }
// }
//
// // 避免 100% CPU 占用
// if (active_indices_.empty()) {
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
// } else {
// std::this_thread::yield();
// }
// }
}
}

View File

@@ -1,73 +0,0 @@
#pragma once
#include <thread>
#include <coroutine>
#include <mutex>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "core/object.h"
namespace mirai {
class vulkan_device;
class vulkan_time_semaphore;
class texture;
class gpu_buffer;
class staging_buffer;
struct resource_upload_task {
std::coroutine_handle<> handle;
uint64_t target_value = 0;
std::atomic<bool> is_active{ false };
resource_upload_task() = default;
resource_upload_task(const resource_upload_task& other) noexcept {
handle = other.handle;
target_value = other.target_value;
is_active.store(other.is_active.load());
}
auto& operator=(const resource_upload_task& other) noexcept {
handle = other.handle;
target_value = other.target_value;
is_active.store(other.is_active.load());
return *this;
}
};
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(const std::shared_ptr<vulkan_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);
uint64_t dispatch_immediate_transfer(std::shared_ptr<gpu_buffer> buffer, std::shared_ptr<staging_buffer> staging);
[[nodiscard]] auto get_semaphore() noexcept { return global_timeline_; }
private:
void upload_thread_func();
std::shared_ptr<vulkan_device> device_;
std::shared_ptr<vulkan_time_semaphore> global_timeline_;
std::atomic<uint64_t> last_value_{0};
// 池化存储
std::vector<resource_upload_task> task_pool_;
std::vector<size_t> active_indices_;
std::mutex active_mutex_;
std::mutex submission_mutex_; // 保护 GPU Queue 提交
std::jthread thread_;
std::atomic<bool> running_{true};
};
}

View File

@@ -1,560 +0,0 @@
#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 简单的低精度法线贴图 (仅存XYZ重建)
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_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,
// 存储图像
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;
}
};
}

View File

@@ -1,43 +0,0 @@
#include "resource_types_to_string.h"
namespace mirai {
std::string to_string(resource_sharing_mode mode) {
switch (mode) {
case resource_sharing_mode::exclusive:
return "Exclusive";
case resource_sharing_mode::concurrent:
return "Concurrent";
default:
return "Unknown";
}
}
std::string to_string(buffer_usage usage) {
std::string result;
if (static_cast<u32>(usage) == 0) {
return "None";
}
if ((usage & buffer_usage::vertex) == buffer_usage::vertex) {
result += "Vertex|";
}
if ((usage & buffer_usage::index) == buffer_usage::index) {
result += "Index|";
}
if ((usage & buffer_usage::uniform) == buffer_usage::uniform) {
result += "Uniform|";
}
if ((usage & buffer_usage::storage) == buffer_usage::storage) {
result += "Storage|";
}
if ((usage & buffer_usage::transfer_src) == buffer_usage::transfer_src) {
result += "TransferSrc|";
}
if ((usage & buffer_usage::transfer_dst) == buffer_usage::transfer_dst) {
result += "TransferDst|";
}
if (!result.empty()) {
result.pop_back(); // 移除最后的 '|'
}
return result;
}
}

View File

@@ -1,9 +0,0 @@
#pragma once
#include <string>
#include "resource_types.h"
namespace mirai {
std::string to_string(resource_sharing_mode mode);
std::string to_string(buffer_usage usage);
}

View File

@@ -1,816 +0,0 @@
#pragma once
#include "resource_types.h"
namespace mirai {
[[nodiscard]] constexpr auto to_vulkan_buffer_usage(buffer_usage usage) noexcept {
vk::BufferUsageFlags flags{};
if (has_flag(usage, buffer_usage::vertex)) {
flags |= vk::BufferUsageFlagBits::eVertexBuffer;
}
if (has_flag(usage, buffer_usage::index)) {
flags |= vk::BufferUsageFlagBits::eIndexBuffer;
}
if (has_flag(usage, buffer_usage::uniform)) {
flags |= vk::BufferUsageFlagBits::eUniformBuffer;
}
if (has_flag(usage, buffer_usage::storage)) {
flags |= vk::BufferUsageFlagBits::eStorageBuffer;
}
if (has_flag(usage, buffer_usage::staging)) {
flags |= vk::BufferUsageFlagBits::eTransferSrc;
flags |= vk::BufferUsageFlagBits::eTransferDst;
}
if (has_flag(usage, buffer_usage::indirect)) {
flags |= vk::BufferUsageFlagBits::eIndirectBuffer;
}
if (has_flag(usage, buffer_usage::transfer_src)) {
flags |= vk::BufferUsageFlagBits::eTransferSrc;
}
if (has_flag(usage, buffer_usage::transfer_dst)) {
flags |= vk::BufferUsageFlagBits::eTransferDst;
}
if (has_flag(usage, buffer_usage::shader_device_address)) {
flags |= vk::BufferUsageFlagBits::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_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:
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 (has_flag(usage, texture_usage::sampled)) {
flags |= vk::ImageUsageFlagBits::eSampled;
}
if (has_flag(usage, texture_usage::storage)) {
flags |= vk::ImageUsageFlagBits::eStorage;
}
if (has_flag(usage, texture_usage::color_attachment)) {
flags |= vk::ImageUsageFlagBits::eColorAttachment;
}
if (has_flag(usage, texture_usage::depth_stencil_attachment)) {
flags |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
}
if (has_flag(usage, texture_usage::input_attachment)) {
flags |= vk::ImageUsageFlagBits::eInputAttachment;
}
if (has_flag(usage, texture_usage::transfer_src)) {
flags |= vk::ImageUsageFlagBits::eTransferSrc;
}
if (has_flag(usage, texture_usage::transfer_dst)) {
flags |= vk::ImageUsageFlagBits::eTransferDst;
}
if (has_flag(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;
}
}
}

View File

@@ -1,103 +0,0 @@
#include "swapchain.h"
#include "core/logger.h"
#include "render/device_utils.h"
#include "render/vulkan_context.h"
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设备无法创建交换链。");
return;
}
auto physical_device = default_device->get_physical_device();
if (!physical_device) {
MIRAI_LOG_ERROR("无有效物理设备,无法创建交换链。");
return;
}
auto queue_families = find_queue_families(physical_device, info.surface);
if (!queue_families.graphics_family) {
MIRAI_LOG_ERROR("未能找到用于交换链创建的图形队列族。");
return;
}
if (!queue_families.present_family) {
MIRAI_LOG_ERROR("未能找到用于交换链创建的呈现队列族。");
return;
}
const auto& query = query_swapchain_support(physical_device, info.surface);
auto format_target = info.hdr_enabled ? vk::Format::eA2B10G10R10UnormPack32 : vk::Format::eB8G8R8A8Srgb;
const auto& extent = query.choose_extent(info.initial_extent.x(), info.initial_extent.y());
const auto image_count = query.choose_image_count();
const auto present_mode = query.choose_present_mode();
const auto image_format = query.choose_surface_format(format_target);
vk::SwapchainCreateInfoKHR create_info{};
create_info.setSurface(info.surface);
create_info.setMinImageCount(image_count);
create_info.setImageFormat(image_format.format);
create_info.setImageColorSpace(image_format.colorSpace);
create_info.setImageExtent(extent);
create_info.setImageArrayLayers(1);
create_info.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment);
create_info.setPreTransform(query.get_transform());
create_info.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque);
create_info.setPresentMode(present_mode);
create_info.setClipped(true);
const auto graphics_queue_family_index = queue_families.graphics_family.value();
const auto present_queue_family_index = queue_families.present_family.value();
// 处理多个队列族(如果 Graphics 和 Present 不是同一个)
if (graphics_queue_family_index != present_queue_family_index) {
uint32_t queueFamilyIndices[] = { graphics_queue_family_index, present_queue_family_index };
create_info.setQueueFamilyIndices(queueFamilyIndices);
create_info.setImageSharingMode(vk::SharingMode::eConcurrent);
} else {
create_info.setImageSharingMode(vk::SharingMode::eExclusive);
}
auto [result, swapchain] = default_device->get_device().createSwapchainKHR(create_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("交换链创建失败:{}", vk::to_string(result));
return;
}
swapchain_ = swapchain;
}
void swapchain::create_image_views() {
// auto default_device = vulkan_context::get().get_default_device();
// u32 image_count = 0;
// auto result = default_device->get_device().getSwapchainImagesKHR(swapchain_, &image_count, images);
// if (result != vk::Result::eSuccess) {
// MIRAI_LOG_ERROR("获取交换链图像数量失败:{}", vk::to_string(result));
// return;
// }
// std::vector<vk::Image> swapchain_images(image_count);
// result = default_device->get_device().getSwapchainImagesKHR(swapchain_, &image_count, swapchain_images.data());
// if (result != vk::Result::eSuccess) {
// MIRAI_LOG_ERROR("获取交换链图像失败:{}", vk::to_string(result));
// return;
// }
}
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
struct swapchain_create_info {
vk::SurfaceKHR surface{};
vec2i initial_extent{800, 600};
bool hdr_enabled{false};
};
class swapchain : public object {
MIRAI_OBJECT_TYPE_INFO(swapchain, object)
swapchain(const swapchain_create_info& info);
auto get_swapchain() const noexcept { return swapchain_; }
protected:
void on_destroying() override;
void create_swapchain(const swapchain_create_info& info);
void create_image_views();
private:
vk::SwapchainKHR swapchain_;
};
}

View File

@@ -1,91 +0,0 @@
#include "texture.h"
#include "gpu_resource/resource_types_vulkan.h"
#include "core/logger.h"
#include "render/vulkan_context.h"
namespace mirai {
texture::texture(vk::Image outer_image, texture_format format) {
is_owner_ = !outer_image;
image_ = outer_image;
format_ = format;
}
texture::texture(vk::Image outer_image, texture_format format, std::shared_ptr<vulkan_queue> queue) : gpu_resource(queue) {
is_owner_ = !outer_image;
image_ = outer_image;
format_ = format;
}
void texture::record_transfer_barrier(vk::CommandBuffer cmd, const std::shared_ptr<vulkan_queue>& dst_queue,
vk::ImageLayout new_layout, vk::PipelineStageFlags2 dst_stage, vk::AccessFlags2 dst_access) {
if (get_sharing_mode() != resource_sharing_mode::exclusive) {
MIRAI_LOG_DEBUG("仅支持独占模式的纹理资源需要进行布局转换。");
return;
}
auto src_queue = queue_.lock();
vk::ImageMemoryBarrier2 barrier;
barrier.srcStageMask = get_current_stages(); // 资源自己记录的状态
barrier.srcAccessMask = get_current_access();
barrier.dstStageMask = dst_stage;
barrier.dstAccessMask = dst_access;
barrier.oldLayout = current_layout_;
barrier.newLayout = new_layout;
// 关键:所有权转移索引
barrier.srcQueueFamilyIndex = src_queue ? src_queue->get_family_index() : VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = dst_queue->get_family_index();
barrier.image = image_;
barrier.subresourceRange = get_full_range(); // 只有资源知道自己的 range
vk::DependencyInfo dep;
dep.setImageMemoryBarriers(barrier);
cmd.pipelineBarrier2(dep);
queue_ = dst_queue;
current_layout_ = new_layout;
current_stages_ = dst_stage;
current_access_ = dst_access;
}
void texture::on_created() {
object::on_created();
if (is_owner_) {
image_ = create_image();
}
image_view_ = create_image_view();
}
void texture::on_destroying() {
object::on_destroying();
auto default_device = vulkan_context::get().get_default_device();
if (image_view_) {
default_device->get_device().destroyImageView(image_view_);
image_view_ = nullptr;
}
if (is_owner_ && image_) {
default_device->get_device().destroyImage(image_);
image_ = nullptr;
}
}
vk::ImageView texture::create_image_view() {
auto default_device = vulkan_context::get().get_default_device();
// 创建图像视图
vk::ImageViewCreateInfo view_info{};
view_info.setImage(image_);
view_info.setViewType(to_vulkan_image_view_type(get_texture_type()));
view_info.setFormat(to_vulkan_format(format_));
view_info.setSubresourceRange(get_full_range());
auto [result, image_view] = default_device->get_device().createImageView(view_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建纹理图像视图失败: {}", vk::to_string(result));
return {};
}
return image_view;
}
}

View File

@@ -1,46 +0,0 @@
#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 get_image() const { return image_; }
[[nodiscard]] auto get_image_view() const { return image_view_; }
[[nodiscard]] virtual vk::ImageSubresourceRange get_full_range() const noexcept = 0;
[[nodiscard]] virtual vk::Extent3D get_extent() const noexcept = 0;
[[nodiscard]] auto get_format() const noexcept { return format_; }
[[nodiscard]] auto get_current_layout() const noexcept { return current_layout_; }
void record_transfer_barrier(
vk::CommandBuffer cmd,
const std::shared_ptr<vulkan_queue>& dst_queue,
vk::ImageLayout new_layout,
vk::PipelineStageFlags2 dst_stage,
vk::AccessFlags2 dst_access
);
protected:
void on_created() override;
void on_destroying() override;
virtual vk::Image create_image() = 0;
virtual vk::ImageView create_image_view();
protected:
// 是否拥有图像资源的所有权
bool is_owner_{true};
vk::Image image_{};
vk::ImageView image_view_{};
vk::ImageLayout current_layout_{vk::ImageLayout::eUndefined};
texture_format format_{texture_format::undefined};
};
}

View File

@@ -1,41 +0,0 @@
#include "texture1d.h"
#include "core/logger.h"
#include "gpu_resource/resource_types_vulkan.h"
#include "render/vulkan_context.h"
namespace mirai {
vk::ImageSubresourceRange texture1d::get_full_range() const noexcept {
return vk::ImageSubresourceRange{
vk::ImageAspectFlagBits::eColor,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS
};
}
vk::Image texture1d::create_image() {
vk::ImageCreateInfo image_info{};
image_info.imageType = vk::ImageType::e2D;
image_info.format = to_vulkan_format(format_);
image_info.extent = vk::Extent3D{
size_,
1,
1
};
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = vk::SampleCountFlagBits::e1;
image_info.tiling = vk::ImageTiling::eOptimal;
image_info.usage = to_vulkan_image_usage(texture_usage::sampled | texture_usage::transfer_dst);
image_info.sharingMode = vk::SharingMode::eExclusive;
image_info.initialLayout = vk::ImageLayout::eUndefined;
auto default_device = vulkan_context::get().get_default_device();
auto [result, image] = default_device->get_device().createImage(image_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建 2D 纹理图像失败: {}", vk::to_string(result));
return {};
}
return image;
}
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include "texture.h"
namespace mirai {
class texture1d : public texture {
MIRAI_OBJECT_TYPE_INFO(texture1d, texture);
public:
texture_type get_texture_type() const noexcept override { return texture_type::texture_1d; }
[[nodiscard]] auto size() const { return size_; }
vk::ImageSubresourceRange get_full_range() const noexcept override;
protected:
vk::Image create_image() override;
private:
u32 size_{0};
};
}

View File

@@ -1,61 +0,0 @@
#include "texture2d.h"
#include "core/logger.h"
#include "gpu_resource/resource_types_vulkan.h"
#include "render/vulkan_context.h"
#include "types/error.h"
namespace mirai {
// resource_async_task texture2d::upload(std::vector<uint8_t> data) {
// auto& manager = resource_manager::get();
// // A. 准备工作 (Staging Buffer 分配等)
// auto staging = manager.allocate_staging(data.size());
// memcpy(staging->map(), data.data(), data.size());
//
// // B. 立即派发 (Immediate Dispatch)
// // 内部调用两次 submit并返回本次任务的全局 target_value
// uint64_t wait_val = manager.dispatch_immediate_transfer(shared_from_this(), staging);
//
// // C. 异步等待
// // 线程会在这里返回,直到 GPU 完成任务后被管理器唤醒
// co_await TimelineAwaiter{ manager.get_global_semaphore(), wait_val, manager };
//
// // D. 恢复执行 (清理工作)
// manager.free_staging(staging);
// MIRAI_LOG_DEBUG("纹理上传及所有权转移完成");
// }
vk::ImageSubresourceRange texture2d::get_full_range() const noexcept {
return vk::ImageSubresourceRange{
vk::ImageAspectFlagBits::eColor,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS
};
}
vk::Image texture2d::create_image() {
vk::ImageCreateInfo image_info{};
image_info.imageType = vk::ImageType::e2D;
image_info.format = to_vulkan_format(format_);
image_info.extent = vk::Extent3D{
static_cast<u32>(size_.x()),
static_cast<u32>(size_.y()),
1
};
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = vk::SampleCountFlagBits::e1;
image_info.tiling = vk::ImageTiling::eOptimal;
image_info.usage = to_vulkan_image_usage(texture_usage::sampled | texture_usage::transfer_dst);
image_info.sharingMode = vk::SharingMode::eExclusive;
image_info.initialLayout = vk::ImageLayout::eUndefined;
auto default_device = vulkan_context::get().get_default_device();
auto [result, image] = default_device->get_device().createImage(image_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建 2D 纹理图像失败: {}", vk::to_string(result));
return {};
}
return image;
}
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include "texture.h"
namespace mirai {
class texture2d : public texture {
MIRAI_OBJECT_TYPE_INFO(texture2d, texture);
// resource_async_task upload(std::vector<uint8_t> data) override;
texture_type get_texture_type() const noexcept override { return texture_type::texture_2d; }
[[nodiscard]] auto size() const { return size_; }
vk::ImageSubresourceRange get_full_range() const noexcept override;
protected:
vk::Image create_image() override;
private:
vec2i size_{0};
};
}

View File

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

View File

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

View File

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

View File

@@ -1,68 +0,0 @@
#pragma once
#include <condition_variable>
#include <queue>
#include <thread>
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
class image_block;
class transfer_task : public object {
MIRAI_OBJECT_TYPE_INFO(transfer_task, object)
public:
void run(std::shared_ptr<image_block> block);
void set_data(std::span<std::byte> data) noexcept {
data_ = data;
complete_.store(true, std::memory_order_release);
}
auto get_data() const noexcept {
return data_;
}
bool is_complete() const noexcept {
return complete_;
}
private:
std::atomic<bool> complete_{false};
std::span<std::byte> data_;
std::function<void()> task_;
};
class transfer_task_copy : public object {
MIRAI_OBJECT_TYPE_INFO(transfer_task_copy, object)
public:
void set_data(const std::vector<std::byte>& data) noexcept {
data_ = data;
complete_.store(true, std::memory_order_release);
}
auto get_data() const noexcept {
return data_;
}
bool is_complete() const noexcept {
return complete_;
}
private:
std::atomic<bool> complete_{false};
std::vector<std::byte> data_;
std::function<void()> task_;
};
class transfer_processor : public object {
MIRAI_OBJECT_TYPE_INFO(transfer_processor, object)
private:
std::jthread thread_;
std::mutex mutex_;
std::condition_variable cv_;
std::queue<std::shared_ptr<transfer_task>> task_queue_;
std::queue<std::shared_ptr<transfer_task_copy>> task_copy_queue_;
bool running_ = true;
vk::Queue transfer_queue_;
void process_tasks();
};
}

View File

@@ -1,151 +0,0 @@
#include "device_utils.h"
#include "core/logger.h"
namespace mirai {
swapchain_support_details query_swapchain_support(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface) {
swapchain_support_details details;
if (!surface) {
return details;
}
// 获取表面能力
auto [result_caps, capabilities] = physical_device.getSurfaceCapabilitiesKHR(surface);
if (result_caps == vk::Result::eSuccess) {
details.capabilities = capabilities;
}
// 获取表面格式
auto [result_formats, formats] = physical_device.getSurfaceFormatsKHR(surface);
if (result_formats == vk::Result::eSuccess) {
details.formats = formats;
}
// 获取呈现模式
auto [result_modes, modes] = physical_device.getSurfacePresentModesKHR(surface);
if (result_modes == vk::Result::eSuccess) {
details.present_modes = modes;
}
return details;
}
queue_family_indices find_queue_families(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface) {
queue_family_indices indices;
std::vector<vk::QueueFamilyProperties> queue_families = physical_device.getQueueFamilyProperties();
for (u32 i = 0; i < queue_families.size(); ++i) {
const auto& queue_family = queue_families[i];
// 检查图形支持
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
indices.graphics_family = i;
}
// 检查呈现支持
if (surface) {
auto [result, present_support] = physical_device.getSurfaceSupportKHR(i, surface);
if (result == vk::Result::eSuccess && present_support) {
indices.present_family = i;
}
}
// 检查独立计算队列
if ((queue_family.queueFlags & vk::QueueFlagBits::eCompute) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
indices.compute_family = i;
}
else if (queue_family.queueFlags & vk::QueueFlagBits::eCompute) {
if (!indices.compute_family.has_value()) {
indices.compute_family = i;
}
}
// 检查独立传输队列
if ((queue_family.queueFlags & vk::QueueFlagBits::eTransfer) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eGraphics) &&
!(queue_family.queueFlags & vk::QueueFlagBits::eCompute)) {
indices.transfer_family = i;
}
else if (queue_family.queueFlags & vk::QueueFlagBits::eTransfer) {
if (!indices.transfer_family.has_value()) {
indices.transfer_family = i;
}
}
}
return indices;
}
physical_device_info populate_physical_device(const vk::PhysicalDevice& physical_device, vk::SurfaceKHR surface) {
physical_device_info info;
info.physical_device = physical_device;
// 获取基本属性和特性
info.properties = physical_device.getProperties();
info.features = physical_device.getFeatures();
info.memory_properties = physical_device.getMemoryProperties();
// 获取 Vulkan 1.1/1.2/1.3 特性
vk::PhysicalDeviceFeatures2 features2{};
features2.setPNext(&info.features_11);
info.features_11.setPNext(&info.features_12);
info.features_12.setPNext(&info.features_13);
physical_device.getFeatures2(&features2);
// 获取队列族
info.queue_families = find_queue_families(physical_device, surface);
// 获取交换链支持
if (surface) {
info.swapchain_support = query_swapchain_support(physical_device, surface);
}
return info;
}
bool is_extension_supported(const char* extension_name) {
const auto& extensions = get_available_extensions();
return std::ranges::any_of(extensions, [extension_name](const vk::ExtensionProperties& ext) {
return std::strcmp(ext.extensionName, extension_name) == 0;
});
}
bool is_layer_supported(const char* layer_name) {
const auto& layers = get_available_layers();
return std::ranges::any_of(layers, [layer_name](const vk::LayerProperties& layer) {
return std::strcmp(layer.layerName, layer_name) == 0;
});
}
std::vector<vk::ExtensionProperties> get_available_extensions() {
auto [result, extensions] = vk::enumerateInstanceExtensionProperties();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("无法枚举 Vulkan 实例扩展: {}", vk::to_string(result));
return {};
}
return extensions;
}
std::vector<vk::LayerProperties> get_available_layers() {
auto [result, layers] = vk::enumerateInstanceLayerProperties();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("无法枚举 Vulkan 实例层: {}", vk::to_string(result));
return {};
}
return layers;
}
u32 get_supported_api_version() {
auto [result, version] = vk::enumerateInstanceVersion();
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("获取支持的Vulkan API版本失败: {}", vk::to_string(result));
return VK_API_VERSION_1_0;
}
return version;
}
}

View File

@@ -1,400 +0,0 @@
#pragma once
#include <set>
#include "types/types.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
/**
* @brief 交换链支持详情
*
* 存储物理设备的交换链支持信息
*/
struct swapchain_support_details {
/// 表面能力
vk::SurfaceCapabilitiesKHR capabilities{};
/// 支持的表面格式列表
std::vector<vk::SurfaceFormatKHR> formats;
/// 支持的呈现模式列表
std::vector<vk::PresentModeKHR> present_modes;
/**
* @brief 检查交换链支持是否足够
* @return 如果至少有一个格式和一个呈现模式返回 true
*/
[[nodiscard]] bool is_adequate() const noexcept {
return !formats.empty() && !present_modes.empty();
}
/**
* @brief 选择最佳的表面格式
* @param preferred_format 首选格式
* @param preferred_color_space 首选颜色空间
* @return 选择的表面格式
*/
[[nodiscard]] vk::SurfaceFormatKHR choose_surface_format(
vk::Format preferred_format = vk::Format::eB8G8R8A8Srgb,
vk::ColorSpaceKHR preferred_color_space = vk::ColorSpaceKHR::eSrgbNonlinear
) const {
// 首先尝试找到首选格式
for (const auto& format : formats) {
if (format.format == preferred_format &&
format.colorSpace == preferred_color_space) {
return format;
}
}
// 如果找不到首选格式,返回第一个可用格式
return formats.empty()
? vk::SurfaceFormatKHR{
vk::Format::eB8G8R8A8Unorm,
vk::ColorSpaceKHR::eSrgbNonlinear
}
: formats[0];
}
/**
* @brief 选择最佳的呈现模式
* @param preferred_mode 首选模式
* @return 选择的呈现模式
*/
[[nodiscard]] vk::PresentModeKHR choose_present_mode(
vk::PresentModeKHR preferred_mode = vk::PresentModeKHR::eMailbox
) const {
// 首先尝试找到首选模式
for (const auto& mode : present_modes) {
if (mode == preferred_mode) {
return mode;
}
}
// 如果找不到首选模式,返回 FIFO总是可用
return vk::PresentModeKHR::eFifo;
}
/**
* @brief 选择交换链范围
* @param window_width 窗口宽度
* @param window_height 窗口高度
* @return 选择的范围
*/
[[nodiscard]] vk::Extent2D choose_extent(u32 window_width, u32 window_height) const {
if (capabilities.currentExtent.width != std::numeric_limits<u32>::max()) {
return capabilities.currentExtent;
}
vk::Extent2D actual_extent = {window_width, window_height};
actual_extent.width = std::clamp(actual_extent.width,
capabilities.minImageExtent.width,
capabilities.maxImageExtent.width);
actual_extent.height = std::clamp(actual_extent.height,
capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
return actual_extent;
}
/**
* @brief 选择图像数量
* @param preferred_count 首选数量
* @return 选择的图像数量
*/
[[nodiscard]] u32 choose_image_count(u32 preferred_count = 3) const {
u32 image_count = std::max(preferred_count, capabilities.minImageCount);
if (capabilities.maxImageCount > 0) {
image_count = std::min(image_count, capabilities.maxImageCount);
}
return image_count;
}
[[nodiscard]] auto get_transform() const noexcept {
return capabilities.currentTransform;
}
};
swapchain_support_details query_swapchain_support(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface);
/// 无效的队列族索引
constexpr u32 invalid_queue_family = std::numeric_limits<u32>::max();
/**
* @brief 队列族索引
*
* 存储 Vulkan 设备的各种队列族索引
*/
struct queue_family_indices {
/// 图形队列族索引
std::optional<u32> graphics_family;
/// 呈现队列族索引
std::optional<u32> present_family;
/// 计算队列族索引
std::optional<u32> compute_family;
/// 传输队列族索引
std::optional<u32> transfer_family;
/**
* @brief 检查是否所有必需的队列族都已找到
* @return 如果图形和呈现队列族都存在返回 true
*/
[[nodiscard]] bool is_complete() const noexcept {
return graphics_family.has_value() && present_family.has_value();
}
/**
* @brief 检查是否有独立的计算队列族
* @return 如果有独立的计算队列族返回 true
*/
[[nodiscard]] bool has_dedicated_compute() const noexcept {
return compute_family.has_value() &&
compute_family.value() != graphics_family.value();
}
/**
* @brief 检查是否有独立的传输队列族
* @return 如果有独立的传输队列族返回 true
*/
[[nodiscard]] bool has_dedicated_transfer() const noexcept {
return transfer_family.has_value() &&
transfer_family.value() != graphics_family.value() &&
transfer_family.value() != compute_family.value_or(invalid_queue_family);
}
/**
* @brief 获取唯一的队列族索引列表
* @return 唯一队列族索引的向量
*/
[[nodiscard]] std::vector<u32> get_unique_families() const {
std::set<u32> unique_families;
if (graphics_family.has_value()) {
unique_families.insert(graphics_family.value());
}
if (present_family.has_value()) {
unique_families.insert(present_family.value());
}
if (compute_family.has_value()) {
unique_families.insert(compute_family.value());
}
if (transfer_family.has_value()) {
unique_families.insert(transfer_family.value());
}
return {unique_families.begin(), unique_families.end()};
}
};
queue_family_indices find_queue_families(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface);
/**
* @brief 物理设备信息
*
* 存储物理设备的详细信息
*/
struct physical_device_info {
/// 物理设备句柄
vk::PhysicalDevice physical_device;
/// 设备属性
vk::PhysicalDeviceProperties properties{};
/// 设备特性
vk::PhysicalDeviceFeatures features{};
/// Vulkan 1.1 特性
vk::PhysicalDeviceVulkan11Features features_11;
/// Vulkan 1.2 特性
vk::PhysicalDeviceVulkan12Features features_12;
/// Vulkan 1.3 特性
vk::PhysicalDeviceVulkan13Features features_13;
/// 设备内存属性
vk::PhysicalDeviceMemoryProperties memory_properties{};
/// 队列族索引
queue_family_indices queue_families;
/// 交换链支持详情
swapchain_support_details swapchain_support;
/// 设备评分(用于选择最佳设备)
i32 score = 0;
/**
* @brief 获取设备名称
* @return 设备名称字符串
*/
[[nodiscard]] std::string get_device_name() const {
return std::string(properties.deviceName.data());
}
/**
* @brief 检查是否为独立 GPU
* @return 如果是独立 GPU 返回 true
*/
[[nodiscard]] bool is_discrete_gpu() const noexcept {
return properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
}
/**
* @brief 检查是否为集成 GPU
* @return 如果是集成 GPU 返回 true
*/
[[nodiscard]] bool is_integrated_gpu() const noexcept {
return properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu;
}
/**
* @brief 检查是否支持 Vulkan 1.3
* @return 如果支持 Vulkan 1.3 返回 true
*/
[[nodiscard]] bool supports_vulkan_1_3() const noexcept {
return properties.apiVersion >= VK_API_VERSION_1_3;
}
/**
* @brief 检查是否支持 Dynamic Rendering
* @return 如果支持 Dynamic Rendering 返回 true
*/
[[nodiscard]] bool supports_dynamic_rendering() const noexcept {
return features_13.dynamicRendering == vk::True;
}
/**
* @brief 检查是否支持 Synchronization2
* @return 如果支持 Synchronization2 返回 true
*/
[[nodiscard]] bool supports_synchronization2() const noexcept {
return features_13.synchronization2 == vk::True;
}
/**
* @brief 检查是否支持描述符索引
* @return 如果支持描述符索引返回 true
*/
[[nodiscard]] bool supports_descriptor_indexing() const noexcept {
return features_12.descriptorIndexing == vk::True;
}
/**
* @brief 检查是否支持 Timeline Semaphores
* @return 如果支持 Timeline Semaphores 返回 true
*/
[[nodiscard]] bool supports_timeline_semaphores() const noexcept {
return features_12.timelineSemaphore == vk::True;
}
/**
* @brief 检查是否支持 Buffer Device Address
* @return 如果支持 Buffer Device Address 返回 true
*/
[[nodiscard]] bool supports_buffer_device_address() const noexcept {
return features_12.bufferDeviceAddress == vk::True;
}
/**
* @brief 检查设备是否适合使用
* @return 如果设备满足最低要求返回 true
*/
[[nodiscard]] bool is_suitable() const noexcept {
return physical_device &&
queue_families.is_complete() &&
swapchain_support.is_adequate() &&
supports_vulkan_1_3() &&
supports_dynamic_rendering() &&
supports_synchronization2();
}
/**
* @brief 计算设备评分
* @return 设备评分
*/
[[nodiscard]] i32 calculate_score() const {
if (!is_suitable()) {
return 0;
}
i32 device_score = 0;
// 独立 GPU 加分
if (is_discrete_gpu()) {
device_score += 1000;
}
else if (is_integrated_gpu()) {
device_score += 100;
}
// 最大纹理尺寸加分
device_score += static_cast<i32>(properties.limits.maxImageDimension2D / 1000);
// 支持可选特性加分
if (supports_descriptor_indexing()) {
device_score += 100;
}
if (supports_timeline_semaphores()) {
device_score += 50;
}
if (supports_buffer_device_address()) {
device_score += 50;
}
// 有独立计算队列加分
if (queue_families.has_dedicated_compute()) {
device_score += 50;
}
// 有独立传输队列加分
if (queue_families.has_dedicated_transfer()) {
device_score += 50;
}
return device_score;
}
};
physical_device_info populate_physical_device(const vk::PhysicalDevice& physical_device, vk::SurfaceKHR surface);
/**
* @brief 检查是否支持指定的实例扩展
* @param extension_name 扩展名称
* @return 如果支持返回 true
*/
[[nodiscard]] bool is_extension_supported(const char* extension_name);
/**
* @brief 检查是否支持指定的验证层
* @param layer_name 层名称
* @return 如果支持返回 true
*/
[[nodiscard]] bool is_layer_supported(const char* layer_name);
/**
* @brief 获取所有可用的实例扩展
* @return 扩展属性列表
*/
[[nodiscard]] std::vector<vk::ExtensionProperties> get_available_extensions();
/**
* @brief 获取所有可用的验证层
* @return 层属性列表
*/
[[nodiscard]] std::vector<vk::LayerProperties> get_available_layers();
/**
* @brief 获取实例支持的最高 API 版本
* @return 支持的 API 版本
*/
[[nodiscard]] u32 get_supported_api_version();
}

View File

@@ -1,34 +0,0 @@
#include "vulkan_command_buffer.h"
#include "vulkan_context.h"
namespace mirai {
vulkan_command_buffer::vulkan_command_buffer(std::shared_ptr<vulkan_queue> queue, vk::CommandBufferLevel level) {
queue_ = queue;
level_ = level;
cmd_ = vulkan_thread_context::get().allocate_command_buffer(queue_, level_);
}
vk::Result vulkan_command_buffer::begin(const vk::CommandBufferBeginInfo& info) const {
return cmd_.begin(info);
}
void vulkan_command_buffer::pipeline_barrier(const vk::DependencyInfo& dependency_info) {
return cmd_.pipelineBarrier2(dependency_info);
}
void vulkan_command_buffer::execute_cleanup_tasks() {
for (const auto& task : cleanup_tasks_) {
if (task.task) {
task.task();
}
}
cleanup_tasks_.clear();
}
void vulkan_command_buffer::on_destroying() {
object::on_destroying();
vulkan_thread_context::get().free_command_buffer(queue_, cmd_);
}
}

View File

@@ -1,59 +0,0 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
class vulkan_queue;
// 用于命令缓冲区完成执行后,如果需要清理资源
struct vulkan_cmd_buffer_cleanup_task {
std::function<void()> task;
#if MIRAI_DEBUG
std::string debug_name;
#endif
};
/**
* 线程安全的command buffer包装器
*/
class vulkan_command_buffer : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_command_buffer, object)
public:
vulkan_command_buffer(std::shared_ptr<vulkan_queue> queue,
vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary);
vk::Result begin(const vk::CommandBufferBeginInfo& info = vk::CommandBufferBeginInfo{
vk::CommandBufferUsageFlagBits::eOneTimeSubmit
}) const;
vk::Result end() const { return cmd_.end(); }
void pipeline_barrier(const vk::DependencyInfo& dependency_info);
void copy_buffer_to_image(const vk::BufferImageCopy& copy_region,
vk::Image image,
vk::ImageLayout layout) const {
cmd_.copyBufferToImage(vk::Buffer{}, image, layout, 1, &copy_region);
}
void copy_buffer(vk::Buffer src, vk::Buffer dst, const vk::BufferCopy& copy_region) const {
cmd_.copyBuffer(src, dst, 1, &copy_region);
}
[[nodiscard]] auto get_queue() const { return queue_; }
[[nodiscard]] auto get_command_buffer() const { return cmd_; }
[[nodiscard]] auto get_level() const { return level_; }
void add_cleanup_task(const vulkan_cmd_buffer_cleanup_task& task) {
cleanup_tasks_.emplace_back(task);
}
void execute_cleanup_tasks();
protected:
void on_destroying() override;
private:
std::shared_ptr<vulkan_queue> queue_;
vk::CommandBuffer cmd_;
vk::CommandBufferLevel level_;
std::vector<vulkan_cmd_buffer_cleanup_task> cleanup_tasks_;
};
}

View File

@@ -1,101 +0,0 @@
#include "vulkan_context.h"
#include "vulkan_queue.h"
#include "window/window_manager.h"
#include "core/logger.h"
namespace mirai {
thread_local vulkan_thread_context vulkan_thread_context::instance;
void vulkan_context::init(const vulkan_context_init_config& config) {
instance_ = make_obj<vulkan_instance>(config.instance_config);
}
void vulkan_context::setup(const vulkan_context_config& config) {
create_default_device(config.surface);
}
void vulkan_context::shutdown() {
for (const auto& d : devices_) {
d->request_destroy();
}
devices_.clear();
instance_.reset();
}
std::shared_ptr<vulkan_device> vulkan_context::create_device(const vulkan_device_config& info) {
auto device = make_obj<vulkan_device>(info);
devices_.push_back(device);
return device;
}
void vulkan_context::create_default_device(const vk::SurfaceKHR& surface) {
std::optional<physical_device_info> selected;
auto infos = instance_->get_physical_devices_info(surface);
for (const auto& info : infos) {
if (info.is_suitable()) {
MIRAI_LOG_INFO("选择 GPU: {} (分数: {})", info.get_device_name(), info.score);
selected = info;
break;
}
}
if (!selected) {
MIRAI_LOG_ERROR("无可用GPU");
return;
}
vulkan_device_config config{
instance_->get_instance(),
selected->physical_device,
instance_->get_api_version(),
true,
true,
256 * 1024 * 1024, // 256 MB
dl
};
create_device(config);
}
vk::CommandBuffer vulkan_thread_context::allocate_command_buffer(const std::shared_ptr<vulkan_queue>& queue,
vk::CommandBufferLevel level) {
const auto pool = get_pool(queue);
const auto device = queue->get_device();
vk::CommandBufferAllocateInfo alloc_info(
pool,
level,
1
);
auto [result, cmd_buffers] = device.allocateCommandBuffers(alloc_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("分配命令缓冲区失败: {}", vk::to_string(result));
throw std::runtime_error("分配命令缓冲区失败");
}
return cmd_buffers[0];
}
void vulkan_thread_context::free_command_buffer(const std::shared_ptr<vulkan_queue>& queue, vk::CommandBuffer cmd) {
const auto pool = get_pool(queue);
const auto device = queue->get_device();
device.freeCommandBuffers(pool, 1, &cmd);
}
vk::CommandPool vulkan_thread_context::get_pool(const std::shared_ptr<vulkan_queue>& queue) {
const auto family_index = queue->get_family_index();
if (!pools_.contains(family_index)) {
// 创建新池子,允许重置单个 CommandBuffer
vk::CommandPoolCreateInfo info(
vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
family_index
);
const auto device = queue->get_device();
auto [result, pool] = device.createCommandPool(info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建命令池失败: {}", vk::to_string(result));
throw std::runtime_error("创建命令池失败");
}
pools_[family_index] = pool;
}
return pools_[family_index];
}
}

View File

@@ -1,62 +0,0 @@
#pragma once
#include "vulkan_device.h"
#include "vulkan_instance.h"
#include "core/object.h"
namespace mirai {
class vulkan_queue;
struct vulkan_context_init_config {
/// Vulkan 实例配置
vulkan_instance_config instance_config;
};
struct vulkan_context_config {
vk::SurfaceKHR surface;
};
class vulkan_context {
public:
static auto& get() noexcept {
static vulkan_context instance;
return instance;
}
void init(const vulkan_context_init_config& config);
void setup(const vulkan_context_config& config);
void shutdown();
std::shared_ptr<vulkan_device> create_device(const vulkan_device_config& info);
[[nodiscard]] auto get_vk_instance() const noexcept { return instance_->get_instance(); }
// 获取默认设备(第一个创建设备, 一定带有呈现队列)
[[nodiscard]] auto get_default_device() const noexcept -> std::shared_ptr<vulkan_device> {
if (devices_.empty()) {
return nullptr;
}
return devices_.front();
}
protected:
void create_default_device(const vk::SurfaceKHR& surface);
private:
vk_dynamic_loader dl;
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::CommandBuffer allocate_command_buffer(const std::shared_ptr<vulkan_queue>& queue, vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary);
void free_command_buffer(const std::shared_ptr<vulkan_queue>& queue, vk::CommandBuffer cmd);
vk::CommandPool get_pool(const std::shared_ptr<vulkan_queue>& queue);
private:
std::unordered_map<u32, vk::CommandPool> pools_;
};
}

View File

@@ -1,186 +0,0 @@
#include "vulkan_device.h"
#include "device_utils.h"
#include "core/logger.h"
#include "gpu_resource/allocator.h"
#include "gpu_resource/resource_manager.h"
#include "vulkan_fence.h"
#include "vulkan_semaphore.h"
#include "vulkan_time_semaphore.h"
namespace mirai {
vulkan_device::vulkan_device(const vulkan_device_config& config) {
physical_device_ = config.physical_device;
// 获取物理设备队列族信息(不需要 surface因为 vulkan_device 不依赖窗口)
auto queue_families = find_queue_families(physical_device_, {});
// 收集唯一的队列族索引
auto unique_queue_families = queue_families.get_unique_families();
// 创建设备队列创建信息
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
const float queue_priority = 1.0f;
for (const auto& family : unique_queue_families) {
vk::DeviceQueueCreateInfo queue_create_info{};
queue_create_info.setQueueFamilyIndex(family)
.setQueueCount(1)
.setPQueuePriorities(&queue_priority);
queue_create_infos.push_back(queue_create_info);
}
// 启用设备扩展
std::vector<const char*> enabled_extensions;
// 必需的交换链扩展
enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// 可选扩展(根据设备支持情况)
enabled_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
enabled_extensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
enabled_extensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
// 获取物理设备特性以检查支持的扩展
auto properties = physical_device_.getProperties();
auto features = physical_device_.getFeatures();
// 检查 Vulkan 1.3 特性支持
vk::PhysicalDeviceFeatures2 features2{};
vk::PhysicalDeviceVulkan11Features features_11{};
vk::PhysicalDeviceVulkan12Features features_12{};
vk::PhysicalDeviceVulkan13Features features_13{};
features_12.setPNext(&features_13);
features_11.setPNext(&features_12);
features2.setPNext(&features_11);
physical_device_.getFeatures2(&features2);
// 创建设备特性结构
vk::PhysicalDeviceVulkan12Features device_features_12{};
vk::PhysicalDeviceVulkan13Features device_features_13{};
// 配置 Vulkan 1.2 特性
device_features_12.setDescriptorIndexing(features_12.descriptorIndexing)
.setDescriptorBindingPartiallyBound(features_12.descriptorBindingPartiallyBound)
.setDescriptorBindingUpdateUnusedWhilePending(
features_12.descriptorBindingUpdateUnusedWhilePending)
.setDescriptorBindingVariableDescriptorCount(features_12.descriptorBindingVariableDescriptorCount)
.setRuntimeDescriptorArray(features_12.runtimeDescriptorArray)
.setBufferDeviceAddress(config.enable_buffer_device_address && features_12.bufferDeviceAddress)
.setTimelineSemaphore(features_12.timelineSemaphore);
// 配置 Vulkan 1.3 特性
device_features_13.setDynamicRendering(features_13.dynamicRendering)
.setSynchronization2(features_13.synchronization2)
.setInlineUniformBlock(features_13.inlineUniformBlock)
.setDescriptorBindingInlineUniformBlockUpdateAfterBind(
features_13.descriptorBindingInlineUniformBlockUpdateAfterBind)
.setMaintenance4(features_13.maintenance4);
// 设置 pNext 链
device_features_12.setPNext(&device_features_13);
// 创建设备创建信息
vk::DeviceCreateInfo device_create_info{};
device_create_info.setQueueCreateInfos(queue_create_infos)
.setPEnabledExtensionNames(enabled_extensions)
.setPNext(&device_features_12);
// 创建逻辑设备
auto [result, device] = physical_device_.createDevice(device_create_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建逻辑设备失败: {}", vk::to_string(result));
return;
}
device_ = device;
MIRAI_LOG_INFO("逻辑设备创建成功: {}", properties.deviceName.data());
// 初始化 dispatch loader
auto proc_addr = config.dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
disp_.init(proc_addr);
disp_.init(config.instance);
disp_.init(device_);
// 配置并初始化 VMA 分配器
allocator_config alloc_config{
config.instance,
physical_device_,
device_,
config.vulkan_api_version,
config.enable_buffer_device_address,
config.enable_memory_budget,
config.preferred_large_heap_block_size,
disp_
};
allocator_ = make_obj<vma_allocator>(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 分配器初始化完成");
}
std::shared_ptr<vulkan_fence> vulkan_device::create_fence() {
return make_obj<vulkan_fence>(shared_this());
}
std::shared_ptr<vulkan_semaphore> vulkan_device::create_semaphore() {
return make_obj<vulkan_semaphore>(shared_this());
}
std::shared_ptr<vulkan_time_semaphore> vulkan_device::create_timeline_semaphore(u32 initial_value) {
return make_obj<vulkan_time_semaphore>(shared_this(), initial_value);
}
void vulkan_device::request_destroy() {
graphics_queue_.reset();
present_queue_.reset();
compute_queue_.reset();
transfer_queue_.reset();
resource_manager_.reset();
allocator_.reset();
}
void vulkan_device::on_created() {
object::on_created();
resource_manager_ = make_obj<resource_manager>(shared_this());
}
void vulkan_device::on_destroying() {
object::on_destroying();
MIRAI_LOG_INFO("Vulkan 设备 {} 正在销毁", get_device_name());
// 销毁逻辑设备
if (device_) {
device_.destroy();
device_ = nullptr;
}
}
}

View File

@@ -1,78 +0,0 @@
#pragma once
#include "vulkan_types.h"
#include "core/object.h"
#include "vulkan_queue.h"
#include <vulkan/vulkan.hpp>
#include "gpu_resource/allocator.h"
#include "types/error.h"
namespace mirai {
class vulkan_time_semaphore;
class vulkan_semaphore;
class vulkan_fence;
class resource_manager;
class vma_allocator;
}
namespace mirai {
struct vulkan_device_config {
vk::Instance instance;
vk::PhysicalDevice physical_device;
/// Vulkan API 版本
u32 vulkan_api_version = VK_API_VERSION_1_3;
/// 是否启用 Buffer Device Address
bool enable_buffer_device_address = true;
/// 是否启用内存预算跟踪
bool enable_memory_budget = true;
/// 首选的大块分配大小(字节)
u64 preferred_large_heap_block_size = 256 * 1024 * 1024; // 256 MB
/// Vulkan 动态加载器引用
vk_dynamic_loader& dl;
};
class vulkan_device : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_device, object)
public:
vulkan_device(const vulkan_device_config& config);
[[nodiscard]] auto get_physical_device() const noexcept { return physical_device_; }
[[nodiscard]] auto& get_device() noexcept { return device_; }
[[nodiscard]] const auto& get_device() const noexcept { return device_; }
[[nodiscard]] auto& get_dispatch_loader() noexcept { return disp_; }
[[nodiscard]] const auto& get_dispatch_loader() const noexcept { return disp_; }
[[nodiscard]] std::string get_device_name() const noexcept { return physical_device_.getProperties().deviceName.data(); }
[[nodiscard]] const auto& get_allocator() const noexcept { return allocator_; }
[[nodiscard]] auto get_graphics_queue() const noexcept { return graphics_queue_; }
[[nodiscard]] auto get_present_queue() const noexcept { return present_queue_; }
[[nodiscard]] auto get_compute_queue() const noexcept { return compute_queue_; }
[[nodiscard]] auto get_transfer_queue() const noexcept { return transfer_queue_; }
[[nodiscard]] auto get_resource_manager() const noexcept { return resource_manager_; }
[[nodiscard]] std::shared_ptr<vulkan_fence> create_fence();
[[nodiscard]] std::shared_ptr<vulkan_semaphore> create_semaphore();
[[nodiscard]] std::shared_ptr<vulkan_time_semaphore> create_timeline_semaphore(u32 initial_value = 0);
void request_destroy();
protected:
void on_created() override;
void on_destroying() override;
private:
// 逻辑设备
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_;
// 保存物理设备映射
vk::PhysicalDevice physical_device_;
};
}

View File

@@ -1,30 +0,0 @@
#include "vulkan_fence.h"
#include "vulkan_device.h"
#include "core/logger.h"
namespace mirai {
vulkan_fence::vulkan_fence(const std::shared_ptr<vulkan_device>& device) {
vk::FenceCreateInfo info{};
auto [result, fence] = device->get_device().createFence(info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建 Vulkan 栅栏失败: {}", to_string(result));
return;
}
fence_ = fence;
}
vk::Result vulkan_fence::device_wait(bool wait_all, u64 timeout) const {
return device_->get_device().waitForFences(fence_, wait_all, timeout);
}
void vulkan_fence::on_destroying() {
object::on_destroying();
if (fence_) {
// 销毁 Vulkan 栅栏
device_->get_device().destroyFence(fence_);
}
}
}

View File

@@ -1,22 +0,0 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "core/object.h"
namespace mirai {
class vulkan_device;
class vulkan_fence : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_fence, object)
public:
vulkan_fence(const std::shared_ptr<vulkan_device>& device);
[[nodiscard]] auto get_fence() const noexcept { return fence_; }
vk::Result device_wait(bool wait_all = true, u64 timeout = UINT64_MAX) const;
protected:
void on_destroying() override;
private:
vk::Fence fence_;
std::shared_ptr<vulkan_device> device_;
};
}

View File

@@ -1,341 +0,0 @@
#include "vulkan_instance.h"
#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;
}
std::vector<vk::PhysicalDevice> vulkan_instance::enumerate_physical_devices() const {
if (!instance_) {
return {};
}
auto [result, devices] = instance_.enumeratePhysicalDevices();
if (result != vk::Result::eSuccess) {
return {};
}
return devices;
}
std::vector<physical_device_info> vulkan_instance::get_physical_devices_info(vk::SurfaceKHR surface) const {
auto devices = enumerate_physical_devices();
std::vector<physical_device_info> infos;
infos.reserve(devices.size());
for (const auto& device : devices) {
auto info = populate_physical_device(device, surface);
info.score = info.calculate_score();
infos.push_back(std::move(info));
}
auto pred = [](const physical_device_info& a, const physical_device_info& b) {
return a.score > b.score;
};
// 按评分排序(降序)
std::ranges::sort(infos, pred);
return infos;
}
void vulkan_instance::on_created() {
object::on_created();
auto result = create_instance(config_);
if (!result.has_value()) {
MIRAI_LOG_ERROR("无法创建Vulkan实例: {}", result.error().full_description());
return;
}
#if MIRAI_DEBUG
if (validation_enabled_) {
result = setup_debug_messenger();
if (!result.has_value()) {
MIRAI_LOG_WARN("调试信使设置失败: {}", result.error().full_description());
}
}
#endif
MIRAI_LOG_INFO("Vulkan 实例对象创建");
}
void vulkan_instance::on_destroying() {
object::on_destroying();
MIRAI_LOG_INFO("Vulkan 实例对象销毁");
#if MIRAI_DEBUG
if (debug_messenger_) {
destroy_debug_messenger();
}
#endif
if (instance_) {
instance_.destroy();
instance_ = nullptr;
}
}
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_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,
"请求的Vulkan API版本不受支持");
}
// 应用信息
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,
"所需的扩展不受支持: {}", 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,
"无法创建Vulkan实例: {}", vk::to_string(result));
}
instance_ = instance;
MIRAI_LOG_INFO("已使用Vulkan API版本创建设备实例 {}.{}.{}",
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, "无法加载 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, "未能创建Vulkan调试消息传递器: {}", vk::to_string(result));
}
debug_messenger_ = messenger;
MIRAI_LOG_DEBUG("Vulkan调试信使已创建完成");
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
}

View File

@@ -1,126 +0,0 @@
#pragma once
#include "core/object.h"
#include "types/error.h"
#include <vulkan/vulkan.hpp>
#include <functional>
#include "device_utils.h"
#include "vulkan_types.h"
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 = {});
auto get_instance() const { return instance_; }
// ============================================================================================
// 扩展和层查询
// ============================================================================================
/**
* @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 获取要求的 API 版本
* @return API 版本
*/
[[nodiscard]] u32 get_api_version() const noexcept { return api_version_; }
[[nodiscard]] std::vector<vk::PhysicalDevice> enumerate_physical_devices() const;
[[nodiscard]] std::vector<physical_device_info> get_physical_devices_info(vk::SurfaceKHR surface = {}) const;
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
};
}

View File

@@ -1,42 +0,0 @@
#include "render/vulkan_queue.h"
namespace mirai {
std::unordered_map<VkQueue, std::mutex> vulkan_queue::lock_map_{};
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);
return {};
}
void_result_t vulkan_present_queue::present(const vk::PresentInfoKHR& present_info) {
return {};
}
} // namespace mirai

View File

@@ -1,80 +0,0 @@
#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

View File

@@ -1,25 +0,0 @@
#include "vulkan_semaphore.h"
#include "vulkan_device.h"
#include "core/logger.h"
namespace mirai {
vulkan_semaphore::vulkan_semaphore(const std::shared_ptr<vulkan_device>& device) : device_(device) {
vk::SemaphoreCreateInfo info{};
auto [result, semaphore] = device_->get_device().createSemaphore(info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建 Vulkan 信号量失败: {}", to_string(result));
return;
}
semaphore_ = semaphore;
}
void vulkan_semaphore::on_destroying() {
object::on_destroying();
if (semaphore_) {
// 销毁 Vulkan 信号量
device_->get_device().destroySemaphore(semaphore_);
}
}
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include "core/object.h"
namespace mirai {
class vulkan_device;
class vulkan_semaphore : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_semaphore, object)
public:
vulkan_semaphore(const std::shared_ptr<vulkan_device>& device);
[[nodiscard]] auto get_semaphore() const noexcept { return semaphore_; }
protected:
void on_destroying() override;
private:
vk::Semaphore semaphore_;
std::shared_ptr<vulkan_device> device_;
};
}

View File

@@ -1,28 +0,0 @@
#include "vulkan_time_semaphore.h"
#include "vulkan_device.h"
#include "core/logger.h"
namespace mirai {
vulkan_time_semaphore::vulkan_time_semaphore(const std::shared_ptr<vulkan_device>& device, u32 initial_value) {
device_ = device;
vk::SemaphoreTypeCreateInfo type_info{ vk::SemaphoreType::eTimeline, initial_value };
vk::SemaphoreCreateInfo sem_info{};
sem_info.setPNext(&type_info);
auto [result, timeline_semaphore] = device_->get_device().createSemaphore(sem_info);
if (result != vk::Result::eSuccess) {
MIRAI_LOG_ERROR("创建 Vulkan 时间信号量失败: {}", to_string(result));
return;
}
semaphore_ = timeline_semaphore;
}
void vulkan_time_semaphore::on_destroying() {
object::on_destroying();
if (semaphore_) {
device_->get_device().destroySemaphore(semaphore_);
}
}
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include "core/object.h"
#include <vulkan/vulkan.hpp>
namespace mirai {
class vulkan_device;
class vulkan_time_semaphore : public object {
MIRAI_OBJECT_TYPE_INFO(vulkan_time_semaphore, object)
public:
vulkan_time_semaphore(const std::shared_ptr<vulkan_device>& device, u32 initial_value = 0);
[[nodiscard]] auto get_semaphore() const noexcept { return semaphore_; }
protected:
void on_destroying() override;
private:
std::shared_ptr<vulkan_device> device_;
vk::Semaphore semaphore_;
};
}

View File

@@ -1,11 +0,0 @@
#pragma once
#include <vulkan/vulkan.hpp>
#if VK_HEADER_VERSION >= 304
using vk_dynamic_loader = vk::detail::DynamicLoader;
using vk_dispatch_loader = vk::detail::DispatchLoaderDynamic;
#else
using vk_dynamic_loader = vk::DynamicLoader;
using vk_dispatch_loader = vk::DispatchLoaderDynamic;
#endif

130
src/render/webgpu-raii.hpp Normal file
View File

@@ -0,0 +1,130 @@
/**
* This is a RAII wrapper for WebGPU native API.
*
* This file is part of the "Learn WebGPU for C++" book.
* https://eliemichel.github.io/LearnWebGPU
*
* MIT License
* Copyright (c) 2022-2025 Elie Michel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <webgpu/webgpu.hpp>
#include <type_traits>
// Emscripten still uses the old 'reference' method, which has been renamed 'addRef'
#ifdef __EMSCRIPTEN__
# define addRef reference
#endif
namespace wgpu::raii {
/**
* RAII wrapper around a raw WebGPU type.
* Use pointer-like dereferencing to access methods from the wrapped type.
*/
template <typename Raw>
class Wrapper {
public:
Wrapper() : m_raw(nullptr) {
}
Wrapper(Raw&& raw) : m_raw(raw) {
}
// We define a destructor...
~Wrapper() {
Destruct();
}
// Copy semantics
Wrapper& operator=(const Wrapper& other) {
Destruct();
assert(m_raw == nullptr);
m_raw = other.m_raw;
if (m_raw != nullptr) {
m_raw.addRef();
}
return *this;
}
Wrapper(const Wrapper& other) : m_raw(other.m_raw) {
if (m_raw != nullptr) {
m_raw.addRef();
}
}
// Move semantics
Wrapper& operator=(Wrapper&& other) {
Destruct();
assert(m_raw == nullptr);
m_raw = other.m_raw;
other.m_raw = nullptr;
return *this;
}
Wrapper(Wrapper&& other) : m_raw(other.m_raw) {
other.m_raw = nullptr;
}
operator bool() const { return m_raw; }
const Raw& operator*() const { return m_raw; }
Raw& operator*() { return m_raw; }
const Raw* operator->() const { return &m_raw; }
Raw* operator->() { return &m_raw; }
private:
void Destruct() {
if (!m_raw)
return;
m_raw.release();
m_raw = nullptr;
}
private:
// Raw resources that is wrapped by the RAII class
Raw m_raw;
};
using Adapter = Wrapper<Adapter>;
using BindGroup = Wrapper<BindGroup>;
using BindGroupLayout = Wrapper<BindGroupLayout>;
using Buffer = Wrapper<Buffer>;
using CommandBuffer = Wrapper<CommandBuffer>;
using CommandEncoder = Wrapper<CommandEncoder>;
using ComputePassEncoder = Wrapper<ComputePassEncoder>;
using ComputePipeline = Wrapper<ComputePipeline>;
using Device = Wrapper<Device>;
using Instance = Wrapper<Instance>;
using PipelineLayout = Wrapper<PipelineLayout>;
using QuerySet = Wrapper<QuerySet>;
using Queue = Wrapper<Queue>;
using RenderBundle = Wrapper<RenderBundle>;
using RenderBundleEncoder = Wrapper<RenderBundleEncoder>;
using RenderPassEncoder = Wrapper<RenderPassEncoder>;
using RenderPipeline = Wrapper<RenderPipeline>;
using Sampler = Wrapper<Sampler>;
using ShaderModule = Wrapper<ShaderModule>;
using Surface = Wrapper<Surface>;
using Texture = Wrapper<Texture>;
using TextureView = Wrapper<TextureView>;
} // namespace wgpu::raii

View File

@@ -0,0 +1,2 @@
#define WEBGPU_CPP_IMPLEMENTATION
#include <webgpu/webgpu.hpp>

View File

@@ -334,14 +334,18 @@ 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__), \
#define MAKE_ERROR_INFO(ec, ...) \
std::unexpected( \
error_info( \
ec, fmt::format(__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( \
#define MAKE_SUCCESS_INFO() \
error_info( \
error_code::success, \
"", \
std::source_location::current().file_name(), \

View File

@@ -5,8 +5,6 @@
#include <utility>
#include "core/logger.h"
#include "gpu_resource/swapchain.h"
#include "render/vulkan_context.h"
namespace mirai {
void_result_t window::make_window(window_config config) {
@@ -25,13 +23,7 @@ namespace mirai {
}
if (config.vulkan_window) {
VkSurfaceKHR surface;
if (!SDL_Vulkan_CreateSurface(win_ptr, vulkan_context::get().get_vk_instance(), nullptr, &surface)) {
SDL_DestroyWindow(win_ptr);
return MAKE_ERROR_INFO(error_code::window_creation_failed,
"SDL_Vulkan_CreateSurface 失败: {}", SDL_GetError());
}
surface_ = surface;
}
if (!SDL_SetWindowTitle(win_ptr, config.title.c_str())) {
@@ -109,20 +101,10 @@ namespace mirai {
}
bool window::need_rebuild_swapchain() const {
if (!swapchain_)
return true;
return false;
}
void_result_t window::rebuild_swapchain(bool hdr_enabled, vec2i new_extent) {
swapchain_create_info info{};
info.surface = surface_;
info.hdr_enabled = hdr_enabled;
info.initial_extent = std::move(new_extent);
swapchain_ = make_obj<swapchain>(info);
if (!swapchain_) {
return MAKE_ERROR_INFO(error_code::swapchain_creation_failed, "交换链创建失败");
}
return {};
}
@@ -132,9 +114,7 @@ namespace mirai {
void window::on_destroying() {
object::on_destroying();
swapchain_.reset();
MIRAI_LOG_INFO("窗口 {} 销毁", get_window_title());
SDL_Vulkan_DestroySurface(vulkan_context::get().get_vk_instance(), surface_, nullptr);
SDL_DestroyWindow(window_);
window_ = nullptr;
}

View File

@@ -3,11 +3,6 @@
#include "types/error.h"
#include <SDL3/SDL.h>
#include <vulkan/vulkan.hpp>
namespace mirai {
class swapchain;
}
namespace mirai {
struct window_config {
@@ -43,7 +38,6 @@ namespace mirai {
auto is_closing() const {
return closing_;
}
auto get_vk_surface() const { return surface_; }
bool need_rebuild_swapchain() const;
void_result_t rebuild_swapchain(bool hdr_enabled, vec2i new_extent);
@@ -55,8 +49,6 @@ namespace mirai {
void on_destroying() override;
private:
SDL_Window* window_{};
vk::SurfaceKHR surface_{};
std::shared_ptr<swapchain> swapchain_{};
bool closing_{false};
bool hdr_enabled_{false};
};

View File

@@ -1,7 +1,5 @@
#include "window_manager.h"
#include <SDL3/SDL_vulkan.h>
#include "core/logger.h"
namespace mirai {
@@ -11,11 +9,6 @@ namespace mirai {
MIRAI_LOG_ERROR("初始化SDL失败: {}", SDL_GetError());
return;
}
// 检查是否已经加载了 Vulkan 库
if (!SDL_Vulkan_LoadLibrary(nullptr)) {
MIRAI_LOG_ERROR("加载 Vulkan 库失败: {}", SDL_GetError());
return;
}
}
void window_manager::setup(const window_manager_config& config) {

1
third_party/SDL vendored Submodule

Submodule third_party/SDL added at 3f0e0975d8

1
third_party/eigen vendored Submodule

Submodule third_party/eigen added at 918a5f1a6f

1
third_party/fmt vendored Submodule

Submodule third_party/fmt added at a9ea225cad

1
third_party/freetype vendored Submodule

Submodule third_party/freetype added at ef04e4eb20

1
third_party/harfbuzz vendored Submodule

Submodule third_party/harfbuzz added at a603a37294

1
third_party/json vendored Submodule

Submodule third_party/json added at 5ed07097fa

1
third_party/spdlog vendored Submodule

Submodule third_party/spdlog added at 472945ba48

4
third_party/stb/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,4 @@
project(stb)
add_library(stb INTERFACE)
target_link_directories(stb INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/stb)

1
third_party/stb/stb vendored Submodule

Submodule third_party/stb/stb added at f1c79c0282

1
third_party/yoga vendored Submodule

Submodule third_party/yoga added at 8ba025e8b1