Ich habe hier ein Problem, das mich ziemlich stutzig gemacht hat, weil ich das halbe Internet danach ohne Erfolg abgesucht habe, vom Forum ganz zu schweigen. Wahrscheinlich ist die Lösung ganz einfach - ich bin nicht nur in OGL, sondern auch in Delphi und im Programmieren noch recht unerfahren und kenne deshalb vermutlich die ein oder andere nüztliche Methode noch nicht.
Es geht um das Einlesen von Daten. zB 1G Vertices. Diese lese ich aus einer Datei aus - aber wie speicher ich sie ab? Ich habe einen dynamischen Array mit Pointern (also List), doch sobald ich die Länge über ca 300M setze, kommt ein EOutOfMemory - zu wenig Arbeitsspeicher, was sicher nicht der Fall ist. Also hab ich mir mehrere Arrays erschaffen, doch an der Höchstgrenze von 300M ändert sich eigentlich nix... Was tun? Soll ich das Zeugs gleich in einer Art Baum speichern?
Registriert: Mo Sep 23, 2002 19:27 Beiträge: 5812
Programmiersprache: C++
Bei einer solch Großen Menge en Vertices sollte man nicht mehr versuchen diese an einem Stück hochladen zu wollen. Zum einen landet dann ja ein beachtlicher Teil im Arbeitsspeicher des PCs, dessen Bandbreite um ein Vielfaches geringer ist als die zwischen GPU und VRAM (DDR2-800 ~3,2 GB/S, Moderne Graka >40GB/s), und zum anderen macht das auch nicht unbedingt jeder Treiber so mit.
Wenn du also so extrem viele Daten hast dann ist es eigentlich unumgänglich diese dynamisch nachzuladen, also zur Laufzeit zu "streamen". Besonders mit Mehrkern-CPUs geht dies sehr bequem, da man dies über den zweiten Kern erledigen kann ohne dass die komplette Anwendung beim Nachladen stehen bleibt. Dann lädst du also nur was sichtbar ist in den Speicher und lädst neu sichtbare Teile dynamisch nach, während sehr alte (schon lange nicht mehr besichtigte Teile) wieder ausm Speicher geworfen werden. VBOs eigenen sich dazu ja sehr gut, da diese u.a. ein Flag haben dass die VBOs fürs dynamische Verändern optimiert.
Es geht um das Einlesen von Daten. zB 1G Vertices. Diese lese ich aus einer Datei aus - aber wie speicher ich sie ab? Ich habe einen dynamischen Array mit Pointern (also List), doch sobald ich die Länge über ca 300M setze, kommt ein EOutOfMemory - zu wenig Arbeitsspeicher,
Lösung siehe Post vor mir^^
Der Grund für deine Fehlermeldung is einfach: du hast zwar mehr als die 300 MB frei. Wenn du aber ein zusammenhängendes Array allokierst, muss der geforderter Speicher adressmäßig am Stück frei sein! Dein Speicher is aber fragmentiert. WEnn du also z.B. 400 MB als Feld haben willst und dein RAM hat einmal 200 und einmal 300 MB frei, dann hast du zwar 500 MB RAM frei, aber halt keinen Block mit 400 MB, ergo kommt die Exception.
_________________ __________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup
Okay. Damit kann ich mich abfinden. 1G Vertices werd ich wohl auch nicht brauchen
Es geht immer noch um meinen .obj -Loader. Ich habe eine 20-mb Datei mit 240k Vertices und 170k Faces, die ich einlese. Irgendwann kommt dann wiede dieser schöne Fehler, ich schaue in den Taskmanager und dort steht bei Project1.exe Speicherauslastung: 40.000K - also 40MB. Da sollte doch dann eigentlich noch kein Fehler auftreten, wenn ich das richtig verstanden hab? Schließlich brauchen einige Programme locker 100MB. Oder liegt es daran, dass ich so lange Arrays hab?
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Also 20 MB sollten normal überhaupt kein Problem sein. Allerdings weiß ich nicht wie du dein Array erstellst? Evtl wäre da etwas Code ganz nützlich. Wenn nicht sogar unabdingbar.
Bei den Programmen die du gesehen hast tippe ich auch mal darauf, dass die eher kleinere Speicherbereiche alloziiert haben. Und wie Pellaeon schon gesagt hatte genügen teilweise ungünstige Konstellationen die dazu führen können, dass der Speicher etwas zerhackstückelt wird. Und ein Array setzt immer vorraus, dass die Daten an einem Stück im Speicher liegen. Spontan könnte ich mir auch gut vorstellen, dass häufiges SetLength bei einem Array (zum Hinzufügen eines Vertices) recht schnell zu so etwas führen kann. Das ist aber nur ein Tipp ins Blaue.
PS: Je nachdem wie groß es später noch werden kann (Du erwähntest etwas von 300 MB) solltest du dir evtl auch eine ganze andere Struktur ausdenken. Denn dann kommt es sehr stark darauf an was du mit den Daten machen willst und wofür der Loader geschrieben wird? Evtl wäre dann bei sehr großen Datenmengen auch ein Ansatz von Nöten bei dem du lediglich die Datei analysierst und dir gewisse Informationen in deiner Anwenung ablegst. Also nur die Informationen welche Meshes in der Datei stecken und wo sie in der Datei stecken um sie später mehr oder weniger direkt von der Festplatte auf den Grafikspeicher zu laden. Welchen Einsatz dein Code später haben wird ist aber etwas was du dir selbst überlegen musst! Aber 20 MB an Daten sind zwar nicht wenig aber auch nicht wirklich viel.
Du könntest versuchen FastMM4 einzubinden. Der Delphi-Speichermanager ist(zumindest in alten Delphis) manchmal etwas seltsam. Im notfall kannst du dir gleich nach programmstart mit VirtualAlloc mit Commit flag einen großen zusammenhängengen speicherbereich reservieren, und dann selbst managen.
Und das dynamische array mit pointern, soll das heißen dass du für jedes Arrayelement ein eigenes Objekt allocierst?
Zuerst einmal Vielen Dank für die schnellen Antworten. Ich habe noch ein bisschen an meinem Projekt gebastelt, und plötzlich ging das Laden - obwohl ich eigentlich kaum etwas verändert habe... Naja, falls das Problem noch einmal autauchen sollte - ich werds vielleicht mal mit ein bisschen größeren Dateien probieren - meld ich mich wieder
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Will hat geschrieben:
Zuerst einmal Vielen Dank für die schnellen Antworten. Ich habe noch ein bisschen an meinem Projekt gebastelt, und plötzlich ging das Laden
Das dürfte sich dann vielleicht mit einer günstigeren Konstellation im Arbeitsspeicher erklären lassen. Wie Lossy sagte, ich denke auch, dass du mal deinen Code auf häufige Größenänderungen bei Array / Strings prüfen solltest. Z.B. ist eine Schleife, wo bei jedem Durchlauf immer ein Element an ein Array / String angehängt wird der Speicherkiller schlechthin.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Ich habe eigentlich schon versucht, die Anzahl der SetLength-Aufrufe gering zu halten, da sich das ja auch sehr stark auf die Geschwindigkeit auswirkt. Der Array wird immer um 100 Elemente vergrößert.
Gibt es ansonsten irgendwo eine Seite, wo der Speicherbedarf und die Speicherstruktur Delphis übersichtlich dargestellt werden?
hm, damit muss ich mich erst einmal genauer auseinanersetzen; danke für den Link.
Ich glaube allerdings nicht, dass ich viele Speicherlöcher habe, denn nach einem kompletten NeuCode hat das Programm eine Speicherauslastung von ca 45.000k (vorher ca 400.000k).
Aber ich habe mich zu früh gefreut - die uses-Zeile um glBitmap.pas ergänzt, und dann war auch schon wieder Essig. Deshalb poste ich hier jetzt mal den Code...
Der Fehler trat bei einer 20mb-Datei mit 240k Vertices+Normals und 170k Faces auf - wie gehabt eben.
Also im Voraus schon einmal entschuldigung für die Länge und den Programmierstil Doch die entscheidenden Stellen für die Struktur der Daten sind eigentlich recht übersichtlich (hoffe ich), und lieber zu viel Code als zu wenig ^^.
[Edit] Diese grässliche Formatierung kommt von den Pascal-Tags... [/Edit]
Die erste Unit uMeshGlobals definiert nur so ein paar Typen, die ich brauche, sowie die Klasse TMesh. Ein TMesh enthält nur die Information, unter welchem Index die Vectoren etc für jedes Face vorhanden sind, die Daten selbst befinden sich in TMeshManager (uMeshManager).
Um zu viel Array-Resizing zu vermeiden, benutze ich die Prozeduren IncRange und FitRange, die die Arrays um jeweils 100 Elemente vergrößern bzw anpassen.
Naja, viel Glück, vielleicht wird ja jemand fündig...
Ohne den Code angesehen zu haben: Warum ein Array? Musst du viele indizierte Zugriffe machen oder traversierst du das am Ende komplett? Wenn letzteres, dann ist wohl eine verkettete Liste 'ne bessere Idee, damit rennst du jedenfalls nicht in die Speicherfragmentierungsproblematik.
Ich weiß jetzt nicht wie das bei anderen 3D-Formaten ist, aber hier bieten sich Arrays natürlich an, da f nur die Indizes der Vertices angibt... Ich wollte dann eigentlich noch mein eigenes Format schreiben, wo das dann vielleicht anders ist, aber zuerst einmal muss ich die Daten einlesen.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Erst mal etwas ganz wichtiges. In Klassen grundsätzlich IMMER inherited benutzen. Bei einer Klasse die von TObject abgelitten ist das zwar nicht soo tragisch ändern aber nichts, dass es eigentlich trotzdem immer benutzen werden sollte.
Da du ja doch schon sehr viel mit wirklich größeren Daten zu tun hast solltest du dir überlegen ob du das nicht vielleicht etwas anders aufziehen solltest. Du darfst auch nicht vergessen. Die Strings sind auch nur dynamische Arrays. Und ein SetLength +1 bei einem String erfordert höchstwahrscheinlich auch schon, dass dieser verschoben werden muss.
Eine Idee von mir wäre mehr oder weniger ganz auf die Arrays zu verzichten und statt dessen kleine Blöcke zu benutzen. Denn wie gesagt. In einem Array müssen die Daten immer am Stück liegen. Und du hast gleich mehrere davon. Und ich denke genau da wird dein Hauptproblem liegen. Jetzt aber meine Idee. Ich bitte zu bedenken, dass ich normal keine Modelloader schreibe weswegen das Ganze eher als grobes Schema zu betrachten ist. Und dabei auch nur das Ablegen der Vertexdaten. Wenn ich deinen Code richtig verstanden habe, dann reagierst du auch nur auf die ersten Texturkoordinaten. Was die Sache vereinfacht also betrachte ich ein Vertex folgendermaßen.
Code:
PVertex =^TVertex;
TVertex =packedrecord
Material: TMaterial;
Vertice: TVector3f;
Normal: TVector3f;
TexCoord: TVector2f;
end;
Für die Speicherverwaltung würde ich eine extra Klasse schreiben. Diese würde dann Methoden bieten AddVertex, GetVertex. Intern würde bei AddVertex dann folgendes ablaufen. Er würde überprüfen ob er noch genug Platz in seinen Buffern hätte. Da du beim Laden aber eher keine Vertices löscht brauchst du auch auch nur am Ende zu schauen ob noch platz ist. Falls nicht genügend Platz vorhanden ist dann würde er einen Speicherblock mit GetMem alloziieren. Dieser hätte die Größe von 500 * SizeOf(TVertex) (500 sind womöglich zu viel). Diesen Speicherblöck würde er in eine TList hängen. Als Rückgabewert von AddVertex würde ich dann den Pointer auf ein freies Vertex innerhalb des Speichers zurückgeben. Also ein PVertex welches du dann ganz normal füllen kannst. Die Daten würden dann direkt in den Speicher wandern.
Dadurch, dass du alles intern handhabst kommen nur neue Blöcke hinzu aber es muss kein Block umkopiert werden etc. Und da alle Blöcke gleich groß sein werden kannst du recht einfach erreichnen wo sich das 2042te Vertex befindet und dieses zurückgeben. Von außen würde es sich auch noch fast wie ein Array anfühlen. Nur, dass es intern halt auf 20x dieser kleineren Blöcke verteilt wird.
Solltest du die Daten in einen anderen Speicherbereich kopieren müssen (VBO) musst du das lediglich bei den Methoden berücksichtigen. Komplizierter wird es allerdings wenn die Vertices eine unterschiedliche Anzahl an Texturkoordinaten haben können. Aber da weiß ich nicht mal ob das vom Format her überhaupt möglich ist.
Ist wie gesagt eine Idee. Lass dir mal durch den Kopf gehen ob es für deine Aufgaben ausreicht. Allerdings würde ich es schon so machen, dass man die Daten in kleineren, sich nicht in der Größe verändernden, Blöcken ablegt. Dann bekommt auch der Speichermanager keinen Schock mehr.
Vielen Dank für die ausführliche Antwort.
Wie bereits erwähnt, bin ich in Delphi oder Programmierung allgemein noch nicht so erfahren, so dass ich von selbst auf eine solche Lösung noch gar nicht gekommen wäre. Wie dem auch sein, ich werde mir gleich mal so eine Klasse für die Speicherverwaltung schreiben und das ganze ausprobieren. Klingt vielversprechend..
Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.