Unterschied zwischen "Buffer" und "Array" in OpenGL?

12

Wenn ich ein Dokument über WebGL oder OpenGL lese, sind einige Muster in der Verwendung von Funktions- und Objektnamen zu sehen. Aber ich kann den Unterschied zwischen einem Pufferobjekt und einem Array nicht verstehen.

Es gibt "Vertex-Buffer-Objekte", "Vertex-Array-Objekte" und sogar eine Art "Buffer-Array" oder "Array-Buffer".

Im OpenGL-Kontext: Wann ist etwas "Array" und wann sollte es stattdessen "Puffer" genannt werden?

coobit
quelle
Einige Perspektive, denken Sie daran, Daten über das Netzwerk abzurufen und ein Protokoll von allem, was empfangen wird, zu speichern. Sie müssen den Socket lesen und die empfangenen Daten irgendwo ablegen, damit Sie sie weitergeben können. Das ist ein Puffer. Es kann sich häufig um einen lokal festgelegten, dynamisch zugewiesenen einfachen Listentyp handeln. Manchmal ist es so einfach wie ein char* buffer = socketRead();(Pseudocode). Das Protokoll hingegen durchläuft den gesamten Lebenszyklus der App. Sie erstellen also irgendwo ein Array und beginnen mit dem Lesen des Sockets, sobald Sie Daten erhalten, die Sie in das Array geschrieben haben. Auf diese Weise erhalten Sie eine übersichtliche Liste aller empfangenen Daten.
Kevin

Antworten:

5

Die Benennung von Vertex Array Object ist etwas unglücklich. Es gibt drei verschiedene Dinge, die in / mit / um Ihre Anwendung herum vorkommen (früher vorkommen) und die (historisch) unterschiedlich benannt wurden, wobei der Name "array" oder "buffer" enthält (nun, es gibt auch Framebuffer-Objekte). aber ich werde das ignorieren).

  1. Daten, die formal und faktisch in Ihrer Anwendung vorhanden sind, aber von OpenGL auf einmal abgerufen werden (im Gegensatz zu vertexweise). Dies war einmal, was Sie Vertex-Array nennen würden .
    Ziel war es, den Zugriff effizienter zu gestalten, da OpenGL zu einem genau definierten Zeitpunkt, zu dem Sie versprochen haben, dass die Daten konsistent sind, das Ganze auf einmal kopieren und über AGP oder was auch immer in einem Block übertragen kann. Das gibt es nicht mehr.
  2. Daten verdeckt und zugänglich durch ein Handle, das "gebunden", dh aktiviert werden kann. Daten können sich tatsächlich im Hauptspeicher oder auf der Grafikkarte befinden oder in eine PCIe-zuordnungsfähige Region verschoben werden, was auch immer Sie formal nicht besitzen (auch wenn sie sich physisch im RAM befinden und die Daten aus Ihrer Anwendung stammen) ) it - es sei denn, Sie haben es aktuell über die entsprechende API "zugeordnet" und erhalten einen beschreibbaren (und manchmal lesbaren) Zeiger zurück. Sie können auch nur eingeschränkt steuern, was mit den Daten geschieht (Sie können einige Hinweise geben, aber das ist so ziemlich alles).
    OpenGL kann diese Daten mehr oder weniger frei bewegen, und Sie dürfen / können nur über die entsprechende API in den / aus dem Puffer kopieren oder auf die Daten zugreifen, während sie zugeordnet werden. Das ist, was Sie ein Pufferobjekt nennen (Scheitelpunkt-Pufferobjekt, wenn es Scheitelpunkte enthält, aber es muss nicht unbedingt Bilddaten oder Uniformen sein, nur Scheitelpunkte waren die ersten, die einmal unterstützt wurden).
    Damit soll sichergestellt werden, dass OpenGL (im Prinzip) tun kann, was es will, und sogar spekulativ den Puffer über PCIe schieben kann, bevor Sie überhaupt zeichnen. Das funktioniert, weil Sie die Daten nicht besitzen (OpenGL!) Und Sie nur über die angegebene API darauf zugreifen können, sodass jederzeit bekannt ist, dass die Daten gültig sind. Der Treiber kann sich sogar dafür entscheiden, Pufferspeicher auf der Grafikkarte zu löschen, wenn Speicher für etwas anderes benötigt wird, und ihn später bei Bedarf von seiner geheimen Kopie wiederherzustellen.
  3. Eine wirklich blöde Fehlbezeichnung, für die ein viel besserer Name so etwas wie eine Puffermenge oder eine Deskriptormenge wäre. Dies ist das berüchtigte Vertex-Array-Objekt . Aus Ihrer Sicht handelt es sich lediglich um eine Reihe von Pufferhandles, die unter einem anderen dunklen Handle (das Sie binden können) zusammengefasst sind. Die Realität ist allerdings etwas komplizierter. In der Tat ist VAO viel näher an der tatsächlichen Hardware. Grafikkarten haben eine kleine Anzahl (oft 2, 4 oder 8) von Deskriptorsätzen (nicht nur für Puffer, sondern auch für Sampler) mit jeweils so vielen Einträgen, zwischen denen sie sehr effizient wechseln können .
    Die Absicht des Vertex-Array-Objekts besteht nun darin, die Anzahl der API-Aufrufe und die Anzahl der Konsistenzprüfungen zu verringern, die OpenGL intern durchführen muss, und natürlich die Hardware so zu verwenden, wie sie funktioniert. Wenn Sie 5 Puffer binden, muss jeder einige möglicherweise teure Prüfungen durchlaufen, und jeder ist ein Kandidat für Cache-Fehler im Treiber, und jeder einzelne erfordert die Kommunikation mit der Grafikkarte, um einen Deskriptor usw. zu ändern binde eine VAO, der Treiber kann (oft) einfach den auf der Grafikkarte eingestellten Deskriptor umschalten und fertig.
