News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

How do I remove screen flicker on repaint

Started by ChrisLeslie, February 25, 2006, 10:55:43 PM

Previous topic - Next topic

ChrisLeslie

I am new to masm32 and practicing some code for my own interest. I have come up with a problem that I hope someone can assist me with:
When painting to a window I code in some TextOuts and primitive Lines according to the guidelines in the tutorial and examples. When the WM_PAINT message arrives and the window is repainted I notice a quick flicker across the window.
I assume that this happens because various components are drawn one after another. I also believe that it is possible to buffer the various paint components as an image and then have the image painted as one, thereby reducing the flicker. Can anybody give me some general guidelines on how to implement such buffering, and, is there a simple example available to demonstrate it?

Chris

PBrennick

Chris,
You need to read up on CreateCompatibleDC.  You can also search the examples in masm32 to see how it is done.  BCraven's Calender (sic) for example has a procedure called GoofyEyes that shows how it is done.  His example grabs the entirre window including the titlebar into a CompatibleDC.  I am sure it will help you.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

ChrisLeslie

Thanks Paul for your suggestions. I have tried doing as in the BCraven's example but I still get the same flicker.
I have inclluded here an example of a callback procedure to illustrate my problem. In it I have used a timer to rapidly repaint a window with some text as this demonstrates the problem well. Could you comment on my attempt?

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL hBmp:DWORD
    LOCAL memDC:DWORD
   
    .IF uMsg==WM_CREATE
      invoke SetTimer,hWnd,1,10,NULL     
               
    .ELSEIF uMsg==WM_TIMER
      .if (wParam==1)
        invoke InvalidateRect,hWnd,NULL,TRUE                     
      .endif
                                       
    .ELSEIF uMsg==WM_DESTROY
      invoke KillTimer,hWnd,1
      invoke PostQuitMessage,NULL
                             
    .ELSEIF uMsg==WM_PAINT       
      invoke BeginPaint,hWnd,ADDR ps
      mov    hdc,eax       
      invoke CreateCompatibleDC,hdc
      mov memDC,eax
      invoke TextOut,hdc,5,25,SADD(" Am I flickering? "),18
      mov hBmp,eax
      invoke SelectObject,memDC,hBmp
      invoke BitBlt,hdc,65,18,142,13,memDC,0,0,SRCCOPY
      invoke DeleteObject,hBmp
      invoke DeleteDC,memDC
      invoke ReleaseDC,hWnd,hdc
      invoke EndPaint,hWnd,ADDR ps
                 
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp

Chris 
:'(

zooba

i think what you want to do is to draw the text to the memory DC rather than the window itself and then BitBlt from memory to the window.

You may also have to change your background brush (in your WNDCLASSEX structure) to NULL or handle the WM_ERASEBKGRND message yourself. Before you receive a WM_PAINT message the window is cleared with the background brush. This is what causes the flicker. There is a small instant in there between the window being cleared and the window being redrawn which is just noticable. Quite easily fixed though :U

MichaelW

