Thursday, December 18, 2008

OpenStreetMap rendering in Silverlight part XI

I had a look to see what was causing the serial drawing nature of the map, and it does appear to being caused by going via the Dispatcher. Silverlight supports multithreading, but any code that want to do UI work must do it in the main thread.

   // get the tile data
   quadrant = GetTileData();
   // we are creating UI elements, go via dispatcher
   map.Dispatcher.BeginInvoke(delegate()
   {
    Canvas canvas = new Canvas();

    Tile tile = new Tile(quadrant, canvas);

    // create instance of renderer
    ITileRenderer tileDrawer = RenderFactory.CreateRenderer(tile);

    // render the tile
    tileDrawer.Render();

    // tell the requester that the tile available to add to UI
    tileLoaded(quadrant.Path, tile);
   });
This means multithreading is great if you have complex calculations going in, and just need to update a simple UI. However for code where the complex work is the UI then you end up hitting a bottleneck. In the code for drawing London I am creating 24 tiles which in total contain about 60k poly-line/gons. I am effectively forced to create them in a serial fashion (my code is non-serial, but each tile has to wait for the last tile to stop using the Dispatcher).

On my PC it took about 30seconds to draw the map of London (once you have run it once all the data should be cached). I've made some changes to speed this up, so it now runs in 15 seconds. I updated the Silverlight Map demo to use this version (you may have to delete the XAP file from your cache to get the newer version)

For the production version I will not be trying to draw the whole of London like this, but it does suggest that progressive rendering will be needed to make the application more responsive. (Progressive as in only draw large items on the tile for the first pass, and then add detail in the subsequent background passes)

I'm not sure if this UI blocking issue will be a problem for other developers (most complex UIs are made using bitmap based tile operations so in this case they would only have 24 UI elements vs. my 60k) and for other non UI intensive applications the current threading model is fine. However it would be nice if this large block could be removed - for example only require the Dispatcher when the tile is added to a visible UI element...

No comments: