GDI Flimmern Backbuffering



  • Hi,
    ich habe ein kleines Problem mit Backbuffering. Ich schreibe gerade an einem Intro , indem so Starwars mässig Text nach oben Scrollt und immer kleiner wird. Im Hintergrund sind Sterne. Und genau da liegt das Problem. Ich nehme nicht ein grosses Bitmap was das ganze Fenster ausfüllt, sondern ein kleines was so rastermässig immer wieder nebeneinander angeordnet wird. Wenn jetzt der Animationsthread gestartet wird der den Text scrollt, flimmert es ganz schrecklich. Ich denke ich habe da irgendwas mit dem Backbuffering nicht ganz verstanden. Ich poste hier mal meine Paintmethode, es wär gut wenn ihr euch das mal angucken würdet und vielleicht den Fehler findet. Habe auch schon alles mögliche probiert aber ich kriegs einfach nicht gebacken. 😞

    void CIntro::paint(HWND hwnd,HBITMAP g_hbmBall){    
        PAINTSTRUCT ps;
        BITMAP bm;
        HDC hdc = BeginPaint(hwnd,&ps);
    
        HDC hdcMem = CreateCompatibleDC(hdc);    
        HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, g_hbmBall);
        GetObject(g_hbmBall, sizeof(bm), &bm);        
        for (int i=0;i<=ysize;i+=bm.bmHeight){
            for (int j=0;j<=xsize;j+=bm.bmWidth){            
                BitBlt(hdc, j, i, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
            }
        }
        SelectObject(hdcMem, hbmOld);    
        DeleteObject(hbmOld);
        DeleteDC(hdcMem);
    
        SetBkMode(hdc,TRANSPARENT); //Hier wird die Schrift gezeichnet
        SetTextColor(hdc,RGB(200,200,50));
        for (int i=0;i<10;i++){
            if (schrift[i]->getActive()) schrift[i]->paint(hdc);
        }
    
        EndPaint(hwnd,&ps);	
    }
    


  • Belial schrieb:

    void CIntro::paint(HWND hwnd,HBITMAP g_hbmBall){
    

    Note: C als Prefix ist schlecht (s. [1],[2]).

    Belial schrieb:

    /* ... */
        GetObject(g_hbmBall, sizeof(bm), &bm);        
        for (int i=0;i<=ysize;i+=bm.bmHeight){
            for (int j=0;j<=xsize;j+=bm.bmWidth){            
                BitBlt(hdc, j, i, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
            }
        }
    

    Hier blittest du mehrmals hdcMem auf hdc. hdc ist dein Frontbuffer, da solltest du am besten nur einmal draufblitten.

    Belial schrieb:

    /* ... */
        for (int i=0;i<10;i++){
            if (schrift[i]->getActive()) schrift[i]->paint(hdc);
        }
    

    Du zeichnest die Schrift direkt auf den Frontbuffer. Da ist es kein Wunder, dass es flimmert.
    Du brauchst einen Backbuffer, der genauso groß wie der Frontbuffer (hdc) ist. Damit machst du die ganzen Zeichenoperationen. Am Schluss blittest du den Backbuffer in einem Rutsch in den Frontbuffer.

    [1] http://schornboeck.net/artikel/cobject.htm
    [2] http://www.c-plusplus.net/forum/viewtopic.php?t=41464



  • Hm, danke für deine Antwort. Diese beiden Links von dir habe ich shcon in einem anderen Beitrag gefunden, irgendwie werde ich daraus aber nicht ganz schlau. Wenn du mir vieleicht einfach ganz kurz anhand meines Quellcodes sagen könntest wie ichs besser machen kann mit einem Quellcode als Antwort? Irgendwie kriege ich es auch nicht gebacken, wenn ich einen Backbuffer nehme (ich habe einfach mal stupide die Klasse aus deinem einen Link gecopy/pasted), ich den dann wieder in den eigentlichen frontbuffer zu kopieren? Bzw. es flackerte nach meinen Modifikationen nur noch schlimmer.



  • Lies bitte meinen Beitrag nochmal genau durch. 😉

    cd9000 schrieb:

    Note: C als Prefix ist schlecht (s. [1],[2]).

    [...]

    [1] http://schornboeck.net/artikel/cobject.htm
    [2] http://www.c-plusplus.net/forum/viewtopic.php?t=41464

    Die beiden Links beziehen sich auf dein C-Prefix, nicht auf Double-Buffering.

    Zu Double-Buffering liefert die Suchfunktion einige Ergebnisse, u.a. auch fertigen Code.



  • Hehe, alles klar 😉 Ich habe auch viele Beispiele gefunden über Backbuffering und auch mal eins umgesetzt. Trotzdem flimmert es fast mehr als vorher. Wenn du es dir mal angucken könntest, irgendein dummer Fehler muss drin sein aber ich verstehe nicht ganz wo der sein soll:

    void CIntro::paint(HWND hwnd,HBITMAP g_hbmBall){    
        PAINTSTRUCT ps;
        BITMAP bm;
        RECT rcBuffer;
        GetClientRect( hwnd, &rcBuffer);
    
        HDC hdc = BeginPaint(hwnd,&ps);
        HDC hdc2 = CreateCompatibleDC(hdc);
        HBITMAP hBM = CreateCompatibleBitmap(hdc, rcBuffer.right - rcBuffer.left, rcBuffer.bottom - rcBuffer.top);
        SelectObject(hdc2, hBM); 
    
        HDC hdcMem = CreateCompatibleDC(hdc);    
        HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, g_hbmBall);
        GetObject(g_hbmBall, sizeof(bm), &bm);        
        for (int i=0;i<=ysize;i+=bm.bmHeight){
             for (int j=0;j<=xsize;j+=bm.bmWidth){            
                	BitBlt(hdc2, j, i, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
             }
        }
        SelectObject(hdcMem, hbmOld);        
        DeleteObject(hbmOld);
        DeleteDC(hdcMem);   
    
        SetBkMode(hdc2,TRANSPARENT);
        SetTextColor(hdc2,RGB(200,200,50));
        for (int i=0;i<10;i++){
            	if (schrift[i]->getActive()) schrift[i]->paint(hdc2);
        }
    
        BitBlt(hdc, 0, 0, rcBuffer.right - rcBuffer.left, rcBuffer.bottom - rcBuffer.top, hdc2, 0, 0, SRCCOPY);
        DeleteObject(hBM);
        DeleteDC(hdc2);
        EndPaint(hwnd,&ps);	
    }
    

    PS: Sowie ich Zeit hab nenne ich mal die Präfixe um, aber erstmal muss diese "doofe" Animation hinhauen.



  • Rufst du irgendwo anders in deinem Code InvalidateRect auf? Wenn ja, ist der letzte Parameter auf FALSE gesetzt?



  • Wenn ich das richtig gesehen habe ist auch der DeleteObject-Aufruf für hbmOld fehl am Platz -> löschen: 🙂



  • Ja, ich rufe InvalidateRectangle auf und der letzte Parameter ist auf true gesetzt. 😉 Ist das schlimm?



  • Ja, denn dann wird vorher unnötiger Weise dein Fenster mit dem in der Fensterklasse angegebenen Brush ausgefüllt - was unnötig ist, wenn du eh nachher alles überblittest. Außerdem führt dies eben dazu, dass zwischendurch das Fenster kurz in der Brushfarbe "aufblitzt" 😉 - Also einfach den letzten Parameter auf FALSE ändern 🙂



  • Jo, jetzt funzt es super. Grosses Danke, im nachhinein isses auch logisch. Das ganze hin und her hat jetzt eh den Vorteil, dass ich mich noch nen bisl intensiver mit den GDI sachen auseinander gesetzt habe.



  • Mh, jetzt hab ich plötzlich nen neues fieses Problem. Auf XP funzt das Prog super, unter Win9x frisst das programm immer mehr ressourcen und friert am ende komplett ein. Habe ich da vergessen irgendwelche Handles korrekt zu schliessen?



  • Mir ist jetzt spontan nichts aufgefallen - du könntest wenn dann noch die Erzeugung des Backbuffers nach WM_CREATE oder evtl. besser WM_SIZE (dort dann nicht vergessen den ggf. schon angelegten Backbuffer vor dem Neu-Anlegen freizugeben) verschieben (Zerstören dann bei WM_PAINT). Und wenn sich der Inhalt des Backbuffers nur bei Größenänderungen verändert (oder wann auch immer), dann könntest du auch die Befüllung des Backbuffers dorthin verschieben 🙂



  • flenders schrieb:

    du könntest wenn dann noch die Erzeugung des Backbuffers nach WM_CREATE oder evtl. besser WM_SIZE (dort dann nicht vergessen den ggf. schon angelegten Backbuffer vor dem Neu-Anlegen freizugeben) verschieben (Zerstören dann bei WM_PAINT).

    Dann musst du aber noch beachten, dass der Backbuffer gelegentlich neu angelegt werden muss (etwa bei WM_SIZE oder WM_DISPLAYCHANGE).



  • Also es ist bei meinem Programm so, dass ein Thread die Schrift nach oben bewegt und dabei mit einer Taktung von 10 Herz WM_PAINT aufruft. Und unter Windoof 98/ME wird das ganze zunehmend langsamer, bis gar nix mehr geht. Deshalb schätze ich dass das Problem irgendwo in WM_PAINT liegt.Ich werd denn jetzt mal probieren die erzeugung des Backbuffers nach WM_CREATE auszulagern. Vielleicht hilfts. Ich find eh merkwürdig das XP anscheinend alle handles korekt zu schliessen scheint und 9x aus irgendwelchen Gründen mit meinem Code nicht klar kommt.



  • Achso und ehe ichs vergesse, das Fenster ist so erzeugt dass die Grösse nicht verändert werden kann. Also brauche ich dann jawohl WM_SIZE nicht zu beachten.


Log in to reply