diff --git a/.kilocode/rules/rules.md b/.kilocode/rules/rules.md index f9043f0..59aba15 100644 --- a/.kilocode/rules/rules.md +++ b/.kilocode/rules/rules.md @@ -12,6 +12,7 @@ 1. 读取现有文件内容。 2. 生成差异(Diff)或补丁块。 3. 调用 `apply_diff` 精确替换目标代码块。 + 4. 头文件引用路径是相对于模块的,每个src文件夹下的子文件夹都是一个模块 * **目的**: 最小化上下文消耗,保留文件中未修改部分的完整性,避免因全量写入导致的意外截断或格式丢失。 ### 代码创建模式 (Creating Code) @@ -24,7 +25,8 @@ 2. 构建完整的文件内容(包含所有必要的头部引用、类定义等)。 3. 调用 `write_to_file` 将内容一次性写入磁盘。 4. **禁止行为**: 严禁一次性编写600行以上代码,这样会导致截断,如果过长必须分多次写入。 + 5. 不需要修改任何CMakeLists.txt,所有源文件都是自动收集的 ### 子任务模式 -> **规则**: 完成子任务时,必须将未完成、临时修改或未来改进部分编写进总结中 +> **规则**: 完成子任务时,必须将未完成、临时修改、简化实现或未来改进部分编写进总结中 diff --git a/example/test_thread/main.cpp b/example/test_thread/main.cpp index 65edddb..e05944b 100644 --- a/example/test_thread/main.cpp +++ b/example/test_thread/main.cpp @@ -20,14 +20,16 @@ int main(int argc, char* argv[]) { config.target_fps = 240; config.enable_validation = true; config.vsync = true; + config.triple_buffering = false; + config.adaptive_sync = true; if (!app.initialize(config)) { return -1; } // auto texture_id = app.texture_mgr()->load_texture("D:\\G2uY1fJa8AAOucs.jpg").value(); - auto texture_id = app.texture_mgr()->load_texture("D:\\screenshot-20251128-165627.png").value(); - auto tex_size = app.texture_mgr()->get_texture(texture_id)->size().cast(); + auto texture_id = app.texture_mgr()->load_texture("D:\\screenshot-20251128-165627.png").value(); + auto tex_size = app.texture_mgr()->get_texture(texture_id)->size().cast(); auto root_widget = new_widget()[ new_widget()[ diff --git a/src/app/application.cpp b/src/app/application.cpp index d2f7891..a00decd 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -228,7 +228,14 @@ namespace mirage::app { threading::coordinator_config coord_config{}; coord_config.enable_multithreading = config.enable_multithreading; coord_config.max_frames_in_flight = 2; - + coord_config.adaptive_sync = config.adaptive_sync; + + // 配置渲染参数 + coord_config.render_cfg.vsync_enabled = config.vsync; + coord_config.render_cfg.target_fps = config.target_fps; + coord_config.render_cfg.triple_buffering = config.triple_buffering; + coord_config.render_cfg.adaptive_sync = config.adaptive_sync; + coordinator_->start(coord_config); // ======================================================================== @@ -240,7 +247,7 @@ namespace mirage::app { std::cout << "[application] Initialized successfully" << std::endl; std::cout << " - Window: " << current_width_ << "x" << current_height_ << std::endl; - std::cout << " - VSync: " << (config.vsync ? "enabled" : "disabled") << std::endl; + std::cout << " - Present Mode: " << get_present_mode_string() << std::endl; std::cout << " - Multithreading: " << (config.enable_multithreading ? "enabled" : "disabled") << std::endl; std::cout << " - Target FPS: " << config.target_fps << std::endl; @@ -323,6 +330,29 @@ namespace mirage::app { resize_callback_ = std::move(callback); } + // ============================================================================ + // 配置查询 + // ============================================================================ + + auto application::is_vrr_enabled() const -> bool { + return config_.adaptive_sync; + } + + auto application::get_present_mode_string() const -> std::string { + if (config_.adaptive_sync) { + return "Adaptive Sync (VRR)"; + } + + if (config_.vsync) { + if (config_.triple_buffering) { + return "VSync + Triple Buffering"; + } + return "VSync (Double Buffering)"; + } + + return "Immediate (No VSync)"; + } + // ============================================================================ // 内部方法 // ============================================================================ diff --git a/src/app/application.h b/src/app/application.h index 4d766bd..37915f6 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -55,6 +55,8 @@ struct application_config { bool enable_multithreading = true; ///< 是否启用多线程渲染 int target_fps = 60; ///< 目标帧率 bool enable_validation = true; ///< 是否启用 Vulkan 验证层 + bool adaptive_sync = false; ///< 是否启用自适应同步(VRR) + bool triple_buffering = true; ///< 是否启用三缓冲 }; // ============================================================================ @@ -210,6 +212,20 @@ public: /// @return 纹理管理器指针 [[nodiscard]] auto texture_mgr() const { return texture_manager_.get(); } + // ======================================================================== + // 配置查询 + // ======================================================================== + + /// @brief 查询当前是否启用了 VRR(可变刷新率) + /// + /// @return 如果启用了自适应同步返回 true + [[nodiscard]] auto is_vrr_enabled() const -> bool; + + /// @brief 获取当前呈现模式描述 + /// + /// @return 呈现模式的字符串描述 + [[nodiscard]] auto get_present_mode_string() const -> std::string; + // ======================================================================== // 回调设置 // ======================================================================== diff --git a/src/app/threading/frame_sync.h b/src/app/threading/frame_sync.h index 2b8c5c4..58f16fd 100644 --- a/src/app/threading/frame_sync.h +++ b/src/app/threading/frame_sync.h @@ -52,10 +52,10 @@ namespace mirage::app::threading { class frame_sync { public: /// @brief 构造函数 - /// - /// @param max_frames_in_flight 最大飞行帧数,默认为 2 + /// + /// @param max_frames_in_flight 最大飞行帧数,默认为 3 /// 这决定了布局线程可以领先渲染线程多少帧 - explicit frame_sync(int max_frames_in_flight = 2) + explicit frame_sync(int max_frames_in_flight = 3) : frame_slots_(max_frames_in_flight) , layout_ready_(0) , frame_number_(0) diff --git a/src/app/threading/render_thread.h b/src/app/threading/render_thread.h index 992e662..7f496ac 100644 --- a/src/app/threading/render_thread.h +++ b/src/app/threading/render_thread.h @@ -32,12 +32,13 @@ namespace mirage::app::threading { // ============================================================================ /// @brief 渲染配置 -/// +/// /// 控制渲染线程的行为参数 struct render_config { bool vsync_enabled = true; ///< 是否启用垂直同步 int target_fps = 60; ///< 目标帧率(当 vsync 禁用时使用) - bool triple_buffering = false; ///< 是否启用三缓冲 + bool triple_buffering = true; ///< 是否启用三缓冲 + bool adaptive_sync = false; ///< 是否启用自适应同步 }; // ============================================================================ diff --git a/src/app/threading/thread_coordinator.cpp b/src/app/threading/thread_coordinator.cpp index 17a2266..5ec506b 100644 --- a/src/app/threading/thread_coordinator.cpp +++ b/src/app/threading/thread_coordinator.cpp @@ -282,8 +282,10 @@ void thread_coordinator::create_threads(const coordinator_config& config) { // 4. 创建渲染线程 render_thread_ = std::make_unique(*tree_buffer_, *frame_sync_, pipeline_); - // 5. 设置渲染配置 - render_thread_->set_config(config.render_cfg); + // 5. 设置渲染配置(包括 adaptive_sync) + render_config render_cfg = config.render_cfg; + render_cfg.adaptive_sync = config.adaptive_sync; + render_thread_->set_config(render_cfg); // 6. 启动线程(先启动渲染线程,再启动布局线程) render_thread_->start(); diff --git a/src/app/threading/thread_coordinator.h b/src/app/threading/thread_coordinator.h index 4cd7075..c3372a5 100644 --- a/src/app/threading/thread_coordinator.h +++ b/src/app/threading/thread_coordinator.h @@ -30,11 +30,12 @@ namespace mirage::app::threading { // ============================================================================ /// @brief 协调器配置 -/// +/// /// 控制线程协调器的行为参数 struct coordinator_config { - int max_frames_in_flight = 2; ///< 最大飞行帧数 + int max_frames_in_flight = 3; ///< 最大飞行帧数 bool enable_multithreading = true; ///< 是否启用多线程(可以禁用回退到单线程模式) + bool adaptive_sync = false; ///< 是否启用自适应同步 render_config render_cfg; ///< 渲染配置 }; diff --git a/src/render/pipeline/frame_scheduler.cpp b/src/render/pipeline/frame_scheduler.cpp index ca13c09..ab039e9 100644 --- a/src/render/pipeline/frame_scheduler.cpp +++ b/src/render/pipeline/frame_scheduler.cpp @@ -15,7 +15,9 @@ namespace mirage { const config& cfg ) : device_(device) , swapchain_(swapchain) - , config_(cfg) { + , config_(cfg) + , vsync_(cfg.vsync) + , adaptive_sync_(cfg.adaptive_sync) { } frame_scheduler::~frame_scheduler() { @@ -348,8 +350,8 @@ namespace mirage { // 清理旧资源 cleanup_swapchain_resources(); - // 重建 Swapchain - auto result = swapchain_.recreate(pending_width_, pending_height_, swapchain_.is_vsync_enabled()); + // 重建 Swapchain,传递 adaptive_sync 参数 + auto result = swapchain_.recreate(pending_width_, pending_height_, vsync_, adaptive_sync_); if (!result) { throw std::runtime_error("Failed to recreate swapchain"); } diff --git a/src/render/pipeline/frame_scheduler.h b/src/render/pipeline/frame_scheduler.h index 0d6c92d..78ca34d 100644 --- a/src/render/pipeline/frame_scheduler.h +++ b/src/render/pipeline/frame_scheduler.h @@ -15,7 +15,9 @@ namespace mirage { public: /// 配置选项 struct config { - uint32_t frames_in_flight = 2; ///< 并行帧数(双缓冲/三缓冲) + uint32_t frames_in_flight = 3; ///< 并行帧数(双缓冲/三缓冲) + bool vsync = true; ///< 垂直同步 + bool adaptive_sync = false; ///< 自适应同步(VRR) }; /// 构造函数 @@ -79,7 +81,11 @@ namespace mirage { private: logical_device& device_; swapchain& swapchain_; - config config_; + config config_; + + /// 同步配置 + bool vsync_ = true; + bool adaptive_sync_ = false; /// Acquire 信号量(按帧索引) std::vector acquire_semaphores_; diff --git a/src/render/pipeline/geometry_renderer.h b/src/render/pipeline/geometry_renderer.h index e4c26cc..f90f976 100644 --- a/src/render/pipeline/geometry_renderer.h +++ b/src/render/pipeline/geometry_renderer.h @@ -21,7 +21,7 @@ namespace mirage { public: /// 配置参数 struct config { - uint32_t frames_in_flight = 2; ///< 并行帧数 + uint32_t frames_in_flight = 3; ///< 并行帧数 uint32_t initial_vertex_capacity = 4096; ///< 初始顶点容量 uint32_t initial_index_capacity = 8192; ///< 初始索引容量 }; diff --git a/src/render/pipeline/image_renderer.h b/src/render/pipeline/image_renderer.h index bb219eb..ffc774f 100644 --- a/src/render/pipeline/image_renderer.h +++ b/src/render/pipeline/image_renderer.h @@ -21,7 +21,7 @@ namespace mirage { public: /// 配置参数 struct config { - uint32_t frames_in_flight = 2; ///< 并行帧数 + uint32_t frames_in_flight = 3; ///< 并行帧数 uint32_t initial_vertex_capacity = 1024; ///< 初始顶点容量(降低以节省内存) uint32_t initial_index_capacity = 2048; ///< 初始索引容量(降低以节省内存) uint32_t max_textures_per_frame = 32; ///< 每帧最大纹理数(降低以节省内存) diff --git a/src/render/pipeline/mask_renderer.h b/src/render/pipeline/mask_renderer.h index f1e670b..4e1bc5b 100644 --- a/src/render/pipeline/mask_renderer.h +++ b/src/render/pipeline/mask_renderer.h @@ -59,7 +59,7 @@ namespace mirage { public: /// @brief 配置参数 struct config { - uint32_t frames_in_flight = 2; ///< 并行帧数 + uint32_t frames_in_flight = 3; ///< 并行帧数 uint32_t initial_target_pool_size = 4; ///< 初始渲染目标池大小 }; diff --git a/src/render/pipeline/post_effect_applicator.h b/src/render/pipeline/post_effect_applicator.h index c695f3f..4439e14 100644 --- a/src/render/pipeline/post_effect_applicator.h +++ b/src/render/pipeline/post_effect_applicator.h @@ -75,7 +75,7 @@ namespace mirage { post_effect_applicator& operator=(const post_effect_applicator&) = delete; /// 初始化 Pipeline 和临时资源 - void initialize(uint32_t max_width, uint32_t max_height, uint32_t frames_in_flight = 2); + void initialize(uint32_t max_width, uint32_t max_height, uint32_t frames_in_flight = 3); // 注入统一目标池 void set_target_pool(mirage::render::unified_target_pool* pool) { target_pool_ = pool; } diff --git a/src/render/pipeline/render_pipeline.cpp b/src/render/pipeline/render_pipeline.cpp index 2756a56..102250c 100644 --- a/src/render/pipeline/render_pipeline.cpp +++ b/src/render/pipeline/render_pipeline.cpp @@ -67,7 +67,8 @@ namespace mirage { device_, swapchain_, frame_scheduler::config{ - .frames_in_flight = config_.frames_in_flight + .frames_in_flight = config_.frames_in_flight, + .adaptive_sync = config_.adaptive_sync } ); scheduler_->initialize(); diff --git a/src/render/pipeline/render_pipeline.h b/src/render/pipeline/render_pipeline.h index d176aa5..fb3a1a9 100644 --- a/src/render/pipeline/render_pipeline.h +++ b/src/render/pipeline/render_pipeline.h @@ -33,7 +33,8 @@ namespace mirage { public: /// 配置选项 struct config { - uint32_t frames_in_flight = 2; ///< 并行帧数(双缓冲/三缓冲) + uint32_t frames_in_flight = 3; ///< 并行帧数(双缓冲/三缓冲) + bool adaptive_sync = false; ///< 自适应同步(VRR) }; /// 构造函数 diff --git a/src/render/vulkan/swapchain.cpp b/src/render/vulkan/swapchain.cpp index 88c3815..fe5a254 100644 --- a/src/render/vulkan/swapchain.cpp +++ b/src/render/vulkan/swapchain.cpp @@ -21,7 +21,9 @@ namespace mirage { , physical_device_(physical_device) , surface_(surface) , resource_manager_(resource_manager) - , vsync_(vsync) { + , vsync_(vsync) + , adaptive_sync_(false) + , current_present_mode_(vk::PresentModeKHR::eFifo) { } swapchain::~swapchain() { @@ -44,6 +46,8 @@ namespace mirage { surface_(other.surface_), resource_manager_(other.resource_manager_), vsync_(other.vsync_), + adaptive_sync_(other.adaptive_sync_), + current_present_mode_(other.current_present_mode_), images_(std::move(other.images_)), image_views_(std::move(other.image_views_)) { other.device_ = nullptr; @@ -60,17 +64,19 @@ namespace mirage { device_.destroySwapchainKHR(swapchain_); } } - device_ = other.device_; - present_queue_ = other.present_queue_; - swapchain_ = other.swapchain_; - format_ = other.format_; - extent_ = other.extent_; - physical_device_ = other.physical_device_; - surface_ = other.surface_; - resource_manager_ = other.resource_manager_; - vsync_ = other.vsync_; - images_ = std::move(other.images_); - image_views_ = std::move(other.image_views_); + device_ = other.device_; + present_queue_ = other.present_queue_; + swapchain_ = other.swapchain_; + format_ = other.format_; + extent_ = other.extent_; + physical_device_ = other.physical_device_; + surface_ = other.surface_; + resource_manager_ = other.resource_manager_; + vsync_ = other.vsync_; + adaptive_sync_ = other.adaptive_sync_; + current_present_mode_ = other.current_present_mode_; + images_ = std::move(other.images_); + image_views_ = std::move(other.image_views_); other.device_ = nullptr; other.swapchain_ = nullptr; @@ -111,27 +117,38 @@ namespace mirage { } auto swapchain::choose_present_mode(const std::vector& available_present_modes, - bool vsync) -> vk::PresentModeKHR { - if (vsync) { - // For VSync, FIFO is the standard mode (guaranteed to be available) - // It waits for the vertical blanking period + bool vsync, + bool adaptive_sync) -> vk::PresentModeKHR { + // 自适应同步模式:优先 FIFO_RELAXED (VRR) + if (adaptive_sync) { + for (const auto& mode : available_present_modes) { + if (mode == vk::PresentModeKHR::eFifoRelaxed) { + return mode; + } + } + // 回退到 FIFO return vk::PresentModeKHR::eFifo; } - - // If VSync is disabled, prefer Mailbox (triple buffering) or Immediate (uncapped) - for (const auto& available_present_mode : available_present_modes) { - if (available_present_mode == vk::PresentModeKHR::eMailbox) { - return available_present_mode; + + // VSync 开启:使用 FIFO + if (vsync) { + return vk::PresentModeKHR::eFifo; + } + + // VSync 关闭:优先 Mailbox (三缓冲),其次 Immediate (无上限) + for (const auto& mode : available_present_modes) { + if (mode == vk::PresentModeKHR::eMailbox) { + return mode; } } - - // Fallback to Immediate if Mailbox is not available and VSync is disabled - for (const auto& available_present_mode : available_present_modes) { - if (available_present_mode == vk::PresentModeKHR::eImmediate) { - return available_present_mode; + + for (const auto& mode : available_present_modes) { + if (mode == vk::PresentModeKHR::eImmediate) { + return mode; } } - + + // 最终回退到 FIFO(保证可用) return vk::PresentModeKHR::eFifo; } @@ -161,7 +178,7 @@ namespace mirage { ) -> expected_t { auto support = query_support(physical_device, surface); auto surface_format = choose_surface_format(support.formats); - auto present_mode = choose_present_mode(support.present_modes, vsync); + auto present_mode = choose_present_mode(support.present_modes, vsync, false); auto extent = choose_extent(support.capabilities, width, height); uint32_t image_count = support.capabilities.minImageCount + 1; @@ -206,6 +223,9 @@ namespace mirage { &resource_manager, vsync ); + + // 存储当前使用的呈现模式 + new_swapchain.current_present_mode_ = present_mode; auto [img_res, images] = device.getSwapchainImagesKHR(swapchain_handle); if (img_res != vk::Result::eSuccess) { @@ -225,12 +245,13 @@ namespace mirage { return new_swapchain; } - auto swapchain::recreate(uint32_t width, uint32_t height, bool vsync) -> expected_t { + auto swapchain::recreate(uint32_t width, uint32_t height, bool vsync, bool adaptive_sync) -> expected_t { // 1. 等待设备空闲 (void)device_.waitIdle(); - - // Update vsync setting + + // Update vsync and adaptive_sync settings vsync_ = vsync; + adaptive_sync_ = adaptive_sync; // 2. 清理旧的图像视图 for (auto view : image_views_) { @@ -241,8 +262,11 @@ namespace mirage { // 3. 获取新的交换链支持信息 auto support = query_support(physical_device_, surface_); auto surface_format = choose_surface_format(support.formats); - auto present_mode = choose_present_mode(support.present_modes, vsync_); + auto present_mode = choose_present_mode(support.present_modes, vsync_, adaptive_sync_); auto extent = choose_extent(support.capabilities, width, height); + + // 存储当前使用的呈现模式 + current_present_mode_ = present_mode; uint32_t image_count = support.capabilities.minImageCount + 1; if (support.capabilities.maxImageCount > 0 && image_count > support.capabilities.maxImageCount) { @@ -372,4 +396,12 @@ namespace mirage { return render_pass; } -} // namespace mirage + + auto swapchain::is_vrr_enabled() const -> bool { + return current_present_mode_ == vk::PresentModeKHR::eFifoRelaxed; + } + + auto swapchain::get_present_mode() const -> vk::PresentModeKHR { + return current_present_mode_; + } + } // namespace mirage diff --git a/src/render/vulkan/swapchain.h b/src/render/vulkan/swapchain.h index c73394b..8ec3d89 100644 --- a/src/render/vulkan/swapchain.h +++ b/src/render/vulkan/swapchain.h @@ -13,6 +13,11 @@ namespace mirage { std::vector present_modes; }; + struct config { + bool vsync = true; + bool adaptive_sync = false; + }; + static auto create( const logical_device& logical_device, vk::PhysicalDevice physical_device, @@ -34,7 +39,7 @@ namespace mirage { swapchain(swapchain&&) noexcept; swapchain& operator=(swapchain&&) noexcept; - auto recreate(uint32_t width, uint32_t height, bool vsync) -> expected_t; + auto recreate(uint32_t width, uint32_t height, bool vsync, bool adaptive_sync = false) -> expected_t; [[nodiscard]] auto acquire_next_image(vk::Semaphore semaphore, vk::Fence fence = nullptr, uint64_t timeout = std::numeric_limits::max()) -> expected_t< @@ -47,6 +52,8 @@ namespace mirage { [[nodiscard]] auto get_extent() const { return extent_; } [[nodiscard]] auto get_format() const { return format_; } [[nodiscard]] auto is_vsync_enabled() const { return vsync_; } + [[nodiscard]] auto is_vrr_enabled() const -> bool; + [[nodiscard]] auto get_present_mode() const -> vk::PresentModeKHR; [[nodiscard]] auto create_default_render_pass() const -> expected_t; @@ -72,8 +79,10 @@ namespace mirage { // Stored for recreation vk::PhysicalDevice physical_device_; vk::SurfaceKHR surface_; - resource_manager* resource_manager_ = nullptr; - bool vsync_ = false; + resource_manager* resource_manager_ = nullptr; + bool vsync_ = false; + bool adaptive_sync_ = false; + vk::PresentModeKHR current_present_mode_ = vk::PresentModeKHR::eFifo; std::vector images_; std::vector image_views_; @@ -82,7 +91,8 @@ namespace mirage { static auto choose_surface_format( const std::vector& available_formats) -> vk::SurfaceFormatKHR; static auto choose_present_mode(const std::vector& available_present_modes, - bool vsync) -> vk::PresentModeKHR; + bool vsync, + bool adaptive_sync) -> vk::PresentModeKHR; static auto choose_extent(const vk::SurfaceCapabilitiesKHR& capabilities, uint32_t width, uint32_t height) -> vk::Extent2D; };