GDI versus Direct2D
Asked Answered
M

4

19

I'm programming a simulation at the moment, and I want to port my application from using GDI to using Direct2D. But my Direct2D code is much slower than my GDI code.

I render a lot of ellipses on the screen. In my GDI application I draw to a memory device context and then use BitBlt to draw on the windows device context. With Direct2D, I draw onto a ID2D1HwndRenderTarget.

My Problem is, when using GDI, I can draw easily 400+ ellipses and still have 400 FPS. When I do the same number of ellipses with Direct2D, my FPS drops down to 30FPS.

I already switched antialiasing off but it doesn't really help. The interesting thing is that drawing just a few ellipses is faster in Direct2D compared to GDI. Is there anything I can do to improve the performance in Direct2D, or should I keep my application using GDI?

Here is my drawing code using GDI:

VOID Begin() {
    SelectObject(this->MemDeviceContext, this->MemoryBitmap);
    this->BackgroundBrush = CreateSolidBrush(this->BackgroundColor);
    HBRUSH OldBrush = (HBRUSH)SelectObject(this->MemDeviceContext, this->BackgroundBrush);
    Rectangle(this->MemDeviceContext, -1, -1, 801, 601);
    SelectObject(this->MemDeviceContext, OldBrush);
    DeleteObject(this->BackgroundBrush);
    SetViewportOrgEx(this->MemDeviceContext, 400, 300, &this->OldOrigin);
}
VOID End() {
    SetViewportOrgEx(this->MemDeviceContext, this->OldOrigin.x, this->OldOrigin.y, 0);
    BitBlt(this->DeviceContext, 0, 0, 800, 600, this->MemDeviceContext, 0, 0, SRCCOPY);
}

Between my Begin and End function, I draw my ellipses the standard GDI way.

Here are my begin and end functions using Direct2D:

VOID BeginDrawing() {
    this->RenderTarget->BeginDraw();
    RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::CornflowerBlue));
    RenderTarget->SetTransform(this->ScalingMatrix * this->TranslateMatrix);
}
VOID EndDrawing() {
    this->RenderTarget->EndDraw();
}

And here is how I set up my Direct2D interfaces. It's all wrapped in class; that's why I cant post the full code:

    if(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &Direct2DFactory) != S_OK)
        throw std::runtime_error("RENDERWINDOW::InitializeDirect2D: Failed to create a factory interface.");
    RECT WindowRect;
    memset(&WindowRect, 0, sizeof(RECT));
    GetClientRect(this->WndHandle, &WindowRect);
    D2D1_SIZE_U WindowSize = D2D1::SizeU(WindowRect.right, WindowRect.bottom);
    Direct2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE), 
    D2D1::HwndRenderTargetProperties(this->WndHandle, WindowSize, D2D1_PRESENT_OPTIONS_IMMEDIATELY), &RenderTarget);

Thank you in advance.

Mosesmosey answered 8/11, 2012 at 3:28 Comment(3)
You didn't show the drawing code of D2DLogrolling
The drawing code is standerd Direct2D code to draw an ellipse. I use the FillEllipse and DrawEllipse function and nothing else thats why i didnt post it.Mosesmosey
The main purpose for introducing Direct2D was a new clean API and the base framework for DirectWrite. Drawing Text is todays most expensive operation and DirectWrite has a huge speed improvement for high quality text. Maybe you should consider testing with other basic drawing primitives. I doubt your app is only drawing elipses.Steno
C
6

Some time ago I’ve refused migrating rendering code from GDI to Direct2D due to low performance. As I understand from google, Direct2D performance depends on driver and hardware optimizations and you shouldn’t expect the same speed on different hardware. GDI is pretty old and works equally almost everywhere.

Must say I’ve tried to use it for drawing simple geometry primitives whereas Direct2D seems to be much more robust library and maybe there could be performance boost on complex scenarios, but this is not my case.

If you need GDI performance with better quality – try to use OpenGL or Direct3D directly.

This is a related question: Is TDirect2DCanvas slow or am I doing something wrong?

Circus answered 8/11, 2012 at 5:4 Comment(3)
Hi, and thanks for your answer. I considered using Direct3D but I only have very limited time for my project. Thats way i took the easiest way to draw ellipses. Achieving the same result in Direct3D is a way more complex. I guess I will just use GDI than and accept the lower quality of the ellipses. It is still strange though that something that should take advantage of your graphics card is alot slower compared to GDI.Mosesmosey
Porting from GDI to GDI+ should be straightforward. GDI+ supports anti-aliased output so the quality is as good as Direct2D and the performance of GDI+ is still way better than Direct2D.Citify
Direct2D depends on the presence a DirectX 10/11 driver. In the case that it's not present Direct2D will fall back to software rendering, in which case it's performance is at worst on par with gdi. When the hardware is DirectX10/11 capable Direct2D outperforms gdi by a LONG shot. GDI+ was a great attempt, however GDI+ suffers from worse performance than gdi. Truthfully, the best answer would be for the OP to use what he's comfortable with, then after he's got a working application, if it's not as per formant as he wants, Optimize!Curt
S
45

A common mistake with first attempts at Direct2D is developers do not properly cache the D2D resources and instead create and destroy resources too often. If all your ellipses are similar sized, you should create and cache this ellipse object once. If you have 30 different sizes/shapes, create ellipse versions for all 30 sizes/shapes only once. This significantly speeds up Direct2D. Same goes for Rectangles and all other primitives. Scaling a cached object versus repeated creation/destruction is also a solution for some scenarios if too many variations exist for a primitive, though using a resource at its native size is ideal and memory cards have quite a bit of memory to store your resources.

