2008-03-13

Deferred Drawing and Scrollbars (Again)

Thinking that the deferred drawing idea was pretty neat, I decided to implement it. I now have a version of Woopsi that defers pretty much all VRAM writes until after all other processing has been done. Gadgets are only redrawn once, no matter how many times they attempt to draw themselves. The queuing system works really well.

Except when it doesn’t.

There’s a big problem with deferring drawing - some of Woopsi’s drawing functions must> be done in realtime. Scrolling is one example. The XORed window dragging rect is another. Both of these scenarios work in the deferred drawing model, but not as well as they do in the standard model. Scrolling leaves large gaps where content to be added is still queued up to be written later. Dragging an unfocused window results in the window’s border being inverted because VRAM writes get prioritised in the wrong order.

On top of those (relatively minor) problems, the deferred system doesn’t actually make Woopsi any faster. There’s no noticeable benefit at all. Scratch that idea, then. Shame, because there’s some nice code in there.

I thought that Jeff’s per-gadget idea would perhaps work better, and then realised that Woopsi already has this functionality. Here’s a code snippet that will resize and move a gadget with only one draw:


myGadget->setVisible(false);
myGadget->resize(10, 20);
myGadget->moveTo(10, 10);
myGadget->setVisible(true);
myGadget->draw();

The trick is to make the gadget invisible before entering a block of code that could potentially call the draw() method. Making a gadget invisible doesn’t remove it from the display; it just disables the draw() function.

It’s possible to cut the code down further by relying on the last function to call draw() for you:


myGadget->setVisible(false);
myGadget->resize(10, 20);
myGadget->setVisible(true);
myGadget->moveTo(10, 10);

Since we know that moveTo() will call draw(), we can make the gadget visible before calling that function.

Only one minor change was needed to make all of this work - gadgets now remember if they’ve been erased and won’t erase themselves a second time without a call to draw() first.

I’ve modified the MultiLineTextBox to use this system instead of the “auto drawing” business, as it’s the same thing. I’ve also fixed a bug in the AnimButton that I introduced the other day, in which the unclicked state was corrupted (swapped height/width values in the drawing function).

Lastly, the vertical scrollbar is now in place. This is a new class consisting of a slider and up/down buttons. There are still a few bugs in it that I need to clear up - the missing two pixels at the bottom of the slider, and the fact that the down arrow moves the grip at a different speed to the up arrow (despite using the same code) - but it more or less works. I think I need to make the slider optionally jump to the nearest whole value, too.

Comments

Jeff on 2008-03-13 at 20:07 said:

I guess its worth asking, are there any other combinations that are going to cause problems anyway? Why not add Gadget::changeDimensions(x,y,w,h) that just encapsulates the other two calls?

ant on 2008-03-14 at 10:10 said:

Good idea!

ant.simianzombie.com » Scrollbars and Greyscale Algorithms on 2009-11-23 at 23:09 said:

[…] have been one of the major banes of Woopsi’s existence for years. No matter what I’ve done, they’ve always been […]