如何制作一个基本的FPS计数器?

11

我试图在我的立方体渲染程序中显示每秒帧数。我想要看到它的性能。那么,我该怎么做呢?我已经对此进行了研究,但是我看到的示例要么使用多个类仍然无法工作,要么使用我没有的库。是否有一种方法可以通过使用预安装的库(如ctime)来获取FPS?我正在使用C++的OpenGL。

这是我的(空)函数:

void GetFPS()
{

}

然后我使用以下代码在我的渲染函数中显示我的FPS:

std::cout << xRot << " " << yRot << " " << zRot << " " << FPS << "\n"; //xRot, yRot, and zRot are my cube's rotation.

我的程序设置为60FPS,但我想看到实际的FPS,而不是设置的FPS。


你使用的是哪个操作系统?不同的操作系统可能需要不同的代码。 - K_Finlay
我使用的是Windows,抱歉我忘了提到这一点。 - Xenonic
另一个答案的问题在于除以0,除此之外是正确的(当然,由于你将有90%的帧时间为0,这有点棘手,请参见我的答案)。 - CoffeDeveloper
可能是 如何计算OpenGL中的FPS? 的重复问题。 - Ciro Santilli OurBigBook.com
可能的重复显示了一种获得粗略近似值的可能方法。这里的答案更完整,所以我不会投票关闭。 - Adrian McCarthy
显示剩余2条评论
6个回答

21

你需要使用clock()采样两个不同的时间间隔,但是需要注意以下几个问题:

  • clock()的分辨率只有几毫秒(你可以使用std :: chrono等方式解决此问题,但是根据实现情况,即使使用chrono,分辨率也可能不太高。在我的PC上,使用GCC 4.9.1时,即使使用std :: chrono,我也从未获得比16毫秒更好的分辨率)
  • 通常使用clock(),你会多次得到0,而某一次你会测量出真正的时间(在我的情况下,它会跳15/16毫秒)
  • 除非您使用垂直同步(vsync),否则您将无法测量实际帧时间,而只是在渲染循环中花费的CPU时间(要激活vsync,您必须使用SetSwapInterval(1)来使用操作系统函数或例如使用像SDL这样提供可移植跨平台实现的库)
  • 要测量实际渲染时间,您可以使用GL的时间查询(您只能绑定一个计时器,因此如果您正在测量帧速率,则不能测量渲染某些特定内容所需的时间)。
  • 不要测量FPS(除非您只想向用户显示它),而是测量以毫秒为单位的帧时间,这可以更直观地近似性能(您知道从100到80 FPS的差距是2.5毫秒,而从40到20 FPS的差距是25毫秒!)

按照上述方法操作:

double clockToMilliseconds(clock_t ticks){
    // units/(units/time) => time (seconds) * 1000 = milliseconds
    return (ticks/(double)CLOCKS_PER_SEC)*1000.0;
}
//...

clock_t deltaTime = 0;
unsigned int frames = 0;
double  frameRate = 30;
double  averageFrameTimeMilliseconds = 33.333;

while(rendering){

    clock_t beginFrame = clock();
    render();
    clock_t endFrame = clock();

    deltaTime += endFrame - beginFrame;
    frames ++;

    //if you really want FPS
    if( clockToMilliseconds(deltaTime)>1000.0){ //every second
        frameRate = (double)frames*0.5 +  frameRate*0.5; //more stable
        frames = 0;
        deltaTime -= CLOCKS_PER_SEC;
        averageFrameTimeMilliseconds  = 1000.0/(frameRate==0?0.001:frameRate);

        if(vsync)
            std::cout<<"FrameTime was:"<<averageFrameTimeMilliseconds<<std::endl;
        else
           std::cout<<"CPU time was:"<<averageFrameTimeMilliseconds<<std::endl;
    }
}

当你执行需要几秒钟的操作时,上面的代码同样有效。我进行了一项计算,每秒更新一次,你也可以更频繁地进行更新。(注意:我在大多数需要FPS的项目中使用这个代码)


