2

一个小的测试程序(见下文)显示了不稳定的输出,我认为这是由 OpenGL glutSwapBuffer() 期间奇怪的时序抖动引起的。与平均水平相比,测试程序经常显示单个帧的渲染时间非常长(~330 微秒与我机器上的~5.8 毫秒)程序的输出显示:

[...]
render time:   330us
render time:   372us
render time:   330us
render time:   331us
render time:  5820us
render time:   335us
render time:   332us
render time:   324us
render time:   346us
[...]

设置:快速 PC(Intel i7 CPU @3.2GHz)、Ubuntu 12.04、NVIDIA GTS450 板、最新的 GL 驱动程序:OpenGL 渲染器字符串:GeForce GTS 450/PCIe/SSE2 OpenGL 版本字符串:4.3.0 NVIDIA 310.19 Samsung Display (23" @我假设是 60Hz,因为所有这些都有 60Hz)

在上面的测试用例中,我通过以下方式明确否决了 vblank 同步:__GL_SYNC_TO_VBLANK=0

启用 vblank 后,我的时间也很不稳定:

__GL_SYNC_TO_VBLANK=1

[...]
render time:  9169us
render time: 14548us
render time: 14613us
render time: 16057us
render time: 18075us
[...]

我还使用了其他参数,例如将 X 服务器置于高优先级(sudo schedtool -p 0 -n -20 -N pgrep X)或将程序本身置于高优先级(chrt -p -a --fifo 99 ... )

仍然生涩的动画仍然存在 - 这(这是我的假设)是由奇怪的时间引起的。

没有 `glutSwapBuffers();' 的计时 在代码中显示了非常稳定的输出:

[...]
render time:     2us
render time:     2us
render time:     2us
render time:     1us
render time:     2us
[...]

因此可以排除外部(其他过程)影响。

谁能解释这种行为?

知道如何改进吗?

该程序:

#include <stdio.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <time.h>

GLfloat gfPosX = 0.0;
GLfloat gfDeltaX = .01;

#define TS_TOUSEC(x) (x.tv_sec * 1000000L + (x.tv_nsec / 1000))
struct timespec t1, t2;

void Draw() {
    int x = 0;

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);

    clock_gettime(CLOCK_REALTIME, &t1);
    glBegin(GL_LINES);
        glVertex3f(gfPosX, 0.25, 0.0);
        glVertex3f(gfPosX, 0.75,0.0);
    glEnd();
    clock_gettime(CLOCK_REALTIME, &t2);
    glutSwapBuffers();
    printf("render time: %5uus\n", TS_TOUSEC(t2) - TS_TOUSEC(t1));

    gfPosX += gfDeltaX;
    if (gfPosX >= 1.0 || gfPosX <= 0.0) {
        gfDeltaX = -gfDeltaX;
    }
}

void Timer(int iUnused)
{
    glutPostRedisplay();
    glutTimerFunc(5, Timer, 0);
}

void Initialize() {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}

int main(int iArgc, char** cppArgv) {
    glutInit(&iArgc, cppArgv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(250, 250);
    glutInitWindowPosition(200, 200);
    glutCreateWindow("XoaX.net");
    Initialize();
    glutDisplayFunc(Draw);
    Timer(0);
    glutMainLoop();
    return 0;
}
4

1 回答 1

0

我不会使用这个:

clock_gettime(CLOCK_REALTIME, &t1);

CLOCK_REALTIME 显示墙上时间(即挂在墙上的时钟显示的时间),即不保证是单调的。它可能会稍微向前或向后,因为时间守护程序会对其进行调整。

要测量帧渲染时间,您需要 CLOCK_MONOTONIC_RAW。

在没有 SwapBuffers 的情况下,您只是在测量发出渲染调用所需的时间。不是渲染时间本身。您必须添加一个 glFinish 来获得它,以便实际执行命令队列。

出于所有实际目的,您不应该花费时间来渲染一帧,而是各个帧之间的时间,即调用显示函数之间的时间间隔。这提供了更准确的动画。

为了解决输入滞后问题,我会使用卡尔曼滤波器来预测渲染完成时的输入状态(因为您需要帧渲染时间,只需收集统计信息)并使用预测的输入状态进行帧推进。

于 2013-01-20T14:29:47.787 回答