Hi!
Ich versuche mir gerade ein effektives Materialsystem zu basteln. Dabei habe ich eine Normale Shaderklasse und eine LinkedShader-Klasse. Die LinkedShader-Klasse stellt dabei die Pipeline dar (Vertex, Fragment und Geometry Shader). Vom Linked Shader kann ich nun ein Objekt der Material-Klasse erstellen lassen. Diese Material Klasse hat einen Zeiger auf das LinkedShader Objekt sowie ein Array von Floats.
Der Plan war, die Uniforms jedes Material selbst speichern kann, und wenn es mal genutzt wird diese dann recht schnell updaten kann. Nun stellt sich mir aber die Frage, wie ich diese am besten speichere. Dazu habe ich auch gleich erstmal ein paar Fragen:
1. Stimmt es, dass jedes Uniform als Float gespeichert wird? Selbst wenn ich dem glUniform2i nutze, werden 2 Floats anstatt von 2 Ints gespeichert?
Nun hätte ich zwei ansätze, um die Uniforms im Material zu speichern:
1. Das Material hat ein Array von Matrizen, eins von Vectoren bis zu 4ten Dimension und von Floats und Ints usw ... . Als erstes finde ich mit glGetProgram heraus, wie viele Maximale Uniforms es gibt, anschließend gehe ich von 0 bis zur Anzahl der Uniforms -1 mit glGetActiveUniform durch und speichere die Informationen die ich geliefert bekomme. Anhand dessen erstelle ich dann die ganzen arrays.
2. Ich mache das genauso wie in 1. beschrieben, nur speichere ich alles in Floats, wodurch ich nurnoch ein Array habe.
Meine eigentliche Frage ist nun: Ist das so umsetzbar, oder gibt es da bessere Möglichkeiten?
[GL] Anzahl der glProgram Register/Uniforms
- Schrompf
- Moderator
- Beiträge: 5162
- Registriert: 25.02.2009, 23:44
- Benutzertext: Lernt nur selten dazu
- Echter Name: Thomas
- Wohnort: Dresden
- Kontaktdaten:
Re: [GL] Anzahl der glProgram Register/Uniforms
Nur Floats sollte eigentlich reichen, denke ich. Es sei denn, Du fängst an, große Bitschiebemagie zu betreiben, um größere Datenmengen in wenige Register zu pressen.
Was ich aber bedenkenswerter finde: Du möchtest alle Shaderkonstanten für alle Shader im Material speichern? Was ist mit den Per-Objekt-Daten wie den Transformationsmatrizen und so? Und wie setzt Du die Beleuchtung um, sprich wechselnde Lichtquellen-Kombinationen? Oder benutzt Du einen Deferred Renderer?
Was ich aber bedenkenswerter finde: Du möchtest alle Shaderkonstanten für alle Shader im Material speichern? Was ist mit den Per-Objekt-Daten wie den Transformationsmatrizen und so? Und wie setzt Du die Beleuchtung um, sprich wechselnde Lichtquellen-Kombinationen? Oder benutzt Du einen Deferred Renderer?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
- kimmi
- Moderator
- Beiträge: 1412
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: [GL] Anzahl der glProgram Register/Uniforms
Geht es nicht eher um die material-spezifischen Konstanten? Aber die Frage von Schrompf bleibt bestehen. Da bin ich un mal neugierig @Schrompf: wie hast du das vom Design gelöst?
Gruß Kimmi
Gruß Kimmi
- Schrompf
- Moderator
- Beiträge: 5162
- Registriert: 25.02.2009, 23:44
- Benutzertext: Lernt nur selten dazu
- Echter Name: Thomas
- Wohnort: Dresden
- Kontaktdaten:
Re: [GL] Anzahl der glProgram Register/Uniforms
@Kimmi: Shaderkonstanten? Gute Frage. Jeder Shader hat CPU-seitig ein Float4-Array, das ich im Code aktualisiere und dann einmalig als Block hochlade. Hat sich als schneller herausgestellt als wenn ich jede Änderung einzeln an DX übermittle. Die Shaderkonstanten werden vor dem Zeichnen zusammengesetzt, eine Gruppierung in Constant Buffers nach Aktualisierungsrate wie für DX10 habe ich bisher nicht berücksichtigt.
Shaderseitig zieht die Engine nach Generierung des jeweiligen Shaders die ganzen Konstanten- und Texturregister aus den Reflection-Strukturen von DX. Es wird also einmalig ermittelt, auf welchen Indizes die Texturen, Trafomatrizen, Lichtparameter, Materialparameter und so weiter liegen. Das Zuweisen der Parameter ist also shader-agnostisch per "if( zielRegister != SIZE_MAX ) SetFloat4(...);" möglich. Die aktuellen Daten für die Trafomatrizen ergeben sich ja live pro Renderjob, die Lichtparameter sind pro Frame statisch und werden nach Bedarf zusammengestellt, die Materialparameter werden über eine Namenszuordnung an Shaderkonstanten zugewiesen, Nebelparameter und sowas sind ein banales if wie oben beschrieben.
Es funktioniert gut! Schwächen sind aber auch bekannt. Zum Einen die Zuordnung der Parameter zu Konstantenregistern anhand ihres Namens - das ist ein String-Lookup in eine kleine Map, das könnte man durch eine simple Enumerierung aller verwendeten Shaderparameter auch als lineares Array gestalten. Etwas mehr Speicher pro Materialparameter-Satz, etwas weniger Rechenzeit in jedem Renderjob. Hab ich bisher nicht gemacht, weil wir eh schon >50% nur in DirectX-Calls verbringen. Und zum Anderen muss ich mir irgendwann noch eine Methode überlegen, wie man pro Renderjob Materialparameter überschreiben kann. So banale Sachen wie das Überblenden von Detailstufen mit Alphatransparenz oder Dithering hängt davon ab, genauso wie spielrelevante Aktionen später, z.B. das Hervorheben von einem Objekt im Spielerfokus. Sind alles Kleinigkeiten, die ich aber bisher vor mir hergeschoben habe, bis ich sie wirklich mal brauche.
Shaderseitig zieht die Engine nach Generierung des jeweiligen Shaders die ganzen Konstanten- und Texturregister aus den Reflection-Strukturen von DX. Es wird also einmalig ermittelt, auf welchen Indizes die Texturen, Trafomatrizen, Lichtparameter, Materialparameter und so weiter liegen. Das Zuweisen der Parameter ist also shader-agnostisch per "if( zielRegister != SIZE_MAX ) SetFloat4(...);" möglich. Die aktuellen Daten für die Trafomatrizen ergeben sich ja live pro Renderjob, die Lichtparameter sind pro Frame statisch und werden nach Bedarf zusammengestellt, die Materialparameter werden über eine Namenszuordnung an Shaderkonstanten zugewiesen, Nebelparameter und sowas sind ein banales if wie oben beschrieben.
Es funktioniert gut! Schwächen sind aber auch bekannt. Zum Einen die Zuordnung der Parameter zu Konstantenregistern anhand ihres Namens - das ist ein String-Lookup in eine kleine Map, das könnte man durch eine simple Enumerierung aller verwendeten Shaderparameter auch als lineares Array gestalten. Etwas mehr Speicher pro Materialparameter-Satz, etwas weniger Rechenzeit in jedem Renderjob. Hab ich bisher nicht gemacht, weil wir eh schon >50% nur in DirectX-Calls verbringen. Und zum Anderen muss ich mir irgendwann noch eine Methode überlegen, wie man pro Renderjob Materialparameter überschreiben kann. So banale Sachen wie das Überblenden von Detailstufen mit Alphatransparenz oder Dithering hängt davon ab, genauso wie spielrelevante Aktionen später, z.B. das Hervorheben von einem Objekt im Spielerfokus. Sind alles Kleinigkeiten, die ich aber bisher vor mir hergeschoben habe, bis ich sie wirklich mal brauche.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Re: [GL] Anzahl der glProgram Register/Uniforms
Für die Lichtberechnung mache ich als Post Processing ala Deferred Rendering. Den Teil übernimmt der Renderer schon von sich aus. Es ist ja nicht so, dass jedes Material auch einen eigenen Shader hat, sondern dass mehrere Materialien auch den selben Shader nutzen können, eigentlich sogar sollen. Im prinziep möchte ich einfach nur das DX Effect Framework mit OpenGL umsetzen, nur das die Konstanten bzw Uniforms nicht im Effect Objekt selbst, sondern in einem seperaten Material Objekt gespeichert werden, und schließlich in einem Rutsch in die Register geschoben werden.
Die Frage ist halt nur wie ich die Daten am besten speichere, denn in OpenGL hat man ja auch die möglichkeit Ints und Bools in die Register zu schieben. Bei den Ints kann man ja noch drüber hinweg sehen, aber wenn man ein bool als float speichert, ist das schon eine Menge Speicherverschwendung.
Die Frage ist halt nur wie ich die Daten am besten speichere, denn in OpenGL hat man ja auch die möglichkeit Ints und Bools in die Register zu schieben. Bei den Ints kann man ja noch drüber hinweg sehen, aber wenn man ein bool als float speichert, ist das schon eine Menge Speicherverschwendung.
Re: [GL] Anzahl der glProgram Register/Uniforms
Du kannst die boolschen Variablen ja immer noch packen, und via floatBitsToUint() auspacken.
Aber erzaehl' doch mal, um wieviel Speicher es sich hierbei handelt...oder bist Du knapp an uniforms?
Aber erzaehl' doch mal, um wieviel Speicher es sich hierbei handelt...oder bist Du knapp an uniforms?
Re: [GL] Anzahl der glProgram Register/Uniforms
Es wird sich nicht um viel Speicher handeln und vorallem werde ich zum großteil nur Uniform Buffer nutzen, aber möchte halt auch kompatiblität zu älteren Treibern gewährleisten, welche keine Uniform-Buffer unterstützen. Pro Model, also eigenes Model mit anderen Texturen etc. gibt es ein Material, demnach werden wohl um die 200 Materialien existieren und vieleicht 20 Shader. In der Summe ist das schon eine Menge speicher.
Ich setze das grad mit void* um. Das sieht dann etwa so aus:
Wirklich sinnvoll ist das nun wirklich nicht, da ich jetzt eine Menge von Zeigern habe.
Aber die Constanten sind doch alle im Shader hintereinander. Kann man nicht irgentwie nen ganzen Block rüber schieben? Ich meine, dass ich meine Konstanten alle mit memcpy und was weiß ich in einem ganzen packet speichere, und wenn gerendert wird, einfach diesen ganzen Datenblock eben schnell rüber kopiere?
EDIT:
Um nochmal klar zu machen was ich genau vorhabe: Ich habe einen Shader, von diesem Shader kann ich mir mit IAvgLinkedShader::CreateMaterial() ein Material erstellen lassen. Dieses Material hat dann ein Array, Datenblock oder was auch immer in dem man die Konstanten für den Shader Quatschen kann, sowie Texturen etc... . Wenn man etwas rendern will, muss man ersteinmal ein RenderContext erstellen (IAvgRenderContext), dieser stellt einen deferred Render Target dar, also Colorbuffer, bormalbuffer, Depthbuffer und einen weiteren individuellen Buffer dar. Dem Context kann man nun Mehses übergeben (AvgPolygonMesh), die der Context sammelt. Außerdem kann man dem Context noch Lichter übergeben (AvgLight) die er ebenfalls sammelt. Wenn der Context nun alle Meshes und Lichter hat, kann man IAvgRenderContext::Render() aufrufen. Dieser sortiert nun ersteinmal alle Meshes nach Shadern, Texturen etc ... . Anschließend werden alle Meshes in den GBuffer gerendert. Danach werden die Lichter deferred gerendert, und man bekommt eine Textur zutück, in der nun das Fertige Bild ist. Es hat mich schon ne Zeit gekostet dieses Konzept aufzustellen und bin damit bisher eigentlich sehr zufrieden, da man immernoch die flexiblität hat, als würde man direkt die OpenGL Befehle nutzen, aber es trotzdem automatisch sortieren und unabhängig vom SceneManager und sogar Multithreaded (Forraussetzung ist OpenGL 3.1) rendern kann. Jetzt wo ich das Konzept fertig habe, will ich anfangen es zu code, und arbeite mich grade vor. Anfangen tu ich mit dem Material System, und da stellt sich mir auch gleich die Frage in den Weg, wie ich die Daten am besten Speichere. Für Texturen ist das ja nun kein Problem, da nutze ich einfach ein Textur Array, aber bei den Constanten sieht das wieder ganz anders aus. Die alternative wäre nun, komplett auf Konstanten zu verzichten und die unterstützung von Uniform Buffern (in DX bekannt unter dem Namen ConstantBuffer) zu fordern. Ich habe vor einmal ein Strategiespiel darauf auf zu bauen, und wenn man nun damit rechnet das es frühestens in 2 Jahren fertig ist, kann man wohl davon ausgehen, dass Uniform Buffer unterstützt werden.
Ein weiterer vorteil von diesem Design ist, dass ich das ganze sehr leicht um einen Voxel Renderer erweitern kann, da ich vor habe das Terrain des Strategiespiels eventuell ala Voxel darstellen möchte (Damit meine ich richtige Voxel, nicht Marching Cubes oder ähnliches).
Ich setze das grad mit void* um. Das sieht dann etwa so aus:
Code: Alles auswählen
struct AvgShaderConstant
{
/// Typ der Constante.
AVG_SHADER_CONSTANT_TYPE Type;
/// Die Daten der Constante, z.B. float, Vector4 oder Matrix4 ...
void* pData;
/// Bei einem Werd > 2 werden die Daten als Array beghandelt.
unsigned int ElementCount;
};
Aber die Constanten sind doch alle im Shader hintereinander. Kann man nicht irgentwie nen ganzen Block rüber schieben? Ich meine, dass ich meine Konstanten alle mit memcpy und was weiß ich in einem ganzen packet speichere, und wenn gerendert wird, einfach diesen ganzen Datenblock eben schnell rüber kopiere?
EDIT:
Um nochmal klar zu machen was ich genau vorhabe: Ich habe einen Shader, von diesem Shader kann ich mir mit IAvgLinkedShader::CreateMaterial() ein Material erstellen lassen. Dieses Material hat dann ein Array, Datenblock oder was auch immer in dem man die Konstanten für den Shader Quatschen kann, sowie Texturen etc... . Wenn man etwas rendern will, muss man ersteinmal ein RenderContext erstellen (IAvgRenderContext), dieser stellt einen deferred Render Target dar, also Colorbuffer, bormalbuffer, Depthbuffer und einen weiteren individuellen Buffer dar. Dem Context kann man nun Mehses übergeben (AvgPolygonMesh), die der Context sammelt. Außerdem kann man dem Context noch Lichter übergeben (AvgLight) die er ebenfalls sammelt. Wenn der Context nun alle Meshes und Lichter hat, kann man IAvgRenderContext::Render() aufrufen. Dieser sortiert nun ersteinmal alle Meshes nach Shadern, Texturen etc ... . Anschließend werden alle Meshes in den GBuffer gerendert. Danach werden die Lichter deferred gerendert, und man bekommt eine Textur zutück, in der nun das Fertige Bild ist. Es hat mich schon ne Zeit gekostet dieses Konzept aufzustellen und bin damit bisher eigentlich sehr zufrieden, da man immernoch die flexiblität hat, als würde man direkt die OpenGL Befehle nutzen, aber es trotzdem automatisch sortieren und unabhängig vom SceneManager und sogar Multithreaded (Forraussetzung ist OpenGL 3.1) rendern kann. Jetzt wo ich das Konzept fertig habe, will ich anfangen es zu code, und arbeite mich grade vor. Anfangen tu ich mit dem Material System, und da stellt sich mir auch gleich die Frage in den Weg, wie ich die Daten am besten Speichere. Für Texturen ist das ja nun kein Problem, da nutze ich einfach ein Textur Array, aber bei den Constanten sieht das wieder ganz anders aus. Die alternative wäre nun, komplett auf Konstanten zu verzichten und die unterstützung von Uniform Buffern (in DX bekannt unter dem Namen ConstantBuffer) zu fordern. Ich habe vor einmal ein Strategiespiel darauf auf zu bauen, und wenn man nun damit rechnet das es frühestens in 2 Jahren fertig ist, kann man wohl davon ausgehen, dass Uniform Buffer unterstützt werden.
Ein weiterer vorteil von diesem Design ist, dass ich das ganze sehr leicht um einen Voxel Renderer erweitern kann, da ich vor habe das Terrain des Strategiespiels eventuell ala Voxel darstellen möchte (Damit meine ich richtige Voxel, nicht Marching Cubes oder ähnliches).