Compare commits
5 Commits
735f1a08b6
...
08aff2e960
| Author | SHA1 | Date | |
|---|---|---|---|
| 08aff2e960 | |||
| e4fc3f2bd8 | |||
| 1b09a2f354 | |||
| 7d3d474ab2 | |||
| d45ae1d436 |
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,37 @@ 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
|
||||
)
|
||||
configure_project_defaults()
|
||||
|
||||
set(SDL_STATIC ON CACHE BOOL "Build SDL as a static library")
|
||||
set(SPDLOG_FMT_EXTERNAL OFF CACHE BOOL "Use external fmt library in spdlog")
|
||||
set(SPDLOG_FMT_EXTERNAL_HO ON CACHE BOOL "Use header-only external fmt library in spdlog")
|
||||
|
||||
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,47 @@
|
||||
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
|
||||
SDL3::SDL3-static
|
||||
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 ()
|
||||
|
||||
if (APPLE)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -x objective-c)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework CoreVideo" "-framework IOKit" "-framework QuartzCore")
|
||||
|
||||
if (IOS)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework UIKit")
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework Cocoa")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "core/time_util.h"
|
||||
#include "render/wgpu_context.h"
|
||||
|
||||
namespace mirai {
|
||||
bool mirai_app::setup(const mirai_app_config& config) {
|
||||
window_mgr_ = make_obj<window_manager>();
|
||||
|
||||
vulkan_context_init_config context_init_config{config.vulkan_instance_cfg};
|
||||
vulkan_context::get().init(context_init_config);
|
||||
|
||||
wgpu_context::instance().setup();
|
||||
window_mgr_->setup(config.window_mgr_config);
|
||||
wgpu_context::instance().create_default_device(window_mgr_->get_main_window()->get_wgpu_surface());
|
||||
|
||||
vulkan_context_config context_config{window_mgr_->get_main_window()->get_vk_surface()};
|
||||
vulkan_context::get().setup(context_config);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mirai_app::shutdown() {
|
||||
window_mgr_.reset();
|
||||
wgpu_context::instance().shutdown();
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
int mirai_app::run() {
|
||||
update_time();
|
||||
|
||||
@@ -24,6 +28,7 @@ namespace mirai {
|
||||
auto delta_time = get_delta_time();
|
||||
|
||||
window_mgr_->update(delta_time);
|
||||
wgpu_context::instance().update();
|
||||
|
||||
update_time();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
#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 {
|
||||
public:
|
||||
bool setup(const mirai_app_config& config);
|
||||
|
||||
void shutdown() {
|
||||
window_mgr_.reset();
|
||||
vulkan_context::get().shutdown();
|
||||
}
|
||||
void shutdown();
|
||||
|
||||
int run();
|
||||
private:
|
||||
|
||||
98
src/core/fixed_size_function.h
Normal file
98
src/core/fixed_size_function.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <expected>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
template <typename Signature, size_t MaxSize = 32>
|
||||
class fixed_size_function;
|
||||
|
||||
template <typename Ret, typename... Args, size_t MaxSize>
|
||||
class fixed_size_function<Ret(Args...), MaxSize> {
|
||||
private:
|
||||
// 对齐缓冲区
|
||||
alignas(std::max_align_t) std::byte buffer[MaxSize];
|
||||
|
||||
struct v_table {
|
||||
Ret (*call)(const void*, Args&&...);
|
||||
void (*destroy)(void*);
|
||||
void (*move)(void* dest, void* src);
|
||||
};
|
||||
|
||||
const v_table* vtable = nullptr;
|
||||
|
||||
template <typename F>
|
||||
static Ret call_impl(const void* ptr, Args&&... args) {
|
||||
// 使用 const_cast 仅在必要时(如果 F 的 operator() 是非 const 的)
|
||||
// 实际上 std::function 默认也是要求 F 是可调用的
|
||||
return (*static_cast<const F*>(ptr))(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static void destroy_impl(void* ptr) {
|
||||
if constexpr (!std::is_trivially_destructible_v<F>) {
|
||||
std::destroy_at(static_cast<F*>(ptr)); // C++17/20 更好的写法
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static void move_impl(void* dest, void* src) {
|
||||
if constexpr (std::is_trivially_copyable_v<F>) {
|
||||
std::memcpy(dest, src, sizeof(F));
|
||||
} else {
|
||||
new (dest) F(std::move(*static_cast<F*>(src)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static constexpr v_table vtable_for = { call_impl<F>, destroy_impl<F>, move_impl<F> };
|
||||
|
||||
public:
|
||||
fixed_size_function() noexcept = default;
|
||||
|
||||
// 使用 C++20 constraints (requires) 替代 std::enable_if
|
||||
template <typename F>
|
||||
requires (!std::is_same_v<std::remove_cvref_t<F>, fixed_size_function> &&
|
||||
std::is_invocable_r_v<Ret, F, Args...>)
|
||||
fixed_size_function(F&& f) {
|
||||
using functor = std::remove_cvref_t<F>;
|
||||
static_assert(sizeof(functor) <= MaxSize, "函数体过大");
|
||||
|
||||
new (buffer) functor(std::forward<F>(f));
|
||||
vtable = &vtable_for<functor>;
|
||||
}
|
||||
|
||||
~fixed_size_function() {
|
||||
if (vtable) vtable->destroy(buffer);
|
||||
}
|
||||
|
||||
// 移动构造
|
||||
fixed_size_function(fixed_size_function&& other) noexcept {
|
||||
if (other.vtable) {
|
||||
other.vtable->move(buffer, other.buffer);
|
||||
vtable = other.vtable;
|
||||
other.vtable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// C++23 deducing this 优化调用逻辑
|
||||
// 我们可以捕捉当前的 const 属性和值类别
|
||||
template <typename Self>
|
||||
Ret operator()(this Self&& self, Args... args) {
|
||||
if (!self.vtable) [[unlikely]] { // C++20 属性优化分支预测
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
return self.vtable->call(self.buffer, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// C++23 可以考虑返回 std::expected 来避免异常
|
||||
// 这里为了保持 operator() 习惯保留了异常,但可以增加 try_call
|
||||
std::expected<Ret, std::string_view> try_call(Args... args) const {
|
||||
if (!vtable) return std::unexpected("空函数");
|
||||
return vtable->call(buffer, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return vtable != nullptr; }
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -45,14 +45,18 @@ namespace mirai {
|
||||
|
||||
struct logger_config {
|
||||
std::string name{"mirai"};
|
||||
#if MIRAI_DEBUG
|
||||
log_level level{log_level::trace};
|
||||
#else
|
||||
log_level level{log_level::info};
|
||||
#endif
|
||||
bool console_enabled{true};
|
||||
bool file_enabled{true};
|
||||
std::filesystem::path file_path{"logs/mirai.log"};
|
||||
bool file_rotation_enabled{true};
|
||||
size_type max_file_size{10 * 1024 * 1024}; // 10 MB
|
||||
size_type max_files{5};
|
||||
std::string pattern{"[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v"};
|
||||
std::string pattern{"[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v"};
|
||||
bool include_source_location{true};
|
||||
bool async_enabled{false};
|
||||
size_type async_queue_size{8192};
|
||||
@@ -73,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)) {
|
||||
@@ -179,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 格式参数
|
||||
*/
|
||||
template<typename... Args>
|
||||
void log_trace(fmt::format_string<Args...> fmt, Args&&... args) {
|
||||
if (auto logger = get_logger()) {
|
||||
logger->trace(fmt, std::forward<Args>(args)...);
|
||||
/**
|
||||
* @brief 实际执行日志记录的底层函数
|
||||
*/
|
||||
template<typename... Args>
|
||||
void do_log(log_level level, fmt::format_string<Args...> fmt, Args&&... args) {
|
||||
if (auto logger = get_logger()) {
|
||||
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) {
|
||||
if (auto logger = get_logger()) {
|
||||
logger->debug(fmt, std::forward<Args>(args)...);
|
||||
template<typename... 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()) {
|
||||
if (logger->should_log(level)) {
|
||||
auto user_message = fmt::format(fmt, std::forward<Args>(args)...);
|
||||
logger->log(level, "[{}] {}", prefix, user_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)...);
|
||||
}
|
||||
}
|
||||
} // 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:
|
||||
@@ -218,11 +218,11 @@ namespace mirai {
|
||||
[[nodiscard]] constexpr virtual const mirai::type_info* get_parent_type() const noexcept override { \
|
||||
return &mirai::get_type_info<parent_t>(); \
|
||||
} \
|
||||
[[nodiscard]] constexpr auto shared_this() noexcept -> std::shared_ptr<class_name> { \
|
||||
return std::static_pointer_cast<class_name>(mirai::object::shared_from_this()); \
|
||||
[[nodiscard]] auto shared_this() noexcept -> std::shared_ptr<class_name> { \
|
||||
return std::static_pointer_cast<class_name>(shared_from_this()); \
|
||||
} \
|
||||
[[nodiscard]] constexpr auto shared_this() const noexcept -> std::shared_ptr<const class_name> { \
|
||||
return std::static_pointer_cast<const class_name>(mirai::object::shared_from_this()); \
|
||||
[[nodiscard]] auto shared_this() const noexcept -> std::shared_ptr<const class_name> { \
|
||||
return std::static_pointer_cast<const class_name>(shared_from_this()); \
|
||||
} \
|
||||
friend struct mirai::object_factory; \
|
||||
template<typename T_> \
|
||||
|
||||
@@ -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,122 +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_queue;
|
||||
|
||||
/**
|
||||
* @brief GPU 分配信息
|
||||
*
|
||||
* 包含单个内存分配的详细信息
|
||||
*/
|
||||
struct allocation_info {
|
||||
/// 分配的大小(字节)
|
||||
u64 size = 0;
|
||||
/// 分配的偏移量
|
||||
u64 offset = 0;
|
||||
/// 设备内存句柄
|
||||
vk::DeviceMemory device_memory{};
|
||||
/// 映射的指针(如果已映射)
|
||||
void* mapped_data = nullptr;
|
||||
/// 内存类型索引
|
||||
u32 memory_type_index = 0;
|
||||
/// 是否为专用分配
|
||||
bool is_dedicated = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Buffer 创建信息
|
||||
*/
|
||||
struct buffer_create_info {
|
||||
/// Buffer 大小(字节)
|
||||
u64 size = 0;
|
||||
/// Buffer 用途
|
||||
buffer_usage usage = buffer_usage::vertex;
|
||||
/// 内存用途
|
||||
memory_usage mem_usage = memory_usage::gpu_only;
|
||||
/// 资源共享模式
|
||||
resource_sharing_mode sharing_mode = resource_sharing_mode::exclusive;
|
||||
/// 是否持久映射
|
||||
bool persistent_mapped = false;
|
||||
/// 调试名称
|
||||
std::string debug_name;
|
||||
};
|
||||
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;
|
||||
/// 是否有效
|
||||
[[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,165 +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();
|
||||
}
|
||||
|
||||
vma::MemoryUsage vma_allocator::to_vma_memory_usage(memory_usage usage) noexcept {
|
||||
switch (usage) {
|
||||
case memory_usage::gpu_only:
|
||||
return vma::MemoryUsage::eGpuOnly;
|
||||
case memory_usage::cpu_only:
|
||||
return vma::MemoryUsage::eCpuOnly;
|
||||
case memory_usage::cpu_to_gpu:
|
||||
return vma::MemoryUsage::eCpuToGpu;
|
||||
case memory_usage::gpu_to_cpu:
|
||||
return vma::MemoryUsage::eGpuToCpu;
|
||||
case memory_usage::auto_prefer_device:
|
||||
return vma::MemoryUsage::eAutoPreferDevice;
|
||||
case memory_usage::auto_prefer_host:
|
||||
return vma::MemoryUsage::eAutoPreferHost;
|
||||
default:
|
||||
return vma::MemoryUsage::eAuto;
|
||||
}
|
||||
}
|
||||
|
||||
vma::AllocationCreateFlags
|
||||
vma_allocator::to_vma_allocation_flags(memory_usage usage, bool persistent_mapped) noexcept {
|
||||
vma::AllocationCreateFlags flags;
|
||||
|
||||
// 持久映射
|
||||
if (persistent_mapped) {
|
||||
flags |= vma::AllocationCreateFlagBits::eMapped;
|
||||
}
|
||||
|
||||
// 根据内存用途设置访问模式
|
||||
switch (usage) {
|
||||
case memory_usage::cpu_only:
|
||||
case memory_usage::cpu_to_gpu:
|
||||
flags |= vma::AllocationCreateFlagBits::eHostAccessSequentialWrite;
|
||||
break;
|
||||
case memory_usage::gpu_to_cpu:
|
||||
flags |= vma::AllocationCreateFlagBits::eHostAccessRandom;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
result_t<buffer_allocation> vma_allocator::alloc_buffer(const buffer_create_info& info) {
|
||||
buffer_allocation alloc;
|
||||
|
||||
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 = to_vma_memory_usage(info.mem_usage);
|
||||
alloc_info.flags = to_vma_allocation_flags(info.mem_usage, info.persistent_mapped);
|
||||
|
||||
vma::AllocationInfo vma_alloc_info{};
|
||||
vk::Result vk_result;
|
||||
vk::Buffer vk_buffer;
|
||||
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
// vk_result = vma::UniqueBuffer(vma_allocator_, &buffer_info, &alloc_info, reinterpret_cast<VkBuffer*>(&vk_buffer),
|
||||
// reinterpret_cast<VmaAllocation*>(&alloc.allocation), &vma_alloc_info);
|
||||
}
|
||||
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);
|
||||
|
||||
vma::MemoryUsage to_vma_memory_usage(memory_usage usage) noexcept;
|
||||
vma::AllocationCreateFlags to_vma_allocation_flags(memory_usage usage, bool persistent_mapped) noexcept;
|
||||
|
||||
result_t<buffer_allocation> alloc_buffer(const buffer_create_info& info);
|
||||
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,21 +0,0 @@
|
||||
#include "gpu_buffer.h"
|
||||
|
||||
#include "allocator.h"
|
||||
|
||||
namespace mirai {
|
||||
gpu_buffer::gpu_buffer(const buffer_create_info& info) {
|
||||
vma_allocator::get();
|
||||
}
|
||||
|
||||
bool gpu_buffer::is_host_visible() const noexcept {
|
||||
switch (mem_usage_) {
|
||||
case memory_usage::cpu_only:
|
||||
case memory_usage::cpu_to_gpu:
|
||||
case memory_usage::gpu_to_cpu:
|
||||
case memory_usage::auto_prefer_host:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +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);
|
||||
|
||||
[[nodiscard]] auto get_buffer() const noexcept { return allocation_.buffer; }
|
||||
[[nodiscard]] u64 get_size() const noexcept { return size_; }
|
||||
[[nodiscard]] buffer_usage get_usage() const noexcept { return usage_; }
|
||||
[[nodiscard]] memory_usage get_memory_usage() const noexcept { return mem_usage_; }
|
||||
[[nodiscard]] bool is_persistent_mapped() const noexcept { return persistent_mapped_; }
|
||||
[[nodiscard]] void* get_mapped_data() const noexcept { return mapped_data_; }
|
||||
private:
|
||||
[[nodiscard]] bool is_host_visible() const noexcept;
|
||||
|
||||
buffer_allocation allocation_;
|
||||
|
||||
// buffer申请大小
|
||||
u64 size_ = 0;
|
||||
// buffer用途
|
||||
buffer_usage usage_ = buffer_usage::vertex;
|
||||
// 内存用途
|
||||
memory_usage mem_usage_ = memory_usage::gpu_only;
|
||||
// 是否持久映射
|
||||
bool persistent_mapped_ = false;
|
||||
// 映射的指针
|
||||
void* mapped_data_ = nullptr;
|
||||
// 调试名称
|
||||
std::string debug_name_;
|
||||
};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include "gpu_resource.h"
|
||||
|
||||
namespace mirai {
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "resource_manager.h"
|
||||
#include "resource_types.h"
|
||||
#include "core/object.h"
|
||||
|
||||
namespace mirai {
|
||||
class vulkan_queue;
|
||||
|
||||
class gpu_resource : public object {
|
||||
MIRAI_OBJECT_TYPE_INFO(gpu_resource, object)
|
||||
public:
|
||||
// 如果没有传入任何参数,则代表资源使用并发(concurrent)模式
|
||||
gpu_resource() : sharing_mode_(resource_sharing_mode::concurrent) {}
|
||||
// 如果传入队列,则代表资源使用独占(exclusive)模式
|
||||
gpu_resource(std::shared_ptr<vulkan_queue> queue) : sharing_mode_(resource_sharing_mode::exclusive), queue_(queue) {}
|
||||
|
||||
virtual resource_async_task upload(std::vector<uint8_t> data) = 0;
|
||||
|
||||
[[nodiscard]] auto get_sharing_mode() const noexcept { return sharing_mode_; }
|
||||
[[nodiscard]] auto get_queue() const noexcept { return queue_.lock(); }
|
||||
[[nodiscard]] auto get_current_stages() const noexcept { return current_stages_; }
|
||||
[[nodiscard]] auto get_current_access() const noexcept { return current_access_; }
|
||||
protected:
|
||||
/// 资源共享模式
|
||||
resource_sharing_mode sharing_mode_ = resource_sharing_mode::exclusive;
|
||||
/// 当前所处队列(当exclusive共享时有效)
|
||||
std::weak_ptr<vulkan_queue> queue_;
|
||||
|
||||
vk::PipelineStageFlags2 current_stages_{};
|
||||
vk::AccessFlags2 current_access_{};
|
||||
};
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,188 +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_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("上传完成,清理暂存缓冲区...");
|
||||
}
|
||||
|
||||
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 = device_->create_command_buffer(vk::CommandBufferLevel::ePrimary);
|
||||
|
||||
cmd.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||||
|
||||
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};
|
||||
|
||||
cmd.pipelineBarrier2(vk::DependencyInfo{0, nullptr, 0, nullptr, 1, &barrier});
|
||||
|
||||
vk::BufferImageCopy copy_region{0, 0, 0,
|
||||
vk::ImageAspectFlagBits::eColor, 0, {0, 0, 1},
|
||||
tex->get_extent()};
|
||||
cmd.copyBufferToImage(staging->buffer, tex->get_image(), vk::ImageLayout::eTransferDstOptimal, 1, ©_region);
|
||||
|
||||
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};
|
||||
cmd.pipelineBarrier2(vk::DependencyInfo{0, nullptr, 0, nullptr, 1, &barrier2});
|
||||
|
||||
cmd.end();
|
||||
|
||||
auto fence = device_->create_fence();
|
||||
vk::SubmitInfo submit_info{0, nullptr, nullptr, 1, &cmd, 0, nullptr};
|
||||
queue.submit(1, &submit_info, fence);
|
||||
|
||||
auto res = fence->device_wait();
|
||||
if (res == vk::Result::eSuccess) {
|
||||
fence.reset();
|
||||
}
|
||||
device_->destroy_command_buffer(cmd);
|
||||
|
||||
return ++last_value_;
|
||||
}
|
||||
|
||||
uint64_t resource_manager::dispatch_immediate_transfer(std::shared_ptr<gpu_buffer> buffer, std::shared_ptr<staging_buffer> staging) {
|
||||
auto& ctx = vulkan_context::get();
|
||||
auto device = ctx.get_default_device();
|
||||
auto queue = ctx.get_transfer_queue();
|
||||
|
||||
auto cmd = device->create_command_buffer(vk::CommandBufferLevel::ePrimary);
|
||||
|
||||
cmd.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||||
|
||||
vk::BufferCopy copy_region{0, 0, buffer->get_size()};
|
||||
cmd.copyBuffer(staging->buffer, buffer->get_buffer(), 1, ©_region);
|
||||
|
||||
cmd.end();
|
||||
|
||||
auto fence = device->create_fence();
|
||||
vk::SubmitInfo submit_info{0, nullptr, nullptr, 1, &cmd, 0, nullptr};
|
||||
queue.submit(1, &submit_info, fence);
|
||||
|
||||
auto result = device->wait_for_fence(fence);
|
||||
if (result == file_result::success) {
|
||||
device->destroy_fence(fence);
|
||||
}
|
||||
device->destroy_command_buffer(cmd);
|
||||
|
||||
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,60 +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 };
|
||||
};
|
||||
|
||||
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,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();
|
||||
}
|
||||
171
src/render/sdl3webgpu.c
Normal file
171
src/render/sdl3webgpu.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* This is an extension of SDL3 for WebGPU, abstracting away the details of
|
||||
* OS-specific operations.
|
||||
*
|
||||
* This file is part of the "Learn WebGPU for C++" book.
|
||||
* https://eliemichel.github.io/LearnWebGPU
|
||||
*
|
||||
* Most of this code comes from the wgpu-native triangle example:
|
||||
* https://github.com/gfx-rs/wgpu-native/blob/master/examples/triangle/main.c
|
||||
*
|
||||
* 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 "sdl3webgpu.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_MACOS)
|
||||
# include <Cocoa/Cocoa.h>
|
||||
# include <Foundation/Foundation.h>
|
||||
# include <QuartzCore/CAMetalLayer.h>
|
||||
#elif defined(SDL_PLATFORM_IOS)
|
||||
# include <UIKit/UIKit.h>
|
||||
# include <Foundation/Foundation.h>
|
||||
# include <QuartzCore/CAMetalLayer.h>
|
||||
# include <Metal/Metal.h>
|
||||
#elif defined(SDL_PLATFORM_WIN32)
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
WGPUSurface SDL_GetWGPUSurface(WGPUInstance instance, SDL_Window* window) {
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
|
||||
#if defined(SDL_PLATFORM_MACOS)
|
||||
{
|
||||
id metal_layer = NULL;
|
||||
NSWindow *ns_window = (__bridge NSWindow *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL);
|
||||
if (!ns_window) return NULL;
|
||||
[ns_window.contentView setWantsLayer : YES];
|
||||
metal_layer = [CAMetalLayer layer];
|
||||
[ns_window.contentView setLayer : metal_layer];
|
||||
|
||||
WGPUSurfaceSourceMetalLayer fromMetalLayer;
|
||||
fromMetalLayer.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
|
||||
fromMetalLayer.chain.next = NULL;
|
||||
fromMetalLayer.layer = metal_layer;
|
||||
|
||||
WGPUSurfaceDescriptor surfaceDescriptor;
|
||||
surfaceDescriptor.nextInChain = &fromMetalLayer.chain;
|
||||
surfaceDescriptor.label = (WGPUStringView){ NULL, WGPU_STRLEN };
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
|
||||
}
|
||||
#elif defined(SDL_PLATFORM_IOS)
|
||||
{
|
||||
UIWindow *ui_window = (__bridge UIWindow *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, NULL);
|
||||
if (!uiwindow) return NULL;
|
||||
|
||||
UIView* ui_view = ui_window.rootViewController.view;
|
||||
CAMetalLayer* metal_layer = [CAMetalLayer new];
|
||||
metal_layer.opaque = true;
|
||||
metal_layer.frame = ui_view.frame;
|
||||
metal_layer.drawableSize = ui_view.frame.size;
|
||||
|
||||
[ui_view.layer addSublayer: metal_layer];
|
||||
|
||||
WGPUSurfaceSourceMetalLayer fromMetalLayer;
|
||||
fromMetalLayer.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
|
||||
fromMetalLayer.chain.next = NULL;
|
||||
fromMetalLayer.layer = metal_layer;
|
||||
|
||||
WGPUSurfaceDescriptor surfaceDescriptor;
|
||||
surfaceDescriptor.nextInChain = &fromMetalLayer.chain;
|
||||
surfaceDescriptor.label = (WGPUStringView){ NULL, WGPU_STRLEN };
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
|
||||
}
|
||||
#elif defined(SDL_PLATFORM_LINUX)
|
||||
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
|
||||
void *x11_display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
uint64_t x11_window = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
if (!x11_display || !x11_window) return NULL;
|
||||
|
||||
WGPUSurfaceSourceXlibWindow fromXlibWindow;
|
||||
fromXlibWindow.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
|
||||
fromXlibWindow.chain.next = NULL;
|
||||
fromXlibWindow.display = x11_display;
|
||||
fromXlibWindow.window = x11_window;
|
||||
|
||||
WGPUSurfaceDescriptor surfaceDescriptor;
|
||||
surfaceDescriptor.nextInChain = &fromXlibWindow.chain;
|
||||
surfaceDescriptor.label = (WGPUStringView){ NULL, WGPU_STRLEN };
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
|
||||
}
|
||||
else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) {
|
||||
void *wayland_display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL);
|
||||
void *wayland_surface = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
|
||||
if (!wayland_display || !wayland_surface) return NULL;
|
||||
|
||||
WGPUSurfaceSourceWaylandSurface fromWaylandSurface;
|
||||
fromWaylandSurface.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
|
||||
fromWaylandSurface.chain.next = NULL;
|
||||
fromWaylandSurface.display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL);
|
||||
fromWaylandSurface.surface = wayland_surface;
|
||||
|
||||
WGPUSurfaceDescriptor surfaceDescriptor;
|
||||
surfaceDescriptor.nextInChain = &fromWaylandSurface.chain;
|
||||
surfaceDescriptor.label = (WGPUStringView){ NULL, WGPU_STRLEN };
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
|
||||
}
|
||||
#elif defined(SDL_PLATFORM_WIN32)
|
||||
{
|
||||
HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
||||
if (!hwnd) return NULL;
|
||||
HINSTANCE hinstance = GetModuleHandle(NULL);
|
||||
|
||||
WGPUSurfaceDescriptorFromWindowsHWND surface_desc_windows = {};
|
||||
surface_desc_windows.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND;
|
||||
surface_desc_windows.hwnd = hwnd;
|
||||
surface_desc_windows.hinstance = hinstance;
|
||||
|
||||
WGPUSurfaceDescriptor surface_desc = {};
|
||||
|
||||
surface_desc.nextInChain = (const WGPUChainedStruct*)&surface_desc_windows;
|
||||
surface_desc.label = "My WebGPU Surface";
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surface_desc);
|
||||
}
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
{
|
||||
# ifdef WEBGPU_BACKEND_EMDAWNWEBGPU
|
||||
WGPUEmscriptenSurfaceSourceCanvasHTMLSelector fromCanvasHTMLSelector;
|
||||
fromCanvasHTMLSelector.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector;
|
||||
# else
|
||||
WGPUSurfaceDescriptorFromCanvasHTMLSelector fromCanvasHTMLSelector;
|
||||
fromCanvasHTMLSelector.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
|
||||
# endif
|
||||
fromCanvasHTMLSelector.chain.next = NULL;
|
||||
fromCanvasHTMLSelector.selector = "canvas";
|
||||
|
||||
WGPUSurfaceDescriptor surfaceDescriptor;
|
||||
surfaceDescriptor.nextInChain = &fromCanvasHTMLSelector.chain;
|
||||
surfaceDescriptor.label = NULL;
|
||||
|
||||
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
|
||||
}
|
||||
#else
|
||||
// TODO: See SDL_syswm.h for other possible enum values!
|
||||
#error "Unsupported WGPU_TARGET"
|
||||
#endif
|
||||
}
|
||||
|
||||
49
src/render/sdl3webgpu.h
Normal file
49
src/render/sdl3webgpu.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* This is an extension of SDL3 for WebGPU, abstracting away the details of
|
||||
* OS-specific operations.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _sdl3_webgpu_h_
|
||||
#define _sdl3_webgpu_h_
|
||||
|
||||
#include <webgpu/webgpu.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get a WGPUSurface from a SDL3 window.
|
||||
*/
|
||||
WGPUSurface SDL_GetWGPUSurface(WGPUInstance instance, SDL_Window* window);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _sdl3_webgpu_h_
|
||||
@@ -1,73 +0,0 @@
|
||||
#include "vulkan_context.h"
|
||||
|
||||
#include "vulkan_queue.h"
|
||||
#include "window/window_manager.h"
|
||||
|
||||
#include "core/logger.h"
|
||||
|
||||
namespace mirai {
|
||||
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() {
|
||||
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::CommandPool vulkan_thread_context::get_pool(const std::shared_ptr<vulkan_queue>& queue) {
|
||||
const auto family_index = queue->get_family_index();
|
||||
const auto device = queue->get_device();
|
||||
if (!pools_.contains(family_index)) {
|
||||
// 创建新池子,允许重置单个 CommandBuffer
|
||||
vk::CommandPoolCreateInfo info(
|
||||
vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
||||
family_index
|
||||
);
|
||||
auto [result, pool] = device.createCommandPool(info);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
MIRAI_LOG_ERROR("创建命令池失败: {}", vk::to_string(result));
|
||||
throw std::runtime_error("创建命令池失败");
|
||||
}
|
||||
pools_[family_index] = pool;
|
||||
}
|
||||
return pools_[family_index];
|
||||
}
|
||||
}
|
||||
@@ -1,60 +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::CommandPool get_pool(const std::shared_ptr<vulkan_queue>& queue);
|
||||
private:
|
||||
std::unordered_map<u32, vk::CommandPool> pools_;
|
||||
};
|
||||
}
|
||||
@@ -1,173 +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 分配器初始化完成");
|
||||
|
||||
resource_manager_ = make_obj<resource_manager>(device_);
|
||||
}
|
||||
|
||||
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::on_destroying() {
|
||||
object::on_destroying();
|
||||
|
||||
MIRAI_LOG_INFO("Vulkan 设备 {} 正在销毁", get_device_name());
|
||||
|
||||
// 销毁逻辑设备
|
||||
if (device_) {
|
||||
device_.destroy();
|
||||
device_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +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);
|
||||
protected:
|
||||
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,38 +0,0 @@
|
||||
#include "render/vulkan_queue.h"
|
||||
|
||||
namespace mirai {
|
||||
vulkan_queue::vulkan_queue(vk::Device device, vk::Queue queue, u32 family_index, u32 queue_index) : device_(device)
|
||||
, queue_(queue)
|
||||
, family_index_(family_index),
|
||||
queue_index_(queue_index) {
|
||||
}
|
||||
|
||||
vk::Result vulkan_queue::submit(
|
||||
const std::vector<vk::CommandBufferSubmitInfo>& command_buffers,
|
||||
const std::vector<vk::SemaphoreSubmitInfo>& wait_semaphores,
|
||||
const std::vector<vk::SemaphoreSubmitInfo>& signal_semaphores,
|
||||
vk::Fence fence) {
|
||||
std::lock_guard lock(get_lock());
|
||||
|
||||
vk::SubmitInfo2 submit_info;
|
||||
submit_info.setCommandBufferInfos(command_buffers);
|
||||
submit_info.setWaitSemaphoreInfos(wait_semaphores);
|
||||
submit_info.setSignalSemaphoreInfos(signal_semaphores);
|
||||
|
||||
return queue_.submit2(submit_info, fence);
|
||||
}
|
||||
|
||||
vk::Result vulkan_queue::wait_idle() {
|
||||
std::lock_guard lock(get_lock());
|
||||
return queue_.waitIdle();
|
||||
}
|
||||
|
||||
void_result_t vulkan_graphics_queue::submit(const std::vector<vk::CommandBufferSubmitInfo>& command_buffers) {
|
||||
vk::PipelineStageFlags waitStages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput};
|
||||
submitInfo.setWaitSemaphores(imageAvailableSemaphore);
|
||||
submitInfo.setPWaitDstStageMask(waitStages);
|
||||
}
|
||||
|
||||
void_result_t vulkan_present_queue::present(const vk::PresentInfoKHR& present_info) {
|
||||
}
|
||||
} // namespace mirai
|
||||
@@ -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>
|
||||
65
src/render/wgpu_context.cpp
Normal file
65
src/render/wgpu_context.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "wgpu_context.h"
|
||||
|
||||
#include "core/logger.h"
|
||||
|
||||
namespace mirai {
|
||||
bool wgpu_context::setup() {
|
||||
// We create a descriptor
|
||||
wgpu::InstanceDescriptor desc{};
|
||||
|
||||
// We create the instance using this descriptor
|
||||
wgpu_instance_ = wgpu::createInstance(desc);
|
||||
if (!wgpu_instance_) {
|
||||
MIRAI_LOG_ERROR("无法创建 WebGPU 实例");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void wgpu_context::shutdown() {
|
||||
wgpu_device_.destroy();
|
||||
wgpu_instance_.release();
|
||||
}
|
||||
|
||||
bool wgpu_context::update() {
|
||||
wgpu_device_.poll(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wgpu_context::create_default_device(wgpu::Surface compatible_surface) {
|
||||
{
|
||||
wgpu::RequestAdapterOptions options{};
|
||||
options.powerPreference = wgpu::PowerPreference::HighPerformance;
|
||||
options.compatibleSurface = compatible_surface;
|
||||
wgpu::raii::Adapter adapter = wgpu_instance_.requestAdapter(options);
|
||||
|
||||
wgpu::DeviceDescriptor device_desc{};
|
||||
device_desc.label = "Default device";
|
||||
device_desc.defaultQueue.label = "Default queue";
|
||||
device_desc.deviceLostCallback = [](WGPUDeviceLostReason reason, char const* message, void* userdata) {
|
||||
MIRAI_LOG_ERROR("设备丢失 {}: {}", static_cast<int>(reason), message);
|
||||
};
|
||||
wgpu_device_ = adapter->requestDevice(device_desc);
|
||||
if (!wgpu_device_) {
|
||||
MIRAI_LOG_ERROR("无法创建 WebGPU 设备");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wgpu_device_.setUncapturedErrorCallback([](wgpu::ErrorType type, char const* message) {
|
||||
MIRAI_LOG_ERROR("未捕获的错误 {}: {}", static_cast<int>(type), message);
|
||||
});
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wgpu::CommandEncoder wgpu_context::get_command_encoder() {
|
||||
wgpu::CommandEncoderDescriptor encoder_desc{};
|
||||
encoder_desc.label = "Main command encoder";
|
||||
return wgpu_device_.createCommandEncoder(encoder_desc);
|
||||
}
|
||||
|
||||
wgpu::Queue wgpu_context::get_queue() {
|
||||
return wgpu_device_.getQueue();
|
||||
}
|
||||
}
|
||||
30
src/render/wgpu_context.h
Normal file
30
src/render/wgpu_context.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "render/webgpu-raii.hpp"
|
||||
|
||||
namespace mirai {
|
||||
class wgpu_context {
|
||||
public:
|
||||
static auto& instance() {
|
||||
static wgpu_context instance;
|
||||
return instance;
|
||||
}
|
||||
bool setup();
|
||||
void shutdown();
|
||||
bool update();
|
||||
bool create_default_device(wgpu::Surface compatible_surface = nullptr);
|
||||
|
||||
wgpu::CommandEncoder get_command_encoder();
|
||||
wgpu::Queue get_queue();
|
||||
|
||||
auto get_instance() {
|
||||
return wgpu_instance_;
|
||||
}
|
||||
auto get_device() {
|
||||
return wgpu_device_;
|
||||
}
|
||||
private:
|
||||
wgpu_context() = default;
|
||||
wgpu::Instance wgpu_instance_;
|
||||
wgpu::Device wgpu_device_;
|
||||
};
|
||||
}
|
||||
@@ -86,6 +86,8 @@ enum class error_code : u32 {
|
||||
synchronization_error = 307,
|
||||
/// 资源绑定错误
|
||||
resource_binding_error = 308,
|
||||
/// 分配失败
|
||||
vulkan_allocation_failed = 309,
|
||||
|
||||
// ---- 窗口/输入错误 (400-499) ----
|
||||
/// 窗口创建失败
|
||||
@@ -332,17 +334,21 @@ using result_t = std::expected<T, E>;
|
||||
|
||||
using void_result_t = result_t<void, error_info>;
|
||||
|
||||
#define MAKE_ERROR_INFO(ec, msg, ...) std::unexpected(error_info( \
|
||||
ec, fmt::format(msg, __VA_ARGS__), \
|
||||
std::source_location::current().file_name(), \
|
||||
std::source_location::current().line(), \
|
||||
std::source_location::current().function_name() \
|
||||
))
|
||||
|
||||
#define MAKE_SUCCESS_INFO() error_info( \
|
||||
error_code::success, \
|
||||
"", \
|
||||
std::source_location::current().file_name(), \
|
||||
std::source_location::current().line(), \
|
||||
std::source_location::current().function_name() \
|
||||
#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( \
|
||||
error_code::success, \
|
||||
"", \
|
||||
std::source_location::current().file_name(), \
|
||||
std::source_location::current().line(), \
|
||||
std::source_location::current().function_name() \
|
||||
)
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
#include "window.h"
|
||||
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include "render/sdl3webgpu.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "gpu_resource/swapchain.h"
|
||||
#include "render/vulkan_context.h"
|
||||
#include "render/wgpu_context.h"
|
||||
|
||||
namespace mirai {
|
||||
void_result_t window::make_window(window_config config) {
|
||||
|
||||
if (config.vulkan_window) {
|
||||
config.flags |= SDL_WINDOW_VULKAN;
|
||||
}
|
||||
else {
|
||||
config.flags &= ~SDL_WINDOW_VULKAN;
|
||||
}
|
||||
|
||||
auto win_ptr = SDL_CreateWindow(config.title.c_str(), config.size.x(), config.size.y(), config.flags);
|
||||
if (!win_ptr) {
|
||||
return MAKE_ERROR_INFO(error_code::window_creation_failed,
|
||||
"SDL_CreateWindow 失败: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
if (config.vulkan_window) {
|
||||
VkSurfaceKHR surface;
|
||||
if (!SDL_Vulkan_CreateSurface(win_ptr, vulkan_context::get().get_vk_instance(), nullptr, &surface)) {
|
||||
if (config.wgpu_window) {
|
||||
wgpu_surface_ = SDL_GetWGPUSurface(wgpu_context::instance().get_instance(), win_ptr);
|
||||
if (!wgpu_surface_) {
|
||||
SDL_DestroyWindow(win_ptr);
|
||||
return MAKE_ERROR_INFO(error_code::window_creation_failed,
|
||||
"SDL_Vulkan_CreateSurface 失败: {}", SDL_GetError());
|
||||
"SDL_GetWGPUSurface 失败: {}", SDL_GetError());
|
||||
}
|
||||
surface_ = surface;
|
||||
}
|
||||
|
||||
if (!SDL_SetWindowTitle(win_ptr, config.title.c_str())) {
|
||||
@@ -52,8 +43,32 @@ namespace mirai {
|
||||
auto result = rebuild_swapchain();
|
||||
if (!result) {
|
||||
MIRAI_LOG_ERROR("窗口 {} 交换链重建失败: {}", get_window_title(), result.error().full_description());
|
||||
return;
|
||||
}
|
||||
}
|
||||
wgpu::raii::CommandEncoder encoder = wgpu_context::instance().get_command_encoder();
|
||||
wgpu::raii::Queue queue = wgpu_context::instance().get_queue();
|
||||
auto [texture, texture_view] = acquire_next_swapchain_texture();
|
||||
|
||||
wgpu::RenderPassColorAttachment color_attachment{};
|
||||
color_attachment.view = texture_view;
|
||||
color_attachment.loadOp = wgpu::LoadOp::Clear;
|
||||
color_attachment.storeOp = wgpu::StoreOp::Store;
|
||||
color_attachment.clearValue = {0.95f, 0.35f, 0.49f, 1.0f};
|
||||
|
||||
wgpu::RenderPassDescriptor render_pass_descriptor{};
|
||||
render_pass_descriptor.colorAttachmentCount = 1;
|
||||
render_pass_descriptor.colorAttachments = &color_attachment;
|
||||
|
||||
wgpu::raii::RenderPassEncoder render_pass = encoder->beginRenderPass(render_pass_descriptor);
|
||||
render_pass->end();
|
||||
wgpu::raii::CommandBuffer command_buffer = encoder->finish();
|
||||
queue->submit(*command_buffer);
|
||||
|
||||
wgpu_surface_.present();
|
||||
|
||||
wgpuTextureViewRelease(texture_view);
|
||||
wgpuTextureRelease(texture.texture);
|
||||
}
|
||||
|
||||
void window::show_window(bool show) {
|
||||
@@ -78,6 +93,11 @@ namespace mirai {
|
||||
return std::string(title);
|
||||
}
|
||||
|
||||
void window::resize(vec2i new_size) {
|
||||
SDL_SetWindowSize(window_, new_size.x(), new_size.y());
|
||||
need_swapchain_rebuild_ = true;
|
||||
}
|
||||
|
||||
vec2i window::get_pos() const {
|
||||
int x, y;
|
||||
if (!SDL_GetWindowPosition(window_, &x, &y)) {
|
||||
@@ -109,32 +129,58 @@ namespace mirai {
|
||||
}
|
||||
|
||||
bool window::need_rebuild_swapchain() const {
|
||||
if (!swapchain_)
|
||||
return true;
|
||||
return false;
|
||||
if (!wgpu_surface_) {
|
||||
return false;
|
||||
}
|
||||
return need_swapchain_rebuild_;
|
||||
}
|
||||
|
||||
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, "交换链创建失败");
|
||||
}
|
||||
wgpu::SurfaceConfiguration wgpu_surface_config{};
|
||||
wgpu_surface_config.usage = wgpu::TextureUsage::RenderAttachment;
|
||||
wgpu_surface_config.device = wgpu_context::instance().get_device();
|
||||
wgpu_surface_config.format = hdr_enabled ? wgpu::TextureFormat::RGB10A2Unorm : wgpu::TextureFormat::BGRA8Unorm;
|
||||
wgpu_surface_config.width = static_cast<uint32_t>(new_extent.x());
|
||||
wgpu_surface_config.height = static_cast<uint32_t>(new_extent.y());
|
||||
wgpu_surface_config.presentMode = wgpu::PresentMode::Mailbox;
|
||||
wgpu_surface_config.alphaMode = wgpu::CompositeAlphaMode::Auto;
|
||||
wgpu_surface_.configure(wgpu_surface_config);
|
||||
need_swapchain_rebuild_ = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<wgpu::SurfaceTexture, wgpu::TextureView> window::acquire_next_swapchain_texture() {
|
||||
wgpu::SurfaceTexture swapchain_texture;
|
||||
wgpu_surface_.getCurrentTexture(&swapchain_texture);
|
||||
if (swapchain_texture.status != wgpu::SurfaceGetCurrentTextureStatus::Success) {
|
||||
MIRAI_LOG_ERROR("获取交换链纹理失败,状态码: {}", static_cast<int>(swapchain_texture.status));
|
||||
return {swapchain_texture, nullptr};
|
||||
}
|
||||
wgpu::TextureViewDescriptor view_desc{};
|
||||
view_desc.label = "Surface Texture View";
|
||||
view_desc.format = wgpuTextureGetFormat(swapchain_texture.texture);
|
||||
view_desc.dimension = wgpu::TextureViewDimension::_2D;
|
||||
view_desc.baseMipLevel = 0;
|
||||
view_desc.mipLevelCount = 1;
|
||||
view_desc.baseArrayLayer = 0;
|
||||
view_desc.arrayLayerCount = 1;
|
||||
view_desc.aspect = wgpu::TextureAspect::All;
|
||||
auto texture_view = wgpuTextureCreateView(swapchain_texture.texture, &view_desc);
|
||||
return {swapchain_texture, texture_view};
|
||||
}
|
||||
|
||||
void window::on_created() {
|
||||
object::on_created();
|
||||
}
|
||||
|
||||
void window::on_destroying() {
|
||||
object::on_destroying();
|
||||
swapchain_.reset();
|
||||
if (wgpu_surface_) {
|
||||
wgpu_surface_.unconfigure();
|
||||
wgpuSurfaceRelease(wgpu_surface_);
|
||||
wgpu_surface_ = nullptr;
|
||||
}
|
||||
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,8 @@
|
||||
#include "types/error.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace mirai {
|
||||
class swapchain;
|
||||
}
|
||||
#include "render/webgpu-raii.hpp"
|
||||
|
||||
namespace mirai {
|
||||
struct window_config {
|
||||
@@ -16,7 +13,7 @@ namespace mirai {
|
||||
std::optional<vec2i> pos;
|
||||
std::string title = "MIRAI Window";
|
||||
SDL_WindowFlags flags{};
|
||||
bool vulkan_window = true;
|
||||
bool wgpu_window = true;
|
||||
bool visible_on_create = true;
|
||||
};
|
||||
|
||||
@@ -36,6 +33,8 @@ namespace mirai {
|
||||
void move_window(vec2i pos);
|
||||
std::string get_window_title();
|
||||
|
||||
void resize(vec2i new_size);
|
||||
|
||||
vec2i get_pos() const;
|
||||
vec2i get_framebuffer_size() const;
|
||||
vec2i get_window_size() const;
|
||||
@@ -43,21 +42,25 @@ 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);
|
||||
void_result_t rebuild_swapchain() {
|
||||
return rebuild_swapchain(hdr_enabled_, get_framebuffer_size());
|
||||
}
|
||||
wgpu::Surface get_wgpu_surface() const {
|
||||
return wgpu_surface_;
|
||||
}
|
||||
|
||||
std::pair<wgpu::SurfaceTexture, wgpu::TextureView> acquire_next_swapchain_texture();
|
||||
protected:
|
||||
void on_created() override;
|
||||
void on_destroying() override;
|
||||
private:
|
||||
SDL_Window* window_{};
|
||||
vk::SurfaceKHR surface_{};
|
||||
std::shared_ptr<swapchain> swapchain_{};
|
||||
bool closing_{false};
|
||||
bool hdr_enabled_{false};
|
||||
SDL_Window* window_{};
|
||||
wgpu::Surface wgpu_surface_{};
|
||||
bool need_swapchain_rebuild_{true};
|
||||
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) {
|
||||
@@ -62,6 +55,12 @@ namespace mirai {
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
g_quit_requested = true;
|
||||
}
|
||||
if (event.type == SDL_EVENT_WINDOW_RESIZED) {
|
||||
auto win = get_window_by_id(event.window.windowID).lock();
|
||||
if (win) {
|
||||
win->resize(vec2i{event.window.data1, event.window.data2});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto w : windows_) {
|
||||
|
||||
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