游戏引擎从零开始(31)-性能分析工具

前言

性能耗时分析是工程中非常重要的一块,所有的技术深入到后面都是在扣性能。

这节我们先暂停引擎核心功能的开发,做一个简单的耗时统计。用到了c++11的chrono库。

耗时统计(Profile)

删掉无用的代码

暂时我们不需要显示imGui的Demo,影响美观,先删掉。

Sandbox/Hazel/src/Hazel/ImGui/ImGuiLayer.h

c++ 复制代码
...
  virtual void OnAttach() override;
  virtual void OnDetach() override;
  //virtual void OnImGuiRender() override;

  void Begin();
  void End();

Sandbox/Hazel/src/Hazel/ImGui/ImGuiLayer.cpp

c++ 复制代码
...
//    void ImGuiLayer::OnImGuiRender() {
//        static bool show = true;
//        ImGui::ShowDemoWindow(&show);
//    }

    ImGuiLayer::ImGuiLayer() : Layer("ImGuiLayer"){}
}

性能统计实现

Sandbox2D中增加ProfileResult,记录耗时,用std::vector存储。

Sandbox/src/Sandbox2D.h

c++ 复制代码
class Sandbox2D : public Hazel::Layer {
public:
    ...
    struct ProfileResult
    {
        const char* Name;
        float Time;
    };

    std::vector<ProfileResult> m_ProfileResults;

    glm::vec4 m_SquareColor = {0.2f, 0.3f, 0.8f, 1.0f};
};

Sandbox/src/Sandbox2D.cpp 增加一个自动记录起始时间的工具类Timer,构造时作为起始时间,释放时在虚函数中记录end时间。

c++ 复制代码
template<typename Fn>
class Timer
{
public:
    Timer(const char* name, Fn&& func) : m_Name(name), m_Func(func), m_Stopped(false)
    {
        // 构造时,记录start时间
        m_StartTimepoint = std::chrono::high_resolution_clock::now();
    }
    ~Timer()
    {
        // 释放时,记录end时间
        if(!m_Stopped) {
            Stop();
        }
    }

    void Stop() {
        // 获取end时间
        auto endTimPoint = std::chrono::high_resolution_clock::now();
        // start时间转成低精度的微妙microseconds
        // time_since_epoch从相对的开始时间到现在经过了多少时间(chrono底层用的可能是system时间,或者是某个其他的时钟)
        // .count转成数量值,计算机最小时间刻度微秒,过了几个微秒就是count返回的值
        long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
        // end时间返回的count值
        long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimPoint).time_since_epoch().count();

        m_Stopped = true;
        // *0.001转成毫秒值
        float duration = (end - start) * 0.001f;
        // 将统计名称、时长回调回去
        m_Func({m_Name, duration});
    }
private:
    const char* m_Name;
    Fn m_Func;
    std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
    bool m_Stopped;
};

分别记录Update和Render阶段的耗时,这两步也是引擎管线中最重要的两步,一个更新场景状态,一个渲染。

c++ 复制代码
#include <chrono>

...

// 设计一个宏定义来构造Timer,代码更整洁
// __LINE__表示行号
#define PROFILE_SCOPE(name) Timer timer##__LINE__(name, [&](ProfileResult profileResult) { \
    m_ProfileResults.push_back(profileResult); })

...

void Sandbox2D::OnUpdate(Hazel::Timestep ts) {
    // Update 第一个耗时统计
    {
        PROFILE_SCOPE("Sandbox2D::OnUpdate");
        m_CameraController.OnUpdate(ts);
    }


    // Render 第二个耗时统计
    {
        PROFILE_SCOPE("Renderer Draw");
        Hazel::RenderCommand::SetClearColor({0.1f, 0.1f, 0.1f, 1.0});
        Hazel::RenderCommand::Clear();
        ...
    }
}

// OnImGuiRender中将update中记录的耗时数据绘制出来
void Sandbox2D::OnImGuiRender() {
    ImGui::Begin("Settings");
    ImGui::ColorEdit4("Square Color", glm::value_ptr(m_SquareColor));

    // 遍历统计的数据,每一条绘制成一个ImGui::Text
    for (auto& result : m_ProfileResults) {
        char label[50];
        strcpy(label, "%.3fms "); // %.3f表示长度为3的float型
        strcat(label, result.Name);
        ImGui::Text(label, result.Time);
    }

    // 每次都需要清理
    m_ProfileResults.clear();

    ImGui::End();
}

代码运行正常的话,能看到界面中出现统计的数据,实时更新

代码 & 总结

代码:
github.com/summer-go/H...

总结:

本章节没有任何游戏、图形的知识,涉及c++开发中很常用的两种编程思路。

  1. 将Timer设计成通用的工具。

通过回调,把不同的逻辑抽离出去,这个Timer就是通用的。

  1. 自动计时。

将起止时间的记录操作放到构造函数和虚函数中,然后将调用的逻辑放到代码块中,充分利用构造函数和虚函数。这种思路在C++中很常见,简单高效。

另外,用到了std::chrono库,不熟悉的同学可以查查API熟悉下。

相关推荐
过过过呀Glik1 小时前
在 Ubuntu 上安装 Muduo 网络库的详细指南
linux·c++·ubuntu·boost·muduo
蜀黍@猿2 小时前
【C++ 基础】从C到C++有哪些变化
c++
Am心若依旧4092 小时前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(一):从电梯出发的状态模式State Pattern
c++·决策树·状态模式
轩辰~2 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
lxyzcm2 小时前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
蜀黍@猿3 小时前
C/C++基础错题归纳
c++
雨中rain3 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
ALISHENGYA4 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战项目二)
数据结构·c++·算法
arong_xu4 小时前
现代C++锁介绍
c++·多线程·mutex