Damon
quelle
8

Ein Vertex-Array-Objekt (VAO) ist ein Objekt, das ein oder mehrere Vertex-Pufferobjekte enthält und die Informationen für ein vollständig gerendertes Objekt speichert.

(aus khronos gezogen )

Jeder Puffer neigt dazu, ein Attribut eines Vertex-Arrays (Objekts) zu bilden. Ein VAO kann viele Scheitelpunktattribute enthalten (z. B. Position, Farbe, UV). Jeder kann in einem eigenen Puffer gespeichert werden, in dem der Puffer eine unformatierte Folge zusammenhängender Bytes angibt und in dem Sie die Größe (Typ) pro Pufferelement für OpenGL-Aufrufe auf der CPU-Seite und für Shader-Arbeiten auf der GPU-Seite explizit angeben müssen.

Das ist ein Weg. Die anderen Möglichkeiten, wie dies funktionieren kann, sind:

  • Alle Attribute werden verschachtelt in einem einzelnen Puffer ODER gespeichert
  • Einige der Attribute befinden sich in eigenen Puffern, andere teilen sich Puffer.

Das folgende Diagramm veranschaulicht diese beiden letzteren Fälle.

Bildbeschreibung hier eingeben

Fazit: Wenn der Ausdruck "Vertex-Array" in OpenGL unqualifiziert verwendet wird, können Sie davon ausgehen, dass dies VAO bedeutet, was sich in einem OpenGL-Kontext (speziell) in der Tat von einem Puffer unterscheidet.

BEARBEITEN bezüglich Ihres Kommentars: Weist GL_ARRAY_BUFFERdarauf hin, dass dieses Pufferobjekt wie oben beschrieben für Scheitelpunktattributdaten verwendet werden soll. Dies liegt daran, dass Puffer nicht nur für Scheitelpunktattribute verwendet werden. Da dies jedoch der häufigste Anwendungsfall ist und Sie nach VAOs fragen, gehe ich nicht auf die anderen ein. Hier ist jedoch eine Liste der anderen Arten von Puffern, die eingerichtet werden können.

