/** * @file frame_sync.cpp * @brief MIRAI 框架帧同步机制实现 * @author MIRAI Team * @version 0.1.0 */ #include "frame_sync.hpp" #include "logger.hpp" namespace mirai { // ================================================================================================ // frame_sync 实现 // ================================================================================================ frame_sync::frame_sync(std::shared_ptr device, std::shared_ptr swap_chain, const frame_sync_config& config) : device_(std::move(device)) , swapchain_(std::move(swap_chain)) , frames_in_flight_(config.frames_in_flight) , fence_timeout_(config.fence_timeout) { // 验证参数 if (frames_in_flight_ < 1) { frames_in_flight_ = 1; } if (frames_in_flight_ > 3) { frames_in_flight_ = 3; } // 创建同步对象和命令缓冲 if (!create_sync_objects()) { MIRAI_LOG_ERROR("Failed to create sync objects"); return; } if (!create_command_buffers()) { MIRAI_LOG_ERROR("Failed to create command buffers"); destroy_sync_objects(); return; } // 初始化图像飞行中栅栏 u32 image_count = swapchain_ ? swapchain_->get_image_count() : 0; images_in_flight_.resize(image_count, nullptr); MIRAI_LOG_DEBUG("Frame sync created with {} frames in flight", frames_in_flight_); } frame_sync::~frame_sync() { if (device_ && device_->is_valid()) { // 等待设备空闲 device_->wait_idle(); // 销毁资源 destroy_command_buffers(); destroy_sync_objects(); } } std::pair frame_sync::begin_frame() { if (!is_valid() || !swapchain_) { return {frame_begin_result::error, 0}; } const auto& resources = frame_resources_[current_frame_]; // 等待当前帧的栅栏 vk::Result result = device_->get_device().waitForFences(resources.in_flight_fence, vk::True, fence_timeout_); if (result == vk::Result::eTimeout) { MIRAI_LOG_WARN("Frame fence wait timed out"); return {frame_begin_result::timeout, 0}; } else if (result == vk::Result::eErrorDeviceLost) { MIRAI_LOG_ERROR("Device lost while waiting for frame fence"); return {frame_begin_result::device_lost, 0}; } else if (result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to wait for frame fence: {}", vk::to_string(result)); return {frame_begin_result::error, 0}; } // 获取下一个交换链图像 auto [acquire_result, image_index] = swapchain_->acquire_next_image( resources.image_available_semaphore); if (acquire_result == acquire_result::out_of_date) { return {frame_begin_result::swapchain_out_of_date, 0}; } else if (acquire_result != acquire_result::success && acquire_result != acquire_result::suboptimal) { MIRAI_LOG_ERROR("Failed to acquire swapchain image"); return {frame_begin_result::error, 0}; } current_image_index_ = image_index; // 检查图像是否仍在使用中 if (images_in_flight_[image_index]) { device_->get_device().waitForFences(images_in_flight_[image_index], vk::True, fence_timeout_); } // 标记该图像为当前帧正在使用 images_in_flight_[image_index] = resources.in_flight_fence; // 重置栅栏 device_->get_device().resetFences(resources.in_flight_fence); // 调用帧开始回调 if (on_frame_begin_) { on_frame_begin_(current_frame_, current_image_index_); } return {frame_begin_result::success, current_image_index_}; } frame_end_result frame_sync::end_frame(vk::CommandBuffer cmd_buffer) { if (!is_valid() || !swapchain_) { return frame_end_result::error; } const auto& resources = frame_resources_[current_frame_]; // 提交命令缓冲 vk::SubmitInfo submit_info{}; vk::Semaphore wait_semaphores[] = {resources.image_available_semaphore}; vk::PipelineStageFlags wait_stages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput}; submit_info.setWaitSemaphores(wait_semaphores) .setWaitDstStageMask(wait_stages) .setCommandBuffers(cmd_buffer); vk::Semaphore signal_semaphores[] = {resources.render_finished_semaphore}; submit_info.setSignalSemaphores(signal_semaphores); vk::Result submit_result = device_->get_graphics_queue().submit(submit_info, resources.in_flight_fence); if (submit_result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to submit command buffer: {}", vk::to_string(submit_result)); return frame_end_result::error; } // 呈现图像 auto present_result = swapchain_->present(current_image_index_, resources.render_finished_semaphore); // 调用帧结束回调 if (on_frame_end_) { on_frame_end_(current_frame_); } // 推进到下一帧 advance_frame(); // 检查呈现结果 if (present_result == present_result::out_of_date) { return frame_end_result::swapchain_out_of_date; } else if (present_result == present_result::suboptimal) { return frame_end_result::swapchain_suboptimal; } else if (present_result != present_result::success) { return frame_end_result::error; } return frame_end_result::success; } frame_end_result frame_sync::end_frame() { return end_frame(frame_resources_[current_frame_].command_buffer); } bool frame_sync::wait_for_current_frame() { if (!is_valid()) { return false; } const auto& resources = frame_resources_[current_frame_]; vk::Result result = device_->get_device().waitForFences(resources.in_flight_fence, vk::True, fence_timeout_); if (result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to wait for current frame: {}", vk::to_string(result)); return false; } return true; } bool frame_sync::wait_for_all_frames() { if (!is_valid()) { return false; } // 收集所有栅栏 std::vector fences; fences.reserve(frames_in_flight_); for (const auto& resources : frame_resources_) { if (resources.in_flight_fence) { fences.push_back(resources.in_flight_fence); } } if (fences.empty()) { return true; } vk::Result result = device_->get_device().waitForFences(fences, vk::True, fence_timeout_); if (result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to wait for all frames: {}", vk::to_string(result)); return false; } return true; } bool frame_sync::recreate(std::shared_ptr new_swapchain) { if (!device_ || !device_->is_valid()) { return false; } // 等待所有帧完成 wait_for_all_frames(); // 更新交换链 swapchain_ = std::move(new_swapchain); // 重新初始化图像飞行中栅栏 u32 image_count = swapchain_ ? swapchain_->get_image_count() : 0; images_in_flight_.clear(); images_in_flight_.resize(image_count, nullptr); // 重置当前帧索引 current_frame_ = 0; current_image_index_ = 0; MIRAI_LOG_DEBUG("Frame sync recreated for new swapchain"); return true; } void frame_sync::on_created() { MIRAI_LOG_DEBUG("Frame sync created"); } void frame_sync::on_destroying() { MIRAI_LOG_DEBUG("Frame sync destroying"); } bool frame_sync::create_sync_objects() { if (!device_ || !device_->is_valid()) { return false; } frame_resources_.resize(frames_in_flight_); vk::SemaphoreCreateInfo semaphore_info{}; vk::FenceCreateInfo fence_info{}; fence_info.flags = vk::FenceCreateFlagBits::eSignaled; // 初始状态为已信号 for (u32 i = 0; i < frames_in_flight_; ++i) { auto& resources = frame_resources_[i]; // 创建图像可用信号量 auto image_sem_result = device_->get_device().createSemaphore(semaphore_info); if (image_sem_result.result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to create image available semaphore: {}", vk::to_string(image_sem_result.result)); return false; } resources.image_available_semaphore = image_sem_result.value; // 创建渲染完成信号量 auto render_sem_result = device_->get_device().createSemaphore(semaphore_info); if (render_sem_result.result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to create render finished semaphore: {}", vk::to_string(render_sem_result.result)); return false; } resources.render_finished_semaphore = render_sem_result.value; // 创建飞行中栅栏 auto fence_result = device_->get_device().createFence(fence_info); if (fence_result.result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to create in flight fence: {}", vk::to_string(fence_result.result)); return false; } resources.in_flight_fence = fence_result.value; } MIRAI_LOG_DEBUG("Created {} sets of sync objects", frames_in_flight_); return true; } bool frame_sync::create_command_buffers() { if (!device_ || !device_->is_valid()) { return false; } // 创建命令池 vk::CommandPoolCreateInfo pool_info{}; pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; if (device_->get_queue_family_indices().graphics_family.has_value()) { pool_info.queueFamilyIndex = device_->get_queue_family_indices().graphics_family.value(); } else { MIRAI_LOG_ERROR("No graphics queue family found"); return false; } auto pool_result = device_->get_device().createCommandPool(pool_info); if (pool_result.result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to create command pool: {}", vk::to_string(pool_result.result)); return false; } command_pool_ = pool_result.value; // 分配命令缓冲 vk::CommandBufferAllocateInfo alloc_info{}; alloc_info.commandPool = command_pool_; alloc_info.level = vk::CommandBufferLevel::ePrimary; alloc_info.commandBufferCount = frames_in_flight_; auto alloc_result = device_->get_device().allocateCommandBuffers(alloc_info); if (alloc_result.result != vk::Result::eSuccess) { MIRAI_LOG_ERROR("Failed to allocate command buffers: {}", vk::to_string(alloc_result.result)); return false; } auto buffers = alloc_result.value; for (u32 i = 0; i < frames_in_flight_; ++i) { frame_resources_[i].command_buffer = buffers[i]; } MIRAI_LOG_DEBUG("Created command pool and {} command buffers", frames_in_flight_); return true; } void frame_sync::destroy_sync_objects() { if (!device_ || !device_->is_valid()) { return; } for (auto& resources : frame_resources_) { if (resources.image_available_semaphore) { device_->get_device().destroySemaphore(resources.image_available_semaphore); resources.image_available_semaphore = nullptr; } if (resources.render_finished_semaphore) { device_->get_device().destroySemaphore(resources.render_finished_semaphore); resources.render_finished_semaphore = nullptr; } if (resources.in_flight_fence) { device_->get_device().destroyFence(resources.in_flight_fence); resources.in_flight_fence = nullptr; } } } void frame_sync::destroy_command_buffers() { if (!device_ || !device_->is_valid()) { return; } // 命令缓冲在命令池销毁时自动释放 if (command_pool_) { device_->get_device().destroyCommandPool(command_pool_); command_pool_ = nullptr; } for (auto& resources : frame_resources_) { resources.command_buffer = nullptr; } } // ================================================================================================ // frame_time_tracker 实现 // ================================================================================================ frame_time_tracker::frame_time_tracker(u32 sample_count) : sample_count_(sample_count) { frame_time_samples_.resize(sample_count, 0.0); } void frame_time_tracker::begin_frame() { frame_start_time_ = std::chrono::high_resolution_clock::now(); } void frame_time_tracker::end_frame() { auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration(end_time - frame_start_time_); current_frame_time_ms_ = duration.count(); // 更新样本 frame_time_samples_[current_sample_index_] = current_frame_time_ms_; current_sample_index_ = (current_sample_index_ + 1) % sample_count_; if (!samples_filled_ && current_sample_index_ == 0) { samples_filled_ = true; } // 更新统计 ++frame_count_; total_time_s_ += current_frame_time_ms_ / 1000.0; } frame_timing frame_time_tracker::get_timing() const { frame_timing timing; timing.cpu_frame_time_ms = current_frame_time_ms_; timing.gpu_frame_time_ms = 0.0; // GPU 时间需要使用 GPU 查询获取 timing.fps = current_frame_time_ms_ > 0.0 ? 1000.0 / current_frame_time_ms_ : 0.0; timing.frame_count = frame_count_; timing.total_time_s = total_time_s_; return timing; } f64 frame_time_tracker::get_average_frame_time_ms() const { u32 count = samples_filled_ ? sample_count_ : current_sample_index_; if (count == 0) { return 0.0; } f64 sum = 0.0; for (u32 i = 0; i < count; ++i) { sum += frame_time_samples_[i]; } return sum / static_cast(count); } f64 frame_time_tracker::get_average_fps() const { f64 avg_time = get_average_frame_time_ms(); return avg_time > 0.0 ? 1000.0 / avg_time : 0.0; } void frame_time_tracker::reset() { std::fill(frame_time_samples_.begin(), frame_time_samples_.end(), 0.0); current_sample_index_ = 0; samples_filled_ = false; current_frame_time_ms_ = 0.0; frame_count_ = 0; total_time_s_ = 0.0; } } // namespace mirai