Autor |
Beitrag |
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 18.06.18 18:56
Hallo EE und alles Gute nachträglich!, ich bins mal wieder.
Wir sind (oder waren) in der Schule gerade bei 3D-Darstellug. Ich hab natürlich mein eigenes Ding gemacht und basierend auf meiner glm-Bibliothek alles zusammengebastelt.
Jetzt hatten wir die verschiedenen Darstellungsformen wie zB. Iso-, Di- oder Trimetrie. Diese Transformationen dürfen allerdings nicht das Objekt selbst ändern, sondern nur die Darstellung beeinflussen.
Also versuche ich mal im Code grob zu erklären, was ich gemacht habe:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45:
| type PTriaCon = ^TTriaCon; TTriaCon = record verts: array[0..2] of IVec4; color: integer; z: double; end;
TTria = class tria: PTriaCon; constructor Create(p1, p2, p3: IVec4; color: integer = 0); destructor Free; procedure transform(mat: IMat4x4); end;
TRect = class {$ifndef splitRects} trias: array[0..1] of TTria; verts: array[0..3] of IVec4; {$else} trias: array[0..3] of TTria; verts: array[0..4] of IVec4; {$endif} constructor Create(p1, p2, p3, p4: IVec4; color: integer = 0); destructor Free; procedure transform(mat: IMat4x4); end;
TCube = class rects: array[0..5] of TRect; {$ifndef splitRects} verts: array[0..7] of IVec4; {$else} verts: array[0..13] of IVec4; {$endif} constructor Create(p1, p2, p3, p4, p5, p6, p7, p8: IVec4); destructor Free; procedure transform(mat: IMat4x4); end;
var trias: array of PTriaCon; lverts: TStringList; tverts: array of IVec4; |
Bei den Klassen, speziell den array of IVec4 nutze ich aus, dass diese nur Referenzen auf das Objekt speichern.
Der Grund, warum ich bei lverts eine TStringList verwendet habe ist, dass ich jeden Eckpunkt nur ein mal vertreten haben möchte und es by default keine Hashtables is Delphi7 gibt. Und ich hatte vorerst noch wenig lust mir selber ein Array zu basteln dass dann per Binärsuche und Insertsort Elemente findet und einfügt. Kommt vmtl noch.
So weit so gut. Jetzt zum Problem. Dieses tritt beim Rückkopieren der zwischengespeicherten Eckpunkte auf:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| procedure DrawTriangles(bmp: TBitmap; matProj: IMat4x4); var i: integer; v0, v1, v2: IVec4; begin for i := 0 to HIGH(tverts) do begin tverts[i].assign(TVec4(lverts.Objects[i])); TVec4(lverts.Objects[i]).mul(matProj); end; SortTriangles(trias);
for i := 0 to HIGH(trias) do begin v0 := trias[i].verts[0]; v1 := trias[i].verts[1]; v2 := trias[i].verts[2]; bmp.Canvas.Brush.Color := trias[i].color; bmp.Canvas.Polygon([ Point(calcx(v0.x), calcy(v0.y)), Point(calcx(v1.x), calcy(v1.y)), Point(calcx(v2.x), calcy(v2.y)) ]); end;
for i := 0 to HIGH(tverts) do begin TVec4(lverts.Objects[i]).assign(tverts[i]); end; end; |
Jetzt passiert folgendes beim Rückkopieren: bei i = 8 (was dem 2. Eckpunkt des Würfels entspricht) wird aus dem IVec4 des Würfels ein Pointer. Alle anderen bleiben IVec4, nur der verliert anscheinend seine Gültigkeit.
Hier einige Screens:
Vor dem Rückkopieren:
Vor der fraglichen Zuweisung:
nach der Zuweisung:
Die TVec4.assign-Methode kopiert übrigens nur die Koordinaten ins andere Objekt, mehr nicht. Das kann also kaum die Ursache sein:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| function TVec4.assign(v: TVec4) : TVec4; begin result := assign(v.x, v.y, v.z, v.w); end; function TVec4.assign(v: IVec4) : TVec4; begin result := assign(v.x, v.y, v.z, v.w); end; function TVec4.assign(x, y, z, w: double): TVec4; begin fx := x; fy := y; fz := z; fw := w; result := self; end; |
Kann sich das jemand erklären, warum ausgerechnet dieses Objekt verschwindet? Ich bin echt ratlos. Vielleicht hat es etwas mit der Verwendung von TStringList und der Konvertierung zu TObject zu tun, das erklärt aber nicht den Einzelfall.
Vielen dank im Vorraus
Einloggen, um Attachments anzusehen!
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Zuletzt bearbeitet von Symbroson am Di 19.06.18 00:00, insgesamt 1-mal bearbeitet
|
|
jaenicke
Beiträge: 19276
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 18.06.18 23:05
Benutzt du dort die Referenzzählung bei der Benutzung von IVec4? Dann ist es keine gute Idee parallel mit Objektreferenzen auf TVec4 zu arbeiten. Entweder nur Interfaces (würde ich empfehlen) oder nur Objektreferenzen, dann klappt es auch mit der Referenzzählung.
Konkret sehe ich auf Anhieb auch keine direkte Ursache, aber es muss denke ich mit der Referenzzählung zu tun haben.
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 18.06.18 23:14
Also wenn du meinst, dass ich den Referenzenzähler selber beeinflusse dann nein. Ich erstelle lediglich die Objekte und weise die dem Array zu, sodass alle Plätze im Array initialisiert wurden. Später nutze ich wie gesagt nur assign, dass nur die Koordinaten übernimmt.
Ich schau mal wie die Zähler sich verhalten…
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mo 18.06.18 23:36
Erwies sich als schwierig - ich kann das zu TVec4 gecastete Object nicht zu IVec4 casten.
Ich schicke aber nochmal die Funktion rein, in der die Liste initialisiert wird - vielleicht bringt uns das weiter:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| constructor TTria.Create(p1, p2, p3: IVec4; color: integer = 0); var len: integer; begin inherited Create; new(tria); tria.color := color; tria.verts[0] := p1; tria.verts[1] := p2; tria.verts[2] := p3;
lverts.AddObject(Format('%p', [pointer(p1)]), TObject(p1)); lverts.AddObject(Format('%p', [pointer(p2)]), TObject(p2)); lverts.AddObject(Format('%p', [pointer(p3)]), TObject(p3)); len := LENGTH(tverts); setLength(tverts, lverts.Count); while len < lverts.count do begin tverts[len] := Vec4(0); inc(len); end; len := length(trias); SetLength(trias, len + 1); trias[len] := tria; end; |
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mi 20.06.18 14:38
Ich hab jetzt lverts doch mal als array of IVec4 implementiert - und da scheint es keine Probleme zu geben.
Merkwürdig
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
jaenicke
Beiträge: 19276
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 20.06.18 21:55
Weil du dort nur mit den Interfaces arbeitest und nicht mit Objektreferenzen. Dadurch bleibt die Referenz in der Liste aktiv.
Referenzzählung ist ja im Grunde erst einmal sehr einfach. Wenn du einer Interfacereferenz eine Instanz zuweist, wird darin der Referenzzähler um 1 inkrementiert. Entfernst du die wieder, wird er wieder dekrementiert.
Nehmen wir mal diesen Quelltext, nur als Beispiel: Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| var p1: IVec4; begin p1 := TVec4.Create; lverts.AddObject(Format('%p', [pointer(p1)]), TObject(p1)); end; |
Nach der Zuweisung an p1 ist der Referenzzähler auf 1. Dann packst du die Referenz als Objekt in die Liste. Am Ende der Methode geht p1 aus dem Scope und wird daher wieder abgeräumt. Der Referenzzähler geht auf 0 und somit wird das Objekt wieder freigegeben. Die Referenz in der Liste ist somit ungültig.
Machst du das gleiche mit deinem Array, geht der Referenzzähler stattdessen auf 2 hoch und beim Abräumen der Variable wieder auf 1 herunter. Das Objekt wird also nicht freigegeben.
Für diesen Beitrag haben gedankt: Symbroson
|
|
Symbroson
Beiträge: 382
Erhaltene Danke: 67
Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
|
Verfasst: Mi 20.06.18 22:08
Ok das macht Sinn. Seltsam bleibt nur, dass nur ein einziges Interface betroffen war.
Danke auf jeden Fall für deine Hilfe!
_________________ most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
|
|
jaenicke
Beiträge: 19276
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 21.06.18 09:59
Symbroson hat folgendes geschrieben : | Seltsam bleibt nur, dass nur ein einziges Interface betroffen war. |
Durch die Freigabe des Objekts wird noch kein Speicher überschrieben, er wird nur wieder als frei markiert. Deshalb merkt man das Problem oft erst später, wenn der Speicher an der Stelle, an der das Objekt lag, anderweitig verwendet wurde und daher dort keine gültigen Daten mehr liegen.
Anders sieht das aus, wenn du FastMM einbindest. Das überschreibt die Daten bei der Freigabe mit einem speziellen Muster, so dass es direkt beim Zugriff immer knallt und dies auch erkannt wird.
|
|
|