This commit is contained in:
2025-02-05 18:54:23 +08:00
parent 2925185c4f
commit e87bc3f7a0
21 changed files with 860 additions and 227 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "third_party/LLGL"]
path = third_party/LLGL
url = https://github.com/LukasBanana/LLGL.git
[submodule "third_party/msdfgen"]
path = third_party/msdfgen
url = https://github.com/Chlumsky/msdfgen.git

View File

@@ -10,7 +10,6 @@ endif ()
set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE)
set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE)
set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE)
set(LLGL_BUILD_STATIC_LIB ON CACHE BOOL "Build LLGL as static library" FORCE)
include(cmake/retrieve_files.cmake)
include(cmake/detect_os.cmake)

View File

@@ -8,15 +8,10 @@
#include "window/renderer_window.h"
int main(int argc, char* argv[]) {
aorii::init(renderer_api::vulkan);
renderer_window::desc_type desc{};
desc.resolution = { 1280, 1280 };
auto window = aorii::create_renderer_window(desc);
while (!aorii::should_exit()) {
aorii::update();
}
aorii::destroy();
window_desc desc{};
desc.title = "Aorii";
desc.resolution.width = 1280;
desc.resolution.height = 720;
desc.resizable = true;
return aorii::run(desc);
}

View File

@@ -1,4 +1,5 @@
project(aorii_core)
set(CMAKE_CXX_STANDARD 26)
find_package(harfbuzz REQUIRED)
find_package(Eigen3 REQUIRED)
@@ -6,6 +7,7 @@ find_package(spdlog REQUIRED)
find_package(Boost REQUIRED)
find_package(Freetype REQUIRED)
find_package(Vulkan REQUIRED)
find_package(glfw3 REQUIRED)
if (APPLE)
find_library(COCOA_LIBRARY Cocoa)
@@ -15,10 +17,11 @@ set(RENDERER_SOURCES "")
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} RENDERER_SOURCES)
add_library(${PROJECT_NAME} STATIC ${RENDERER_SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype harfbuzz::harfbuzz Eigen3::Eigen spdlog::spdlog msdfgen-full Boost::boost Vulkan::Vulkan)
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype glfw harfbuzz Eigen3::Eigen spdlog::spdlog msdfgen-full Boost::boost Vulkan::Vulkan)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE NOMINMAX)
add_os_definitions(${PROJECT_NAME})
configure_glfw_native(${PROJECT_NAME})
if (APPLE)
target_link_libraries(${PROJECT_NAME} PUBLIC ${COCOA_LIBRARY})
endif ()

View File

