Qt 4.4 and painting performance

Although the Nokia acquisition has sucked away some time from development and made it somewhat difficult to stay focused, it doesn't mean we are halted. Our coffee machine is still up and running so there should be no need to worry.

I've been working on improving the painting performance lately, and I can tell you we've found nasty stuff. Resizing top-level windows has always been quite frankly horrible. With Qt3 it was snappy, but looking at it almost felt like staring at a strobe emitting a series of flashes. I'm pretty sure it was a useful feature at the discos back in the days. With Qt 4.1.4 and the backing store, we closed down that business and started focusing on usability and managed to get rid of most of the flicker at the cost of performance. We have continuously worked on improving the performance in the 4.2 and 4.3 series, but up until the alien technology, we struggled with flicker. With flicker-free child widgets in place, the only problem left was the sluggish top-level flicker, and that's what I've been working on recently. Tests showed that in worst-case an application could easily repaint itself three times per resize. So, with 100 resize events you could actually get 300 repaint events, which is 200 more than needed! Now imagine how this appears with an application having lots of complex widgets with expensive paint events. It's ridiculous!

This has now been fixed and reduced down to one repaint per resize, but the story doesn't end here. Another nasty thing I found was useless QPainter redirection operating on a global list involving several mutex locks and for loop iterations. More precisely (per paint event); 2 + X mutex locks and 1 + X for-loops, where X is the number of painters created during the paint event.

In addition, Jens found out how we could easily get rid of the black background fill appearing on resize on Windows Vista, and it really makes things look much better!

Aah, yes, and there's one more thing I'd like to mention. If we go few months back in time when the Widgets on The Canvas project was integrated, you can see how we were able to draw widgets directly onto the canvas with resolution independence. You might wonder how on earth we were able to do that. Well, there is an easy answer: The new QWidget::render overload taking an arbitrary QPainter :-) I really had to wrap my brain around lots of pain in order to achieve that. As you probably know, creating a QPainter on a widget boils down to initializing the paint engine using the backing store as the paint device. It just means we're doing exactly the same over and over again, which in turn means it's possible to use a shared painter! Yes! Easy, then we simply pass the shared painter in QPaintEvent and everything is fine. Or wait... Damn, then we would have to change every single paintEvent in Qt and customers code. Bad idea! So what we ended up doing instead was to hijack sub-sequent QPainter constructions/initializations and set the d-pointer of the newly created QPainter to the shared painter's d-pointer and push/pop the state accordingly. Everything happens behind the scene without requiring changes to existing code. When it comes to performance it means we don't have to initialize the paint engine (which is an expensive operation) each time we construct a QPainter. Great, isn't it?

Unfortunately, I don't have any demo to show you this time, but I hope it encourages you to download the latest snapshot and try it with your application. If you're brave, you could also run it against 4.1, 4.2 and 4.3 and see how we gradually become better and better. It clearly shows we're going in the right direction.

Performance is really important and needs serious attention, so you can expect to see more action on this topic in upcoming releases of Qt.

Happy hacking!


Blog Topics:

Comments