我通过使用Direct2D绘制一个巨大的折线测试它,结果是荒谬的慢,即使是使用GDI运行相同测试的数据少了500倍的数据(我甚至没有使用位图作为GDI中的backbuffer,我直接画到窗体画布)。
所以我猜:
a)Direct2D比GDI慢;
b)TDirect2DCanvas缓慢;
c)我做错了事情
希望是c)。
我写的测试代码是:
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.
此外,我真的愿意在实际代码中绘制长折线,作为我正在开发的系统需要绘制大约2500点的折线(至少10K)。
更新(2010-11-06)
我以前发现Direct2D似乎不喜欢折线,如果你使用了很多单行(2点折线),它会更快地绘制。
感谢Chris Bensen,我发现使用反锯齿时,使用大折线时的缓慢。所以我禁止反锯齿,因为克里斯建议,性能从约6000ms到〜3500ms绘制5万行。
事情仍然可以改善,因为Direct2D在使用抗锯齿时不会处理好多线。消除抗锯齿功能恰恰相反。
现在用Direct2D绘制50k行的时候,如果我画大折线没有抗锯齿,就是〜50ms。好,呃!
事情是,如果我绘制一个位图,GDI仍然比Direct2D更快,在完成之后,我将BitBlt的结果返回到表单,它绘制在〜35ms,并具有相同的图形质量。而且,Direct2D也似乎使用了一个backbuffer(它只是在EndDraw()被调用时绘制)。
那么,可以用某种方式改进Direct2D的价值吗?
这里是更新的代码:
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;
顺便说一句,即使我使用Chris的建议来预先创建几何,速度与GDI的速度大致相同,但仍然不会更快。
我的电脑正常运行Direct3D和OpenGL应用程序,这里的dxDiag输出:http://mydxdiag.pastebin.com/mfagLWnZ
如果有人能解释为什么这么慢,我会很高兴的。示例代码非常感谢。
解决方法
FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
TDirect2DCanvas在可能的情况下与TCanvas接口兼容,所以它可以替代TCanvas,所以一些绘图例程有点低效。例如,Polyline在每次调用时创建几何,并将其抛弃。提高保持几何形状的性能。
看看TDirect2DCanvas.Polyline的实现,并提出你的应用程序,如下所示:
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;
然后画出如下所示:
procedure TForm2.WMPaint(var Message: TWMPaint); begin FD2DCanvas.BeginDraw; FD2DCanvas.Pen.Color := clRed; FD2DCanvas.RenderTarget.DrawGeometry(FGeometry,FD2DCanvas.Pen.Brush.Handle); FD2DCanvas.EndDraw; end;