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

Gab*_*iel 4 iphone multithreading opengl-es objective-c

我有一个用于我正在开发的iPhone的OpenGL ES应用程序,是另一个平台的面向二维应用程序的端口.出于性能原因,我选择使用OpenGL ES渲染图形.但是,主应用程序在一个单独的线程上运行(由于原始的应用程序设计),所以从我的app委托中我执行此操作:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

但是,我注意到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];
}
Run Code Online (Sandbox Code Playgroud)

然后像我期望的那样立即将纹理更新到屏幕.

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

[NSThread sleepForTimeInterval:5.0]; // sleep for 5 seconds
Run Code Online (Sandbox Code Playgroud)

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

所以 - 有什么方法可以绕过这个,或者我做了一些明显错误的事情?

zou*_*oul 6

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

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

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

在简单的英语中,它绑定帧缓冲区,遍历游戏对象图(=渲染场景),在屏幕上显示帧缓冲内容并解除帧缓冲区的绑定.该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];
}
Run Code Online (Sandbox Code Playgroud)

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

希望有所帮助.