Erstellung eines Worker-Threads zur Grafikausgabe



  • @DocShoe
    Ich war bisher leider zu blöd dazu, den Grund für das WM_PAINT rauszubekommen.
    Und leider weiss ich es immernoch nicht (ausser Timeout). Und auch nicht, wie ich es verhindern kann.

    Dann dachte ich (sogar schon seit Längerem), ein Thread wäre ein Experiment wert.



  • Ich finde das eigentlich nachvollziehbar: Während des extrem langen Zeichenvorgangs werden keine Nachrichten abgeholt, Windows stellt Inaktivität fest und graut das Fenster aus. Nach Abschluss der Zeichenoperation und dem Leeren der Queue veranlasst Windows das Programm, das Fenster neu zu zeichnen und das Spiel beginnt von vorne.



  • @yahendrik

    Das denke ich auch. Vielleicht gibt´s irgendwo noch nen Timer, der ein Neuzeichnen auslöst. Oder es treffen schneller neue Messdaten ein als der Graph gezeichnet werden kann. Aber jetzt wird´s spekulativ.



  • @yahendrik
    Das ist eben derzeit auch meine Überlegung.
    Und bei teilweise ein paar Millionen Daten, die auch mehrenen Messgrafen zugeordnet werden müssen, ist es nicht unbedingt ein sehr schlechter Quellcode. Es dauert einfach.
    Ich habe schon länger nach Optimierungen gesucht.



  • @elmut19

    Dann erzähl doch mal ein bisschen. Wie sieht die Darstellung aus? Wie werden die Linien gezeichnet? Bietet sich da ggf. eine Datenreduktion an? Ich kann mir nicht vorstellen, wie man so viele Datenpunkte sinnvoll anzeigen kann, wenn man wirklich alle Datenpunkte anzeigt.



  • @DocShoe Ich selbst (oder mein Vorgänger) haben keinen eigenen Timer gesetzt.
    Daher denke ich ans Betriebssystem.
    Aber auch hier hat die Standard-Routine

    	while (::PeekMessage(&struMessage, NULL, 0, 0, PM_NOREMOVE)) {
    		if (::GetMessage(&struMessage, NULL, 0, 0)) {
    			::TranslateMessage(&struMessage);
    			::DispatchMessage(&struMessage);
    		}
    		else {
    			break;
    		}
    	}
    

    leider auch nix gebracht.



  • @elmut19
    Natürlich nicht, was soll denn das passieren? Das PeekMessage 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!



  • @elmut19

    Was ist denn die Wirkung?

    Edit:
    Käse



  • @DocShoe
    Bei mir hats eben gar nix gebracht. Auch keinen Lock.
    ... Also meine Version.



  • @elmut19

    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 die while 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 ist DrawLines 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.



  • @elmut19

    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).


Anmelden zum Antworten