【问题标题】:Is TDirect2DCanvas slow or am I doing something wrong?TDirect2DCanvas 是慢还是我做错了什么?
【发布时间】:2011-05-02 14:40:33
【问题描述】:

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

事情仍有待改进,因为 Direct2D 在使用抗锯齿时不能很好地处理折线。禁用抗锯齿功能则相反。

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

问题是 GDI 仍然比 Direct2D 快,如果我绘制位图并在完成后将结果 BitBlt 返回到表单,它会以大约 35 毫秒的速度绘制 >,并具有相同的图形质量。而且,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

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

【问题讨论】:

  • 尝试使用性能分析器。也许幕后正在进行一项不明显的昂贵操作。
  • 我没有任何答案,但我知道你不是第一个提到它似乎并不比 GDI 快的人。

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


【解决方案1】:

问题是抗锯齿已打开。禁用抗锯齿,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 分钟才绘制然后放弃)。是的,就像克雷格所说,欢迎来到 SO! =)
  • 目前的结果是:7000ms D2D with antialiasing; 3500ms D2D 无抗锯齿; +INFms D2D 抗锯齿 + 直接创建路径; 50ms D2D 无抗锯齿 + 创建路径直接; 240 毫秒 GDI,直接在表单上直接绘制 Polyline(FData);并且...最后 50ms D2D 没有抗锯齿并直接使用FD2DCanvas.Polyline(FData)。如您所见,问题在于对长折线进行抗锯齿处理,不要问我为什么。使用抗锯齿短线要快得多,而没有抗锯齿则恰恰相反......奇怪。谢谢!
  • 哦,但早些时候我忘记在绘制位图时对 GDI 进行基准测试,然后在绘制完成后将结果传送到表单中。令人惊讶的是它在 34 毫秒内绘制了 50k 行,GDI 仍然比别名 D2D 版本快...
  • 谢谢,很高兴来到这里。希望我有时间回答一些问题。
  • 其实很有意义。试试我上面的例子,我首先创建几何图形,启用抗锯齿,然后在行之间创建更多空间的数据,这样你就可以看到发生了什么。现在在 RenderTarget.DrawGeometry 调用中更改画笔的大小。如果你把它做得非常小,就像 0.2 渲染将永远需要,你就会明白为什么,它们是纸上的细线。由于亚像素精度,这种绘图非常昂贵。
【解决方案2】:

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

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

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

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

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

【讨论】:

  • 问题是我想要更好的性能,而不是更好的线路质量。通过直接使用 Direct3D,我可以达到比 GDI 快 10 倍的速度,使用 OpenGL 也是如此,所以我想问题不是我的驱动程序或 gfx 卡。
  • “我可以”,你的意思是你这样做,还是你认为你应该这样做? GDI 也可以进行硬件加速,因此如果您不偏离 GDI 的最佳位置,可能不会有太多收获。
  • 我的意思是“我曾经在基准测试中达到了那个速度”,could 就像过去时 can =) 我做了基准测试。这就是我不明白 Direct2D 运行速度与 GDI 相同的原因之一。
  • Direct2D 和 GDI 都是硬件加速的,为什么会跑得更快呢?区别仅在于驱动程序和 API 开销,而 Direct2D 并不是那么“直接”。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    相比之下,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)。

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

    【讨论】:

    • 我刚刚在windows7taskforce.com/view/3607 中读到,Windows 7 中的 GDI+ 加速已损坏。 Micro$oft 的另一个缺点... GDI+ 在 XP 或 Vista 中表现良好,但在 7 中较慢。但是我无法在网络上获得一些真正的基准。有什么想法吗?
    • 很好,我喜欢 GDI+。但这里的问题是为什么使用 Direct2D 没有任何好处:与 GDI 相同的速度,没有抗锯齿(抗锯齿会慢得多)。一开始我想要更快的速度,而不是更好的质量,因为我需要在屏幕上绘制大量数据,这对于使用 GDI 性能时的用户交互来说是无法容忍的。
    • 顺便说一句,虽然使用最快的 CompositingQuality GDI+ 比 GDI 慢大约 8 倍。
    猜你喜欢
    • 1970-01-01
    • 2010-12-14
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多