@@ -60,8 +60,8 @@ public:
return res;
}
template<typename f, typename callback, typename ...args>
auto submit_with_callback(f&& in_func, callback&& in_callback, args&&... in_args) {
template<typename f, typename callback_f, typename ...args>
auto submit_with_callback(f&& in_func, callback_f&& in_callback, args&&... in_args) {
using return_type = std::invoke_result_t<f, args...>;
if (stop) { throw std::runtime_error("submit on stopped ThreadPool"); }
@@ -92,7 +92,7 @@ public:
/**
* @brief 提交一个任务到线程池,并在主线程中执行回调函数
* @tparam f 任务函数类型
* @tparam callback 回调函数类型
* @tparam callback_f 回调函数类型
* @tparam args 任务函数参数类型
* @param in_func 任务函数
* @param in_callback 回调函数
@@ -104,8 +104,8 @@ public:
* auto res = thread_pool.submit_with_main_thread_callback(func, callback, 1, 2);
* @endcode
*/
template<typename f, typename callback, typename ...args>
auto submit_with_main_thread_callback(f&& in_func, callback&& in_callback, args&&... in_args) {
template<typename f, typename callback_f, typename ...args>
auto submit_with_main_thread_callback(f&& in_func, callback_f&& in_callback, args&&... in_args) {
using return_type = std::invoke_result_t<f, args...>;
if (stop) { throw std::runtime_error("submit on stopped ThreadPool"); }
@@ -117,7 +117,7 @@ public:
auto res = task->get_future();
{
auto task_with_callback = [this, task, callback = std::forward<callback>(in_callback), shared_res = res.share()] {
auto task_with_callback = [this, task, callback = std::forward<callback_f>(in_callback), shared_res = res.share()] {
std::optional<return_type> callback_value;
try {
(*task)();

View File

@@ -7,15 +7,10 @@ glyph_cache::~glyph_cache() {
clear();
}
void glyph_cache::initialize(LLGL::RenderSystem& in_render_system) {
render_system = &in_render_system;
void glyph_cache::initialize() {
}
void glyph_cache::clear() {
if (!render_system)
return;
for (auto& page : active_atlases)
render_system->Release(*page.texture);
active_atlases.clear();
}
@@ -27,33 +22,33 @@ glyph_metadata glyph_cache::get_glyph(const glyph_key& in_key) {
return glyph;
}
bool glyph_cache::atlas_page::put_glyph(const LLGL::TextureRegion& in_size, const LLGL::ImageView& in_data,
Eigen::AlignedBox2f& out_uv_rect) {
if (current_x + in_size.offset.x > texture->GetDesc().extent.width) {
current_x = 0;
current_y += row_height;
row_height = 0;
}
if (current_y + in_size.offset.y > texture->GetDesc().extent.height)
return false;
render_system->WriteTexture(*texture, in_size, in_data);
bool glyph_cache::atlas_page::put_glyph(Eigen::AlignedBox2f& out_uv_rect) {
// if (current_x + in_size.offset.x > texture->GetDesc().extent.width) {
// current_x = 0;
// current_y += row_height;
// row_height = 0;
// }
// if (current_y + in_size.offset.y > texture->GetDesc().extent.height)
// return false;
//
// render_system->WriteTexture(*texture, in_size, in_data);
return true;
}
glyph_cache::atlas_page glyph_cache::allocate_space(uint32_t in_width, uint32_t in_height) {
LLGL::TextureDescriptor descriptor{};
descriptor.type = LLGL::TextureType::Texture2D;
descriptor.cpuAccessFlags = LLGL::CPUAccessFlags::Write;
descriptor.extent.width = in_width;
descriptor.extent.height = in_height;
atlas_page page{};
page.texture = render_system->CreateTexture(descriptor);
active_atlases.push_back(page);
return page;
// LLGL::TextureDescriptor descriptor{};
// descriptor.type = LLGL::TextureType::Texture2D;
// descriptor.cpuAccessFlags = LLGL::CPUAccessFlags::Write;
// descriptor.extent.width = in_width;
// descriptor.extent.height = in_height;
//
// atlas_page page{};
// page.texture = render_system->CreateTexture(descriptor);
//
// active_atlases.push_back(page);
// return page;
return {};
}
glyph_metadata glyph_cache::make_glyph(const glyph_key& in_key) {

View File

@@ -1,11 +1,9 @@
#pragma once
#include <cstdint>
#include <Eigen/Eigen>
#include <LLGL/Texture.h>
#include "font_type.h"
#include "containers/lrucache.hpp"
#include "LLGL/RenderSystem.h"
struct glyph_key {
font_family family{};
@@ -40,7 +38,7 @@ private:
};
struct glyph_metadata {
LLGL::Texture* atlas_texture;
// LLGL::Texture* atlas_texture;
Eigen::AlignedBox2f uv_rect;
Eigen::Vector2f size;
Eigen::Vector2f bearing;
@@ -50,24 +48,24 @@ class glyph_cache {
public:
~glyph_cache();
void initialize(LLGL::RenderSystem& in_render_system);
void initialize();
void clear();
glyph_metadata get_glyph(const glyph_key& in_key);
private:
struct atlas_page {
LLGL::Texture* texture = nullptr;
LLGL::RenderSystem* render_system = nullptr;
// LLGL::Texture* texture = nullptr;
// LLGL::RenderSystem* render_system = nullptr;
uint32_t current_x = 0;
uint32_t current_y = 0;
uint32_t row_height = 0;
bool put_glyph(const LLGL::TextureRegion& in_size, const LLGL::ImageView& in_data, Eigen::AlignedBox2f& out_uv_rect);
bool put_glyph(Eigen::AlignedBox2f& out_uv_rect);
};
cache::lru_cache<glyph_key, glyph_metadata> metadata_cache{500};
std::vector<atlas_page> active_atlases;
std::mutex atlas_mutex;
LLGL::RenderSystem* render_system = nullptr;
// LLGL::RenderSystem* render_system = nullptr;
atlas_page allocate_space(uint32_t in_width, uint32_t in_height);
glyph_metadata make_glyph(const glyph_key& in_key);

View File

@@ -179,7 +179,7 @@ struct pixel {
}
template<typename U, int N2>
auto operator=(const pixel<U, N2>& rhs) {
auto& operator=(const pixel<U, N2>& rhs) {
constexpr int Num = std::min(N, N2);
memset(data, 0, sizeof(data));
for (int i = 0; i < Num; ++i) {
@@ -189,7 +189,7 @@ struct pixel {
}
template<typename U, int N2>
operator pixel() {
explicit operator pixel<U, N2>() {
pixel<U, N2> result = *this;
return result;
}
@@ -217,7 +217,7 @@ struct image_accessor {
void copy_from(const image_accessor<T>& rhs) {
const int temp_width = std::min(width, rhs.width);
const int temp_height = std::min(height, rhs.height);
if constexpr (std::is_same<T, float>::value) {
if constexpr (std::is_same_v<T, float>) {
auto row_size = std::min(temp_width * sizeof(T), row_pitch);
for (int y = 0; y < temp_height; ++y) {
auto src_row = rhs.get_row(y);
@@ -304,7 +304,7 @@ struct image_accessor {
return get_pixel(pos.x(), pos.y());
}
void set_row_pitch(const int in_row_pitch) {
void set_row_pitch(const uint64_t in_row_pitch) {
assert(in_row_pitch >= width);
row_pitch = in_row_pitch;
}
@@ -326,9 +326,9 @@ struct image_accessor {
void* const data;
const int width;
const int height;
int row_pitch; // 行跨度,支持对齐
uint64_t row_pitch; // 行跨度,支持对齐
private:
uint8_t* get_row(const int y) const {
[[nodiscard]] uint8_t* get_row(const int y) const {
return (uint8_t*)data + y * row_pitch;
}
};

View File

@@ -15,13 +15,13 @@ void pipeline::destroy() {
void pipeline::set_triangle(const std::span<const aorii_vertex>& in_vertexes,
const std::span<const aorii_triangle>& in_triangles) {
if (vertex_buffer->get_size() < in_vertexes.size()) { vertex_buffer->resize(static_cast<int>(in_vertexes.size())); }
const auto v_buffer = static_cast<aorii_vertex*>(vertex_buffer->lock());
std::ranges::copy(in_vertexes, v_buffer);
vertex_buffer->unlock();
if (index_buffer->get_size() < in_triangles.size()) { index_buffer->resize(static_cast<int>(in_triangles.size())); }
const auto i_buffer = static_cast<aorii_triangle*>(index_buffer->lock());
std::ranges::copy(in_triangles, i_buffer);
index_buffer->unlock();
// if (vertex_buffer->get_size() < in_vertexes.size()) { vertex_buffer->resize(static_cast<int>(in_vertexes.size())); }
// const auto v_buffer = static_cast<aorii_vertex*>(vertex_buffer->lock());
// std::ranges::copy(in_vertexes, v_buffer);
// vertex_buffer->unlock();
//
// if (index_buffer->get_size() < in_triangles.size()) { index_buffer->resize(static_cast<int>(in_triangles.size())); }
// const auto i_buffer = static_cast<aorii_triangle*>(index_buffer->lock());
// std::ranges::copy(in_triangles, i_buffer);
// index_buffer->unlock();
}

View File

@@ -0,0 +1,176 @@
#pragma once
#include <vector>
#include <vulkan/vulkan.hpp>
#include <spdlog/spdlog.h>
class device_selector {
enum class device_type { combined, display, compute, unknown };
public:
using device_group = std::vector<vk::PhysicalDevice>;
void classify_and_sort_devices(const vk::Instance instance, const vk::SurfaceKHR surface) {
const auto& physical_devices = instance.enumeratePhysicalDevices();
// 获取设备的评分列表
auto device_scores = get_device_scores(physical_devices, surface);
// 按评分排序设备(从高到低)
auto sort_pred = [](const auto& a, const auto& b) { return a.second > b.second; };
std::ranges::sort(device_scores, sort_pred);
// 根据排序结果将设备分组
group_devices(device_scores, surface);
}
vk::PhysicalDevice get_main_device() const {
// 优先使用同时支持显示和计算的设备
if (!combined_group.empty()) {
return combined_group[0];
}
// 如果没有同时支持显示和计算的设备,则使用显示设备
if (!display_group.empty()) {
return display_group[0];
}
// 如果没有显示设备,则使用计算设备
if (!compute_group.empty()) {
spdlog::warn("没有找到同时支持显示和计算的设备,将使用计算设备");
return compute_group[0];
}
return nullptr;
}
device_group combined_group; // 同时支持显示和计算的设备组
device_group display_group; // 显示设备组
device_group compute_group; // 计算设备组
private:
using device_score = std::pair<vk::PhysicalDevice, int>;
static std::vector<device_score> get_device_scores(const std::vector<vk::PhysicalDevice>& devices,
const vk::SurfaceKHR surface) {
std::vector<device_score> deviceScores;
for (const auto& dev: devices) {
int score = score_device(dev, surface);
deviceScores.emplace_back(dev, score);
}
return deviceScores;
}
static int score_device(const vk::PhysicalDevice& dev, const vk::SurfaceKHR surface) {
int score = 0;
// 获取设备属性和特性
const auto& properties = dev.getProperties();
const auto& features = dev.getFeatures();
// 首选独立显卡
if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
score += 1000;
} else if (properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) {
score += 500;
}
// 查询队列族属性
const auto& queue_props = dev.getQueueFamilyProperties();
bool has_graphics = false;
bool has_compute = false;
bool can_present = false;
for (uint32_t i = 0; i < queue_props.size(); ++i) {
const auto& prop = queue_props[i];
if (prop.queueFlags & vk::QueueFlagBits::eGraphics) {
has_graphics = true;
score += 100;
}
if (prop.queueFlags & vk::QueueFlagBits::eCompute) {
has_compute = true;
score += 100;
}
// 检查呈现能力
const VkBool32 present_support = dev.getSurfaceSupportKHR(i, surface);
if (present_support == VK_TRUE) {
can_present = true;
score += 100;
}
}
// 如果同时支持图形、计算和呈现,则加额外分
if (has_graphics && has_compute && can_present) {
score += 500; // 额外奖励
}
// 考虑内存大小
const auto& memory_properties = dev.getMemoryProperties();
uint64_t total_memory = 0;
for (const auto& heap : memory_properties.memoryHeaps) {
if (heap.flags & vk::MemoryHeapFlagBits::eDeviceLocal) {
total_memory += heap.size;
}
}
score += static_cast<int>(total_memory / (1024 * 1024 * 1024)); // 每GB加1分
return score;
}
void group_devices(const std::vector<device_score>& deviceScores,
const vk::SurfaceKHR surface) {
for (const auto& [dev, score] : deviceScores) {
const device_type& type = classify_device(dev, surface);
switch (type) {
case device_type::combined:
combined_group.push_back(dev);
display_group.push_back(dev);
compute_group.push_back(dev);
break;
case device_type::display:
display_group.push_back(dev);
break;
case device_type::compute:
compute_group.push_back(dev);
break;
default:
// 忽略不符合条件的设备
break;
}
}
}
static device_type classify_device(const vk::PhysicalDevice& dev, const vk::SurfaceKHR surface) {
bool has_graphics = false;
bool has_compute = false;
bool can_present = false;
const auto& queue_props = dev.getQueueFamilyProperties();
for (uint32_t i = 0; i < queue_props.size(); ++i) {
const auto& prop = queue_props[i];
if (prop.queueFlags & vk::QueueFlagBits::eGraphics) {
has_graphics = true;
}
if (prop.queueFlags & vk::QueueFlagBits::eCompute) {
has_compute = true;
}
// 检查呈现能力
const VkBool32 present_support = dev.getSurfaceSupportKHR(i, surface);
if (present_support == VK_TRUE) {
can_present = true;
}
}
if (has_graphics && has_compute && can_present) {
return device_type::combined;
}
if (has_graphics && can_present) {
return device_type::display;
}
if (has_compute) {
return device_type::compute;
}
return device_type::unknown;
}
};

View File

@@ -10,10 +10,6 @@
#include "window/window_manager.h"
#include "misc/stb_image.h"
#if DX_BACKEND
#include "backend/dx/dx_renderer.h"
#endif
using time_type = decltype(std::chrono::high_resolution_clock::now());
std::chrono::duration<double> delta_time = {};
time_type begin_time = {};
@@ -73,15 +69,42 @@ aorii_renderer* s_renderer{};
// return texture;
// }
bool aorii::init() {
create_renderer();
create_window_manager();
return true;
namespace aorii {
bool init(const window_desc& in_main_window_desc) {
if (!create_window_manager())
return false;
const auto main_window = get_window_manager()->create_window(in_main_window_desc);
if (!create_renderer())
return false;
return true;
}
void destroy() {
destroy_renderer();
destroy_window_manager();
}
void update() {
thread_pool::global().process_main_thread_callbacks();
const auto& current_time = std::chrono::high_resolution_clock::now();
delta_time = current_time - last_time;
last_time = current_time;
get_window_manager()->update();
std::this_thread::yield();
}
}
void aorii::destroy() {
destroy_window_manager();
destroy_renderer();
int aorii::run(const window_desc& in_main_window_desc) {
if (!init(in_main_window_desc))
return -1;
while (!should_exit()) {
update();
}
destroy();
return 0;
}
aorii_renderer* aorii::get_renderer() {
@@ -95,6 +118,12 @@ bool aorii::create_renderer() {
if (s_renderer) return true;
s_renderer = new aorii_renderer();
if (!s_renderer->init()) {
delete s_renderer;
s_renderer = nullptr;
spdlog::error("创建渲染器失败");
return false;
}
last_time = std::chrono::high_resolution_clock::now();
return s_renderer != nullptr;
}
@@ -102,22 +131,11 @@ bool aorii::create_renderer() {
void aorii::destroy_renderer() {
if (!s_renderer) return;
s_renderer->destroy();
delete s_renderer;
aorii_text::destroy_freetype();
}
void aorii::update() {
thread_pool::global().process_main_thread_callbacks();
const auto& current_time = std::chrono::high_resolution_clock::now();
delta_time = current_time - last_time;
last_time = current_time;
s_window_manager->update();
std::this_thread::yield();
}
const std::chrono::duration<double>& aorii::get_delta_time() {
return delta_time;
}
@@ -125,3 +143,81 @@ const std::chrono::duration<double>& aorii::get_delta_time() {
std::chrono::duration<double> aorii::get_total_time() {
return std::chrono::high_resolution_clock::now() - begin_time;
}
bool aorii_renderer::init() {
spdlog::info("初始化渲染器");
vk::ApplicationInfo app_info{};
app_info.setPEngineName("Aorii");
app_info.setEngineVersion(VK_MAKE_VERSION(1, 0, 0));
app_info.setApiVersion(VK_API_VERSION_1_0);
app_info.setPApplicationName("AoriiHelloWorld");
app_info.setApplicationVersion(VK_MAKE_VERSION(1, 0, 0));
vk::InstanceCreateInfo create_info{};
create_info.setPApplicationInfo(&app_info);
std::vector<const char*> extensions;
// #if DEBUG
// extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
// #endif
uint32_t glfw_extension_count = 0;
auto glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
for (int i = 0; i < glfw_extension_count; ++i) {
extensions.push_back(glfw_extensions[i]);
}
create_info.setPEnabledExtensionNames(extensions);
instance = createInstance(create_info);
if (!instance) {
spdlog::critical("创建 Vulkan 实例失败");
return false;
}
const auto& main_window = aorii::get_window_manager()->get_main_window().lock();
if (!main_window->create_surface())
return false;
selector.classify_and_sort_devices(instance, main_window->get_surface());
std::vector<const char*> device_extensions;
device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
vk::DeviceCreateInfo device_create_info{};
device_create_info.setPEnabledExtensionNames(device_extensions);
main_device.create_device(selector.get_main_device(), device_create_info);
vk::SwapchainCreateInfoKHR swap_chain_create_info{};
swap_chain_create_info.setMinImageCount(2);
swap_chain_create_info.setImageFormat(vk::Format::eB8G8R8A8Unorm);
swap_chain_create_info.setImageColorSpace(vk::ColorSpaceKHR::eSrgbNonlinear);
swap_chain_create_info.setImageExtent(main_window->get_framebuffer_size());
swap_chain_create_info.setImageArrayLayers(1);
swap_chain_create_info.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment);
swap_chain_create_info.setImageSharingMode(vk::SharingMode::eExclusive);
swap_chain_create_info.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity);
swap_chain_create_info.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque);
swap_chain_create_info.setPresentMode(vk::PresentModeKHR::eFifo);
swap_chain_create_info.setClipped(true);
swap_chain_create_info.setOldSwapchain(nullptr);
// main_window->create_swap_chain(swap_chain_create_info);
return true;
}
void aorii_renderer::destroy() {
instance.destroy();
glfwTerminate();
}
vk::SurfaceKHR aorii_renderer::create_surface(GLFWwindow* in_window) {
VkSurfaceKHR surface;
if (instance && glfwCreateWindowSurface(instance, in_window, nullptr, &surface) != VK_SUCCESS) {
spdlog::critical("创建窗口表面失败");
return nullptr;
}
return vk::SurfaceKHR(surface);
}
vk::SwapchainKHR aorii_renderer::create_swap_chain(const vk::SwapchainCreateInfoKHR& in_create_info) {
return main_device->createSwapchainKHR(in_create_info);
}

View File

@@ -2,23 +2,33 @@
#include <chrono>
#include <filesystem>
#include <vulkan/vulkan.hpp>
#include <GLFW/glfw3.h>
#include "device_selector.h"
#include "renderer_types.h"
struct window_desc;
class aorii_renderer {
public:
bool init();
void destroy();
vk::SurfaceKHR create_surface(GLFWwindow* in_window);
vk::SwapchainKHR create_swap_chain(const vk::SwapchainCreateInfoKHR& in_create_info);
private:
vk::Instance instance;
device_selector selector;
device_set main_device;
};
namespace aorii {
bool init();
void destroy();
int run(const window_desc& in_main_window_desc);
aorii_renderer* get_renderer();
bool create_renderer();
void destroy_renderer();
void update();
const std::chrono::duration<double>& get_delta_time();
std::chrono::duration<double> get_total_time();

View File

@@ -1,20 +1,85 @@
#include "renderer_buffer.h"
void renderer_buffer::resize(const uint64_t new_size) {
if (new_size == get_size())
#include "renderer_buffer_memory_strategy.h"
uint32_t device_set::get_physical_device_id() const {
const auto& props = physical_device.getProperties();
return props.deviceID;
}
renderer_buffer_desc::operator vk::BufferCreateInfo() const {
vk::BufferCreateInfo buffer_info;
buffer_info.size = size;
buffer_info.usage = usage_flags;
buffer_info.sharingMode = concurrent ? vk::SharingMode::eConcurrent : vk::SharingMode::eExclusive;
buffer_info.queueFamilyIndexCount = static_cast<uint32_t>(queue_families.size());
buffer_info.pQueueFamilyIndices = queue_families.data();
return buffer_info;
}
renderer_buffer::renderer_buffer(const device_set& in_device, const renderer_buffer_desc& in_desc) {
device = in_device;
desc = in_desc;
memory_strategy = buffer_memory_strategy::create(in_desc.memory_props);
buffer = in_device->createBuffer(in_desc);
const auto& mem_reqs = in_device->getBufferMemoryRequirements(buffer);
memory = allocate_memory(mem_reqs, in_desc.memory_props);
device->bindBufferMemory(buffer, memory, 0);
}
renderer_buffer::~renderer_buffer() {
device->destroyBuffer(buffer);
device->freeMemory(memory);
}
void renderer_buffer::copy_from(const void* in_data, vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer) {
memory_strategy->copy_from(device, buffer, memory, in_data, in_size, in_command_buffer);
}
void renderer_buffer::resize(const vk::DeviceSize in_new_size, const vk::CommandBuffer in_command_buffer, const bool in_preserve_data, const bool in_allow_in_place,
const float in_growth_factor) {
const vk::DeviceSize old_size = get_size();
if (in_new_size == old_size)
return;
if (new_size < 1) {
// 如果允许原地扩展并且可以原地扩展
if (in_allow_in_place && try_resize_in_place(in_new_size)) {
desc.size = in_new_size;
return;
}
void* temp_buffer = malloc(new_size);
ON_SCOPE_EXIT {
free(temp_buffer);
};
auto new_desc = get_desc();
new_desc.size = new_size;
const auto new_buffer = aorii::get_renderer()->CreateBuffer(new_desc, temp_buffer);
aorii::get_command_buffer()->CopyBuffer(*new_buffer, 0, *buffer, 0, std::min(get_size(), new_size));
aorii::get_renderer()->Release(*buffer);
// 计算新的大小
desc.size = calculate_new_size(in_new_size, in_growth_factor);
// 创建新的缓冲区
const auto& mem_reqs = device->getBufferMemoryRequirements(buffer);
const auto new_memory = allocate_memory(mem_reqs, desc.memory_props);
const auto new_buffer = device->createBuffer(desc);
device->bindBufferMemory(new_buffer, new_memory, 0);
// 如果需要保留数据
if (in_preserve_data) {
if (desc.is_host_visible()) {
void* src_data = device->mapMemory(memory, 0, old_size);
void* dst_data = device->mapMemory(new_memory, 0, desc.size);
memcpy(dst_data, src_data, std::min(old_size, desc.size));
device->unmapMemory(memory);
device->unmapMemory(new_memory);
} else {
vk::BufferCopy copy_region {};
copy_region.size = std::min(old_size, desc.size);
in_command_buffer.copyBuffer(buffer, new_buffer, copy_region);
}
}
// 释放原有资源
device->destroyBuffer(buffer);
device->freeMemory(memory);
// 更新资源
buffer = new_buffer;
memory = new_memory;
}

View File

@@ -2,45 +2,81 @@
#include <span>
#include <spdlog/spdlog.h>
#include "LLGL/Buffer.h"
#include "LLGL/RenderSystem.h"
#include "renderer.h"
#include "misc/scope_exit.h"
#include "renderer_types.h"
class buffer_memory_strategy;
class renderer_buffer {
public:
explicit renderer_buffer(const LLGL::BufferDescriptor& in_desc) {
buffer = aorii::get_renderer()->CreateBuffer(in_desc);
}
explicit renderer_buffer(const device_set& in_device, const renderer_buffer_desc& in_desc);
virtual ~renderer_buffer() {
aorii::get_renderer()->Release(*buffer);
}
virtual ~renderer_buffer();
virtual void* lock(const LLGL::CPUAccess access = LLGL::CPUAccess::WriteDiscard) {
return aorii::get_renderer()->MapBuffer(*buffer, access);
[[nodiscard]] const auto& get_memory() const {
return memory;
}
virtual void unlock() {
return aorii::get_renderer()->UnmapBuffer(*buffer);
}
template<class T>
void set_data(const T& in_data) {
aorii::get_command_buffer()->UpdateBuffer(*buffer, 0, &in_data, sizeof(T));
}
template<class T>
void set_data(const std::span<const T>& in_data) {
aorii::get_command_buffer()->UpdateBuffer(*buffer, 0, in_data.data(), sizeof(T) * in_data.size());
}
void resize(uint64_t new_size);
[[nodiscard]] auto get_desc() const {
return buffer->GetDesc();
[[nodiscard]] const auto& get_desc() const {
return desc;
}
[[nodiscard]] auto get_size() const {
return get_desc().size;
return desc.size;
}
[[nodiscard]] auto get_buffer() const {
return buffer;
}
template<class T>
void set_data(const T& in_data, vk::CommandBuffer in_command_buffer = nullptr) {
copy_from(&in_data, sizeof(T), in_command_buffer);
}
template<class T>
void set_data(const std::span<const T>& in_data, vk::CommandBuffer in_command_buffer = nullptr) {
copy_from(in_data.data(), in_data.size_bytes(), in_command_buffer);
}
void copy_from(const void* in_data, vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer = nullptr);
/**
* @brief 重新分配缓冲区大小
* @param in_new_size 新的大小
* @param in_command_buffer 命令缓冲区
* @param in_preserve_data 是否保留原有数据
* @param in_allow_in_place 是否允许原地扩展
* @param in_growth_factor 扩展因子
*/
void resize(vk::DeviceSize in_new_size, vk::CommandBuffer in_command_buffer, bool in_preserve_data = true, bool in_allow_in_place = true, float in_growth_factor = 1.5f);
protected:
LLGL::Buffer* buffer;
device_set device;
renderer_buffer_desc desc;
vk::Buffer buffer;
vk::DeviceMemory memory;
std::unique_ptr<buffer_memory_strategy> memory_strategy;
private:
[[nodiscard]] auto find_memory_type(const uint32_t type_filter, const vk::MemoryPropertyFlags properties) const {
const auto& mem_properties = device.physical_device.getMemoryProperties();
for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) {
if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
throw std::runtime_error("failed to find suitable memory type!");
}
vk::DeviceMemory allocate_memory(const vk::MemoryRequirements& in_requirements, vk::MemoryPropertyFlags props) {
vk::MemoryAllocateInfo alloc_info;
alloc_info.allocationSize = in_requirements.size;
alloc_info.memoryTypeIndex = find_memory_type(in_requirements.memoryTypeBits, props);
return device->allocateMemory(alloc_info);
}
bool try_resize_in_place(vk::DeviceSize in_size) {
const auto& mem_props = device->getBufferMemoryRequirements(buffer);
return in_size <= mem_props.size;
}
[[nodiscard]] vk::DeviceSize calculate_new_size(const vk::DeviceSize in_new_size, const float in_growth_factor) const {
const vk::DeviceSize growth_size = get_size() * in_growth_factor;
return std::max(in_new_size, growth_size);
}
};

View File

@@ -0,0 +1,44 @@
#include "renderer_buffer_memory_strategy.h"
#include "renderer_buffer.h"
std::unique_ptr<buffer_memory_strategy> buffer_memory_strategy::create(vk::MemoryPropertyFlags in_memory_props) {
if (in_memory_props & vk::MemoryPropertyFlagBits::eHostVisible) {
return std::make_unique<host_visible_strategy>();
}
if (in_memory_props & vk::MemoryPropertyFlagBits::eDeviceLocal) {
return std::make_unique<device_local_strategy>();
}
throw std::runtime_error("Unsupported memory property flags");
}
void host_visible_strategy::copy_from(const device_set& in_device, vk::Buffer in_buffer, const vk::DeviceMemory in_device_memory,
const void* in_data, const vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer) {
void* mapped = in_device->mapMemory(in_device_memory, 0, in_size);
std::memcpy(mapped, in_data, in_size);
vk::MappedMemoryRange range;
range.memory = in_device_memory;
range.offset = 0;
range.size = in_size;
in_device->flushMappedMemoryRanges(range);
in_device->unmapMemory(in_device_memory);
}
void device_local_strategy::copy_from(const device_set& in_device, vk::Buffer in_buffer, vk::DeviceMemory in_device_memory,
const void* in_data, const vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer) {
// 创建暂存缓冲区
const renderer_buffer staging_buffer(in_device, renderer_buffer_desc::staging_buffer(in_size));
// 复制数据到暂存缓冲区
void* mapped = in_device->mapMemory(staging_buffer.get_memory(), 0, in_size);
std::memcpy(mapped, in_data, in_size);
in_device->unmapMemory(staging_buffer.get_memory());
// 记录复制命令
vk::BufferCopy copy_region{};
copy_region.size = in_size;
in_command_buffer.copyBuffer(staging_buffer.get_buffer(), in_buffer, copy_region);
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <memory>
#include "renderer_types.h"
class buffer_memory_strategy {
public:
virtual ~buffer_memory_strategy() = default;
virtual void copy_from(const device_set& in_device, vk::Buffer in_buffer, vk::DeviceMemory in_device_memory,
const void* in_data, vk::DeviceSize in_size,
vk::CommandBuffer in_command_buffer) = 0;
static std::unique_ptr<buffer_memory_strategy> create(vk::MemoryPropertyFlags in_memory_props);
};
class host_visible_strategy : public buffer_memory_strategy {
public:
virtual void copy_from(const device_set& in_device, vk::Buffer in_buffer, vk::DeviceMemory in_device_memory, const void* in_data, vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer) override;
};
class device_local_strategy : public buffer_memory_strategy {
public:
virtual void copy_from(const device_set& in_device, vk::Buffer in_buffer, vk::DeviceMemory in_device_memory, const void* in_data, vk::DeviceSize in_size, vk::CommandBuffer in_command_buffer) override;
};

View File

@@ -0,0 +1,84 @@
#pragma once
#include <vulkan/vulkan.hpp>
struct device_set {
vk::PhysicalDevice physical_device;
vk::Device device;
// 重载->运算符使得device_set可以直接调用vk::Device的成员函数
vk::Device* operator->() { return &device; }
const vk::Device* operator->() const { return &device; }
explicit operator vk::PhysicalDevice() const { return physical_device; }
explicit operator vk::Device() const { return device; }
operator bool() const { return static_cast<bool>(device); }
bool operator!() const { return !device; }
void create_device(const vk::PhysicalDevice in_physical_device, const vk::DeviceCreateInfo& in_create_info) {
physical_device = in_physical_device;
device = physical_device.createDevice(in_create_info);
}
// 获取物理设备GUID
[[nodiscard]] uint32_t get_physical_device_id() const;
};
struct renderer_buffer_desc {
// 大小
vk::DeviceSize size;
// 缓冲使用标志
vk::BufferUsageFlags usage_flags;
// 内存属性
vk::MemoryPropertyFlags memory_props;
// 内存共享标志
bool concurrent = false;
// 队列族
std::vector<uint32_t> queue_families;
[[nodiscard]] bool is_host_visible() const {
return (memory_props & vk::MemoryPropertyFlagBits::eHostVisible)
== vk::MemoryPropertyFlagBits::eHostVisible;
}
[[nodiscard]] bool is_device_local() const {
return (memory_props & vk::MemoryPropertyFlagBits::eDeviceLocal)
== vk::MemoryPropertyFlagBits::eDeviceLocal;
}
operator vk::BufferCreateInfo() const;
operator vk::BufferCreateInfo() {
return static_cast<const renderer_buffer_desc&>(*this);
}
static renderer_buffer_desc staging_buffer(const vk::DeviceSize in_size) {
return {
.size = in_size,
.usage_flags = vk::BufferUsageFlagBits::eTransferSrc,
.memory_props = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
};
}
static renderer_buffer_desc uniform_buffer(const vk::DeviceSize in_size) {
return {
.size = in_size,
.usage_flags = vk::BufferUsageFlagBits::eUniformBuffer,
.memory_props = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
};
}
static renderer_buffer_desc index_buffer(const vk::DeviceSize in_size) {
return {
.size = in_size,
.usage_flags = vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
.memory_props = vk::MemoryPropertyFlagBits::eDeviceLocal
};
}
static renderer_buffer_desc vertex_buffer(const vk::DeviceSize in_size) {
return {
.size = in_size,
.usage_flags = vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
.memory_props = vk::MemoryPropertyFlagBits::eDeviceLocal
};
}
};

View File

@@ -0,0 +1,91 @@
#include "renderer_window.h"
#include <spdlog/spdlog.h>
#include "window_manager.h"
renderer_window::renderer_window(const window_desc& in_desc) {
glfwWindowHint(GLFW_RESIZABLE, in_desc.resizable);
glfwWindowHint(GLFW_DECORATED, in_desc.decorated);
glfwWindowHint(GLFW_VISIBLE, in_desc.visible);
glfwWindowHint(GLFW_FOCUSED, in_desc.focused);
glfwWindowHint(GLFW_FOCUS_ON_SHOW, in_desc.focus_on_show);
glfwWindowHint(GLFW_MAXIMIZED, in_desc.maximized);
glfwWindowHint(GLFW_FLOATING, in_desc.floating);
glfwWindowHint(GLFW_CENTER_CURSOR, in_desc.center_cursor);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, in_desc.transparent);
window = glfwCreateWindow(in_desc.resolution.width, in_desc.resolution.height, in_desc.title.c_str(), in_desc.monitor, nullptr);
glfwSetWindowUserPointer(window, this);
if (!window) {
spdlog::error("Failed to create window");
return;
}
glfwSetWindowSizeCallback(window, [](GLFWwindow* in_window, int in_width, int in_height) {
const auto my_window = static_cast<renderer_window*>(glfwGetWindowUserPointer(in_window));
my_window->on_resize(in_width, in_height);
});
glfwSetWindowCloseCallback(window, [](GLFWwindow* in_window) {
const auto my_window = static_cast<renderer_window*>(glfwGetWindowUserPointer(in_window));
my_window->on_glfw_close();
});
if (in_desc.create_surface) {
create_surface();
}
}
renderer_window::~renderer_window() {
}
void renderer_window::set_title(const std::wstring& in_title) const {
}
void renderer_window::close() const {
}
bool renderer_window::create_surface() {
const auto renderer = aorii::get_renderer();
if (!renderer)
return false;
surface = renderer->create_surface(window);
if (!surface) {
spdlog::error("创建窗口表面失败");
return false;
}
return true;
}
bool renderer_window::create_swap_chain(vk::SwapchainCreateInfoKHR in_create_info) {
const auto renderer = aorii::get_renderer();
if (!renderer)
return false;
if (!surface) {
spdlog::error("窗口表面未创建");
return false;
}
in_create_info.setSurface(surface);
swap_chain = renderer->create_swap_chain(in_create_info);
if (!swap_chain) {
spdlog::error("创建交换链失败");
return false;
}
return true;
}
#if !AORII_MACOS
void* renderer_window::get_native_handle() const {
return nullptr;
}
#endif
void renderer_window::on_resize(int in_width, int in_height) {
spdlog::info("窗口大小变更:{}x{}", in_width, in_height);
}
void renderer_window::on_glfw_close() {
spdlog::info("窗口关闭");
aorii::get_window_manager()->destroy_window(this);
}

View File

@@ -3,55 +3,58 @@
#include "vulkan/vulkan.hpp"
struct window_desc {
std::string title;
vk::Extent2D resolution;
GLFWmonitor* monitor = nullptr;
bool fullscreen = false;
bool resizable = true;
bool borderless = false;
bool vsync = true;
bool always_on_top = false;
bool transparent = false;
bool decorated = true;
bool focused = true;
bool focus_on_show = true;
bool maximized = false;
bool visible = true;
bool floating = false;
bool center_cursor = false;
bool create_surface = true;
};
class renderer_window {
friend class window_manager;
public:
explicit renderer_window();
explicit renderer_window(const window_desc& in_desc);
virtual ~renderer_window();
void set_title(const std::wstring& in_title) const;
void close() const;
bool should_close() const { return glfwWindowShouldClose(window); }
vk::Extent2D get_size() const {
int width, height;
glfwGetWindowSize(window, &width, &height);
return vk::Extent2D(width, height);
}
vk::Extent2D get_framebuffer_size() const {
int width, height;
glfwGetFramebufferSize(window, &width, &height);
return vk::Extent2D(width, height);
}
bool create_surface();
[[nodiscard]] auto get_surface() const { return surface; }
bool create_swap_chain(vk::SwapchainCreateInfoKHR in_create_info);
[[nodiscard]] auto get_swap_chain() const { return swap_chain; }
[[nodiscard]] void* get_native_handle() const;
protected:
virtual void on_resize(int in_width, int in_height);
virtual void on_glfw_close();
private:
GLFWwindow* window;
vk::SwapchainKHR swap_chain;
vk::SurfaceKHR surface;
};
inline renderer_window::renderer_window(const desc_type& in_desc) {
swap_chain = aorii::get_renderer()->CreateSwapChain(in_desc);
surface = static_cast<surface_type*>(&swap_chain->GetSurface());
}
inline renderer_window::~renderer_window() { if (swap_chain) { aorii::get_renderer()->Release(*swap_chain); } }
inline void renderer_window::set_title(const std::wstring& in_title) const {
if (const auto window = get_surface()) { window->SetTitle(in_title.c_str()); }
}
inline void renderer_window::close() const {
#if !AORII_IS_MOBILE
if (const auto window = get_surface()) { window->Show(false); }
#endif
}
#if !AORII_MACOS
inline void* renderer_window::get_native_handle() const {
#if !AORII_IS_MOBILE
if (auto surface = get_surface()) {
LLGL::NativeHandle native_handle;
surface->GetNativeHandle(&native_handle, sizeof(LLGL::NativeHandle));
#if AORII_WINDOWS
return native_handle.window;
#elif AORII_LINUX
return native_handle.window;
#endif
}
#endif
return nullptr;
}
#endif

View File

@@ -1,55 +1,70 @@
#include "window_manager.h"
#include <GLFW/glfw3.h>
#include <spdlog/spdlog.h>
#include "renderer_window.h"
#include "renderer/renderer.h"
class manager_window_event_listener : public LLGL::Window::EventListener {
public:
explicit manager_window_event_listener(window_manager* in_manager) :
manager(in_manager) {
}
protected:
virtual void OnQuit(LLGL::Window& sender, bool& veto) override {
EventListener::OnQuit(sender, veto);
manager->on_window_close(sender);
}
window_manager* manager;
};
window_manager* s_window_manager;
window_manager::~window_manager() {
windows.clear();
windows_to_remove.clear();
}
std::weak_ptr<renderer_window> window_manager::create_window(const LLGL::SwapChainDescriptor& in_desc) {
auto window = std::make_shared<renderer_window>(in_desc);
#if !IS_MOBILE
const auto& manager_listener = std::make_shared<manager_window_event_listener>(this);
window->get_surface()->AddEventListener(manager_listener);
#endif
windows.push_back(window);
return window;
bool window_manager::init() {
if (glfwInit() != GLFW_TRUE) {
spdlog::error("初始化GLFW失败");
return false;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
return true;
}
void window_manager::destroy() {
}
std::weak_ptr<renderer_window> window_manager::create_window(const window_desc& in_desc) {
auto window_ptr = std::make_shared<renderer_window>(in_desc);
windows.push_back(window_ptr);
return window_ptr;
}
void window_manager::destroy_window(renderer_window* in_window) {
auto it = std::ranges::find_if(windows, [in_window](const auto& window) {
return window.get() == in_window;
});
if (it != windows.end()) {
windows.erase(it);
}
}
void window_manager::update() {
windows_to_remove.clear();
glfwPollEvents();
}
void window_manager::on_window_close(LLGL::Window& in_sender) {
auto has_removed = std::ranges::remove_if(windows, [&in_sender](const std::shared_ptr<renderer_window>& w) {
return w->get_surface() == &in_sender;
});
void window_manager::on_window_close() {
}
if (has_removed.begin() != has_removed.end()) {
windows_to_remove.push_back(*has_removed.begin());
windows.erase(has_removed.begin(), has_removed.end());
spdlog::info("Window begin close");
} else {
spdlog::error("Failed to close window!");
window_manager* aorii::get_window_manager() {
return s_window_manager;
}
bool aorii::create_window_manager() {
s_window_manager = new window_manager();
if (!s_window_manager->init()) {
delete s_window_manager;
s_window_manager = nullptr;
spdlog::error("创建窗口管理器失败");
return false;
}
return true;
}
void aorii::destroy_window_manager() {
s_window_manager->destroy();
delete s_window_manager;
}
bool aorii::should_exit() {

View File

@@ -8,30 +8,30 @@ class window_manager {
public:
~window_manager();
std::weak_ptr<renderer_window> create_window(const LLGL::SwapChainDescriptor& in_desc);
bool init();
void destroy();
std::weak_ptr<renderer_window> create_window(const window_desc& in_desc);
void destroy_window(renderer_window* in_window);
std::weak_ptr<renderer_window> get_main_window() {
return windows.empty() ? std::weak_ptr<renderer_window>() : windows.front();
}
const auto& get_windows() const { return windows; }
void update();
[[nodiscard]] bool should_exit() const {
return windows.empty() && windows_to_remove.empty();
return windows.empty() || windows[0]->should_close();
}
private:
void on_window_close(LLGL::Window& in_sender);
void on_window_close();
std::vector<std::shared_ptr<renderer_window>> windows;
std::vector<std::shared_ptr<renderer_window>> windows_to_remove;
};
namespace aorii {
inline window_manager* s_window_manager;
inline void create_window_manager() { s_window_manager = new window_manager(); }
inline void destroy_window_manager() { delete s_window_manager; }
window_manager* get_window_manager();
bool create_window_manager();
void destroy_window_manager();
bool should_exit();
inline auto create_renderer_window(const LLGL::SwapChainDescriptor& in_desc) {
return s_window_manager->create_window(in_desc);
}
inline const auto& get_all_window() { return s_window_manager->get_windows(); }
}