使用OpenGL在OS X上以不同的速率进行渲染和更新

时间:2016-06-11 22:18:48

标签: macos cocoa opengl

我的目标是构建一个针对OSX 10.4+的原生Cocoa应用程序,该应用程序使用OpenGL呈现,以便移植我正在构建的游戏。我的问题是,按照Apple的技术说明(见下面的链接),我无法弄清楚如何在Cocoa中分离更新和渲染。这是我的问题:Cocoa应用程序如何以不同的速率更新?换句话说,Cocoa的渲染是基于事件的,我不想等待事件来更新我的模拟。

以下是我工作的内容:

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

    - (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
    {
        //TODO: find somewhere better to put this.
        //TODO: the magic number is not doing what I expect it to.
        float dt = (outputTime->videoTime - lastCapturedTime) / 36000000.0f;
        lastCapturedTime = outputTime->videoTime;
        update(dt);

        NSOpenGLContext *currentContext = [self openGLContext];
        [currentContext makeCurrentContext];

        // must lock GL context because display link is threaded
        CGLLockContext((CGLContextObj)[currentContext CGLContextObj]);

        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        render();
        glFlush();

        [currentContext flushBuffer];

        CGLUnlockContext((CGLContextObj)[currentContext CGLContextObj]);
        return kCVReturnSuccess;
    }

我不喜欢上面的代码是渲染和更新发生在同一个事件上,而事件的目的是操作系统告诉我的应用程序它应该渲染一个框架。

在Windows上,将下面的代码放在WinMain()中会给我正是我正在寻找的东西。

while(true) {
    process_events();  //PeekMessage, handle WM_ events
    int elapsedTime = GetElapsedSeconds();
    prepare(elapsedTime);
    if (fps_lock(elapsedTime, 60)) {
        render();
    }
    swapBuffers();
}

这就是我所看到的(我发布的内容不仅仅是两个链接,因为我已经完成了一系列研究,但我的低级代表阻止了我这样做):

提前感谢您指出我可能错过的内容,或完全不合时宜的事情。

1 个答案:

答案 0 :(得分:0)

我假设update()prepare()是类似的,只是更新物理等,而不是做任何OpenGL。你想尽快/频繁地做到这一点吗?

如果您的代码是线程安全的,则可以基于计时器执行此操作。您可以在串行队列上使用Grand Central Dispatch timer source。同样,您可以在运行其运行循环的线程的运行循环上使用NSTimer。现在,应用程序的主线程运行其运行循环,主调度队列是一个串行队列,但您可能不想使用主线程/队列,因为这会干扰UI事件处理。因此,您可能希望使用自己生成的自定义队列或线程,并运行其运行循环。

如果需要将渲染与更新同步,则可以使用同步原语(如调度信号量),或者可以执行诸如将渲染同步提交到计时器调度源所针对的同一串行队列之类的操作。或者使用-performSelector:onThread:withObject:waitUntilDone:定位您的主题。

顺便说一句,在你的Windows伪代码中,为什么要在没有渲染的传递上交换缓冲区呢?