代码之家  ›  专栏  ›  技术社区  ›  Trinidad

tDirect2Canvas速度慢还是我做错了什么?

  •  22
  • Trinidad  · 技术社区  · 14 年前

    在寻找替代GDI的方法时,我试着测试Delphi的2010 t方向2画布 在Windows 7中的性能。

    我用direct2d绘制了一条巨大的多段线来测试它,结果非常慢,即使数据比我用gdi进行的相同测试少了500倍(我甚至没有在gdi中使用位图作为backbuffer,我只是直接绘制到表单画布)。

    所以我想要么:
    (a) Direct2d比GDI慢;
    b) tDirect2Canvas速度慢;
    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点多段线(其中至少有10个)。

    更新(2010-11-06)

    我之前发现Direct2d似乎不喜欢多段线,如果使用很多单段线(2点多段线),它的绘制速度会更快。

    多亏了 Chris Bensen 我发现慢下来的是大折线。 使用消除混叠时 . 所以我按照Chris的建议禁用了消除混叠,绘制50k行的性能从~6000ms提高到~3500ms。

    因为Direct2d不能很好地处理多段线,所以事情还是可以改进的。 使用消除混叠时 . 禁用消除混叠后,情况正好相反。

    现在用direct2d绘制50k线的时间是~50ms。很好,呃!

    问题是 GDI仍然比Direct2d快 如果我绘制一个位图,在位图完成后,我将结果返回表单,它将绘制 约35毫秒 具有相同的图形质量。而且,direct2d似乎已经在使用backuffer了(它只是在 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 回复  |  直到 9 年前
        1
  •  26
  •   Chris Bensen    14 年前

    问题是反锯齿已打开。禁用抗锯齿,Direct2d的性能将达到PAR或比GDI更快。要在创建tdirect2dcanavas后执行此操作,请执行以下调用:

    
      FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
    

    tDirect2dcans在可能的情况下与tcanvas接口兼容,因此可以用tcanvas替换掉它,因此某些绘图例程效率有点低。例如,折线每次调用时都会创建一个几何图形,并将其丢弃。以提高保持几何体周围的性能。

    查看tdirect2dcavans.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;
    
        2
  •  3
  •   Eric Grange    14 年前

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

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

    这些问题与通过OpenGL或常规DirectX渲染二维或三维时的问题相同,如果您偏离了被良好践踏的渲染路径,情况就不那么乐观了。

    至于呈现非抗锯齿图形,我的建议是坚持使用GDI,实现是可靠的,有广泛的硬件支持。

    对于抗锯齿图形、gdi+、graphics32、agg以及一般来说,仅软件解决方案是首选的IME 无论何时 您无法控制最终用户硬件。否则,就要为客户支持问题做好准备。

        3
  •  3
  •   David Berneda    12 年前

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

        4
  •  1
  •   Arnaud Bouchez    14 年前

    相比之下,gdi+速度如何?

    我们编写了一个免费/开源单元,能够使用gdi+引擎呈现任何VCL TCANVAS内容(使用tmeta文件)。

    在实际操作中,性能非常好,并采取了防飞措施。 我们在几个项目中使用它,将常规组件内容绘制成位图,然后使用该位图在屏幕上绘制表单内容(这将避免任何闪烁问题)。 随着反认证,市场营销人员对结果很满意,其他程序员(使用C或WPF)想知道它是如何工作的:绘图速度非常快,应用程序是反应式的(像构建良好的Delphi应用程序),使用很少的内存,屏幕上的结果看起来很现代。(尤其是如果您使用Calibri或类似字体(如果您的系统可用)。

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

    它将与Delphi的任何版本(从Delphi6到DelphiXE)一起工作,并将在Windows的任何版本上工作(XP、Vista、Seven-需要与以前的操作系统一起部署标准gdiplus.dll)。

    我们的设备在XP上使用Pascal代码进行gdi到gdi+的转换,在Vista、Seven或如果PC上安装了Office2003/2007,则使用本机的Microsoft隐藏API。