如果你感到困惑,我可以重构代码使其更易读。 - CoffeDeveloper
@Synthetic:既然你在使用Windows系统,可以看一下这个链接(https://dev59.com/53I-5IYBdhLWcg3wqqTh)和这个链接(https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx)。性能计数器应该可以提供你非常高的精度(<1微秒)。 - BDL
2
为什么FPS最多只有2位小数时需要1000000倍的额外精度? - CoffeDeveloper

8

只需在渲染场景前后保存时间“刻度”,然后进行简单的计算。

以下是使用<ctime>clock()函数的示例。(请注意,clock()在不同的平台上有所不同)

clock_t current_ticks, delta_ticks;
clock_t fps = 0;
while(true)// your main loop. could also be the idle() function in glut or whatever
{
    current_ticks = clock();

    render();

    delta_ticks = clock() - current_ticks; //the time, in ms, that took to render the scene
    if(delta_ticks > 0)
        fps = CLOCKS_PER_SEC / delta_ticks;
    cout << fps << endl;
}

1
@Synthetic 或许 clock() 不够精确?你可以尝试使用 std::chrono::high_resolution_clock - HolyBlackCat
1
有了像OpenGL这样的API,其中渲染异步在GPU上进行,这将无法测量实际的渲染时间。 - Reto Koradi
我尝试了我写的代码。它按预期工作。尝试用Sleep()函数替换render(),以便调查它的工作原理。 - McLovin
除零的时候感到开心! glFinish 只是将所有命令发送到 GPU。 GPU 处理这些命令需要多长时间是未知的,除非您使用特定的功能,例如计时器查询!请查看我的回答。 - CoffeDeveloper
@DarioOO 你错过了if语句吗? - McLovin
显示剩余7条评论

4

在任何循环中调用此函数以测量每秒钟的调用次数。

#include <chrono>

void printFPS() {
    static std::chrono::time_point<std::chrono::steady_clock> oldTime = std::chrono::high_resolution_clock::now();
    static int fps; fps++;

    if (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - oldTime) >= std::chrono::seconds{ 1 }) {
        oldTime = std::chrono::high_resolution_clock::now();
        std::cout << "FPS: " << fps <<  std::endl;
        fps = 0;
    }
}

0

快速而完整

#include <time.h>   // clock
#include <stdio.h>  // snprintf

unsigned int iMemClock, iCurClock, iLoops;
char aFPS[12];

/* main loop */
{
    if (iMemClock > (iCurClock = clock()))
        iLoops++;
    else
    {
        snprintf(aFPS, sizeof(aFPS),"FPS: %d",iLoops);
        iMemClock = iCurClock + CLOCKS_PER_SEC;
        iLoops = 0;
    }

    /*
    someRoutines(); 
    */
}
/* main loop */

clock() 不是最好的函数,因为它不能计算睡眠时间,可能还有其他问题。 - HolyBlackCat

0
如果您只是为了打印而测量FPS,可以使用std::chrono,因为它测量的是墙钟时间。使用std::clock()std::chrono测量更准确,因为它测量的是处理时间,但也许您不想以如此高的精度打印FPS。
下面的解决方案使用std::chrono来计算程序的正常运行时间,并在每次帧更新后递增帧计数器。将帧计数器除以程序的正常运行时间即可得到FPS。
#include <chrono>
#include <iostream>
#include <thread>

using namespace std::chrono;

steady_clock::time_point first_tp;
unsigned long frame_count = 0;

duration<double> uptime()
{
    if (first_tp == steady_clock::time_point{})
        return duration<double>{ 0 };

    return steady_clock::now() - first_tp;
}

double fps()
{
    const double uptime_sec = uptime().count();

    if (uptime_sec == 0)
        return 0;

    return frame_count / uptime_sec;
}

void time_consuming_function()
{
    std::this_thread::sleep_for(milliseconds{ 100 });
}

void run_forever()
{
    std::cout << "fps at first: " << fps() << '\n';

    first_tp = std::chrono::steady_clock::now();

    while (true)
    {
        std::cout << "fps: " << fps() << '\n';
        
        time_consuming_function();

        frame_count++;
    }
}

int main()
{
    run_forever();
}

在我的电脑上运行它会产生:

$ ./measure_fps
fps at first: 0
fps: 0
fps: 9.99108
fps: 9.99025
fps: 9.98997
fps: 9.98984

如果将其适应于std::clock(),则会得到

$ ./measure_fps
fps at first: 0
fps: 0
fps: 37037
fps: 25316.5
fps: 23622
fps: 22346.4

0
我试了这些方法都没成功。这是我使用这种方法来获取毫秒的解决方案。
// Gets the time in milliseconds
#include <chrono>
typedef std::chrono::system_clock Clock;

auto ms() {
    auto now = Clock::now();
    auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
    auto fraction = now - seconds;

    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(fraction);
    return milliseconds.count();
}

// Main loop
auto prevTime = ms();
while (running) {
  frames++;

  processRender();

  if (deltaTime >= 1000) {
    // Do something here every second
    second();
    deltaTime = 0;
    frames = 0;
  }

  auto currTime = ms();
  diff = currTime - prevTime;
  if (diff > 0) {
    deltaTime += diff;
  }
  prevTime = currTime;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接