[D3D 11] Wie viele Constant Buffers?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
[D3D 11] Wie viele Constant Buffers?
Man soll Constant Buffers nach Änderungshäufigkeit sortieren. Okay.
Ich habe hier eine Szene mit 100.000 Objekten und 10.000 verschiedenen Materialien (Ambient, Diffuse, …). Die Transformationsmatrizen ändern sich fast nie. Die Materialien ändern sich noch viel seltener. Wir können also erstmal übereinkommen, dass Transformationsmatrizen und Materialdaten in getrennte Constant Buffers gehen.
Soll ich jetzt 100.000 Constant Buffers mit Transformationsmatrizen anlegen? Oder nur einen, und den in jedem Frame 100.000 Mal überschreiben? Soll ich 10.000 Constant Buffers mit Materialdaten anlegen? Oder nur einen, den ich in jedem Frame 10.000 Mal überschreibe?
Dauerndes Überschreiben geht irgendwie dem Sinn von Buffern zuwider. (Es soll ja auf der GPU gepuffert bleiben, so lange es sich nicht ändert. Der große Unterschied zu D3D 9, wo man jeden Frame alle Konstanten neu zur GPU geschickt hat.) Andererseits erscheint mir das exzessive Erzeugen von Constant Buffers bedenklich. Dann wiederum finde ich aber keinen Hinweis in den Resource Limits, dass das irgendwie nach oben begrenzt wäre.
Ich habe jetzt testweise 10.000 Constant Buffers für die Materialien erzeugt, und das läuft richtig gut. (Zig Megabyte weniger Working Set pro Frame!) Mache ich alles richtig, oder wirft gerade jemand die Hände über dem Kopf zusammen?
Ich habe hier eine Szene mit 100.000 Objekten und 10.000 verschiedenen Materialien (Ambient, Diffuse, …). Die Transformationsmatrizen ändern sich fast nie. Die Materialien ändern sich noch viel seltener. Wir können also erstmal übereinkommen, dass Transformationsmatrizen und Materialdaten in getrennte Constant Buffers gehen.
Soll ich jetzt 100.000 Constant Buffers mit Transformationsmatrizen anlegen? Oder nur einen, und den in jedem Frame 100.000 Mal überschreiben? Soll ich 10.000 Constant Buffers mit Materialdaten anlegen? Oder nur einen, den ich in jedem Frame 10.000 Mal überschreibe?
Dauerndes Überschreiben geht irgendwie dem Sinn von Buffern zuwider. (Es soll ja auf der GPU gepuffert bleiben, so lange es sich nicht ändert. Der große Unterschied zu D3D 9, wo man jeden Frame alle Konstanten neu zur GPU geschickt hat.) Andererseits erscheint mir das exzessive Erzeugen von Constant Buffers bedenklich. Dann wiederum finde ich aber keinen Hinweis in den Resource Limits, dass das irgendwie nach oben begrenzt wäre.
Ich habe jetzt testweise 10.000 Constant Buffers für die Materialien erzeugt, und das läuft richtig gut. (Zig Megabyte weniger Working Set pro Frame!) Mache ich alles richtig, oder wirft gerade jemand die Hände über dem Kopf zusammen?
- kimmi
- Moderator
- Beiträge: 1405
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
In einem Talk eines Unreal-Engine-Engineers habe ich zu dem Thema mal die folgende Antwort bekommen: "Strongly depends on you current situation and environment."
Wenn du trotz der Switches zwischen den Constant-Buffern keinen signifikanten Performance-Bottlenecks findest, hat es für dein Szenario hin. Ansonsten könntest du die jeweiligen Buffer vermutlich noch etwas in Cluster unterteilen, um zusammen gehörende Gruppen besser andressieren zu können.
Aber wenn es läuft: warum nicht :).
Gruß Kimmi
Wenn du trotz der Switches zwischen den Constant-Buffern keinen signifikanten Performance-Bottlenecks findest, hat es für dein Szenario hin. Ansonsten könntest du die jeweiligen Buffer vermutlich noch etwas in Cluster unterteilen, um zusammen gehörende Gruppen besser andressieren zu können.
Aber wenn es läuft: warum nicht :).
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Hmmm; okay. Nvidia sagt (https://developer.nvidia.com/content/co ... ant-pain-0 ), dass man mit XXSetConstantBuffers() + DrawIndexed() doppelt so viel gerendert kriegt wie mit Map(DISCARD) + Unmap() + DrawIndexed() + cache miss (Kopieraufwand noch nicht einberechnet), und bisher deckt sich das mit meiner Leistung.
- Schrompf
- Moderator
- Beiträge: 5074
- Registriert: 25.02.2009, 23:44
- Benutzertext: Lernt nur selten dazu
- Echter Name: Thomas
- Wohnort: Dresden
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Ich hatte Constant Buffers so verstanden, dass es nur kleine kontinuierliche Blöcke GPU-Speicher sind, und in einem Descriptor Block (oder wie der hieß) liegt ein Zeiger darauf. Und da in Vulkan und DX12 ja jeder DrawCall nur noch ein Gesamt-Satz an solchen Zeigern ist, *müsste* es völlig ok sein, hundertausende winzige Constant Buffers rumliegen zu haben.
Der alte C++-Bastler in mir will einen Custom Allocator dafür schreiben, aber ich glaube, wir haben immer noch keinen Direktzugriff auf die Speicher und Zeiger der GPU.
Der alte C++-Bastler in mir will einen Custom Allocator dafür schreiben, aber ich glaube, wir haben immer noch keinen Direktzugriff auf die Speicher und Zeiger der GPU.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
- kimmi
- Moderator
- Beiträge: 1405
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Mit Vulkan sollte das gehen :). DX11? Ich weiß ja nicht ...
Re: [D3D 11] Wie viele Constant Buffers?
Hi,
du musst bedenken was im Hintergrund passiert wenn du einem Puffer überschreibst: Der Speicher kann in diesem Fall ja nicht wirklich überschrieben werden, weil die GPU den Inhalt (i.A.) noch gar nicht verarbeiten konnte. Würde der Speicher überschrieben werden dann hätte man ein Szenario, wo unterschiedliche Objekte mit den selben Konstanten gerendert werden würden. Aus diesem Grund wird der Puffer "umbenannt", der Treiber alloziert also einen neuen Speicherbereich und packt diesen hinter den Handle des Puffers. Das Update geht dann also in den neuen Speicherbereich, so das vorher abgesetzte Drawcalls/Dispatches ihre eigene Version des CBuffers behalten. Im Endeffekt ist der Speicherfootprint also ähnlich, hat aber einen gewissen Managementoverhead auf Treiberseite. Dort wird nämlich häufig versucht den Prozess zu optimieren, z.B. indem der Treiber bereits im Vorfeld einen größeren Speicherblock reserviert und dann solang einen Offset verschiebt bis der Block aufgebraucht ist. Im Idealfall sind beim nächsten Discard die ersten Bereiche bereits von der GPU konsumiert, so das eine Anfrage beim Speichermgmnt effektiv verhindert wird (unwahrscheinlich bei deinem Szenario). Das Pattern kennt man z.B. auch bei Streamingbuffern, wenn beispielsweise Instanzdaten gesammelt, oder Partikeldaten von der CPU geschrieben werden. In diesem Fall würde man einen Puffer reservieren der WorstCaseGröße * MaximaleLatenz groß ist und jeweils mit NO_OVERWRITE mappen + offsetten bis ein Überlauf entsteht, ansonsten mit DISCARD um den Puffer zu resetten. Im Idealfall würde der Puffer also nie wirklich 'renamed' werden.
Ein weiteres potentielles Problem sind Cacheflushes/Pipelinesyncs die ggf notwendig sind, bevor die Daten von der GPU gelesen werden können.
Im Falle von sehr häufigen Updates kann der Overhead groß werden, so das du Puffer nicht sharen solltest (manche APIs bieten die Möglichkeit solche Konstanten direkt in den Commandbuffer zu schreiben, z.B. über Rootkonstanten in DX12). Für Puffer mit weniger hohen Update-Frequenz ist der Overhead häufig vertretbar. In vielen Fällen sind die Konstanten aber tatsächlich unveränderlich (z.B. häufig bei Materialparametern o.Ä.). In diesen Fällen kann es Sinn machen die Daten offline in einzelne Puffer zu backen und einfach zu binden. Das gibt den Treiber auch die Möglichkeit die Daten in GPU 'nahmen' Speicher (z.B. VRAM) zu lagern, weil kein CPU Zugriff notwendig ist.
Ein Allokationsschema für CBuffer mit DX11 ist schwierig, weil 'partial' Updates nicht möglich sind (DX11.1 bietet das an). Ein CBuffer-Pool ist allerdings eine Möglichkeit die ggf am nächsten an die Idee eines Sperichermanagement ran kommt. DX12/Vulkan sind da etwas flexibler, Zugriff auf Speicheradressen hat man zwar auch dort nicht, ist aber für einen Allokator auch nicht notwendig. Ich hatte in einem Spiel mal gesehen, das statt CBuffern sämtliche Konstanten über TBuffer an die GPU gefüttert wurden um eine art Speicherverwaltung zu ermöglichen. Ist sicher auch eine Möglichkeit. :-)
du musst bedenken was im Hintergrund passiert wenn du einem Puffer überschreibst: Der Speicher kann in diesem Fall ja nicht wirklich überschrieben werden, weil die GPU den Inhalt (i.A.) noch gar nicht verarbeiten konnte. Würde der Speicher überschrieben werden dann hätte man ein Szenario, wo unterschiedliche Objekte mit den selben Konstanten gerendert werden würden. Aus diesem Grund wird der Puffer "umbenannt", der Treiber alloziert also einen neuen Speicherbereich und packt diesen hinter den Handle des Puffers. Das Update geht dann also in den neuen Speicherbereich, so das vorher abgesetzte Drawcalls/Dispatches ihre eigene Version des CBuffers behalten. Im Endeffekt ist der Speicherfootprint also ähnlich, hat aber einen gewissen Managementoverhead auf Treiberseite. Dort wird nämlich häufig versucht den Prozess zu optimieren, z.B. indem der Treiber bereits im Vorfeld einen größeren Speicherblock reserviert und dann solang einen Offset verschiebt bis der Block aufgebraucht ist. Im Idealfall sind beim nächsten Discard die ersten Bereiche bereits von der GPU konsumiert, so das eine Anfrage beim Speichermgmnt effektiv verhindert wird (unwahrscheinlich bei deinem Szenario). Das Pattern kennt man z.B. auch bei Streamingbuffern, wenn beispielsweise Instanzdaten gesammelt, oder Partikeldaten von der CPU geschrieben werden. In diesem Fall würde man einen Puffer reservieren der WorstCaseGröße * MaximaleLatenz groß ist und jeweils mit NO_OVERWRITE mappen + offsetten bis ein Überlauf entsteht, ansonsten mit DISCARD um den Puffer zu resetten. Im Idealfall würde der Puffer also nie wirklich 'renamed' werden.
Ein weiteres potentielles Problem sind Cacheflushes/Pipelinesyncs die ggf notwendig sind, bevor die Daten von der GPU gelesen werden können.
Im Falle von sehr häufigen Updates kann der Overhead groß werden, so das du Puffer nicht sharen solltest (manche APIs bieten die Möglichkeit solche Konstanten direkt in den Commandbuffer zu schreiben, z.B. über Rootkonstanten in DX12). Für Puffer mit weniger hohen Update-Frequenz ist der Overhead häufig vertretbar. In vielen Fällen sind die Konstanten aber tatsächlich unveränderlich (z.B. häufig bei Materialparametern o.Ä.). In diesen Fällen kann es Sinn machen die Daten offline in einzelne Puffer zu backen und einfach zu binden. Das gibt den Treiber auch die Möglichkeit die Daten in GPU 'nahmen' Speicher (z.B. VRAM) zu lagern, weil kein CPU Zugriff notwendig ist.
Ein Allokationsschema für CBuffer mit DX11 ist schwierig, weil 'partial' Updates nicht möglich sind (DX11.1 bietet das an). Ein CBuffer-Pool ist allerdings eine Möglichkeit die ggf am nächsten an die Idee eines Sperichermanagement ran kommt. DX12/Vulkan sind da etwas flexibler, Zugriff auf Speicheradressen hat man zwar auch dort nicht, ist aber für einen Allokator auch nicht notwendig. Ich hatte in einem Spiel mal gesehen, das statt CBuffern sämtliche Konstanten über TBuffer an die GPU gefüttert wurden um eine art Speicherverwaltung zu ermöglichen. Ist sicher auch eine Möglichkeit. :-)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Danke!
Das kann ich klären: Der oben verlinkte Nvidia-Artikel sagt, dass auch unter D3D 11 alle Constant Buffer Updates direkt in den Command Buffer kopiert werden, und der ist auf 128 MiB limitiert (danach Flush & Stall). Eine API-Garantie ist das aber natürlich nicht.(manche APIs bieten die Möglichkeit solche Konstanten direkt in den Commandbuffer zu schreiben, z.B. über Rootkonstanten in DX12)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Entschuldigt die späte Rückmeldung, aber ich bin erst jetzt zum Testen gekommen. (Mit Materialien haben die vielen Buffer ja gut funktioniert; nun wollte ich die Transformationen ebenfalls umstellen.)
Vorher: Ein globaler Constant Buffer, der pro Objekt gemappt wird und die Transformationsmatrizen transportiert.
Nachher: Für jedes Objekt ein eigener Constant Buffer im Layout
float4x4 objectToWorld;
float3x3 normalsToWorld;
der zur Laufzeit nur noch via VSSetConstantBuffers() gebunden wird.
Das Ergebnis mit 30.000 Objekten war katastrophal:
Ich möchte weiterhin vermeiden, alle Konstanten in jedem Frame neu auf die GPU zu laden. Soll ich einen Structured Buffer ausprobieren, auf den jedes Objekt via Index zugreift? Ich wollte das eigentlich nicht machen, weil das uniforme Lesen aus Constant Buffers viel schneller sein soll. Ich würde gewissermaßen die Upload-Zeit sparen, und dafür in den Shadern wesentlich höhere Latenz haben.
Alternativ würde ich direkt zu Deferred Contexts gehen um zu prüfen, ob eine aufgezeichnete Sequenz von Map() und Draw() schneller ist.
Nachtrag: Heiliger Bimbam, dieses Diagramm ist ja nicht gerade vielversprechend:
Achtfach parallel in Deferred Contexts schreiben ist bloß 20 % schneller als alles in einem Thread im Immediate Context zu machen?!
Da geht es um den Fall, dass sich alle Objekte in jedem Frame bewegen. Bei mir geht es eher darum, dass alles statisch ist und ich deshalb einfach einmal alle Draw Calls aufzeichnen könnte, statt sie in jedem Frame zu wiederholen. Mein Gedanke war: „Hey, dann spult der Treiber die aufgezeichnete Liste direkt auf der GPU ab! Superschnell!“ Das Diagramm suggeriert aber eher, dass die Befehle auf API-Ebene aufgezeichnet und abgespult werden, statt auf Treiber-Ebene. Das einzige, was ich gegenüber meiner jetzigen Render Loop sparen würde, wären die paar ifs, mit denen ich auf redundante Texturen prüfe. Da vergeht mir direkt die Lust.
Vorher: Ein globaler Constant Buffer, der pro Objekt gemappt wird und die Transformationsmatrizen transportiert.
Nachher: Für jedes Objekt ein eigener Constant Buffer im Layout
float4x4 objectToWorld;
float3x3 normalsToWorld;
der zur Laufzeit nur noch via VSSetConstantBuffers() gebunden wird.
Das Ergebnis mit 30.000 Objekten war katastrophal:
- zehn Sekunden zusätzliche Ladezeit nur zum Initialisieren der Constant Buffers (Rest lädt in unter zwei Sekunden!)
- 1500 MiB zusätzlicher Speicherverbrauch – bei nur 112 * 30.000 = 3,4 MiB Nutzdaten! Das entspricht 50 KiB Overhead pro Constant Buffer.
- Framerate ungefähr geviertelt.
Ich möchte weiterhin vermeiden, alle Konstanten in jedem Frame neu auf die GPU zu laden. Soll ich einen Structured Buffer ausprobieren, auf den jedes Objekt via Index zugreift? Ich wollte das eigentlich nicht machen, weil das uniforme Lesen aus Constant Buffers viel schneller sein soll. Ich würde gewissermaßen die Upload-Zeit sparen, und dafür in den Shadern wesentlich höhere Latenz haben.
Alternativ würde ich direkt zu Deferred Contexts gehen um zu prüfen, ob eine aufgezeichnete Sequenz von Map() und Draw() schneller ist.
Nachtrag: Heiliger Bimbam, dieses Diagramm ist ja nicht gerade vielversprechend:
Achtfach parallel in Deferred Contexts schreiben ist bloß 20 % schneller als alles in einem Thread im Immediate Context zu machen?!
Da geht es um den Fall, dass sich alle Objekte in jedem Frame bewegen. Bei mir geht es eher darum, dass alles statisch ist und ich deshalb einfach einmal alle Draw Calls aufzeichnen könnte, statt sie in jedem Frame zu wiederholen. Mein Gedanke war: „Hey, dann spult der Treiber die aufgezeichnete Liste direkt auf der GPU ab! Superschnell!“ Das Diagramm suggeriert aber eher, dass die Befehle auf API-Ebene aufgezeichnet und abgespult werden, statt auf Treiber-Ebene. Das einzige, was ich gegenüber meiner jetzigen Render Loop sparen würde, wären die paar ifs, mit denen ich auf redundante Texturen prüfe. Da vergeht mir direkt die Lust.
Re: [D3D 11] Wie viele Constant Buffers?
Hab beim experimentieren festgestellt, dass StructuredBuffers und VTF ok sind. Vermutlich gar nicht mal so schlecht für World-Transformationen. Bei constant buffers ist die Idee, dass der Shader auf ALLE Daten möglichst schnellen zugriff hat (weiss grad nicht mehr den genauen GPU-Term, aber sowas wie cascade, d.h. alle Kernels bekommen die Daten zugeschoben). Für Instanz-Daten "bleibt" der VS ja ne Zeitlang bei derselben World-Transformation, d.h. die kommen dann aus dem Texturecache.
Siehe unten Screenshots einer Animation von ca. 4000 Objekten (Ich hab grad die Version nicht, wo ich gesamthaft noch viel mehr Objekte hatte, allerdings nich alle animierte). Animation ist ohne GPU Hexerei, auf CPU die 4x4 Matrizen berechnen. Danach ein Upload zu einem Stage.
Anschliessend kann man den dann auf den eigentlichen Buffer mittels CopySubResourceRegion kopieren. Evtl. punktuell nötig, also mehrere Aufrufe. Ich ging allerdings einen noch indirekteren Weg: Scattering der Daten per PS (oder per CS). Damit kopiert man ergo nur Zeugs rum, das wirklich geändert hat.
Hier gab's ein kleines Problem bez. API (weil das drei instanced draw calls mit unterschiedlichen Meshes sind): SV_InstanceID fängt immer bei 0 an, auch wenn man offsets benutzt. Dazu legt man halt künstlich einen Instance-Stream mit integers an (0,1,2,3,4,5). Auch fürs Scattering hat man dann einen dynamischen integer buffer (zum festlegen wo die einzelnen updates hingehen sollen, scattering eben).
Falls Du nur statisches Zeugs hast, ist das natürlich alles viel einfacher. Vielleicht hinkt der Vergleich aber auch, weil ich sehr polygonarme Meshes habe, und wenig draw calls. Aber ein Versuch ists schon nur Wert, weil Du statt 30000 nur zwei bis vier Buffers brauchst. Meine App startet nach ca. 1 sekunde (C# / SlimDX, erst noch im DX debug mode, AMD Phenom, GTX960, 60 FPS).
Siehe unten Screenshots einer Animation von ca. 4000 Objekten (Ich hab grad die Version nicht, wo ich gesamthaft noch viel mehr Objekte hatte, allerdings nich alle animierte). Animation ist ohne GPU Hexerei, auf CPU die 4x4 Matrizen berechnen. Danach ein Upload zu einem Stage.
Anschliessend kann man den dann auf den eigentlichen Buffer mittels CopySubResourceRegion kopieren. Evtl. punktuell nötig, also mehrere Aufrufe. Ich ging allerdings einen noch indirekteren Weg: Scattering der Daten per PS (oder per CS). Damit kopiert man ergo nur Zeugs rum, das wirklich geändert hat.
Hier gab's ein kleines Problem bez. API (weil das drei instanced draw calls mit unterschiedlichen Meshes sind): SV_InstanceID fängt immer bei 0 an, auch wenn man offsets benutzt. Dazu legt man halt künstlich einen Instance-Stream mit integers an (0,1,2,3,4,5). Auch fürs Scattering hat man dann einen dynamischen integer buffer (zum festlegen wo die einzelnen updates hingehen sollen, scattering eben).
Falls Du nur statisches Zeugs hast, ist das natürlich alles viel einfacher. Vielleicht hinkt der Vergleich aber auch, weil ich sehr polygonarme Meshes habe, und wenig draw calls. Aber ein Versuch ists schon nur Wert, weil Du statt 30000 nur zwei bis vier Buffers brauchst. Meine App startet nach ca. 1 sekunde (C# / SlimDX, erst noch im DX debug mode, AMD Phenom, GTX960, 60 FPS).
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Erste Sahne; danke für die Ausführung! Dann weiß ich jetzt, was zu tun ist. Der Umbau ist nur leider etwas zu groß für heute abend :(
Naja; Constant Buffers haben halt geringe Latenz, so lange alle Threads genau gleichzeitig auf die selben Daten zugreifen. Cascaded ist AFAIK, wenn alle Threads auf *unterschiedliche* Daten zugreifen, und da sind Structured Buffers perfekt. Nvidia sagt sogar explizit, dass man Bandbreite und Latenz verschwendet, wenn alle Threads von der selben Stelle eines Structured Buffers lesen. Der Knackpunkt ist wohl, dass Latenz != Stall, und dass die Latenz nichts ausmacht, so lange die GPU in der Zwischenzeit andere Dinge zum Ausführen findet.unbird hat geschrieben:Bei constant buffers ist die Idee, dass der Shader auf ALLE Daten möglichst schnellen zugriff hat (weiss grad nicht mehr den genauen GPU-Term, aber sowas wie cascade, d.h. alle Kernels bekommen die Daten zugeschoben). Für Instanz-Daten "bleibt" der VS ja ne Zeitlang bei derselben World-Transformation, d.h. die kommen dann aus dem Texturecache.
Re: [D3D 11] Wie viele Constant Buffers?
Keine Ursache. Und Danke für die Klarstellung ich hatte das tatsächlich falsch rum in Erinnerung.
Den Ausdruck den ich suchte heisst übrigens "constant waterfalling" (Structured Buffers vs. Constant Buffers). Man muss die Ausdrücke kennen um sinnvoll gurgeln zu können ;)
Edit : Das mit dem Speicherverbrauch ist wirklich komisch. Klingt, als würde pro CB fast das Resourcenlimit von 64k reserviert ( 1500 MB / 30000 = ca. 50k ). Weiss jemand etwas darüber ? Ist das Treiber/Hardwarespezifisch ?
Den Ausdruck den ich suchte heisst übrigens "constant waterfalling" (Structured Buffers vs. Constant Buffers). Man muss die Ausdrücke kennen um sinnvoll gurgeln zu können ;)
Edit : Das mit dem Speicherverbrauch ist wirklich komisch. Klingt, als würde pro CB fast das Resourcenlimit von 64k reserviert ( 1500 MB / 30000 = ca. 50k ). Weiss jemand etwas darüber ? Ist das Treiber/Hardwarespezifisch ?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [D3D 11] Wie viele Constant Buffers?
Sooo; ich habe mich mal durchgebissen …
Bei mir musste ich es zum Glück nicht so weit treiben: Die zwei Matrizen, die sich pro Modell ändern, kann ich auch via Vertex Buffer übergeben statt einen Structured Buffer anlegen zu müssen. Also:
Nun der nächste Schritt – Objektpositionen mit der CPU verändern:
Als nächstes fasse ich die Object-to-World und World-to-Screen-Matrizen im Compute Shader zusammen statt im Vertex Shader der Objekte.
… damit sind auch schon viele andere auf die Schnauze geflogen.unbird hat geschrieben:Hier gab's ein kleines Problem bez. API (weil das drei instanced draw calls mit unterschiedlichen Meshes sind): SV_InstanceID fängt immer bei 0 an, auch wenn man offsets benutzt.
… ganz genau so macht das Ogre seit 2.1 für die gesamte Szene und das ist tatsächlich der optimale Weg.Dazu legt man halt künstlich einen Instance-Stream mit integers an (0,1,2,3,4,5).
Bei mir musste ich es zum Glück nicht so weit treiben: Die zwei Matrizen, die sich pro Modell ändern, kann ich auch via Vertex Buffer übergeben statt einen Structured Buffer anlegen zu müssen. Also:
- Einen zusätzlichen Vertex Buffer für die pro-Mesh-Matrizen angelegt.
- Input Layout um zusätzliche Komponenten erweitert:
- InputSlot auf 1 gesetzt (also aus zweitem Vertex Buffer in Slot 1 lesen)
- InputSlotClass auf D3D11_INPUT_PER_INSTANCE_DATA gesetzt
- InstanceDataStepRate auf 1 gesetzt
- einige Online-Tutorials empfehlen, AlignedByteOffset auf 0 zurückzusetzen – ist nicht nötig; D3D berechnet das Alignment pro Slot, und wir haben einen neuen Slot begonnen
- Protip am Rande: Wenn man seinen Positionsdaten den Namen POSITION gibt, kann der Graphics Debugger von Visual Studio den Vertex Buffer in 3D anzeigen!
- den zusätzlichen Vertex Buffer in Slot 1 gebunden
- DrawIndexedInstanced() aufgerufen – StartInstanceLocation ist der Index der Matrix, die benutzt werden soll
Nun der nächste Schritt – Objektpositionen mit der CPU verändern:
- Den Vertex Buffer mit den Matrizen nicht D3D11_USAGE_IMMUTABLE deklarieren sondern D3D11_USAGE_DEFAULT
- Wenn ein Objekt verschoben wird, via UpdateSubresource() die neuen Matrizen in den Vertex Buffer schreiben
- ???
- PROFIT!!!
Als nächstes fasse ich die Object-to-World und World-to-Screen-Matrizen im Compute Shader zusammen statt im Vertex Shader der Objekte.