Richtige Architektur zum Rendern des Verlaufs von gestreamten Daten (Punkten)

7

Überblick

Mein Programm empfängt einige Datenpunkte (0-400 pro Sekunde).

// simplified example of point's data structure
struct Point {
    QPoint p;
    QTime t;
    quint32 value;
}
  • Ich muss einige gefilterte Punkte rendern (zB das point.time > currentTime-5und point.value>5).
  • Ich muss den Datenverlauf speichern (bis zu ~ 50'000 Punkte).
  • Die Daten können auch auf der CPU verwendet werden.
  • Ich werde die Objektauswahl verwenden.
  • Punkte werden in 3d gerendert.
  • Die Kameraposition kann sich ändern, aber die Punktedaten sind konstant.

Frage

Was ist die beste Architektur, um diese Art von Problem zu implementieren? Was sind Best Practices für diese Art von Daten und welche Methoden sollten vermieden werden?

Mögliche Lösungen

  • Speichern und Filtern von Daten.
    1. Speichern Sie die Daten auf der CPU (Weltkoordinaten), filtern Sie Daten auf einer CPU und senden Sie gefilterte Daten an VBO. Berechnen Sie dabei die Bildschirmraumkoordinaten mit Shadern.
    2. Speichern Sie die Daten auf einer CPU und einer GPU (aktualisieren Sie einen VBO, wenn ein neuer Punkt eintrifft).
      Filtern Sie Punkte auf der CPU und berechnen Sie Offsets und Verwendung glMultiDrawArrays.
    3. Speichern Sie die Daten nur auf einer GPU (aktualisieren Sie VBO, wenn der Punkt eintrifft).
      Filtern Sie es mit Shadern und holen Sie bei Bedarf Daten von VBO zur CPU.

Lösung 1. ist ziemlich einfach, aber ich denke, es ist eine schlechte Praxis.
Lösung 2. hat Datenvervielfältigung, aber es ist einfach, die Daten zu filtern.
Lösung 3. hat Daten lesen GPU-> CPU.

Ich habe diese Lösungen ausprobiert, und Lösung 2 erscheint mir als die beste, aber es gibt einige Probleme damit:

  • Die Multiplikation von Daten muss vorsichtig gehandhabt werden.
  • Es ist nicht die am einfachsten zu lesende und zu ändernde Lösung.
  • Wenn sich Daten ändern, müssen viele Dinge geändert werden.
    Ich habe so etwas wie einen Ringpuffer auf VBO verwendet und Offsets gesendet, um darauf zu rendern.

Umgebung

  • OpenGL 3.3
  • Qt 5+ Bindungen
  • Linux

PS
Ich bin ziemlich neu in Computergrafik und openGL, also hat diese Art von Problem vielleicht sogar ihren Namen. Ich habe meine Implementierungen einige Male geändert, es hat viel Zeit gekostet und ich bin aufgrund seiner Komplexität nicht zufrieden damit. Alle Materialien, Anleitungen und Gedanken sind willkommen.

BPiek
quelle

Antworten:

4

Mein Rat wäre eigentlich, bei Lösung 1 zu bleiben, es sei denn und bis etwas mehr benötigt wird. Es ist am einfachsten und am einfachsten zu verstehen, und es sollte durchaus machbar sein, dass es eine gute Leistung erbringt.

Einerseits mag es verschwenderisch erscheinen, die Punkte neu zu filtern und in jedem Frame eine Reihe von Daten erneut in den VBO hochzuladen. Ich denke, deshalb sagst du, es sei eine "schlechte Praxis". Berücksichtigen Sie jedoch die Gesamtdatenmenge. Eine Größenordnungsschätzung zeigt, dass die Verarbeitung dieser Datenmenge in jedem Frame für einen modernen Computer nicht viel Arbeit bedeutet.

Die PointStruktur, die Sie zitieren, sieht aus wie ungefähr 20 Bytes (vorausgesetzt, das psind 3 Floats und tein Float oder Int). Mit 50.000 Punkten beträgt die Gesamtgröße der Daten etwa 1 MB. Selbst wenn Ihre tatsächliche Datenstruktur etwas größer ist, beträgt die Gesamtgröße der Daten nur wenige MB - nach heutigen Maßstäben eine kleine Menge. Die Zeit, die benötigt wird, um diese Datenmenge auf der CPU zu verarbeiten und an die GPU zu übertragen, liegt in der Größenordnung von ~ 0,1 ms. (Unter der Annahme einer CPU-Bandbreite von ~ 20 GB / s und einer PCIe-Übertragungsbandbreite von ~ 8 GB / s.) Dies ist also eine völlig vernünftige Datenmenge, um jeden Frame erneut zu verarbeiten, und tatsächlich könnten Sie ein Vielfaches mehr verarbeiten, ohne Ihre zu belasten System überhaupt.

Halten Sie die Dinge für den Speicherzugriff einfach und effizient:

  1. Speichern Sie die Punkte in einem großen flachen Array (verwenden Sie möglicherweise einen Ringpuffer, damit neue eingehende Punkte alte überschreiben können).
  2. Scannen Sie in jedem Frame die Punkte mit einer engen Schleife auf der CPU und kopieren Sie diejenigen, die den Filter passieren, in den VBO.
    • Für eine optimale Effizienz sollten Sie wahrscheinlich die Verwendung GL_DYNAMIC_DRAWverwenden, wenn Sie das VBO erstellen, dann glMapBufferRangejeden Frame verwenden und die gefilterten Punkte direkt in den zugeordneten Puffer kopieren.
    • Verwenden Sie GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BITfür den accessParameter zu glMapBufferRange; Auf diese Weise kann der GL-Treiber eine Doppelpufferung unter der Haube durchführen, um die Daten effizient an die GPU zu streamen. Weitere Informationen dazu finden Sie unter Pufferobjekt-Streaming .
  3. Entfernen Sie dann die Zuordnung des Puffers und geben Sie die Zeichenbefehle aus. Verwenden Sie dazu einen Vertex-Shader, um die Punkte wie gewohnt in den Bildschirmbereich umzuwandeln.
Nathan Reed
quelle