Gdi ellipses look absolutely terrible and using Direct3D directly is fairly complex, especially for ellipses, large polygons, and higher level primitives. With proper use of Direct2D you should be able to get good speed and high quality rendering.

Schuler answered 22/9, 2013 at 22:1 Comment(2)
Can I just add to this that Direct3d isn't the answer because you'd have to do precisely the same things as you have to do in Direct2d with respect to caching/pre-creating geometries, to get best performance out of it. Direct2d is built on Direct3d and to a great extent takes away the pain of doing 2d graphics with it, especially things like brushes, AA, lines and so on.Osteoid
D2D and D3D are quite different. They can share memory, create composite renderings, but the preparation and rendering of a D2D scene versus a D3D scene is different. Though it isn't too hard to composite. Use D2D for the border, text, various sparse portions of the scene, and then add D3D for large data portions of the scene. D3D used for 2D is a simpler form of rendering than full 3D but everything is still a line or triangle topology so plan on using trigonometry to create everything with vertices in the D3D world. D2D shields you from having to use only vertices.Schuler
C
6

Some time ago I’ve refused migrating rendering code from GDI to Direct2D due to low performance. As I understand from google, Direct2D performance depends on driver and hardware optimizations and you shouldn’t expect the same speed on different hardware. GDI is pretty old and works equally almost everywhere.

Must say I’ve tried to use it for drawing simple geometry primitives whereas Direct2D seems to be much more robust library and maybe there could be performance boost on complex scenarios, but this is not my case.

If you need GDI performance with better quality – try to use OpenGL or Direct3D directly.

This is a related question: Is TDirect2DCanvas slow or am I doing something wrong?

Circus answered 8/11, 2012 at 5:4 Comment(3)
Hi, and thanks for your answer. I considered using Direct3D but I only have very limited time for my project. Thats way i took the easiest way to draw ellipses. Achieving the same result in Direct3D is a way more complex. I guess I will just use GDI than and accept the lower quality of the ellipses. It is still strange though that something that should take advantage of your graphics card is alot slower compared to GDI.Mosesmosey
Porting from GDI to GDI+ should be straightforward. GDI+ supports anti-aliased output so the quality is as good as Direct2D and the performance of GDI+ is still way better than Direct2D.Citify
Direct2D depends on the presence a DirectX 10/11 driver. In the case that it's not present Direct2D will fall back to software rendering, in which case it's performance is at worst on par with gdi. When the hardware is DirectX10/11 capable Direct2D outperforms gdi by a LONG shot. GDI+ was a great attempt, however GDI+ suffers from worse performance than gdi. Truthfully, the best answer would be for the OP to use what he's comfortable with, then after he's got a working application, if it's not as per formant as he wants, Optimize!Curt
P
1

I'm working on d2d. Basically it is around 2x or 3x more fast than gdi, if you stay on basic drawing. Drawing line, rectangles, ellipses, .. Like RenderTarget->DrawLine. Globally, drawing directly on the render target is 2x 3x more fast than gdi. Also don't forget to draw on a d2d backbuffer instead of the hwnd. It's exactly the same process as using a gdi bitmap backbuffer but using direct2d resources.

If you want to use the advanced d2d objects, like geometry, this is another thing.

By example, you cannot move a rectangle without creating an instance of a transformed geometry of this rectangle.

D2D resources are immutables, despite they are managed at the cpu level, you cannot modify the source shape and just draw it. You have to create a copy of this shape for each translations, rotations...

This is not a big problem if you use an app drawing like paint... But if you want to use a real time system, with a quantity of shapes, mouse hit testing, scaling, scrolling, etc... Then that can give a hole in performances.

On my tests, it take me (in debug mode) around 0.5s to transform/translate a source geometry 1000000 times.

Code test example:

void TestGeometryPerf() { //1000000 = 0.35s/0.45s (in msvc 2019 debug mode)
    ID2D1RectangleGeometry *r;
    ID2D1TransformedGeometry *t;
    D2D1_RECT_F rect = D2D1::RectF(0, 0, 1, 1);
    D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F::Translation(10,10);

    //create geometry source
    m_factory->CreateRectangleGeometry(rect, &r);

    for(int x = 0; x < 1000000; x++) {

        //create a transformed geometry from geometry source
        m_factory->CreateTransformedGeometry(r, matrix, &t);
        if( t->Release() != 0) {
            throw;
        }

    }
    if( r->Release() != 0) {
        throw;
    }
}
Parasitize answered 11/5, 2020 at 10:4 Comment(0)
J
0

Of course, as other posters mentioned, in hardware-accelerated graphics important thing is caching resources, although there are complicated cases.

I wrote some solution that draws rectangles with some padding, filling the screen. On 1980x1080 there are about 300 rectangles and there are about 40 fps.

The most important thing is that Direct2D seems to be not the fastest solution, at least because Direct2D is just a wrapper on d3d11 (or maybe 12). If You inspect Chromium (at least Edge) render window via Spy++, You will notice, that it’s class is called “Intermediate D3D Window”.

I am sure (it’s open source though) that Blink’s graphics (Skia seems to be) even for stuff like just drawing primitives is written using exactly D3D and not Direct2D. As well as, uwp apps, for example.

Jonme answered 17/9, 2022 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.