Jump to content
FPiette

Direct2D 1.1 canvas much slower that Direct2D 1.0

Recommended Posts

I had an application (A UI in front of an image SQLite database) using Direct2D 1.0 thru Delphi provided TDirect2DCanvas (VCL). This work well and it is fast.

 

To be able to use recent features of Direct2D, I wrote a modified version of TDirect2DCanvas to support Direct2D 1.1 and later. For the changes, I followed this guide.

It works nicely except it is ten times slower now!

 

Measuring performance was easy: Simple with Direct2D 1.0 it is almost instantly (less than one second for 49 images as shown in the screen dump below while Direct2D 1.1 take roughly ten seconds.

 

The code for rendering the bitmap is the same for both Direct2D 1.0 and 1.0 and is the following:

 

    BeginPaint(Handle, PaintStruct);
    RenderTarget.BeginDraw;
    RenderTarget.Clear(D2D1ColorF(FBackColor));
    if FBitmapToPaint <> nil then begin
        RenderTarget.SetTransform(FTransform);
        RenderTarget.DrawBitmap(FBitmapToPaint, nil, 1.0,
                                D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
                                nil);
    end;
    RenderTarget.EndDraw;
    // Next line only for Direct2D 1.1 version
    DXGISwapChain.Present1(SyncInterval, PresentFlags, PresentParams);
    EndPaint(Handle, PaintStruct);

 

RenderTarget is a ID2D1RenderTarget for Direct2D 1.0 and an ID2D1DeviceContext for Direct2D 1.1. FBitmapToPaint is a ID2D1Bitmap. PresentFlags and SyncInterval are always zero. PresentParams is a zeroed TDXGIPresentParameters structure. The code to initialize all the interfaces are inspired by the code shown on the guide I mentioned above.

In the application, there are as much such small windows as fit on the application window (49 in the screen dump).

 

The application currently does only image rendering: Loading JPG small files, scaling and rotating to fit the small window used for rendering. Those are thumbnails, so there are maybe 50 or 100 such small window on screen. No geometry, no text, no other drawing. Only a single bitmaps per window.

 

I have not yet used anything specific to Direct2D 1.1. If can switch from one version to the other just by using Delphi provide Vcl.Direct2D unit in the uses clause, or using my own modified version for Direct2D V1.1 and later.

 

My question are:

1) Is Direct2D 1.1 really slower than 1.0? (I suppose not)

2) Which tool (free if possible) can I use to measure everything involved in the creation of the window and the rendering so that I can find out what is slower exactly, hoping to find why it is slower.

 

BTW: I am using Windows 10 1909.

 

Capture.JPG

Share this post


Link to post
Guest
1 hour ago, FPiette said:

2) Which tool (free if possible) can I use to measure everything involved in the creation of the window and the rendering so that I can find out what is slower exactly, hoping to find why it is slower.

Nexus Quality Suite , it does have few tools, two of them will pinpoint your problem in minutes, Method Timer and Line Timer.

 

1 hour ago, FPiette said:

1) Is Direct2D 1.1 really slower than 1.0? (I suppose not)

No sure there, as from very known fact that GDI+ does have better variable performance than GDI, in some places it is 5 times faster, yet when it comes to text rendering it may be slower in 5 times depends on the parameters of rendering, though it provide way better text quality, so may be you have similar situation here, only the difference is the color depth or the alpha channel, are you rendering full images there ? or thumbnails ? have you kept/produced AlphaChannel for the thumbnails ? because this will play some important role in rendering.

Sorry for the guess work about this, and i am sure someone else might have better knowledge about this behaviour.

Share this post


Link to post

@Kas Ob.

I will have a look at Nexus Quality Suite. Thanks.

 

I am talking about Direct2D. The speed of Direct2D V1.0 implmented by Embarcadero in TDirect2DCanvas is excellent. The exact same program with a modified TDirect2DCanvas using Direct2D V1.1 or later is much slower. No GDI involved, not change if image format (The bitmap displayed are Direct2D bitmaps in format DXGI_FORMAT_B8G8R8A8_UNORM so yes there is an alpha channel).

 

 

Share this post


Link to post
Guest
1 minute ago, FPiette said:

I am talking about Direct2D.

I understand that, just pointing that some OS component evolve for better quality and slower speed.

 

 

I want to ask this, have you considered GDI and GDIP ?

There is an advantage for GDI's over Direct2D and OpenGL, in virtualized system OpenGL might not render to begin with, while Direct2D might be very slow if the hardware acceleration is not enabled.

Share this post


Link to post
4 minutes ago, FPiette said:

Nexus Quality Suite

Look a nice product. But it is expensive (€269,-), at least for my budget 😞

 

 

Share this post


Link to post
2 minutes ago, Kas Ob. said:

I want to ask this, have you considered GDI and GDIP ?

I used GDI+ before Direct2D. Direct2D (V1.0 at least) is faster than GDI+ for my purpose. That's why I switched.

 

Share this post


Link to post

