2009-12-31 40 views
4

我有一个用于iPhone的OpenGL ES应用程序,我正在开发,作为另一个平台的面向2D应用程序的端口。出于性能考虑,我选择使用OpenGL ES来渲染图形。然而,主要应用在单独的线程上运行(由于原来的应用程序设计),所以从我的应用程序委托中我这样做:如何使iPhone OpenGL ES上下文更新直接消失?

- (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秒延迟完成后的画面仅更新。这使我相信只有在线程本身终止时才会更新屏幕(需要进行更多测试以确认)。这意味着当我用实际的应用程序代码替换多个屏幕更新时,实际上没有任何更新发生(留下白色屏幕),直到应用程序线程退出,而不是我想要的!

那么 - 有没有什么办法可以解决这个问题,还是我做了一件明显错误的事情?

+1

你能告诉我们你的'glView :: drawView'里面有什么吗? – zoul 2009-12-31 09:35:49

+0

- (void)drawView {EAGLContext setCurrentContext:context]; glBindRenderbufferOES(GL_RENDERBUFFER_OES,viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; } – Gabriel 2009-12-31 15:35:15

+0

你好同伴阳光海岸开发! – alex 2010-09-24 00:45:45

回答

6

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

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

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

用简单的英语,它结合了帧缓冲区,走游戏对象图(=渲染场景),提供对帧缓冲内容屏幕并取消绑定帧缓冲区。 presentRenderbuffer调用有点错位,它在设计中处于更高的位置 - 舞台应该渲染成帧缓冲区,让你做任何你想做的帧缓冲区。但我找不到合适的地方,所以我只是把电话留在了那里。

否则我对设计非常满意:所有的类都是简单的,一致的和可测试的。还有创建线程并尽可能快地呼吁阶段的主循环一个调度器类:

- (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环境同步。)

希望有所帮助。

+0

您是否需要使用共享组来共享线程之间的上下文? – Gabriel 2009-12-31 15:48:59

+0

绝对有帮助,并表明我的问题是在别的东西。 – Gabriel 2009-12-31 16:21:06

+0

不需要共享组,我甚至不知道那是什么。 – zoul 2010-01-01 00:22:02

0

看来我错过了出血明显...

glViewport(0, 0, rect.size.width, rect.size.height); 
glScissor(0, 0, rect.size.width, rect.size.height); 

...和更新显示,因为他们应该。我认为发生的事情是没有在使用共享组的子线程上下文(在父线程上下文中设置)上设置视口和剪刀,只有当退出的子线程使用正确的视口更新视图时,才会最终显示我的更新。或类似的东西! (我仍然是一个OpenGLES新手!)