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:
- 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.
- GDI+ działa odwrotnie, im mniejsze okno, tym rysowanie trwa krócej, ale ogólnie jakby wolniej, niż w przypadku zwykłego GDI.
- 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:
- 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?
- 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.