2013-02-11 3 views
5

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.

+0

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

+0

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

+0

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

Antwort

2

Um die 2D-WPF-Rendering-Leistung zu verbessern, können Sie sich die (für WPF> = 3.5) oder die BitmapCache-Klasse (für WPF> = 4) ansehen.

Diese Klassen sind für Cached Composition

Von MSDN verwendet:

Durch den Einsatz der neuen BitmapCache und BitmapCacheBrush Klassen, Sie einen komplexen Teil des visuellen Struktur als Bitmap zwischenspeichern können und verbessern erheblich die Rendering-Zeit . Die Bitmap reagiert auf Benutzereingaben wie Mausklicks und Sie können sie wie jeden anderen Pinsel auf andere Elemente malen.

+0

Dann, Sie für diese Vorschläge. Es kann möglich sein, Caching beim Zeichnen einer großen Menge von Geometrie zu verwenden, aber ich bin nicht mit dieser Option zufrieden, weil ich nicht verstehe, was WPF macht, dass es so lange dauert, ein paar Zeilen zu rendern. Selbst wenn sie die Vertex- und Indexpuffer auf der CPU (und die Stiftbreite) aufbauen und sie jeden Bildspeicher in den Videospeicher hochladen, sollte das nicht 50 ms für nur 20k Zeilensegmente dauern. Bearbeiten Sobald ich zu Hause bin, I wird ein Video der Anwendung hochladen, das auch erklären sollte, warum ich mit der Verwendung einer zwischengespeicherten Bitmap nicht zufrieden bin. – Simon

+0

Klaus78, du bist mein Geschmack! Ich danke dir sehr.Ich habe mit der langsamen Wiedergabe von WPF-Polygonen gekämpft (ich habe 100.000 bis Millionen von ihnen). Nach dem Rendern ist es so träge, zu scrollen, zu schwenken und zu zoomen. Ganz zu schweigen davon, dass ich das Objekt sogar ziehen, skalieren, drehen und neigen muss. Ich habe versucht, NVidia Quardro K2200 zu installieren, aber es hat sich nicht viel verbessert. Der Grund dafür ist, dass WPF nur GPU (20% seiner vollen Stärke) während OnRender verwendet. Beim Schwenken, Zoomen, Blättern usw. ist die GPU 0%. Durch BitmapCache das UIElement, das diese Polygone enthält, verschwindet dieses Problem. –

Verwandte Themen