Problem mit CVDisplaylink

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Problem mit CVDisplaylink

    Hallo alle.

    Ich habe ein Problem und benötige mal Inspirationen für eine Fehlersuche. Ich poste keinen Code, da es erstmal um theoretische Überlegungen geht.
    Ich schreibe ein Programm, in dem mit OpenGL Kugeln in einem View dargestellt werden.

    Diese Kugeln bewegen sich, wenn ich auf einen Button des Views drücke. Es passiert zweierlei: Meine Klasse XY, die eine Liste der Kugel-Objekte enthält, berechnet die neuen Positionen in einem Zeitintervall. Diese Berechnung erfolgt mittels eines Timers, von dem aus die Berechnungsroutine von XY für alle Kugeln aufgerufen wird. Wenn ich auf den Stop-Button des Views drücke, wird der Timer gelöscht.

    Gleichzeitig passiert folgendes: Da die Animation mit Knopfdruck startet, hänge ich den CVDisplaylink ein. Wenn ich auf Stop drücke, wird er wieder "ausgehängt".

    Soweit so gut. Ihr werdet es nicht glauben, alles funktioniert tadellos.

    Ich habe auch eine kleine Routine für Color Picking geschrieben. Ein MouseDown über meinem View wird weitergeleitet an den Controller des Views. Die Kugeln werden im Backbuffer gezeichnet usw. Die ausgewählte Kugel erhält einen WireCube drumrum.

    Ich kann also zig mal Play und Stop drücken und es läuft wie erwünscht. Sobald ich aber mit der Maus eine Kugel anwähle und wie gewünscht der WireCube um sie gezeichnet wird, funktioniert der Displaylink nicht mehr. Ich kann also Kugeln auswählen, die eine, und danach die andere, es wird auch der Wechsel gezeichnet, aber wenn ich dann auf Play drücke, bewegt sich nichts mehr. Trotzdem weiß ich, dass die Berechnung der Kugeln durch den Timer weiterläuft. Ich kann durch einen Button auch ein einmaliges Neuzeichnen des Views erzeugen (hilfsweise) und sehe dann die veränderten Positionen. Wenn ich dann auf Stop drücke und der Displaylink wird "ausgehängt" (kenne nicht den richtigen Begriff!), hängt sich das Programm auf und das wars. Offensichtlich scheint also etwas mit dem Displaylink nicht mehr zu funktionieren, sobald ich mit der Maus auf das View geklickt habe und meine Color Picking Routine gestartet hat.

    Ich weiß, dass solche theoretischen Dinge immer schwierig sind, aber ich habe keinen Plan, wo ich nach dem Fehler suchen soll.

    Vielleicht hat ja jemand eine Idee. Wenn noch Infos benötigt werden, sagt Bescheid. Und schon mal Danke.
  • Das ist ziemlich umständlich und fehleranfällig wie du das machst. Du musst doch nicht immer nen Timer oder Displaylink erstellen und dann wieder löschen, erstell die beiden beim Starten und lass sie laufen. Wenn sich nix bewegen soll, ändere die Geschwindigkeit der Bewegung auf 0.

    Pseudo

    Quellcode

    1. if(shouldMove)
    2. velocity.x=10.0f;
    3. else
    4. velocity.x=0.0f;



    in deinem Timer wo du renderst machst du einfach:

    Quellcode

    1. position.x+=velocity.x*deltaTime; // Position des Objektes (Kugel)
    2. glTranslatef(position.x, 0.0, 0.0);
  • Was Du vorschlägst, ist für mich nicht praktikabel. Ich habe letztendlich eine dokumentenbasierte App, in der ich verschiedene Planetensysteme verwalte. Diese Planetensysteme haben in einem Array verschiedene Kugeln (Planeten) abgelegt. Jedes Planetensystem berechnet für sich mit seinen Einstellungen - wenn es denn auf Play gestellt wird - die neuen Positionen. Die Berechnung hat erstmal gar nichts mit der Darstellung zu tun, so dass ich auch nicht aus dem Timer heraus rendere. Jedes Planetensystem hat auch verschiedene Beobachter in einem Array. Mit beliebig vielen Views und deren Controllern regele ich die Darstellung. So kann ich ein System aus verschiedenen Blickwinkeln (oder auch mit den gleichen Beobachtern) anschauen. Es kann sein, dass ein System auf Play steht, ein anderes jedoch nicht. Ich möchte Darstellung und Berechnung auch voneinander trennen, daher habe ich die ganze Geschichte mit dem Displaylink geregelt, wie es Apple ja auch vorsieht, so dachte ich zumindest.

    Mein geschildertes Problem hingegen besteht schon bei EINEM System mit EINEM View. Wie gesagt, bevor ich ein MouseDown verursache, läuft alles wie geschmiert (übrigens auch mit vielen Systemen und Views).

    Ich erstelle einmal pro View einen Displaylink:

    QUELLCODE:
    - (void) setupDisplayLink
    {

    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);

    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);

    CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
    CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
    }

    Play und Stop der Berechnung löse ich über einen Timer der Klasse des Planetensystems (dort ist auch die Berechnungsroutine), Play und Stop der Darstellung löse ich mit folgendem Code in meinem(n) View(s):

    QUELLCODE:
    - (void) startAnimation
    {
    CVDisplayLinkStart(displayLink);

    }

    - (void) stopAnimation
    {
    CVDisplayLinkStop(displayLink);

    }

    Das ist insofern auch praktisch als ich Berechnungen anderer Systeme grundsätzlich weiterlaufen lassen kann, aber die Darstellung verhindere, z.B. wenn eines meiner Views in Fullscreen geht.

    Ideen für die Suche nach dem bestehenden Fehler? Übrigens hängt sich das Programm (nach MouseDown) auf in der Routine (void)stopAnimation, nicht in (void)startAnimation, obwohl schon hier die Darstellung nicht mehr funktioniert. Irgendwie wird nach dem MouseDown der Displaylink fehlerhaft.
  • Du musst beachten, dass der Displaylink in einem eigenen Thread läuft und es dort keinen ARP gibt. Ausserdem empfiehlt es sich, den GLContext zu locken. Benutzt Du ein NSOpenGLView? Um was für eine Timer Klasse handelt es sich in Deinem Planetensystem? NSTimer? Eigene Timerklasse?

    Quellcode

    1. // This is the renderer output callback function
    2. static CVReturn MyDisplayLinkCallback( CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext )
    3. {
    4. CVReturn result = [ ((MGLOpenGLView*)displayLinkContext) getFrameForTime:outputTime ];
    5. return result;
    6. }
    7. - (CVReturn)getFrameForTime:( const CVTimeStamp* )outputTime
    8. {
    9. NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];
    10. // Add your drawing codes here
    11. [ self renderScene ];
    12. [ pool drain ];
    13. return kCVReturnSuccess;
    14. }
    15. - (void)renderScene
    16. {
    17. [[self openGLContext] makeCurrentContext];
    18. // This method will be called on both the main thread (through -drawRect:) and a secondary thread (through the display link rendering loop)
    19. // Also, when resizing the view, -reshape is called on the main thread, but we may be drawing on a secondary thread
    20. // Add a mutex around to avoid the threads accessing the context simultaneously
    21. CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
    22. ...rendern
    23. CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
    24. CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
    25. }
    Alles anzeigen

    Keine Ahnung ob es daran liegt. Aber ich denke, um wirklich Hilfe zu bekommen musst Du ein Beispielprojekt posten...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Markus Müller ()

  • Fehler gefunden. Erklärung willkommen. Wohl grundsätzlicher Denkfehler meinerseits.
    Meine Color-Picking-Routine sieht so ähnlich aus:

    QUELLCODE:
    - (MBSpaceObject *) getSpaceObjectAtX:(float) x andY:(float) y {

    CGLLockContext([[self openGLContext] CGLContextObj]);

    [[self openGLContext] makeCurrentContext];

    glDisable( GL_LIGHTING );
    glDisable( GL_TEXTURE );
    glDisable( GL_DITHER );
    glDisable( GL_COLOR_MATERIAL );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    gluLookAt([[[observer spacePoint] position] x],
    [[[observer spacePoint] position] y],
    [[[observer spacePoint] position] z],
    [[observer lookAt] x],
    [[observer lookAt] y],
    [[observer lookAt] z],
    [[[observer spacePoint] up] x],
    [[[observer spacePoint] up] y],
    [[[observer spacePoint] up] z]);

    int i;
    GLubyte color;
    glColor3ub(0, 200, 1);
    MBSpaceObject *object;
    MBVector *p;
    for ( i = 0; i < [[[observer owner] spaceObjects] count]; i++ ) {
    glPushMatrix();
    object = [[[observer owner] spaceObjects] objectAtIndex:i];
    p = [[object spacePoint] position];
    sphere = gluNewQuadric();
    color = i;
    glColor3ub(color, 200, 1);
    glTranslated([p x], [p y], [p z]);
    if (wireModel) gluQuadricDrawStyle(sphere, GLU_LINE );
    else gluQuadricDrawStyle(sphere, GLU_FILL );
    gluSphere( sphere , [object radius] , [object slices] , [object stacks] );

    gluDeleteQuadric(sphere);
    glPopMatrix();
    }

    GLint viewport[4];
    GLubyte pixel[3];

    glGetIntegerv( GL_VIEWPORT, viewport);
    glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, (void *)pixel);

    if ( pixel[1] == 200 ) return [[[observer owner] spaceObjects] objectAtIndex:pixel[0]];
    else return nil;

    glEnable( GL_LIGHTING );
    glEnable( GL_TEXTURE );
    glEnable( GL_DITHER );
    glEnable( GL_COLOR_MATERIAL );

    CGLUnlockContext([[self openGLContext] CGLContextObj]);

    [self drawView];

    }

    Der Fehler ist, dass mein

    if ( pixel[1] == 200 ) return [[[observer owner] spaceObjects] objectAtIndex:pixel[0]];
    else return nil;

    VOR

    CGLUnlockContext([[self openGLContext] CGLContextObj]);

    ausgeführt wird. Der Fehler ist behoben, wenn ich meine Funktionsvariablerückgabe ans Ende der Funktion stelle. Ich dachte, mein return könne irgendwo stehen, der führt erstmal den ganzen Code aus und gibt das erst am Ende zurück.

    Naja, irgendwie falsch gedacht.
  • lucia schrieb:

    Ich dachte, mein return könne irgendwo stehen, der führt erstmal den ganzen Code aus und gibt das erst am Ende zurück.


    Neenee, ein return wird dort ausgeführt, wo er steht. Wer hat Dich darauf gebracht, dass das anders sein könnte? Es gibt einige Leute, die mehrere Returnpfade in Funktionen und Methoden grundsätzlich als böse ansehen, weil eben gerade sowas passieren kann.

    Wenn ich Deinen Code richtig gesehen habe, kannst Du Dir das Erzeugen und Löschen der Kugel in jedem Schleifendurchlauf sparen und stattdessen einen Quadric für die ganze Schleife wiederverwenden, oder?
    Multigrad - 360°-Produktfotografie für den Mac
  • Ja, da kann ich wirklich einen Quadric nehmen. Danke für die Antworten.

    Komisch ist noch, dass ich nach dem Color Picking beim nächsten Zeichnen einen Farbfehler bei der Kugel habe, die als erste gerendert wird. Wenn ich meine "normale" Szene 2x danach rendere, ist es ok, allerdings flackert dann natürlich die als erste gezeichnete Kugel (weil ja einmal zwischendurch eine falsche Farbe gerendert wird).

    Muss ich nach dem Picking noch manuell irgendeinen Buffer löschen, oder was ist da los?
  • Warum zeichnest Du die Kugel für das Picking nicht in ein FBO? Dann ist es ganz getrennt. Eventuell noch besser ist ein FBO in mehreren Color-Attachments, da kannst Du in ein Target Deine fertige Szene zeichnen und ins andere die farbcodierten Objekte.

    Wenn Du nur Kugeln im Weltraum zeichnen willst, ist vielleicht sowieso Picking über Raycasting noch einfacher, die Tiefenwerte hast Du ja ohnehin immer, damit kannst Du dann für jeden Fenster-Punkt die 3D-Koordinaten bestimmen und suchst dann das Objekt dazu.

    Naja, hat alles seine Vor- und Nachteile.. :)
    C++
  • lucia schrieb:

    Naja, ich habe nicht nur Kugeln. Und da finde ich die Farbmethode eigentlich ganz elegant. Wenn ich diesen Fehler nicht wegkriege, werde ich aber wohl auf den SelectionMode von OpenGL zurückgreifen.

    Davon wird offiziell abgeraten :P Sollte auch schon seit einer Weile deprecated sein...

    Von daher würde ich auch bei der Farbmethode bleiben ;) Dein Fehler liegt evtl. daran, dass eins der glEnable() das erste mal nach dem Picking nicht ausgeführt wird, weil Du vorher ein Return hast? Ich kann nur raten, meine Glaskugel ist auch kaputt...
    C++
  • Wahrscheinlich verstellt Dein Picking irgendeinen State - testweise könntest Du versuchen, den Zustand per glPushAttrib() / glPopAttrib() beim Picking auszulagern. Allerdings sind die seit OpenGL3.01 deprecated, sodass ich das nicht endgültig so lösen würde.

    Selection oder Feedback Mode würde ich nicht mehr vewenden - ist deprecated und gilt laut opengl.org als Fehler. Die Idee mit Color Coding und glReadPixels() ist schon ok, wenn Du nicht selbst tracen willst. Allerdings, wie zerm sagt, in ein FBO. Wenn Dich nur der Bereich um den Mauszeiger interessiert, kannst Du den gezeichneten Bereich noch z.B. per gluPickMatrix() verkleinern, dann kann mehr gecullt werden.

    Edit: zerm war schneller...
    Multigrad - 360°-Produktfotografie für den Mac
  • Schade, habe gerade den SelectionMode implementiert und klappt super.

    Die meisten Tutorials beschreiben ja beim Color Picking einfach zu rendern und mit glReadPixels(x,y,1,1,...) die Farbe auszulesen. Ich finde es auch viel sinnvoller, nur das zu rendern, was man wirklich braucht, also zuerst gluPickMatrix, nur den Pixel der Mauspos. zeichnen und dann glReadPixels(0,0,1,1,...). Geht wunderbar.

    Zum Fehler: Vorher Disable ich Lighting, Texture, Dither und ColorMaterial, danach Enable ich es wieder. Was kann da schiefgehen?
  • lucia schrieb:

    Zum Fehler: Vorher Disable ich Lighting, Texture, Dither und ColorMaterial, danach Enable ich es wieder. Was kann da schiefgehen?

    Checkst Du glError? Ich glaube, dass GL_TEXTURE nicht für glDisable/glEnable funktioniert, nur GL_TEXTURE_nD. Könnte aber auch was anderes sein - irgendwas am Zustand ist offensichtlich geändert, sonst würde es ja richtig gerendert werden. Irgendwas, was Du in Deiner Render-Funktion setzt, denn er erholt sich ja offensichtlich.
    Multigrad - 360°-Produktfotografie für den Mac
  • mattik schrieb:

    lucia schrieb:

    Zum Fehler: Vorher Disable ich Lighting, Texture, Dither und ColorMaterial, danach Enable ich es wieder. Was kann da schiefgehen?

    Checkst Du glError? Ich glaube, dass GL_TEXTURE nicht für glDisable/glEnable funktioniert, nur GL_TEXTURE_nD. Könnte aber auch was anderes sein - irgendwas am Zustand ist offensichtlich geändert, sonst würde es ja richtig gerendert werden. Irgendwas, was Du in Deiner Render-Funktion setzt, denn er erholt sich ja offensichtlich.

    Quellcode

    1. glDisable(GL_TEXTURE_2D);


    :)
  • wolf_10de schrieb:

    Seh ich inzwischen auch nicht mehr so eng, ich nehm die deprecated und fertig, was in 10 Jahren ist, wird man dann sehen. Apple sind da nicht die schnellsten

    Naja, regt mich halt auf, weil ich das nicht nachvollziehen kann. Ich musste mal stundenlang Shader umschreiben, weil ich den ursprünglichen Code unter Windows geschrieben hatte. Ich glaub sogar, noch unter XP. Naja...Bin mal gespannt, ob das mit OpenCL auch so weiter geht. Vielleicht steig ich dann doch wieder um, zurück zu CUDA...
    C++
  • Finde es auch sehr nervig, dass Apple da so hinterher hinkt. Zwar gibt es seit 10.6.3 OpenGL 3 (per extensions), aber die GLSL kann trotzdem nur 1.2. Das bedeutet zwei Versionen der Shader zu pflegen, wenn man plattformneutral bleiben möchte - was ja irgendwo auch der Witz an OpenGL ist.

    Allerdings ist es jetzt schon möglich, komplett auf das legacy/deprecated-Zeug zu verzichten. Apple rät selber dazu in den OpenGL-Session Videos.

    An den TE: Du solltest Dir ein neueres Tutorial suchen (schau mal bei OpenGL.org vorbei) oder leg Dir die OpenGL SuperBible 5th Edition zu, damit lernst Du alles über modernes OpenGL.