Dies ist meine erste Frage, aber ich bin eine lange Zeit lurker. Ich werde das in zwei Teile aufteilen, einen Teil, der erklärt, was ich tue und warum ich denke, dass dies der richtige Weg ist. Der zweite ist die eigentliche Frage, die ich selbst nicht lösen kann.Verbessern von DrawingContext Performance Rendering Geometrie (Polygone und Polylinien)
Was mache ich? Ich entwickle gerade ein Framework für das Rendern von 2-dimensionalen Features, die in Echtzeit angezeigt werden sollen. Sie können sich eine Anwendung wie Google Maps in Ihrem Browser vorstellen, das Framework soll jedoch alle Arten von geografischen Daten rendern (nicht nur achsgerichtete Rasterdaten wie diese Google Tiles).
Das Framework soll in das neueste Produkt unseres Unternehmens integriert werden, eine WPF-Anwendung für Desktop und Laptop.
Daher wählte ich WPF für die tatsächliche Darstellung der Geometrie nur; Sichtbarkeit und Occlusion Culling werden von mir selbst sowie Eingabe-Handling (Maus-Picking), Bewegen der Kamera, etc.
Als eine Echtzeit-Anwendung muss es mindestens 30 FPS erreichen. Das Framework funktioniert beim Rendern von Bildern angemessen: Ich kann mehrere tausend Bitmaps pro Bild ohne Probleme zeichnen, polyonale Daten erweisen sich jedoch als ein großes Problem.
Die eigentliche Frage ich meine ganze Menge Linienzug und Polygon-Daten unter Verwendung von WPF-Rendering, speziell DrawingContext und StreamGeometry verwenden. Soweit ich weiß, ist dies der richtige Weg, wenn ich Leistung brauche. Aber ich kann die Ergebnisse, die ich erwartet habe, nicht erreichen.
Dies ist, wie ich die StreamGeometry mit aktuellen Daten füllen:
using (StreamGeometryContext ctx = Geometry.Open())
{
foreach (var segment in segments)
{
var first = ToWpf(segment[0]);
ctx.BeginFigure(first, false, false);
// Skip the first point, obviously
List<Point> points = segment.Skip(1).Select(ToWpf).ToList();
ctx.PolyLineTo(points, true, false);
}
}
Geometry.Freeze();
Und das ist, wie ich meine Geometrie zeichnen:
_dc.PushTransform(_mercatorToView);
_dc.DrawGeometry(null, _pen, polyline);
_dc.Pop();
Als Test geladen ich ESRI Formen von OpenStreetMap in meinen Anwendung, um seine Leistung zu testen, aber ich bin überhaupt nicht zufrieden: Meine Testdaten besteht aus ~ 3500 Liniensegmente mit insgesamt ~ 20k Zeilen.
Mapping jedes Segment zu seiner eigenen StreamGeometry durchgeführt extrem schlecht, aber ich irgendwie erwartet, dass bereits: Rendering dauert etwa 14 Sekunden.
Ich habe dann versucht, mehr Segmente in die gleiche StreamGeometry zu packen, mit mehreren Abbildungen: 80 StreamGeometry, Rendering dauert etwa 50ms.
Allerdings kann ich keine besseren Ergebnisse als das bekommen. Wenn Sie die Anzahl der Zeilen auf etwa 100k erhöhen, wird meine Anwendung fast unbrauchbar: Das Rendern dauert mehr als 100ms. Was kann ich noch neben dem Einfrieren sowohl der Geometrie als auch des Stiftes beim Rendern von Vektordaten tun?
Ich bin an dem Punkt, an dem ich lieber DirectX selbst nutzen würde, als mich auf WPF zu verlassen, weil etwas damit schrecklich schief läuft.
bearbeiten
Zur weiteren Klärung, was ich tue: Die Anwendung visualisiert geografische Daten in Echtzeit, sehr viel wie eine Anwendung wie Google Maps im Browser: Aber es soll viel visualisieren , viel mehr Daten. Wie Sie vielleicht wissen, erlaubt Google Maps sowohl das Zoomen als auch das Schwenken, was> 25 FPS erfordert, damit es flüssig dargestellt wird. alles weniger fühlt sich nicht fließend an.
* Sorry, aber ich sollte kein Video von diesem hochladen, bevor das eigentliche Produkt veröffentlicht wird. Sie können sich jedoch etwas wie Google Maps vorstellen, jedoch mit Tonnen von Vektordaten (Polygone und Polylinien). *
Es gibt zwei Lösungen, von denen sehr oft gesagt wird:
Cache schwere Zeichnungen in einer Bitmap
Die Umsetzung scheint ein bisschen einfach, aber ich einige Probleme mit diesem Ansatz: Um Panning richtig zu implementieren, muss ich vermeiden zeichnen die schweren Sachen jeden Rahmen, und damit bin ich mit der Wahl entweder nicht aktualisieren die zwischengespeicherte Bitmap beim Schwenken der Kamera, oder Erstellen einer Bitmap, die eine noch größere Region umfasst als das Ansichtsfenster, so dass ich nur updaten muss e die zwischengespeicherte Bitmap von Zeit zu Zeit.
Die zweite "Problem" ist mit Zoomen verwandt. Es ist jedoch eher ein visuelles Artefakt als ein echtes Problem: Da die zwischengespeicherte Bitmap bei 30 fps nicht richtig aktualisiert werden kann, muss ich das auch beim Zoomen vermeiden. Ich kann sehr gut skalieren die Bitmap beim Zoomen, nur ein neues Bitmap erstellen, wenn der Zoom endet, aber die Breite der Polylinien würde nicht eine konstante Dicke haben, obwohl sie sollten.
Dieser Ansatz scheint von MapInfo verwendet zu werden, ich kann jedoch nicht sagen, dass ich ihn zu sehr mag. Es scheint jedoch am einfachsten zu implementieren.
Split Geometrie in verschiedene Zeichnung Visuals
Dieser Ansatz scheint anders mit dem Problem zu befassen. Ich bin nicht sicher, ob dieser Ansatz überhaupt funktioniert: Es hängt davon ab, ob ich richtig verstanden habe, wie WPF in diesem Bereich funktionieren soll. Anstatt ein DrawingVisual für alle Sachen zu verwenden, die gezeichnet werden müssen, sollte ich mehrere verwenden, damit nicht jeder RenderOpened() sein muss. Ich könnte einfach die Parameter ändern, zum Beispiel die Matrix im obigen Beispiel, um sowohl das Kameraschwenk als auch das Verschieben wiederzugeben. Allerdings sehe ich auch einige Probleme mit diesem Ansatz: Das Schwenken der Kamera bringt unweigerlich neue Geometrie in das Ansichtsfenster, daher müsste ich etwas Ähnliches wie in der ersten Annäherung ausführen, tatsächlich Dinge rendern, die momentan nicht sichtbar sind, aber möglicherweise werden sichtbar aufgrund der Kameraführung; Alles zu zeichnen kommt nicht in Frage, da es für eine ziemlich kleine Datenmenge lächerliche Zeiten dauern kann.
Problem in Bezug auf beide Ansätze Ein großes Problem, das keiner dieser Ansatz lösen kann, ist, dass selbst wenn die Gesamt Frame-Rate ist stabil, gelegentlich hickups, entweder wenn die im Cache gespeicherten Bitmaps zu aktualisieren (okay, das doesn ‚t gilt, wenn die zwischengespeicherte Bitmap nur aktualisiert wird, wenn die Kamera nicht mehr ausgewaschen) oder RenderOpen Aufruf des sichtbare Stück Geometrie zu ziehen, scheint unvermeidlich zu sein.
Meine Gedanken so weit
Da dies die einzigen beiden Lösungen sind immer ich für dieses Problem sehen (ich habe meinen gerechten Anteil getan von mehr als ein Jahr googeln), ich denke, die einzige Lösung, so weit ist es, Framerate-Hickups sogar auf den leistungsstärksten GPUs zu akzeptieren (die in der Lage sein sollten, Hunderte von Millionen von Primitiven pro Sekunde zu rastern), eine verzögerte Aktualisierung des Darstellungsbereichs (in dem Fall, dass Bitmaps nur aktualisiert werden, wenn das Darstellungsfenster nein ist) länger verschoben) oder WPF überhaupt nicht verwenden und direkt auf DirectX zurückgreifen.
Ich bin sehr froh für die Hilfe, aber ich kann nicht sagen, dass ich von WPF-Rendering-Leistung bisher beeindruckt bin.
Haben die Polylinien wirklich auf jeden Rahmen ändern, so dass sie neu gezeichnet werden müssen? Wenn nicht, würde es ausreichen, nur die Transformation "mercatorToView" zu aktualisieren. – Clemens
Die Polylinien können jeden Rahmen ändern, aber derzeit nicht. Andere Teile meiner Zeichnung ändern sich jedoch sehr häufig, daher zeichne ich alles neu. Ist es sinnvoll, meine Zeichnung in verschiedene DrawingContexte zu zerlegen, wobei jedes Rendering nur bei Bedarf durchgeführt wird, während ansonsten nur Parameter aktualisiert werden, z. B. die Matrizen, aber keine neuen Zeichnungsbefehle ausgegeben werden? – Simon
Sie sollten immer versuchen, so wenige Zeichnungen wie möglich neu zu zeichnen. Wenn sich nur wenige Zeichnungen mit einer viel höheren Frequenz ändern als viele andere, würden Sie Zeit damit verschwenden, viele unveränderte Zeichnungen häufig neu zu zeichnen. Und in Ihrem Codebeispiel wird nicht nur eine StreamGeometry neu gezeichnet, sondern auch die Polyliniendaten durch häufige Aufrufe Ihrer 'ToWpf'-Methode neu erstellt. Das braucht auch etwas Zeit. – Clemens