【问题标题】:Silverlight 5: Terrible Performance Issues with DropShadowEffectSilverlight 5:DropShadowEffect 的严重性能问题
【发布时间】:2012-04-24 21:26:06
【问题描述】:

环境信息:

Windows 7 64 位 SP1

6GB 内存

Intel(R) Core(TM) i5-2400S CPU @ 2.50GHz(4 核)

银光 5

我在 Silerlight 中使用 DropShadowEffect 时遇到了非常糟糕的性能问题。

我为它创建了一个最小化的演示: http://www.peterlee.com.cn/public/ShadowEffectTestDemo/ShadowEffectTestTestPage.html

单击按钮Zoom 尝试缩放画布(带有DropShadowEffect 的星形)。你会发现Zoom = 64的时候,你的CPU和内存都很疯狂。

但是,如果您通过单击Remove Effect 按钮删除DropShadowEffect,然后对其进行缩放,一切都会好起来的。

但是,如果我们使用 TextBlockDropShadowEffect,一切都很好。您可以通过单击“使用 TextBlock` 按钮来尝试。

我不知道 Silverlight 正在处理 TextBlock 以及我为 DropShadowEffect 自定义的星形。

请帮帮我。谢谢。

更新: 根据回复:

我也在Release模式下尝试过(现在示例演示链接是在Release模式下构建的);

我还添加了 ChrisF 建议的 GPA 加速:

<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();
        }
    }
}

【问题讨论】:

    标签: silverlight performance effect


    【解决方案1】:

    我已尝试针对您的应用程序运行 WinDbg 并转储应用程序。这是Result. 现在别担心,我也不懂一半。但我更深入一点,似乎有大量的数组/列表,其中至少 80% 为空。

    您可能想亲自查看WinDbg。这是一个小Tutorial

    如果可以的话,我会在早上尝试查看它,希望我至少在正确的方向上帮助了你

    【讨论】:

    • 嗨 Rikkos。我尝试安装 WinDbg,但在安装 Windows SDK 时遇到了一些问题。至于原来的问题,我觉得可能是 Silverlight 的渲染问题。但我就是不明白为什么 TextBlockDropShadowEffect 是可以的。
    • 顺便说一句,我更新了示例,以防您想进行更深入的分析。
    【解决方案2】:

    这是我的诀窍:永远不要使用 DropShadowEffect。创建一个您想要为其创建阴影的任何内容的克隆,将其放在主要内容的后面,并在其上使用 BlurEffect。性能好得多,质量好得多。我不知道为什么。

    【讨论】:

    • 嗨 Zenexer,克隆作为影子也是我的初步想法。但效果不会像DropShadowEffect 那样好看。我也试过BlurEffect,还是很慢。
    【解决方案3】:

    我认为问题出在这里:

    uie.CacheMode = new BitmapCache();
    

    通常,这会告诉 silverlight 尝试将位图缓存在内存中,这样计算机就不需要在您的应用程序中重新渲染元素。

    在此处查看有关 Silverlight 性能的建议提示:

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

    【讨论】:

    • 感谢您的回复。 uie.CacheMode = new BitmapCache(); 这里用于测试 GPU 加速 &lt;param name="EnableGPUAcceleration" value="true" /&gt;。不管有没有这个声明,我都有同样的问题。
    【解决方案4】:

    问题与 Silverlight 的渲染管道如何工作有关。 DropShadowEffect 只是一个像素着色器,像素着色器在 Silverlight 中通过创建它们将应用到的图像的缓冲副本来工作,然后允许您使用缓冲副本的值更改真实图像的像素属性。此缓冲副本是整个图像,不受剪辑影响。因为缩放会创建一个非常大的预剪裁图像,所以像素着色器必须制作一个非常大的预剪裁图像的物理缓冲副本……这就是缩放时性能如此糟糕的原因。

    我找到的唯一解决方案是在缩放(或平移缩放的图像)时禁用像素着色器效果,并在图像放大(或平移完成)时将其添加回来。这样一来,您就不会因为每一帧的缩放或平移都应用像素着色器而导致性能损失。

    【讨论】:

    • 嗨,迈克,disable the pixel shader effect 是什么意思?您的意思是在缩放或平移时删除DropShadowEffect
    猜你喜欢
    • 2015-05-01
    • 2018-02-28
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多