Is TDirect2DCanvas slow or am I doing something wrong?
Asked Answered
F

4

24

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.

F answered 29/10, 2010 at 20:0 Comment(2)
Try using a performance profiler. Maybe there's an expensive operation going on behind the scenes that isn't readily apparent.Libido
I dont have any answers, but I know you're not the first person to mention that it doesnt seem any faster than GDI.Swanee
P
26

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;
Publia answered 5/11, 2010 at 16:44 Comment(5)
I'm going to try your suggestions, specially disabling antialiasing, even though I don't remember lines being antialiased at all, but I could be wrong. But notice that the multi-polyline for is actually an optimization I did as drawing a large polyline caused an unberable delay (once I waited more than 2 minutes for it to draw and then gave up). And yes, like Craig said, welcome to SO! =)F
So far the results are: 7000ms D2D with antialiasing; 3500ms D2D without antialiasing; +INFms D2D with antialiasing + creating path directly; 50ms D2D without antialiasing + creating path direclty; 240ms GDI with an straightforward Polyline(FData) drawing directly on form; and... finally 50ms D2D without antialiasing and using FD2DCanvas.Polyline(FData) directly. As you can see, the problem was doing antialiasing of long polylines, don't ask me why. With antialiasing short lines are much faster and without antialiasing it's just the opposite... Weird. Thanks!F
Oh but earlier I forgot to benchmark GDI while drawing to a bitmap then bliting the results to the form after drawing is complete. Surprisingly it draws the 50k lines at 34ms, GDI is still faster than the aliased D2D version...F
Thanks, good to be here. Hopefully I get some time to answer some questions.Publia
It makes a lot of sense actually. Try my example above where I create the geometry first, enable antialiasing, then create the data with a bit more space between the lines so you can see what's going on. Now change the size of the pen in the RenderTarget.DrawGeometry call. If you make it really small, like 0.2 rendering will take forever and you can see why, they are paper thin lines. This sort of drawing is very expensive because of the sub-pixel accuracy.Publia
T
3

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.

Tartaric answered 8/11, 2010 at 7:49 Comment(4)
The thing is I want better performance, not better line quality. By using Direct3D directly I could achieve a speed 10x faster than GDI, the same goes for using OpenGL, so I suppose the problem is not my driver or gfx card.F
By "I could", do you mean you do, or you think you should? GDI can be hardware accelerated too, so there may not be much to gain if you don't stray outside of GDI's sweet spot.Tartaric
I meant "I once achieved that speed in a benchmark", could as in past tense of can =) I did benchmark it. That's one of the reasons I don't get why Direct2D runs at the same speed GDI does.F
Both Direct2D and GDI are hardware accelerated, why would one run faster? The differences will be in the driver and API overhead only, and Direct2D isn't that "direct".Tartaric
S
3

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.

Seasonal answered 16/11, 2012 at 10:18 Comment(0)
R
1

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.

Reger answered 7/11, 2010 at 6:36 Comment(3)
I just read in windows7taskforce.com/view/3607 that GDI+ Acceleration in Windows 7 is broken. Another bad point from Micro$oft... GDI+ performs well in XP or Vista, but is slower with Seven. But I was not able to get some real benchmark on the web. Any ideas?Reger
That's nice, I like GDI+. But the thing here is why is there no benefit of using Direct2D: same speed as GDI, no antialiasing (with antialiasing it's much slower). At first I want more speed, not better quality, because I need to plot a huge set of data on screen which is untolerable for user interaction while using GDI's performance.F
BTW, while using the fastest CompositingQuality GDI+ is about 8x slower than GDI.F

© 2022 - 2024 — McMap. All rights reserved.