Benutzer mit den meisten Antworten
Zeichnenmethode optimieren?

Frage
-
Hallo zusammen!
Ich entwickle derzeit in Visual Basic ein Programm zur Animation der Mandelbrot-Menge.
Ich habe bereits eigentlich ein sehr umfangreiches Programm zur Mandelbrotmenge in der Programmiersprache Delphi geschrieben, doch leider war das sehr langsam, obwohl der Quellcode relativ optimiert hat. Ich habe das dann einfach mal auf die 10 Jahre alte IDE von Delphi geschoben und habe mich dann zur Leistungssteigerung für die modernere Sprache Visual Basic entschieden, da sie sehr ähnlich zu Delphi ist.
Jedenfalls habe ich dann direkt mal - um zu testen, wie schnell Visual Basic in Sachen Grafisches Rendern ist - ein Programm gemacht, dass überhaupt nichts berechnet, sondern mithilfe zweier For-Schleifen jeden Pixel schwarz einfärbt.
Hierbei sei mal zu erwähnen, dass ich erst mal eine Stunde (!) gebraucht habe, um herauszufinden, wie die grafischen Schnittstellen in Visual Basic funktionieren, da die Hälfte der Beschreibungen zum Zeichnen in der neusten Visual Basic Version gar nicht funktionieren (?)
Jedenfalls habe ich den Code dann mal ausgeführt und habe mich direkt erschreckt: Obwohl Nichts (!) berechnet wird und nur jeder Pixel eingefärbt wird, dauert es Jahre bis der Bildschirm eingefärbt war.
Der Code war ungefähr so:
Dim Canvas As Graphics Dim MyPen as New Pen=colors.black Private Sub Rendern Canvas = Form1.createGraphics() For X As Integer = 0 to width For Y As Integer = 0 to height Canvas.Drawline(Mypen,X,Y,X+1,Y+1) Next Next End Sub
Die Helfermethode „Rendern“ wird hierbei direkt am Anfang - beim Form Load - ausgeführt.
Und wie ich bereits sagte, dauert das so ewig.
Meine Frage wäre jetzt, ob es vielleicht noch effizientere Zeichnenmethoden gibt?
Ich benutze ja ein drawline, um einen Pixel einzufärben, denn ich habe selbst nach ewigen Googlen (oder eher „Bingen“) keine Methode gefunden, um einen Pixel direkt einzufärben.
Wäre echt schade, wenn Visual Basic wirklich so langsam ist, denn eigentlich habe ich ja extra Visual Basic rausgekramt, um mein Programm schneller zu machen... :/
Vielen Dank schon mal im Vorraus für jede Hilfe
MfG, Magnus Groß
Antworten
-
Hallo,
da kannst du weder VB noch Delphi wirklich schuld geben. Es ist einfach GDI+, der Grafikalgorithmus für Windows. Er ist nicht dafür ausgelegt schnell sehr komplexe Dinge zu zeichnen.
Direkt ein Pixel einzufärben geht auch nicht. Die einzige Möglichkeit viele Pixel schnell zu zeichnen wäre die Bytes einer Bitmap manuell zubearbeiten und das Bild anschließend zu zeichnen.Außerdem solltest du immer im Paint-Event neu zeichnen. Dieses Event wird immer aufgerufen, wenn sich das Fenster neu zeichnen muss. Das geschieht beispielsweise bei einer Größenänderung. Bei deinem Code könnte es passieren, das plötzlich nichts mehr angezeigt wird - da das Graphics-Objekt aufgelöst wurde.
Im Paint-Event erhälst du das Graphics-Objekt direkt aus den Eventargs.
(Wenn man nicht weiß was beim zeichnen genau passiert, funktioniert kaum ein Beispiel wie gedacht.
PS: Wenn du CreateGraphics verwendest, solltest du das Graphics-Objekt in einem Using-Block verwenden um es wieder frei geben zu können.)Da die Berechnung eines Mandelbrotes nicht gerade leistungsschonend ist, würde ich dir empfehlen einen Shader für WPF zu schreiben, der auf der Grafikkarte gerendert wird. Das geht so gut wie in Echtzeit.
Ich beschäftigfte mich auch mal etwas mit der Thematik, dabei fand ich dieses Beispiel. Das ist zwar C#, aber wenn du soweit mit VB.NET klar kommst, die Klassen heßen alle gleich (dank .NET) und in meiner Signatur findest du einen Konverter. Der Shader muss aber in HLSL geschrieben werden.
Du wirst dich natürlich eine gewisse Zeit einarbeiten müssen, dafür wird es aber am Ende besser gehen.
Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Bearbeitet Tom Lambert (Koopakiller)Moderator Samstag, 14. Juni 2014 20:50 PS
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 11:54
-
Hallo Magnus,
zum einen, wie Tom schon schreibt, ist es keine gute Idee via CreateGraphics auf ein Steuerelement (Form) zu zeichnen. Wird die Fenstergröße verändert oder Teile durch andere Fenster überdeckt, geht Dein Kunstwerk verloren.
Besser ist es, die Ausgabe zuerst in eine Bitmap zu zeichnen und diese beim Form Paint Ereignis via DrawImage (skaliert) auf den Bildschirm zu zeichnen oder eine PictureBox zu nehmen.
Da man bei einer Bitmap besser auf einzelne Pixel zugreifen kann wird die Sache schneller als wenn man den Umweg über den Grafiktreiber nehmen muss - denn es liegen heute etliche Schichten dazwischen, siehe Desktop Window Manager
Mandelbrot und andere Routinen lassen sich dabei zudem besser parallelisieren, d. h. dafür mehrere Cores nutzen. In den Samples for Parallel Programming with the .NET Framework findest Du u. a. ein (C#/C++) Beispiel für ein Mandelbrot Set, das zeigt wie man die Task Parallel Library dafür nutzen kann.
Gruß Elmar
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 11:54
-
Hallo Magnus,
So in etwa. Zeichnen solltest Du wenn Du eine "Arbeitseinheit" erledigt hast (notfalls periodisch).
Das Paint-Ereignis löst man mittels Invalidate aus - wobei das Zeichnen verzögert wird, wenn noch Windows-Meldungen anstehen.
Alternativ kannst Du die Bitmap einer PictureBox, die erledigt dann das Zeichen für Dich. Bei Änderungen des Inhalts der Bitmap müsstest Du ebenfalls Invalidate aufrufen oder aber eine neue Bitmap zuweisen - dann sollte man für die alte Dispose aufrufen, um den Speicher freizugeben.
Gruß Elmar
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 12:41
Alle Antworten
-
Hallo,
da kannst du weder VB noch Delphi wirklich schuld geben. Es ist einfach GDI+, der Grafikalgorithmus für Windows. Er ist nicht dafür ausgelegt schnell sehr komplexe Dinge zu zeichnen.
Direkt ein Pixel einzufärben geht auch nicht. Die einzige Möglichkeit viele Pixel schnell zu zeichnen wäre die Bytes einer Bitmap manuell zubearbeiten und das Bild anschließend zu zeichnen.Außerdem solltest du immer im Paint-Event neu zeichnen. Dieses Event wird immer aufgerufen, wenn sich das Fenster neu zeichnen muss. Das geschieht beispielsweise bei einer Größenänderung. Bei deinem Code könnte es passieren, das plötzlich nichts mehr angezeigt wird - da das Graphics-Objekt aufgelöst wurde.
Im Paint-Event erhälst du das Graphics-Objekt direkt aus den Eventargs.
(Wenn man nicht weiß was beim zeichnen genau passiert, funktioniert kaum ein Beispiel wie gedacht.
PS: Wenn du CreateGraphics verwendest, solltest du das Graphics-Objekt in einem Using-Block verwenden um es wieder frei geben zu können.)Da die Berechnung eines Mandelbrotes nicht gerade leistungsschonend ist, würde ich dir empfehlen einen Shader für WPF zu schreiben, der auf der Grafikkarte gerendert wird. Das geht so gut wie in Echtzeit.
Ich beschäftigfte mich auch mal etwas mit der Thematik, dabei fand ich dieses Beispiel. Das ist zwar C#, aber wenn du soweit mit VB.NET klar kommst, die Klassen heßen alle gleich (dank .NET) und in meiner Signatur findest du einen Konverter. Der Shader muss aber in HLSL geschrieben werden.
Du wirst dich natürlich eine gewisse Zeit einarbeiten müssen, dafür wird es aber am Ende besser gehen.
Tom Lambert - C# MVP
Bitte bewertet- und markiert Beiträge als Antwort. Danke.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Bearbeitet Tom Lambert (Koopakiller)Moderator Samstag, 14. Juni 2014 20:50 PS
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 11:54
-
Hallo Magnus,
zum einen, wie Tom schon schreibt, ist es keine gute Idee via CreateGraphics auf ein Steuerelement (Form) zu zeichnen. Wird die Fenstergröße verändert oder Teile durch andere Fenster überdeckt, geht Dein Kunstwerk verloren.
Besser ist es, die Ausgabe zuerst in eine Bitmap zu zeichnen und diese beim Form Paint Ereignis via DrawImage (skaliert) auf den Bildschirm zu zeichnen oder eine PictureBox zu nehmen.
Da man bei einer Bitmap besser auf einzelne Pixel zugreifen kann wird die Sache schneller als wenn man den Umweg über den Grafiktreiber nehmen muss - denn es liegen heute etliche Schichten dazwischen, siehe Desktop Window Manager
Mandelbrot und andere Routinen lassen sich dabei zudem besser parallelisieren, d. h. dafür mehrere Cores nutzen. In den Samples for Parallel Programming with the .NET Framework findest Du u. a. ein (C#/C++) Beispiel für ein Mandelbrot Set, das zeigt wie man die Task Parallel Library dafür nutzen kann.
Gruß Elmar
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 11:54
-
Vielen Dank schon mal für die schnelle Hilfe :)
Danke für die Stichwörter, wie Shader; jetzt weiß ich schon mal, wo genau ich nach suchen muss.
Wenn es sich dann tatsächlich in Echtzeit berechnen lässt, lohnt sich die Arbeit auch. (In meinem Delphi-Programm dauert das Rendern im Vollbildmodus ca. 5 Sekunden)
Den Using-Block habe ich nicht verwendet, weil ich dachte, dass durch das Löschen des Graphics-Objekt auch das bereits Gezeichnete gelöscht wird.
-
Danke für die Antwort.
Also soll ich eine programminterne Objektvariable vom Typ Bitmap erstellen, und nur auf diesem Bitmap, und nicht direkt auf dem Formular zeichnen, wobei ich aber immer das Paint-Ereignis nach dem Verändern des Bitmaps manuell aufrufe?
Gruß, Magnus
-
Hallo Magnus,
So in etwa. Zeichnen solltest Du wenn Du eine "Arbeitseinheit" erledigt hast (notfalls periodisch).
Das Paint-Ereignis löst man mittels Invalidate aus - wobei das Zeichnen verzögert wird, wenn noch Windows-Meldungen anstehen.
Alternativ kannst Du die Bitmap einer PictureBox, die erledigt dann das Zeichen für Dich. Bei Änderungen des Inhalts der Bitmap müsstest Du ebenfalls Invalidate aufrufen oder aber eine neue Bitmap zuweisen - dann sollte man für die alte Dispose aufrufen, um den Speicher freizugeben.
Gruß Elmar
- Als Antwort markiert Magnus2552 Sonntag, 15. Juni 2014 12:41