The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: Jimg on October 26, 2005, 04:16:54 PM

Title: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 26, 2005, 04:16:54 PM
I just spent the last week and a half trying to cure flicker problems in a dialog with a dozen edit boxes.  The problem occurs when the user resized the dialog and I internally resize the edit boxes to fit  ( along with some labels and group boxes).

I tried all the usual fixes to no avail.  Then I found some code by MichaelW designed to reduce tearing in a graphics app.  After first resizing all the controls without updating the screen, then using the directdraw function GetScanLine to wait for the vertical retrace/blanking and then doing an invalidaterect of the whole dialog solved the problem.

I have several questions about the code though-

I've never used directdraw before, and the information is very confusing.

First, this structure is created:

.data
    lpdd_1      dd 0
    scanLine    dd 0
    maxScanLine dd 0
    vbStart     dd 0
    vbEnd       dd 0
    temp        dd 0

Then a call to-
invoke DirectDrawCreate,NULL,ADDR lpdd_1,NULL

1. Does this create something that I have to ultimately delete before terminating my program to avoid memory leaks?


Next, there is a proc included that looks like this:

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


Which is always called with-

invoke DDGetScanLine, ADDR scanLine

2.  Is the relative locations of the data variables defined above important?  That is, is it a structure of some kind, or just a collection of variables?


Next, the following code is executed:

    mov   ebx, 100000
    .WHILE (ebx)
        invoke DDGetScanLine, ADDR scanLine
        .IF (eax == DD_OK)
            mov   eax, scanLine
            .IF (eax > maxScanLine)
                mov   maxScanLine, eax
            .ENDIF
        .ENDIF
        dec   ebx
    .ENDW

3.  I can't see where maxScanLine is used anywhere else, is this code necessary to initialize something I am not seeing? (Again, same as question 2, is the location of maxScanLine important because the whole data structure above is used somehow?)


And similarly, this code computes some more variables I'm not sure about needing-

    invoke DDGetScanLine, ADDR scanLine
    .WHILE (scanLine)
        invoke DDGetScanLine, ADDR scanLine
    .ENDW
    .WHILE (TRUE)
        invoke DDGetScanLine, ADDR scanLine
        .IF (eax != DDERR_VERTICALBLANKINPROGRESS)
            m2m   vbStart, scanLine
            inc   vbStart
        .ELSE
            .WHILE (eax == DDERR_VERTICALBLANKINPROGRESS)
                invoke DDGetScanLine, ADDR scanLine
            .ENDW
            m2m   vbEnd, scanLine
            .BREAK
        .ENDIF
    .ENDW




I can see where it might be interesting to see the results, but is it necessary to set up everything else?





Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: P1 on October 26, 2005, 04:24:55 PM
Disable the paint on your controls until you finish with the resize.  Then force the repaint.

This also has a speed benefit.

Regards,  P1  :8)
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Tedd on October 26, 2005, 04:26:30 PM
use LockWindowUpdate :U
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 26, 2005, 06:15:48 PM
Hi Tedd-

That was one of the many things I tried and there was still the ficker caused by the system erasing everything to the background (in gray) and then redrawing the control (in white) giving a gray flash.  I tried suppressing the erase, but it is absolute required because the controls change size, and the old position needs to be erased.

Hi P1-

It sound like you are suggesting I subclass all the controls, correct?  I briefly played with that to try to erase only those parts that would need erasing, but it was terribly complicated because of overlap of controls, etc.   Or are you saying that I can suppress all painting some other way?  I tried supperessing wm_paint in the dlgproc while I was resizing controls, and then sending a wmpaint to the dlgproc when I was done, but the controls were not repainted.   Doing an invalidaterect caused the controls to paint, but also returned the flicker.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: P1 on October 26, 2005, 08:22:25 PM
No, not exactly.

When I was loading controls with lots of data the re-paint/re-drawing was an issue.  So I temporarily disabled the re-draw.

invoke SendMessage TreeView1.hWnd, WM_SETREDRAW, FALSE, 0

Of course, you will need to enum all the controls to false, then to true.  I had no success to setting the windows' redraw to false.

Regards,  P1  :8)
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Tedd on October 27, 2005, 10:40:36 AM
Well it should only be erased once per re-paint, so unless you're re-painting every second, is it really that bad?

Quick thought: if the flicker is caused by filling the window with the background brush ('erase') and then redrawing everything on top of that, then maybe you should set the background brush to something more appropriate (gray in your case?) That should remove most of the noticable flicker.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 27, 2005, 12:51:51 PM
I don't think I'm explaining very well :'(

