OpenGL Textur-Fehler

  • OpenGL Textur-Fehler

    Hi!

    Ich habe ein - wahrscheinlich nicht ganz alltägliches - OpenGL-Problem:
    Mit Hilfe von mach_star patche ich eine spezielle Funktion (CGLFlushDrawable) in OpenGL-Anwendungen (Spielen), um eigene Sachen über den aktuellen Bildinhalt zu zeichnen.
    Wen da nähere Details interessieren, hier die Projektseite, tut aber denke ich nichts zur Sache.

    Bei vielen Spielen funktioniert das auch sehr gut und einwandfrei. Bei anderen - als Beispiel sei hier Angry Birds Rio genannt - bekomme ich nach dem Laden einer Textur Grafikfehler im Spiel. In diesem Beispiel wird der Cursor statt einer Hand zu einem weißen Rechteck. Das ganze passiert direkt beim Laden der Textur, unabhängig davon, ob ich sie tatsächlich zeichne oder nicht.
    Konkret sieht mein Code zum Laden der Textur wie folgt aus:

    Quellcode

    1. GLuint LoadTexture(NSString* file)
    2. {
    3. GLuint mytex=0;
    4. GLsizei width, height;
    5. BOOL hasAlpha;
    6. GLenum format;
    7. unsigned char* bitmapData;
    8. NSImage *texture1 = [[NSImage alloc] initWithContentsOfFile:file];
    9. if (texture1) {
    10. NSBitmapImageRep *imageRep = (NSBitmapImageRep *)[texture1 bestRepresentationForDevice:nil];
    11. glGenTextures( 1, &mytex );
    12. glBindTexture( GL_TEXTURE_2D, mytex );
    13. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    14. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    15. width = [imageRep pixelsWide];
    16. height = [imageRep pixelsHigh];
    17. hasAlpha = [imageRep hasAlpha];
    18. bitmapData = [imageRep bitmapData];
    19. if(hasAlpha) {
    20. format = GL_RGBA;
    21. } else {
    22. format = GL_RGB;
    23. }
    24. // gluBuild2DMipmaps(GL_TEXTURE_2D, format, width, height, format, GL_UNSIGNED_BYTE, bitmapData);
    25. glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, bitmapData);
    26. // glBindTexture( GL_TEXTURE_2D, 0);
    27. }
    28. [texture1 release];
    29. return mytex;
    30. }
    Alles anzeigen


    In dieser Funktion habe ich den Übeltäter auch schon identifiziert. Wider erwarten ist es nicht das Binden der Textur oder Setzen der Texturparameter, sondern das eigentliche Generieren der Textur:

    Quellcode

    1. glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, bitmapData);


    Ich bin jetzt nicht so der OpenGL-Guru, aber das ist ja schon eine sehr grundlegende Funktion, und ich sehe nicht wo ich da was falsch machen könnte... Denke ich bin mir nicht so ganz bewusst, was da intern eigentlich genau passiert...
    Kommentiere ich o.g. Zeile aus, läuft alles wunderbar - nur eben ohne meine Textur. ;) Die Verwundung von gluBuild2DMipmaps statt glTexImage2D ändert nichts, genauso wie das setzen auf die vorher gebundene Textur oder das Wechseln der TextureUnit.
    Was mich dabei stutzig macht: Zum Zeichnen von Text benutze ich die Klasse GLString aus einem Apple Beispielcode. Die verwenden da zum internen Generieren der Textur ebenfalls glTexImage2D, was keinerlei Probleme verursacht.

    So... Ich hoffe mal einer von euch hat einen Tipp, wie ich eine Textur aus einem .jpg (oder was auch immer) generieren kann ohne die aktuelle OpenGL-Darstellung zu verunstalten. Kann ja eigentlich nicht so schwer sein, bestimmt mach ich einfach was grundlegendes falsch...
    Naja, danke Euch schon mal für die Hilfe! :)
  • Ein paar Ideen:

    1. Hol dir vorher die aktuell gebundene Textur, und binde die am Ende wieder. War irgend ein glGet call, hab vergessen, welcher. Vielleicht hat das ja damit was zu tun. Klingt jetzt nicht so, aber ein Versuch wäre es wert.

    2. Gib mal statt "bitmapData" einfach 0 an. Theoretisch sollte die Textur dann schwarz sein - nur um auszuschliessen, dass ein Problem mit der Darstellung Deiner Textur besteht.

    3. Installier/benutz den gDEBugger, der ist inzwischen kostenlos und Du kannst zur Laufzeit alle Texturen und Shader inspizieren. Das hilft sicherlich auch um das Problem zu lokalisieren.

    PS: Und bau mal überall ein glGetError ein ;) -- wobei das gDEBugger auch schon für Dich übernehmen kann.
    C++
  • Danke dir schon mal für die superschnelle Antwort! :thumbup:

    1) Das hatte ich schon probiert, war auch meine erste Vermutung. Ändert allerdings nichts. Ist ja auch klar irgendwie, mein Code kommt ganz am Ende, würde ja keinen Sinn machen wenn sich das Spiel darauf verließe, dass beim nächsten Frame noch eine bestimmte Textur geladen ist...

    2) Die Fehler bleiben dann genauso vorhanden. Allerdings wird meine Textur davon nicht schwarz, sondern es wird scheinbar irgendein Müll angezeigt, der im VRAM liegt. Mit jedem neuen Generieren der Textur sehe ich andere Grafikelemente aus dem Spiel.

    3) Das Tool sieht nach dem aus, was ich schon länger gebraucht hätte! :D Dummerweise verabschiedet es sich beim Start mit der Meldung, dass mein OS nicht unterstützt wird. Danke, Lion...

    Mir kam allerdings noch eine andere Idee: Der Apple-Sample-Code macht eigentlich alles genauso wie ich, mit der kleinen Ausnahme, dass statt GL_TEXTURE_2D die Erweiterung GL_TEXTURE_RECTANGLE_EXT benutzt wird.
    Dadurch umgeht man scheinbar die "Kollisionen" mit den anderen Texturen, denn jetzt funktioniert es einwandfrei! :) Werde das noch in anderen Spielen/Szenarios testen, aber sieht schon mal gut aus.

    Ich hatte nur gerade einen Crash, nachdem ich die Textur zum Test extrem häufig in kurzen Intervallen nachgeladen habe:

    Quellcode

    1. Thread 6 Crashed:: CVDisplayLink
    2. 0 com.apple.GeForceGLDriver 0x8f0114c1 0x8effe000 + 79041
    3. 1 com.apple.GeForceGLDriver 0x8f0dae21 0x8effe000 + 904737
    4. 2 com.apple.GeForceGLDriver 0x8f0b52d1 gldUpdateDispatch + 851
    5. 3 GLEngine 0x04f0e449 gleDoDrawDispatchCore + 454
    6. 4 GLEngine 0x04eb0258 glDrawArrays_IMM_Exec + 231
    7. 5 libGL.dylib 0x9a5de090 glDrawArrays + 44
    8. 6 com.rovio.mac.AngryBirdsRio 0x001025bb gr::GL_Primitive::render() + 1027
    9. 7 com.rovio.mac.AngryBirdsRio 0x00102f00 gr::GL_RenderBatcher::flush() + 708
    10. 8 com.rovio.mac.AngryBirdsRio 0x000f7a2a gr::GL_Context::present() + 22
    11. 9 com.rovio.mac.AngryBirdsRio 0x00128904 GameApp::update(float, gr::Context*, int) + 2024
    12. 10 com.rovio.mac.AngryBirdsRio 0x0015211c -[AppDelegate update] + 262
    13. 11 com.rovio.mac.AngryBirdsRio 0x00153ff4 -[OpenGLView updateGame] + 198
    14. 12 com.rovio.mac.AngryBirdsRio 0x001534a1 -[OpenGLView getFrameForTime:] + 79
    15. 13 com.rovio.mac.AngryBirdsRio 0x0015357f _ZL21MyDisplayLinkCallbackP15__CVDisplayLinkPK11CVTimeStampS3_yPyPv + 40
    16. 14 com.apple.CoreVideo 0x9015b185 CVDisplayLink::performIO(CVTimeStamp*) + 489
    17. 15 com.apple.CoreVideo 0x9015a088 CVDisplayLink::runIOThread() + 876
    18. 16 com.apple.CoreVideo 0x90159d05 _ZL13startIOThreadPv + 160
    19. 17 libsystem_c.dylib 0x96d14ed9 _pthread_start + 335
    20. 18 libsystem_c.dylib 0x96d186de thread_start + 34
    Alles anzeigen


    Sieht mir jetzt aber nicht so aus als könnte ich da direkt was dran ändern, oder? War ja nicht mal mein Code "direkt" involviert...
  • Zu (2): Ja, sorry. Der Datapointer darf 0 sein, aber der Inhalt ist unspezifiziert (in Deinem Fall also alte Dinge im VRAM). Bin grad noch am überlegen, ob es uns was sagt, dass das Problem dann immernoch weiter bestehen bleibt.

    Das mit TEXTURE_RECTANGLE ist ein guter Hinweis. Aber dann musst Du Deine ganzen Shader umschreiben (naja nicht so wild..), aber Du hast immer noch keine Garantie, dass es nicht beim nächsten Spiel, was _RECTANGLE verwendet, wieder schiefgeht...Und mich würde wirklich interessieren, was man da machen kann :)

    Du kannst mal etwas "verrücktes" probieren: Lass mal das glGenTextures weg, und setz "mytex" stattdessen auf irgendetwas hohes - einfach um zu vermeiden, dass glGenTextures Dir keinen freien Namen generiert. Soetwas hatte ich schonmal im Multithreading, wenn ich mich recht erinnere.
    (N.B. Wenn ich mich richtig erinnere, ist glGenTextures "unnötig", es liefert Dir nur einen freien Namen. Stattdessen könntest Du auch alle Namen (=Nummern) selber verwalten. Müsste ich nochmal genauer nachschauen, ob das so stimmt. Aber funktionieren sollte es eigentlich edit: Ja, der Standard gibt mir recht. Wenn noch niemand vorher z.B. 12345 als Namen verwendet hat, kannst Du auch einfach mit glBindTexture(...,12345) unter diesem Namen etwas anlegen - GenTextures nimmt Dir nur die Verwaltung ab).
    C++
  • Danke für die neuen Tipps!

    Quellcode

    1. bitmapimagerep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];

    Funktioniert genauso gut oder schlecht wie die bisherige Methode. Textur wird angezeigt, aber im Spiel wird irgendwas kaputt gemacht.

    Statt "mytex" irgendeine Phantasiezahl einzutragen geht ebenfalls. Ändert aber leider auch nichts am Problem...

    Werde wohl wenn ich Zeit und Laune dazu habe wieder 10.6 installieren, um den gDEBugger nutzen zu können. Dann schaue ich mal, ob sich noch irgendwas rausfinden lässt.
    Muss mich noch mal näher mit dem Apple-Code auseinandersetzen. Ist halt echt merkwürdig, dass der Code von Apple wirklich absolut NIE irgendwelche Probleme oder Abstürze verursacht und immer perfekt dargestellt wird. Naja, mal sehen...
  • Ansonsten schau nochmal deinen rendering-Code an, vielleicht liegt der Fehler ja doch dort - und dass Du den Fehler durch Aenderungen an der Textur-Erstellung beheben kannst, ist nur ein Nebeneffekt.

    Ausserdem kannst Du Dir mal die Thread-ID ausgeben lassen, bei Texture-Anlegen und bei der Verwendung. Waere mal interessant, ob das alles Multithreaded laeuft, weil dann ist ohnehin alles kompliziert ;)
    C++
  • Gute Idee! Könnte durchaus damit zu tun haben.
    Habe mir das hier als integer geprintet:

    Quellcode

    1. mach_port_t tid = pthread_mach_thread_np(pthread_self());


    Bei einem Spiel, das keine Problem macht, kommt sowohl beim Laden der Textur als auch beim Zeichnen der selbe Wert raus.
    Bei Angry Birds dagegen sind die IDs unterschiedlich! Werde das mal näher untersuchen, ob das bei dem Apple-Code auch so ist.
    Habe gerade die Vermutung, dass ich an den mehreren Threads selbst schuld sein könnte, da ich das Laden der Textur manchmal beim Empfangen einer DistributedNofiticaion anstoße und manchmal im Rendering-Code. Das ist sicherlich nicht sinnvoll.

    Der eigentliche Rendering-Code ist wenig spektakulär: Textur binden, Quadrat zeichnen, Textur-Koordinaten setzen (warum zur Hölle wird bei Rechteckigen Texturen nicht mit auf 1 normalisierten Werten sondern mit absoluten Pixeln gerechnet?! Wär gestern fast wahnsinnig geworden! :D ), fertig. Die Methode zum Zeichnen der GLStrings wird direkt davor und danach aufgerufen, das Setup ist also das gleiche, und der Code eigentlich auch...
    Wollte das ganze eben so einfach wie möglich halten, für größtmögliche Kompatibilität. Dass es nur ein Kompromiss werden kann ist mir schon klar, selbst das Overlay von Steam läuft nicht in allen Spielen einwandfrei.

    Naja - geht weiter wenns Regnet, jetzt erstmal den Grill anheizen! :thumbup: