Performance of rendering routines is quite crucial for a graphical application like QGIS. Therefore we should try to optimize it as much as possible. In following section you can find some benchmarks I have done on vector rendering, at the end there are several optimizations I think that might be useful to implement.

Area I've been studying was mainly drawing vector layer containing lines, but some optimizations should improve rendering speed in general. I haven't researched drawing of raster layers so far.

Benchmarks

For testing purposes I took a line vector layer that contains nearly 35 000 features (a roadmap). Such greater number of features can show where are the greater speed issues. Values I present here are averages of the values I have measured by doing refresh of map canvas while having the whole layer shown. I've tested on my AMD Athlon XP (1.83GHz) computer, with no CPU-intensive applications running. Map area in QGIS was about 900x530 pixels. These should just give an overview of the performance problems. Tests were done on debug version of QGIS with no optimizations.

Test 1: Vector provider speeds

OGR (shapefile) GRASS Postgres
1.97 s 1.53 s 1.26 s

|
Result:

  • PostgreSQL is as expected fastest
  • First draw of newly loaded GRASS layer takes cca double time to complete (is there any caching happening?)

Next tests were done with OGR provider (with shapefile driver), other providers give similar results with the same difference as above.

Test 2: on the fly projection

Projections turned off Using unprojected coordinate system Using projected coordinate system
1.97 s ca 7 s ca 11-14 s

|
Result:

  • on the fly projection is big performance killer
  • results vary greatly depending on SRS being projected to

Test 3: painting engines and antialiasing

Antialiasing off Antialiasing on drawPolyline call (aa. off) drawPolyline call (aa. on)
QPixmap 1.97 s 5.8 s ca 0.2 s 3.51 s
QImage 1.74 s 2.18 s cca 0.2 s 0.6 s
QGLPixelBuffer 2.03 s
0.24 s

|
Notes:

  • I have experimentally changed rendering to QImage instead of QPixmap, there's no option to change it at run-time
  • QGLPixelBuffer doesn't bring better performance as I expected, even doesn't support antialiasing

Result:

  • QImage gives slightly better results without antialiasing
  • QImage is MUCH better with antialiasing turned on
  • it seems that rendering call is very fast - most of the time is spent with feature retreival and preparing for drawing

Test 4: getting features with/without attributes

no attributes attr. for classification all attributes
OGR 1.97 s 2.24 s 5.6 s

|

Notes:

  • test was done by changing getNextFeature draw in QgsVectorLayer::draw()
  • results as expected
  • need to benchmark also Postgres and GRASS provider for comparison

Tips for optimizing

Here are some areas that might be optimized to speed up the rendering. I've done tests on some of the optimizations to prove they do a speedup (at least 0.1 sec)

  • [done] use QImage instead of QPixmap
    • main difference is that QPixmap is stored on X server, while QImage isn't. This means that QPixmap should be faster with drawing itself on screen, but QImage is faster for offscreen drawing
    • after rendering QImage is converted to QPixmap to get drawing in on screen faster
  • pen with transparency should be initialized only once, not every time it comes to drawing
    • where should be transparent pen created? in QgsVectorLayer, classes derived from QgsRenderer or in QgsSymbol?
    • how to propagage easily transparency settings?
  • use getNextFeature call that don't create new feature every time
  • resolve how caching of geometries should work - currently every render, cached geometry of features is deleted and then saved again. Also it's used only for layer editing, not drawing
  • optimize OGR provider - add caching (probably with chance of setting cache size) for features
  • review on the fly projections implementation to find out possible performance killers (or are those transformations really so intensive on CPU?)
  • possibly add a feature to reproject the layers when loaded (or permanently) so on the fly projection will have a faster alternative

Optimizations that don't work as I expected: :-)

  • remove QgsClipper as it's not needed anymore
    • reason 1: we should use QImage for drawing and it doesn't depend on X server
    • reason 2: drawing to QPixmap should be also OK as X11ZoomBug refers to Qt3 documentation that states that it will be resolved in Qt4
    • Actually it's still needed because of bug in rasterizer for QImage - it makes application crash when zoomed in too much. Thanks Gavin for finding this problem.

Comments by Tim:

  • Did you build spatial indexes for the ogr test? It can make a very big difference to performance.
    • MD: Building spatial index doesn't make sense in this case since all features from layer were drawn.
  • I agree the pen transparency stuff I did is not well optimised for performance
  • Getting rendering speed of rasters to the pre canvas merge state would be nice. I will revisit my render code in raster to see if I can draw stratight to qimage. If I reacall correctly it actually does draw to qimage then renders onto qpixmmap paint device afterwards.