The app will change the size of the edit, static, and groupbox controls to fit the size of the dialog.  The user grabs the side of the dialog and resizes it.  As he is doing so, I get the WM_SIZE message and do a MoveWindow,eax,L,T,W,H,FALSE for each control with its new size and position.  The FALSE prevents any paint or redraw caused by anything I am doing.  When all the controls are set to their new position, I do a single InvalidateRect,hWin,0,TRUE to update all the changes.  I also tried using DeferWindowPos/EndDeferWindowPos with the exact same results.  When windows ultimately does the paint, it erases the area of the white edit boxes with the default background color and then repaint the new edit box in white.  The flash of gray background is quite visible and annoying where the edit box was and is.  I'll try to put together a simple stripped down example to look at.

After trying everything I could find, I was thrilled to find the simple solution of redrawing while the vertical retrace (blanking) is happening.  The erase and redraw all take place while the monitor is not actually drawing on the screen, so the user never sees any part of the intermediate steps.  The only real problem I see is that it requires directx.  How many people do not have directx in some form on their windows based computer?  I would have prefered to find some other way, but there doesn't seem to be any way to get this blanking interval that doesn't require an additional device driver as it is determined by looking at some port bits in ring 0.  Any thoughts on other ways to get the vertical blanking interval will be greatly appreciated.

Another possible method that I just haven't been able to find much information on is that there is a way to double-buffer a dialog in .net code.  I don't now or ever plan to use .net, but I would certainly be interested in anyway to double-buffer the dialog in normal code.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 27, 2005, 01:12:54 PM
Here's a somewhat stripped down test app.  Grab the right border and stretch it out.  To see the effect of waiting for the vertical blanking interval, uncomment the line near the end of the code that says-

call WaitForVerticalBlank



[attachment deleted by admin]
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Tedd on October 27, 2005, 05:46:19 PM
I don't think you can get vblank without hardware access.
I was going to suggest double-buffering, but can't really think how to do it... Ownerdraw the dialog box?
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: P1 on October 27, 2005, 08:31:48 PM
Jimg,

I have had a chance to look at your demo of your problem.

The re-size process erases and re-draw your controls, giving the flicker, when the window background is exposed.

Some ideas:

1.  Temporarily use white as the dialog back ground.  I sort of look for code on that, but it might be easier to say than to do.
2.  Reverse Idea, Temporarily change control(s) background to match window's background.  Also provides a visual feedback to the resizing process.
3.  Interleave the wait between resizing of controls.  It seems you can not resize all the controls without the next trace occuring.
4.  Some form of "invoke PostMessage, hWin, WM_SETREDRAW, FALSE, 0"  will control when the redraw occurs.  I had some interesting affects with the window when I was experimenting with it.

I am going with 2, easy with side benefits.

Regards,  P1  :8)



Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: P1 on October 28, 2005, 01:36:16 PM
5.  Set all the controls to Visible = False.

Regards,  P1  :8)
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 28, 2005, 02:03:05 PM
Ok, I looked at everything you suggested, even those in jest.  The most interesting is
"3.  Interleave the wait between resizing of controls.  It seems you can not resize all the controls without the next trace occuring."

This implies that you tried the vertical blanking and still got flicker.  On my machine, the vertical blanking code completely removed the flicker.  So I moved the   "wait for retrace/invalidate rect"   code to a routine, did a postmessage to my self (WM_USER) to let windows finish whatever it was in the middle of doing, and then did the wait/invalidate, and I got much worse flicker than originally.

Since I was able to remove the flicker in one instance, but the same code run at a slightly different time made it worse, it means the code itself did not solve the problem, it just did a time shift so the redraw didn't flicker.  Which would probably work differently on different computers, and so is no solution at all.  I'm trying to trace what goes on inbetween but no luck so far.

All I know for sure it that this is getting way too complicated :eek
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: MichaelW on October 29, 2005, 06:56:27 AM
Jim,

The code I posted was intended to get and display the maximum scan line, the vertical blank start and end scan lines, and the approximate refresh rate. Everything else was just a hoop I had to jump through to accomplish the task. I don't know the answer to your first question. I have seen code like this:

// Get a DirectDraw 1 interface
if (FAILED(DirectDrawCreate(NULL ,&throwAway, NULL)))
  throw (EXC_DDRAW_INIT);        // throw an exception
...
// release the DirectDraw1 interface
throwAway->Release();

