去他妈的Vulkan,这东西就不是人用的
This commit is contained in:
27
.gitmodules
vendored
27
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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
93
cmake/webgpu.cmake
Normal 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()
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -22,7 +22,6 @@ enum class property_flags : u32 {
|
||||
required = 1 << 5,
|
||||
deprecated = 1 << 6,
|
||||
};
|
||||
|
||||
MIRAI_FLAG_ENUM(property_flags)
|
||||
|
||||
struct property_change_event {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
|
||||
namespace mirai {
|
||||
void set_thread_name(std::jthread& t, const std::string& name) {
|
||||
// 将std::string转换为std::wstring
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include "gpu_resource.h"
|
||||
|
||||
namespace mirai {
|
||||
}
|
||||
@@ -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_{};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
}
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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_{};
|
||||
};
|
||||
}
|
||||
@@ -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, ¤t_val);
|
||||
return (result == vk::Result::eSuccess) && (current_val >= target_value);
|
||||
}
|
||||
|
||||
// 2. 挂起时的逻辑
|
||||
// 当 await_ready 返回 false 时调用,handle 是编译器生成的协程句柄
|
||||
void await_suspend(std::coroutine_handle<> handle) noexcept {
|
||||
// 将此协程注册到管理器的任务池中
|
||||
// 管理器线程会轮询这个信号量,并在满足 target_value 时调用 handle.resume()
|
||||
device->get_resource_manager()->add_tracking_task(semaphore, target_value, handle);
|
||||
}
|
||||
|
||||
// 3. 恢复时的逻辑
|
||||
// 当协程被 resume() 唤醒后,这里执行的代码是回到原函数后的第一行
|
||||
void await_resume() const noexcept {
|
||||
// 这里可以返回结果,比如一个错误码,但对于上传任务,通常返回 void
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
resource_manager::resource_manager(const std::shared_ptr<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();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
}
|
||||
@@ -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 简单的低精度法线贴图 (仅存XY,Z重建)
|
||||
rg8_snorm,
|
||||
/// @brief 双通道 8位 无符号整数
|
||||
rg8_uint,
|
||||
/// @brief 双通道 8位 有符号整数
|
||||
rg8_sint,
|
||||
|
||||
/// @brief 四通道 8位 无符号归一化 (红绿蓝透)
|
||||
/// @usage 最通用的颜色纹理、漫反射贴图 (Diffuse/Albedo)、UI 纹理
|
||||
rgba8_unorm,
|
||||
/// @brief 四通道 8位 有符号归一化
|
||||
rgba8_snorm,
|
||||
/// @brief 四通道 8位 无符号整数
|
||||
rgba8_uint,
|
||||
/// @brief 四通道 8位 有符号整数
|
||||
rgba8_sint,
|
||||
|
||||
/// @brief 四通道 8位 无符号归一化 (蓝绿红透)
|
||||
/// @usage Windows 平台的 Swapchain (交换链) 默认首选格式,因为历史原因显存通常以 BGR 顺序存储
|
||||
bgra8_unorm,
|
||||
/// @brief 四通道 8位 sRGB 非线性空间
|
||||
/// @usage 需要进行伽马校正的 Swapchain 呈现图像
|
||||
bgra8_srgb,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 16位格式 (每个分量16位) ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 单通道 16位 无符号归一化 [0, 1]
|
||||
/// @usage 高精度高度图 (地形渲染),避免梯田效应
|
||||
r16_unorm,
|
||||
/// @brief 单通道 16位 有符号归一化 [-1, 1]
|
||||
r16_snorm,
|
||||
/// @brief 单通道 16位 无符号整数
|
||||
r16_uint,
|
||||
/// @brief 单通道 16位 有符号整数
|
||||
r16_sint,
|
||||
/// @brief 单通道 16位 浮点数 (Half Float)
|
||||
/// @usage 节省显存的 HDR 单通道数据
|
||||
r16_float,
|
||||
|
||||
/// @brief 双通道 16位 无符号归一化
|
||||
/// @usage 高精度 UV 坐标存储
|
||||
rg16_unorm,
|
||||
/// @brief 双通道 16位 有符号归一化
|
||||
/// @usage 高品质法线贴图 (避免 8-bit 法线的波纹瑕疵)
|
||||
rg16_snorm,
|
||||
/// @brief 双通道 16位 无符号整数
|
||||
rg16_uint,
|
||||
/// @brief 双通道 16位 有符号整数
|
||||
rg16_sint,
|
||||
/// @brief 双通道 16位 浮点数
|
||||
/// @usage 速度向量 (Velocity Buffer) 用于动态模糊或 TAA
|
||||
rg16_float,
|
||||
|
||||
/// @brief 四通道 16位 无符号归一化
|
||||
rgba16_unorm,
|
||||
/// @brief 四通道 16位 有符号归一化
|
||||
rgba16_snorm,
|
||||
/// @brief 四通道 16位 无符号整数
|
||||
rgba16_uint,
|
||||
/// @brief 四通道 16位 有符号整数
|
||||
rgba16_sint,
|
||||
/// @brief 四通道 16位 浮点数 (Half Float)
|
||||
/// @usage 标准的 HDR 场景渲染目标 (Scene Color),兼顾精度与带宽
|
||||
rgba16_float,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 32位格式 (每个分量32位) ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 单通道 32位 无符号整数
|
||||
/// @usage 原子计数器、大范围索引、GPGPU 数据
|
||||
r32_uint,
|
||||
/// @brief 单通道 32位 有符号整数
|
||||
r32_sint,
|
||||
/// @brief 单通道 32位 浮点数
|
||||
/// @usage 深度纹理 (线性深度)、阴影贴图 (VSM)、科学计算数据
|
||||
r32_float,
|
||||
|
||||
/// @brief 双通道 32位 无符号整数
|
||||
rg32_uint,
|
||||
/// @brief 双通道 32位 有符号整数
|
||||
rg32_sint,
|
||||
/// @brief 双通道 32位 浮点数
|
||||
/// @usage 高精度复杂 UV 或 2D 坐标数据
|
||||
rg32_float,
|
||||
|
||||
/// @brief 三通道 32位 无符号整数
|
||||
/// @note 显卡对 3通道 (RGB) 格式支持较差,通常会有对齐填充,尽量使用 RGBA
|
||||
rgb32_uint,
|
||||
rgb32_sint,
|
||||
rgb32_float,
|
||||
|
||||
/// @brief 四通道 32位 无符号整数
|
||||
rgba32_uint,
|
||||
/// @brief 四通道 32位 有符号整数
|
||||
rgba32_sint,
|
||||
/// @brief 四通道 32位 浮点数
|
||||
/// @usage G-Buffer 中的世界坐标 (Position Buffer),或需要极高精度的物理模拟数据
|
||||
rgba32_float,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 深度/模板格式 ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief 16位 深度归一化 [0, 1]
|
||||
/// @usage 节省显存的阴影贴图 (Shadow Map)
|
||||
depth16_unorm,
|
||||
/// @brief 24位 深度归一化 (通常打包在32位中)
|
||||
depth24_unorm,
|
||||
/// @brief 32位 浮点深度
|
||||
/// @usage 现代桌面端游戏的标准深度缓冲格式 (Reverse Z 友好)
|
||||
depth32_float,
|
||||
/// @brief 24位 深度 + 8位 模板
|
||||
/// @usage 经典的深度模板组合,适用于大多数需要模板测试的渲染
|
||||
depth24_unorm_stencil8_uint,
|
||||
/// @brief 32位 浮点深度 + 8位 模板
|
||||
depth32_float_stencil8_uint,
|
||||
/// @brief 仅 8位 模板
|
||||
stencil8_uint,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 压缩格式 (Block Compression) ----
|
||||
// BC 格式通过压缩 4x4 像素块来大幅减少显存占用 (通常减少 4-6 倍)
|
||||
// =================================================================================
|
||||
|
||||
/// @brief BC1 (DXT1) RGB, 无 Alpha 或 1位 Alpha
|
||||
/// @usage 不透明的颜色贴图 (Albedo),如石头、墙壁
|
||||
bc1_rgb_unorm,
|
||||
/// @brief BC1 (DXT1) sRGB
|
||||
/// @usage 需要 sRGB 校正的不透明颜色贴图
|
||||
bc1_rgb_srgb,
|
||||
/// @brief BC1 (DXT1) RGBA
|
||||
bc1_rgba_unorm,
|
||||
/// @brief BC1 (DXT1) sRGBA
|
||||
bc1_rgba_srgb,
|
||||
|
||||
/// @brief BC2 (DXT3) 显式 Alpha (4位清晰度)
|
||||
/// @usage 具有急剧边缘 Alpha 的纹理 (如铁丝网),现代引擎较少使用
|
||||
bc2_unorm,
|
||||
/// @brief BC2 (DXT3) sRGB
|
||||
bc2_srgb,
|
||||
|
||||
/// @brief BC3 (DXT5) 插值 Alpha
|
||||
/// @usage 最常用的透明贴图格式 (如植被叶子、半透明玻璃)。Alpha 渐变平滑
|
||||
bc3_unorm,
|
||||
/// @brief BC3 (DXT5) sRGB
|
||||
bc3_srgb,
|
||||
|
||||
/// @brief BC4 (ATI1) 单通道
|
||||
/// @usage 压缩的高度图、高光强度图 (Gloss)、环境光遮蔽 (AO)
|
||||
bc4_unorm,
|
||||
/// @brief BC4 (ATI1) 单通道 有符号
|
||||
bc4_snorm,
|
||||
|
||||
/// @brief BC5 (ATI2/3Dc) 双通道
|
||||
/// @usage **法线贴图的标准压缩格式** (存储 X, Y,在 Shader 中重建 Z)
|
||||
bc5_unorm,
|
||||
/// @brief BC5 双通道 有符号
|
||||
bc5_snorm,
|
||||
|
||||
/// @brief BC6H 无符号浮点 (HDR)
|
||||
/// @usage HDR 环境贴图 (Skybox)、光照探针 (Light Probe)。不带 Alpha。
|
||||
bc6h_ufloat,
|
||||
/// @brief BC6H 有符号浮点 (HDR)
|
||||
bc6h_sfloat,
|
||||
|
||||
/// @brief BC7 高质量 RGBA
|
||||
/// @usage 现代 GPU 的首选压缩格式。比 BC1/3 质量更好,伪影更少。适用于所有类型的颜色/Alpha 贴图。
|
||||
bc7_unorm,
|
||||
/// @brief BC7 高质量 sRGBA
|
||||
bc7_srgb,
|
||||
|
||||
// =================================================================================
|
||||
// ---- 特殊/组合格式 ----
|
||||
// =================================================================================
|
||||
|
||||
/// @brief R11 G11 B10 浮点数 (无符号,无 Alpha)
|
||||
/// @details 总共32位 (11+11+10)。只能存储正数。
|
||||
/// @usage 极高效率的 HDR 颜色缓冲。常用于后期处理链 (Post-processing) 或无需 Alpha 的场景渲染。
|
||||
/// 比 rgba16_float 节省一半显存带宽。
|
||||
r11g11b10_float,
|
||||
|
||||
/// @brief RGB9 E5 共享指数浮点数
|
||||
/// @details 3个通道共享 5位指数。
|
||||
/// @usage 预计算的 HDR 数据,如天空盒或全局光照体积。不支持渲染目标 (Render Target) 输出。
|
||||
rgb9e5_float,
|
||||
|
||||
/// @brief RGB 10位 + Alpha 2位 无符号归一化
|
||||
/// @details 总共32位 (10+10+10+2)。
|
||||
/// @usage G-Buffer 法线存储 (比 8位精度高,比 16位省空间),或广色域 (HDR10) 输出。
|
||||
rgb10a2_unorm,
|
||||
|
||||
/// @brief RGB 10位 + Alpha 2位 无符号整数
|
||||
/// @usage 紧凑的数据打包 (如将 Mesh ID 和 Material ID 压入同一个 buffer)
|
||||
rgb10a2_uint,
|
||||
};
|
||||
|
||||
enum class texture_type : u32 {
|
||||
texture_1d = 0,
|
||||
texture_2d = 1,
|
||||
texture_3d = 2,
|
||||
texture_cube = 3,
|
||||
texture_1d_array = 4,
|
||||
texture_2d_array = 5,
|
||||
texture_cube_array = 6,
|
||||
};
|
||||
|
||||
enum class texture_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;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include "texture3d.h"
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class texture3d {
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
@@ -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, ©_region);
|
||||
}
|
||||
|
||||
void copy_buffer(vk::Buffer src, vk::Buffer dst, const vk::BufferCopy& copy_region) const {
|
||||
cmd_.copyBuffer(src, dst, 1, ©_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_;
|
||||
};
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
}
|
||||
@@ -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
130
src/render/webgpu-raii.hpp
Normal 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
|
||||
2
src/render/webgpu_impl.cpp
Normal file
2
src/render/webgpu_impl.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define WEBGPU_CPP_IMPLEMENTATION
|
||||
#include <webgpu/webgpu.hpp>
|
||||
@@ -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(), \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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
1
third_party/SDL
vendored
Submodule
Submodule third_party/SDL added at 3f0e0975d8
1
third_party/eigen
vendored
Submodule
1
third_party/eigen
vendored
Submodule
Submodule third_party/eigen added at 918a5f1a6f
1
third_party/fmt
vendored
Submodule
1
third_party/fmt
vendored
Submodule
Submodule third_party/fmt added at a9ea225cad
1
third_party/freetype
vendored
Submodule
1
third_party/freetype
vendored
Submodule
Submodule third_party/freetype added at ef04e4eb20
1
third_party/harfbuzz
vendored
Submodule
1
third_party/harfbuzz
vendored
Submodule
Submodule third_party/harfbuzz added at a603a37294
1
third_party/json
vendored
Submodule
1
third_party/json
vendored
Submodule
Submodule third_party/json added at 5ed07097fa
1
third_party/spdlog
vendored
Submodule
1
third_party/spdlog
vendored
Submodule
Submodule third_party/spdlog added at 472945ba48
4
third_party/stb/CMakeLists.txt
vendored
Normal file
4
third_party/stb/CMakeLists.txt
vendored
Normal 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
1
third_party/stb/stb
vendored
Submodule
Submodule third_party/stb/stb added at f1c79c0282
1
third_party/yoga
vendored
Submodule
1
third_party/yoga
vendored
Submodule
Submodule third_party/yoga added at 8ba025e8b1
Reference in New Issue
Block a user