2010-02-23 4 views
12

Ich arbeite an einer spielähnlichen App, die bis zu tausend Formen (Ellipsen und Linien) hat, die sich ständig mit 60 fps ändern. Nachdem ich eine excellent article on rendering many moving shapes gelesen habe, habe ich dies mit einem benutzerdefinierten Canvas-Nachkommen implementiert, der OnRender überschreibt, um die Zeichnung über eine DrawingContext zu machen. Die Leistung ist ziemlich vernünftig, obwohl die CPU-Auslastung hoch bleibt.Leistung von DrawingVisual vs Canvas.OnRender für viele sich ständig ändernden Formen

Allerdings schlägt der Artikel, dass der effizienteste Ansatz für ständig in Bewegung Formen viele DrawingVisual Instanzen verwenden statt OnRender. Leider erklärt es aber nicht warum das für dieses Szenario schneller sein sollte.

Die Implementierung auf diese Weise zu ändern, ist kein kleiner Aufwand, daher würde ich gerne die Gründe und die Frage verstehen, ob sie auf mich anwendbar sind, bevor Sie sich für den Wechsel entscheiden. Warum konnte der Ansatz DrawingVisual in diesem Szenario zu einer geringeren CPU-Auslastung führen als der Ansatz OnRender?

+0

Romkyns, Sie könnten mit DrawingVisual und Canvas.OnRender() eine vereinfachte Version erstellen, um die Leistung zu vergleichen, bevor Sie in große Veränderungen eintauchen. Was die Antwort betrifft - ich stimme Charlie vollkommen zu. – Anvaka

Antwort

7

Ich dachte Petzold erklärt in diesem Absatz;

Die ScatterPlotVisual Klasse Werke von ein DrawingVisual Objekt für jedes Datapoint zu schaffen. Wenn sich die Eigenschaften eines DataPoint-Objekts ändern, muss die Klasse nur ändern, um die DrawingVisual , die diesem DataPoint zugeordnet ist, zu ändern.

, die auf einer früher Erklärung baut;

Jedes Mal, wenn die Itemssource-Eigenschaft Änderungen oder die Sammlung Änderungen oder eine Eigenschaft der Datapoint-Objekte in der Sammlung Änderungen, ruft ScatterPlotRender InvalidateVisual. Dies erzeugt einen Aufruf an OnRender, der das gesamte Streudiagramm zeichnet.

Sind Sie gefragt?

Übrigens, this ist ein ziemlich neues Hochleistungs-WPF-Tutorial, viele Zehntausende von Punkten in diesem Plot, es ist 3D gerendert und animiert auch (sogar Mauseingabe verwendet, um einige der Transformationen zu fahren).

+2

Ja, es scheint, dass ich verwirrt war, als ich nicht erkannte, dass der "DrawingVisual" -Ansatz nur die "beste" Methode für dieses spezielle Szenario ist - wo sich viele Punkte ändern, aber nicht die ganze Menge. Wenn sich alles bewegt (was für mich der Fall ist), klingt "OnRender" wie der beste Ansatz. Netter Link auch - danke. –

+0

Es gibt einen anderen Ansatz, den ich als die beste Leistung für die Mutation einer App mit "vielen sich ständig ändernden Formen" empfunden habe, was die Frage des OP war. Ihre App kann einen Baum von DrawingGroups manipulieren. Sie müssen RenderOpen/DrawDrawing nur einmal beim Start der App im Stammverzeichnis dieser Baumstruktur aufrufen.Ab diesem Zeitpunkt bemerkt WPF alle Änderungen an der Struktur und aktualisiert sie automatisch, einschließlich Transformationen und Einfügen/Entfernen von tief verschachtelten DrawingGroup-Kindern. Sie benötigen eine Datenstruktur, die den Status des Baums verfolgt/verwaltet. Siehe meinen Kommentar unter http://stackoverflow.com/a/716469/147511. –

15

Von Pro WPF in C# 2008:

Das durch diese Anwendungen gestellte Problem ist nicht die Komplexität der die Kunst, sondern die schiere Anzahl der einzelner grafischer Elemente. Selbst wenn Sie Ihre Pfadelemente durch leichtere Geometry-Objekte ersetzen, wird der Overhead die Leistung der -Anwendung immer noch beeinträchtigen. Die WPF Lösung für diese Art von Situation ist , um die untere visuelle Ebene Modell zu verwenden. Die Grundidee ist, dass Sie jedes grafische Element als Visual Objekt, das ist eine extrem leichte Zutat, die weniger Overhead als ein Geometry-Objekt oder ein Path-Objekt hat.

