During the summer I worked a lot with one of the latest addition to the Qt framework
,
the graphics view. The graphics view is basically a canvas to draw
2D-graphics, and replaced the QCanvas class from Qt 3.x. It was added
to Qt in version 4.2.
The application I worked on is used for displaying a sea map along
with various other maritime related information (and a lot of other
stuff that’s not really relevant). While the application functioned the
way it was, we wanted to replace our old (somewhat hackish) way of
representing the parts of the program as CSS-themed top-level widgets
and using a embedded graphics view to represent the entire UI of the
application. This approach would give us a better ability to integrate
the various parts of the application, as well as support transitions
between various view states of the program (and other nifty graphics
effects). With the recent addition of QGraphicsProxyWidget in Qt 4.4
this was finally an option, so we decided to give it a shot.
During the initial porting of the application, we did not notice
much in the way of performance loss, but as soon as we added more (and
larger) dynamical elements to the GUI, it started slowing down. This
led me to investigate why the application was slowing down, and how to
remedy the situation. The reason why is pretty simple: in order for Qt
to draw widgets inside the graphics view, it apparently has to render
them twice. Qt will first render the widget to an off-screen buffer,
and then render the content of the off-screen buffer to the graphics
scene as a texture. As soon as we knew why, we started looking at how
to optimize it.
Accelerating the Scene:
Qt allows you to use OpenGL to accelerate the scene. This can
greatly increase the performance, assuming the graphics card in your
computer has decent support for hardware accelerating 2D operations
(this is not the case for nVidia today). To enable OpenGL acceleration,
you will have to create a QGLWidget and tell the graphics scene to use
it as a viewport. Here’s an example:
QGLWidget* viewport = new QGLWidget(QGLFormat(QGL::DoubleBuffer | QGL::Rgba |
QGL::AlphaChannel | QGL::DirectRendering | QGL::SampleBuffers));
myGraphicsScene->setViewport(viewport);
Item Cache:
The graphics view allows you to independently set the cache mode for each item in the scene. Setting the cache mode to QGraphicsItem::DeviceCoordinateCache
will make Qt use the cached version of a item instead of redrawing the
item (twice) every time the scene is updated. This sort of cache will
not work if you need to transform (rotate, scale, shear, etc.) the
item. If you need transformation you should have a look at the QGraphicsItem::ItemCoordinateCache
cache mode.
Increasing the Pixmap Cache Size:
While caching the items works good when you’ve got a small amount of
small objects, I soon figured out that when adding larger items, or
just a lot of small ones, the drawing becomes slow again. It took me
some time, but I finally figured out why. When you tell Qt to use cache
for graphics items, it uses its internal QPixmapCache as a buffer for
the items. This is not explicitly mentioned in the documentation, only
a hint is given in the “See also”-section of the documentation. The
default size for this cache is 1024kb
, which explains
why it gets slower the more items you add. Increasing the cache size is
imperative if your application will use a lot of objects and you want
cache. You can achieve this by calling the static function QPixmapCache::setCacheLimit(int)
, just keep in mind it takes kilobytes as the argument.
Other tricks:
While these tricks are the ones that gave the greatest increase in
performance (a dogmatic conclusion, no real benchmarks where made) in
our application, there are several other functions you might want
to look at:
QGraphicsView::setOptimizationFlag
QGraphicsView::setViewportUpdateMode
QGraphicsScene::setItemIndexMethod
These functions may give an increase in performance, depending on the nature of the application.