OpenGL:如何获得GPU使用百分比?

时间:2010-09-23 12:07:23

标签: c++ opengl

这甚至可能吗?

4 个答案:

答案 0 :(得分:4)

不是真的,但您可以使用供应商的实用程序获得不同的性能计数器,对于您拥有NVPerfKit和NVPerfHUD的NVIDIA。其他供应商也有类似的实用程序。

答案 1 :(得分:2)

不。在如此高度并行的环境中严格定义甚至很难。但是,您可以使用ARB_timer_query扩展名来近似它。

答案 2 :(得分:1)

我在OpenGL渲染线程实现中实现了基于计时器查询的GPU执行时间测量框架。我将在下面分享计时器查询部分:

假设

  • enqueue在渲染线程上运行一个函数
  • limiter.frame60每60帧仅等于0

代码:

struct TimerQuery
{
    std::string description;
    GLuint timer;
};
typedef std::deque<TimerQuery> TimerQueryQueue;

...

TimerQueryQueue timerQueryQueue;

...

void GlfwThread::beginTimerQuery(std::string description)
{
    if (limiter.frame60 != 0)
        return;

    enqueue([this](std::string const& description) {
        GLuint id;
        glGenQueries(1, &id);
        timerQueryQueue.push_back({ description, id });
        glBeginQuery(GL_TIME_ELAPSED, id);
    }, std::move(description));
}

void GlfwThread::endTimerQuery()
{
    if (limiter.frame60 != 0)
        return;

    enqueue([this]{
        glEndQuery(GL_TIME_ELAPSED);
    });
}


void GlfwThread::dumpTimerQueries()
{
    while (!timerQueryQueue.empty())
    {
        TimerQuery& next = timerQueryQueue.front();

        int isAvailable = GL_FALSE;
        glGetQueryObjectiv(next.timer,
                           GL_QUERY_RESULT_AVAILABLE,
                           &isAvailable);
        if (!isAvailable)
            return;

        GLuint64 ns;
        glGetQueryObjectui64v(next.timer, GL_QUERY_RESULT, &ns);

        DebugMessage("timer: ",
                     next.description, " ",
                     std::fixed,
                     std::setprecision(3), std::setw(8),
                     ns / 1000.0, Stopwatch::microsecText);

        glDeleteQueries(1, &next.timer);

        timerQueryQueue.pop_front();
    }
}

以下是一些示例输出:

Framerate t=5.14 fps=59.94 fps_err=-0.00 aet=2850.67μs adt=13832.33μs alt=0.00μs cpu_usage=17%
instanceCount=20301 parallel_μs=2809
timer: text upload range    0.000μs
timer: clear and bind   95.200μs
timer: upload    1.056μs
timer: draw setup    1.056μs
timer: draw  281.568μs
timer: draw cleanup    1.024μs
timer: renderGlyphs    1.056μs
Framerate t=6.14 fps=59.94 fps_err=0.00 aet=2984.55μs adt=13698.45μs alt=0.00μs cpu_usage=17%
instanceCount=20361 parallel_μs=2731
timer: text upload range    0.000μs
timer: clear and bind   95.232μs
timer: upload    1.056μs
timer: draw setup    1.024μs
timer: draw  277.536μs
timer: draw cleanup    1.056μs
timer: renderGlyphs    1.024μs
Framerate t=7.14 fps=59.94 fps_err=-0.00 aet=3007.05μs adt=13675.95μs alt=0.00μs cpu_usage=18%
instanceCount=20421 parallel_μs=2800
timer: text upload range    0.000μs
timer: clear and bind   95.232μs
timer: upload    1.056μs
timer: draw setup    1.056μs
timer: draw  281.632μs
timer: draw cleanup    1.024μs
timer: renderGlyphs    1.056μs

这允许我在我的opengl绘制调用之前调用renderThread->beginTimerQuery("draw some text");或者之后调用renderThread->endTimerQuery();来测量经过的GPU执行时间。

这里的想法是,它在测量部分之前向GPU命令队列发出命令,因此glBeginQuery TIME_ELAPSED记录某个实现定义的计数器的值。 glEndQuery发出GPU命令来存储当前计数与TIME_ELAPSED查询开头存储的计数之间的差异。该结果由GPU存储在查询对象中,并且可用于&#34;在某个异步的未来时间。我的代码保留了一个已发出的计时器查询队列,每秒检查一次以完成测量。只要队列头部的计时器查询仍然可用,我的dumpTimerQueue就会一直打印测量值。最终它会命中一个暂时不可用的计时器并停止打印消息。

我添加了一个额外的功能,它在测量功能的60次调用中掉了59次,所以它只对我程序中的所有仪器每秒测量一次。这可以防止过多的垃圾邮件并使其可用于转储到stdout进行开发,并防止测量引起的过多性能干扰。这就是limiter.frame60的东西,frame60保证&lt;它结束了。

虽然这并不能完美地回答这个问题,但您可以通过记录所有绘制调用的已用时间与经过的挂钟时间来推断GPU使用情况。如果帧为16ms且定时器查询TIME_ELAPSED为8ms,则可以推断出大约50%的GPU使用率。

还有一点需要注意:测量是通过将GPU命令放入GPU队列来测量GPU执行时间。线程与它无关,如果enqueue内的操作在一个线程中执行则相当。

答案 3 :(得分:0)

我从未见过这样的事情。通常,您尽可能快地渲染帧进行一些CPU帧后处理或预处理并渲染下一帧,因此使用率会在0到100%之间fla。 FPS仅限于最大数量,并且只有在这种情况下这才是有意义的数字。