diff --git a/.gitmodules b/.gitmodules index c54d914..764bea8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d55da9..55405eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/example/src/main.cpp b/example/src/main.cpp index 18032d8..b366361 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -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); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8b59d0d..fa0a8b7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 () diff --git a/src/core/async/thread_pool.h b/src/core/async/thread_pool.h index d83cbc4..ae1770b 100644 --- a/src/core/async/thread_pool.h +++ b/src/core/async/thread_pool.h @@ -60,8 +60,8 @@ public: return res; } - template - auto submit_with_callback(f&& in_func, callback&& in_callback, args&&... in_args) { + template + auto submit_with_callback(f&& in_func, callback_f&& in_callback, args&&... in_args) { using return_type = std::invoke_result_t; 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 - auto submit_with_main_thread_callback(f&& in_func, callback&& in_callback, args&&... in_args) { + template + auto submit_with_main_thread_callback(f&& in_func, callback_f&& in_callback, args&&... in_args) { using return_type = std::invoke_result_t; 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(in_callback), shared_res = res.share()] { + auto task_with_callback = [this, task, callback = std::forward(in_callback), shared_res = res.share()] { std::optional callback_value; try { (*task)(); diff --git a/src/core/fonts/glyph_cache.cpp b/src/core/fonts/glyph_cache.cpp index e48abac..c98f984 100644 --- a/src/core/fonts/glyph_cache.cpp +++ b/src/core/fonts/glyph_cache.cpp @@ -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) { diff --git a/src/core/fonts/glyph_cache.h b/src/core/fonts/glyph_cache.h index e9e1899..8ad23dd 100644 --- a/src/core/fonts/glyph_cache.h +++ b/src/core/fonts/glyph_cache.h @@ -1,11 +1,9 @@ #pragma once #include #include -#include #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 metadata_cache{500}; std::vector 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); diff --git a/src/core/misc/pixel.h b/src/core/misc/pixel.h index 90c2c72..1cca690 100644 --- a/src/core/misc/pixel.h +++ b/src/core/misc/pixel.h @@ -179,7 +179,7 @@ struct pixel { } template - auto operator=(const pixel& rhs) { + auto& operator=(const pixel& 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 - operator pixel() { + explicit operator pixel() { pixel result = *this; return result; } @@ -217,7 +217,7 @@ struct image_accessor { void copy_from(const image_accessor& rhs) { const int temp_width = std::min(width, rhs.width); const int temp_height = std::min(height, rhs.height); - if constexpr (std::is_same::value) { + if constexpr (std::is_same_v) { 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; } }; diff --git a/src/core/pipeline/pipeline.cpp b/src/core/pipeline/pipeline.cpp index bf43ccd..65bccfe 100644 --- a/src/core/pipeline/pipeline.cpp +++ b/src/core/pipeline/pipeline.cpp @@ -15,13 +15,13 @@ void pipeline::destroy() { void pipeline::set_triangle(const std::span& in_vertexes, const std::span& in_triangles) { - if (vertex_buffer->get_size() < in_vertexes.size()) { vertex_buffer->resize(static_cast(in_vertexes.size())); } - const auto v_buffer = static_cast(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(in_triangles.size())); } - const auto i_buffer = static_cast(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(in_vertexes.size())); } + // const auto v_buffer = static_cast(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(in_triangles.size())); } + // const auto i_buffer = static_cast(index_buffer->lock()); + // std::ranges::copy(in_triangles, i_buffer); + // index_buffer->unlock(); } diff --git a/src/core/renderer/device_selector.h b/src/core/renderer/device_selector.h new file mode 100644 index 0000000..5b1c9f6 --- /dev/null +++ b/src/core/renderer/device_selector.h @@ -0,0 +1,176 @@ +#pragma once +#include +#include +#include + +class device_selector { + enum class device_type { combined, display, compute, unknown }; +public: + using device_group = std::vector; + + 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; + + static std::vector get_device_scores(const std::vector& devices, + const vk::SurfaceKHR surface) { + std::vector 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(total_memory / (1024 * 1024 * 1024)); // 每GB加1分 + + return score; + } + + void group_devices(const std::vector& 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; + } +}; diff --git a/src/core/renderer/renderer.cpp b/src/core/renderer/renderer.cpp index 5d85ec6..72ce85d 100644 --- a/src/core/renderer/renderer.cpp +++ b/src/core/renderer/renderer.cpp @@ -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 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& aorii::get_delta_time() { return delta_time; } @@ -125,3 +143,81 @@ const std::chrono::duration& aorii::get_delta_time() { std::chrono::duration 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 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 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); +} diff --git a/src/core/renderer/renderer.h b/src/core/renderer/renderer.h index a346433..2155a60 100644 --- a/src/core/renderer/renderer.h +++ b/src/core/renderer/renderer.h @@ -2,23 +2,33 @@ #include #include #include +#include + +#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& get_delta_time(); std::chrono::duration get_total_time(); diff --git a/src/core/renderer/renderer_buffer.cpp b/src/core/renderer/renderer_buffer.cpp index e754361..af9fb40 100644 --- a/src/core/renderer/renderer_buffer.cpp +++ b/src/core/renderer/renderer_buffer.cpp @@ -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(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; } + diff --git a/src/core/renderer/renderer_buffer.h b/src/core/renderer/renderer_buffer.h index 9c7fa2b..48f57bc 100644 --- a/src/core/renderer/renderer_buffer.h +++ b/src/core/renderer/renderer_buffer.h @@ -2,45 +2,81 @@ #include #include -#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 - void set_data(const T& in_data) { - aorii::get_command_buffer()->UpdateBuffer(*buffer, 0, &in_data, sizeof(T)); - } - template - void set_data(const std::span& 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 + void set_data(const T& in_data, vk::CommandBuffer in_command_buffer = nullptr) { + copy_from(&in_data, sizeof(T), in_command_buffer); + } + template + void set_data(const std::span& 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 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); + } }; diff --git a/src/core/renderer/renderer_buffer_memory_strategy.cpp b/src/core/renderer/renderer_buffer_memory_strategy.cpp new file mode 100644 index 0000000..aa1879e --- /dev/null +++ b/src/core/renderer/renderer_buffer_memory_strategy.cpp @@ -0,0 +1,44 @@ +#include "renderer_buffer_memory_strategy.h" + +#include "renderer_buffer.h" + +std::unique_ptr buffer_memory_strategy::create(vk::MemoryPropertyFlags in_memory_props) { + if (in_memory_props & vk::MemoryPropertyFlagBits::eHostVisible) { + return std::make_unique(); + } + if (in_memory_props & vk::MemoryPropertyFlagBits::eDeviceLocal) { + return std::make_unique(); + } + 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); +} diff --git a/src/core/renderer/renderer_buffer_memory_strategy.h b/src/core/renderer/renderer_buffer_memory_strategy.h new file mode 100644 index 0000000..cedd30c --- /dev/null +++ b/src/core/renderer/renderer_buffer_memory_strategy.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +#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 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; +}; diff --git a/src/core/renderer/renderer_types.h b/src/core/renderer/renderer_types.h new file mode 100644 index 0000000..b7fc485 --- /dev/null +++ b/src/core/renderer/renderer_types.h @@ -0,0 +1,84 @@ +#pragma once +#include + +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(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 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(*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 + }; + } +}; + diff --git a/src/core/window/renderer_window.cpp b/src/core/window/renderer_window.cpp new file mode 100644 index 0000000..ff78203 --- /dev/null +++ b/src/core/window/renderer_window.cpp @@ -0,0 +1,91 @@ +#include "renderer_window.h" + +#include + +#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(glfwGetWindowUserPointer(in_window)); + my_window->on_resize(in_width, in_height); + }); + glfwSetWindowCloseCallback(window, [](GLFWwindow* in_window) { + const auto my_window = static_cast(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); +} diff --git a/src/core/window/renderer_window.h b/src/core/window/renderer_window.h index baf5ff3..0bdcf30 100644 --- a/src/core/window/renderer_window.h +++ b/src/core/window/renderer_window.h @@ -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(&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 diff --git a/src/core/window/window_manager.cpp b/src/core/window/window_manager.cpp index f603c75..a7b6c65 100644 --- a/src/core/window/window_manager.cpp +++ b/src/core/window/window_manager.cpp @@ -1,55 +1,70 @@ #include "window_manager.h" +#include #include #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 window_manager::create_window(const LLGL::SwapChainDescriptor& in_desc) { - auto window = std::make_shared(in_desc); -#if !IS_MOBILE - const auto& manager_listener = std::make_shared(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 window_manager::create_window(const window_desc& in_desc) { + auto window_ptr = std::make_shared(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& 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() { diff --git a/src/core/window/window_manager.h b/src/core/window/window_manager.h index acc1ba4..2235cc5 100644 --- a/src/core/window/window_manager.h +++ b/src/core/window/window_manager.h @@ -8,30 +8,30 @@ class window_manager { public: ~window_manager(); - std::weak_ptr create_window(const LLGL::SwapChainDescriptor& in_desc); + bool init(); + void destroy(); + std::weak_ptr create_window(const window_desc& in_desc); + void destroy_window(renderer_window* in_window); + + std::weak_ptr get_main_window() { + return windows.empty() ? std::weak_ptr() : 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> windows; - std::vector> 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(); } }