2013-05-20 19 views
8

Poniżej znajduje się (bardzo naiwna) implementacja gry życia Conwaya w WPF. To tylko demo ...Pędzle. Powoli spowalniają wyświetlanie grafiki.

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> 

kod za:

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

Oto jak to wygląda:

enter image description here

Gdybym zastąpić new SolidColorBrush(new Color()) z Brushes.White program działa znacznie wolniej. Czemu?

Testuję na 64-bitowym Windows 7 przy użyciu 2010 Express.

+2

Aby określić, czy chodzi o renderowanie, czy o pobieranie, jeśli zachowane zostanie odwołanie do pamięci podręcznej pędzla poza pętlą i użyte w tym miejscu odwołanie zamiast "Szczotki.Białe", czy prędkość ma wpływ? Pędzle systemowe są buforowane w 'Dictionary ', które musi być zablokowane za każdym razem, gdy element zostanie pobrany. –

+2

Czy zachowujesz się tak samo w przypadku 'Brushes.Transparent'? – Rachel

+1

@SimonMcKenzie Dobra sugestia. Jednak próbowałem go i nie robi różnicy. (Przy okazji, gratulacje dla MapSnap, bardzo fajna aplikacja WP7!) – dharmatech

Odpowiedz

1

Ponieważma wartość alfa równą zero, co oznacza, że ​​WPF nie musi jej renderować, ponieważ jest w pełni przezroczysta - z drugiej strony alfa koloru białego ma wartość 255, co oznacza, że ​​jest to całkowicie biały, biały kolor, który musi być renderowany .

+0

Dobra sugestia Jaska. Ale gdyby tak było, byłoby to równie powolne: 'nowy SolidColorBrush (nowy kolor() {R = 255, G = 255, B = 255, A = 255})'. Jednak tak nie jest. – dharmatech

0

Nie ma nic specjalnego w używaniu Brushes.White.

Jeśli zdefiniujesz własny lokalny pędzel poza obsługą zdarzeń macroStep, a następnie go zamrozisz, będzie on zachowywał się identycznie jak w przypadku pędzla.Wszystko. Jeśli go nie zamrozisz, zachowa się znacznie gorzej.

Najlepszą wydajnością jest utworzenie pędzla raz na początku każdego połączenia do makroStep, przed pętlą, a następnie zablokowanie. Jest to znaczące spowolnienie, jeśli utworzysz nowy pędzel wewnątrz najbardziej wewnętrznej pętli.

Ponadto, jeśli zwiększysz przedział czasu dla nieprawidłowo zachowującego się kodu, naprawi on problem z wydajnością. Domyślam się, że istnieje pewien rodzaj czyszczenia zasobów, który wystąpiłby w wątku w tle po zakończeniu renderowania za każdym razem, który jest powiązany z wewnętrznymi elementami pędzla, ale jest głodny, ponieważ jest w stanie wykonać jego czyszczenie, ponieważ obracasz się w prawo i używanie pędzla w następnej iteracji. Aby to wykazać, stworzyłem pulę pędzli i użyć inną szczotkę za każdym razem:

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

Jeśli ustawić liczbę szczotek do 1, to daje takie samo zachowanie jak przy użyciu Brushes.White. Ale jeśli ustawisz 2 lub więcej, uzyskasz oczekiwaną wydajność.

+0

Dzięki za sugestię Bryce. Czy przetestowałeś ten kod w swoim systemie, porównując różne odmiany? W moim systemie tworzenie nowego pędzla w wewnętrznej pętli jest nadal szybsze niż tworzenie i zamrażanie pędzla, zarówno na zewnątrz, jak i wewnątrz 'macroStep'. To bardzo dziwne. – dharmatech

+1

@dharmatech, Właśnie przetestowałem wszystkie trzy przypadki - twój oryginał z "nowym SolidColorBrush (nowy kolor())", sugerowany przez Bryce'a - z tablicami zamarzniętych pędzli i znowu oryginalny z pędzelkami. Białe. I na mojej maszynie najszybszy był drugi - z tablicami Pędzli (tu nie ma niespodzianki). Najwolniejszy był ostatni - z Brushes.White. A przypadek z oryginalnym "nowym SolidColorBrush (new Color())" był gdzieś pośrodku. Warto dodać różnicę między zwycięzcą a drugim, choć było to zauważalne, ale nie aż tak, jak między drugim a trzecim. – Sevenate