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

5

这个有可能吗?


我从未见过这个参数被测量过。 - Andrey
4个回答

5

不是完全不行,但你可以使用厂商提供的工具来获取不同的性能计数器,对于 NVIDIA 来说,你可以使用 NVPerfKit 和 NVPerfHUD。其他厂商也有类似的工具。


3

不行。在这样一个高度并行的环境下,严格定义它甚至都很困难。但是,您可以使用ARB_timer_query扩展来近似计算。


1
你能够举个使用 ARB_timer_query 扩展的例子吗? - Newbie

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存储在查询对象中,并在某个异步未来时间“可用”。我的代码保持已发出的计时器查询的队列,并每秒检查完成的测量。只要队列头部的计时器查询仍然可用,dumpTimerQueue就会一直打印测量结果。最终,它会遇到尚不可用的计时器并停止打印消息。
我添加了一个额外的功能,它会在测量函数中每60次调用中丢弃59次,因此对于程序中所有的仪器,它每秒只进行一次测量。这可以防止太多的垃圾信息,并使其可用于开发时将其转储到stdout,还可以防止由测量引起的过多性能干扰。这就是limiter.frame60的含义,frame60保证小于60。 它会循环回来。
虽然这并不完全回答问题,但可以通过注意所有绘制调用的经过时间与经过的墙钟时间来推断GPU使用情况。 如果帧为16ms,计时器查询TIME_ELAPSED为8ms,则可以推断大约50%的GPU使用率。
还有一点需要注意:测量是以GPU执行时间为基础进行的,通过将GPU命令放入GPU队列中来实现。 线程与此无关,如果在一个线程中执行那些enqueue内部的操作将是等效的。

0

我从未见过这样的情况。通常,您会尽可能快地呈现一帧,进行一些 CPU 帧后处理或前处理,然后呈现下一帧,因此使用率在 0% 和 100% 之间波动。只有在很少的情况下,FPS 才会限制在最大数量,并且仅在这种情况下,这才是一个有意义的数字。


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