But I have no idea how you would do this with MASM code.

I think the answer to your second question is on the page I used as my primary information source when I wrote the code:

http://www.gamedev.net/reference/articles/article757.asp#DD

And for your third question, I determined the value of maxScanLine so I could display it, nothing more.

I am surprised that you could not resize everything in one vertical frame. Even if your vertical refresh were running at 80Hz, you would still have 12.5ms. It might be worth mentioning that since you have access to the current scan line you can arrange to have more than a single vertical frame time to complete your update, without tearing or flickering. Problems with tearing or flickering are usually the result of the screen update overtaking the screen refresh, or the screen refresh overtaking the screen update. This causes either the lower (update overtaking refresh) or the upper (refresh overtaking update) part of the update area to appear first, followed by the other part during the next refresh cycle. Assuming the screen refresh is overtaking the screen update, you could increase the time you have to complete the update by starting the update immediately after the current scan line passes the area you will be updating first.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on October 29, 2005, 06:30:08 PM
Thanks Michael-

I think the real secret here is to do the waitforverticalblank JUST before the screen will actually be updated.  Unfortunately, I have no idea when that is.  Somewhere buried in BeginPaint??  I need to do a lot more googling I guess.  I'm beginning to think part of the problem is the WM_CTLCOLOREDIT etc.  messages that occur before the WM_PAINT but I haven't figured out how to handle them yet.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 12, 2007, 10:40:42 PM
And here I am again, a year and a half later, with a new program with lots of controls, and the same flickering problem I never solved.  Anyone have a sure-fire answer yet?
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: u on April 13, 2007, 02:51:54 AM
Uhm... simply make your dialog/window have the WS_CLIPCHILDREN flag.

No acrobatics (like DDraw scanlines, or using bad funcs like LockWindowUpdate) are necessary.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: BogdanOntanu on April 13, 2007, 08:21:52 AM
The solution is diferent for standard GUI or for Games/Directdraw.

For games / directdraw just use a back buffer and this will eliminate any kind of flicker

For GUI with lots of controls the solution si more complicated. Check this:
http://win32assembly.online.fr/source0.html

Search for ESF (Eliminate Screen Flicker) program sample. But for many controls it will be a lot of work :D
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: u on April 13, 2007, 12:32:26 PM
I strongly oppose against doing a lot of work just because you've added more controls to your window. [I'm lazy...] . So, let's dissect flickering:
Your window has a hbrBackground - a brush.
When your window's client area has to be updated, Windows sends a WM_ERASEBKGND message. If you let DefWindowProc handle it, DefWindowProc  gets that brush, gets the window clipper (region)for the client area, and fills-in that whole area.
Then, Windows posts a WM_PAINT message. Note that it posts it, not sends. So... it's not rare for Windows to make your app wait 30-60ms before it actually starts processing the WM_PAINT msg. Meanwhile, the videocard redraws everything onscreen - and so for those 30-60ms it shows your window completely in one color.
So now, finally you start drawing on the window: the result of any intermediate operation that you complete (a large BitBlt for background, or a FillRect..) - is accessible by the videocard. If you're making long drawing operations, again you get flicker (showing of intermediate, non-final results).

Then finally after the window is drawn, its children get redrawn. If you had drawn just under a button, it'll flicker.  So, why not actually make GDI avoid drawing where children are!? It's as simple as that WS_CLIPCHILDREN flag.

If you're going to draw the whole background of your window at WM_PAINT, then make your window-class' hbrBackground be NULL, or simply don't let DefWindowProc handle WM_ERASEBKGND.
If you're going to draw a big bitmap, and then many more things on top of it - you _must_ use double-buffering. And it's way simpler than that ESF example. And it's completely disregarding how many child-windows (buttons and stuff) you have - as long as you use the WS_CLIPCHILDREN.

The only problem would be if the child-windows are semi-transparent (where the final output is an alphablend of the background window and the child window). But there you should use custom-drawn, custom-handled virtual-windows, with double-buffering anyway.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: ramguru on April 13, 2007, 01:15:34 PM
Just because Raymond Chen said something bad about LockWindowUpdate I won't reject that API, he just talks, I haven't seen any program written by him.
Style WS_CLIPCHILDREN will make trouble if there are any BS_GROUPBOX style windows in main program (though I have cure)
You can avoid sometimes double-buffer, but not when dealing with images, icons, the trick in flicker-free drawing is that there are no trick
  you just draw everything once, the main words here are everything & once: everything means whole update region ps.rcPaint
  and use InvalidateRect with bErase flag set to false...also it's worth to keep in mind that with this API you're defining update region (ps.rcPaint), so in WM_PAINT routine one should be sure to paint no bigger area than one is invalidating
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 13, 2007, 02:42:46 PM
Thanks guys.

