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

发布于 2024-09-29 09:50:47 字数 3460 浏览 2 评论 0原文

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

通过使用 Direct2D 绘制一条巨大的折线进行测试,结果速度慢得离谱,即使速度慢了 500 倍数据量比我使用 GDI 运行相同测试的数据量(我什至没有使用位图作为 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 条线时,性能从约 6000 毫秒提高到约 3500 毫秒。

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

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

问题是,如果我绘制到位图,并且完成后我将结果 BitBlt 返回到表单,则 GDI 仍然比 Direct2D 更快,它以约 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< /a>

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

While looking for alternatives to replace GDI, I was trying to test Delphi's 2010 TDirect2DCanvas performance in Windows 7.

I tested it by drawing a huge polyline using Direct2D and the result was absurdly slow, even with 500 times less data than the amount I've ran the same test using GDI (and I didn't even use a bitmap as backbuffer in GDI, I just drew to the form canvas directly).

So I guess either:
a) Direct2D is slower than GDI;
b) TDirect2DCanvas is slow;
c) I'm doing something wrong
and hopefully it's c).

The test code I wrote is:

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.

Also, I'm really willing to draw long polylines in real code, as a system I'm working on need to draw plenty of ~2500 points polylines (at least 10K of them).

Updated (2010-11-06)

I've found out earlier that Direct2D doesn't seem to like polylines, it draws faster if you use a lot of single lines (2 points polylines).

Thanks to Chris Bensen I found out the slowness was with large polylines while using anti-aliasing. So I disabled anti-aliasing as Chris suggested and performance went from ~6000ms to ~3500ms for drawing 50k lines.

Things could still be improved because Direct2D just doesn't handle well polylines while using anti-aliasing. With anti-aliasing disabled it's just the opposite.

Now the time for drawing with Direct2D the 50k lines, if I draw the large polyline without anti-aliasing, is ~50ms. Nice, eh!

The thing is that GDI is still faster than Direct2D if I draw to a bitmap and after it's done I BitBlt the result back to the form, it paints at ~35ms, and with the same graphics quality. And, Direct2D also seems to be using a backbuffer already (it just draws when EndDraw() is called).

So, can this be improved somehow to make using Direct2D worth speed-wise?

Here's the updated code:

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;

By the way, even if I use Chris' suggestion of creating the geometry beforehand the speed is about the same speed as GDI, but still not faster.

My computer is running Direct3D and OpenGL apps normally and here's dxDiag output: http://mydxdiag.pastebin.com/mfagLWnZ

I'll be glad if anyone can explain me why is this slowness. Sample code is much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

岁月无声 2024-10-06 09:50:47

问题是抗锯齿功能已打开。禁用抗锯齿功能后,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;

The problem is antialiasing is turned on. Disable antialiasing and the performance of Direct2D will be on par or faster than GDI. To do that after TDirect2DCanvas is created, make this call:


  FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

TDirect2DCanvas is interface compatible where possible with TCanvas so it can be a drop in replacement with TCanvas, so some of the drawing routines are are a bit inefficient. For example, Polyline creates a geometry each time it is called and throws it away. To increase performance keeping the geometry around.

Take a look at the implementation for TDirect2DCanvas.Polyline and hoist that out into your application for something like this:


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;

And then draw it like so:


procedure TForm2.WMPaint(var Message: TWMPaint);
begin
  FD2DCanvas.BeginDraw;
  FD2DCanvas.Pen.Color := clRed;
  FD2DCanvas.RenderTarget.DrawGeometry(FGeometry, FD2DCanvas.Pen.Brush.Handle);
  FD2DCanvas.EndDraw;
end;
陌伤浅笑 2024-10-06 09:50:47

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

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

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

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

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

Direct2D relies on the driver and hardware implementation, so you're bound to have performance oddities depending on the hardware and driver you're running on (same bag of issues as 3D rendering engines face).

For instance on the issue of rendering lines, you'll likely face some (hidden) underlying hardware buffer issues: on a given hardware+driver, when drawing a polyline, if the underlying datasize is below a certain threshold, the performance could be high, with full hardware acceleration. Above that threshold, you could be falling back to a partially software or unoptimized path, and performance will plummet. The threshold will depend on hardware, driver and brush/drawing options, can be there, or not.

These are the same issues as when rendering 2D or 3D via OpenGL or regular DirectX, if you stray outside of well trodden rendering paths, things aren't so rosy.

As far as rendering non-antialiased graphics goes, my advice would be to stick with GDI, the implementations are solid with widespread hardware support.

For antialiased graphics, GDI+, Graphics32, AGG, and by and large, software-only solutions are preferable IME whenever you have no control over the end-user hardware. Otherwise, prepare yourself for customer support issues.

深海夜未眠 2024-10-06 09:50:47

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

In all my benchmark tests OpenGL (with and without MSAA antialiasing) is faster than GDI, GDI+ or Direct2D, for the particular case of drawing 2D elements like polygons, lines, rectangles, etc.

撞了怀 2024-10-06 09:50:47

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

What about GDI+ speed, in comparison?

We wrote a free/open source unit, able to render any VCL TCanvas content (using a TMetaFile) using the GDI+ engine.

In practice, performance is very good, and anti-aliaising was on...
We use this in several projects, drawing regular components content into a bitmap, then using this bitmap for drawing the form content on screen (this will avoid any flicker problem).
And with anti-aliaising, marketing people were happy about the result, and other programmers (using C# or WPF) were wondering how it was working: the drawing is very fast and the applications are reactive (like well built Delphi apps), use very little memory, and the result on screen looks modern (especially if you use Calibri or such fonts if available on your system).

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

It will work with any version of Delphi (from Delphi 6 up to Delphi XE), and will work on any version of Windows (XP, Vista, Seven - need to deploy the standard gdiplus.dll with previous OS).

Our unit uses pascal code for the GDI to GDI+ conversion on XP, and native Microsoft hidden API under Vista, Seven or if Office 2003/2007 is installed on the PC.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文