Szybkie wyświetlanie bitmapy na ekranie (GDI / GDI+)

Odpowiedz Nowy wątek
2019-11-05 19:04
0

Mam spory problem z regularnym wyświetlaniem klatek obrazu (bitmap) na ekranie. Chcąc poprawić wydajność, całkowicie porzuciłem VCL i jako DC (device context) używam okienka stworzonego przy pomocy czystego WinApi. Próbowałem wyświetlać bitmapę na dwa sposoby, przy pomocy starszego GDI i nowszego GDI+.

GDI

{Procedurka odpowiedzialna za tworzenie okna}

//MainDC i BufferDC to HWND
//BMP i ObjectKeeper to HBITMAP
//Handle to uchwyt okna

MainDC := GetDC(Handle);
BufferDC := CreateCompatibleDC(0);
BMP := LoadImage(hInstance, 'D:\Test.bmp', IMAGE_BITMAP, 1920, 1080, LR_LOADFROMFILE);
ObjectKeeper := SelectObject(BufferDC, BMP);

{Obsługa komunikatu WM_PAINT}

BeginPaint(Handle, PS);
BitBlt(MainDC, 0, 0, 1920, 1080, BufferDC, 0, 0, SRCCOPY);
EndPaint(Handle, PS)

GDI+

{Procedurka odpowiedzialna za tworzenie okna}

//Graphics to klasa TGPGraphics
//IMG to klasa TGPImage

MainDC := GetDC(Handle);
Graphics := TGPGraphics.Create(MainDC);
IMG := TGPImage.Create('D:\Test.bmp', False);

{Obsługa komunikatu WM_PAINT}

BeginPaint(Handle, PS);
Graphics.DrawImage(IMG, 0, 0);
EndPaint(Handle, PS)

Wnioski:

  1. GDI jest znacznie szybszy przy rysowaniu dużych powierzchni. Im większy obszar roboczy okna, tym rysowanie trwa krócej. Np. dla okienka 300x300 trwa to u mnie ok.6 ms, ale już w trybie pełnoekranowym, przy rozdzielczości 1080p, ok. 1 ms.
  2. GDI+ działa odwrotnie, im mniejsze okno, tym rysowanie trwa krócej, ale ogólnie jakby wolniej, niż w przypadku zwykłego GDI.
  3. W obu przypadkach czas rysowania jest bardzo niestabilny. Raz okno rysuje się 1 milisekundę, za chwilę już 3, by następnie skoczyć do 5 i potem wrócić do około 1.

Pytania:

  1. Czy ktoś, kto ogarnia GDI/GDI+ mógłby mi doradzić, jak przy tych bibliotekach uzyskać w miarę sensowną i konsekwentną szybkość wyświetlania bitmapek na ekranie, niezależnie od wielkości okna?
  2. Czy znacie może jakiś alternatywny sposób, na osiągnięcie mojego zamierzonego celu? Zaznaczam, że chcę to załatwić software'owo, a więc bez użycia bibliotek, wspieranych przez GPU (a więc np. Direct2D, Direct3D, OpenGL itd.).

Głównie ten rozstrzał czasu rysowania mi przeszkadza. Mam silnik, który w zamyśle ma wyświetlać 60 klatek na sekundę, czyli ok. 16 ms na klatkę. Całość działa tak, że klatka jest renderowana najszybciej jak się da (np. w 9 ms), następnie silnik czeka w bezczynności do pełnych 16 ms (a więc na tym przykładzie, przez 7 ms), by następnie wyświetlić ją na ekranie. Tyle że przecież rysowanie na ekranie też swoje trwa, więc wypadałoby ten czas uwzględnić w obliczeniach. Tylko jak tu uwzględnić coś, co raz trwa 1 ms, a innym razem 5 ms?

Teoretycznie mógłbym to obejść (przynajmniej częściowo) w ten sposób, że silnik zamiast czekać do pełnych 16 ms, mógłby rysować kolejne klatki zaraz po wyrenderowaniu, tyle że sam bufor klatkowy byłby odświeżany co 16 ms. W ten sposób mimo nieregularnej liczby klatek na sekundę, obraz zachowywałby płynność stałych 60 klatek, a niektóre byłby po prostu wyświetlane wielokrotnie. Minus jest jednak taki, że całość dużo bardziej obciąży CPU, więc to też nie jest idealne rozwiązanie.

No ale niezależnie od implementacji powyższego, chciałbym najpierw nieco zoptymalizować to rysowanie. Wszelkie sugestie mile widziane.

edytowany 2x, ostatnio: Crow, 2019-11-05 19:05

Pozostało 580 znaków

2019-11-07 02:41
0
Crow napisał(a):

Zaznaczam, że chcę to załatwić software'owo, a więc bez użycia bibliotek, wspieranych przez GPU (a więc np. Direct2D, Direct3D, OpenGL itd.).

Zależy ci na wydajności, ale narzucasz sobie takie dziwne i niezrozumiałe ograniczenie.

Renderować bitmapę możesz sobie dowolnym sposobem, ale nie ma powodu aby odrzucać np. Direct3D jako API wyświetlające bitmapę na ekranie, jeżeli miałoby to być akurat działać szybciej.

Pozostało 580 znaków