First, I'm talking about making a normal GUI dialog behave.  Full graphics programs are pretty simple with back-buffering.  No problem.  Windows controls on a dialog, however are another matter, and that's what I would really like to cover here.

BogdanOntanu-  The "gui" solution looks like a full graphics solution to me.  He fully drawing everything from scratch.  It pretty much totally defeats the reason for using standard controls and yes, as you said, it is a LOT of work.  Not an appropriate solution for your everyday dialog with flicker problems.

Ultrano-  You had me real excited there for a moment.  Attach is the exe only of the earlier program with the dialog changed to WS_CLIPCHILDREN.  Even ignoring the problem with the group boxes for now,  the buttons don't get redrawn, and the outlines for the edits don't get redrawn, and fragments of the edit window itself gets left behind when resizing.   But I believe what you say about the timing.  I don't know how to set hbrBackground be NULL for a simple dialog control but I'll work on it, and try to redraw/clear the background in the WM_PAINT event.  The real solution seems to me to be a combination of the two methods.  Get windows to paint on a back-buffer, and when it is done, blit the whole thing to the real dc.  I have no idea how to get windows to do this however.

ramguru - I'm still digesting what you said, but I don't see how most of it applies to a standard GUI dialogs and controls.

[attachment deleted by admin]
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: hutch-- on April 13, 2007, 03:35:20 PM
Jim,

You can get it to redraw with the following code but the flicker is very bad.


        invoke SetClassLong,hWnd,GCL_STYLE,CS_VREDRAW or CS_HREDRAW


I have just had a look at your source in the earlier zip file and uses WinAsm Studio for its dialog styles which are set in hex rather than readable styles but I would try the two WNDCLASSEX styles "CS_BYTEALIGNWINDOW or CS_BYTEALIGNCLIENT" without the two REDRAW styles even if you have to set them with SetClassLong.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: ramguru on April 13, 2007, 04:34:53 PM
Your application is doomed to flicker, it's crappy EDIT controls and BS_GROUPBOX who are untreatable espeacially when resizing 20 windows at once.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 13, 2007, 04:55:32 PM
Hutch-

Ok, I tried this in the WM_INITDIALOG section

  inv GetClassLong,hWnd,GCL_STYLE
  or eax,CS_BYTEALIGNWINDOW or CS_BYTEALIGNCLIENT
  invoke SetClassLong,hWnd,GCL_STYLE,eax

I didn't seen any effect on the program I tried it on.

is it too late at this point to apply the style?
do I have to apply this style to every control in the dialog?

Sorry for the dumb questions, I've never bothered with this stuff before.



Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 13, 2007, 09:33:38 PM
Allright you gurus, you were flirting with the answer, just not quit all the way there.  I asked over on the WinAsm Studio forum, and Antonis Kyprianou, the creater of WinAsm Studio showed me how.  Yes, you need to set WS_CLIPCHILDREN on the dialog, but you also have to set WS_CLIPSIBLINGS on all the controls.  Then, don't try to fake out windows by doing the MoveWindow with bRepaint set to FALSE and InvalidateRect and trapping WM_ERASEBKGND or any of the other things people suggested, just do the MoveWindow with bRepaint set to TRUE, and that's it.  Simple and effective.  Here's the results.  It needs a little cleanup up on placement and zorder, but there is NO flickering on my computer.  Hooray!  :cheekygreen:

edit:  fixed static label problem just so it wouldn't distract from fixing the flickering problem.

[attachment deleted by admin]
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: u on April 13, 2007, 10:41:32 PM
Still, fix up those labels - they are prone to getting dirty.
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 14, 2007, 01:02:13 AM
absolutely.  I was so stoked I couldn't wait that long to post the answer!
Title: Re: Flicker, DirectDraw, WaitForVerticalBlank
Post by: Jimg on April 14, 2007, 03:18:03 PM
Ok, I fixed static label problem just so it wouldn't distract from fixing the flickering problem.  I'm surprised that a static doesn't recenter the text when its size changes.  I'm also surprised that I couldn't find an API to tell the static to update itself, I had to do a gettext and a settext to get it to update.  But that has nothing to do with fixing the flickering problem.