游戏引擎从零开始(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熟悉下。

相关推荐
编程版小新2 分钟前
C++初阶:STL详解(四)——vector迭代器失效问题
开发语言·c++·迭代器·vector·迭代器失效
AlexMercer10121 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
小灰灰爱代码2 小时前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
BeyondESH3 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++
豆浩宇3 小时前
Halcon OCR检测 免训练版
c++·人工智能·opencv·算法·计算机视觉·ocr
WG_173 小时前
C++多态
开发语言·c++·面试
Charles Ray5 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码5 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
迷迭所归处11 小时前
C++ —— 关于vector
开发语言·c++·算法
CV工程师小林11 小时前
【算法】BFS 系列之边权为 1 的最短路问题
数据结构·c++·算法·leetcode·宽度优先