Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Fr Jul 18, 2025 04:00

Foren-Übersicht » Programmierung » OpenGL
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 
Autor Nachricht
BeitragVerfasst: Sa Sep 22, 2007 13:50 
Offline
DGL Member

Registriert: Mi Mär 31, 2004 15:24
Beiträge: 114
Hallo Leute!
Ich habe eine Klasse zur Benutzung von Vertex Buffer Objects (VBOs) erstellt. Mein Problem: Wenn ich VBOs benutze, um Arrays aus Vertices zu rendern, ist das bedeutend langsamer als die Benutzung von Displaylisten. Vielleicht könnte ja jemand einmal den Code überfliegen und mir sagen, wo die Geschwindigkeitseinbuße liegt. Das wäre echt nett :) Ich denke, dass das Problem wahrscheinlich in den Prozeduren "FillBuffer" oder "Render" liegt. Codebasis für die Codes bzgl. des VBO ist das VBO-Tutorial hier im Wiki.

Code:
  1.  
  2. // Delphi 5 Prof.
  3.  
  4.   type
  5.     PVertex = ^TVertex;
  6.     TVertex = Record
  7.       s,t : Single;
  8.       x,y,z : Single;
  9.   end;
  10.   PVertices = ^TVertices;
  11.   TVertices = Array of TVertex;
  12.  
  13.   PTriangle = ^TTriangle;
  14.   TTriangle = Array[0..2] of TVertex;
  15.  
  16.   PTriangles = ^TTriangles;
  17.   TTriangles = Array of TTriangle;
  18.  
  19.  
  20.   type
  21.     PVBObject = ^TVBObject;
  22.     TVBObject = class(TObject)
  23.       private
  24.         Number : Integer;
  25.         VBOPointer : PVertex;
  26.       public
  27.         Triangles : TTriangles;
  28.         VertexCount : Integer;
  29.         constructor Create(); reintroduce;
  30.         destructor Destroy(); reintroduce;
  31.         procedure FillBuffer (TrianglesPointer : PTriangles = nil);
  32.         procedure BindBuffer;
  33.         procedure ClearBuffer;
  34.         procedure Render;
  35.   end;
  36.  
  37. implementation
  38.  
  39. constructor TVBObject.Create; // Erstellt eine Instanz und generiert VBO
  40. begin
  41.   inherited Create;
  42.   glGenBuffers(1,@Number);
  43.   glBindBuffer(GL_ARRAY_BUFFER, Number);
  44.   glEnable(GL_VERTEX_ARRAY);
  45.   glEnable(GL_TEXTURE_COORD_ARRAY); // Auch glEnableClientState bringt keinen Unterschied
  46. end;
  47.  
  48. destructor TVBObject.Destroy;
  49. begin
  50.   ClearBuffer;
  51.   inherited Destroy;
  52. end;
  53.  
  54. procedure TVBObject.FillBuffer(TrianglesPointer : PTriangles = nil);
  55.   var
  56.     VertexPointer : PVertex;
  57.     i : Integer;
  58. begin
  59.   glBindBuffer(GL_ARRAY_BUFFER, Number);
  60.   if TrianglesPointer = nil then
  61.     TrianglesPointer := @Triangles;
  62.  
  63.     VertexCount := Length(TrianglesPointer^)*3;
  64.  
  65.     glBufferData(GL_ARRAY_BUFFER, VertexCount*SizeOf(TVertex), nil, GL_STATIC_DRAW);
  66.  
  67.     VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
  68.       VertexPointer    := VBOPointer;
  69.  
  70.     for i := 0 to (VertexCount div 3)-1 do
  71.     begin
  72.       VertexPointer^.S := TrianglesPointer^[i][0].S;
  73.       VertexPointer^.T := TrianglesPointer^[i][0].T;
  74.       VertexPointer^.X := TrianglesPointer^[i][0].X;
  75.       VertexPointer^.Y := TrianglesPointer^[i][0].Y;
  76.       VertexPointer^.Z := TrianglesPointer^[i][0].Z;
  77.       Inc(Integer(VertexPointer), SizeOf(TVertex));
  78.       VertexPointer^.S := TrianglesPointer^[i][1].S;
  79.       VertexPointer^.T := TrianglesPointer^[i][1].T;
  80.       VertexPointer^.X := TrianglesPointer^[i][1].X;
  81.       VertexPointer^.Y := TrianglesPointer^[i][1].Y;
  82.       VertexPointer^.Z := TrianglesPointer^[i][1].Z;
  83.       Inc(Integer(VertexPointer), SizeOf(TVertex));
  84.       VertexPointer^.S := TrianglesPointer^[i][2].S;
  85.       VertexPointer^.T := TrianglesPointer^[i][2].T;
  86.       VertexPointer^.X := TrianglesPointer^[i][2].X;
  87.       VertexPointer^.Y := TrianglesPointer^[i][2].Y;
  88.       VertexPointer^.Z := TrianglesPointer^[i][2].Z;
  89.       Inc(Integer(VertexPointer), SizeOf(TVertex));
  90.     end;
  91.  
  92.   glUnMapBuffer(GL_ARRAY_BUFFER);
  93. end;
  94.  
  95. procedure TVBObject.BindBuffer;
  96. begin
  97.   glBindBuffer(GL_ARRAY_BUFFER, Number);
  98. end;
  99.  
  100. procedure TVBObject.ClearBuffer;
  101. begin
  102.   glDeleteBuffers(1, @Number);
  103. end;
  104.  
  105. procedure TVBObject.Render;
  106. begin
  107.   BindBuffer;
  108.   glInterleavedArrays(GL_T2F_V3F, Sizeof(TVertex), nil);
  109.   glDrawArrays(GL_TRIANGLES,0,VertexCount);
  110. end;
  111.  


