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

24

在寻找替代GDI的方案时,我试图测试Delphi 2010的TDirect2DCanvas在Windows 7上的性能表现。

我通过使用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 似乎不喜欢折线,在使用许多单线段(两点折线)时绘制更快。

感谢 Chris Bensen,我发现缓慢是由于 启用抗锯齿 时绘制大折线造成的。所以我像 Chris 建议的那样禁用了抗锯齿,性能从 ~6000ms 提高到 ~3500ms,可以绘制 50k 条线。

Direct2D 在 启用抗锯齿 时无法很好地处理折线,而在禁用抗锯齿时则相反。

如果我没有启用抗锯齿来绘制大型折线,使用 Direct2D 绘制 50k 条线的时间为 ~50ms。很不错,对吧!

问题在于,如果我将结果绘制到位图上并在完成后 BitBlt 回表单,GDI 仍然比 Direct2D 快,在 ~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

如果有人可以解释为什么会出现这种缓慢,我会很高兴。欢迎提供示例代码。


尝试使用性能分析器。也许有一些昂贵的操作在幕后进行,而这并不容易被察觉。 - Kenneth Cochran
4
我没有答案,但我知道你不是第一个提到它似乎不比GDI快的人。 - GrandmasterB
4个回答

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;

我将尝试您的建议,特别是禁用抗锯齿,尽管我不记得线条被完全抗锯齿,但我可能错了。但请注意,多折线for实际上是我进行的优化,因为绘制大型折线会导致难以忍受的延迟(有一次我等待了超过2分钟才画出来,然后放弃了)。像Craig所说的那样,欢迎来到SO!=) - Trinidad
到目前为止,结果如下:带抗锯齿的D2D花费了7000毫秒;不带抗锯齿的D2D花费了3500毫秒;带抗锯齿并直接创建路径的D2D花费了无限时间;不带抗锯齿并直接创建路径的D2D花费了50毫秒;在窗体上直接使用Polyline(FData)绘制的简单GDI花费了240毫秒;最后,不带抗锯齿并直接使用FD2DCanvas.Polyline(FData)的D2D只需要50毫秒。可以看出,问题出在对长折线进行抗锯齿处理上,不要问我为什么。对于短线条,带抗锯齿的速度更快,而不带抗锯齿则相反...奇怪。谢谢! - Trinidad
1
哦,但是之前我忘了在绘制到位图时对GDI进行基准测试,然后将结果复制到绘制完成的窗体上。令人惊讶的是,它以34毫秒绘制了50,000条线条,GDI仍然比带有锯齿的D2D版本更快... - Trinidad
1
谢谢,很高兴能来到这里。希望我能抽出一些时间来回答一些问题。 - Chris Bensen
其实这很有道理。试试我的示例,首先创建几何图形,启用抗锯齿,然后创建数据并在线之间留出更多空间,以便您可以看到发生了什么。现在更改RenderTarget.DrawGeometry调用中笔的大小。如果将其设置得非常小,例如0.2,则渲染将花费很长时间,您可以看到它们是纸片一样的薄线。由于亚像素精度,这种绘图非常昂贵。 - Chris Bensen

3

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

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

这些是在OpenGL或常规DirectX中渲染2D或3D时遇到的相同问题,如果您偏离了常用的渲染路径,情况并不乐观。

至于渲染非反锯齿图形,我的建议是使用GDI,实现稳定且具有广泛的硬件支持。

对于平滑处理的图形,IME更喜欢使用GDI+,Graphics32,AGG以及基本上仅限于软件解决方案,无论您是否控制最终用户硬件。否则,请准备好客户支持问题。


问题是我想要更好的性能,而不是更好的线路质量。通过直接使用Direct3D,我可以实现比GDI快10倍的速度,使用OpenGL也是如此,因此我认为问题不在于我的驱动程序或图形卡。 - Trinidad
“我能”是指你已经这样做了,还是你认为你应该这样做?GDI 也可以进行硬件加速,因此如果您不离开 GDI 的甜点区域,则可能没有太多可获得的收益。 - Eric Grange
我的意思是“我曾经在基准测试中达到了那个速度”,“could”是“can”的过去式。我确实进行了基准测试。这就是我不明白为什么Direct2D的运行速度与GDI相同的原因之一。 - Trinidad
Direct2D和GDI都是硬件加速的,为什么一个会运行得更快?差异只在驱动程序和API开销上,而Direct2D并不是那么“直接”。 - Eric Grange

3

在我所有的基准测试中,OpenGL(无论是启用还是禁用MSAA抗锯齿)都比GDI、GDI+或Direct2D更快,特别是对于绘制2D元素(如多边形、线条、矩形等)的情况。


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)。

我们的单元在XP上使用Pascal代码进行GDI到GDI+转换,在Vista、Seven或PC安装了Office 2003/2007时使用本机Microsoft隐藏API。


我刚在http://www.windows7taskforce.com/view/3607上读到,Windows 7中的GDI+加速功能已经失效。这是微软的又一大败笔... GDI+在XP或Vista中表现良好,但在Seven中速度较慢。但我在网上找不到真正的基准测试数据。有什么想法吗? - Arnaud Bouchez
很好,我喜欢GDI+。但是问题在于为什么使用Direct2D没有任何好处:速度与GDI相同,没有抗锯齿(启用抗锯齿会更慢)。起初我需要更快的速度,而不是更好的质量,因为我需要在屏幕上绘制大量数据集,而使用GDI的性能无法承受用户交互。 - Trinidad
顺便提一下,使用最快的CompositingQuality GDI+比GDI慢大约8倍。 - Trinidad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接