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
[...]

セットアップ: Fast 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と思います)

上記のテストケースでは、__GL_SYNC_TO_VBLANK=0 によって vblank 同期を明示的に却下しました。

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 に答える