c++ - GDI versus Direct2D
winapi (2)
Hace algún tiempo rechacé la migración del código de renderizado de GDI a Direct2D debido al bajo rendimiento. Como entiendo de google, el rendimiento de Direct2D depende de las optimizaciones de controladores y hardware, y no se debe esperar la misma velocidad en hardware diferente. GDI es bastante viejo y funciona igual en casi todos lados.
Debo decir que he tratado de usarlo para dibujar primitivas de geometría simple, mientras que Direct2D parece ser una biblioteca mucho más robusta y tal vez podría haber un aumento del rendimiento en escenarios complejos, pero este no es mi caso.
Si necesita un rendimiento GDI con mejor calidad, intente utilizar OpenGL o Direct3D directamente.
Esta es una pregunta relacionada: ¿TDirect2DCanvas es lento o estoy haciendo algo mal?
Estoy programando una simulación en este momento, y quiero transferir mi aplicación de GDI a usar Direct2D. Pero mi código Direct2D es mucho más lento que mi código GDI.
Represento muchas elipsis en la pantalla. En mi aplicación GDI dibujo un contexto de dispositivo de memoria y luego uso BitBlt para dibujar en el contexto del dispositivo de Windows. Con Direct2D, dibujo en un ID2D1HwndRenderTarget.
Mi problema es que, al usar GDI, puedo dibujar fácilmente más de 400 elipses y aún tengo 400 FPS. Cuando hago el mismo número de elipses con Direct2D, mi FPS cae a 30 FPS.
Ya cambié el antialiasing pero realmente no ayuda. Lo interesante es que dibujar solo unas pocas elipsis es más rápido en Direct2D en comparación con GDI. ¿Hay algo que pueda hacer para mejorar el rendimiento en Direct2D, o debo mantener mi aplicación usando GDI?
Aquí está mi código de dibujo usando GDI:
VOID Begin() {
SelectObject(this->MemDeviceContext, this->MemoryBitmap);
this->BackgroundBrush = CreateSolidBrush(this->BackgroundColor);
HBRUSH OldBrush = (HBRUSH)SelectObject(this->MemDeviceContext, this->BackgroundBrush);
Rectangle(this->MemDeviceContext, -1, -1, 801, 601);
SelectObject(this->MemDeviceContext, OldBrush);
DeleteObject(this->BackgroundBrush);
SetViewportOrgEx(this->MemDeviceContext, 400, 300, &this->OldOrigin);
}
VOID End() {
SetViewportOrgEx(this->MemDeviceContext, this->OldOrigin.x, this->OldOrigin.y, 0);
BitBlt(this->DeviceContext, 0, 0, 800, 600, this->MemDeviceContext, 0, 0, SRCCOPY);
}
Entre mi función Begin y End, dibujo mis elipses de la manera estándar GDI.
Aquí están mis funciones de inicio y fin usando Direct2D:
VOID BeginDrawing() {
this->RenderTarget->BeginDraw();
RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::CornflowerBlue));
RenderTarget->SetTransform(this->ScalingMatrix * this->TranslateMatrix);
}
VOID EndDrawing() {
this->RenderTarget->EndDraw();
}
Y así es como configuré mis interfaces Direct2D. Todo está envuelto en clase; es por eso que no puedo publicar el código completo:
if(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &Direct2DFactory) != S_OK)
throw std::runtime_error("RENDERWINDOW::InitializeDirect2D: Failed to create a factory interface.");
RECT WindowRect;
memset(&WindowRect, 0, sizeof(RECT));
GetClientRect(this->WndHandle, &WindowRect);
D2D1_SIZE_U WindowSize = D2D1::SizeU(WindowRect.right, WindowRect.bottom);
Direct2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE),
D2D1::HwndRenderTargetProperties(this->WndHandle, WindowSize, D2D1_PRESENT_OPTIONS_IMMEDIATELY), &RenderTarget);
Gracias de antemano.
Un error común con los primeros intentos de Direct2D es que los desarrolladores no guardan en caché los recursos de D2D y en su lugar crean y destruyen recursos con demasiada frecuencia. Si todas sus elipsis son de un tamaño similar, debe crear y almacenar en caché este objeto elipse una vez. Si tiene 30 tamaños / formas diferentes, cree versiones de elipse para los 30 tamaños / formas solo una vez. Esto acelera significativamente Direct2D. Lo mismo ocurre con Rectangles y todas las demás primitivas. Escalar un objeto almacenado en caché versus creación / destrucción repetida también es una solución para algunos escenarios si existen demasiadas variaciones para una primitiva, aunque es ideal utilizar un recurso en su tamaño original y las tarjetas de memoria tienen bastante memoria para almacenar sus recursos.
Las elipsis de Gdi se ven absolutamente terribles y el uso directo de Direct3D es bastante complejo, especialmente para las elipses, los polígonos grandes y las primitivas de mayor nivel. Con el uso adecuado de Direct2D, podrá obtener una buena velocidad y un renderizado de alta calidad.