2

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

Любые идеи относительно того, что может вызвать такую ​​анимацию настолько дорого? Есть ли хорошие статьи, которые содержат рекомендации по оптимизации анимации в целом? Вот мой 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> 

Update:

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

ответ

2

При анимации текста в Silverlight вы должны установить прикрепленное свойство TextHintingMode к «Анимированные» на TextBlock. Для улучшения удобочитаемости текста 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. Существует good tutorial при использовании этого средства командной строки, чтобы увидеть, где большая часть вашего времени процессора расходуется во время работы части вашего приложения Silverlight. Вы должны обратить внимание на то, сколько времени процессора затрачено на agcore.dll, npctrl.dll и coreclr.dll. Если ваши проблемы с производительностью связаны с перерисовкой, большая часть времени процессора, скорее всего, будет потрачена на agcore.dll, так как большинство работ, связанных с графикой, для Silverlight. Затем вы можете просверлить это и увидеть конкретные функции в файле agcore.dll, которые чаще всего вызываются во время вашего образца. Это может помочь вам понять, какие части вашего кода вызывают поражение производительности и как вы можете оптимизировать.

+0

Отличный ответ. Я попытался изменить режим текстовых подсказок на анимацию, и хотя это помогло немного, использование ЦП по-прежнему огромно. Я попробую XPerf. – Jacob

+0

Взрыв! Похоже, что XPerf недоступен в Windows XP, который по какой-то причине наша компания предпочитает придерживаться. Есть ли что-то подобное для XP? – Jacob

+0

Похоже, вы можете запустить xperf.exe, который используется для сбора данных CPU в Windows XP, но для запуска и анализа данных вам понадобится Server 2008, Vista или 7, чтобы запустить xperfview.exe. Это почти бесполезно, не имея возможности легко просматривать данные. Не уверен в другом способе без более новой ОС или какой-то виртуальной машины ... http://blogs.msdn.com/b/pigscanfly/archive/2008/02/24/xperf-support-for-xp. ASPX –

0

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

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

+1

Спасибо за ваш ответ, но я добавил «BitmapCache», потому что производительность была еще хуже без него. В частности, все окно постоянно обновлялось. – Jacob

2

Оказалось, что содержимое нашего ChildWindow не связано с высоким уровнем использования ЦП. Вместо этого многие DropShadowEffect объекты позадиChildWindow подрывали наш CPU. По-видимому, 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

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

Смежные вопросы