Rewrite: redrawLayers

319 views
Skip to first unread message

emux

unread,
May 2, 2013, 5:54:34 AM5/2/13
to mapsfo...@googlegroups.com
Hi Thilo,

I use an arrow for my location icon in order to rotate it at the direction of the user, based on Sensor.TYPE_ORIENTATION
In order to refresh the arrow direction I call mapView.getLayerManager().redrawLayers() at onSensorChanged.

At rewrite branch  while the call of redrawLayers at onSensorChanged is frequent enough, the draw of my location icon is now following the same frequency.
As a result the arrow rotates with large time spaces of immobility between its draws, it seems to skip many redrawLayers calls.

For reference at main branch the call mapView.getOverlayController().redrawOverlays() is being called with the same frequency, but there the my location arrow is rotating quickly with sensor changes.

Any advice?

Regards.

Thilo Mühlberg

unread,
May 2, 2013, 3:19:58 PM5/2/13
to mapsfo...@googlegroups.com
In order to debug this problem effectively you first need to understand
the fundamental differences in the architecture of the rewrite branch.

In the old "main" branch redrawing was always done in the Android GUI
thread. To improve the responsiveness and make better use of modern
multi core CPUs this has been moved to a separate thread in the rewrite
branch. The GUI thread now only draws a single bitmap, no matter how
many layers the application has. The time needed for this is constant.

Besides the physical hardware and the Android version of your device
there are several parameters which influence the FPS ratio and the
general drawing performance. The size of the actual drawn canvas is one
of them. Currently the default overdraw factor is 1.5 which means that
2.25 the visible area of the screen is painted. You should try out
different values and measure the effect on your application:

mapView.getModel().frameBufferModel.setOverdrawFactor(double);

To avoid too fast redrawing there is a built-in throttle in the
LayerManager which limits the frame rate of the layer redrawing. So far
the frame time was set to 50 ms which equals a maximum of 20 FPS. I just
lowered that value to 30 ms which means 33 FPS max. Another important
parameter is the priority of the LayerManager thread.

Conclusion: fast layer redrawing is really crucial and complex. You
should use the method profiling of the DDMS (traceview) to debug your
application and detect potential performance bottlenecks. If you can
share your code with us (only a minimum working example please) I might
be able to give further hints for optimization.

Greetings,
Thilo
> --
> You received this message because you are subscribed to the Google
> Groups "mapsforge-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to mapsforge-de...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>


signature.asc

emux

unread,
May 3, 2013, 5:41:46 AM5/3/13
to mapsfo...@googlegroups.com
Thanks Thilo for your help!

I'd like to share my results after several tests.
I think that the cause of my problems is indeed the size of drawn canvas (though I did not find any solution).

I set
mapView.getModel().frameBufferModel.setOverdrawFactor(1);
because I have already map view's size larger that the screen. Specifically it is square with side hypot(w, h), in order to avoid black corners for my map rotation mode.
That extra map view size leads to slow layer redrawing.

But if I set map view size to be exactly the screen size (as it is supposed to be) the layer redrawing is back to normal. But this is with setOverdrawFactor(1).

Because if I leave overdraw factor to its default value 1.5 and map view size = screen size (as the default way to use the map view), then again I see slow layer redrawing.

For screen resolution / cpu cores reference, I perform the tests at a Nexus 7.

Regards.

emux

unread,
May 5, 2013, 4:17:46 AM5/5/13
to mapsfo...@googlegroups.com
Hi,

Playing with capacity of first level (memory) tile cache seems to improve the layer redrawing (Nexus 7).
Specifically the above behavior was with InMemoryTileCache(32).
Changing with InMemoryTileCache(64) the layer redrawing speeds up significantly.

Regards.

Ludwig

unread,
May 5, 2013, 5:21:15 AM5/5/13
to mapsfo...@googlegroups.com
Using a fixed-size first-level cache is wrong: if you choose it too big, then the app will run out of memory on smaller systems, if you choose it too small the effect is that tiles are purged from the cache even in one redraw cycle, meaning the cache is never used (but always written).

A better strategy is to caculate the cache size for a particular device and a mapView:

        double overdrawFactor = mapView.getModel().frameBufferModel.getOverdrawFactor()
        WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        double minimumCacheSize = (1 + display.getHeight() * overdrawFactor / Tile.TILE_SIZE)
                * (1 + display.getWidth() * overdrawFactor / Tile.TILE_SIZE);

This will give you the minimum cache size to hold all the tiles required for one redraw cycle (no movement, no zoom-ops) if your mapView takes the full screen.

It seems to be possible to double this minimum value (and round) without problems and for better performance.