Ingenieur
quelle
Die Puffer sind also: 1. in der GPU enthalten, 2. die meiste Zeit eine Art von Daten enthalten (nur Scheitelpunkt, nur Farbe usw.), 3. Daten sind verschachtelt, das heißt 111122223333 usw. 4. Bieten Sie keine Methode für den Datenzugriff an (nicht buffer [2] oder buffer [vertex_3434]). Nun sind Arrays : 1. Sammlung von Puffern, 2. Speicherung von Informationen zum Parsen von Puffern, die sie enthalten (dh Array Store Stride) , Größe eines Elements, Offsets, damit korrekt auf Daten aus Puffern zugegriffen werden kann
coobit
1. An beiden Enden sind Puffer vorhanden, die zwischen CPU und GPU übertragen werden (möglicherweise hin und her). Wie würden Sie die Daten auffüllen, die auf die GPU hochgeladen werden sollen, wenn Sie ein Mesh von der Festplatte laden? Ja, Elemente haben im gesamten Puffer einen einheitlichen Typ, aber abhängig von der verwendeten Technologie kann jedes Pufferelement entweder ein Grundelement oder ein structTyp sein. Die Daten können pro Puffer verschachtelt oder vollständig einheitlich sein. Sie können sie wie bei einem herkömmlichen C-Array auf der CPU indizieren. Array- Objekte (Verwenden Sie diese korrekte Terminologie oder verwirren Sie sich selbst!) ... (Fortsetzung unten)
Ingenieur
2. Ja, und Sie müssen explizit sicherstellen, dass Ihre In-Shader-Pufferdeklarationen mit den Spezifikationen übereinstimmen, die Sie auf Ihrer VAO-Seite auf CPU-Seite festgelegt haben Array-Objekt. " (khronos docs)
Ingenieur
Also, nur um den Nagel mehr zu treffen ... Wie haben die Leute vor AO nur mit BO gearbeitet? Oder waren AO immer in OpenGL präsent und es ist nur VAO, das später als VBO eingeführt wurde?
Coobit
@coobit io7m.com/documents/history-vertex-spec - hier erhalten Sie eine Vorstellung von den Unterschieden zwischen OpenGL, 3Dfx usw. für feste Pipelines (alte Schule) und modernen, programmierbaren Pipelines OpenGL und Direct3D.
Ingenieur
5

Diese Terminologie ist in der Geschichte von OpenGL verwurzelt. Es ist wichtig zu wissen, dass OpenGL für die meisten GL-Versionen, die hier relevant sind, schrittweise weiterentwickelt wurde, indem einer bereits vorhandenen API neue Funktionen hinzugefügt wurden, anstatt die API zu ändern.

Die erste Version von OpenGL hatte keinen dieser Objekttypen. Das Zeichnen wurde durch das Ausgeben mehrerer glBegin / glEnd-Aufrufe erreicht, und ein Problem bei diesem Modell war, dass es hinsichtlich des Funktionsaufruf-Overheads sehr ineffizient war.

OpenGL 1.1 hat die ersten Schritte unternommen, um dies durch die Einführung von Vertex-Arrays zu beheben. Anstatt Vertex-Daten direkt anzugeben, können Sie sie jetzt aus C / C ++ - Arrays beziehen - daher der Name. Ein Vertex-Array ist also genau das - ein Array von Vertices und der GL-Status, der erforderlich ist, um sie anzugeben.

Die nächste wichtige Entwicklung kam mit GL 1.5 und ermöglichte das Speichern von Vertex-Array-Daten im GPU-Speicher anstatt im Systemspeicher ("clientseitig"). Eine Schwäche der GL 1.1 Vertex-Array-Spezifikation bestand darin, dass der vollständige Satz von Vertex-Daten jedes Mal, wenn Sie ihn verwenden wollten, auf die GPU übertragen werden musste. Wenn es sich bereits auf der GPU befand, konnte diese Übertragung vermieden und potenzielle Leistungssteigerungen erzielt werden.