Einmalig wird das Objekt instantiiert und mit "FillBuffer" dann mit Daten gefüllt. Danach wird einfach nur noch mit "Render" gerendert.

Wenn "FillBuffer" direkt Vertices verlangt, anstatt Dreiecke wie jetzt, ist es auch nicht schneller. Irgendwo muss ein Konzeptfehler von mir sein.
Falls jemand was für sein Projekt nützliches finden sollte, kann er es behalten :)

Danke!


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 07:37 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Apr 25, 2005 17:51
Beiträge: 464
Da deine Daten aus einem zusammenhängendem Buffer kommen, würde ich glBufferData zur Übergabe benutzen, anstatt der for-Schleife. Und da du deine Daten anscheinend im nachinein nicht änderst, dann das ganze als GL_STATIC_DRAW_ARB ablegen.
Das Füllen alleine dürfte nicht die Performance beeinflussen, da es ja Initialisierungsarbeit ist, esseidenn du tust mittem im Rendern immer mal neue VBOs anlegen. Dann macht es natürlich was aus.

Code:
  1. procedure TVBObject.Render;
  2. begin
  3.    BindBuffer;
  4.    glInterleavedArrays(GL_T2F_V3F, Sizeof(TVertex), nil);
  5.    glDrawArrays(GL_TRIANGLES,0,VertexCount);
  6. end;


Ich würde das BindBUffer direkt reinschreiben, oder zumindest inline machen.

Insgesamt würde ja VBO vs DL heir shcon ab udn an diskuttiert. Und bei statischer Geometrie geht wohl nichts wirklich schneller als die Listen in OGL.

_________________
__________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 07:43 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Apr 25, 2005 17:51
Beiträge: 464
Ach ja und man sollte glDrawRangeElements nutzen, da ist die Chance höhe das die GraKa optimieren kann.

_________________
__________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 13:02 
Offline
DGL Member

Registriert: Mi Mär 31, 2004 15:24
Beiträge: 114
Ja, danke für die Antwort! Ich benutze jetzt als Alternative auch ein Objekt für Displaylisten. Bei den VBO's:


Code:
  1. glBufferData(GL_ARRAY_BUFFER, VertexCount*SizeOf(TVertex), nil, GL_STATIC_DRAW);

Obigen Befehl benutze ich ja, um Speicher zu reservieren. Und schreib dann später die Vertices mit der for-Schleife dort hinein.

Sollte ich jetzt folgenden Code anstatt der for-Schleife benutzen? Gelangen die Daten dann immer noch in den VRAM?
Code:
  1.  
  2.   VBOPointer := glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
  3.   glBufferData(GL_ARRAY_BUFFER, VertexCount*SizeOf(TVertex), @Vertices, GL_STATIC_DRAW);
  4.  


Zitat:
Ich würde das BindBUffer direkt reinschreiben, oder zumindest inline machen.

Hilft das bei der Geschwindigkeit? Achja, und was heißt eigentlich inline? :)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 13:11 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Apr 25, 2005 17:51
Beiträge: 464
Du schreibst nach dem Binden des Buffers nur

Code:
  1. glBufferData(GL_ARRAY_BUFFER, VertexCount*SizeOf(TVertex), @Vertices, GL_STATIC_DRAW);


Dadurch werden die Daten automatisch in dein VBO kopiert. Das Mappen ist dann nicht mehr notwendig, das brauchst du nur, wenn du "per Hand" im VBO rummehren willst.

