Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

Trying to reduce Flickering when using SetTimer

Sonx_hvn7Sonx_hvn7 Member Posts: 54
How do i avoid this?

[code]

case WM_CREATE:
{
/* Create timer to refresh screen */
SetTimer(hwnd, IDT_REFRESH, 50,(TIMERPROC) NULL);

............
............

case WM_TIMER:
{
// Refresh screen
InvalidateRect (hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
break;
[/code]

My whole window is flickering and when selecting menu, sometimes menu is black (wont see text on menu) until i move mouse over menu then text clears out.. Lets just say the window operations would not function properly if i use timer as shown... I just wanna refresh screen every 50ms
«1

Comments

  • MT2002MT2002 Member Posts: 1,444
    Did you set up double buffering?

    Basically, you render to an offscreen DC. After every frame of animation, do a single BitBlt() to copy the backbuffer to your windows DC. Double, Triple buffering, and Page flipping are very common techniques used to eliminate flicker.

    [link=http://www.codeproject.com/KB/cpp/DoubleBuffering.aspx]Heres a nice tutorial[/link]
    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • Sonx_hvn7Sonx_hvn7 Member Posts: 54
    : Did you set up double buffering?
    :
    : Basically, you render to an offscreen DC. After every frame of
    : animation, do a single BitBlt() to copy the backbuffer to your
    : windows DC. Double, Triple buffering, and Page flipping are very
    : common techniques used to eliminate flicker.
    :
    : [link=http://www.codeproject.com/KB/cpp/DoubleBuffering.aspx]Heres a
    : nice tutorial[/link]
    : [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS
    : Operating
    : System[rightbr][leftbr][link=http://www.brokenthorn.com]Website ::
    : OS Development Series[rightbr][/link][/size]
    :
    Ok, here's my attempt

    [code]
    case WM_PAINT:
    {
    hdc=BeginPaint(hwnd, &pntS);

    /* Create memory DC and add hdc (paint) to it */
    CreateBuffer(0, hdc);

    /* *** User Interface Now draw on the new memDC *** */
    DrawArea(hwnd, my_DC_Buffer[0], colors);
    RadioButtonsText(hwnd, my_DC_Buffer[0], polarLbl, stdFont);
    TargetText(hwnd, targetLbl, sqm, mtrs, my_DC_Buffer[0], stdFont);
    GenParams(hwnd, genPara, fareRng, dpRng, my_DC_Buffer[0], stdFont);
    RadaAntennaText(hwnd, rdrAntenaLbl, deg, db, halfMin, mtrs, my_DC_Buffer[0], stdFont);
    DisplayResultsBox(hwnd, my_DC_Buffer[0], rdrTrans_recv, targetLbl, rsltFont);
    RadarReceiverText(hwnd, rdrTrans_recv, hz, mhz, mtrs, kw, usec, db, wvlnthFig, my_DC_Buffer[0], stdFont);

    // Calculate & show results
    output = ProcessData(hwnd);
    DisplayResults(hwnd, my_DC_Buffer[0], &output, rsltFont);

    CopyToScreen(hdc, 0);
    EndPaint(hwnd, &pntS);
    }
    return 0;
    [/code]

    CreateBuffer(0)....

    [code]
    /* Create Device Context on buffer for client window */
    void CreateBuffer(int which, HDC hDc)
    {
    GetWindowRect(my_handle,&dc_rect[which]);
    // my_DC = GetDC(my_handle);
    my_DC_Buffer[which] = CreateCompatibleDC(hDc);
    hbm_Buffer[which] = CreateCompatibleBitmap(hDc,dc_rect[which].right ,
    dc_rect[which].bottom );
    hbm_oldBuffer[which] = (HBITMAP) SelectObject(my_DC_Buffer[which],
    hbm_Buffer[which]);
    }
    [/code]

    CopyToScreen()

    [code]
    /* Copy Device Context from buffer to screen */
    void CopyToScreen(HDC hdc, int which)
    {
    BitBlt(hdc,0,0,dc_rect[which].right,dc_rect[which].bottom,my_DC_Buffer[which],0,0,SRCCOPY);
    }
    [/code]

    Problems...
    Now the entire window background is black....

    Questions

    I've removed

    [code]
    InvalidateRect (hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
    [/code]

    .. cause the screen was doing worse than flickering... This is not an easy excersize
  • MT2002MT2002 Member Posts: 1,444
    Do your painting in your main loop. Have WM_PAINT only repaint the window via BeginPaint/EndPaint.

    You still seem to be rendering to the main parent window--dont do this. Do *all* of your rendering to an offscreen DC. At the end of each frame, use BitBlt to copy the backbuffer to your windows DC in one shot.

    The only other thing I dont like is your CreateBuffer routine, as you should only have a single backbuffer per window.
    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • Sonx_hvn7Sonx_hvn7 Member Posts: 54
    : Do your painting in your main loop. Have WM_PAINT only repaint the
    : window via BeginPaint/EndPaint.

    [color=Blue]
    Could you explain a bit further...[/color]

    :
    : You still seem to be rendering to the main parent window--dont do
    : this. Do *all* of your rendering to an offscreen DC. At the end of
    : each frame, use BitBlt to copy the backbuffer to your windows DC in
    : one shot.
    :

    [color=Blue]Where is off screen? Within WM_CREATE, or WM_TIMER, or where?[/color]

    : The only other thing I dont like is your CreateBuffer routine, as
    : you should only have a single backbuffer per window.

    [color=Blue]@MT2002... I have a single client window and 5 rectangles drawn unto it with lot's of text, some displayed as floating values (using SetWindowText())... I don't have multiple windows or dialog windows
    [/color]

    [color=Blue]Concerns...

    When creating a CompatibleDC, should i use hdc from BeginPaint() or should i rather GetDC() of the ClientArea (note the commented GetDC()), then create a compatible DC from it?[/color]
  • MT2002MT2002 Member Posts: 1,444
    : Where is off screen? Within WM_CREATE, or WM_TIMER, or
    : where?

    The offscreen DC was created with CreateCompatableDC.

    Typically programs create one single offscreen DC and bitmap during initialization. Afterwords, all rendering is done to this offscreen DC rather then the main window.

    For example... (Not tested, but I hope it helps)

    [code]//! your window is created (it is HWND hwnd)

    //! get window dc and create new backbuffer
    HDC hdc = GetDC(hwnd);
    HDC bmapDC = CreateCompatibleDC(hdc);

    //! get client rectangle of our window
    RECT clientRECT;
    GetClientRect(hwnd,&clientRECT);

    //! create new bitmap that we can render to. Select it into the offscreen DC
    HBITMAP hbmap =
    CreateCompatibleBitmap(hdc,clientRECT.right,clientRECT.bottom);
    SelectObject( bmapDC, hbmap );[/code]

    Now, bmapDC is your offscreen DC (Your backbuffer). Every time you want to render, always use bmapDC instead of your main window.

    ...And, at the end of each loop in your main program loop, do a simple BitBlt to copy the backbuffer to your main window:

    [code]while (1) {

    //! clear backbuffer DC
    FillRect(bmapDC,&clientRECT,BackGroundBrush);

    //! program logic code here... Render everything to
    //! bmapDC (As shown in the above FillRect)

    //! done, so copy backbuffer to main window (hwnd is your window)
    BitBlt(GetDC (hwnd),0,0,clientRECT.right,clientRECT.bottom,bmapDC,0,0,SRCCOPY);

    }[/code]

    I hope this clarifies things better :)

    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • AsmGuru62AsmGuru62 Member Posts: 6,519
    [color=Blue]In addition you have to clean up. Where are your calls to:

    1. SelectObject(oldBmp) for SelectObject(compatibleBmp)
    2. DeleteDC() for CreateCompatibleDC()
    3. DeleteObject() for CreateCompatibleBitmap()

    All these resources are leaked very fast - because your WM_PAINT is running at 50 milliseconds!!

    Also, to make it even better do the creation of DC and bitmap once and then cache it. That means that your WM_PAINT only draws to memory DC and then BitBlt() it back to screen DC. Faster than you do now. Localize your variables in a function as static members and then use a parameter to tell the function how to proceed.

    Here is how it is done in good old C. You can create C++ class and encapsulate all that there. In your WM_PAINT just call this function with a proper HDC and area sizes and pass cmdAllocateBackBuffer command. When WM_DESTROY is received call the same function with cmdCleanupBackBuffer command.
    [code]
    enum BackBufferCommands
    {
    cmdAllocateBackBuffer,
    cmdCleanupBackBuffer
    };

    HDC MemoryDCForArea
    (
    HDC hDC_from_BeginPaint,
    int iAreaWidth,
    int iAreaHeight,
    int iCommandWhatToDo
    )
    {
    static HDC hMemoryDC = NULL;
    static HGDIOBJ hMemDCBitmap = NULL;
    static HBITMAP hCanvasBitmap = NULL;
    static int iCachedWidth = 0;
    static int iCachedHeight = 0;

    switch (iCommandWhatToDo)
    {
    case cmdAllocateBackBuffer:
    [color=Green]//
    // Check if the cached area size already been done
    //[/color]
    if ( (iAreaWidth == iCachedWidth) &&
    (iAreaHeight == iCachedHeight) )
    {
    [color=Green]//
    // We already have the memory DC
    //[/color]
    return hMemoryDC;
    }
    else
    {
    [color=Green]//
    // Area have changed or we here for the first time.
    // Tell ourselves to clean up.
    //[/color]
    MemoryDCForArea (0, 0, 0, cmdCleanupBackBuffer);

    [color=Green]// Prepare new buffer
    //[/color]
    hMemoryDC = CreateCompatibleDC (hDC_from_BeginPaint);

    hCanvasBitmap = CreateCompatibleBitmap (
    hDC_from_BeginPaint,
    iCachedWidth = iAreaWidth,
    iCachedHeight = iAreaHeight);

    hMemDCBitmap = SelectObject (hMemoryDC, hCanvasBitmap);
    return hMemoryDC;
    }
    break;

    case cmdCleanupBackBuffer:
    [color=Green]//
    // Reverse all we done when creating back buffer.
    //[/color]
    if (hMemoryDC != NULL)
    {
    SelectObject (hMemoryDC, hMemDCBitmap);
    DeleteObject (hCanvasBitmap);
    DeleteDC (hMemoryDC);

    hMemoryDC = NULL;
    hMemDCBitmap = NULL;
    hCanvasBitmap = NULL;
    iCachedWidth = iCachedHeight = 0;
    }
    break;
    }
    }
    [/code]
    [/color]
  • Sonx_hvn7Sonx_hvn7 Member Posts: 54
    : [color=Blue]In addition you have to clean up. Where are your calls
    : to:
    :
    : 1. SelectObject(oldBmp) for SelectObject(compatibleBmp)
    : 2. DeleteDC() for CreateCompatibleDC()
    : 3. DeleteObject() for CreateCompatibleBitmap()
    :
    : All these resources are leaked very fast - because your WM_PAINT is
    : running at 50 milliseconds!!
    :
    : Also, to make it even better do the creation of DC and bitmap once
    : and then cache it. That means that your WM_PAINT only draws to
    : memory DC and then BitBlt() it back to screen DC. Faster than you do
    : now. Localize your variables in a function as static members and
    : then use a parameter to tell the function how to proceed.
    :
    : Here is how it is done in good old C. You can create C++ class and
    : encapsulate all that there. In your WM_PAINT just call this function
    : with a proper HDC and area sizes and pass cmdAllocateBackBuffer
    : command. When WM_DESTROY is received call the same function with
    : cmdCleanupBackBuffer command.
    : [code]:
    : enum BackBufferCommands
    : {
    : cmdAllocateBackBuffer,
    : cmdCleanupBackBuffer
    : };
    :
    : HDC MemoryDCForArea
    : (
    : HDC hDC_from_BeginPaint,
    : int iAreaWidth,
    : int iAreaHeight,
    : int iCommandWhatToDo
    : )
    : {
    : static HDC hMemoryDC = NULL;
    : static HGDIOBJ hMemDCBitmap = NULL;
    : static HBITMAP hCanvasBitmap = NULL;
    : static int iCachedWidth = 0;
    : static int iCachedHeight = 0;
    :
    : switch (iCommandWhatToDo)
    : {
    : case cmdAllocateBackBuffer:
    : [color=Green]//
    : // Check if the cached area size already been done
    : //[/color]
    : if ( (iAreaWidth == iCachedWidth) &&
    : (iAreaHeight == iCachedHeight) )
    : {
    : [color=Green]//
    : // We already have the memory DC
    : //[/color]
    : return hMemoryDC;
    : }
    : else
    : {
    : [color=Green]//
    : // Area have changed or we here for the first time.
    : // Tell ourselves to clean up.
    : //[/color]
    : MemoryDCForArea (0, 0, 0, cmdCleanupBackBuffer);
    :
    : [color=Green]// Prepare new buffer
    : //[/color]
    : hMemoryDC = CreateCompatibleDC (hDC_from_BeginPaint);
    :
    : hCanvasBitmap = CreateCompatibleBitmap (
    : hDC_from_BeginPaint,
    : iCachedWidth = iAreaWidth,
    : iCachedHeight = iAreaHeight);
    :
    : hMemDCBitmap = SelectObject (hMemoryDC, hCanvasBitmap);
    : return hMemoryDC;
    : }
    : break;
    :
    : case cmdCleanupBackBuffer:
    : [color=Green]//
    : // Reverse all we done when creating back buffer.
    : //[/color]
    : if (hMemoryDC != NULL)
    : {
    : SelectObject (hMemoryDC, hMemDCBitmap);
    : DeleteObject (hCanvasBitmap);
    : DeleteDC (hMemoryDC);
    :
    : hMemoryDC = NULL;
    : hMemDCBitmap = NULL;
    : hCanvasBitmap = NULL;
    : iCachedWidth = iCachedHeight = 0;
    : }
    : break;
    : }
    : }
    : [/code]:
    : [/color]


    [color=Blue]My attempt to your suggestions
    [/color]
    [code]
    case WM_PAINT:
    {
    hdc = BeginPaint(hwnd, &pntS);

    [color=Green]/* Retrieve the client's area width and height */[/color]
    GetClientRect(hwnd, &clientRECT);

    [color=Green]/* Create memory DC on back buffer */[/color]
    MemDC = MemoryDCForArea(hdc,
    clientRECT.right, [color=Green]// width[/color]
    clientRECT.bottom, [color=Green]// height[/color]
    0);

    [color=Green]/* *** Rendering screen data (User Interface) *** */[/color]
    DrawArea(hwnd, MemDC, colors);

    [color=Green]// From menDC to screen[/color]
    BitBlt(hdc,0,0,clientRECT.right,clientRECT.bottom,MemDC,0,0,SRCCOPY);

    [color=Green]/* Calculate & show results
    output = ProcessData(hwnd);
    DisplayResults(hwnd, hdc, &output, rsltFont);*/[/color]

    [color=Green]/* Delete/release memory DC on back buffer */[/color]
    MemDC = MemoryDCForArea( BeginPaint(hwnd, &pntS),
    clientRECT.right, [color=Green]// width[/color]
    clientRECT.top, 1); [color=Green]// height[/color]

    EndPaint(hwnd, &pntS);
    }
    return 0;
    [/code]

    [color=Blue]No improvement on flickering, and now the window background is black....[/color]

    [color=Blue]Below is the function called in WM_PAINT to draw on client MemDC[/color]
    [code]

    [color=Green]// Draw Rectangles on Window Area[/color]
    void DrawArea(HWND hwnd, HDC hdc, RGBColor *colors)
    {
    PaintObject(hwnd,initRecArea(10,10,340,230),colors, hdc, BLACK, GREY); [color=Green]// Left rectangle(RADAR ANTENNA)[/color]
    PaintObject(hwnd,initRecArea(345,10,625,285),colors, hdc, BLACK, GREY); [color=Green]// Right rectangle(RADAR TRANSMITTER/RECEIVER)[/color]
    PaintObject(hwnd,initRecArea(10,235,340,335),colors, hdc, BLACK, GREY); [color=Green]// Second left (TARGET)[/color]
    PaintObject(hwnd,initRecArea(10,340,340,440),colors, hdc, BLACK, GREY); [color=Green]// Third left (GENERAL PARAMETERS)[/color]
    PaintObject(hwnd,initRecArea(10,445,690,780),colors, hdc, BLACK, KHAKI); [color=Green]// Yellow window (RESULTS)[/color]
    PaintObject(hwnd,initRecArea(390,550,680,770),colors, hdc, BLACK, KHAKI); [color=Green]// Yellow mini window (RESULTS)[/color]

    [color=Green]// Ranges small Rectangles[/color]
    PaintObject(hwnd,initRecArea(580,605,640,630),colors, hdc, RED, YELLOW);
    PaintObject(hwnd,initRecArea(580,635,640,660),colors, hdc, RED, YELLOW);
    PaintObject(hwnd,initRecArea(580,665,640,690),colors, hdc, RED, YELLOW);
    PaintObject(hwnd,initRecArea(580,705,640,760),colors, hdc, RED, YELLOW);
    }
    [/code]

    [color=Blue]and my timer[/color]

    [code]
    case WM_TIMER:
    {
    InvalidateRect (hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
    }
    break;
    [/code]
  • Sonx_hvn7Sonx_hvn7 Member Posts: 54
    : [code]: while (1) {
    :
    : //! clear backbuffer DC
    : FillRect(bmapDC,&clientRECT,BackGroundBrush);
    :
    : //! program logic code here... Render everything to
    : //! bmapDC (As shown in the above FillRect)
    :
    : //! done, so copy backbuffer to main window (hwnd is your window)
    : BitBlt(GetDC (hwnd),0,0,clientRECT.right,clientRECT.bottom,bmapDC,0,0,SRCCOPY);
    :
    : }[/code]:
    [color=Blue]
    Where does this code belong MT? ... WM_PAINT/WM_CREATE/WM_TIMER
    [/color]

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    My attempt to your suggestions
    : [color=Blue]
    You call BeginPaint() twice in one WM_PAINT cycle. Also, why you are releasing MemDC here at the end of drawing? You should do it in WM_DESTROY.

    Also, the enum was there to make the code readable. With 0 and 1 it is confusing now.
    : [/color]
    : [code]:
    : case WM_PAINT:
    : {
    : hdc = [color=Red]BeginPaint[/color](hwnd, &pntS);
    :
    : [color=Green]/* Retrieve the client's area width and height */[/color]
    : GetClientRect(hwnd, &clientRECT);
    :
    : [color=Green]/* Create memory DC on back buffer */[/color]
    : MemDC = MemoryDCForArea(hdc,
    : clientRECT.right, [color=Green]// width[/color]
    : clientRECT.bottom, [color=Green]// height[/color]
    : 0);
    :
    : [color=Green]/* *** Rendering screen data (User Interface) *** */[/color]
    : DrawArea(hwnd, MemDC, colors);
    :
    : [color=Green]// From menDC to screen[/color]
    : BitBlt(hdc,0,0,clientRECT.right,clientRECT.bottom,MemDC,0,0,SRCCOPY);
    :
    : [color=Green]/* Calculate & show results
    : output = ProcessData(hwnd);
    : DisplayResults(hwnd, hdc, &output, rsltFont);*/[/color]
    :
    : [color=Green]/* Delete/release memory DC on back buffer */[/color]
    : MemDC = MemoryDCForArea( [color=Red]BeginPaint[/color](hwnd, &pntS),
    : clientRECT.right, [color=Green]// width[/color]
    : clientRECT.top, 1); [color=Green]// height[/color]
    : [color=Red]// This whole ^^^ statement should be in WM_DESTROY.
    : // Also, when you releasing pass zeros as HDC and size.[/color]
    :
    : EndPaint(hwnd, &pntS);
    : }
    : return 0;
    : [/code]:
  • Sonx_hvn7Sonx_hvn7 Member Posts: 54
    [color=Blue]My changes to the code, still flickers... Notice am not using the BeginPaint() DC to create MemDC, this according to some advice[/color]

    [code]
    case WM_CREATE:
    {
    [color=Green]/* Retrieve the client's area width and height */[/color]
    GetWindowRect(hwnd, &clientRECT);

    [color=Green]/* Create memory DC on back buffer */[/color]
    MemDC = MemoryDCForArea(hwnd,
    clientRECT.right, [color=Green]// width[/color]
    clientRECT.bottom, [color=Green]// height[/color]
    cmdAllocateBackBuffer);

    [color=Green]/* *** Rendering screen data (User Interface) *** */[/color]
    PaintObject(initRecArea(10,10,340,230),
    colors, MemDC, BLACK, GREY);

    [color=Green]/* Screen refresh time */[/color]
    SetTimer(hwnd, IDT_REFRESH, REFRESHTIME, NULL);

    [color=Green]/* Create edit controls */[/color]
    EditBoxes(hwnd, cs);

    [color=Green]/* Create radio buttons */[/color]
    RadioButtons(hwnd, cs);
    }
    return 0;

    case WM_PAINT:
    {
    hdc = BeginPaint(hwnd, &pntS);
    BitBlt(hdc,0,0,clientRECT.right,clientRECT.bottom,MemDC,0,0,SRCCOPY);
    EndPaint(hwnd, &pntS);
    }
    return 0;

    case WM_TIMER:
    {
    InvalidateRect (hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
    }
    break;
    case WM_DESTROY:
    {
    KillTimer(hwnd, IDT_REFRESH);
    PostQuitMessage(0);

    [color=Green]/* Delete/release memory DC on back buffer */[/color]
    MemoryDCForArea(hwnd,
    0, [color=Green]// width[/color]
    0, [color=Green]// height[/color]
    cmdCleanupBackBuffer);
    }
    return 0;
    [/code]

    [color=Blue]Also, why does the client window losses its background color to black when i do double buffering?[/color]


    [code]

    // Double Buffering function
    HDC MemoryDCForArea (HWND hWnd, int iAreaWidth, int iAreaHeight, int iCommandWhatToDo )
    {
    static HDC hDC = NULL;
    static HDC hMemoryDC = NULL;
    static HGDIOBJ hMemDCBitmap = NULL;
    static HBITMAP hCanvasBitmap = NULL;
    static int iCachedWidth = 0;
    static int iCachedHeight = 0;

    switch (iCommandWhatToDo)
    {
    case cmdAllocateBackBuffer:
    //
    // Check if the cached area size already been done
    //
    if ( (iAreaWidth == iCachedWidth) &&
    (iAreaHeight == iCachedHeight) )
    {
    //
    // We already have the memory DC
    //
    return hMemoryDC;
    }
    else
    {
    //
    // Area have changed or we here for the first time.
    // Tell ourselves to clean up.
    //
    MemoryDCForArea (hWnd, 0, 0, cmdCleanupBackBuffer);

    // Prepare new buffer
    //
    hDC = GetDC(hWnd);
    hMemoryDC = CreateCompatibleDC (hDC);

    hCanvasBitmap = CreateCompatibleBitmap (
    hDC,
    iCachedWidth = iAreaWidth,
    iCachedHeight = iAreaHeight);

    hMemDCBitmap = SelectObject (hMemoryDC, hCanvasBitmap);
    return hMemoryDC;
    }
    break;

    case cmdCleanupBackBuffer:
    //
    // Reverse all we done when creating back buffer.
    //
    if (hMemoryDC != NULL)
    {
    SelectObject (hMemoryDC, hMemDCBitmap);
    DeleteObject (hCanvasBitmap);
    DeleteDC (hMemoryDC);

    hMemoryDC = NULL;
    hMemDCBitmap = NULL;
    hCanvasBitmap = NULL;
    iCachedWidth = iCachedHeight = 0;
    }
    break;
    }
    return hMemoryDC;
    }
    [/code]
«1
Sign In or Register to comment.