22

在寻找替代 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,我发现在使用 anti-aliasing 时,大型折线的速度很慢。所以我按照 Chris 的建议禁用了抗锯齿,绘制 50k 线的性能从 ~6000ms 到 ~3500ms。

事情仍然可以改进,因为 Direct2D在使用 anti-aliasing 时不能很好地处理折线。禁用抗锯齿功能则相反。

现在用 Direct2D 绘制 50k 线的时间,如果我在没有抗锯齿的情况下绘制大折线,大约是 50 毫秒。不错,嗯!

问题是,如果我绘制位图, 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

4 回答 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;
于 2010-11-05T16:44:20.710 回答
3

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

于 2012-11-16T10:18:46.030 回答
3

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

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

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

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

对于抗锯齿图形、GDI+、Graphics32、AGG 以及总体而言,您无法控制最终用户硬件时,纯软件解决方案是更可取的 IME。否则,请为客户支持问题做好准备。

于 2010-11-08T07:49:12.137 回答
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、7 - 需要在以前的操作系统中部署标准 gdiplus.dll)。

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

于 2010-11-07T06:36:58.193 回答