Qt 4.4 and painting performance

Posted by Bjørn Erik Nilsen on February 4, 2008 · 19 comments

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!

QShare(this)

No related posts.


19 comments

1 manyoso February 4, 2008 at 4:50 pm
 

You can be absolutely sure I’ll be trying this. The shared pointer solution so that we are not reinitializing the painter every time we create one sounds like a very good improvement. Also sounds like a no-brainer :)

2 NasaK February 4, 2008 at 4:50 pm
 

Hello there.. :) Nice to see, that the world’s best toolkit is getting even better.. ;) When all flickerness go away I have to buy you a beer.. I swear! ;o)

3 Jason February 4, 2008 at 5:23 pm
 

Any news on if a phonon video widget will work correctly (real-time video) in a QGraphicsView? I’ve been working with getting alpha going with QGraphicsItems and what I found is that the control won’t update unless there is a ItemChange notification.

4 Shawn Starr February 4, 2008 at 5:34 pm
 

It would be nice if some other TT people can test WOC with Plasma, theres some bugs I’ve encountered that bibr is also looking at.

5 David Nolden February 4, 2008 at 5:46 pm
 

That’s great news.

However painting-speed is also very slow even when showing menus and similar stuff in kde-4 with my geforce 7600 and proprietary drivers, and feels much better on my 3 years old notebook with integrated intel graphics, so there’s probably problems in combination with the nvidia driver. All I’ve heard about this is pure speculation, so maybe this would be a great place for further investigation ;)

6 Carina Denkmann February 4, 2008 at 5:55 pm
 

Three other render-performance related issues (for X11) that are floating around in my mind:

1.) Question: As far as I understand X11, with “composite support” enabled the window manager keeps a backing store for faster composition operations. Does Qt render directly into this buffer, or does it use its own (secondary) backing store?

2.) Suggestion: When a window is initially shown, it is rendered two times: Inactive, then Active (because Qt does not yet know, when/if the window is going to be activated by the window manager). However, this leads to flicker in _most_ cases, as the window manager usually obeys requests to activate a window. So reversing this heuristics might lead to less flicker.
Maybe there is even a NETWM property or X11 request, considering that modern managers have focus steal prevention, to find out if the window manager is going to honor the activation request.

3.) Suggestion: When a window is resized from a corner different than the bottom-right corner, the window manager copies the old contents to the new position, makes room for the new contents, then Qt redraws the whole window. It would flicker less and be faster when Qt would instruct the window manager to not copy the window contents on resizes. If I understand X11 right, this can be done using X11 Gravity flags.

Thanks and keep up the amazing work!

7 Remon February 4, 2008 at 6:23 pm
 

@David Nolden

This problem was noticed by a user of my application as well, and it appeared that Option “RenderAccel” was set to “false” in his xorg.conf. Enabling it made painting go really fast again :)

8 David February 4, 2008 at 6:45 pm
 

Hello,
is there a roadmap/release plan of QT4.4?

9 David Nolden February 4, 2008 at 7:21 pm
 

@Remon
Thanks for the tip, but RenderAccel is already set to true in my config file. I’ve tried out all the tips that are out there.

I’ve also noticed the same problem on my girlfriends new laptop: Geforce 8600 gt, fresh Kubuntu Hardy(I’m using opensuse on my own machine).

So the only common thing is the video driver.

10 mmmm February 4, 2008 at 7:54 pm
 

David Nolden:
This is problem in proprietary NVidia drivers, not in Qt. XRender performance on new NVidia cards is really bad.

11 Andreas February 4, 2008 at 8:14 pm
 

Shawn Starr: I haven’t forgot you :-) . Several of us are involved in testing KDE4 against Qt 4.4 to make sure we iron out these wrinkles. The WOC bug you reported is open in my task tracker ;-) .

12 liquidat February 4, 2008 at 9:17 pm
 

I wonder if this fixes the unfortunate flicker bug in KDE 4 described for example here (more detailed reports are linked inside the blog post). It sounds to me that the flicker bug described there is indeed a top-level window flicker.

13 Lee February 4, 2008 at 9:29 pm
 