The flicker is because the update is not synchronized with vertical retrace, as demonstrated by the attachment. Hopefully there are better ways to do this, but in the short time I spent on it I could not figure out how to make the BitBlt from memory work correctly.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
    include \masm32\include\ddraw.inc
    includelib \masm32\lib\ddraw.lib

    DD_OK EQU 0

    DDO_GetScanLine EQU 64
   
    DDERR_INVALIDPARAMS EQU E_INVALIDARG
    DDERR_UNSUPPORTED   EQU E_NOTIMPL
    DDERR_INVALIDOBJECT EQU ((1 shl 31) or (876h shl 16) or ( 130 ))
    DDERR_VERTICALBLANKINPROGRESS EQU ((1 shl 31)or(876h shl 16)or(537))

    DDGetScanLine PROTO scanLine:DWORD
    WaitForVerticalBlank PROTO
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
        lpdd_1    dd          0
        hInst     dd          0
        hWnd      dd          0
        vSync     dd          0
        wc        WNDCLASSEX  <>
        msg       MSG         <>
        rect      RECT        <>
        className db          "vsynctestclass",0
       
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    ; ------------------------------------
    ; Create a default DirectDraw object.
    ; ------------------------------------
    invoke DirectDrawCreate,NULL,ADDR lpdd_1,NULL
    .IF eax
        MsgBox 0,"DirectDrawCreate failed",0,0
    .ENDIF

    mov   hInst, rv(GetModuleHandle, NULL)
    mov   wc.cbSize,        sizeof WNDCLASSEX
    mov   wc.style,         CS_HREDRAW or CS_VREDRAW \
                              or CS_BYTEALIGNWINDOW
    mov   wc.lpfnWndProc,   OFFSET WndProc
    mov   wc.cbClsExtra,    NULL
    mov   wc.cbWndExtra,    NULL
    m2m   wc.hInstance,     hInst
    mov   wc.hbrBackground, COLOR_WINDOW+1
    mov   wc.lpszMenuName,  NULL
    mov   wc.lpszClassName, OFFSET className
    mov   wc.hIcon,         NULL
    mov   wc.hCursor,       NULL
    mov   wc.hIconSm,       0
    invoke RegisterClassEx, ADDR wc
    invoke CreateWindowEx,  WS_EX_OVERLAPPEDWINDOW,
                            ADDR className,
                            chr$("VSync Test"),
                            WS_OVERLAPPEDWINDOW,
                            0,0,600,400,
                            NULL, NULL,
                            hInst, NULL
    mov   hWnd, eax

    invoke ShowWindow, hWnd, SW_SHOWNORMAL
    invoke UpdateWindow, hWnd
  msgLoop:
    invoke GetMessage, ADDR msg, NULL, 0, 0
    .IF (eax != 0)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
        jmp   msgLoop
    .ENDIF
    exit msg.wParam

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

WndProc proc hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
    LOCAL hDC:HDC
    LOCAL ps:PAINTSTRUCT
   
    .IF uMsg==WM_CREATE
      invoke SetTimer,hWin,1,10,NULL
    .ELSEIF uMsg==WM_TIMER
      .IF wParam==1
        .IF vSync
          invoke WaitForVerticalBlank
        .ENDIF 
        invoke InvalidateRect,hWin,NULL,TRUE
      .ENDIF
    .ELSEIF uMsg==WM_LBUTTONDOWN
      not   vSync       
    .ELSEIF uMsg==WM_DESTROY
      invoke KillTimer,hWin,1
      invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_PAINT
      invoke BeginPaint,hWin,ADDR ps
      mov   hDC,eax
      .IF vSync
        invoke TextOut,hDC,5,5,chr$("CLICK CLIENT AREA TO TOGGLE VSYNC OFF"),37
      .ELSE
        invoke TextOut,hDC,5,5,chr$("CLICK CLIENT AREA TO TOGGLE VSYNC ON"),36
      .ENDIF
      invoke EndPaint,hWin,ADDR ps
    .ELSE
      invoke DefWindowProc,hWin,uMsg,wParam,lParam
      ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

DDGetScanLine proc lpScanLine:DWORD

    push  lpScanLine
    push  lpdd_1
    mov   eax, lpdd_1
    mov   eax, [eax]
    call  DWORD PTR[eax+DDO_GetScanLine]
    ret

DDGetScanLine endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

WaitForVerticalBlank proc

    LOCAL localScanLine:DWORD

    invoke DDGetScanLine, ADDR localScanLine
    ; -------------------------------
    ; Wait until not vertical blank.
    ; -------------------------------
    .WHILE (eax == DDERR_VERTICALBLANKINPROGRESS)
        invoke DDGetScanLine, ADDR localScanLine
    .ENDW
    ; ---------------------------
    ; Wait until vertical blank.
    ; ---------------------------
    .WHILE (eax != DDERR_VERTICALBLANKINPROGRESS)
        invoke DDGetScanLine, ADDR localScanLine
    .ENDW

    ret

WaitForVerticalBlank endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start

[attachment deleted by admin]
eschew obfuscation

ramguru

