Erstellung eines Worker-Threads zur Grafikausgabe
-
@elmut19
Natürlich nicht, was soll denn das passieren? DasPeekMessage
drumrum braucht man nicht, die übliche Message Pump sieht so aus:MSG msg; BOOL success; while( (success = GetMessage( &msg, NULL, 0, 0 )) != 0 ) { if( success== -1 ) { // handle the error and possibly exit } else { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
-
@DocShoe
Die Methode werd ich dann auch nochmal probieren.
Aber was heisst "braucht man nicht"?So wie Du das ausdrückst, würde es bedeuten, dass es auch seine gewünschte Wirkung verfehlt!
-
-
@DocShoe
Bei mir hats eben gar nix gebracht. Auch keinen Lock.
... Also meine Version.
-
Ne, ich hab´s auch falsch verstanden. Aber so, wie du die Pump implementierst, kann sie eigentlich nicht funktionieren. Sobald ein Mal alle Nachrichten abgearbeitet sind liefert
PeekMessage
0 zurück und verlässt diewhile
Schleife. Damit ist für deine Anwendung dann Feierabend. Oder gibt´s noch mehr Stellen, wo Nachrichten behandelt werden?
-
@DocShoe
Es werden natürlich diverse Buttons abgearbeitet.
Somit gibt es schon solche Abfragen, auch irgendwo explizit.
Dort funktionieren sie auch. Auch mit PeekMessage().
-
@elmut19
Passiert das an verschiedenen Stellen? Also wird mal hier die MQ behandelt, dann mal da und später noch woanders?
Eigentlich egal, das hat mit dem ursprünglichen Problem jedenfalls nichts zu tun. Also zurück zum Thema:
Wo genau entsteht denn der Engpass? Hast du einen Profiler, den du benutzen kannst? Oder istDrawLines
als Flaschenhals identifiziert?
-
@DocShoe Mit dem Profilerim Visual Studio kenne ich michnicht so gut aus.
Aber der Flaschenhals liegt schon eindeutig in dieser DrawLines().
Ich kann das sogar genauer sagen.
Innerhalb werden unterschiedliche Bereiche der GRafikausgabe angesprochen. Zudem wird für jeden Punkt der Grafik (oder auch "LineTo()") ein neuer "Pen" angesprochen, da es fas beliebig viele Grafen innerhalb der Grafikausgabe gibt.
Dann werden auch alle Werte vorab gelesen, um die Zeitachse zu bestimmen. Das aber geht relativ schnell.
Also es gibt sicher ein paar Optimierungsmöglichkeiten.
Aber ich habe noch nicht so viel Optimierungen gefunden,
und ich denke, dass es nicht genug bringen wird.
Und ich gebe auch zu, dass ich da auch noch was machen muss.Ich dachte mir, dass es einen Versuch wert wäre, einfach mal einen Thread einzusetzen und damit zu experimentieren.
Wenn alleine die Grafik zuende gezeichnet würde, wäre das ein grosser Erfolg.
-
Optimierungsvorschläge:
- Nicht jedes Mal einen neuen Pen erzeugen, sondern iwo zwischenspeichern und wiederbenutzen.
- Anzahl der Datenpunkte reduzieren. Ich kann mir nicht vorstellen, wie mehrere Millionen Linien bei Bildschirmauflösung von 1920x1080 sinnvoll angezeigt werden können. Wir hatten letztens einen Thread, wo aus X Werte Y Zielwerte bestimmt werden sollten, das ist doch schon mal ein Ansatz, wenn Y deutlich kleiner als X ist. Man muss sich dann nur überlegen, welche Y Werte sinnvoll sind. Wir haben eine Software, die zur Diagrammdarstellung etwas Ähnliches macht und unser Reduktionsalgo bietet mehrere Extraktionsmöglichkeiten: Minimum/Maximum/median/Durchschnitt aus einem Block oder das Minimum und Maximum aus einem Block.
-
@DocShoe
Das habe ich auch schon bemerkt, dass punkte dabei einfach mehrmals vorkommen und auch gezeichnet werden.
Und an das, was Du sagst habe ich auch schon gedacht.
Und das werde ich auch noch nachholen, da Warten auf die Anzeige trotzdem für den Anwender sch... ist.
Pens werden auch schon vorher erzeugt und dann gewechselt.
Trotzdem muss ich den "Point vorher berechnen und dann vergleichen, ob er schon da war.
Also es gibt Sachen, die ich auf jeden Fall nacharbeiten werde.
-
Nichts gegen Optimierungen, aber selbst wenn du wirklich gute Optimierungen findest und den Zeichenvorgang auf 10 Sekunden drücken könntest - das exakt gleiche Problem wäre immer noch da.
Microsoft hat vermutlich auch einige Empfehlungen, wie lange so ein Zeichenvorgang (Edit: also beim Malen in den DC) maximal dauern darf. Mehr als einige Zehntelsekunden sollten es nicht sein.
Ob ein separater Thread verwendet wird oder nicht: an double buffering führt meiner Meinung nach kein Weg vorbei.
Und so kompliziert ist das Ganze wirklich nicht.
-
@elmut19 sagte in Erstellung eines Worker-Threads zur Grafikausgabe:
@DocShoe
...
Trotzdem muss ich den "Point vorher berechnen und dann vergleichen, ob er schon da war.
...Ne, musst du nicht. Du überlegst dir, wie viele Datenpunkte pro Kurve sinnvoll sind. Wenn dein Diagramm netto 1500 Pixel breit ist macht es keinen Sinn, mehr als 1500 Stützpunkte einzuzeichnen. Wenn du jetzt (der Einfachheit halber) 15.000 Datenpunkte hast kannst die Datenmenge in 10 Blöcke einteilen und aus jedem Block einen Wert bestimmen, den du einzeichnen möchtest. Da muss weiter nix mehr überprüft werden, du musst dir nur überlegen, welche Daten du haben möchtest. Wahrscheinlich willst du weder Minimum noch Maximum eines Block verlieren, also brauchst du vielleicht zwei Werte je Block. Und dann musst du die Anzahl der Blöcke halbieren, weil jeder Block zwei statt einem Wert liefert.
-
@DocShoe
Ich muss z.B. Events von der Anlage abprüfen. Wenn das innerhalb eines Blockes ist, den ich dann nur "grob überfliege", hab ich schon verloren. Ich kann keinen Messpunkt ignorieren.
-
Datenhaltung (Logik) und Zeichnen sind aber zwei verschiedene Dinge. Und für das Zeichnen legst du dir halt eine eigene Datenstruktur an (mit deutlich weniger Werten).
-
@elmut19
Wieso das denn? Du sollst keine Werte wegwerfen, sondern dir überlegen, welche tatsächlich anzeigerelevant sind.
Nochmal:
Bei einer Diagrammbreite von 1500 Pixeln macht es keinen Sinn, 150.000 Stützpunkte pro Kurve zu zeichnen. Das wären ja in der Breite pro Pixel 100 Linien, da reicht doch Minimum und Maximum, die restlichen 98 Linien sind nur Ballast.
-
@DocShoe
Das stimmt. Und die 100 identischen Pixel habe ich auch selbst schon gezählt.
Und ich werde da auch noch was machen.
Mir schwirrt eben nur schon seit langem diese Thread-Lösung im Kopf rum,
und ich wollte das einfach auch ausprobieren.
-
Du kannst auch mal messen, ob
Polyline
schneller ist alsLineTo
. Wenn das messbar schneller ist kannst du Linien bis zum Farbwechsel en bloc zeichnen.
-
@DocShoe
PolyLine werd ich mir auch noch ansehn. Danke.
Jedenfalls sind einige sinnvolle Tipps bei Euren Beiträgen dabei, um die ich nicht herumkommen werde.
Dafür jedenfalls schon mal vielen Dank an alle.
-
@DocShoe
Nun habe ich einige Zeit mit dem Thread rumexperimentiert und habe es nun auch hinbekommen, dass es funktioniert.
Ich habe den Thread so gebaut, wie auch die anderen Threads in der Anwendung.
Und zumindest ist mir im Verhalten noch nichts negatives aufgefallen.Ich wollte nun dieses Thema noch zu Ende bringen.
Aber vielleicht fällt jemand ja doch noch etwas ein, was ich vielleicht falsch gemacht oder vergessen habe.
Ich habe hier Deklaration, Aufruf des Thread und die Definition des Thread zusammengestellt.
Natürlich auf das Wesentliche beschränkt:friend VOID GrafikThread(LPVOID lpvParam); // Deklaration als public-Var der Klasse "CViewGrafik" (".h"-Datei) void CViewGrafik::DrawGrafik(CDC *pDC,pCBaseAnlage pAnlage) { ... // einige Vorberechnungen // Aufruf des Thread if (m_hGrafikThread) { CleanUpGrafikThread(); } if (!m_hGrafikThread) { m_bGrafikEnde = FALSE; if (!(m_hGrafikThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)GrafikThread, (LPVOID)this, CREATE_SUSPENDED, &m_dwGrafikThreadID))) { m_bGrafikEnde = TRUE; } if (!SetThreadPriority(m_hGrafikThread, CMRCAN_THREAD_PRIORITY)) { m_bGrafikEnde = TRUE; } if (ResumeThread(m_hGrafikThread) == 0xFFFFFFFF) { m_bGrafikEnde = TRUE; } } // Der Thread selbst, ohne Angabe einer Klasse static VOID GrafikThread(LPVOID lpvParam) { CViewGrafik* pView = (CViewGrafik*)lpvParam; CDC cDC; cDC.Attach(GetDC(pView->m_hWnd)); pView->DrawSkalierung(&cDC); pView->DrawKurven(&cDC); ... ExitThread(0); }
Die Reduzierung um doppelte Grafik-Punkte ist dann eine andere Sache.
Die müssen auch wirklich identisch sein, also auch in der y-AchseVielen Dank jedenfalls für Eure Hilfe
-
Ob das so gutgeht hängt davon ab was die Funktionen
DrawSkalierung
undDrawKurven
machen. (BTW: Bitte nicht Englisch und Deutsch mischen, das tut weh.)