I have a Silverlight app that's using Bing Maps. I need to show ~10,000 markers across Earth's surface. These markers are simple Path
instances, most of which are the same shape, although they have different rotations, which are applied via a RotateTransform
.
Performance is at around 6 FPS right now. I've tried various approaches to improve the situation, but nothing is getting higher than that.
The logical tree resembles:
<Map>
<MapLayer>
<Path ... />
<Path ... />
<Path ... /> <!-- and so on, several thousand times -->
The Path
instances are added programmatically however. Their positions are established via bindings, though these bindings do not fire during panning and so shouldn't be contributing to the problems I'm seeing.
var path = new Path { /* ... */ };
path.SetBinding(MapLayer.PositionProperty, new Binding("Location")
{
Source = asset,
Mode = BindingMode.OneWay
});
I tried enabling GPU acceleration and bitmap caching for the MapLayer
and validated that this was correctly enabled by observing GPU stats in Process Explorer, and enabling FPS counters/cache overlays in the Silverlight host as well. This had little or no effect on FPS. Even if this did help, it's not possible to cache bitmaps above some threshold (2048 pixels square?) and so when you zoom in a little, it falls back to software rendering.
Digging in a little further with the dotTrace CPU profiler, it seems that the limiting factor is repeated calls to Measure
/Arrange
. I suspect this is because as children to the left of the map are scrolled far enough to the left, they wrap around Earth and are moved to the far right (and vice versa), which causes invalidation of the layout, as well as any bitmap cache. I don'开发者_开发知识库t think there is an easy way around paying this penalty.
Has anyone else encountered this issue, and ideally found an avenue for exploration?
Some random ideas of things to try:
- Deriving directly from
MapLayerBase
instead ofMapLayer
and implementing the layout algorithm myself, bypassing theMapLayer.PositionProperty
dependency property and support forIProjectable
altogether. - Investigate using
MapItemsControl
instead ofMapLayer
. - Projecting children onto images and rendering as simpler tiles.
To speed up basic pan and zoom operations, you can disable (during the mouse events) the render of marker-rich layers by inheriting from MapLayer and hooking into the parent map's ViewChange events as follows. I forget where I found this originally so if the owner wants to take credit go ahead -- very useful for maintaining at least a consistent panning UI experience when you're up around 10K pins. Beyond that I would use some custom clustering, which is more involved at the model layer obviously. HTH
public class OnPanDisableRenderMapLayer : MapLayer
{
#region Private Properties
private Visibility _visibility;
#endregion
#region Constructor
///<summary>
/// Constructor
///</summary>
public OnPanDisableRenderMapLayer()
: base()
{
this.Loaded += (sender, evt) =>
{
Map map = (Map)base.ParentMap;
map.ViewChangeStart += (s, e) =>
{
_visibility = base.Visibility;
if (base.Visibility == Visibility.Visible)
{
base.Visibility = Visibility.Collapsed;
}
};
map.ViewChangeEnd += (s, e) =>
{
base.Visibility = _visibility;
};
};
}
#endregion
#region Public Properties
public Visibility Visibility
{
get { return base.Visibility; }
set
{
base.Visibility = value;
_visibility = value;
}
}
#endregion
}
精彩评论