Silverlight 5: ужасные проблемы с производительностью с DropShadowEffect - PullRequest
1 голос
/ 10 февраля 2012

Информация об окружающей среде:

Windows 7 64bit SP1

6 ГБ памяти

Процессор Intel® Core ™ TM i5-2400S @ 2,50 ГГц (4 ядра)

Silverlight 5

У меня очень ужасные проблемы с производительностью DropShadowEffect в Silerlight.

Я создал для него свернутую демоверсию: http://www.peterlee.com.cn/public/ShadowEffectTestDemo/ShadowEffectTestTestPage.html

Нажмите кнопку Zoom, чтобы попытаться увеличить холст (в форме звезды со DropShadowEffect). Вы обнаружите, что когда Zoom = 64, ваш процессор и память очень сумасшедшие.

Но если вы удалите DropShadowEffect, нажав кнопку Remove Effect, а затем увеличите его, все будет в порядке.

Однако, если мы используем TextBlock с DropShadowEffect, все в порядке. Вы можете попробовать это, нажав кнопку «Использовать TextBlock`.

Я не знаю, что Silverlight имеет дело с TextBlock и моей индивидуальной формой звезды для DropShadowEffect.

Пожалуйста, помогите мне. Спасибо.

UPDATE : Согласно ответам:

Я также пробовал это в режиме выпуска (теперь демонстрационная ссылка примера была построена в режиме выпуска);

Я также добавил ускорение GPA, предложенное ChrisF:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
  <param name="EnableGPUAcceleration" value="true" />
  <param name="source" value="ShadowEffectTest.xap"/>
  <param name="onError" value="onSilverlightError" />
  <param name="background" value="white" />
  <param name="minRuntimeVersion" value="5.0.61118.0" />
  <param name="autoUpgrade" value="true" />
  <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.61118.0" style="text-decoration:none">
     <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
  </a>
</object>

Вот ОБНОВЛЕННЫЙ код

MainPage.xmal:

<UserControl x:Class="ShadowEffectTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <Button x:Name="btnZoom" Width="75" Height="23" Content="Zoom"
                Grid.Column="0" Grid.Row="0"
                Click="btnZoom_Click"/>
        <Button x:Name="btnRemoveEffect" Width="100" Height="23" Content="Remove Effect"
                Grid.Row="0" Grid.Column="1"
                Click="btnRemoveEffect_Click"/>

        <Button x:Name="btnUseTextBlock" Width="120" Height="23" Content="Use TextBlock"
                Grid.Row="1" Grid.Column="0"
                Click="btnUseTextBlock_Click" />

        <Canvas x:Name="mainCanvas" Width="200" Height="150" Background="LightBlue"
                Grid.Column="1" Grid.Row="1" />
    </Grid>
</UserControl>

MainPage.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Windows.Media.Effects;

namespace ShadowEffectTest
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

// These are the points for a Star shape
            List<Point> points = new List<Point>()
            {
                new Point(0, 36.3634469292486),
                new Point(-12.3606797688474, 43.2188191057189),
                new Point(-24.7213595376948, 50.0741912821893),
                new Point(-37.0820393065422, 56.9295634586596),
                new Point(-34.4313595448175, 42.9654855127096),
                new Point(-31.7806797830927, 29.0014075667595),
                new Point(-29.130000021368, 15.0373296208095),
                new Point(-39.4200000080385, 5.31010468057062),
                new Point(-49.709999994709, -4.41712025966827),
                new Point(-59.9999999813794, -14.1443451999072),
                new Point(-46.0011100186002, -15.919247828416),
                new Point(-32.002220055821, -17.694150456925),
                new Point(-18.0033300930418, -19.4690530854338),
                new Point(-12.0022200620278, -32.3361808961241),
                new Point(-6.00111003101392, -45.2033087068145),
                new Point(0, -58.0704365175048),
                new Point(6.00111003101392, -45.2033087068145),
                new Point(12.0022200620278, -32.3361808961241),
                new Point(18.0033300930418, -19.4690530854338),
                new Point(32.002220055821, -17.694150456925),
                new Point(46.0011100186002, -15.919247828416),
                new Point(59.9999999813794, -14.1443451999072),
                new Point(49.709999994709, -4.41712025966827),
                new Point(39.4200000080385, 5.31010468057062),
                new Point(29.130000021368, 15.0373296208095),
                new Point(31.7806797830927, 29.0014075667595),
                new Point(34.4313595448175, 42.9654855127096),
                new Point(37.0820393065422, 56.9295634586596),
                new Point(24.7213595376948, 50.0741912821893),
                new Point(12.3606797688474, 43.2188191057189),
                new Point(0, 36.3634469292486)
            };

            uie = RenderBezier(points);

// This the evil for the performance issues (crazy memory and CPU)
            uie.Effect = ShadowEffect;
            uie.CacheMode = new BitmapCache();

            uie.SetValue(Canvas.LeftProperty, 25.0);
            uie.SetValue(Canvas.TopProperty, 25.0);

            mainCanvas.Children.Add(uie);
        }
        private UIElement uie = null;

        public Path RenderBezier(List<Point> ctrlPoints, List<List<Point>> innersCtrlPoints = null)
        {
            // Step 0: Merge ctrlPoints lists
            List<List<Point>> ctrlPointsLists;
            if (innersCtrlPoints == null)
            {
                ctrlPointsLists = new List<List<Point>>(1);
                ctrlPointsLists.Add(ctrlPoints);
            }
            else
            {
                ctrlPointsLists = new List<List<Point>>(1 + innersCtrlPoints.Count);
                ctrlPointsLists.Add(ctrlPoints);
                foreach (List<Point> list in innersCtrlPoints)
                    ctrlPointsLists.Add(list);
            }

            PathGeometry pg = new PathGeometry();
            foreach (List<Point> list in ctrlPointsLists)
            {
                // Step 0: check (Debug.Assert)
                Debug.Assert(list.Count % 3 == 1,
                    "public Path RenderBezier(IList<Point> ctrlPoints): number of control points is not 3n+1.");

                int n = (list.Count - 1) / 3; // Number of BezierSegments
                Debug.Assert(n > 0,
                    "public Path RenderBezier(IList<Point> ctrlPoints): at least one Bezier segment required.");

                // Step 1: Add BezierSegments to PathFigure
                PathFigure pf = new PathFigure();
                pf.StartPoint = list[0];
                for (int i = 0; i < n; ++i)
                    pf.Segments.Add(GetBezierSegment(
                                                        list[3 * i + 1],
                                                        list[3 * i + 2],
                                                        list[3 * i + 3]
                                                    ));

                // Step 2: Add PathFigures to PathGeometry
                pg.Figures.Add(pf);
            }

            // Step 3: Add PathGemotry to GeometryGroup
            GeometryGroup gg = new GeometryGroup();
            gg.Children.Add(pg);

            // Step 4: Set GeometryGroup as Path.Data
            Path path = new Path();
            path.Data = gg;

            // Step 5: Set some Path properties
//            if (ShowOutline)
            {
                path.Stroke = new SolidColorBrush(Colors.Black);
                path.StrokeThickness = 1.0;
                path.StrokeEndLineCap = PenLineCap.Round;
                path.StrokeLineJoin = PenLineJoin.Round;
                path.StrokeStartLineCap = PenLineCap.Round;
            }

            // Finally, return it
            return path;
        }

// This the evil for the performance issues (crazy memory and CPU)
        private static DropShadowEffect ShadowEffect
        {
            get
            {
                return new DropShadowEffect()
                       {
                           Color = Colors.Blue,
                           BlurRadius = 5,
                           Direction = 0,
                           ShadowDepth = 0
                       };
            }
        }

        private static BezierSegment GetBezierSegment(Point p1, Point p2, Point p3)
        {
            BezierSegment bs = new BezierSegment();
            bs.Point1 = p1;
            bs.Point2 = p2;
            bs.Point3 = p3;
            return bs;
        }

        public static readonly double[] ZoomingSteps = new double[]
        {
            1.0,
            1.5,
            2.0,
            3.0,
            4.0,
            6.0,
            8.0,
            12.0,
            16.0,
            24.0,
            32.0,
            48.0,
            64.0,
            128.0
        };
        private int index = 0;
        private void btnZoom_Click(object sender, RoutedEventArgs e)
        {
            if (index >= ZoomingSteps.Length - 1)
                return;

            ScaleTransform st = new ScaleTransform();
            st.ScaleX = st.ScaleY = ZoomingSteps[index++];

            btnZoom.Content = ZoomingSteps[index].ToString();

            mainCanvas.RenderTransform = st;
        }

        private void btnRemoveEffect_Click(object sender, RoutedEventArgs e)
        {
            index = 0;
            btnZoom.Content = "Zoom";
            uie.Effect = null;
            mainCanvas.RenderTransform = new ScaleTransform();
        }


// If I use TextBlock as the child UIElement, then everything is okay
//            path = new TextBlock() { Text = "Text Block" };
        private void btnUseTextBlock_Click(object sender, RoutedEventArgs e)
        {
            mainCanvas.Children.Remove(uie);

            index = 0;
            btnZoom.Content = "Zoom";
            uie = new TextBlock() { Text = "Text Block" };

            mainCanvas.Children.Add(uie);

            uie.Effect = ShadowEffect;
            uie.CacheMode = new BitmapCache();
            mainCanvas.RenderTransform = new ScaleTransform();
        }
    }
}

Ответы [ 4 ]

0 голосов
/ 24 апреля 2012

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

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

0 голосов
/ 10 февраля 2012

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

0 голосов
/ 10 февраля 2012

Я думаю, что проблема здесь:

uie.CacheMode = new BitmapCache();

Как правило, это заставляет silverlight попытаться кэшировать растровое изображение в памяти, чтобы компьютеру не нужно было повторно визуализировать элемент в вашем приложении.

Ищите здесь советы по производительности Silverlight:

http://msdn.microsoft.com/en-us/library/cc189071(v=vs.95).aspx

0 голосов
/ 10 февраля 2012

Я попытался запустить WinDbg для вашего приложения и сбросить его.вот результат . Теперь не волнуйтесь, я тоже не понимаю и половины этого.Но я пошел немного глубже, и, кажется, существует большое количество массивов / списков, где по меньшей мере 80% равны нулю.

Возможно, вы захотите посмотреть WinDbg самостоятельно.Вот небольшое Учебное пособие

Я постараюсь взглянуть на него утром, если смогу, и я надеюсь, что помог вам хотя бы в правильном направлении

...