Повышение производительности Bing Maps для Silverlight при использовании MapLayer со многими дочерними элементами

#.net #silverlight #performance #bing-maps

#.net #silverlight #Производительность #bing-maps

Вопрос:

У меня есть приложение Silverlight, которое использует Bing Maps. Мне нужно показать ~ 10 000 маркеров по всей поверхности Земли. Эти маркеры представляют собой простые Path экземпляры, большинство из которых имеют одинаковую форму, хотя имеют разные повороты, которые применяются через RotateTransform .

Сейчас производительность составляет около 6 кадров в секунду. Я пробовал различные подходы к улучшению ситуации, но ничего не улучшается.

Логическое дерево напоминает:

 <Map>
  <MapLayer>
    <Path ... />
    <Path ... />
    <Path ... /> <!-- and so on, several thousand times -->
  

Однако Path экземпляры добавляются программно. Их позиции устанавливаются с помощью привязок, хотя эти привязки не срабатывают во время панорамирования и поэтому не должны способствовать проблемам, которые я вижу.

 var path = new Path { /* ... */ };
path.SetBinding(MapLayer.PositionProperty, new Binding("Location")
{
    Source = asset,
    Mode = BindingMode.OneWay
});
  

Я попытался включить ускорение графического процессора и кэширование растровых изображений для MapLayer и подтвердил, что это было правильно включено, наблюдая за статистикой графического процессора в Process Explorer, а также включив счетчики кадров в секунду / наложение кэша на хост Silverlight. Это практически не повлияло на FPS. Даже если бы это помогло, невозможно кэшировать растровые изображения выше некоторого порога (2048 квадратных пикселей?), И поэтому, когда вы немного увеличиваете масштаб, он возвращается к программному рендерингу.

Копая немного дальше с профилировщиком процессора dotTrace, кажется, что ограничивающим фактором являются повторяющиеся вызовы Measure / Arrange . Я подозреваю, что это связано с тем, что, когда дочерние элементы слева от карты прокручиваются достаточно далеко влево, они оборачиваются вокруг Земли и перемещаются в крайний правый угол (и наоборот), что приводит к аннулированию макета, а также любого растрового кэша. Я не думаю, что есть простой способ заплатить этот штраф.

Кто-нибудь еще сталкивался с этой проблемой и в идеале нашел путь для исследования?

Несколько случайных идей, которые стоит попробовать:

  • Вывод непосредственно из MapLayerBase instead of MapLayer и реализация алгоритма компоновки самостоятельно, минуя свойство MapLayer.PositionProperty dependency и поддержку IProjectable totally .
  • Исследуйте использование MapItemsControl вместо MapLayer .
  • Проецирование дочерних элементов на изображения и рендеринг в виде более простых плиток.

Ответ №1:

Чтобы ускорить основные операции панорамирования и масштабирования, вы можете отключить (во время событий мыши) визуализацию слоев с маркерами, наследовав от MapLayer и подключившись к событиям ViewChange родительской карты следующим образом. Я забыл, где я нашел это изначально, поэтому, если владелец хочет получить кредит, продолжайте — очень полезно для поддержания, по крайней мере, последовательного панорамирования пользовательского интерфейса, когда у вас около 10 тыс. контактов. Помимо этого я бы использовал некоторую пользовательскую кластеризацию, которая, очевидно, больше задействована на уровне модели. 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
}
  

Комментарии:

1. Спасибо, Джаред. В итоге мы использовали что-то похожее на это. Мы также загружаем дочерние UIElements в MapLayer пакетами, что периодически освобождает поток пользовательского интерфейса, чтобы поддерживать адаптивность пользовательского интерфейса, что, похоже, немного помогло, хотя, конечно, когда есть много маркеров, для потоковой передачи требуется некоторое время.

2. Это помогло мне. Кстати, версия MapLayer для WPF не имеет ParentMap . Вы можете передать родительскую карту в качестве тега с привязкой ElementName .