Wydajność Bitmap-Wzorce Optymalizacji

Znalazłem kilka wzorców optymalizacji obsługi Bitmap w WPF. Nie rozumiem jednak, kiedy stosować poszczególne wzory. Ponieważ uważam, że jest to powszechny problem, podsumowałem to, co Rozumiem i co myślę i proszę o pomoc. Jeśli możesz dodać wzory , wyjaśnić w jaki sposób różnią się one, wyjaśnić, czy używają CPU lub GPU i nauczyć Kiedy używać każdego i Jak je łączyć, byłoby to ogromną pomocą!

Kontekst-obrazy Scenariusz "Grid":

Moja aplikacja musi wyświetlać wiele obrazów bitmapowych. Obrazy są wyświetlane na ekranie w strukturze siatki w wierszach i kolumnach (niekoniecznie klasy Grid lub UniformGrid, widok albumu think Window Media Player). Obrazy mogą poruszać się między różnymi komórkami siatki. Niektóre obrazy w dowolnych komórkach mogą być zastąpione przez inne. Obrazy powinny być klikalne, powinny zawierać menu kontekstowe, powinny być wybierane, przeciągane itp. Innymi słowy, " połącz małe buggers into one big bitmap" Nie dotyczy, przynajmniej nie naiwnie.

Pattern 0: The Hack

Czy połączyć małe gnojki w bitmapę (jak? rysowanie kontekstu?) i użyj tego jako tła. Nakładaj to na obrazy z pustą zawartością, które będą obsługiwać trafienia, menu kontekstowe, zdarzenia itp.

Zaletą jest to, że mówimy tu tylko o dwóch bitmapach: aktualnie wyświetlanej i tej, która powinna ją zastąpić. To powinno być naprawdę szybkie. Jednak moje wieloletnie doświadczenie podnosi czerwoną flagę niebezpieczeństwa. Twoje komentarze?

Wzór 1: Zmniejsz Rozmiar Obrazu

Nie trzeba się zastanawiać, kiedy z góry znasz Rozmiar obrazu do zmiany rozmiaru i kiedy jesteś gotowy stracić szczegóły (kolor) dla wydajności:
  1. Zmniejsz rozmiar bitmapy za pomocą BitmapImage.DecodePixelWidth
  2. zmniejsz informacje o kolorze za pomocą FormatConvertedBitmap.DestinationFormat
  3. Ustawia zachowanie skalowania kontrolki Ustawianie obrazu.Stretch to Stretch.None
  4. Ustaw SetBitmapScalingMode dla obrazu na LowQuality.
  5. zamrozić gnoja

Zobacz kod tutaj .

Pattern 2: Background pre-fetch

Ten wzór ma zastosowanie, gdy myślisz, że możesz skorzystać z tego, że użytkownik patrzy na obrazy na ekranie i przygotowuje następne obrazy do wyświetlenia. Minusem twojego projektu, oprócz narzutu pamięci, jest to, że musi obsługiwać docelowy. Net Framework 4, a nie tylko profil klienta, więc może to spowodować instalację na kliencie. sam będziesz musiał cierpieć ból programowania asynchronicznego.

W tym wzorze tworzysz dokładnie wymaganą liczbę kontrolek obrazu. Gdy bitmapy muszą być dodawane, przenoszone lub usuwane, modyfikujesz tylko bitmapy sterujące obrazem. Zadanie BackgroundWorker jest odpowiedzialne za wstępne pobieranie BitmapSource (s) (ewentualnie za pomocą wzoru "Reduce Image Size" powyżej) i Wkładanie ich do pamięci.

Aby to zadziałało, musisz ustawić CacheOption BitmapImage na OnLoad, tak aby praca była wyłączona do workera w tle.

Wzór 3: Kontekst Rysowania

To zostało zasugerowane przez Sheldon Ziao z Microsoft Support na forum MSDN WPF tutaj . Zobacz stronę 494, Rozdział 15 "grafika 2D" w WPF 4 Unleashed Adama Nathana, aby uzyskać opis Drawingkontekstu. Nie mogę powiedzieć, że to Rozumiem. Zgodnie z odpowiedzią tutaj , zakładam, że poprawiłoby to obsługę rysunków geometrii, a nie bitmap. Następnie, nie sądzę, że będzie to wspierać wymagania dotyczące ostrości i zdarzeń dla obrazów (mój błąd za to, że nie wyjaśniam wymagań lepiej na forum) Co więcej, martwię się podsumowaniem książki: "zauważ, że użycie DrawingContext nie zmienia faktu, że działasz w systemie trybu zatrzymanego. Określone rysowanie nie następuje od razu. polecenia są wykonywane przez WPF, dopóki nie będą potrzebne."Oznacza to, że gdy nasz nawet handler ponownie nie możemy skorzystać z równoległości, jak w "Background pre-fetch".

Wzór 4: Bitmapy Zapisywalne

