TDirect2DCanvas是慢还是我做错了什么?

时间:2010-10-29 20:00:15

标签: delphi delphi-2010 gdi delphi-xe direct2d

在寻找替代GDI的替代品时,我试图在Windows 7中测试Delphi的2010 TDirect2DCanvas 性能。

我通过使用Direct2D绘制一条巨大的折线来测试它,结果非常慢,即使数据量少于我使用GDI进行相同测试的数量少500倍(我甚至没有使用位图作为后备缓冲区在GDI中,我只是直接绘制了表单画布。

所以我猜也是:
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,用于绘制50k线。

事情仍然可以改进,因为在使用消除锯齿时,Direct2D无法处理好的折线。如果禁用抗锯齿,则恰恰相反。

现在,使用Direct2D绘制50k线的时间,如果我绘制没有抗锯齿的大折线,则为~50ms。很好,呃!

事实是 GDI仍然比Direct2D 更快如果我绘制到位图并且在完成之后我将结果BitBlt返回到表单,它在<35ms 绘制 >,并具有相同的图形质量。并且,Direct2D似乎也在使用后备缓冲区(它只是在调用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

如果有人能解释我为什么这么慢,我会很高兴的。示例代码非常感谢。

4 个答案:

答案 0 :(得分:26)

问题是打开了抗锯齿功能。禁用抗锯齿功能,Direct2D的性能将与GDI相当或更快。要在创建TDirect2DCanvas之后执行此操作,请进行以下调用:


  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;

答案 1 :(得分:3)

Direct2D依赖于驱动程序和硬件实现,因此根据您运行的硬件和驱动程序,您必然会遇到性能异常(与3D渲染引擎面临的问题相同)。

例如,在渲染线的问题上,您可能会遇到一些(隐藏的)底层硬件缓冲区问题:在给定硬件+驱动程序上,绘制折线时,如果基础数据大小低于某个阈值,则性能可以很高,完全硬件加速。超过该阈值,您可能会回到部分软件或未经优化的路径,性能将直线下降。阈值取决于硬件,驱动程序和画笔/绘图选项,可以存在与否。

这些问题与通过OpenGL或常规DirectX渲染2D或3D时的问题相同,如果您偏离了经过良好渲染的渲染路径,事情就不那么美好了。

就渲染非抗锯齿图形而言,我的建议是坚持使用GDI,这些实现在广泛的硬件支持下非常可靠。

对于抗锯齿图形,GDI +,Graphics32,AGG以及大体上仅限软件的解决方案是优选的IME 每当 您无法控制最终用户硬件。否则,请准备好迎接客户支持问题。

答案 2 :(得分:3)

在我的所有基准测试中,OpenGL(有和没有MSAA抗锯齿)比GDI,GDI +或Direct2D更快,因为绘制2D元素(如多边形,线条,矩形等)的特殊情况。

答案 3 :(得分:1)

相比之下,GDI +速度怎么样?

我们编写了一个免费/开源单元,能够使用GDI +引擎渲染任何VCL TCanvas内容(使用TMetaFile)。

在实践中,表现非常好,并且正在进行反对... 我们在几个项目中使用它,将常规组件内容绘制到位图中,然后使用此位图在屏幕上绘制表单内容(这将避免任何闪烁问题)。 通过反对,营销人员对结果感到高兴,其他程序员(使用C#或WPF)想知道它是如何工作的:绘图速度非常快,应用程序是反应性的(如构建良好的Delphi应用程序),使用非常好内存很少,屏幕上的结果看起来很现代(特别是如果您使用Calibri或系统上提供的此类字体)。

请参阅http://synopse.info/forum/viewtopic.php?id=10

它适用于任何版本的Delphi(从Delphi 6到Delphi XE),适用于任何版本的Windows(XP,Vista,Seven) - 需要使用以前的操作系统部署标准gdiplus.dll。 / p>

我们的部门使用pascal代码在XP上进行GDI到GDI +转换,在Vista,Seven或者如果在PC上安装了Office 2003/2007,则使用本机Microsoft隐藏API。