2016-06-11 17 views
0

Mein Ziel ist es, eine native Cocoa-App für OSX 10.4+ zu erstellen, die mit OpenGL rendert, um ein Spiel zu portieren, das ich gerade erstelle. Mein Problem ist, dass ich nach Apples Tech Notes (siehe Links unten) nicht herausfinden kann, wie man das Aktualisieren und Rendern in Cocoa trennt. Das ist meine Frage: Wie kann eine Cocoa-App mit einer anderen Rate aktualisiert werden, als sie rendert? Mit anderen Worten, das Rendering von Cocoa ist ereignisbasiert, und ich möchte nicht auf ein Ereignis warten, um meine Simulation zu aktualisieren.Rendern und Aktualisieren mit OS X unter OpenGL

Hier ist das Fleisch von dem, was ich habe Arbeit:

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) 
    { 
     CVReturn result = [(__bridge GameView*)displayLinkContext getFrameForTime:outputTime]; 
     return result; 
    } 

    - (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime 
    { 
     //TODO: find somewhere better to put this. 
     //TODO: the magic number is not doing what I expect it to. 
     float dt = (outputTime->videoTime - lastCapturedTime)/36000000.0f; 
     lastCapturedTime = outputTime->videoTime; 
     update(dt); 

     NSOpenGLContext *currentContext = [self openGLContext]; 
     [currentContext makeCurrentContext]; 

     // must lock GL context because display link is threaded 
     CGLLockContext((CGLContextObj)[currentContext CGLContextObj]); 

     glClearColor(0, 0, 0, 0); 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
     render(); 
     glFlush(); 

     [currentContext flushBuffer]; 

     CGLUnlockContext((CGLContextObj)[currentContext CGLContextObj]); 
     return kCVReturnSuccess; 
    } 

Was ich ist nicht oben über den Code wie das Rendering und die Aktualisierung auf demselben Ereignis geschehen, und die Absicht ist der Fall, dass das Betriebssystem erzählt meine Anwendung, dass es einen Rahmen rendern sollte.

Unter Windows würde ich diesen Code unten in einem WinMain() geben würde genau das, was ich suche.

while(true) { 
    process_events(); //PeekMessage, handle WM_ events 
    int elapsedTime = GetElapsedSeconds(); 
    prepare(elapsedTime); 
    if (fps_lock(elapsedTime, 60)) { 
     render(); 
    } 
    swapBuffers(); 
} 

Hier ist, was ich angeschaut habe (ich würde mehr schreiben als nur zwei Links, weil ich ein paar Forschung habe ich getan habe, aber meine niedrige rep hinderte mich daran, das zu tun):

Vielen Dank im Voraus für mich etwas zu zeigen ich habe vielleicht verpasst haben, oder war etwa völlig abwegig.

+0

Viele Informationen hier für eine grundlegende Frage. Vielleicht möchten Sie es vereinfachen, z. B. "Rendern und Aktualisieren mit unterschiedlichen Raten?" und fügen Sie Ihr Codebeispiel hinzu. Viel Glück. :-) –

Antwort

0

Ich gehe davon aus, dass update() und prepare() sind analog und sind nur über die Aktualisierung der Physik, etc., und keine OpenGL. Und Sie möchten das so schnell/so häufig wie möglich tun?

Wenn Ihr Code threadsicher ist, können Sie dies anhand eines Timers tun. Sie können einen Grand Central Dispatch timer source in einer seriellen Warteschlange verwenden. Ebenso können Sie einen NSTimer auf der Laufschleife eines Threads verwenden, der seine Laufschleife ausführt. Jetzt führt der Hauptthread einer App seine Ausführungsschleife aus, und die Hauptversandwarteschlange ist eine serielle Warteschlange. Sie möchten jedoch möglicherweise nicht den Hauptthread/die Hauptwarteschlange verwenden, da dies die UI-Ereignisbehandlung beeinträchtigt. Vielleicht möchten Sie also eine benutzerdefinierte Warteschlange oder einen Thread, den Sie selbst erstellt haben, verwenden, um die Ausführungsschleife auszuführen.

Wenn Sie das Rendering mit der Aktualisierung synchronisieren müssen, können Sie ein Synchronisierungs-Primitiv wie ein Dispatch-Semaphor verwenden oder das Rendering synchron an dieselbe serielle Warteschlange senden, auf die die Timer-Dispatch-Quelle abzielt. Oder verwenden Sie -performSelector:onThread:withObject:waitUntilDone:, um Ihren Thread zu targetieren.

Übrigens, warum tauschen Sie in Ihrem Windows-Pseudocode Puffer auf Pässe aus, die Sie nicht gerendert haben?

+0

Ken, deine Annahme über die update/prepare Methoden ist korrekt (sie sind analog). Und ja, was ich nicht will, ist für das Spiel zu sitzen und nur auf Ereignisse wie Maus oder Tastatureingabe zu aktualisieren. Es veranlasste mich, meinen Code genauer zu betrachten, und ich sah, dass sie einige Vertexdaten in einem dynamischen Zeichenpuffer mit glBindBuffer() und glBufferSubData() änderten. Es werden keine anderen GL-Anrufe getätigt. Die render() - Routine ruft erneut glBindBuffer() auf und ruft dann die Shader-Programme auf. Auch über den Windows-Pseudocode sollte swap_buffers() in dem if-Block nach render() sein. –

+0

Sie können möglicherweise die OpenGL von der 'update()', indem Sie ein paar Regeln befolgen: nur einen gegebenen Kontext aus einem einzelnen Thread zu einer Zeit, stellen Sie sicher, dass der Kontext für den Thread aktuell gemacht wird, wenn Sie nicht sicher sind Ist (dh Dispatch-Warteschlangen können jede Aufgabe in einem anderen Thread ausführen), ist es wahrscheinlich am besten, den Kontext für den Thread zu löschen, bevor er zurückkehrt, wenn Sie nicht der Thread sind. Sie können gemeinsam genutzte Kontexte verwenden, um GL-Objekte in einem Thread zu bearbeiten und in einem anderen zu rendern. Achten Sie darauf, 'glFlush()' oder 'glFlushRenderAPPLE()' zu verwenden, um sicherzustellen, dass Änderungen im anderen Thread "sichtbar" sind. –

+0

Ken, ich möchte Ihnen vielmals für Ihre Antwort danken. Ich arbeite jetzt reibungslos und bin sehr zufrieden mit dem Ansprechverhalten des Timers. Ich habe nur eine letzte Frage: Gibt es in einer Cocoa-Anwendung, die GCD verwendet, eine Best Practice für den Start des Timers? Ich rufe 'dispatch_resume()' von meiner Unterklasse 'NSOpenGLView' in der 'prepareOpenGL()' Methode auf. Scheint zu funktionieren, aber ich frage mich, ob Apple ein bestimmtes Ereignis vorschreibt, um darauf zu reagieren, ist gut für das Starten von Threads? Ich habe das in keiner der Entwickler-Dokumente gefunden. –