2019-11-07 04:34
0

@Azarien: dawniej DirectX nie było, a gry (w tym 3D) pisano – fajnie zrobić to samo. :)


edytowany 1x, ostatnio: furious programming, 2019-11-07 04:34

Pozostało 580 znaków

2019-11-07 09:03
0
furious programming napisał(a):

@Azarien: dawniej DirectX nie było, a gry (w tym 3D) pisano – fajnie zrobić to samo. :)

Ale nic nie stoi na przeszkodzie żeby używać Direct3D czy OpenGL jako wyjściowego API jeżeli okaże się że to działa lepiej/szybciej/wygodniej niż na przykład GDI.
Cały czas mówimy o „regularnym wyświetlaniu klatek obrazu”, nie o budowaniu sceny 3D przy użyciu tych bibliotek.

Sam bym pewnie użył do tego DirectDraw, która to nazwa jeszcze w tym wątku nie padła. Tak wiem że jest „deprecated”, ale co z tego, ponadto DDraw znam a GDI nie bardzo.

edytowany 3x, ostatnio: Azarien, 2019-11-07 09:07

Pozostało 580 znaków

2019-11-07 09:56
0

@Azarien: Tak jak furious napisał - bo mogę :). Gdybym chciał zrobić profesjonalną grę, to jasne, wtedy najlepszym wyjściem byłoby nie tylko korzystanie z zaawansowanych bibliotek 3D jak Direct3D czy OpenGL, ale nawet użycie któregoś z darmowych silników (np. Unity), jednak nie taki mam cel. Ja chcę po prostu stworzyć retro-silnik, napędzany metodami sprzed upowszechnienia się akceleracji sprzętowej (przy okazji ucząc się od początku do końca jak wygląda pipeline przy tworzeniu grafiki 3D), który pozwoli mi uzyskać efekt zbliżony do renderowanych software'owo Unreal'a, Quake'a czy Half-Life'a (a niektóre z tych renderów potrafią być zadziwiająco szybkie!).

DirectDraw to chyba raczej niezbyt dobry pomysł. Gdyby sięgać do tej rodziny bibliotek, to już lepiej do Direct2D. Nawet próbowałem, ale ten wrapper od Embarcadero jest jakiś dziwny (stworzyli TDirect2DCanvas obudowując TCustomCanvas i mam wrażenie, że po drodze zgubili z połowę funkcjonalności...), a dokumentacja do niego jest uboga i dziurawa (nawet ich własne przykłady się nie kompilują ;s). Próbowałem to obejść, sięgając bezpośrednio do stosownych struktur, klas i funkcji (korzystając z dokumentacji MSDN), ale całość mi się cały czas wysypywała, mimo że robiłem wszystko zgodnie z zaleceniami (tak jak w przypadku GDI/GDI+, z którymi nie miałem takich problemów).

Jeszcze chyba sprawdzę te FMXy. Ogólnie to ja wielu funkcjonalności nie potrzebuję. Ot, muszę mieć bufor pikseli, który będę mógł łatwo i szybko modyfikować, a potem wyświetlać na ekranie. Całą resztę robię sam, własnymi funkcjami, więc nie potrzebuję żadnej wbudowanej akceleracji rysowania linii czy prymitywów,

edytowany 7x, ostatnio: Crow, 2019-11-07 10:07

Pozostało 580 znaków

2019-11-07 11:25
1
Crow napisał(a):

@Azarien: Tak jak furious napisał - bo mogę :). Gdybym chciał zrobić profesjonalną grę, to jasne, wtedy najlepszym wyjściem byłoby nie tylko korzystanie z zaawansowanych bibliotek 3D jak Direct3D czy OpenGL, ale nawet użycie któregoś z darmowych silników (np. Unity), jednak nie taki mam cel. Ja chcę po prostu stworzyć retro-silnik, napędzany metodami sprzed upowszechnienia się akceleracji sprzętowej (przy okazji ucząc się od początku do końca jak wygląda pipeline przy tworzeniu grafiki 3D), który pozwoli mi uzyskać efekt zbliżony do renderowanych software'owo Unreal'a, Quake'a czy Half-Life'a (a niektóre z tych renderów potrafią być zadziwiająco szybkie!).

Nadal nie rozumiesz. Mam na myśli użycie D3D tylko jako output, rysowałbyś tylko jeden wielki bitmapowy prostokąt. To co na nim będzie generujesz sobie software'owo jak chcesz.
Jest szansa, choć nie ma gwarancji, że będzie to działać sprawniej niż przez GDI.

Przykładem takiej aplikacji jest DOSBox - to co masz na ekranie jest efektem emulacji, siłą rzeczy wszystko jest renderowane programowo, ale może być (w zależności od ustawień) wyrzucane na ekran przez GDI, DDraw, OpenGL, i co tam jeszcze.

Innym przykładem jest MPC-HC (video player) - wśród różnych trybów renderowania filmów jest VMR-9, używający Direct3D 9 (pozostałe nie wiem czego używają, ale czegoś na pewno)

PS. GDI+ jest znany z tego że jest wolny, raczej nie nadaje się do tego celu.

edytowany 1x, ostatnio: Azarien, 2019-11-07 11:26

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0