This post originated from an RSS feed registered with Agile Buzz
by James Robertson.
Original Post: Display, VisualWorks and Flicker - Part 4
Feed Title: Pollock
Feed URL: http://www.cincomsmalltalk.com/rssBlog/pollock-rss.xml
Feed Description: Pollock - the next VW GUI
Ok, you say, "So You Got Flicker, What Are You Going To Do About It?"
In Wrapper, VisualWorks has a damage display policy for each window. By default, it is exactly as stated in Part 2. However, you can change that policy to be DoubleBufferingWindowDisplayPolicy. The name isn't exactly right though. First let's describe what it does. When you have installed the DoubleBufferingWindowDisplayPolicy as the "Display Policy" for a window, when a "Damage" cycle is being processed, instead of taking the GraphicsContext from the window and doing all of the drawing for the Damage areas directly to the screen, it takes a Pixmap, and has all of the window's widgets draw on that. Finally, like the people who do animation, it then slaps that Pixmap to the screen, all at once (using the wonders of BitBlt under the hood of the OS).
Wait a second, there are some details in there that we haven't covered yet. A Pixmap is a non visual display medium. One could say it is a stand in for a Screen or Window. A Pixmap has a GraphicsContext, the difference is that when you "paint" on it, the result isn't displayed anywhere. Not one dot gets to your screen. So, when you want to draw on a Pixmap, you grab it's GraphicsContext, then you act exactly like you would with the "Direct To Window" GraphicsContext.
So, what the DoubleBufferingWindowDisplayPolicy does is instead of where it get's the Window's GraphicsContext and then asks the widgets to paint themselves on that, it gets a Pixmap and then asks the widgets to paint themselves on the Pixmap's GraphicsContext. Where the normal display policy would just stop there, the DoubleBufferingWindowDisplayPolicy then takes that Pixmap and as I said before, slams it to the screen.
As you can imagine, except for the fact that we have no control of when that slam to the screen takes place in terms of the monitor's blanking and retrace, the update will happen all at once. Worse case and unfortunately by law of averages a not uncommon case, it will happen during a monitor's raster, so some lower portion will be displayed and then the rest as monitor does it's next refresh. On the up side, unless your window is full screen, there is a reasonable chance, depending on what size your window is in relation to the rest of the screen, that when the Pixmap get's slapped to the screen, the monitor will be either above or below the area that the Pixmap is going to display, and you'll get no flicker at all. Also on the up side, if you are only redisplaying a portion of your window, you again have less chance of having your Pixmap slam to the screen during a raster.
All of these are good things. There remain two issues. First is the fact that the DoubleBufferingWindowDisplayPolicy only affects the Damage version of displaying. An Invalidate Now doesn't use the DoubleBufferingWindowDisplayPolicy, nor does "Direct Write." So, we only get partial joy here. Secondly, Pixmaps take up resources. They are pretty smart in that they don't take up much in the way of Smalltalk resources, but they do take up OS resources under the hood. Let's say you have a screen, like mine, which is 1900x1200. Now, let's create a Pixmap for a window that takes up the whole screen. Now, if you were using a monochrome monitor, VisualWorks will have asked the OS to create an object that has a size of at least 2,280,000 bits aka 285,000 bytes! Now, if you are using TrueColor display, with a depth of 32 bits (4 bytes), then you'd have asked the OS to create an object that is at least 1,140,000 bytes. If you have 10 windows full screen, then... well, you can do the math.
This last issue turns out to be one of those bad things. Particularly if you are running on one of the DOS based Windows platforms (95/98/98se/ME) which have an extreme limit to the amount of resources that the whole OS can have active at a time. In 95 it is 64Meg, in 98 and above, it is 128Meg. Watch the details of what I said. That's not just for a single application, that is for the whole OS at any given time. If you have ever used 95 (as I did a long time ago) you'll be familiar with the handy "Out Of Resources, Close An Application" that Windows throws at you from time to time. Isn't it lovely that even when you do close some applications, the problem often continues to show until you reboot? Isn't it lovely that the few times that closing applications does work, that suddenly your screen and the icons and the whole world suddenly acts as if were taken over by the long departed soul of Jackson Pollock himself?
So we have the following going on if we activate DoubleBufferingWindowDisplayPolicy.
It only has an effect for "Damage" displaying, not for "Invalidate Now" or "Direct Write"
It takes up OS resources which on some OSs is a dangerous thing to do
Because of #1, it only deals with about 30-50% of all displaying
This brings us down to the final question: What will Pollock do about this?
Would you believe wait for Part 5?
No, that would be too cruel.
Pollock will have full time Double Buffering for both Invalidate Now and Damage for those OSs that won't die if we turn it on (we can detect the OS and version number).
We will go back and remove ALL Direct Write code that currently exists in Pollock
We will add some new helper methods for Pollock Artists and panes to allow us to easily ask for invalidate subregions of a pane.
The result is, Pollock will only use displaying that can take advantage of Double Buffered display.
For OS's that can't remain reasonably stable (Windows 95/98/98se/ME) with Double Buffered display, they will, very simply, have flicker by default. I'll put in a "Force Double Buffering" flag somewhere so if a developer is willing to deal with the consequences, they can turn it on even for those platforms.
All of this will be good for Pollock. What it doesn't and can't do is force any Pollock tool or widget developer to only use the invalidate. If a developer wants to get the window's GraphicsContext and write to it, they are not prevented from doing so.
Why can't we do something there? Well, when an Invalidate Now or Damage cycle is executing, it starts by getting a GraphicsContext and then tells all sub-panes to display on it. In effect, after that call to "All My SubPanes Display On This GraphicsContext," control returns to the method that sent that. We then know that for the current draw cycle, all drawing is done. At that point, we can take the Pixmap that was being drawn to and send it to the window. However, if you are doing Direct Write, there is no place that the SYSTEM can know when you're done writing to that GraphicsContext.
There are two possible but unrealistic solutions to that. One, you could put a timer on the GraphicsContext and wait a bunch of milliseconds and if that time as elapsed, slap the Pixmap to the screen. The other is that you could instead NEVER give the direct GraphicsContext to the caller, and then demand that the user send some new message like "slapToScreen" when they're done. I'll leave it to you to figure out why the first one is an unmanageable horror. The latter one is bad because it demands that the developer know that they have to manage the resource just so. This makes it act differently when the GraphicsContext is writing to the screen vs when it is writing to a Pixmap. No sir, I don't like it.
Flicker and Emulated Widgets. Not a pretty or short subject, but never let it be said that I'm adverse to massive missives...