I have used D2D to draw SVG images (see https://github.com/EtheaDev/SVGIconImageList/blob/master/Source/D2DSVGFactory.pas) and the performance is very good!

I found that Vcl.Direct2D was not worth the trouble and that it was easier to work directly with the D2D interfaces.   Also I preferred to use the DCRenderTarget as opposed to HWndRendertarget, since the former blends better with GDI and GDI+ applications.

  

One issue I had was with hardware acceleration.

 

    if not Succeeded(D2DFactory.CreateDCRenderTarget(
      D2D1RenderTargetProperties(
        {$IFDEF GPUSupprt}
        D2D1_RENDER_TARGET_TYPE_DEFAULT,
        {$ELSE}
        D2D1_RENDER_TARGET_TYPE_SOFTWARE, // much faster in my desktop with a slow GPU
        {$ENDIF}
        D2D1PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
        0, 0, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE),
        RenderTarget))

In the code above D2D1_RENDER_TARGET_TYPE_SOFTWARE avoids hardware acceleration and it was massively faster in a Desktop with a slow GPU.  You may want to try that.

Share this post


Link to post

I have a collection of these profilers. I've never worked with them!
For inspiration.
2030664225_2020-09-1118_32_30-pc-TotalCommander (x64) 9.51-Ing.StanislavHruska.png.670ab0e596c6606c7d1500265152397a.png.

Edited by Stano

Share this post


Link to post
17 hours ago, pyscripter said:

D2DFactory.CreateDCRenderTarget

@pyscripter I'm surprised, that you call CreateDCRenderTarget, you get a ID2D1DCRenderTarget while you need a ID2D1DeviceContext to call Direct2D 1.1 or later specific functions. Maybe I missed something?

 

Looking at https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/may/windows-with-c-introducing-direct2d-1-1 I saw this sentence:

Quote

To take advantage of the improvements in Direct2D 1.1 you need to eschew this render target, however, and instead opt for ID2D1DeviceContext, the new device context render target.

So I'm using CreateDeviceContext instead CreateHwndRenderTarget. And all other steps required as explained in the above mentioned article.

 

When using a single Direct2D window, the speed difference is not really visible. But as you saw in my application, I can have 50 or even 100 small Direct2D windows. And with as much windows, the speed difference is highly visible. The screen shot in the first message of this thread has 49 small windows and takes 10 seconds to build with Direct2D 1.1 and less than one second with Direct2D 1.0. Then if I open a single image to see it in big, there is no noticeable speed difference (Zoom, pan, flip and rotate) because the time scale is to short. Everything is done in a fraction of a second.

 

I could replace the small windows by a single one showing a single bitmap constructed from all thumbnails. But this would require a lot of works and is much much less convenient. Not to much visible on the screen dump, but the is a checkbox and also frames and various display effect which are really easy to do with the small windows and would be much more work with a single window.

Edited by FPiette
Added more details.

Share this post


Link to post
4 hours ago, FPiette said:

So I'm using CreateDeviceContext instead CreateHwndRenderTarget

You can get a Device context from the Render Target.  I am using ID2D1DeviceContext5 which was introduced with the Creators Update.

 

if Supports(RenderTarget, ID2D1DeviceContext5, DeviceContext5)

 

And as I said you do not need (multiple) D2D Windows.  Just use standard forms and  one DCRenderTarget to use in the OnPaint event of the form or by overriding the Paint method of Controls.

 

And in terms of speed D2DSVG renders 300 svgs to bitmaps in less than 15ms.

Edited by pyscripter

Share this post


Link to post
On ‎9‎/‎11‎/‎2020 at 4:37 PM, FPiette said:

2) Which tool (free if possible) can I use to measure everything involved in the creation of the window and the rendering so that I can find out what is slower exactly, hoping to find why it is slower.

You can use the System.Diagnostic unit, and the TStopWatch for high precision timing. With that you can "timing" every line of your code (of course if the code is blocking).
If you use not blocking code the free method is recording all the "Windows messages" in that time (10 seconds) and analyze the events timing.

 

For those who are old enough, they will surely remember WinSight.

 

I never used tools other the TStopWatch, with this i resolved all the issues.     

Share this post


Link to post

By the way, you can also use the hardware CPU timer (rdtsc instruction) to get the maximum accuracy in timing (with a resolution to the nanosecond or even lower, depending on the CPU), but obviously in an environment like Windows (also for Linux and other non-real time operating systems) the analysis of processing times can only be understood as an "average" value.

Share this post


Link to post
3 hours ago, pyscripter said:

And as I said you do not need (multiple) D2D Windows.  Just use standard forms and  one DCRenderTarget to use in the OnPaint event of the form or by overriding the Paint method of Controls.

But then I have to manage a huge number of bitmaps, frames, checkboxes in that alone window. Now i have a component, like a TPicture which does everything and I use NxM of such components, only changing the coordinates according to the size the use gives to the application window. This approach is very efficient with Direct2D 1.0 and 10 times slower with Direct2D 1.1.
 

