none
Brushes must be frozen before use. Period.

    Question

  • I find it fascinating that brushes must be frozen or they simply don't work. Unfrozen brushes are not only several orders of magnitude slower than frozen brushes -- they cause lockups in UiElement.RenderClose(). Here is some sample code for observing this yourself. Simply clicking on the background should cause the dot pattern to change. Comment out the freeze line and then observe what happens.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    using System.Diagnostics;
    using System.Windows.Media.Imaging;
    using System.Windows.Media;
    using System.Windows.Controls;
    using System.ComponentModel;
    using System.Threading;

    namespace WpfSpeedTest
    {
        public class Canvas1 : Canvas
        {
            private readonly Window _owner;
            public Canvas1(Window owner)
            {
                _owner = owner;
            }

            protected override void OnRender(DrawingContext drawingContext)
            {
                renderSW.Reset();
                renderSW.Start();
                base.OnRender(drawingContext);
                renderSW.Stop();

                Random rand = new Random();
                double width = this.ActualWidth;
                double height = this.ActualHeight;
                if (double.IsNaN(width) || double.IsNaN(height))
                    return;

                createSW.Reset();
                createSW.Start();
                SolidColorBrush _infiniteCellBrush = new SolidColorBrush(Colors.Black);
                _infiniteCellBrush.Freeze(); // comment me out to make bad things happen
                createSW.Stop();

                drawSW.Reset();
                drawSW.Start();
                for (int i = 0; i < 10000; i++)
                {
                    Point a = new Point(rand.NextDouble() * (width - 1) - 0.5, rand.NextDouble() * (height - 1) - 0.5);
                    Rect rect = new Rect(a, new Vector(1, 1));
                    drawingContext.DrawRectangle(_infiniteCellBrush, null, rect);
                }
                drawSW.Stop();

                _owner.Title = string.Format("WPF: {0} s. create, {1} s. draw, {2} s. render ({3} x {4})", createSW.Elapsed.TotalSeconds, drawSW.Elapsed.TotalSeconds, renderSW.Elapsed.TotalSeconds, width, height);
            }

            protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
            {
                base.OnMouseLeftButtonDown(e);
                InvalidateVisual();
            }

            private Stopwatch drawSW = new Stopwatch(), renderSW = new Stopwatch(), createSW = new Stopwatch();
        }

        public class Window1 : Window
        {
            public Window1()
            {
                Canvas1 canv = new Canvas1(this);
                canv.Background = SystemColors.ControlBrush;
                DockPanel dp = new DockPanel();
                dp.LastChildFill = true;
                dp.Children.Add(canv);
                this.Content = dp;
            }
        }

        internal sealed class MyMain
        {
            [System.STAThread()]
            public static void Main()
            {
                Application app = new Application();
                app.Run(new Window1());
            }
        }
    }

    Saturday, January 05, 2008 5:25 PM

Answers

  • This is a common pattern to freeze any Freezable objects to gain better performance in terms of rendering, because when the Freezable objects get frozen, the underlying compositor doesn't need to keep track of its update/change, this presumably can save a lot of CPU cycles.

    Note that VisualBrush cannot be frozen as I already explained in this thread.
    Friday, January 11, 2008 8:20 AM

All replies

  • Our of curiosity, why is this particular post pinned as an announcement?

    Monday, January 07, 2008 4:26 PM
  • "Unfrozen brushes are not only several orders of magnitude slower than frozen brushes -- they cause lockups in UiElement.RenderClose(). "

     

    By "lockups" do you mean that the code will hang/crash?

     

    Also, what if you want to use a MediaElement showing a video as a VisualBrush (i.e. you set the Visual property of the VisualBrush to a MediaElement instance)? If you freeze the VisualBrush, then I suppose the area painted with the VisualBrush will not be updated anymore, i.e. it will only contain the video image that was shown at the time when the VisuaBrush was frozen, or?

    Monday, January 07, 2008 4:27 PM
  • By "lockups" I mean that the code goes into an infinite loop in RenderClose. It does hang. Try the example code. You'll won't see the pattern change properly.

    As far as the visual brush goes, using unfrozen brushes doesn't always cause infinite loops in RenderClose. It appears that you don't get a problem there if you are only rendering a trivial number of objects.
    Monday, January 07, 2008 4:58 PM
  • I tried to run the example with the "freeze line" commented out but the code did not go into an infinite loop. However it did take a very long time to execute. More specifically it took about 20 seconds until the dot pattern changed compared to less than 1 second when the freeze line was not commented out.


    So isn't this just a sign that a frozen brush is much more efficient (as expected)? I.e. it is not necessary to freeze a brush, but if you do things will run much faster.

    Tuesday, January 08, 2008 11:49 AM
  • This is a common pattern to freeze any Freezable objects to gain better performance in terms of rendering, because when the Freezable objects get frozen, the underlying compositor doesn't need to keep track of its update/change, this presumably can save a lot of CPU cycles.

    Note that VisualBrush cannot be frozen as I already explained in this thread.
    Friday, January 11, 2008 8:20 AM