Quote from: MichaelW on February 26, 2006, 03:44:43 PM
.... how to make the BitBlt from memory work correctly.

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL ps   :PAINTSTRUCT
    LOCAL hBmp :DWORD
    LOCAL memDC:DWORD
   
    .IF uMsg==WM_CREATE
      invoke SetTimer, hWnd, 1, 10, 0
               
    .ELSEIF uMsg==WM_TIMER
      .if (wParam==1)
        invoke InvalidateRect, hWnd, 0, 0  ;!!!!! last arg. should be 0
      .endif
                                       
    .ELSEIF uMsg==WM_DESTROY
      invoke KillTimer, hWnd, 1
      invoke PostQuitMessage, 0
                             
    .ELSEIF uMsg==WM_PAINT       
      invoke BeginPaint, hWnd, ADDR ps
      invoke CreateCompatibleDC, ps.hdc
      mov    memDC, eax
      invoke CreateCompatibleBitmap, ps.hdc, 150, 40 ; something that was missing
      mov    hBmp,  eax
      invoke SelectObject, memDC, hBmp
      invoke TextOut,      memDC, 10, 10, SADD(" Am I flickering? "), 18
      invoke BitBlt,       ps.hdc, 10, 10, 150, 40, memDC, 0, 0, SRCCOPY
      invoke DeleteObject, hBmp
      invoke DeleteDC,     memDC
      invoke EndPaint, hWnd, ADDR ps
                 
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp


Actually after you create compatibleDC, ...Bitmap you can draw things not necessary during WM_PAINT message (if memDC is global var.)

PBrennick

ChrisLeslie,
I am sorry, I mislead you as those examples employ a static bitmap.  The attachment I am including is 'the bomb,'' it is an analog clock that shows you how to draw several lines from point to point with no flicker.  The best part is it also shows you how to erase and redraw a line from one set of coordinates to another (the second hand), also with no flicker.  This example is from Ron Thomas who is a real genius at this type of thing so it is a good teaching tool.

EDIT:  The readme file contains his web address where you can find more neat stuff!

Paul


[attachment deleted by admin]
The GeneSys Project is available from:
The Repository or My crappy website

MichaelW

Thanks ramguru,

I missed the ;!!!!! last arg. should be 0. When done correctly it does eliminate any visible flicker, not because the update is synchronized with vertical retrace as I was assuming it would be, but because the background is not changing, as becomes evident if you set the last arg to non-zero. In a situation where you needed to change the background this method would not eliminate flicker.


eschew obfuscation

ChrisLeslie

Tankyou to all the forum members for the overwhelming interest in my problem. That makes me extra keen on doing more assembler coding.
Zooba -  I could not get the BitBlt to work but I thought about painting my own background and erasing each item to the background color prior to drawing it. This actually worked pretty well, and then PBrennick posted an example that did something the same!
MichaelW - I did not get around to trying the synchronism with vertical retrace solution because ramguru came up with how to correctly use BitBlt and bitmaps, which seems to be a very tidy solution. I would never have thought of using ps.hdc. I don't understand that useage but I shall check it out.
Hopefully now I can take it from here.

Regards

Chris  :clap:

PBrennick

That is, after all, the idea, for you to take it from here or else we would ruin all your fun.
Paul
The GeneSys Project is available from:
The Repository or My crappy website

zcoder

invoke InvalidateRect,hWnd,NULL,TRUE
should be set to FALSE to not paint the window
with the class brush when invalidating.


Zcoder....
Back in 1979, My computer ran so fine.
And there was no such thing,
As a Microsoft Crashed Machine.
http://zcoder.110mb.com
http://www.dietzel.com/partner/idevaffiliate.php?id=345_6  Free Domain Names

ChrisLeslie

Paul

Is Ron Thomas aware that the minute hand on his "clock" runs 12 minutes fast??!!

Chris

Don

Actually it is running 12 minutes and 12 seconds fast. :U

High word of EAX needs to be cleared at the beginning of DrawHands proc. oh those pesky "B"s  :wink

Regards, Don.

AsmER

Hi, ChrisLeslie

I think that you already got what you wanted, but there is something more what can help.
It is API function (ChoosePixelFormat (HDC, CONST PIXELFORMATDESCRIPTOR)) that allows you to set double buffering to your window.
I do not sending any example source code, because I starting with assembler as well as you.
This function giving you a little bit more options.

Good luck Chris :U
//File I attached to this post containing description of the function in RTF file format.


[attachment deleted by admin]