Анимация прокрутки Silverlight использует большое количество процессорного времени - PullRequest
2 голосов
/ 17 августа 2010

В нашем приложении у нас есть несколько прокручиваемых кредитов в ChildWindow. При отображении этого окна загруженность нашего процессора очень высока. Текст использует BitmapCache и аппаратное ускорение включено. Даже после удаления прямоугольника отсечения и тени от дочернего окна загрузка ЦП возрастает до 80-90%. Когда я включаю визуализацию области перерисовки, я вижу, что перерисовывается только прокручиваемый текст, поэтому я не уверен, почему процессор сходит с ума. Я попытался анимировать Canvas.Top и TranslateY свойство CompositeTransform, чтобы выполнить прокрутку.

Есть идеи о том, что может быть причиной того, что эта анимация настолько дорогая? Есть ли хорошие статьи, в которых есть рекомендации по оптимизации анимации в целом? Вот мой XAML:

<c:ChildWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:c="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               x:Class="OurNamespace.UI.Views.AboutWindow"
               Title="About Our App" Width="575"
               Height="330" Style="{StaticResource ChromelessChildWindowStyle}"
               mc:Ignorable="d" 
               MouseRightButtonDown="ChildWindow_MouseRightButtonDown" 
               Background="Black">
  <Grid x:Name="LayoutRoot" CacheMode="BitmapCache">
    <Grid.Triggers>
      <EventTrigger RoutedEvent="Canvas.Loaded">
        <BeginStoryboard>
          <Storyboard Storyboard.TargetName="CreditsTransform" 
                      Storyboard.TargetProperty="TranslateY">
            <DoubleAnimation To="-750" RepeatBehavior="Forever" 
                             Duration="0:0:30"/>
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </Grid.Triggers>
    <Image HorizontalAlignment="Left" VerticalAlignment="Top" 
           Source="/Assets/Graphics/SplashAbout/OurBackground.png"/>
    <Grid Height="150" Width="570" HorizontalAlignment="Right" 
          Margin="0,0,0,80" VerticalAlignment="Bottom">
      <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition/>
      </Grid.RowDefinitions>
      <TextBlock x:Name="AppVersionTextBlock" Margin="10,0" 
                 VerticalAlignment="Center" FontFamily="Arial" 
                 FontSize="12" Foreground="White" 
                 Text="{Binding VersionInfo, FallbackValue=Version 2.0.0}" 
                 TextWrapping="Wrap"/>
      <TextBlock x:Name="FirmwareVersionTextBlock" Margin="10,0" 
                 VerticalAlignment="Center" FontFamily="Arial" FontSize="12"
                 Foreground="White" Text="{Binding FirmwareVersion.Value, FallbackValue=Firmware Version 1.0.0}" 
                 TextWrapping="Wrap" 
                 Visibility="{Binding FirmwareVersionVisibility.Value}" 
                 TextAlignment="Right"/>
      <Canvas Margin="0" Grid.Row="1" x:Name="Viewport">
        <Canvas.Clip>
          <RectangleGeometry Rect="0,0,575,120"/>
        </Canvas.Clip>
        <TextBlock FontFamily="Arial" FontSize="12" Width="555" 
                   Foreground="White" TextWrapping="Wrap" Canvas.Left="10"
                   Text="{Binding Credits}" x:Name="Credits" 
                   TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
          <TextBlock.RenderTransform>
            <CompositeTransform TranslateY="0" x:Name="CreditsTransform"/>
          </TextBlock.RenderTransform>
          <TextBlock.CacheMode>
            <BitmapCache/>
          </TextBlock.CacheMode>
        </TextBlock>
      </Canvas>
    </Grid>
    <TextBlock Foreground="White" Text="{Binding CopyrightInfo, FallbackValue=© 2010 Our Company}" 
               TextWrapping="Wrap" Width="413" FontSize="10" 
               FontFamily="Arial" Height="44" HorizontalAlignment="Right" 
               Margin="0,0,30,21" VerticalAlignment="Bottom"/>
    <Button x:Name="CancelButton" Width="575" Height="330" Opacity="0" 
            Click="CancelButton_Click" HorizontalAlignment="Right" 
            Margin="0" VerticalAlignment="Bottom"/>
  </Grid>
</c:ChildWindow>

Обновление:

Проблема с процессором не была напрямую связана с самим ChildWindow, а с объектами DropShadowEffect, под которыми Silverlight расточительно повторно рендерил. Я добавил ответ, чтобы описать, как я с этим справился.

Ответы [ 4 ]

2 голосов
/ 17 августа 2010

