viel zu zeichnen - wie machen dass nicht manche controls "stehenbleiben"
-
Also... ich muss einige Videos auf den Schirm zeichnen. Habe auch schon eine andere Frage dazu offen (hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-215046.html ).
Nun bekomme ich allerdings einen sehr unschönen Effekt wenn ich einfach mit Invalidate() arbeite. Und zwar wenn mehrere Controls am Schirm sind, die alle "dauernd" neu gezeichnet werden wollen, und der UI Thread dadurch komplett ausgelastet wird.
Da WM_PAINT eine low-priority Nachricht ist kommen die ganzen über BeginInvoke angetretenen Handler zwar noch dran (welche dann das Invalidate() absetzen), allerdings wird mir nur ein Control "flüssig" neu gezeichnet, ein weiteres stottert so vor sich hin, die anderen "stehen" total.
Wenn ich in dem über BeginInvoke gestarteten Handler allerdings ein "this.Update()" einbaue kann ich das Programm nichtmehr bedienen.(Zur Info: die Bilder kommen aus einen eigenen Thread per Callback daher, die Callback Funktion kopiert dann nur die Bilddaten, und setzt dann sofort ein BeginInvoke ab. Dabei wird sogar sichergestellt dass nur ein BeginInvoke gemacht wird wenn nicht schon eines "ausständig" ist. Bringt aber alles nix, die Anwendung wird sofort unbedienbar wenn ich direkt in dem über BeginInvoke angetretenen Handler zeichne)
Jetzt hab' ich mir einen "IdleTimeScheduler" geschrieben der sich in den Application.Idle Event reinhängt, und eine eigene Liste von Tasks verwaltet. Jedes Control hat dabei max. einen Task in dieser Liste. Anstatt im Handler jetzt direkt zu zeichnen schmeisse ich einen Task in den IdleTimeScheduler (der einen ggf. noch ausständigen Task für dasselbe Control überschreibt), und warte darauf dass der dann irgendwann ausgeführt wird.
Funktioniert eigentlich auch recht gut, die Controls werden jetzt alle gleich "flüssig" bis gleich "ruckelig" gezeichnet - was das Ziel der ganzen Übung war. Allerdings bleiben meine Videos dann stehen wenn ich das Fenster resize, oder an der Titelleiste packe damit ich es rumschieben kann. Ich kann nur davon ausgehen dass in so einem Fall dann der Application.Idle Event nichtmehr getriggert wird. Auch doof
Hat jmd. eine Idee wie man sowas ab besten macht?
Ich habe mir auch schon überlegt dass ich meinen "Scheduler" genauso gut über Windows Messages antreten könnte (anstatt den Application.Idle Event zu verwenden), allerdings stellt sich dann die Frage: welche Message soll ich verwenden? Nehme ich einen normale Message (z.B. WM_USER) hab' ich vermutlich wieder das Problem dass das Programm unbedienbar wird.
Ich suche also eine Möglichkeit ein BeginInvoke oder auch nur ein ols-school PostMessage zu machen welches von der Priorität her gleich wie WM_PAINT und WM_TIMER behandelt wird.
Kann ich einfach eine WM_TIMER mit PostMessage posten? Oder wäre das aus bestimmten Gründen garnicht schlau. Ich weiss (bzw. habe gelesen) dass WM_TIMER normalerweise in GetMessage() bzw. PeekMessage() synthetisiert wird, also eigentlich nie in der Queue steht, von daher habe ich da einige Bedenken.Irgendwelche Ideen/Vorschläge?
Danke fürs Lesen und für eure Zeit
-
Nein, aber schöne Sache sonst so, echt hübsch.
Lese sowas immer wieder gern.
Guter Ebay'er jederzeit gerne wieder, alles Klasse
-
Wenn der GDI+ krempel zu langsam ist, lohnt es sich eventuell doch sich den D3D krempel anzusehen. Tipps kann man sich sicher aus den Spieleforum (oder ähnlich angesiedelten Gemeinschaften) organisieren.
Ist ein zweischneidiges Schwert bei dem man nicht weiß auf welcher Seite die Zeit verheizt ist.
-
Wenn der GDI+ krempel zu langsam ist, lohnt es sich eventuell doch sich den D3D krempel anzusehen. Tipps kann man sich sicher aus den Spieleforum (oder ähnlich angesiedelten Gemeinschaften) organisieren.
Ja, wenn ich Zeit dafür habe. Was ich im Moment nicht habe. Aber irgendwann guck ich mir das sicher an.
Ist ein zweischneidiges Schwert bei dem man nicht weiß auf welcher Seite die Zeit verheizt ist.
Was ist ein zweischneidiges Schwert? Ich weiss ziemlich genau wo die Zeit draufgeht, nämlich beim Rendern, also direkt in OnPaint. Oder beziehst du das jetzt auf D3D?
BTW: PostMessage(WM_TIMER) hab ich jetzt versucht, funktioniert leider nicht, wird NICHT nach hinten gereiht wie man es im Netz oft zu lesen findet.
-
Hallo
Ich habe wirklich keine Ahnung, ob dir das hilft oder ob das überhaupt geht, aber vielleicht kannst du die Videos ja auch mit XNA rendern. Da ist die Einarabeitungszeit nicht sehr hoch.
chrische
-
Jo ich muss mir das erst ansehen.
Das Dumme bei D3D und XNA und den ganzen ist dass ich ein Device anlegen muss, und mit dem Device dann - soweit ich weiss - nur auf ein Fenster rendern kann, nämlich auf das mit dem ich das Device angelegt habe.
(EDIT: und ich mir nicht so ganz sicher bin ob es Probleme gibt wenn man mehrere Devices in einem Prozess anlegt und wieder niederreisst und das auch noch wiederholt. Steht eigentlich im anderen Thread auch schon, also warum ich von DX erstmal die Finger lasse.)p.S.: eigentlich geht's mir hier garnicht darum das Rendern zu beschleunigen, dafür wäre der andere Thread. Ich möchte hier eher wissen ob ihr auch schonmal den beschriebenen Effekt hattet bei Applikationen die viele Controls in sehr kurzen Intervallen refreshen, und ob/wie ihr das dann "gelöst" habt.
-
Auf den Scannern kommt die UI manchmal zu kurz was das Zeichnen betrifft. Hier wird dann ein Application.DoEvents abgesetzt, was die Abarbeitung aller Nachrichten erzwingt. (Jedoch hat das ggf. auch Seiteneffekte die nicht immer angenehm sind.)
~Das mit dem Schwert war auf: Mehrere Wege, jeder Weg ist lang um ihn zu gehen (testen) aber die Zeit reicht nur für einen Weg :/~
-
Das mit dem Schwert war auf: Mehrere Wege, jeder Weg ist lang um ihn zu gehen (testen) aber die Zeit reicht nur für einen Weg
Jo, genau das
Da es im Moment erstmal halbwegs funktioniert werd ich das auch vertagen. Wenn später Zeit dafür da ist gut, sonst bleibt es halt so wie es ist - mit nem eigenen Scheduler in Application.Idle.Was DoEvents angeht: bringt in meinem Fall nix (wenn ich das richtig einschätze). DoEvents arbeitet alle Messages ab, richtig. Allerdings passiert bei mir (ohne den Trick mit Application.Idle) folgendes: in einer Form sitzen ein Haufen Video Controls. Video Control 1 bekommt nun ein Invalidate(). Das führt nicht dazu dass eine Message gepostet wird, sondern es wird die Update-Region für das entsprechende Fenster angepasst. Beim nächsten GetMessage bzw. PeekMessage wo keine andere Nachricht gefunden wird, gehen die Funktionen her und gucken ob es irgendwelche Fenster gibt wo die Update-Region nicht "leer" ist. Ist das der Fall wird für das erste gefundene Fenster eine WM_PAINT synthetisiert. WM_PAINT landet also niemals in der Queue, wird daher also auch nicht gereiht.
Nun fängt der Paint Handler dieses Controls an zu laufen. Das dauert leider eine Zeit lang. Nehmen wir an dass es so lange dauert dass neue Frames für alle Controls in dieser Zeit ankommen. In der entsprechenden "RecieveFrame" Funktion (die asynchron in einem anderen Thread läuft) wird nun mittels BeginInvoke ein Callback abgesetzt um den entsprechenden Code im UI Thread auszuführen. BeginInvoke macht intern im Prinzip ein PostMessage.
Wenn das nächste mal GetMessage/PeekMessage ausgeführt wird ist die Queue also nichtmehr leer, d.h. die von BeginInvoke verwendete Message wird abgearbeitet. Der so ausgeführte Code macht wieder ein Invalidate, und zwar für alle Controls, da ja für jedes Control ein BeginInvoke gemacht wurde.
Erst nachdem alle BeginInvoke fertig abgearbeitet sind ist die Queue also wieder leer, und erst dann macht sich GetMessage/PeekMessage wieder auf die Suche nach Fenstern deren Update-Region nicht leer ist.Und hier kommt der entscheidende Punkt: Windows nimmt einfach das erste gefundene Fenster dessen Update-Region nicht leer ist, und schickt diesem eine WM_PAINT. Wenn nun also Invalidate() so schnell für alle Controls aufgerufen wird, dass quasi immer alle Controls eine nicht-leere Update-Region haben wenn GetMessage/PeekMessage zu suchen anfängt, dann wird immer nur ein Control neu gezeichnet - immer dasselbe (das erste gefundene, und die Reihenfolge in der gesucht wird ändert sich ja nicht, ist einfach die Reihenfolge in der die Controls angelegt wurden)! Würde Windows dagegen das Fenster auswählen welches schon am längsten nichtmehr neu gezeichnet wurde, wäre das Problem gegessen. (Genau das "simuliere" ich ja auch über meinen Scheduler)
Und... da DoEvents im Prinzip auch nur einen Message-Loop enthält, bringt das in dem Fall nix. DoEvents würde schlicht und einfach NIE mehr verlassen werden, da es IMMER noch eine Message findet die es zu bearbeiten gilt. Sei es nun eine aus der Queue oder eine synthetisierte WM_PAINT.
Mit WM_TIMER ist es im Prinzip dasselbe wie mit WM_PAINT. WM_TIMER wird auch erst von GetMessage/PeekMessage synthetisiert wenn keine anderen Messages mehr vorhanden sind, und ein Timer gefunden wird der "abgelaufen" ist.