Dokumentacja MSDNtutaj opisuje go jako podwójny system buforów: Twój wątek UI aktualizuje bufor; wątek renderujący WPF przenosi go do pamięci wideo.

Zamierzone użycie (zobacz tutaj ) jest dla bitmap, które zmieniają się wiele w filmie wideo, takim jak display. Nie jestem jasne, ale możliwe, że można to zhakować i połączyć z wzorcem pobierania wstępnego w tle i wykorzystać w scenariuszu siatki.

Pattern 5: Cached Bitmap

Niewiele informacji na temat MSDN (tutaj ). W Archiwum forum WPF (tutaj ) wyjaśniono, że " BitmapCache API jest przeznaczony do buforowania zawartości (podczas renderowania w sprzęcie) w pamięci wideo, co oznacza, że pozostaje rezydentny na Twoim GPU. Pozwala to zaoszczędzić koszty ponownego renderowania tej zawartości podczas rysowania na ekran."To świetny pomysł. Nie jestem jednak pewien, jakie są pułapki i jak z nich korzystać.

Pattern 6: RenderTargetBitmap

RenderTargetBitmap konwertuje wizualizację na bitmapę. Nie jestem pewien, czy to ma tu znaczenie. Zobacz tutaj .

Edit : odnośnie pytania Paula Hoenecke: napisałem, że "moja aplikacja musi wyświetlać wiele iage bitmapowych". Nie udało mi się wspomnieć, że muszę wyświetlić około 800 zdjęć .

O problemach z wydajnością można przeczytać w moich pytaniach so wydajność bitmap WPF i Jak mogę uczynić wyświetlanie obrazów na WPF bardziej "snappy"?

Zmodyfikowałem opis wzorca 1, aby podkreślić koncepcję, że elementy sterujące obrazem nie są tworzone lub usuwane (chyba że chcemy wyświetlić większą lub mniejszą siatkę). Tylko ich źródła są ustawione na inne, nowe lub zerowe zasoby Bitmapowe.

Edit : to pytanie opublikowane na forum wsparcia WPF , z kilkoma odpowiedziami od personelu MS.

Author: Community, 2012-02-16

1 answers

Nie jestem w stanie znaleźć konkretnego pytania w Twoim poście, poza prośbą o komentarze do poniższych podejść. Nie twierdzę, że Wiem wszystko powyżej, ale powiem ci, co wiem, pracując przez jakiś czas nad tworzeniem wysokowydajnych interfejsów użytkownika przy użyciu WPF i Silverlight.

Pattern 0: The Hack. Łączenie wszystkich w jeden obraz

Unikałbym tego, jeśli to możliwe. Wygląda na to, że chcesz wyświetlić duży panel zawijania składający się z tysięcy małych obrazów. Każdy obraz jest więc miniaturka (ponieważ nie można wyświetlić 1000 dużych obrazów na raz). W rezultacie zalecałbym buforowanie/zmianę rozmiaru nad kombinacją.

Wzór 1: Zmniejsz Rozmiar Obrazu

Jeśli wyświetlasz 1000 obrazów na ekranie jednocześnie, rozważ dostępną nieruchomość ekranu. Przeciętny monitor to 1280x1024 pikseli, czyli nieco ponad 1,3 Mpikseli. 1000 obrazów sugeruje, że uzyskasz maksymalny rozmiar 1300 pikseli na obraz, lub 36*36. Powiedzmy, że Twoje obrazy mają rozmiar 32*32. Powinieneś zdecydowanie tworzymy miniaturę tego rozmiaru obrazu do renderowania na ekranie, a następnie po kliknięciu (lub innej akcji) Pokaż pełny rozmiar obrazu.

Należy również wziąć pod uwagę nie tylko narzut renderowania przy zmianie rozmiaru dużego obrazu, ale także wysyłanie dużego obrazu do GPU w celu zmiany rozmiaru. Te dane wymagają przepustowości do wysłania. Duży obraz może wynosić kilka megabajtów, podczas gdy Miniatura o rozmiarze 32*32 może wynosić kilka kilobajtów.

Jeśli potrzebujesz dynamicznego rozmiaru, w porządku, ale musisz eksperymentować z tworzeniem wiele miniaturek lub generowanie ich w locie.

Pattern 2: Background pre-fetch

Jest to technika, o której nie słyszałem, jednak wydaje się wiarygodna. Jaki jest narzut w Twojej aplikacji? czy to aktualizacja obrazu.Właściwość Source lub tworzenie nowego obrazu, tessellating, wykonywanie układu i wysyłanie informacji, aby renderować go do GPU?

Wszystkie powyższe występują na procesorze z wyjątkiem ostatecznego renderowania. Zmniejszając obciążenie po stronie procesora i aktualizacja źródła może być na coś. Połącz to z writeablebitmap jako źródłem, a możesz jeszcze bardziej poprawić wydajność(patrz poniżej).