Seems to me there’s a lot of confusion between flicker and repainting. I learnt the technique of vsync’d (synch’ed to vertical screen refresh) double- and triple-buffering as a as kid, back on amigas. Even back then, you could draw whatever you wanted, with as many paint events as you wanted, and have NO flicker. That’s because flicker and repaint are entirely separate operations, in a decent, double-buffered graphics system. The only cost for this is reduced frames per second, and that’s hardly crucial (until you start REALLY slowing things down) in a GUI. Todays composite, 3D-based GUIs should easily be able to do that. Seems to me that Qt isn’t properly taking advantage of the right setup, or isn’t specifying what the preferred setup should be, that’s all.

Anyway, on the separate issue of repainting, it sounds like you’re making impressive progress. It’s always great to hear about the stuff Qt is accomplishing. Well done :)

14 jospoortvliet February 4, 2008 at 9:34 pm
 

Liquidat: the issue I was talking about wasn’t flicker, but the drawing speed Bjorn is talking about. Redrawing at 3 frames a second – that sucked. I haven’t tried this snapshot yet, but as soon as KDE 4 switches to Qt 4.4 in it’s qt-copy, I’ll be seeing the diff.

Anyway, from this blog I understand the slow redrawing got better. Which is a good thing…

15 liquidat February 4, 2008 at 10:11 pm
 

jospoortvliet, thanks for the info, it seems that my remembrance deceived me.

16 Dan February 5, 2008 at 12:35 am
 

This is great improvements! Thanks.

If I may, I’d like to share some of my experience concerning slow painting in Qt. After using KDE4 for a few months I decided to look into the slow painting issue a little further. I found two things; First, the slow painting issue doesn’t have only one cause, no surprise. Second, one of the biggest improvement I was able to gain was by switching my Xorg over for Xgl. Yes, thats right, Xgl. I suspect this finding to be very specific to the fact I have an NVIDIA video card (8800GTS 640MB) and I’m running the latest NVIDIA proprietary driver (171.05) since I have often read that Xgl is slow as hell, and I even experienced it before with Intel and ATI cards. Also, Qt’s painting is a lot faster on my laptop which has an onboard Intel card. I also wanted to rule out compositing and tried both AIGLX (Xorg) and Xgl using KWin’s compositing mode with both XRender & OpenGL with all the various settings (Texture from pixmaps, shared memory, vsync, DRI, etc.). I found that compositing does slow down the painting in Qt, but way less than I expected.

Anyway, I believe the painting done in Qt to be quite good despite all the complaints (including myself) but it sure does have a lot to gain in order to match the painting speed I get under Vista (with compositing turned on). There just seems to be so many factors to affect the painting and its quite hard to nail them all down. This is simply just not all of Qt’s fault. :-)

I haven’t tried this new snapshot but will surely give it a shot tonight… Hopefully I’ll be surprised even more.

PS: I’m running Ubuntu Gutsy, with Xgl from Hardy (recompiled under Gutsy using apt-source). One of the fastest setting I found with my setup is with Xgl, no compositing and running Qt with the “Windows” theme (yes, I know). The Oxygen theme is somewhat slow, but so beautiful!

Cheers,
Dan.

17 Max Howell February 5, 2008 at 2:28 pm
 

Us here at Last.fm love you. xxx

18 Abdel February 5, 2008 at 3:24 pm
 

Hello,

Thanks for this work that will for sure help the performance of our Application (LyX).

Could you please elaborate on any improvement WRT text painting in Qt4.4? We still have a lot of user on Mac that complain about the poor speed of LyX since we switched to Qt4. I know there was some plan to switch the Mac text drawing engine someday, is that done now?

Thanks in advance.

19 Joe February 5, 2008 at 4:21 pm
 

@Lee

“Back in the day” graphics systems were less arrogant about flicker than they became with the advent of the “IBM compatible PC.” They provided “vertical blank interrupt” (VBI) signals which enabled banishing flicker entirely. I believe in today’s systems, presence of VBI is still hit and miss as a feature – they consider their raw power and speed sufficient without worrying about when the screen is in an inter-frame repaint pause.

There’s also the complexity of LCD displays and multiple refresh rates, things were simpler in Amigas that were all hooked up to standard TV sets.

Comments on this entry are closed.

Previous post:

Next post: