iphone - 如何使 iPhone OpenGL ES 上下文立即更新?

标签 iphone objective-c multithreading opengl-es

我有一个用于我正在开发的 iPhone 的 OpenGL ES 应用程序,它是来自另一个平台的面向 2d 的应用程序的端口。出于性能原因,我选择使用 OpenGL ES 渲染图形。但是,主应用程序在单独的线程上运行(由于最初的应用程序设计),所以我在我的应用程序委托(delegate)中这样做:

- (void) applicationDidFinishLaunching:(UIApplication *)application {
    CGRect rect = [[UIScreen mainScreen] bounds];
    glView = [[EAGLView alloc] initWithFrame:rect];
    [window addSubview:glView];

    // launch main application in separate thread
    [NSThread detachNewThreadSelector:@selector(applicationMainThread) toTarget:self withObject:nil];
}

但是,我注意到在 applicationMainThread 中尝试将某些内容呈现到屏幕上的任何调用都不会呈现任何内容,直到该线程终止为止。

我在子应用程序线程上设置实际的 OpenGL ES 上下文,而不是在 UI 线程上。如果我这样做:

- (void) applicationMainThread {
    CGRect rect = [[UIScreen mainScreen] bounds];
    [glView createContext]; // creates the open GL ES context

    //Initialize OpenGL states
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glMatrixMode(GL_PROJECTION);
    glOrthof(0, rect.size.width, 0, rect.size.height, -1, 1);
    glMatrixMode(GL_MODELVIEW);

    Texture2D *tex = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"iphone_default.png"]];

    glBindTexture(GL_TEXTURE_2D, [tex name]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glDisable(GL_BLEND);
    [tex drawInRect:[glView bounds]];
    glEnable(GL_BLEND);

    [tex release];

    [glView drawView];
}

然后纹理几乎立即更新到屏幕上,正如我所期望的那样。

但是,如果在 [glView drawView] 调用之后添加这一行:

[NSThread sleepForTimeInterval:5.0]; // sleep for 5 seconds

然后屏幕仅在 5 秒延迟完成后更新。这让我相信屏幕只会在线程本身终止时更新(需要做更多的测试来确认)。这意味着当我替换执行多个屏幕更新的实际应用程序代码时,在应用程序线程退出之前没有任何更新实际发生(留下白屏),这不是我想要的!

那么 - 有什么办法可以解决这个问题,还是我做错了什么?

最佳答案

您一定是在做一些明显错误的事情,因为多线程 OpenGL 渲染在 iPhone 上工作得很好。我不能告诉你你的代码有什么问题,但我可以告诉你我们是怎么做的。我花了好几次迭代才到达那里,因为来自 Apple 的示例 OpenGL 代码将所有内容混合在一起。

最后我想出了三个类:Stage、Framebuffer和GLView。 Stage 包含游戏渲染逻辑,并且知道如何将自己渲染到帧缓冲区。 framebuffer 类是 OpenGL framebuffer 的包装器并渲染到渲染缓冲区或 EAGLDrawable。 GLView 是渲染帧缓冲区的可绘制对象,它包含所有 OpenGL 设置内容。在应用程序入口点,我创建了一个 OpenGL 上下文、一个 GLView、一个渲染到此 GLView 的帧缓冲区和一个使用帧缓冲区渲染的舞台。 Stage update 方法在单独的线程中运行,看起来有点像这样:

- (void) mainLoop
{
    [fbuffer bind];
    [currentScene visit];
    [[EAGLContext currentContext]
        presentRenderbuffer:GL_RENDERBUFFER_OES];
    [fbuffer unbind];
}

用简单的英语来说,它绑定(bind)帧缓冲区,遍历游戏对象图(= 渲染场景),在屏幕上呈现帧缓冲区内容并取消绑定(bind)帧缓冲区。 presentRenderbuffer 调用有点放错了地方,它属于设计中更高的地方——舞台应该只渲染到帧缓冲区中,让你可以用帧缓冲区做任何你想做的事情。但是我找不到合适的地方,所以我就把电话留在那里了。

除此之外,我对设计非常满意:所有类都简单、连贯且可测试。还有一个 Scheduler 类,它创建线程并尽可能快地调用 Stage 的 mainLoop:

- (void) loop
{
    [EAGLContext setCurrentContext:context];
    while (running)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        @synchronized (context)
        {
            [stage mainLoop];
        }
        [fpsCounter update];
        [pool release];
    }
}

- (void) run
{
    NSAssert(!running, @"Scheduler already running.");
    running = YES;
    [fpsCounter reset];
    context = [EAGLContext currentContext];
    [NSThread detachNewThreadSelector:@selector(loop)
        toTarget:self withObject:nil];
}

游戏更新线程使用 OpenGL 上下文进行同步,因此我们可以确保不会破坏主线程中的上下文。 (简单规则:所有绘图都必须在游戏更新循环中完成或由 GL 上下文同步。)

希望对您有所帮助。

关于iphone - 如何使 iPhone OpenGL ES 上下文立即更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1984539/

相关文章:

iphone - 最佳实践 : Animating a view into display onto iPhone

java - 为什么我的 URLClassLoader 无法在另一个线程中工作?

c++ - 我听说 i++ 不是线程安全的,++i 是线程安全的吗?

ios - 创建存储患者信息的对象。需要在整个应用程序中使用它

c# - 添加 UITabBarController 时出错

iphone - 获取 NULL 值解析 JSON 字符串

iphone - 在4.2 SDK中打印-希望支持4.0而不会导致应用崩溃

ios - 二进制表达式的操作数无效错误

iphone - UIButton 渐变不起作用

java - Swing 定时器同步