Sdílet prostřednictvím


Blitter i 2d Framebuffer w Silverlight

Przyglądając się moim ostatnim odkryciom w kwestii gier na Silverlight, a w szczególności znanej grze Quake, zacząłem się zastanawiać jak to zrobiono. Quake ma własny software’owy silnik 3D napisany przez id Software, aby działał on wydajnie w Silverlight w zasadzie jedyne co jest potrzebne to szybki dostęp do blittera i/lub framebuffera.

Jeśli przyjrzycie się bibliotece Silversprite, to zobaczycie, że taki blitter i framebuffer jest tam zaimplementowany. Silversprite jest o tyle ciekawy, że ma API bardzo bliskie XNA. Migracja projektu pomiędzy Xna a Silverlight, dzięki tej bibliotece, jest stosunkowo łatwa.

To mnie na tym etapie tak strasznie nie zainteresowało, raczej zainteresowała mnie wydajność samego blittera. Skonstruowałem sobie prosty przykład napisany przeze mnie od zera, gdzie szukałem technologicznej sposobności osadzenia bezpośredniego dostępu do pamięci ekranu (okna) w Silverlight.

Znalazłem taką sposobność i poniżej macie działający przykład na Silverlight 4.0 oraz Silverlight 3.0:

https://www.dbiesiada.com/labs/Silverlated/
https://www.dbiesiada.com/labs/Silverlated/v3/

Te przykłady, może niezbyt ładne, natomiast badają bezpośrednie wypełnienie bufora pamięci okna (czyszczenie ekranu w dwóch kolorach) oraz blit (kopiowanie) na ekran obrazów 2d i ścieżek w postaci linii prostych i krzywych beziera. W kopiowaniu wykorzystałem mechanizmy Silverlight (bez żadnej dodatkowej optymalizacji).

Ja mam SL4 zainstalowane, Tomasz Kopacz dodał mi dzisiaj rano, że ten przykład mu pokazuje, że wersja na SL4 jest szybsza niż SL3, co jest dobrym sygnałem, który jeszcze muszę zweryfikować, czekam na wasz komentarz.

Aby taki przykład zadziałał w zasadzie musicie się przyjrzeć dwóm elementom:
* Klasa WriteableBitmap, która pozwala na bezpośredni dostęp do tablicy pixeli.
* Klasa DispatcherTimer, do synchronizacji, gdzieś na jakimś forum przeczytałem, że wykorzystanie storyboardów zamiast DispatcherTimer, może jeszcze przyspieszyć sprawę, na razie tego nie testowałem. Silversprite wykorzystuje storyboardy na 100%.

