2012-06-13 8 views
24

Obecnie pracuję nad grą i chcę mieć główne menu z obrazem tła.Czy Graphics.DrawImage jest zbyt wolny dla większych obrazów?

Jednak metoda jest bardzo powolna. Dokonałem pomiaru. Załóżmy, że MenuBackground jest moim obrazem zasobów o rozdzielczości 800 x 1200 pikseli. Narysuję go na innej bitmapie 800 x 1200 (najpierw renderuję wszystko do mapy bitowej bufora, potem skaluję ją i wreszcie rysuję na ekranie - tak radzę sobie z możliwością rozwiązywania wielu graczy, ale to nie powinno mieć wpływu w jakikolwiek sposób, patrz następny paragraf).

Więc mam zmierzyć następujący kod:

Stopwatch SW = new Stopwatch(); 
SW.Start(); 

// First let's render background image into original-sized bitmap: 

OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground, 
    new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight)); 

SW.Stop(); 
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds"); 

Rezultatem jest cicha zaskakujące dla mnie - ten Stopwatch mierzy coś pomiędzy 40 - 50 milliseconds. A ponieważ obraz tła nie jest jedyną rzeczą, którą należy narysować, całe menu zajmuje około 100 ms, aby wyświetlić, co implikuje obserwowalne opóźnienie.

Próbowałem narysować go na obiekt graficzny podany przez zdarzenie Paint, ale wynik był 30 - 40 milliseconds - niewiele zmienić.

Czy oznacza to, że Graphics.DrawImage() nie nadaje się do rysowania większych obrazów? Jeśli tak, co powinienem zrobić, aby poprawić wydajność mojej gry?

+1

Jeśli przez "bezużyteczny" rozumiesz "powolny jak ślimak w melasie", to tak. Jak powiedzieli inni, wypróbuj XNA. Trzymaj się 2D renderowania duszków, aż przyzwyczaisz się do struktury XNA, a następnie przejdź do 3D, jeśli jesteś tak pochłonięty. – CodeHxr

+1

Podtrzymuję to tylko dla analogii. :) – Ani

Odpowiedz

84

Tak, jest zbyt wolny.

Wpadłem na ten problem kilka lat temu podczas tworzenia Paint.NET (od samego początku, właściwie to było frustrujące!). Wydajność renderowania była fatalna, ponieważ zawsze była proporcjonalna do rozmiaru mapy bitowej, a nie rozmiaru obszaru, który miał przerysować. Oznacza to, że ilość klatek na sekundę spadła wraz ze wzrostem rozmiaru mapy bitowej, a liczba klatek na sekundę nigdy nie wzrosła, ponieważ rozmiar nieprawidłowego/przerysowanego obszaru spadł podczas wdrażania OnPaint() i wywoływania Graphics.DrawImage(). Mała bitmapa, powiedzmy 800x600, zawsze działała dobrze, ale większe obrazy (na przykład 2400x1800) były bardzo powolne. (Można jednak założyć, że w poprzednim paragrafie nic się nie działo, na przykład skalowanie za pomocą jakiegoś kosztownego filtru Bicubic, który miałby niekorzystny wpływ na wydajność).

Możliwe jest wymuszenie na WinForms używania GDI zamiast GDI + i unikaj nawet tworzenia obiektu Graphics, w którym możesz nałożyć inny zestaw narzędzi do renderowania (np. Direct2D). Jednak nie jest to proste. Robię to w Paint.NET i widzisz, co jest wymagane, używając czegoś podobnego do Reflectora w klasie o nazwie GdiPaintControl w bibliotece DLL SystemLayer, ale dla tego, co robisz, uznałbym to za ostateczność.

