代码生成完成,待整理
This commit is contained in:
511
src/render/frame_sync.cpp
Normal file
511
src/render/frame_sync.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
/**
|
||||
* @file frame_sync.cpp
|
||||
* @brief MILAI 框架帧同步机制实现
|
||||
* @author MILAI Team
|
||||
* @version 0.1.0
|
||||
*/
|
||||
|
||||
#include "frame_sync.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
namespace milai {
|
||||
|
||||
// ================================================================================================
|
||||
// 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()) {
|
||||
LOG_ERROR("Failed to create sync objects");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!create_command_buffers()) {
|
||||
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, VK_NULL_HANDLE);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
frame_sync::frame_sync(frame_sync&& other) noexcept
|
||||
: device_(std::move(other.device_))
|
||||
, swapchain_(std::move(other.swapchain_))
|
||||
, command_pool_(other.command_pool_)
|
||||
, frame_resources_(std::move(other.frame_resources_))
|
||||
, images_in_flight_(std::move(other.images_in_flight_))
|
||||
, frames_in_flight_(other.frames_in_flight_)
|
||||
, current_frame_(other.current_frame_)
|
||||
, current_image_index_(other.current_image_index_)
|
||||
, fence_timeout_(other.fence_timeout_)
|
||||
, on_frame_begin_(std::move(other.on_frame_begin_))
|
||||
, on_frame_end_(std::move(other.on_frame_end_))
|
||||
{
|
||||
other.command_pool_ = VK_NULL_HANDLE;
|
||||
other.frames_in_flight_ = 0;
|
||||
other.current_frame_ = 0;
|
||||
other.current_image_index_ = 0;
|
||||
}
|
||||
|
||||
frame_sync& frame_sync::operator=(frame_sync&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// 清理现有资源
|
||||
if (device_ && device_->is_valid()) {
|
||||
device_->wait_idle();
|
||||
destroy_command_buffers();
|
||||
destroy_sync_objects();
|
||||
}
|
||||
|
||||
// 移动资源
|
||||
device_ = std::move(other.device_);
|
||||
swapchain_ = std::move(other.swapchain_);
|
||||
command_pool_ = other.command_pool_;
|
||||
frame_resources_ = std::move(other.frame_resources_);
|
||||
images_in_flight_ = std::move(other.images_in_flight_);
|
||||
frames_in_flight_ = other.frames_in_flight_;
|
||||
current_frame_ = other.current_frame_;
|
||||
current_image_index_ = other.current_image_index_;
|
||||
fence_timeout_ = other.fence_timeout_;
|
||||
on_frame_begin_ = std::move(other.on_frame_begin_);
|
||||
on_frame_end_ = std::move(other.on_frame_end_);
|
||||
|
||||
// 清空源对象
|
||||
other.command_pool_ = VK_NULL_HANDLE;
|
||||
other.frames_in_flight_ = 0;
|
||||
other.current_frame_ = 0;
|
||||
other.current_image_index_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
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_];
|
||||
|
||||
// 等待当前帧的栅栏
|
||||
VkResult result = vkWaitForFences(device_->get_device(), 1,
|
||||
&resources.in_flight_fence,
|
||||
VK_TRUE, fence_timeout_);
|
||||
|
||||
if (result == VK_TIMEOUT) {
|
||||
LOG_WARN("Frame fence wait timed out");
|
||||
return {frame_begin_result::timeout, 0};
|
||||
} else if (result == VK_ERROR_DEVICE_LOST) {
|
||||
LOG_ERROR("Device lost while waiting for frame fence");
|
||||
return {frame_begin_result::device_lost, 0};
|
||||
} else if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to wait for frame fence: {}", vk_result_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) {
|
||||
LOG_ERROR("Failed to acquire swapchain image");
|
||||
return {frame_begin_result::error, 0};
|
||||
}
|
||||
|
||||
current_image_index_ = image_index;
|
||||
|
||||
// 检查图像是否仍在使用中
|
||||
if (images_in_flight_[image_index] != VK_NULL_HANDLE) {
|
||||
vkWaitForFences(device_->get_device(), 1,
|
||||
&images_in_flight_[image_index],
|
||||
VK_TRUE, fence_timeout_);
|
||||
}
|
||||
|
||||
// 标记该图像为当前帧正在使用
|
||||
images_in_flight_[image_index] = resources.in_flight_fence;
|
||||
|
||||
// 重置栅栏
|
||||
vkResetFences(device_->get_device(), 1, &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(VkCommandBuffer cmd_buffer) {
|
||||
if (!is_valid() || !swapchain_) {
|
||||
return frame_end_result::error;
|
||||
}
|
||||
|
||||
const auto& resources = frame_resources_[current_frame_];
|
||||
|
||||
// 提交命令缓冲
|
||||
VkSubmitInfo submit_info{};
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
VkSemaphore wait_semaphores[] = {resources.image_available_semaphore};
|
||||
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = wait_semaphores;
|
||||
submit_info.pWaitDstStageMask = wait_stages;
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &cmd_buffer;
|
||||
|
||||
VkSemaphore signal_semaphores[] = {resources.render_finished_semaphore};
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = signal_semaphores;
|
||||
|
||||
VkResult result = vkQueueSubmit(device_->get_graphics_queue(), 1,
|
||||
&submit_info, resources.in_flight_fence);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to submit command buffer: {}", vk_result_to_string(result));
|
||||
return frame_end_result::error;
|
||||
}
|
||||
|
||||
// 呈现图像
|
||||
VkSemaphore present_wait_semaphores[] = {resources.render_finished_semaphore};
|
||||
auto present_result = swapchain_->present(device_->get_present_queue(),
|
||||
{present_wait_semaphores, 1});
|
||||
|
||||
// 调用帧结束回调
|
||||
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_];
|
||||
VkResult result = vkWaitForFences(device_->get_device(), 1,
|
||||
&resources.in_flight_fence,
|
||||
VK_TRUE, fence_timeout_);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to wait for current frame: {}", vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool frame_sync::wait_for_all_frames() {
|
||||
if (!is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 收集所有栅栏
|
||||
std::vector<VkFence> fences;
|
||||
fences.reserve(frames_in_flight_);
|
||||
for (const auto& resources : frame_resources_) {
|
||||
if (resources.in_flight_fence != VK_NULL_HANDLE) {
|
||||
fences.push_back(resources.in_flight_fence);
|
||||
}
|
||||
}
|
||||
|
||||
if (fences.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VkResult result = vkWaitForFences(device_->get_device(),
|
||||
static_cast<u32>(fences.size()),
|
||||
fences.data(), VK_TRUE, fence_timeout_);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to wait for all frames: {}", vk_result_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, VK_NULL_HANDLE);
|
||||
|
||||
// 重置当前帧索引
|
||||
current_frame_ = 0;
|
||||
current_image_index_ = 0;
|
||||
|
||||
LOG_DEBUG("Frame sync recreated for new swapchain");
|
||||
return true;
|
||||
}
|
||||
|
||||
void frame_sync::on_created() {
|
||||
LOG_DEBUG("Frame sync created");
|
||||
}
|
||||
|
||||
void frame_sync::on_destroying() {
|
||||
LOG_DEBUG("Frame sync destroying");
|
||||
}
|
||||
|
||||
bool frame_sync::create_sync_objects() {
|
||||
if (!device_ || !device_->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame_resources_.resize(frames_in_flight_);
|
||||
|
||||
VkSemaphoreCreateInfo semaphore_info{};
|
||||
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
|
||||
VkFenceCreateInfo fence_info{};
|
||||
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; // 初始状态为已信号
|
||||
|
||||
for (u32 i = 0; i < frames_in_flight_; ++i) {
|
||||
auto& resources = frame_resources_[i];
|
||||
|
||||
// 创建图像可用信号量
|
||||
VkResult result = vkCreateSemaphore(device_->get_device(), &semaphore_info,
|
||||
nullptr, &resources.image_available_semaphore);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create image available semaphore: {}",
|
||||
vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建渲染完成信号量
|
||||
result = vkCreateSemaphore(device_->get_device(), &semaphore_info,
|
||||
nullptr, &resources.render_finished_semaphore);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create render finished semaphore: {}",
|
||||
vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建飞行中栅栏
|
||||
result = vkCreateFence(device_->get_device(), &fence_info,
|
||||
nullptr, &resources.in_flight_fence);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create in flight fence: {}",
|
||||
vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 创建命令池
|
||||
VkCommandPoolCreateInfo pool_info{};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
pool_info.queueFamilyIndex = device_->get_queue_family_indices().graphics_family;
|
||||
|
||||
VkResult result = vkCreateCommandPool(device_->get_device(), &pool_info,
|
||||
nullptr, &command_pool_);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create command pool: {}", vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 分配命令缓冲
|
||||
std::vector<VkCommandBuffer> buffers(frames_in_flight_);
|
||||
|
||||
VkCommandBufferAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
alloc_info.commandPool = command_pool_;
|
||||
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
alloc_info.commandBufferCount = frames_in_flight_;
|
||||
|
||||
result = vkAllocateCommandBuffers(device_->get_device(), &alloc_info, buffers.data());
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to allocate command buffers: {}", vk_result_to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < frames_in_flight_; ++i) {
|
||||
frame_resources_[i].command_buffer = buffers[i];
|
||||
}
|
||||
|
||||
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 != VK_NULL_HANDLE) {
|
||||
vkDestroySemaphore(device_->get_device(),
|
||||
resources.image_available_semaphore, nullptr);
|
||||
resources.image_available_semaphore = VK_NULL_HANDLE;
|
||||
}
|
||||
if (resources.render_finished_semaphore != VK_NULL_HANDLE) {
|
||||
vkDestroySemaphore(device_->get_device(),
|
||||
resources.render_finished_semaphore, nullptr);
|
||||
resources.render_finished_semaphore = VK_NULL_HANDLE;
|
||||
}
|
||||
if (resources.in_flight_fence != VK_NULL_HANDLE) {
|
||||
vkDestroyFence(device_->get_device(),
|
||||
resources.in_flight_fence, nullptr);
|
||||
resources.in_flight_fence = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void frame_sync::destroy_command_buffers() {
|
||||
if (!device_ || !device_->is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 命令缓冲在命令池销毁时自动释放
|
||||
if (command_pool_ != VK_NULL_HANDLE) {
|
||||
vkDestroyCommandPool(device_->get_device(), command_pool_, nullptr);
|
||||
command_pool_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
for (auto& resources : frame_resources_) {
|
||||
resources.command_buffer = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 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 milai
|
||||
Reference in New Issue
Block a user