inline bedeutet, dass an die Stelle des Funktionaufrufes direkt vom Compiler der Quellcode der Funktion eingesetzt wird. Also sowas wie die Makros in Assembler.
Dadurch sparst du dann an dieser Stellen den Function Overhead. Normalerweise bedeutet inline, dass der Code größer wird. Da deine Methode aber nur 1 Zeile enthält, nämlich widerrum nen Funktionsaufruf, kannst du nur gewinnen wenn du inline machst^^

_________________
__________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 13:12 
Offline
Ernährungsberater
Benutzeravatar

Registriert: Sa Jan 01, 2005 17:11
Beiträge: 2068
Programmiersprache: C++
Eine Funktion hat normalerweise etwas Overhead weil es einen SRJ (SubRoutineJump) benötigt. Dazu müssen noch die Variablen kopiert werden und ähnliches.
Wenn hinter der Funktion aber inline steht wird diese bei einem Aufruf nicht aufgerufen sondern einfach an die entsprechende Stelle des Codes kopiert, was Geschwindigkeit bringt (aber dafür grösseren Code, nur der ist eh egal).
Ist mit Delphi in den neueren Versionen möglich.

_________________
Steppity,steppity,step,step,step! :twisted:
❆ ❄ ❄ ❄ ❅ ❄ ❆ ❄ ❅ ❄ ❅ ❄ ❅ ❄ ❄
❄ ❄ ❄ ❅ ❄ ❄ ❄ ❅ ❄ ❄ ❆ ❄ ❄


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 13:59 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Also ich weiß nicht ob Borland das wieder eingebaut hat aber die Hilfe von dem Delphi 7 sagt hier folgendes.
Delphi Hilfe hat geschrieben:
Das reservierte Wort inline und die Direktive assembler werden aus Gründen der Abwärtskompatibilität beibehalten. Sie haben keine Auswirkungen auf den Compiler.

Was aber nicht ganz stimmt, denn sobald ich inline benutze bekomme ich einen Kompilerfehler.

Aber im Endeffekt ist es auch ziemlich egal. Funktionsoverhead ja. Das Stimmt. Aber wenn diese Funktion 2-200 mal pro Sekunde aufgerufen wird dann macht das überhaupt nichts aus. Spannend wird es wenn an der 200 noch 2-4 Nullen anhängen. Aber sonst gibt es ganz andere Stellen auf die man achten sollte.

DisplayListen: Innerhalb einer DisplayListe werden normal auch nur VBOs benutzt. Von daher sollte das Ergebniss bei idealen Bedingungen eigentlich gleich sein. Dass der Treiber es mit DisplayListen zwar trotzdem irgendwie immer noch besser hinbekommt irritiert mich persönlich auch ein bisschen. Ich vermute da einfach mal, dass es spezielle Konstrukte gibt bei denen die Grafikkarten intern besonders gut optimieren können und dadurch massiv an zeit einsparen können. Wie Pallaeon sagte. glDrawRangeElements. Dieses benötigt aber einen zusätzlichen Speicherbereich.

Ich muss aber gestehen, dass meine letzte Benutzung von VBOs mittlerweile auch schon locker 2 Jahre zurück liegt. In der Zwischezeit hat sich bei den Karten ein bisschen was getan.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Di Sep 25, 2007 14:11 
Offline
DGL Member

Registriert: Mi Mär 31, 2004 15:24
Beiträge: 114
Ja, das sagt mir die Delphi Hilfe auch - bei Delphi 5 Prof.
Wenn ich "inline" als Direktive hinter die Funktion setze(in der Implemation und in "Definition" in Klassenobjekt) gibt er mir einen Kompilierungsfehler zurück. Mit Lazarus kompiliert er aber den Code.

Ok, ich denke, ich werde es einfach mal mit glDrawRangeElements beim Render probieren und das Binden des Puffers direkt reinschreiben.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Mo Okt 01, 2007 17:00 
Offline
DGL Member

Registriert: Di Jun 06, 2006 09:59
Beiträge: 474
Das reservierte wort inline und die inline direktive sind etwas verschiedenes. Früher konnte man asm-code mit inline einbinden. Die inline direktive teilt delphi mit, dass die funktion inline einzubinden ist, was jedoch nur unter bestimmten Voraussetzungen und in neuen Delphis (7 ist afaik zu alt) funktioniert.

_________________
Bild


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 
Foren-Übersicht » Programmierung » OpenGL


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 5 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.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.009s | 15 Queries | GZIP : On ]