Graphics.Transform ist massiv ineffizient, was kann ich dagegen tun?

8

Ich schreibe eine Partikel-Engine und habe festgestellt, dass sie massiv langsamer ist als sie sein sollte (ich habe sehr unoptimierte 3D C ++ - Partikel-Engines geschrieben, die 50k Partikel mit 60 fps wiedergeben können, wobei diese auf 32 fps fällt 1.2k ..), habe ich eine Analyse auf den Code unter der Annahme der Rendering der Partikel oder die Rotationen waren die CPU-intensive Operation, aber ich habe festgestellt, dass in der Tat diese beiden kleinen Eigenschaften des Grafik-Objekts sind tatsächlich über 70% essen meiner Leistung ....

%Vor%

Was meine Leistung tötet, sind speziell diese Zeilen:

%Vor%

Ein kleiner Hintergrund, das Grafikobjekt wird von paintteventargs erfasst, es werden dann Partikel auf dem Bildschirm in einer Render-Partikel-Methode dargestellt, die diese Methode anwendet, um irgendwelche Rotationen auszuführen, Multithreading ist keine Lösung wie die Grafik Objekt kann nicht zwischen mehreren Threads geteilt werden. Hier ist ein Link zu der Codeanalyse, die ich gerade ausgeführt habe, damit Sie sehen können, was gerade passiert:

Ссылка

Ich denke, das ist etwas, dem man nicht wirklich helfen kann, weil es so aussieht, als ob die Eigenschaft selbst die Performance zerstört und nichts, was ich tatsächlich gemacht habe (obwohl ich sicher bin, dass es Raum für Verbesserungen gibt), besonders seitdem Die DLL, die die Klasse aufruft, verwendet die meiste CPU-Leistung. Wie auch immer, jede Hilfe würde sehr geschätzt werden, wenn versucht wird, dies zu optimieren ... vielleicht werde ich nur die Rotation aktivieren / deaktivieren, um die Leistung zu erhöhen, wir werden sehen ...

    
Trevor Hart 23.12.2015, 07:34
quelle

3 Antworten

3

Nun, Sie sollten Ihren Kopf eine Weile über die Profilergebnisse, die Sie sehen, kratzen. Es gibt etwas else , das beim Zuweisen der Transform-Eigenschaft ausgeführt wird. Etwas, das Sie ausdenken können, indem Sie feststellen, dass ResetTransform () nichts kostet. Es macht natürlich keinen Sinn, dass diese Methode auch die Eigenschaft Transform ändert.

Beachten Sie, dass DrawRectangle () die teuerste Methode sein sollte, da diese das Pedal zum Metal macht und echte Zeichenbefehle generiert. Wir können nicht sehen, was es von Ihrem Screenshot kostet, kann nicht mehr als 30% betragen. Das ist nicht annähernd genug.

Ich denke, was Sie hier sehen, ist eine obskure Eigenschaft von GDI / plus, es Stapel Zeichenbefehle. Mit anderen Worten, intern generiert es eine Liste mit Zeichenbefehlen und übergibt sie nicht so lange an den Grafiktreiber, bis dies erforderlich ist. Der native Winapi hat eine Funktion, die explizit das Löschen dieser Liste erzwingt, es ist GdiFlush () . Dies wird jedoch von der .NET-Graphics-Klasse nicht offengelegt, dies geschieht automatisch.

Eine ziemlich attraktive Theorie ist also, dass GDI + intern GdiFlush () aufruft, wenn Sie die Eigenschaft Transform zuweisen. Die Kosten, die Sie sehen, sind tatsächlich die Kosten eines vorherigen Aufrufs von DrawRectangle ().

Sie müssen weiterkommen, indem Sie ihm mehr Möglichkeiten zum Chargen geben. Sehr stark bevorzugen Sie die Graphics-Klasse-Methode, mit der Sie eine große Anzahl von Elementen zeichnen können. Mit anderen Worten, zeichne nicht jedes einzelne Teilchen, sondern zeichne viele. Sie werden DrawRectangles (), DrawLines (), DrawPath () mögen. Leider keine DrawPolygons (), die Sie wirklich mögen, technisch könnten Sie PolyPolygon () anpinvoke aber das ist schwer zu gehen.

Wenn meine Theorie falsch ist, beachten Sie, dass Sie Graphics.Transform nicht benötigen. Sie können auch Matrix.TransformPoints () und Graphics.DrawPolygon () verwenden. Ob Sie wirklich weiterkommen können, ist ein wenig zweifelhaft. Die Graphics-Klasse verwendet die GPU-Beschleunigung nicht direkt, so dass sie niemals so gut mit DirectX konkurriert.

    
Hans Passant 23.12.2015, 10:19
quelle
3

Ich bin nicht sicher, ob das Folgende helfen würde, aber es ist einen Versuch wert. Anstatt die neue Matrix zuzuweisen / zuzuteilen / zu verteilen, verwenden Sie die zuvor zugewiesene Methode Graphics.Transform via Graphics - RotateTransform , ScaleTransform , TranslateTransform (und stellen Sie sicher, dass zu ResetTransform , wenn Sie fertig sind.

Das Graphics enthält kein direktes Äquivalent von Matrix.RotateAt Methode, aber es ist nicht schwer, eins zu machen

%Vor%

Dann können Sie Ihren Code so aktualisieren und sehen, ob das hilft

%Vor%     
Ivan Stoev 23.12.2015 09:30
quelle
1

Können Sie einen Offscreen-Puffer erstellen, um Ihr Partikel zu zeichnen, und OnPaint einfach Ihren Offscreen-Puffer rendern lassen? Wenn Sie Ihren Bildschirm regelmäßig aktualisieren müssen, können Sie Ihr OnScreen-Steuerelement / Canvas ungültig machen, z. B. mit Timer

%Vor%


Ein weiterer Grund, ein Matrixobjekt jedes Mal zu erstellen, wenn Sie RotateParticle aufrufen? Ich habe es nicht ausprobiert, aber die MSDN-Dokumente scheinen darauf hinzuweisen, dass get und set auf Graphics.Transform immer eine Kopie erstellen. Sie können also ein Matrix -Objekt auf Klassenebene behalten und es für die Transformation verwenden. Stellen Sie sicher, dass Sie Matrix.Reset() aufrufen, bevor Sie es verwenden. Dies kann zu einer Leistungsverbesserung führen.

    
Vikhram 25.12.2015 01:54
quelle

Tags und Links