Pattern 3: Drawing Context

Ok, wszystko to pozwala na kolejkowanie wywołań rysowania w trybie zachowanym przy użyciu składni stylu "OnPaint", która w niczym nie przypomina starego GDI OnPaint. Z mojego doświadczenia OnRender nie poprawia wydajności, ale pozwala na drobnoziarnistą elastyczność nad tym, co jest rysowane i kiedy. OnRender zapewnia kontekst, który ma funkcję DrawImage, umożliwiając rysowanie BitmapSource do potoku renderowania bez potrzeby kontroli obrazu. Jest to dobre, ponieważ usuwa pewne narzuty, jednak wprowadza problemy podobne do tych, które są widoczne w Pattern 0 (stracisz układ i będziesz musiał obliczyć pozycję wszystkich obrazów). Jeśli to zrobisz, równie dobrze możesz powołać się na wzorzec 0, który odradzałem.

Pattern 4: Writeable Bitmapy

WriteableBitmaps są mało używanym i niezwykle potężnym podsystemem w WPF. Używam ich do tworzenia komponentu Wykresów zdolnego do renderowania dużych ilości danych w czasie rzeczywistym. Sugerowałbym sprawdzenie projektu writeablebitmapex codeplex ujawnienie, przyczyniłem się do tego raz i zobaczę, czy można połączyć go z innymi wzorcami. W szczególności funkcja Blit, która umożliwi zapisanie buforowanej bitmapy do źródła bitmap na obraz.

Na przykład, dobrą techniką może być Wzór 1 + 2 + 4.

Możesz mieć siatkę N kontrolek obrazu na ekranie w określonych miejscach w kontrolce siatki. Każdy z nich jest statyczny i nie jest przewijany, więc nie ma żadnych kreacji/usunięć. Teraz, na dodatek, Zmień rozmiar obrazu i napisz do writeablebitmap, która jest ustawiona jako właściwość Source na każdym obrazie. Podczas przewijania pobieraj następne / poprzednie miniatury i aktualizuj źródła za pomocą WriteableBitmapEx.Blit. Pow! zwirtualizowane, buforowane, wielowątkowe obrazowanie.

Wzór 5: Buforowana Bitmapa

Jest to próba Microsoftu, aby 1+2+4 Jak omówiłem powyżej. To, co próbuje zrobić, to po Układzie (po stronie CPU), teselacji (po stronie CPU), wysłaniu instrukcji renderowania trybu zatrzymanego do GPU (po stronie CPU) i renderowaniu (po stronie GPU) buforuje obraz rastrowy renderowanego elementu, który jest ponownie używany podczas następnego renderowania. Tak mało znany fakt o WPF jest to, że wspaniały silnik zasilany GPU jest strasznie powolny, ponieważ większość swojej pracy wykonuje na CPU: p

Chciałbym eksperymentować z BitmapCache i zobaczyć, jak to działa. Istnieją zastrzeżenia, A są one takie, że po aktualizacji UIElement musi odtworzyć pamięć podręczną, aby elementy statyczne działały znacznie lepiej niż dynamiczne. Również nie widziałem znaczącej poprawy wydajności przy użyciu tego, podczas gdy techniki stylu writeablebitmap mogą dać poprawę o rząd wielkości.

Pattern 6: RenderTargetBitmap

Ta ostatnia technika pozwala renderować UIElement do bitmapy-wiesz o tym - ale co ciekawe, może to wykonać generator miniatur (lub zmienić rozmiar). Na przykład ustaw obraz z BitmapSource swojego pełnowymiarowego obrazu. Teraz ustaw rozmiar kontrolki obrazu na 32*32 i Renderuj do bitmapy. Voila! Masz miniaturkę BitmapSource do użycia w połączeniu z niektórymi zamianami (wzór 2) i/lub zapisywalnymi bitmapami.

Ok na koniec chciałem tylko powiedzieć, że wymaganie, które masz, popchnie WPF do swoich granic, jednak istnieją sposoby, aby go wykonać. Jak już powiedziałem, mam systemy, które renderowały tysiące lub miliony elementów na ekranie jednocześnie, używając wspaniałego obejścia, jakim jest writeablebitmap. Zejście w dół standardowej trasy WPF spowoduje piekło wydajności, więc będziesz musiał zrobić coś egzotycznego, aby rozwiązać ten problem.

Tak jak powiedziałem moja rekomendacja jest 1+2+4. Musisz zmienić rozmiar a miniaturka, nie mam wątpliwości. Pomysł posiadania statycznej siatki kontroli obrazu i aktualizacji źródeł jest bardzo dobry. Pomysł użycia writeablebitmap (konkretnie writeablebitmapex BLIT function) do aktualizacji źródeł jest również wart zbadania.

Powodzenia!

 19
Author: Dr. ABT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-27 15:32:13