При анимации текста в Silverlight вы должны установить для присоединенного свойства TextHintingMode значение «Анимированный» в вашем текстовом блоке. Для улучшения читабельности текста Silverlight обычно использует подсказки для сглаживания каждого глифа текста. Это может оказать значительное влияние на производительность при анимации текста, поскольку изменение приведет к пересчету того, как наиболее легко читаемый глиф, что может происходить со скоростью до 60 кадров в секунду в анимации.

<TextBlock TextOptions.TextHintingMode="Animated"
           FontFamily="Arial" FontSize="12" Width="555" 
           Foreground="White" TextWrapping="Wrap" Canvas.Left="10"
           Text="{Binding Credits}" x:Name="Credits" 
           TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
...
</TextBlock>

Если это не решит вашу проблему, я бы порекомендовал вам начать отладку производительности с XPerf . Есть хорошее руководство по использованию этого инструмента командной строки, чтобы увидеть, на что тратится большая часть вашего процессорного времени во время работы части приложения Silverlight. Следует обратить внимание на то, сколько процессорного времени тратится на agcore.dll, npctrl.dll и coreclr.dll . Если проблемы с производительностью связаны с перерисовкой, большая часть процессорного времени, скорее всего, расходуется на agcore.dll, так как это делает большую часть работы с графикой для Silverlight. Затем вы можете углубиться в это и увидеть конкретные функции в agcore.dll, которые чаще всего вызываются во время выборки. Это часто может помочь вам понять, какие части вашего кода вызывают снижение производительности и как вы можете оптимизировать.

2 голосов
/ 20 августа 2010

Оказалось, что на самом деле не содержание нашего ChildWindow вызывало высокую загрузку ЦП.Вместо этого многие DropShadowEffect объекты позади ChildWindow истощали наш процессор.Очевидно, Silverlight действительно глуп, когда дело доходит до логики перерисовки его эффектов.

В конце концов, мы собираемся отказаться от использования эффектов, что очень печально.Но так как это большая работа, тем временем я создал удобное свойство присоединенного свойства и служебные методы для временного отключения эффектов и их повторного включения:

private static IDictionary<UIElement, Effect> _effects = 
    new Dictionary<UIElement, Effect>();

public static readonly DependencyProperty CanDisableEffectsProperty = DependencyProperty.RegisterAttached(
    "CanDisableEffects", typeof(bool), typeof(FrameworkUtils),
    new PropertyMetadata(onCanDisableEffectsChanged));

public static bool GetCanDisableEffects(DependencyObject obj)
{
    return (bool)obj.GetValue(CanDisableEffectsProperty);
}

public static void SetCanDisableEffects(
    DependencyObject obj, bool value)
{
    obj.SetValue(CanDisableEffectsProperty, value);
}

private static void onCanDisableEffectsChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    var enable = (bool)args.NewValue;

    var uiElement = obj as UIElement;
    var fElement = obj as FrameworkElement;

    if (uiElement != null)
    {
        if (enable && uiElement.Effect != null)
        {
            _effects[uiElement] = uiElement.Effect;
        }
    }

    if (fElement != null)
    {
        Action applyToChildren = () => uiElement.GetVisualChildren()
            .ForEach(c => SetCanDisableEffects(c, enable));

        applyToChildren();
        fElement.Loaded += (s, e) => applyToChildren();
    }
}

public static void DisableAllEffects()
{
    _effects.Keys.ForEach(ui => ui.Effect = null);
}

public static void EnableAllEffects()
{
    _effects.ForEach(p => p.Key.Effect = p.Value);
}

Итак, я сделал это, прикрепив свойство CanDisableEffectsко всем предметам, содержащим эффекты.Затем, когда наши дочерние окна с анимацией загружены, я вызываю метод DisableAllEffects.Затем, когда происходит событие ChildWindow.Closed, я вызываю EnableAllEffects для повторного включения.Так как наложение для ChildWindow в любом случае затемняет фон, удаление эффектов не заметно, но снижается загрузка ЦП.

Я принимаю ответ Дэна Оклера, так как он отвечает на мой вопрос в том виде, в котором его задавали.Я опубликовал этот ответ, чтобы помочь всем, кто испытывает проблемы с эффектами.

0 голосов
/ 26 марта 2011

Другое решение может состоять в том, чтобы реструктурировать элементы управления, примененные с эффектами тени. Скажем, если вам нужна граница, чтобы иметь эффект тени, вы можете обернуть ее сеткой и создать другую рамку, которая находится позади первой. Таким образом, у элемента drop shadow нет дочерних элементов, поэтому он не будет перерисован.

0 голосов
/ 17 августа 2010

Возможно, вы устанавливаете режим кэширования на BitmapCache слишком агрессивно. Использование BitmapCache может снизить производительность в некоторых случаях.

Некоторые основные рекомендации по проблемам производительности Silverlight можно найти по адресу http://msdn.microsoft.com/en-us/library/cc189071(VS.95).aspx (включая некоторые советы по использованию BitmapCache).

...