Worauf es hinausläuft ist, dass jede einzelne dieser Ellipsen und Linien, die Sie erstellen, eine separate FrameworkElement ist; Das heißt, es unterstützt nicht nur Treffer-Tests, sondern auch Layout, Eingabe, Fokus, Ereignisse, Stile, Datenbindung, Ressourcen und Animation. Das ist ein ziemlich schweres Objekt für das, was Sie zu tun versuchen! Das Objekt Visual überspringt all das und erbt direkt von DependencyObject. Es bietet weiterhin Unterstützung für Hit-Tests, Koordinatentransformationen und Bounding-Box-Berechnungen, aber keine der anderen Dinge, die die Shapes unterstützen. Es ist viel leichter und würde wahrscheinlich Ihre Leistung immens verbessern.

EDIT:

Ok, ich falsch verstehe Ihre Frage zum ersten Mal um.

Wenn Sie OnRender verwenden, hängt es wirklich davon ab, wie Sie die Visuals erstellen und anzeigen. Wenn Sie DrawingContext verwenden und alle visuellen Elemente zu einem einzelnen Element hinzufügen, unterscheidet sich dies nicht von der Verwendung des Ansatzes DrawingVisual. Wenn Sie für jedes erstellte Visual ein separates Element erstellen würden, wäre dies ein Problem. Es scheint mir, dass Sie die Dinge richtig machen.

+2

Sehr gute Antwort! +1 von mir. – Anvaka

+1

Warte, warte, ich erstelle kein FrameworkElements! :) Ich benutze 'OnRender' +' DrawingContext'. Wenn DrawingContext nicht hinter den Kulissen eine Reihe von FrameworkElements erzeugt (was ich bezweifle), ist dies nicht der Fall. –

+0

Mein schlechtes. Eine Änderung hinzugefügt. :) – Charlie

7

Jeder in den Antworten hat es falsch verstanden. Die Frage ist, ob das Rendern von Formen direkt im Zeichnungskontext schneller ist als das Erstellen von DrawingVisual. Die Antwort ist offensichtlich "Ja". Funktionen wie DrawLine, DrawEllipse, DrawRectangle usw. erzeugen kein UI-Element. DrawingVisual ist viel langsamer, da es ein leichtgewichtiges UI-Element erstellt. Die Verwirrung in den Antworten ist, weil Leute einfach kopieren/einfügen, das DrawingVisual führt besser als unterschiedliche UIElement Shapes-Anweisung von MSDN.

+4

Ich bin mir nicht ganz sicher, ob die Antwort so offensichtlich ist. In Fällen, in denen sich die meisten Formen nicht bewegen, ist der Zeichnungskontext nicht * langsamer *? Es zeichnet alles neu, während der DrawingVisual-Ansatz es dem Framework ermöglicht, die zuvor gezeichneten Elemente zwischenzuspeichern und wiederzuverwenden. Oder ist das nicht so? –

+3

Der DrawingContext zeichnet nicht alles neu, weil WPF ein beibehaltenes Zeichnungsmodell besitzt. DrawingVisual enthält eine Logik, die steuert, ob das Visual neu gezeichnet werden soll oder nicht. Sie können dieselbe Logik in der OnRender() -Methode implementieren und nur dann zeichnen, wenn es erforderlich ist. WPF wird Ihre Zeichnung nicht mit 60 fps ungültig machen, OnRender() wird nur aufgerufen, wenn Sie InvalidateVisual() aufrufen oder wenn WPF Ihre Zeichnung ungültig machen muss. – user275587

2

In meinen Tests jedoch (Schwenkanimationen) bemerke ich keinen Geschwindigkeitsunterschied. Ich würde sagen, dass die Verwendung eines Host-Elements für viele Zeichnungsobjekte etwas schneller ist. Dieser Ansatz, bei dem Sie Ihren visuellen Baum mit vielen visuellen Elementen erstellen, gibt Ihnen mehr Kontrolle. Wenn Sie einen komplexen Hit-Test durchführen möchten, ist der Filterprozess schneller, weil Sie ganze "Zweige" von Visuals überspringen können

Verwandte Themen