Silverlight 5:DropShadowEffect 的严重性能问题

发布于 2025-01-04 16:36:00 字数 11352 浏览 2 评论 0原文

环境信息:

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 一起使用,一切都很好。您可以通过单击“Use 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();
        }
    }
}

Environment Info:

Windows 7 64bit SP1

6GB Memory

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

Silverlight 5

I have very terrible performance issues with DropShadowEffect in Silerlight.

I created a minimized demo for it:
http://www.peterlee.com.cn/public/ShadowEffectTestDemo/ShadowEffectTestTestPage.html

Click the button Zoom to try to zoom the canvas (with a Star shape with DropShadowEffect). You will find that when the Zoom = 64, your CPU and memory are very crazy.

But, if you remove the DropShadowEffect by clicking the Remove Effect button, and then zoom it, everything is okay.

However, if we use a TextBlock with DropShadowEffect, everything is fine. You can try it by clicking the "Use TextBlock` button.

I don't know what Silverlight is dealing with the TextBlock and my customized Star shape for the DropShadowEffect.

Please help me out. Thanks.

UPDATE:
According to the replies:

I also tried it in Release mode (now the sample demo link was built in Release mode);

I also added GPA acceleration suggested by 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>

Here is the UPDATED code

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

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

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

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

I've tried running WinDbg against your application and dumpheap the application. here is the Result. Now dont worry, I dont understand half of it either. But I went a little deeper and there seem to be large amounts of arrays/lists where atleast 80% are null.

You might want to look at WinDbg yourself. Here is a small Tutorial

I'll try to look at it in the morning if I can and I hope I've helped you atleast in the right direction

月亮是我掰弯的 2025-01-11 16:36:00

这是我的技巧:永远不要使用 DropShadowEffect。创建您想要为其创建阴影的任何内容的克隆,将其放在主要内容后面,然后对其使用模糊效果。更好的性能,更好的质量。我不知道为什么。

Here's my trick: never use DropShadowEffect. Create a clone of whatever you want to create a drop shadow for, put it behind the main content, and use a BlurEffect on it. Much better performance, much better quality. I have no idea why.

陌伤ぢ 2025-01-11 16:36:00

我认为问题出在这里:

uie.CacheMode = new BitmapCache();

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

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

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

I think the problem is here:

uie.CacheMode = new BitmapCache();

Typically, this tells silverlight to try and cache the bitmap in memory so that the computer does not need to re-render the element in your application.

Look here for the suggested tips on silverlight performance:

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

我恋#小黄人 2025-01-11 16:36:00

该问题与 Silverlight 渲染管道的工作方式有关。 DropShadowEffect 只是一个像素着色器,像素着色器在 Silverlight 中工作的方式是创建要应用的图像的缓冲副本,然后允许您使用缓冲副本的值更改真实图像的像素属性。此缓冲副本是整个图像并且不受剪切影响。由于缩放会创建非常大的预裁剪图像,因此像素着色器必须制作非常大的预裁剪图像的物理缓冲副本...这就是缩放时性能如此糟糕的原因。

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

The problem has to do with how the rendering pipeline of Silverlight works. The DropShadowEffect is just a pixel shader, and pixel shaders work in Silverlight by creating a buffered copy of the image they will be applied to and then allow you to change pixel properties of the real image using the values of the buffered copy. This buffered copy is the entire image and is not affected by clipping. Because zooming creates a very large pre-clipped image, the pixel shader must make a physical buffered copy of a very large pre-clipped image...this is why the performance is so bad when you zoom.

The only solution I have found for this is to disable the pixel shader effect while you are zooming (or panning the zoomed image), and add it back when the image is zoomed in (or panning is complete). This way you don't incur the performance penalty of the pixel shader being applied for every frame of zooming or panning.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文