Files
mirai/src/render/frame_sync.cpp
2025-12-31 12:27:48 +08:00

444 lines
14 KiB
C++

/**
* @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<vulkan_device> device,
std::shared_ptr<swapchain> 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_begin_result, u32> 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<vk::Fence> 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<swapchain> 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<f64, std::milli>(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<f64>(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