Also mache ich eine DirectX-Entwicklung, und zwar mit SharpDX unter .NET (aber DirectX / C ++ - API-Lösungen sind anwendbar). Ich bin auf der Suche nach der schnellsten Möglichkeit, Linien in einer orthogonalen Projektion (z. B. Simulation von 2D-Linien für wissenschaftliche Apps) mit DirectX zu rendern.
Ein Screenshot der Arten von Plots, die ich zu rendern versuche, folgt:
Es ist nicht ungewöhnlich, dass diese Arten von Plots Linien mit Millionen von Segmenten und variabler Dicke aufweisen, mit oder ohne zeilenweisem Antialiasing (oder Vollbild-AA ein / aus). Ich muss die Eckpunkte für die Linien sehr häufig aktualisieren (z. B. 20 Mal pro Sekunde) und so viel wie möglich auf die GPU auslagern.
Bisher habe ich versucht:
- Software-Rendering, z. B. GDI +, ist eigentlich keine schlechte Leistung, belastet aber offensichtlich die CPU
- Direct2D-API - langsamer als GDI, insbesondere mit aktiviertem Antialiasing
- Direct3D10 emuliert mit dieser Methode AA mit Vertexfarben und Tessellation auf der CPU-Seite. Auch langsam (ich habe ein Profil erstellt und 80% der Zeit wird für die Berechnung der Scheitelpunktpositionen aufgewendet)
Bei der dritten Methode verwende ich Vertex-Puffer, um einen Dreiecksstreifen an die GPU zu senden und alle 200 ms mit neuen Vertices zu aktualisieren. Ich erhalte eine Bildwiederholfrequenz von ca. 5 Bildern pro Sekunde für 100.000 Liniensegmente. Ich brauche im Idealfall Millionen!
Jetzt denke ich, dass der schnellste Weg wäre, die Tessellation auf der GPU durchzuführen, z. B. in einem Geometry Shader. Ich könnte die Eckpunkte als Linienliste senden oder in eine Textur packen und in einen Geometrie-Shader entpacken, um die Quads zu erstellen. Oder senden Sie einfach Rohpunkte an einen Pixel-Shader und implementieren Sie das Zeichnen von Bresenham-Linien vollständig in einem Pixel-Shader. Mein HLSL ist rostig, Shader Model 2 aus dem Jahr 2006, also weiß ich nicht, was moderne GPUs für verrückte Sachen können.
Die Frage ist also: - Hat das schon jemand gemacht und haben Sie Vorschläge, die Sie ausprobieren sollten? - Haben Sie Vorschläge zur Verbesserung der Leistung durch eine schnelle Aktualisierung der Geometrie (z. B. alle 20 ms eine neue Scheitelpunktliste)?
UPDATE 21. Jan
Ich habe seitdem Methode (3) oben mit Geometry Shader mit LineStrip und Dynamic Vertex Buffers implementiert. Jetzt erhalte ich 100 FPS bei 100.000 Punkten und 10 FPS bei 1.000.000 Punkten. Dies ist eine enorme Verbesserung, aber jetzt bin ich auf Füllrate und Rechenleistung beschränkt, sodass ich über andere Techniken / Ideen nachdenke.
- Was ist mit der Hardware-Instanzierung einer Liniensegmentgeometrie?
- Was ist mit Sprite Batch?
- Was ist mit anderen (Pixel Shader) orientierten Methoden?
- Kann ich die GPU oder die CPU effizient ausmerzen?
Ihre Kommentare und Vorschläge werden sehr geschätzt!
Antworten:
Wenn du rendern willst
Y = f(X)
nur Grafiken möchten, empfehle ich die folgende Methode.Die Kurvendaten werden als Texturdaten übergeben , wodurch sie dauerhaft sind und teilweise Aktualisierungen beispielsweise durchlaufen können
glTexSubImage2D
. Wenn Sie scrollen müssen, können Sie sogar einen Ringpuffer implementieren und nur einige Werte pro Frame aktualisieren. Jede Kurve wird als Vollbild-Quad gerendert und die gesamte Arbeit wird vom Pixel-Shader erledigt.Der Inhalt der Einkomponenten-Textur könnte folgendermaßen aussehen:
Die Arbeit des Pixel-Shaders ist wie folgt:
41.3
würde es wählen40
,41
,42
und43
.X,Y
Paare in Bildschirmgröße umJe nach möglicher Zoomstufe können Sie 4 durch größere Werte ersetzen.
Ich habe einen sehr schnellen und schmutzigen GLSL-Shader geschrieben, der diese Funktion implementiert . Ich kann die HLSL-Version später hinzufügen, aber Sie sollten sie ohne großen Aufwand konvertieren können. Das Ergebnis ist unten mit unterschiedlichen Liniengrößen und Datendichten zu sehen:
Ein klarer Vorteil ist, dass die übertragene Datenmenge sehr gering ist und die Anzahl der Drawcalls nur eins beträgt.
quelle
Es gab ein GPU Gems-Kapitel zum Rendern von Antialias-Linien: Schnelle vorgefilterte Linien . Die Grundidee besteht darin, jedes Liniensegment als Quad zu rendern und bei jedem Pixel eine Gaußsche Funktion des Abstands des Pixelzentrums vom Liniensegment zu berechnen.
Dies bedeutet, dass jedes Liniensegment im Diagramm als separates Quad dargestellt wird. In D3D11 können Sie jedoch durchaus einen Geometrie-Shader und / oder eine Instanz zum Generieren der Quads verwenden, um die an die GPU zu übertragende Datenmenge auf die Datenpunkte zu reduzieren sich. Ich würde die Datenpunkte wahrscheinlich als StructuredBuffer zum Lesen durch den Vertex / Geometry-Shader einrichten und dann einen Draw-Aufruf ausführen, in dem die Anzahl der zu zeichnenden Segmente angegeben wird. Es gäbe keine tatsächlichen Vertex-Puffer. Der Vertex-Shader verwendet lediglich SV_VertexID oder SV_InstanceID, um zu bestimmen, welche Datenpunkte betrachtet werden sollen.
quelle
@ Dr.ABT - Entschuldigung, das ist eine Frage, keine Antwort. Ich habe in der Antwort von Sam Hocevar oben keine Möglichkeit gefunden, danach zu fragen.
Können Sie weitere Details darüber mitteilen, wie Sie die Linien für Ihr Diagramm schließlich implementiert haben? Ich habe das gleiche Bedürfnis nach einer WPF-App. Vielen Dank im Voraus für alle Details oder Codes, die Sie dazu mitteilen können.
quelle