How to configure iPhone OpenGL ES contextual update right away?

I have an OpenGL ES application for the iPhone that I am developing as a port of a 2d-oriented application from another platform. I decided to display graphics using OpenGL ES for performance reasons. However, the main application runs in a separate thread (due to the original design of the application), so from my application delegate I do this:

- (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]; } 

However, I notice that any calls in applicationMainThread that try to display something on the screen do not display anything until this thread completes.

I set the actual OpenGL ES context in the thread of the child application, and not in the user interface thread. If I do this:

 - (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]; } 

Then the texture is updated on the screen almost immediately, as expected.

However, if after calling [glView drawView] I add this line:

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

Then the screen is updated only after the 5-second delay. This leads me to believe that the screen is updated only when the stream itself ends (more testing is needed to confirm). This means that when I replace the actual code of an application that performs many screen updates, none of the updates actually happen (leaving a white screen) until the application stream exits, and not what I wanted!

So - is there a way around this, or have I done something obviously wrong?

+4
source share
2 answers

You should be doing something clearly wrong, as OpenGL multi-threaded rendering works fine on iPhone. I can’t tell you what is wrong with your code, but I can show you how we do it. It took me a few iterations to get there, because Apple's OpenGL code sample brings it all together.

In the end, I came up with three classes: Stage, Framebuffer, and GLView. The stage contains the game's playback logic and knows how to display itself in the framebuffer. The framebuffer class is a wrapper around an OpenGL framebuffer and displays a EAGLDrawable or EAGLDrawable . GLView is the ability to draw a framebuffer; it contains all the OpenGL settings. At the application entry point, I create an OpenGL context, GLView, a framebuffer that displays this GLView, and a stage that displays using the framebuffer. The Stage update method works in a separate thread and looks something like this:

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

In plain English, it links the framebuffer, looks at the graph of the game object (= creates a scene), presents the contents of the framebuffer on the screen, and unties the framebuffer. The call to presentRenderbuffer little out of place, it is somewhere higher in the design - the Stage should just render into the framebuffer and let you do whatever you want to do with the framebuffer. But I could not find a suitable place, so I just left the call there.

Otherwise, I am satisfied with the design: all classes are simple, consistent, and verifiable. Theres also a Scheduler class that creates a thread and calls Stages mainLoop as quickly as possible:

 - (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]; } 

No game updates are synchronized using the OpenGL context, so we can be sure that we have not messed up the context in the main thread. (A simple rule: the whole picture must be executed in the game update cycle or synchronized in the GL context.)

Hope this helps.

+6
source

I seem to have missed the obvious bleeding ...

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

... and updates will appear as they should. I think that what happened does not have a viewport and scissor in the context of the child stream that used the share group (was set in the context of the parent stream), only when the child stream exited it updated the view with the corresponding viewport, thus finally displaying my updates, Or something like that! (I'm still new to OpenGLES!)

0
source

All Articles