Brushes.White slows down graphics

Below is a (very naive) implementation of Conway Game of Life in WPF. This is just a demo ...

XAML:

<Window x:Class="wpf_conway_life_2013_05_19.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="500" Width="500"> <Grid> <Canvas Name="canvas" Width="auto" Height="auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> </Canvas> </Grid> </Window> 

code behind:

 using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; namespace wpf_conway_life_2013_05_19 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var random = new Random(); var data = new int[100, 100]; var dataB = new int[100, 100]; Func<int, int, int> at = (x, y) => { if (x < 0) x = 100 + x; if (x >= 100) x = x % 100; if (y < 0) y = 100 + y; if (y >= 100) y = y % 100; return data[x, y]; }; for (var x = 0; x < 100; x++) for (var y = 0; y < 100; y++) data[x, y] = random.Next(2); var rectangles = new Rectangle[100, 100]; for (var x = 0; x < 100; x++) for (var y = 0; y < 100; y++) { rectangles[x, y] = new Rectangle(); canvas.Children.Add(rectangles[x, y]); } canvas.SizeChanged += (s, e) => { for (var x = 0; x < 100; x++) { for (var y = 0; y < 100; y++) { rectangles[x, y].Width = canvas.ActualWidth / 100; rectangles[x, y].Height = canvas.ActualHeight / 100; Canvas.SetLeft(rectangles[x, y], (canvas.ActualWidth / 100) * x); Canvas.SetTop(rectangles[x, y], (canvas.ActualHeight / 100) * y); } } }; Action macroStep = () => { dataB = new int[100, 100]; for (var x = 0; x < 100; x++) { for (var y = 0; y < 100; y++) { var neighbors = 0; for (var i = -1; i <= 1; i++) for (var j = -1; j <= 1; j++) if (i == 0 && j == 0) continue; else neighbors += at(x + i, y + j); dataB[x, y] = data[x, y]; if (neighbors < 2) dataB[x, y] = 0; if (neighbors == 3) dataB[x, y] = 1; if (neighbors > 3) dataB[x, y] = 0; rectangles[x, y].Fill = dataB[x, y] == 0 ? new SolidColorBrush(new Color()) : Brushes.Black; } } data = dataB; }; var timer = new DispatcherTimer(); timer.Tick += (s, e) => macroStep(); timer.Start(); } } } 

Here's what it looks like:

enter image description here

If you replace new SolidColorBrush(new Color()) with Brushes.White , the program will work much slower. Why?

I am testing the 64-bit version of Windows 7 using Express Express 2010.

+8
c # wpf conways-game-of-life
source share
2 answers

Because new Color() has an alpha value of zero, which means that WPF should not display it, because it is completely transparent - on the other hand, the white color of alpha is 255, which means that it is a completely solid white color, which should to be provided.

+1
source share

There is nothing special about using Brushes.White .

If you define your own local brush outside the macroStep event handler, and then freeze it, it will behave just like using Brushes.White. If you do not freeze at first, it will behave much worse.

The best performance is to create your brush once at the beginning of each macro step call before the loop, and then freeze it. This is a significant slowdown if you create a new brush inside the innermost cycle.

In addition, if you increase the interval on the timer for poor code behavior, it will actually fix the performance issue. I assume that there is some kind of resource cleaning that will occur in the background thread after it finishes rendering each time, tied to the internal elements of the brush, but it was starving of the ability to clean because you are turning right around and using the brush at the next iteration. To demonstrate this, I created a pool of brushes and used different brushes each time:

 SolidColorBrush[] brushes = new SolidColorBrush[2]; for (int i = 0; i < brushes.Length; i++) { var brush = new SolidColorBrush(new Color()); brush.Freeze(); brushes[i] = brush; } int brushIx = 0; Action macroStep = () => { dataB = new int[100, 100]; var brush = brushes[brushIx++ % brushes.Length]; ... rectangles[x, y].Fill = dataB[x, y] == 0 ? brush : Brushes.Black; data = dataB; }; 

If you set the number of brushes to 1, this will give the same behavior as when using Brushes.White . But if you set it to 2 or more, you get the expected performance.

0
source share

All Articles