【问题标题】:Missing frames when drawing in CVDisplayLink Callback在 CVDisplayLink 回调中绘制时缺少帧
【发布时间】:2015-12-01 00:02:28
【问题描述】:

在用于科学研究的 OS X 中,我们使用 NSOpenGLView 在第二个 LCD 显示器上渲染图形。如果我们将绘图代码放在 drawRect: 中并使用计时器驱动它,那效果很好。但是,我们需要更好地与帧速率同步,因此我们正在尝试迁移到 CVDisplayLink,如Apple Q&A 1385 中所述。

我们使用了该示例的清单 1 中的代码,除了调用 CVDisplayLinkCreateWithActiveCGDisplays() 之外,我们调用 CVDisplayLinkCreateWithCGDisplay() 并传入第二个 LCD 显示器的 ID。

我们已将绘图代码从 -drawRect: 移至一个新的 -draw 方法,该方法由我们的 CVDisplayLink 回调调用,如下面的代码所示。

我注意到 OS X 创建的不是一个而是 两个 线程,名为 CVDisplayLink。他们交替调用我们的回调。这正常吗?

随机地,在大约 10% 的帧上,我们的绘图代码创建的场景会根据需要出现在第二个 LCD 显示器上。对于其他 90% 的帧,此显示显示垃圾或纯白色,即使我认为我们正在回调中清除显示。

哦,我继承了这个项目,并且是 OpenGL 的新手,所以它可能非常简单,或者与我的描述完全无关。我会很感激任何线索。

- (void)prepareOpenGL
{
    GLint swapInt = 1;
    stimGLContext = [self openGLContext];
    [stimGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];

    // Create a display link capable
    CVDisplayLinkCreateWithCGDisplay(self.displayID, &displayLink);

    // Set the renderer output callback function
    CVDisplayLinkSetOutputCallback(
                                   displayLink,
                                   &MyDisplayLinkCallback,
                                   (__bridge void *)(self));

    // Set the display link for the current renderer
    CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
    CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
                                                      displayLink,
                                                      cglContext,
                                                      cglPixelFormat);

    // Activate the display link
    CVDisplayLinkStart(displayLink);
}

// Renderer output callback function
static CVReturn MyDisplayLinkCallback(
                                      CVDisplayLinkRef displayLink,
                                      const CVTimeStamp* now,
                                      const CVTimeStamp* outputTime,
                                      CVOptionFlags flagsIn,
                                      CVOptionFlags* flagsOut,
                                      void* displayLinkContext)
{
    CVReturn result = [(__bridge StimulusGLView*)displayLinkContext getFrameForTime:outputTime];
    return result;
}

- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
{
    /* We are on a secondary "CVDisplayLink" thread here. */

    NSOpenGLContext* context = [self openGLContext];
    [context makeCurrentContext];
    CGLLockContext([context CGLContextObj]);

    [self draw] ;

    CGLFlushDrawable([context CGLContextObj]);
    CGLUnlockContext([context CGLContextObj]);

    return kCVReturnSuccess;
}

- (void)draw
{
    //  Clear out and show a solid blue background
    glClearColor((GLclampf)0, (GLclampf), (GLclampf).8, 0);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    ... more glXxxxx() drawing statements follow here
}

【问题讨论】:

    标签: macos opengl


    【解决方案1】:

    尝试删除对CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext() 的调用:文档说它“选择刷新率最低的显示器”。我认为这个调用再次设置了显示链接的CGDisplay,可能是与self.displayID 不同的显示并导致您的问题。

    如果这不起作用,您可以尝试使用CVDisplayLinkCreateWithActiveCGDisplays(),然后调用CVDisplayLinkSetCurrentCGDisplay() 将其设置为您的辅助显示器。

    【讨论】:

    • 谢谢你,布伦丹。我尝试了您的两个建议,以及我想到的一些细微变化,但仍然得到相同的结果。你知道两个CVDisplayLink线程交替运行正常吗?
    • 我刚刚测试了我的(相当简单的)OpenGL 应用程序,它使用了CVDisplayLink,并且只从一个线程调用回调。
    • 谢谢你提供的线索,布伦丹。我暂时把它放在一边,等我弄明白了再回来。
    【解决方案2】:

    啊,我应该发布更多代码。

    我在 -draw 中省略的代码中有一个对 -[NSOpenGLContext flushBuffer] 的调用。这是从我们不使用 CVDisplayLink 的旧代码继承而来的。当我将 Apple 的 CVDisplayLink 示例代码粘贴到我们的项目中时,我没有意识到他们代码中的 CGLFlushDrawable() 等效于它的 Objective-C 包装器 -[NSOpenGLContext flushBuffer],我们已经在 -draw 中做了。

    有趣的是,我在试验时发现,如果缓冲区每帧刷新奇数次(1 或 3 次)看起来不错,但如果像我所做的那样,缓冲区刷新偶数次( 2)。我认为这是有道理的,因为这个上下文是双缓冲的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      相关资源
      最近更新 更多