【问题标题】:Why does OpenGL double-buffering seem to be inconsistent performance-wise?为什么 OpenGL 双缓冲在性能方面似乎不一致?
【发布时间】:2013-03-01 19:17:39
【问题描述】:

我正在 OSX 上制作一个简单的图形应用程序。我刚开始通过检查帧之间的系统时间差异来监控帧速率。它似乎每隔几帧就会挂起。

应用在 NSTimer 对象上运行,当前设置为 200 fps(5 毫秒帧)。为了确保不仅仅是我的主代码变慢了,我注释掉了整个主循环,除了 [self.context flushBuffer]; (当然还有帧率采样代码)。即使重复这个单一的调用也会导致它一次挂起几帧。这是我的控制台输出的示例:

4
5
6
10
5
5
5
4
6
10
5
5
5
5
5
5
20
4
5
6
20
5
5
5

这是每次循环迭代之间的时间(以毫秒为单位)。在 200 fps 时,每帧应该是 5 毫秒左右,但它会反复挂起 10、15 甚至 20 毫秒。正如您在我的 -initOpenGL 函数中所见,垂直同步已禁用。

-(void)initOpenGL{
    NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFADoubleBuffer, 0};
    NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: attributes];
    self.context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
    [self.context setView:[self.window contentView]];
    [self.context makeCurrentContext];

    //Disable Vsync
    int temp = 0;
    [self.context setValues: &temp forParameter: NSOpenGLCPSwapInterval];

    glViewport(0, 0, windowWidth, windowHeight);
    glMatrixMode(GL_PROJECTION);
    glOrtho(0, windowWidth, 0, windowHeight, -1, 100);

    //Flip all textures right side up
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glScalef(1.0f, -1.0f, 1.0f);

    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_SCISSOR_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

编辑,新信息:我可以确认这与双缓冲与单缓冲有关。当我初始化没有 DoubleBuffer 属性的 NSOpenGLPixelFormat 时,它以 200 fps 的速度完美运行。而有了这个属性,我仍然会结结巴巴。

【问题讨论】:

    标签: objective-c macos opengl frame-rate double-buffering


    【解决方案1】:

    在不知道实际渲染代码(未发布)的情况下,一个很可能的原因是首先使用NSTimer 进行帧速率控制并不是一个好主意。请参阅文档(已添加重点):

    计时器不是实时机制;它仅在已添加计时器的运行循环模式之一正在运行并且能够检查计时器的触发时间是否已过时触发。由于典型的运行循环管理的各种输入源,计时器的时间间隔的有效分辨率限制在 50-100 毫秒的数量级。 p>

    因此,期望使用 5 毫秒的计时器从根本上来说是有缺陷的。

    除此之外,可能还有其他影响(进程调度、纹理上传和 OpenGL 中的停顿等)会使帧时间不是 100% 恒定。虽然由这些引起的抖动通常会小得多。

    【讨论】:

    • 感谢您的回复!我一直认为 NSTimer 是 Apple 设备上帧率控制的标准。它似乎比其他任何东西都更频繁地使用。什么是更好的选择?另外,我省略了渲染代码,因为它似乎与问题无关。就像我说的那样,即使所有内容都被注释掉了,这种情况也会发生,而且我实际上只是在调用 [context flushBuffer];。因此,我们可以排除代码中的任何内容似乎是合乎逻辑的(除非我在 OpenGL 中不正确地初始化了某些内容)。
    • 我也只是缩小了一点;阅读 OP 上的编辑。
    • Apple 以及我所知道的任何其他平台上的帧速率控制标准是启用垂直同步。计时器(或更糟糕的是,睡眠)不是而且从来没有成为“标准”,因为它们具有不可预测的非实时性质(在非 RT 操作系统上,例如 OSX)。此外,使用计时器是不明智的,因为两个不同的时钟(应用计时器和垂直同步)最终总是会不同步(想想两辆车上的指示灯!)。我不知道为什么关闭双缓冲似乎“工作”更好,但这并不能改变这种方法存在缺陷的事实。
    • 谢谢各位,看完the Run Loop reference,我意识到NSTimer的真正用途,我应该只使用一个while循环并自己控制时间。你会惊讶于我看到有多少人以这种方式滥用计时器。
    【解决方案2】:

    如果您想在 OSX 上实现良好的同步,您应该使用 CoreVideo DisplayLink。它是在每次屏幕垂直刷新时调用的回调,因此如果您的屏幕为 60 FPS,则此回调将每秒调用 60 次(在单独的线程中,请注意 OpenGL 线程),这就是应该触发 OpenGL 刷新的原因.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-07
      • 1970-01-01
      • 2012-07-29
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      相关资源
      最近更新 更多