Jednak rozmiar mapy bitowej, której używasz (800x1200), powinien nadal działać wystarczająco dobrze w GDI + bez konieczności uciekania się do zaawansowanego współdziałania, chyba że celujesz na coś tak niskiego, jak Pentium II 300 MHz. Oto kilka wskazówek, które mogą pomóc się:

  • Jeśli używasz nieprzezroczystą bitmapę (bez kanału alfa/przezroczystości) w wywołaniu Graphics.DrawImage(), a zwłaszcza jeśli jest to mapa bitowa 32-bitowe z kanałem alfa (ale wiem jest nieprzezroczysty lub nie obchodzi cię to), a następnie ustaw Graphics.CompositingMode na CompositingMode.SourceCopy przed wywołaniem DrawImage() (pamiętaj, aby przywrócić pierwotną wartość po, w przeciwnym razie zwykłe prymitywy rysunku będą wyglądać bardzo brzydko). Pomija to dużo dodatkowej liczby matematycznej na piksel.
  • Upewnij się, że Graphics.InterpolationMode nie jest ustawiony na wartość podobną do InterpolationMode.HighQualityBicubic. Najszybciej będzie używać parametru NearestNeighbor, ale jeśli jest jakieś rozciągnięcie, może nie wyglądać zbyt dobrze (chyba że rozciągnie się dokładnie 2x, 3x, 4x, itd.), To zwykle dobry kompromis. Nigdy nie powinieneś używać niczego poza NearestNeighbor, jeśli rozmiar bitmapy odpowiada pikselowi obszaru, na który rysujesz.
  • Zawsze należy narysować obiekt Graphics podany w OnPaint().
  • Zawsze wykonuj swój rysunek w OnPaint. Jeśli chcesz przerysować obszar, zadzwoń pod numer Invalidate(). Jeśli chcesz, aby rysowanie stało się teraz, zadzwoń pod numer Update() po Invalidate(). Jest to rozsądne podejście, ponieważ wiadomości WM_PAINT (które powodują wywołanie OnPaint()) są komunikatami "o niskim priorytecie". Wszelkie inne przetwarzanie przez menedżera okien zostanie wykonane jako pierwsze, a zatem w przeciwnym razie może dojść do pominięcia wielu klatek.
  • Używanie System.Windows.Forms.Timer jako licznika czasu/klatek na sekundę nie działa zbyt dobrze. Są one implementowane przy użyciu Win32-a SetTimer i skutkują komunikatami WM_TIMER, które skutkują podniesieniem zdarzenia Timer.Tick, a WM_TIMER jest kolejną wiadomością o niskim priorytecie, która jest wysyłana tylko wtedy, gdy kolejka komunikatów jest pusta. Lepiej skorzystaj z System.Threading.Timer, a następnie użyj Control.Invoke() (aby upewnić się, że jesteś we właściwym wątku!) I dzwonisz pod numer Control.Update().
  • Ogólnie nie należy używać Control.CreateGraphics(). (Następujące "zawsze rysuj w OnPaint()" i "zawsze używaj Graphics podanej ci przez OnPaint() ')
  • Nie polecam używania procedury obsługi zdarzenia Paint. Zamiast tego zaimplementuj OnPaint() w klasie, którą piszesz, która powinna pochodzić od Control. Pochodzące z innej klasy, np. PictureBox lub UserControl, albo nie doda żadnej wartości dla ciebie, albo doda dodatkowy narzut. (BTW PictureBox jest często źle rozumiany i prawdopodobnie prawie nigdy nie będziesz go używał.)

Nadzieję, że pomaga.

+2

zasługujesz na tony przebojów. Do rysowania siatki tła jest jakaś wskazówka? TextureBrush wydaje się szybki, ale nie działa, gdy masz niestandardowe skalowanie z współczynnikiem powiększenia, ponieważ nie pasuje do – GorillaApe

+1

Najlepszą optymalizacją tego wpisu był dla mnie tryb interpolacji. Zmiana 'Bilinear' na' NearestNeighbor' spowodowała, że ​​rysunek był 8 razy szybszy. – Pietu1998

+0

Wiele lat później nadal pomagasz ludziom z obrazem w winFormie – Nathan

3

GDI + prawdopodobnie nie jest najlepszym wyborem do gier. DirectX/XNA lub OpenGL powinny być preferowane, ponieważ wykorzystują wszelkie przyspieszenia grafiki i są bardzo szybkie.

3

GDI + nie jest demonem prędkości w żaden sposób. Każda poważna manipulacja obrazem zwykle musi przejść do rodzimej strony rzeczy (wywołania pInvoke i/lub manipulacja za pomocą wskaźnika uzyskanego przez wywołanie LockBits.)

Czy obejrzałeś XNA/DirectX/OpenGL ?. Są to platformy zaprojektowane do tworzenia gier i będą o rzędy wielkości bardziej wydajne i elastyczne niż przy użyciu struktury interfejsu użytkownika, takiej jak WinForm lub WPF. Wszystkie biblioteki oferują wiązania C#.

Można pInvoke na kod natywny, używając funkcji takich jak BitBlt, ale istnieje również obciążenie związane z przekraczaniem granic kodu zarządzanego.

+0

Polecam również OpenGL/DirectX. XNA nie jest tak naprawdę najlepszym wyborem per se z różnych powodów. Łatwość użycia, tak - ale na dłuższą metę to trochę upośledzenie. – Ani

+0

@antanthonline: Masz rację, są warte wspomnienia. DirectX jest lepszy od OpenGL, jeśli nie zależy Ci na obsłudze wielu platform. Czy możesz rozwinąć swój komentarz * "na dłuższą metę [XNA to] trochę handicapu" *.Prawdopodobnie mam kilka dziur w mojej wiedzy, ponieważ nie jestem poważnym twórcą gier, tylko moim hobby. –

+0

XNA przyjmuje kilka założeń do jego użycia. Jednym z poważnych ograniczeń, które mogę sobie wyobrazić, jest to, że wiele rendertargets nie może współdzielić bufora głębi, dzięki czemu powszechne scenariusze, takie jak wydajne wykonywanie odroczonego renderowania, są niemożliwe. Zobacz tutaj, aby uzyskać więcej informacji (http://forums.create.msdn.com/forums/p/64179/397431.aspx) – Ani

Powiązane problemy