Mając już te mechanizmy zaimplementowane główna pętla programowa w zasadzie sprowadza się do takiego kodu 
(https://www.wklej.eu/index.php?id=07f6cabdcb)

 if (!pause) {
device.Clear(redrawalCount % 2 == 0 ? 0xFF000000 : 0xFF202020);

           for (int i = 0; i < (int)imagesCounter.Value; i++)
{
             switch (objidx) {
             case 0:

Image oscar = (Image)Application.Current.Resources["oscar"];
device.BackBuffer.Blit(oscar, randomizer.Next(640),
randomizer.Next(480),
(float)randomizer.NextDouble(),
(float)randomizer.NextDouble(),
randomizer.Next(360));

                  break;

              case 1:

LineGeometry lineGeom = new LineGeometry();
lineGeom.StartPoint = new Point(0,0);
lineGeom.EndPoint = new Point(0,100);
Path linePath = new Path();

linePath.Stroke = new SolidColorBrush(Colors.White);
linePath.StrokeThickness = 5;
linePath.Data = lineGeom;

device.BackBuffer.Blit(linePath, randomizer.Next(640),
randomizer.Next(480),
(float)randomizer.NextDouble(),
(float)randomizer.NextDouble(),
randomizer.Next(360));

                  break;

               case 2:

Path bezier = (Path)Application.Current.Resources["bezier"];
device.BackBuffer.Blit(bezier, randomizer.Next(640),
randomizer.Next(480),
(float)randomizer.NextDouble(),
(float)randomizer.NextDouble(),
randomizer.Next(360));
                   break;
}
}
}

Device w tym przypadku to moja klasa organizująca DispatcherTimer i główną pętlę programową. Istotniejszy jest device.BackBuffer, który jest drugą klasą stworzoną przeze mnie – to po prostu wrapper na WriteableBitmap:
(https://www.wklej.eu/index.php?id=2d6a826192)

Powyższy link zawiera całą klasę, istotą tak naprawdę są dwie zaimplementowane teraz metody:

        public void Clear(uint rawColor)
{
            for (int i = 0; i < width * height; i++)
{
                unchecked
{
surfaceBitmap.Pixels[i] = (int)rawColor;
}
}
}

        public void Blit(UIElement element, int xpos, int ypos,
                            float scalex, float scaley, float angle)
{
TranslateTransform position = new TranslateTransform();
ScaleTransform scale = new ScaleTransform();
RotateTransform rotation = new RotateTransform();

position.X = xpos;
position.Y = ypos;
scale.ScaleX = scalex;
scale.ScaleY = scaley;
rotation.Angle = angle;

TransformGroup transform = new TransformGroup();
transform.Children.Add(position);
transform.Children.Add(scale);
transform.Children.Add(rotation);

surfaceBitmap.Render(element, transform);
}

Jedna, która na ten moment jednym kolorem wypełnia całą tablicę bitmapy. Pokazuje jak dostać się do tej tablicy z własnym kodem. Drugi to blitter wszelkich UIElement z Silverlight z fransformacjami.

Jaki FPS u was się pokazuje przy maksymalnej ilości obiektów każdego typu i na jakim sprzęcie (CPU/RAM/GPU/GPUMEM)?

Comments

  • Anonymous
    December 04, 2009
    Fx 3.5 oraz IE8, SL3 oraz SL4 - wyniki takie same. Figurka, 100obiektów - 26FPS. Athlon II X2 @3GHz, 4GB RAM DDR2 800MHz, karta integra Radeon 3200 bez własnej pamięci, Windows 7 x64,

  • Anonymous
    December 04, 2009
    U mnie na Lenovo T61p,SL4 100 oscarów: 23FPS 100 linii: 45FPS 100 wektorów: 32FPS SL3 wciąż nie sprawdzałem. Testowałeś obie wersje na runtime 4.0 czy odinstalowałeś SL4 i zainstalowałeś SL3 aby sprawdzić wersję v3?

  • Anonymous
    December 04, 2009
    Najpierw SL3, potem upgrade do SL4.

  • Anonymous
    December 04, 2009
    100 oskarów - 47 fps 100 linii - 88fps 100 wektorów - 74fps sl3, bo taki mam ;). sprzęcior, i5/4GB/gtx260/1.8GB win7x64. co ciekawe, przy zwiększeniu ilości obiektów framerate... wzrósł. Potem dopiero zaczął opadać - pewnie wina turbo mode w i5 ;]

  • Anonymous
    December 04, 2009
    jeszcze przeprowadziłem kilka eksperymentów: (beziery) obiekty | fps 1 - 65 fps 25 - 65 fps 50 - 102fps 75 - 85fps 100 - 74fps czyli, im więcej tym szybciej :]

  • Anonymous
    December 06, 2009
    @p1: solidny wynik, daje do myślenia, zwłaszcza z twoimi obserwacjami dotyczącymi wzrostów i spadków. Ten benchmark rozwijam dalej testując inne mechanizmy z Silverlight 3.0 i bardziej już 4.0. Jak skończę (między innymi ray-tracer, który właśnie optymalizuję) to się odezwę.

  • Anonymous
    April 02, 2010
    100 oskarów - 42fps na: INTEL E8500 + geforce9300 Na temat "wyższości" timera Storyboard nad DispatcherTimer można przeczytać na blogu Mike Snowa http://blogs.silverlight.net/blogs/msnow/archive/2008/07/09/storyboard-versus-dispatchertimer-for-animation-and-game-loops.aspx Świetny artykuł. Szkoda (dla mnie), że muszę przerobić renderowanie w swoim projekcie bo te jest o niebo wydajniejsze:)