[Gelöst] Speicherleiche bei Texturenerzeugung mit SDL und OpenGL
-
Guten Tag,
bei der Nutzung von Texturen mit OpenGL und SDL stoße ich fast immer die Möglichkeit folgender Funktion:
inline GLuint loadTexture( const std::string &fileName ) { SDL_Surface *image = IMG_Load( fileName.c_str() ); SDL_DisplayFormatAlpha(image); unsigned object(0); glGenTextures(1, &object); glBindTexture(GL_TEXTURE_2D, object); //Texture behaving glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); //Free Surface SDL_FreeSurface(image); image = NULL; return object; }
Das Problem ist nun, dass der genutzte Arbeitsspeicher sich bei jedem Funktionsaufruf erhöht.
Beispiel:
GLuint texture = loadTexture("img.png"); //13,8 MiB RAM genutzt texture = loadTexture("img.png"); //15,9 MiB RAM genutzt texture = loadTexture("img.png"); //17,8 MiB RAM genutzt //...
Auch wenn ich die Variable
texture
vor jedem Aufruf vonloadTexture()
zerstöre, steigt der genutzte Bereich des Arbeitsspeichers auf diese Weise, jedoch finde ich die Stelle in der Funktion nicht, an der sich die Speicherleiche befindet. Hat jemand eine Idee?Mit freundlichen Grüßen,
Ki
-
Blödsinn, habe den letzten Passus nicht gelesen.
Was meinst du mit 'zerstören'? Gib mal ein konkretes Beispiel.
-
Der aus dem Westen ... schrieb:
Was meinst du mit 'zerstören'? Gib mal ein konkretes Beispiel.
GLuint *image = new GLuint; *image = loadTexture("img.png"); delete image;
EDIT:
SDL_Surface
durchGLuint
ersetzt - Tippfehler.
-
Versuchs mal so:
inline GLuint loadTexture( const std::string &fileName ) { SDL_Surface *image = IMG_Load( fileName.c_str() ); SDL_Surface *imageFormated = SDL_DisplayFormatAlpha(image); unsigned object(0); glGenTextures(1, &object); glBindTexture(GL_TEXTURE_2D, object); //Texture behaving glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageFormated->w, imageFormated->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageFormated->pixels); //Free Surface SDL_FreeSurface(imageFormated); SDL_FreeSurface(image); image = NULL; return object; }
SDL_DisplayFormatAlpha erstellt eine Kopie, die dem momentanen Display Format entspricht.
Das image = NULL kann man sich sparen, da die Variable eh nur in der Funktion existiert.
Übrigens würde ich noch das object als GLuint definieren.
-
Danke, das hat das Problem verkleinert, ist jedoch leider noch nicht beseitigt.
Der genutzte Bereich im Arbeitsspeicher steigt immer noch bei jedem Funktionsaufruf im etwa einen MiB an. Das geladene Bild ist übrigens 506,9 KB groß, falls das hilft.
Mit freundlichen Grüßen,
Ki
-
Überprüfe noch, ob eine der Funktionen, welche du verwendest, einen Zeiger als Rückgabewert hat (mit OpenGL hatte ich jetzt noch nicht so viel zu schaffen). Den solltest du eventuell auch noch abfangen und beim Rücksprung löschen.
-
Ki schrieb:
Der genutzte Bereich im Arbeitsspeicher steigt immer noch bei jedem Funktionsaufruf im etwa einen MiB an.
Schonmal überlegt, dass es daran liegen könnte, dass in jedem Aufruf eine neue Textur erzeugt wird? Kommentier eben mal das glTexImage2D() aus und schau, ob das Leak immer noch da ist
-
Nach ein paar Tests via Auskommentieren scheint es so, als würde
glTexImage2D()
der Problemverursacher sein. Ist diese Auskommentiert, tritt das Problem nicht mehr auf. Jedoch kann ich in Referenzen nichts dazu finden. Einen Rückgabewert hatglTexImage2D()
nicht, da es eine Funktion vom Typvoid
ist.Hat jemand einen Einfall?
Mit freundlichen Grüßen,
KiEDIT:
dot, du hast scheinbar RechtUnd wie kann man das Problem beheben?
-
Ich versteh nicht ganz wo dein Problem liegt? Es passiert doch genau das, was du programmiert hast!? Bei jedem Aufruf dieser Funktion wird eine neue Textur erzeugt. Natürlich braucht diese Textur auch Speicher!? Die Frage die du dir stellen solltest ist: Warum ruf ich diese Funktion so oft auf und was passiert mit den erzeugten Texturen, wenn sie nichtmehr benötigt werden?
-
dot schrieb:
Ich versteh nicht ganz wo dein Problem liegt? Es passiert doch genau das, was du programmiert hast!? Bei jedem Aufruf dieser Funktion wird eine neue Textur erzeugt. Natürlich braucht diese Textur auch Speicher!? Die Frage die du dir stellen solltest ist: Warum ruf ich diese Funktion so oft auf und was passiert mit den erzeugten Texturen, wenn sie nichtmehr benötigt werden?
Ich will im Prinzip nur eine bereits erzeugte Textur aus dem Speicher bekommen, um keine Speicherleichen zu haben.
Innerhalb der Laufzeit sollen schließlich mehrere Texturen erzeugt werden, die später nicht mehr notwendig sind.
-
Wenn du eine bereits erzeugte Textur willst, dann verwend doch einfach die bereits erzeugte Textur, anstatt eine neue zu erzeugen!?
-
ich kann doch nicht alle Texturen, die irgendwann mal gebraucht werden könnten, in den Speicher laden. Dann hätte ich bei vielen Texturen einen Instant Overflow, anstatt Leichen zu stapeln.
-
Ki schrieb:
ich kann doch nicht alle Texturen, die irgendwann mal gebraucht werden könnten in den Speicher laden.
Wer sagt dass du das sollst?
Mir ist leider immer noch völlig unklar, was genau jetzt dein Problem ist. Wenn du eine Textur nichtmehr benötigst, dann gib sie eben frei. Und wenn du eine Textur benötigst, die du noch nicht geladen hast, dann lade sie. Und wenn sie aber doch schon geladen wurde, dann verwend die geladene, anstatt eine neue zu erzeugen. Wo genau scheiterts jetzt?
-
Ich scheitere am Freigeben des Speichers innerhalb der Funktion
loadTexture()
.Ich will
loadTexture()
nutzen, um in der Laufzeit einer Variable, z.B.texture
, eine Textur zuzuweisen. Die Textur soll nur im Gültigkeitsbereich dieser Variable existieren.
-
Persönlich würde ich versuchen, das zu vermeiden. Lieber am Anfang versuchen, alles direkt zu initialisieren, einzuladen und zu sortieren, als die Textur dann zu laden, wenn Sie gebraucht wird. Früher, als der Speicher noch wirklich rar war und man selbst den Soundchipspeicher verwendet hat, um alles, was man im Spiel wollte, unterzukriegen, konnte man das machen, aber heutzutage ist Speicher billig und das Leben kurz, da sollte man die CPU/GPU nicht in der Laufzeit mit dem Laden einer eventuell aufwendigen Textur belasten - stört nur den Spielfluss.
Meine unnötige Meinung zu dem Thema.
Dann kann innerhalb des Programms mit der Tabelle gearbeitet werden, und es muss nicht ständig auf die Festplatte zugegriffen werden.
-
Der aus dem Westen ... schrieb:
Persönlich würde ich versuchen, das zu vermeiden. Lieber am Anfang versuchen, alles direkt zu initialisieren, einzuladen und zu sortieren, als die Textur dann zu laden, wenn Sie gebraucht wird. Früher, als der Speicher noch wirklich rar war und man selbst den Soundchipspeicher verwendet hat, um alles, was man im Spiel wollte, unterzukriegen, konnte man das machen, aber heutzutage ist Speicher billig und das Leben kurz, da sollte man die CPU/GPU nicht in der Laufzeit mit dem Laden einer eventuell aufwendigen Textur belasten - stört nur den Spielfluss.
Mein Programm ist modular in Szenen aufgebaut. Jede Szene ist ein Objekt einer speziellen Tochterklasse einer gemeinsamen Elternklasse. Zu Beginn jeder Szene, also beim Anlegen des Objektes, werden daher die Texturen eingeladen.
-
Was hält dich davon ab, Verweise auf die geladenen Texturen zu erstellen? Eingeladen werden müssen die so oder so, aber wenn du die Texturen in einer internen Tabelle mit Schlüsseln verpackst, sparst du einiges an Laufzeit. Wenn die Szene dann aufgerufen wird, weißt du bloß die entsprechenden und bereits geladenen Texturen zu, und fertig. Würde ich zumindest so machen . so ersparst du dir ständige Zugriffe auf die Festplatte.
-
@TE
Du solltest dich mal mit den Grundlagen beschäftigen. loadImage() gibt ein GLUint zurück - keinen Pointer. Darauf delete? Schlechte Idee. glTexImage2D kopiert das Bild übrigens, da wird Speicher reserviert! Lies die Doku! Wie wäre es, glDeleteTexture aufzurufen?
Ich will loadTexture() nutzen, um in der Laufzeit einer Variable, z.B. texture, eine Textur zuzuweisen. Die Textur soll nur im Gültigkeitsbereich dieser Variable existieren.
Was gibt es besseres als eine Klasse? Im Konstruktor erstellst du die Texture, im Destruktor gibst du sie wieder frei. Einfacher geht es wirklich nicht. Dazu noch nen Move-Konstruktor und alles ist in Butter.
-
cooky451 schrieb:
@TE
Du solltest dich mal mit den Grundlagen beschäftigen. loadImage() gibt ein GLUint zurück - keinen Pointer. Darauf delete? Schlechte Idee. glTexImage2D kopiert das Bild übrigens, da wird Speicher reserviert! Lies die Doku! Wie wäre es, glDeleteTexture aufzurufen?
Wird wo gemacht?
cooky451 schrieb:
Was gibt es besseres als eine Klasse? Im Konstruktor erstellst du die Texture, im Destruktor gibst du sie wieder frei. Einfacher geht es wirklich nicht. Dazu noch nen Move-Konstruktor und alles ist in Butter.
Die Freigabe ist das, wonach ich in diesem Thread frage. Wie gibt man den Speicherbereich der Textur effektiv frei?
EDIT:
cooky451 schrieb:
glTexImage2D kopiert das Bild übrigens, da wird Speicher reserviert! Lies die Doku! Wie wäre es, glDeleteTexture aufzurufen?
glDeleteTexture war das, wonach ich brauchte, danke
-
Ki schrieb:
Wird wo gemacht?
Hast du gerade wegeditiert,nice. Der Code ist aber immer noch ziemlich hässlich.
Ki schrieb:
GLuint *image = new GLuint; *image = loadTexture("img.png"); delete image;
Oh man.. bist du Java programmierer?
Ich habe dir doch schon geschrieben, wie du du das freigibst. Lies die Beiträge hier. glDeleteTexture. [Edit: Na das hast du gefunden, immerhin. :p ]
Mal ein Beiespiel:
class Texture { GLUint id_; public: Texture(const std::string &filename) { // Hier Textur laden. Als "object" id_ nutzen. } Texture(Texture &&texture) : id_(texture.id_) // Move Konstruktor. { texture.id_ = 0; } ~Texture() { glDeleteTextures(1, &id_); // Hier wird die Textur freigegeben. } };
Anwendung:
void foo() { Texture texture("xyz.zyx"); // Hier wird die Textur geladen } // Destruktor gibt frei.