2009-12-31 6 views
4

J'ai une application OpenGL ES pour l'iPhone que je développe, étant un port d'une application orientée 2d à partir d'une autre plateforme. J'ai choisi de rendre les graphiques en utilisant OpenGL ES pour des raisons de performances. Cependant, l'application principale fonctionne sur un thread séparé (en raison de la conception de l'application d'origine), donc à partir de mon délégué application que je fais:Comment mettre à jour la mise à jour du contexte iPhone OpenGL ES tout de suite?

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

Cependant, je remarque que tous les appels dans le applicationMainThread qui tentent de rendre quelque chose à l'écran ne rend rien, jusqu'à ce que ce thread se termine.

Je configure le contexte OpenGL ES réel sur le thread d'application enfant, pas le thread UI. Si je fais ceci:

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

Puis la texture est mise à jour à l'écran presque immédiatement, comme je m'attendais.

Cependant, si après la [GLView drawView] appeler ajouter cette seule ligne:

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

L'écran est mis à jour après que le délai de 5 secondes se termine. Cela m'amène à croire que l'écran ne se met à jour que lorsque le thread se termine (il faut faire plus de tests pour confirmer). Cela signifie que lorsque je substitue le code d'application réel, qui fait plusieurs mises à jour d'écran, aucune des mises à jour n'arrive (laissant un écran blanc) jusqu'à ce que le thread d'application se termine, pas exactement ce que je voulais! Donc, y a-t-il un moyen de contourner ce problème ou est-ce que j'ai fait quelque chose qui ne va pas?

+1

Pouvez-vous nous montrer ce qu'il y a dans votre 'glView :: drawView'? – zoul

+0

- (void) drawView { [EAGLContext setCurrentContext: contexte]; glBindRenderbufferOES (GL_RENDERBUFFER_OES, viewRenderbuffer); [contextRenderbuffer de contexte: GL_RENDERBUFFER_OES]; } – Gabriel

+0

Salut compatriote Sunshine Coast dev! – alex

Répondre

6

Vous devez faire quelque chose qui ne va pas, car le rendu OpenGL multithread fonctionne très bien sur iPhone. Je ne peux pas vous dire quel est le problème avec votre code, mais je peux vous montrer comment nous le faisons. Il m'a fallu plusieurs itérations pour y arriver, car l'exemple de code OpenGL d'Apple écrase tout ensemble. En fin de compte, j'ai trouvé trois classes: Stage, Framebuffer et GLView. La scène contient la logique de rendu du jeu et sait comment se rendre à un framebuffer. La classe framebuffer est un wrapper autour du framebuffer OpenGL et rend à un rendererbuffer ou un EAGLDrawable. GLView est le drawable pour rendre le framebuffer à, il contient tous les trucs d'installation OpenGL. Dans le point d'entrée de l'application, je crée un contexte OpenGL, un GLView, un framebuffer qui rend à ce GLView et une scène qui rend l'utilisation du framebuffer. La méthode étape update fonctionne dans un thread séparé et ressemble un peu à ceci:

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

il se lie En clair, le framebuffer, parcourt le graphe d'objet de jeu (= rend la scène), présente le contenu de mémoire vidéo sur la écran et délie le framebuffer. L'appel presentRenderbuffer est un peu égaré, il appartient à quelque chose de plus haut dans la conception - la scène devrait juste rendre dans le framebuffer et vous laisser faire ce que vous voulez faire avec le framebuffer. Mais je ne pouvais pas trouver le bon endroit, alors j'ai juste laissé l'appel là. Sinon, je suis assez content de la conception: toutes les classes sont simples, cohérentes et testables. Il y a aussi une classe de programmation qui crée le fil et appelle aussi vite que possible MainLoop Stage:

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

Le fil de mise à jour du jeu est synchronisé à l'aide du contexte OpenGL afin que nous puissions être sûrs que nous faisons le contexte non corrompu le fil principal.(Règle simple: Tout le dessin doit être fait dans la boucle de mise à jour du jeu ou synchronisé par le contexte GL.)

Espérons que ça aide.

+0

avez-vous eu besoin d'utiliser un groupe de partage pour partager le contexte entre les threads? – Gabriel

+0

Aide certainement, et indique que mon problème est dans quelque chose d'autre. – Gabriel

+0

Aucun groupe partagé n'était nécessaire, je ne sais même pas ce que c'est. – zoul

0

semble que je manqué l'évidence des saignements ...

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

... et les mises à jour apparaissent comme ils le devraient. Je pense que ce qui est arrivé est sans la fenêtre et le jeu de ciseaux sur le contexte fils qui utilisait un groupe de partage (était défini sur le contexte parent), seulement quand le thread fils quittait la vue avec la fenêtre appropriée, affichant ainsi mes mises à jour . Ou quelque chose comme ça! (Je suis toujours un débutant OpenGLES!)

Questions connexes