2009-12-31 69 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新手!)