Ludwig






Regards.

--

Thilo Mühlberg

unread,
May 5, 2013, 7:04:45 AM5/5/13
to mapsfo...@googlegroups.com
Rather than using the display size, the actual dimension of the MapView
should be used in this formula. But then also the size of the first
level has to be adjusted every time the size of the MapView changes.

Greetings,
Thilo

On 05/05/13 11:21, Ludwig wrote:
> Using a fixed-size first-level cache is wrong: if you choose it too big,
> then the app will run out of memory on smaller systems, if you choose it
> too small the effect is that tiles are purged from the cache even in one
> redraw cycle, meaning the cache is never used (but always written).
>
> A better strategy is to caculate the cache size for a particular device
> and a mapView:
>
> double overdrawFactor =
> mapView.getModel().frameBufferModel.getOverdrawFactor()
> WindowManager wm = (WindowManager)
> c.getSystemService(Context.WINDOW_SERVICE);
> Display display = wm.getDefaultDisplay();
> double minimumCacheSize = (1 + display.getHeight() *
> overdrawFactor / Tile.TILE_SIZE)
> * (1 + display.getWidth() * overdrawFactor /
> Tile.TILE_SIZE);
>
> This will give you the minimum cache size to hold all the tiles required
> for one redraw cycle (no movement, no zoom-ops) if your mapView takes
> the full screen.
>
> It seems to be possible to double this minimum value (and round) without
> problems and for better performance.
>
> Ludwig
>
>
>
>
> On 5 May 2013 16:17, emux <deve...@gmail.com
> <mailto:deve...@gmail.com>> wrote:
>
> Hi,
>
> Playing with capacity of first level (memory) tile cache seems to
> improve the layer redrawing (Nexus 7).
> Specifically the above behavior was with InMemoryTileCache(32).
> Changing with InMemoryTileCache(64) the layer redrawing speeds up
> significantly.
>
>
> Regards.
>
> --
> You received this message because you are subscribed to the Google
> Groups "mapsforge-dev" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to mapsforge-de...@googlegroups.com
> <mailto:mapsforge-dev%2Bunsu...@googlegroups.com>.
signature.asc

Ludwig

unread,
May 5, 2013, 7:43:42 AM5/5/13
to mapsfo...@googlegroups.com
Yes, and the problem is that the size of the mapview is not known until the screen is layed-out (and could change anytime, e.g. if optionally something else is displayed).

I think the better approach is to use the screen-size as a starting point and adjust if you know e.g. that half the screen is taken up by something else.

Thilo Mühlberg

unread,
May 5, 2013, 7:56:53 AM5/5/13
to mapsfo...@googlegroups.com
Well, no tiles are being downloaded or rendered until the layout has
been finished. So if the TileCache has an initial capacity of zero,
nothing should break (in theory at least).

The problem is that the TileCache interface currently does not define a
setCapacity(int) method because it is unclear how changing the capacity
of a TwoLevelTileCache should be implemented. If we implement this we
can add this method to the interface and then every tile cache could be
easily adjusted at each invocation of the onSizeChanged() method.

The problem with your approach is also that it relies on Android
specific classes which means we cannot add it to the mapsforge-map
module and reuse it for AWT/Swing based applications. But AWT/Swing
based applications need dynamic cache sizes as well.

Greetings,
Thilo

On 05/05/13 13:43, Ludwig wrote:
> Yes, and the problem is that the size of the mapview is not known until
> the screen is layed-out (and could change anytime, e.g. if optionally
> something else is displayed).
>
> I think the better approach is to use the screen-size as a starting
> point and adjust if you know e.g. that half the screen is taken up by
> something else.
>
>
> On 5 May 2013 19:04, Thilo Mühlberg <thilo.m...@gmail.com
> > <mailto:deve...@gmail.com <mailto:deve...@gmail.com>>> wrote:
> >
> > Hi,
> >
> > Playing with capacity of first level (memory) tile cache seems to
> > improve the layer redrawing (Nexus 7).
> > Specifically the above behavior was with InMemoryTileCache(32).
> > Changing with InMemoryTileCache(64) the layer redrawing speeds up
> > significantly.
> >
> >
> > Regards.
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "mapsforge-dev" group.
> > To unsubscribe from this group and stop receiving emails from it,
> > send an email to mapsforge-de...@googlegroups.com
> <mailto:mapsforge-dev%2Bunsu...@googlegroups.com>
> > <mailto:mapsforge-dev%2Bunsu...@googlegroups.com
> <mailto:mapsforge-dev%252Buns...@googlegroups.com>>.
signature.asc
Reply all
Reply to author
Forward
0 new messages