修复 UnrealInsights 帧率显示问题

UnrealInsights 是 UE4 新的性能分析工具,可以方便地实时 Profile 游戏性能。但是使用过程中发现一个问题,当游戏帧率稳定的时候,UnrealInsights 里显示的帧率仍然是上下波动的,如图

framerate_before.png

这样会对性能分析造成困扰,因为看不出哪里是真正的帧率波动了。经过修复后的帧率图如下

framerate_after.png

这样就能很容易看出哪里产生了性能问题

分析问题

产生这个问题的原因是 UnrealInsights 收集一帧耗时不正确,弄错了一帧的开始时间和结束时间。当在 UnrealInsights 中选择一帧的时候,它显示的范围如图

frametime.png

Insights 收集的一帧时间,实际上是前一帧的后半部分加上后一帧的前半部分,因此就造成实际帧率稳定平滑,但是 Insights 却显示的不平滑的问题

具体代码在 FEngineLoop::Tick 中,UnrealInsights 收集的一帧时间就是这个函数的总时间,但是实际上这个函数应该拆成两部分

第一个 SCOPE_CYCLE_COUNTER(STAT_FrameTime); 所处的代码块其实还是上一个 Tick 的内容,会计算上一个 Tick 的耗时和帧率,如果不超过 16ms(60帧) 或者 33ms(30帧),则会在 UpdateTimeAndHandleMaxTickRate 中进行 Sleep 来限制帧率

第二个 SCOPE_CYCLE_COUNTER(STAT_FrameTime); 代码块才是真正的的下一帧的开始,会在这里递增 GFrameCounter。因此 UnrealInsights 收集 CPU 一帧的性能宏 TRACE_BEGIN_FRAME(TraceFrameType_Game)TRACE_END_FRAME(TraceFrameType_Game),应该放在这里,如下

void FEngineLoop::Tick()
{
    static bool bTraceStart = false; // 记录是否开始了 Trace

    {
        // TRACE_BEGIN_FRAME(TraceFrameType_Game); 注释掉原有的帧开始
        SCOPE_CYCLE_COUNTER(STAT_FrameTime);
        
        // 其他代码
        // ...
    }

    if (bTraceStart)
    {
        TRACE_END_FRAME(TraceFrameType_Game); // 结束上一帧
    }
    
    bTraceStart = true;
    TRACE_BEGIN_FRAME(STAT_FrameTime); // 开始下一帧

    {
        SCOPE_CYCLE_COUNTER( STAT_FrameTime );
        
        // 其他代码
        // ...

        // TRACE_END_FRAME(TraceFrameType_Game); 注释原有的帧结束
    }
}

这样收集的一帧时间戳就能和实际的帧率正确对应起来了,如图

frametime_correction.png