Tutorial OpenGL3 Das Objekt System von OpenGL3: Unterschied zwischen den Versionen
K (Bischen -> Bisschen) |
|||
Zeile 54: | Zeile 54: | ||
Möchte man z.B. FrameBuffer abschalten, weil man diesen nicht mehr braucht, dann benutzt man den passenden Bind Befehl und setzt als ID die 0 ein. | Möchte man z.B. FrameBuffer abschalten, weil man diesen nicht mehr braucht, dann benutzt man den passenden Bind Befehl und setzt als ID die 0 ein. | ||
− | Um das ein | + | Um das ein bisschen zu festigen kommt nun mal ein kleines Beispiel. |
<source lang="cpp">//Erstellung | <source lang="cpp">//Erstellung | ||
GLuint TextureID=0; | GLuint TextureID=0; |
Aktuelle Version vom 21. März 2012, 15:04 Uhr
Seit OpenGL3 sind alle verwendbaren Daten in Objekten verpackt. Die OpenGL3 Bibliothek ist in c geschrieben und kennt daher keine Klassen, daher werden sogenannte ID's zur Assoziation verwendet. Wenn man also eine der Funktionen aufruft, die zur Erstellung eines Objektes zuständig ist, dann bekommt man eine 32Bit große, Vorzeichenlose Nummer zurück. Diese sind nicht zwingend aufsteigend Nummeriert, sondern können vom Treiber frei gewählt werden. Die einzige Bedingung die gestellt wird, ist das die ID 0 nicht vergeben wird, denn diese wird vom Treiber automatisch am Start reserviert und ein Null-Objekt erzeugt.
glBind[Objekttyp](...) Aufruf:
1.) Zuerst wird die Übergebene ID in einer Tabelle gesucht, diese Tabelle wird vom OpenGL Treiber verwaltet und wird vor dem Programmierer versteckt.
2.) Sollte die ID nicht existieren wird die Adresse(Pointer) des Null-Objekt gewählt sonnst der gefundene Pointer. Dieser wird nun in eine Variable gespeichert, welche ebenfalls vom OpenGL Treiber verwaltet wird.
3.) Abhängig von dem Ausgeführten Befehl, kann es sein, dass die ID direkt Übergeben wird(z.B. glGen[Objekttyp]s(...) ), ohne vorher die Ressource Binden zu müssen. Hierzu wird der 1.) Schritt ausgeführt und dann der Pointer verwendet um direkt auf den Speicherbereich, auf der Grafikkarte(VideoRAM) zu arbeiten.
glTexImage2D(...), ... Aufruf:
4.) Die meisten OpenGL Befehle arbeiten über die in Schritt 2.) kennen gelernte Variable, um nicht jedes mal die ID in der Tabelle suchen zu müssen und somit Schneller den Befehl abarbeiten zu können(Schritt 3.) ).
Die Verwendung von Objekten Teilt sich in 4 Bereiche, Erstellung, Verändern, Verwenden und Vernichtung.
Da es verschiedene Typen von Objekten gibt, müssen diese auch Unterschieden werden können und unterschiedlich anwendbar sein.
Es gibt in OpenGL3 folgende Objekte:
- Buffer(auch als VBO bekannt)
- VertexArray
- Query
- Textur
- FrameBuffer(auch als FBO bekannt)
- Shader
- Program
Für jeden Objekttyp gibt es eigene Funktionssammlungen für die 4 oben erwähnten Bereiche. Durch eine Namenskonvention sind diese recht einfach zu erkennen. So werden alle Befehle, zum erzeugen der Objekte wie folgt aufgebaut.
glGen[Objekttyp]s(Anzahl,Pointer auf ID-Array);
Als Beispiel folgendes:
glGenTextures(1,&TextureID);
Die Zerstörung und das aktivieren eines Objektes wird auch über Namenskonventionen erleichtert.
glBind[Objekttyp](...,ID);//Anzahl und Typ der Parameter sind Unterschiedlich.
glDelete[Objekttyp]s(Anzahl,Pointer auf ID-Array);
Als Beispiel folgendes:
glBindTexture(GL_TEXTURE_2D,TextureID);
glDeleteTexture(1,&TextureID);
Sobald man ein Objekt, mit der zu den Objekttyp passenden Generierungs-Funktion erstellt, erhält man eine ID und diese kann man nun verwenden um z.B. ab zu fragen was das Objekt für ein Typ ist und ob es Aktiv ist.
bool glIs[Objekttyp](ID);
Als Beispiel folgendes:
if (glIsTexture(TextureID))
{...}//Objekt ist eine Textur.
else
{...}//Objekt ist keine Textur.
Wenn nun statt einer erhaltenen ID die 0 bei einer OpenGL Funktion übergeben wird, dann zeigt man also auf das Null-Objekt, welches bei allen Objekt-Bind Befehlen zufolge hat, dass dieser Buffertyp als deaktiviert gilt. Möchte man z.B. FrameBuffer abschalten, weil man diesen nicht mehr braucht, dann benutzt man den passenden Bind Befehl und setzt als ID die 0 ein.
Um das ein bisschen zu festigen kommt nun mal ein kleines Beispiel.
//Erstellung
GLuint TextureID=0;
glGenTextures(1,&TextureID);//Es wird ein Objekt erzeugt und die ID dieses Objektes in die TextureID Variable geschrieben.
//Weise Daten dem Objekt zu.
//...
//Zeichenroutine(Verwendung)
if (glIsTexture(TextureID))
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,TextureID);//Bindet das Objekt als OpenGL 2D-Textur.
//...
glBindTexture(GL_TEXTURE_2D,0);
glDisable(GL_TEXTURE_2D);
}
//...
//Aufräumen
glDeleteTextures(1,&TextureID);
Ein Objekt bleibt solange Aktiv und/oder gebunden, bis ein anderes gebunden bzw. der Objektyp deaktiviert wurde. Durch das ständige Binden,Aktivieren und Deaktivieren geht sehr viel Zeit verloren, die sich auf die Programmgeschwindigkeit auswirkt. OpenGL war Ursprünglich eine Statemachiene, also etwas galt solange, bis es verändert wurden, dann galt der neue Zustand. Dieses ist in OpenGL3 immer noch der Fall, allerdings in einer wesentlich abgeschwächten Form, da die meisten States schon entfernt werden konnten. Das gleiche System findet auch bei der Objektverwaltung seinen Platz, was heute allerdings wenig Sinn macht. Früher hat man beim Binden eine Hashtable verwendet, um aus der ID die dazugehörige Speicheradresse, der Objektdaten, auf im VideoRam zu ermitteln und im entsprechendem Globalen Variable zu setzen. Dies hat die Leistung in den früheren OpenGL Versionen erhöht, da die Art, wie man gezeichnet hat eine ganz andere war, als wie heute. Durch die generischen Buffer und der vom Entwickler bestimmten Shaderpipeline ist dieses ständige Binden zu dem Flaschenhals in der OpenGL3 Programmierung geworden. Die ID's, die man von OpenGL bekommt sind allerdings nur 32Bit groß und die Speicheraddressen, passen nicht in die 32Bit. Deshalb ist es bis jetzt noch nicht möglich, über die gegebene API das Problem zu lösen. Man müsste also mit jeder Speicherbus Erweiterung, die eine Grafikkarte mit sich bringt, auch die API anpassen, was etwas Aufwand ist. Der Grafikkartenhersteller Nvidia hat allerdings eine Erweiterung für OpenGL2 und Aufwärts zur verfügung gestellt, welche erlaubt die Speicheraddressen von den OpenGL Objekten ab zu fragen und diese in einen BuffeObjekt zu hinterlegen. Wenn man nun alle logisch zusammengehörenden Objektaddressen in jeweils einen Buffer hinterlegt, dann kann man mit Hilfe dieses alle Bind Befehle auf einen einzigen reduzieren und somit den Flaschenhals auf ein anderen Bereich verschieben. Dies ist allerdings noch eine Extension, wird aber wohl aufgrund seiner Einfachheit recht schnell in die ARB und dann in den OpenGL-Kern finden. Bis dahin muss der Programmierer versuchen das Problem so stark wie möglich zu reduzieren.
Wie schon erwähnt, ist OpenGL3 immer noch Statebasiert, was man sich zu nutze machen kann. Wenn man z.B. alle genutzen Daten nach bestimmten Kriterien sortiert, kann man somit die Anzahl der Bindings und Stateänderungen reduzieren. Im Laufe der einzelnen Artikel wird man noch die ein oder andere Möglichkeit sehen, weitere Optimierungen vor zu nehmen.