Модераторы: gambit, Kefir, Partizan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> WPF. Тормоза анимации. Неясны причины, custom container vs ScrollViewer 
:(
    Опции темы
AleksPingvin
Дата 5.1.2012, 14:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 62
Регистрация: 20.5.2011

Репутация: нет
Всего: нет



Долго я мучался, но так как не смог найти решения спрашиваю вас, дорогие друзья.

Начнем с того, что имеется: есть клиентский компьютер с двумя видеокартами и 4-мя FullHD мониторами. На каждый монитор выводится список с тяжеловесными итемами (много прозрачностей, таблицы в собственных контейнерах и т.п.)
Каждый список должен скролироваться от итема к итема, с заданным периодом, при этом их высота может меняться со временем, или они могут вообще исчезать. 

Проблема: скролинг должен быть карусельный. До этого был ListBox у которого анимировался ScrollViewer через DependencyProperties которое изменяло ScrollVerticalOffset и впринципе все более-менее сносно ползало с 25% загрузкой CPU. Поскольку поступила задача оптимизировать расход ЦПУ и сделать карусель, то листбокс был выкинут и создан собственный класс, код которого представлен ниже. Так вот - анимация стала гораздо тормозней, хотя загрузка CPU не превышает 2-5%.

Код

public class ItemCarusel : Canvas
{

       static ItemCarusel ()
        {
            FrameworkPropertyMetadata offsetData =
                new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsArrange, new PropertyChangedCallback(OnOffsetYChanged));
            OffsetYProperty = DependencyProperty.Register("OffsetY", typeof(Double), typeof(ItemCarusel), offsetData , null);
        }
        private static void OnOffsetYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ItemCarusel container = (ItemCarusel )d;
            double value = (double)e.NewValue;
            container.OffsetY = value;
            
        }
        public Double OffsetY
        {
            set
            {
                this.SetValue(OffsetYProperty, value);
            }
            get { return (Double)this.GetValue(OffsetYProperty); }
        }
        public static readonly DependencyProperty OffsetYProperty;

protected override Size MeasureOverride(Size constraint)
        {
            double h = 0;
            foreach (Object childObject in Children)
            {
                Item gItem = childObject as Item;
                if (!gtem.IsMeasureValid)
                    gItem.Measure(new Size(constraint.Width, Double.PositiveInfinity));
                h += gItem.DesiredSize.Height;
            }
            this.Clip = new RectangleGeometry(new Rect(new Point(0, 0), constraint));
            new Size(constraint.Width, h);
        }
       
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double y = 0; 
            double realY = y - OffsetY; 
            double minY = 0;

            for (int i = 0; i < Children.Count; i++)
            {
                Item gItem = Children[i] as Item;
                MyData data = gItem.DataContext as MyData;
                
                if (realY + gItem.DesiredSize.Height <= 0 || realY >= C_HEIGHT)
                    data.Rendered = false;
                else
                    data.Rendered = true;
                
                gItem.Arrange(new Rect(0, realY, gItem.DesiredSize.Width, gItem.DesiredSize.Height));
               
               
                y += gItem.DesiredSize.Height;
                realY = y - OffsetY;
                if (i == 0)
                    minY = realY;
                                
            }
            
            if (realY >= 0 && realY <= this.RenderSize.Height)
            {
                //Если в отрендереном контейнере имеется свободное место для айтемов, то дорисовываем их 
                for (int i = 0; i < Children.Count; i++)
                {
                    Item gameItem = Children[i] as Item;
                    MyData data = gItem.DataContext as MyData;
                    if (data.Rendered || realY > this.RenderSize.Height)
                        break;
                    gItem.Arrange(new Rect(0, realY, gItem.DesiredSize.Width, gItem.DesiredSize.Height));
                    y += gItem.DesiredSize.Height;
                    realY = y - OffsetY;
               }
            }
            return arrangeSize;
        }
}


Вопрос как всегда простой - почему и что делать?
PM MAIL   Вверх
Gvozdin
Дата 5.1.2012, 17:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 172
Регистрация: 9.8.2011
Где: Москва

Репутация: 8
Всего: 8



1. используйте профайлер что бы посмотреть куда тратиться процессорное время

2. код контрола на первый взгляд нормальный, только непонятно зачем устанавливать Clip

3. ответ тоже как всегда, давайте солюшн с воспроизведением, в процессе может и сами все решите
--------------------
http://gvozdin.ru
PM MAIL WWW   Вверх
AleksPingvin
Дата 8.1.2012, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 62
Регистрация: 20.5.2011

Репутация: нет
Всего: нет



Прогнал профайлером на требуемой конфигурации, которая будет стоять у клиента и на более мощной тачке (на ней кстати тормозов нет), итог таков:
На слабой машине время выполнения ArrangeOverride = 13 ms, на мощной - 1,5 ms.

Я к несчастью не знаю как реализован ScrollViewer, но по видимому он не вызывает Arrange у контейнера (во всяком случае в ListBox-е), ибо в случае с ним на слабой машине нет тормозов.
Если это так, то я не могу представить не тормозную версию карусели. Обычно такие вещи не в wpf я делал так: брал холст равный двойной высоте от требуемой, рендерил итема на него, два раза, и дальше путем Clip-инга выводил все это куда-то. Но тут же так нельзя, я ведь не могу два раза отрендерить Сhild в разные места.

Есть идея выводить не тяжеловесные контейнеры, а их битмапы (я пробовал BitmapCached - не могло), но это сложно очень в реализации, ибо итема могут менять свои размеры в процессе анимации.

Есть идеи?
PM MAIL   Вверх
Gvozdin
Дата 8.1.2012, 20:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 172
Регистрация: 9.8.2011
Где: Москва

Репутация: 8
Всего: 8



1. Для отрисовки картинок вместо айтемов посмотрите http://blogs.msdn.com/b/kaelr/archive/2008...napshooter.aspx

2. 15ms это время одного выполнения ArrangeOverride у вашего контрола? Если это так то это как-то медленно, оптимизируйте

3. Оптимизируйте работу контрола в общем, посмотрите
3.a что бы небыло лишних и повторных вызовов MeasureOverride, как вашего контрола так и айтемов
3.b аналогично что бы не было непонятных вызовов ArrangeOverride

Вы ведь сами управляете анимацией и знаете когда должны происходить вызовы Measure и когда Arrange. Вставьте трассировку и смотрите что бы все было как вы хотите.


--------------------
http://gvozdin.ru
PM MAIL WWW   Вверх
AleksPingvin
Дата 9.1.2012, 09:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 62
Регистрация: 20.5.2011

Репутация: нет
Всего: нет



Буду смотреть... думать.... спасибо за ответы!



Это сообщение отредактировал(а) AleksPingvin - 9.1.2012, 09:59
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | WPF и Silverlight | Следующая тема »


 




[ Время генерации скрипта: 0.0720 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.