问题描述
在寻找替代GDI的替代方法时,我正在尝试在Windows 7中测试Delphi在2010年的 TDirect2DCanvas 性能。
While looking for alternatives to replace GDI, I was trying to test Delphi's 2010 TDirect2DCanvas performance in Windows 7.
使用Direct2D绘制一条巨大的折线,结果非常缓慢,即使数据量比使用GDI运行相同测试的量少500倍(而且我什至没有在GDI中使用位图作为后备缓冲区,我只是想
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).
所以我想:
a) Direct2D比GDI慢;
b) TDirect2DCanvas速度很慢;
c)我做错了事
,希望它是c)。
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).
我编写的测试代码为:
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点折线(其中至少有1万条)。
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).
我之前发现Direct2D似乎不喜欢折线,如果您使用很多单线,绘制起来会更快(2点折线)。
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).
感谢我发现使用抗锯齿时,大型折线的运行速度很慢。因此,我按照Chris的建议禁用了抗锯齿功能,并且绘制了5万条线条的性能从〜6000ms变为〜3500ms。
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.
由于Direct2D不能处理,事情仍然可以得到改善。良好的折线使用抗锯齿功能。
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.
现在使用Direct2D绘制50k线的时间(如果我在不使用抗锯齿的情况下绘制大的折线)大约是50毫秒。很好,嗯!
Now the time for drawing with Direct2D the 50k lines, if I draw the large polyline without anti-aliasing, is ~50ms. Nice, eh!
问题是,如果我绘制位图,则 GDI仍比Direct2D快,完成后我将结果BitBlt回到表格,它以约35ms的速度绘制 ,并且具有相同的图形质量。而且,Direct2D似乎也已经在使用backbuffer(在调用 EndDraw()
时才绘制)。
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).
因此,可以通过某种方式加以改进以使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;
即使我使用建议事先创建几何图形的速度与GDI大致相同,但是
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.
我的计算机正常运行Direct3D和OpenGL应用,以下是dxDiag输出:
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.
推荐答案
问题是打开了抗锯齿功能。禁用抗锯齿功能,Direct2D的性能将与GDI相当或更快。为此,请在创建TDirect2DCanvas之后进行以下调用:
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在可能的情况下与TCanvas接口兼容,因此可以取代TCanvas,因此有些的绘制例程效率很低。例如,折线在每次调用时都会创建一个几何并将其丢弃。为了提高性能,保持几何形状不变。
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.
看看TDirect2DCanvas的实现。多行并将其提升到应用程序中,如下所示:
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;
然后像这样绘制它:
procedure TForm2.WMPaint(var Message: TWMPaint);
begin
FD2DCanvas.BeginDraw;
FD2DCanvas.Pen.Color := clRed;
FD2DCanvas.RenderTarget.DrawGeometry(FGeometry, FD2DCanvas.Pen.Brush.Handle);
FD2DCanvas.EndDraw;
end;
这篇关于是TDirect2DCanvas慢还是我做错了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!