feat: 帧率控制——手动限帧 + VSync 开关
- Application 帧率控制 API:SetLimitFps/SetTargetFps/SetVSync。 限帧目标最低 24fps(防止过低刷新导致交互卡死),无上限; vsync 开启时 present 自带节奏,手动限帧不生效 - 限帧策略:sleep 到剩 ~2ms 后自旋,精度高且不烧 CPU - Swapchain 加 VSync 开关:ChoosePresentMode 按开关选 Mailbox(低延迟垂直同步)/ Immediate(不限速可撕裂), FIFO 兜底;切换经 SetVSync → 重建 swapchain(复用 HandleResize) - Scene Debug 面板新增 Frame 区块(vsync/limit/目标滑条) - Run 循环新增 CPU 归属遥测(update/ui/render/total,每 120 帧)
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
@@ -67,6 +68,13 @@ namespace Engine {
|
||||
[] { return ImGui::GetIO().WantCaptureMouse; });
|
||||
}
|
||||
|
||||
void Application::SetVSync(bool vsync)
|
||||
{
|
||||
if (m_VSync == vsync) return;
|
||||
m_VSync = vsync;
|
||||
m_Renderer->SetVSync(vsync);
|
||||
}
|
||||
|
||||
void Application::Run()
|
||||
{
|
||||
ENGINE_INFO("Application starting");
|
||||
@@ -110,6 +118,26 @@ namespace Engine {
|
||||
// Present,draw data 在 ImGui pass 里才真正提交 GPU ----
|
||||
m_Renderer->Render(*m_Scene, *m_Assets);
|
||||
|
||||
// ---- 帧率控制:vsync 开启时 present 自带节奏;否则按目标限帧 ----
|
||||
// 策略:先 sleep 到剩 ~2ms,再自旋——精度高且不烧 CPU
|
||||
if (!m_VSync && m_LimitFps) {
|
||||
const auto deadline = frameStart +
|
||||
std::chrono::microseconds(static_cast<long long>(
|
||||
1e6 / m_TargetFps));
|
||||
auto now2 = std::chrono::steady_clock::now();
|
||||
if (now2 < deadline) {
|
||||
const auto remaining = std::chrono::duration_cast<
|
||||
std::chrono::milliseconds>(deadline - now2);
|
||||
if (remaining.count() > 2) {
|
||||
std::this_thread::sleep_for(
|
||||
remaining - std::chrono::milliseconds(2));
|
||||
}
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
// 自旋等待最后 ~2ms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CPU 侧遥测(每 120 帧一行):定位帧耗时的 CPU 归属
|
||||
const auto frameEnd = std::chrono::steady_clock::now();
|
||||
static u32 cpuTelemetryCount = 0;
|
||||
|
||||
@@ -754,6 +754,13 @@ namespace Engine
|
||||
m_RenderGraph->SetTargetExtent(m_Extent); // 触发首次 Compile
|
||||
}
|
||||
|
||||
void Renderer::SetVSync(bool vsync)
|
||||
{
|
||||
if (m_Swapchain->GetVSync() == vsync) return;
|
||||
m_Swapchain->SetVSync(vsync);
|
||||
HandleResize(); // present mode 变化需要重建 swapchain
|
||||
}
|
||||
|
||||
void Renderer::HandleResize()
|
||||
{
|
||||
m_Device->GetDevice().waitIdle();
|
||||
|
||||
@@ -151,9 +151,15 @@ namespace Engine
|
||||
|
||||
vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector<vk::PresentModeKHR>& modes)
|
||||
{
|
||||
// vsync 开:Mailbox(三缓冲垂直同步,低延迟);关:Immediate
|
||||
//(不限速,可能撕裂)。FIFO 是所有实现必须支持的兜底
|
||||
const vk::PresentModeKHR preferred = m_VSync
|
||||
? vk::PresentModeKHR::eMailbox
|
||||
: vk::PresentModeKHR::eImmediate;
|
||||
|
||||
for (const auto& m : modes)
|
||||
{
|
||||
if (m == vk::PresentModeKHR::eMailbox)
|
||||
if (m == preferred)
|
||||
{
|
||||
return m;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user