delphi delphi-2010 gdi delphi-xe direct2d

delphi - ¿TDirect2DCanvas es lento o estoy haciendo algo mal?



delphi-2010 gdi (4)

¿Qué hay de la velocidad de GDI +, en comparación?

Escribimos una unidad de fuente abierta / gratuita, capaz de representar cualquier contenido VCL TCanvas (usando un TMetaFile) usando el motor GDI +.

En la práctica, el rendimiento es muy bueno y se utilizó anti-aliaising ... Usamos esto en varios proyectos, dibujando contenido de componentes regulares en un mapa de bits, luego usando este mapa de bits para dibujar el contenido del formulario en la pantalla (esto evitará cualquier problema de parpadeo) ) Y con anti-aliaising, la gente de marketing estaba contenta con el resultado, y otros programadores (usando C # o WPF) se preguntaban cómo estaba funcionando: el dibujo es muy rápido y las aplicaciones son reactivas (como aplicaciones Delphi bien construidas), usan muy poca memoria, y el resultado en la pantalla parece moderno (especialmente si usa Calibri o tales fuentes si están disponibles en su sistema).

Ver http://synopse.info/forum/viewtopic.php?id=10

Funcionará con cualquier versión de Delphi (desde Delphi 6 hasta Delphi XE), y funcionará en cualquier versión de Windows (XP, Vista, Seven) necesita implementar el estándar gdiplus.dll con el sistema operativo anterior.

Nuestra unidad utiliza el código pascal para la conversión de GDI a GDI + en XP, y la API oculta de Microsoft nativa bajo Vista, Seven o si Office 2003/2007 está instalado en la PC.

Mientras buscaba alternativas para reemplazar GDI, intentaba probar el rendimiento 2010 de Delphi TDirect2DCanvas en Windows 7.

Lo probé dibujando una gran polilínea usando Direct2D y el resultado fue absurdamente lento, incluso con 500 veces menos datos que la cantidad que he ejecutado la misma prueba usando GDI (y ni siquiera utilicé un mapa de bits como backbuffer en GDI, Acabo de dibujar directamente en el lienzo del formulario).

Entonces supongo que:
a) Direct2D es más lento que GDI;
b) TDirect2DCanvas es lento;
c) Estoy haciendo algo mal
y con suerte es c).

El código de prueba que escribí es:

unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Direct2D, D2D1; type TForm2 = class(TForm) private { Private declarations } FD2DCanvas: TDirect2DCanvas; FData: array[0..50000] of TPoint; public procedure CreateWnd; override; procedure WMSize(var Message: TWMSize); message WM_SIZE; procedure WMPaint(var Message: TWMPaint); message WM_PAINT; { Public declarations } end; var Form2: TForm2; implementation uses utils; {$R *.dfm} procedure TForm2.CreateWnd; var i: Integer; begin inherited; FD2DCanvas := TDirect2DCanvas.Create(Handle); for i := 0 to High(FData) do begin FData[i].X := Random(Self.ClientWidth div 2); FData[i].Y := Random(Self.ClientHeight); end; end; procedure TForm2.WMPaint(var Message: TWMPaint); var PaintStruct: TPaintStruct; begin BeginPaint(Handle, PaintStruct); try FD2DCanvas.BeginDraw; try FD2DCanvas.Polyline(FData); finally FD2DCanvas.EndDraw; end; finally EndPaint(Handle, PaintStruct); end; end; procedure TForm2.WMSize(var Message: TWMSize); begin if Assigned(FD2DCanvas) then begin ID2D1HwndRenderTarget(FD2DCanvas.RenderTarget).Resize(D2D1SizeU(ClientWidth, ClientHeight)); end; end; end.

Además, estoy realmente dispuesto a dibujar polilíneas largas en código real, ya que un sistema en el que estoy trabajando necesita dibujar un montón de ~ 2.500 puntos de polilíneas (al menos 10K de ellos).

Actualizado (2010-11-06)

Descubrí antes que a Direct2D no parece gustarle la polilínea, se dibuja más rápido si usa muchas líneas simples (polilíneas de 2 puntos).

Gracias a Chris Bensen descubrí que la lentitud era con polilíneas grandes al usar anti-aliasing . Así que deshabilité el anti-aliasing como sugirió Chris y el rendimiento pasó de ~ 6000ms a ~ 3500ms para dibujar 50k líneas.

Las cosas aún se pueden mejorar porque Direct2D simplemente no maneja polilíneas bien al usar anti-aliasing . Con anti-aliasing desactivado es todo lo contrario.

Ahora el tiempo para dibujar con Direct2D las 50k líneas, si dibujo la gran polilínea sin anti-aliasing, es ~ 50ms. Bien, eh!

