Eliminating Screen Flicker A Win32 ASM Tutorial By Chris Hobbs Table of Contents
IntroductionI am sure that everybody has noticed that annoying flicker when an application updates the display window. Granted, many programs do not have enough controls, or text, to have this be very noticeable. But, those that do...well, you get my drift. This is something that can become very annoying. Example: You are trying to paint a picture in a paint program. How long before you would toss the program out, if it flickered every single time you painted a pixel? Probably not long at all. Yet, we continually settle for this kind of effect in our normal applications, such as text editors, databases, etc. This does not have to be the case. Eliminating screen flicker and making your applications' window updates as smooth as possible is a very simple process. Yet, there is some underlying theory to what we will discuss, so pay close attention. Just kidding. If you want to skip the gory details feel free, it will work just as good. But, for those of you that really want to understand the problem and it's solution please read on to the next section. So...what is the problem?Well, in order to really understand the problem you must first see the problem. The tutorial package has this document, the source code, and the executable demo in it. So, if you haven't done so already, download the tutorial package and run the demo. Pay particular attention to how the screen flickers when you update with normal methods. DOWNLOAD TUTORIAL PACKAGEOkay. You ran it right. Well, for those of you who did, did you notice how the screen seems to go white then the update gradually occurs? That, my friends, is the essence of our problem. Gradually occurs? I am sure many of you are wondering if I have fallen off of the deep end. I assure you I have not. I only dive into shallow water, thank you very much. Getting back to the problem, this "gradual", albeit fast, update is the reason we see the flicker. Yes, this demo shows a very "extreme" case where the flicker is present. But I had to make sure you really understood how serious this problem could get. Even in an application that only updates some text in the window this could be very noticeable (read: annoying). I am sure many of you have guessed by now the true source of our problems. Say it with me: "Vertical Re-Trace". With that said, I guess I will have to explain what it is for those of you who are un-familiar with it. Vertical retrace, also known as Vertical Refresh, is the period in which your monitor is updating the display by spraying a beam across the rows of pixels. This beam is what lights up the "pixels" that you see. This creates a problem because we are not able to update the display quick enough to keep it from flickering. The white flash you see is the background color of the window, when it gets erased. To further accentuate the problem, as you re-draw the different portions on your window the screen flickers even more because you cross over those vertical-retrace periods. Thus, the user sees a partially filled Window for many micro-seconds before you finish drawing. It isn't long enough for a person to discern what is there. But it is certainly long enough to notice the flickering. That is what I meant by "gradually" occurring. See, I told you I wasn't off my rocker. To summarize, the problem is found in drawing during the vertical re-trace period. This step-by-step drawing process causes you to step over several vertical re-trace periods. This is what causes the flicker. For several micro-seconds the user sees a little bit, then a little more, then a little more, and then, finally, the entire screen. Now that we know where the problem lies, how do we correct this little nuance? We can't very well draw everything at once, can we? The solution please!Well, let's think about our problem and the different ways that we could solve it. Hmmm, we could only erase a small portion of our window, and update just that piece. This would work fine, if we only had one or two things to update, and those things had a static size. But, more often than not, we have many things in our window that we need to update, and those things can change size. So, that means, we would have to erase the entire window during re-paint. And, we still have to draw step-by-step. There is no way around that simple fact. Lucky for us, this type of problem was solved ages ago by game programmers. The technique is called back-buffering, or double-buffering. The premise behind it is as simple as can be. First, you create a surface that doesn't get displayed, and is the same size as the one you want to display. Then, you fill it with the background color. After that, you draw, as you normally would, only onto this special surface you have created. Once you have finished drawing everything needed, you take what is on the back-buffer and draw it onto the window you are using. This single draw happens quick enough to stay out of the vertical re-trace period. You know what that means, don't you? NO FLICKER! Just dandy. We have an answer to our problem, but exactly how do we implement something like this in Windows? The answer is pretty simple once you have seen it. So, I suggest you head on to the next section. Once there, I will take you through each, and every, step of creating a flicker-free application. Not a single detail will be withheld. And, you can follow along in the source code I provided, if the little snippets of code are not enough. Step - By - StepHere we are, ready to confront the evil screen flicker, and vanquish it once and for all. So...where do we start, and where do we go? Well, let's go through this a step at a time, until we have reached the end. That is usually the best way to tackle a problem. STEP 1: The very first thing that we need to do is obtain a handle to the Device Context of the Window that needs updating. This is the same as you would normally do for any application, when you want to update the window. We are also going to obtain the dimensions of our client area. We will be using these in the next step.
STEP 2: Next, we need to create a compatible Device Context, and a compatible bitmap, of the proper size. Once we have those created, we select the bitmap into the DC we created. This DC is, essentially, our back-buffer. You should always preserve the object that is returned to you by SelectObject(). In this case there should be nothing there, but better safe than sorry. Besides it is a good habit to get into. We do not want memory leaks!
STEP 3: We now have everything setup that we are going to to need to. So, the next obvious step is to fill our new bitmap with the background color. This, in essence covers up, or erases if you prefer, whatever was on the main window when we draw to it. If we neglected this process some old lines, or pieces of text, could be left sticking out. Once we have done that, you can draw everything that you need to draw on the screen. I prefer to have it in a separate routine, in this case called "DrawWindow". Just make sure you use the DC you created and not the one you retrieved from the window.
STEP 4: After drawing everything that we need onto the back-buffer, we need to display it somehow. It turns out we can use one very simple call to "Blit" the contents of our Bitmap in the back-buffer onto the DC of the window we are updating.
STEP 5: Finally, we just need to select all of our old objects back in to the DC's, and delete anything we created. Then we are finished. We will have successfully implemented a back buffering system in our little application.
NOTE: You may also want to return a TRUE value in the WM_ERASEBKGND message. If you do not, Windows will still try erasing whatever is on your display. This isn't a must....just a little tip. That's a wrapI hope you enjoyed and learned from this small tutorial. This was a very simple concept to implement in code as you saw above. Yet, the results, as those of you who ran the demo saw, are staggering. It is hard to believe that something so simple can have such an amazing effect on the outcome of the entire project. As I stated elsewhere, I have included the source code, in HTML format, with this tutorial package. You may feel free to use it however you wish. Or, you can use it as a guide when developing your own code. I have a few coding quirks that I tried to outline in the top of the source code. But, alas, I can not account for all of my weirdness. So if something just seems a bit too odd, send me an e-mail and I will clarify whatever the problem is. As many of you have most likely guessed, things like this are just the tip of the iceberg. There is so much more that you can do with Assembly Language. It is truly astounding. Yet, it all comes down to making the best programs possible. Every time you sit down, and write a piece of code, you should try and better yourself. Don't worry about keeping up with the Jones'. Just do your best and the rest will fall into place with time. A powerful language, such as Assembly, deserves to have the best programmers in the world using it. Do not let the language down. Make the most of it. Chris Hobbs |