Quote

 

You can get a Device context from the Render Target

 

How do you get the RenderTarget? Do you do the same as with Direct2D 1.0, that is just call CreateHwndRenderTarget or CreateDCRenderTarget? No Direct3D device, DXGIBackBuffer, SwapChain and other things mentioned in https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/may/windows-with-c-introducing-direct2d-1-1  and https://katyscode.wordpress.com/2013/01/23/migrating-existing-direct2d-applications-to-use-direct2d-1-1-functionality-in-windows-7/ ?

 

Thanks

Share this post


Link to post
46 minutes ago, DelphiUdIT said:

You can use the System.Diagnostic unit, and the TStopWatch for high precision timing.

My application has thousands lines of code. I need a tool which does this automatically.

  • Like 1

Share this post


Link to post

Regarding the RenderTarget I showed the code above.  For details have a look at https://github.com/EtheaDev/SVGIconImageList/blob/master/Source/D2DSVGFactory.pas) .

 

What I am suggesting is that you build your app as if it was a GDI app.  And just use D2D in your painting code (override Paint, OnPaint event etc) utilising a single DCRenderTarget.

Edited by pyscripter

Share this post


Link to post
15 minutes ago, pyscripter said:

Regarding the RenderTarget I showed the code above.  For details have a look at https://github.com/EtheaDev/SVGIconImageList/blob/master/Source/D2DSVGFactory.pas) .

Thanks. This is Direct2D 1.0 code that already use. I see you get a DeviceContext when you need it to render the SVG document.

So you are not following at all what all the articles about Direct2D 1.1 explain. I wonder where the drawback is... Future will tell...

 

Share this post


Link to post
On 9/12/2020 at 8:01 PM, FPiette said:

Thanks. This is Direct2D 1.0 code that already use. I see you get a DeviceContext when you need it to render the SVG document.

So you are not following at all what all the articles about Direct2D 1.1 explain. I wonder where the drawback is... Future will tell...

 

It has been a while, but did you get a better understanding of the differences in the two approaches and the reasons for the slowness?

Share this post


Link to post

By the way FMX graphics on Windows are based on DirectX and they use the IDXGIDevice stuff (units FMX.Context.DX11 and FMX.Canvas.D2D).

Share this post


Link to post
8 hours ago, FPiette said:

my application is there:

I see you are using ID2D1HwndRenderTarget.   However your project also refers to Vcl.Direct2D_1 which should be ..\..\Direct2D_1\Vcl.Direct2D_1.pas but that directory is not part of the project.   I wonder whether you can share that as well.

Share this post


Link to post
16 hours ago, pyscripter said:

I see you are using ID2D1HwndRenderTarget.   However your project also refers to Vcl.Direct2D_1 which should be ..\..\Direct2D_1\Vcl.Direct2D_1.pas but that directory is not part of the project.   I wonder whether you can share that as well. 

Added to the repository.

  • Thanks 1

Share this post


Link to post

I know, this thread is a little bit dated, but I take a look to your github repository.

https://github.com/fpiette/OvbImgOrganizer/blob/main/Source/Direct2D_1/Vcl.Direct2D_1.pas

 

Have you ever tried to change your SwapEffect in DXGI_SWAP_CHAIN_DESC1?

You use DXGI_SWAP_EFFECT_DISCARD in Line 1207 but the most recent examples recommend DXGI_SWAP_EFFECT_FLIP_DISCARD or  DXGI_SWAP_EFFECT_SEQUENTIAL_DISCARD.

https://docs.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect

 

Maybe you should use just the normal Present-Method of the Swap Chain.

So, just write DXGISwapChain.Present instead DXGISwapChain.Present1 and get rid of the PresentParams : PDXGIPresentParameters because you don't use it anyway.

 

https://social.msdn.microsoft.com/Forums/en-US/4737f4f6-68a4-45eb-8941-13f68802153a/why-direct2d-idxgiswapchain1present1-is-much-slower-than-directdraw?forum=windowssdk

 

https://walbourn.github.io/care-and-feeding-of-modern-swapchains/

 

 

Are you familiar with the DirectX debug layers?

So, if you do something fishy with your DirectX stuff, the Debug Layers will tell you in the (Debug) Event Log (you must read both links).

It's a great help during development:

 

https://docs.microsoft.com/en-us/windows/win32/direct3d11/using-the-debug-layer-to-test-apps

https://docs.microsoft.com/en-us/windows/win32/Direct2D/direct2ddebuglayer-overview

 

Please add D2D1_DEBUG_LEVEL_INFORMATION to FactoryOptions.DebugLevel before calling D2D1CreateFactory and D3D11_CREATE_DEVICE_DEBUG to the CreationFlags before calling D3D11CreateDevice.

If you wrap it between {$IFDEF DEBUG} and {$ENDIF}, you have a clean Release code.

 

image.thumb.png.74de50ad74d110490689ec09cfa87dc7.png

Edited by TiGü
  • Like 2
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×