Lo que pasa es que GDI es aún más rápido que Direct2D si dibujo un mapa de bits y, una vez hecho esto, BitBlt devuelve el resultado al formulario, pinta a ~ 35 ms y con la misma calidad de gráficos. Y, Direct2D también parece estar usando un backbuffer ya (simplemente se dibuja cuando se llama a EndDraw() ).

Entonces, ¿se puede mejorar de alguna manera para hacer que el uso de Direct2D valga la pena?

Aquí está el código actualizado:

type TArray = array[0..1] of TPoint; PArray = ^TArray; procedure TForm2.WMPaint(var Message: TWMPaint); var PaintStruct: TPaintStruct; begin FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); BeginPaint(Handle, PaintStruct); try FD2DCanvas.BeginDraw; try FD2DCanvas.Pen.Color := clRed; FD2DCanvas.Polyline(FData); finally FD2DCanvas.EndDraw; end; finally EndPaint(Handle, PaintStruct); end; end;

Por cierto, incluso si uso la sugerencia de Chris de crear la geometría de antemano, la velocidad es aproximadamente la misma velocidad que GDI, pero aún no es más rápida.

Mi computadora ejecuta aplicaciones Direct3D y OpenGL normalmente y aquí está la salida dxDiag: http://mydxdiag.pastebin.com/mfagLWnZ

Me alegrará si alguien puede explicarme por qué es esta lentitud. El código de muestra es muy apreciado.


Direct2D depende de la implementación del controlador y del hardware, por lo que es probable que tenga rarezas de rendimiento según el hardware y el controlador en el que se esté ejecutando (la misma cantidad de problemas que enfrentan los motores de renderización 3D).

Por ejemplo, en el tema de líneas de representación, es probable que se enfrenten a problemas de búfer de hardware subyacentes (ocultos): en un hardware / controlador determinado, al dibujar una polilínea, si el tamaño de datos subyacente está por debajo de un cierto umbral, el rendimiento podría ser alto , con aceleración de hardware completa. Por encima de ese umbral, podría estar retrocediendo a un camino parcialmente software o no optimizado, y el rendimiento se desplomará. El umbral dependerá del hardware, el controlador y las opciones de pincel / dibujo, puede estar allí, o no.

Estos son los mismos problemas que al renderizar en 2D o 3D a través de OpenGL o DirectX regular, si se pierde fuera de las rutas de representación trilladas, las cosas no son tan optimistas.

En lo que se refiere a la renderización de gráficos no antialias, mi consejo sería seguir con GDI, las implementaciones son sólidas y cuentan con un amplio soporte de hardware.

Para los gráficos antialias, GDI +, Graphics32, AGG y, en general, las soluciones de solo software son IME preferibles siempre que no tenga control sobre el hardware del usuario final. De lo contrario, prepárese para problemas de atención al cliente.


El problema es antialiasing está activado. Desactive el antialiasing y el rendimiento de Direct2D estará a la par o más rápido que GDI. Para hacerlo después de crear TDirect2DCanvas, realice esta llamada:

FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

TDirect2DCanvas es compatible con la interfaz siempre que sea posible con TCanvas, por lo que puede ser un reemplazo en sustitución de TCanvas, por lo que algunas de las rutinas de dibujo son un poco ineficientes. Por ejemplo, Polyline crea una geometría cada vez que se invoca y la descarta. Para aumentar el rendimiento manteniendo la geometría alrededor.

Eche un vistazo a la implementación de TDirect2DCanvas.Polyline y extiéndalo en su aplicación para algo como esto:

procedure TForm2.CreateWnd; var i: Integer; HR: HRESULT; Sink: ID2D1GeometrySink; begin ... D2DFactory.CreatePathGeometry(FGeometry); HR := FGeometry.Open(Sink); try Sink.BeginFigure(D2D1PointF(FData[0].X + 0.5, FData[0].Y + 0.5), D2D1_FIGURE_BEGIN_HOLLOW); try for I := Low(FData) + 1 to High(FData) - 1 do Sink.AddLine(D2D1PointF(FData[I].X + 0.5, FData[I].Y + 0.5)); finally Sink.EndFigure(D2D1_FIGURE_END_OPEN); end; finally hr := Sink.Close; end;

Y luego dibujarlo así:

procedure TForm2.WMPaint(var Message: TWMPaint); begin FD2DCanvas.BeginDraw; FD2DCanvas.Pen.Color := clRed; FD2DCanvas.RenderTarget.DrawGeometry(FGeometry, FD2DCanvas.Pen.Brush.Handle); FD2DCanvas.EndDraw; end;


En todas mis pruebas de referencia OpenGL (con y sin antialiasing de MSAA) es más rápido que GDI, GDI + o Direct2D, para el caso particular de dibujar elementos 2D como polígonos, líneas, rectángulos, etc.