Daher wurde ein neuer Typ eines GL-Objekts erstellt, um das Speichern dieser Daten auf der GPU zu ermöglichen. Genau wie ein Texturobjekt zum Speichern von Texturdaten verwendet wird, speichert ein Scheitelpunktpufferobjekt Scheitelpunktdaten. Dies ist eigentlich nur ein Sonderfall eines allgemeineren Pufferobjekttyps, der unspezifische Daten speichern kann.

Die API für die Verwendung von Vertex-Pufferobjekten wurde in die bereits vorhandene Vertex-Array-API integriert. Aus diesem Grund sehen Sie darin seltsame Dinge wie das Konvertieren von Byte-Offsets in Zeiger. Jetzt haben wir eine Vertex-Array-API, die nur den Status speichert, wobei die Daten aus Pufferobjekten und nicht aus speicherinternen Arrays stammen.

Dies bringt uns fast zum Ende unserer Geschichte. Die resultierende API war ziemlich ausführlich, wenn es darum ging, den Vertex-Array-Status anzugeben. Ein weiterer Weg der Optimierung bestand darin, einen neuen Objekttyp zu erstellen, der diesen gesamten Status zusammenfasste, mehrere Änderungen des Vertex-Array-Status in einem einzigen API-Aufruf zuließ und GPUs zuließ um möglicherweise Optimierungen durchzuführen, weil Sie im Voraus wissen, welcher Status verwendet werden soll.

Geben Sie das Vertex-Array-Objekt ein, das all dies zusammenfasst.

Zusammenfassend begann ein Vertex-Array als Sammlung von Status und Daten (in einem Array gespeichert) zum Zeichnen mit. Ein Vertex-Puffer ersetzt den speicherinternen Array-Speicher durch einen GL-Objekttyp, sodass das Vertex-Array nur noch den Status aufweist. Ein Vertex-Array-Objekt ist nur ein Containerobjekt für diesen Status, sodass es einfacher und mit weniger API-Aufrufen geändert werden kann.

Maximus Minimus
quelle
0

Ich habe eine Weile nicht mehr mit OpenGL gearbeitet, daher bin ich vielleicht nur zur Hälfte richtig. Allgemein gesagt: Puffer speichern ein Array von unformatiertem Speicher. Ein Array ist ein allgemeiner Begriff für zusammenhängenden Speicher.

Ein Puffer muss an den Kontext gebunden sein, während ein Array nur ein Array von Daten ist. Wenn ich mich richtig erinnere, sollen die Daten im Puffer auf die Grafikkarte (daher die Bindung) kopiert werden.

Hoffe das hilft ein bisschen

Juicef
quelle
Was ist dann GL_ARRAY_BUFFER? Warum hieß es so? Nach Ihrer Hypothese ist es "Unformatierter zusammenhängender Speicher" :)
coobit
Nun, dieses Beispiel ist nur eine ID für einen Puffer (an den Sie Ihr Array binden). Der Array-Puffer (in Ihrem Beispiel) wird für Scheitelpunktattribute verwendet, sodass Sie Ihr Scheitelpunktattributarray im Grunde an einen Puffer binden. Klingt verwirrend, lassen Sie mich Ihnen ein Beispiel geben. Sie haben ein Array auf der CPU-Seite, das Farbe, Normal, Positionen usw. sein kann, und jetzt möchten Sie, dass die GPU darauf zugreift. Das ist, wenn der bindBuffer hereinkommt, im Grunde genommen Ihr "CPU-Array" dem "GPU-Array" zuzuordnen.
Juicef
Warum es so genannt wurde, kann ich nicht beantworten. Ich würde annehmen, dass es daran liegt, dass Sie eine Reihe verschiedener Daten haben, die dort
eingehen