简体   繁体   English

GDI 与 Direct2D

[英]GDI versus Direct2D

I'm programming a simulation at the moment, and I want to port my application from using GDI to using Direct2D.我现在正在编写一个模拟程序,我想将我的应用程序从使用 GDI 移植到使用 Direct2D。 But my Direct2D code is much slower than my GDI code.但是我的 Direct2D 代码比我的 GDI 代码慢得多。

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.在我的 GDI 应用程序中,我绘制到 memory 设备上下文,然后使用 BitBlt 绘制 windows 设备上下文。 With Direct2D, I draw onto a ID2D1HwndRenderTarget.使用 Direct2D,我在 ID2D1HwndRenderTarget 上绘图。

My Problem is, when using GDI, I can draw easily 400+ ellipses and still have 400 FPS.我的问题是,当使用 GDI 时,我可以轻松绘制 400 多个椭圆并且仍然有 400 FPS。 When I do the same number of ellipses with Direct2D, my FPS drops down to 30FPS.当我用 Direct2D 做相同数量的椭圆时,我的 FPS 下降到 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.有趣的是,与 GDI 相比,在 Direct2D 中仅绘制几个椭圆会更快。 Is there anything I can do to improve the performance in Direct2D, or should I keep my application using GDI?我可以做些什么来提高 Direct2D 的性能,还是应该让我的应用程序继续使用 GDI?

Here is my drawing code using GDI:这是我使用 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.在我的开始和结束 function 之间,我以标准 GDI 方式绘制椭圆。

Here are my begin and end functions using Direct2D:这是我使用 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.下面是我设置 Direct2D 接口的方法。 It's all wrapped in class;全部用 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.先感谢您。

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.首次尝试 Direct2D 的一个常见错误是开发人员没有正确缓存 D2D 资源,而是过于频繁地创建和销毁资源。 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.如果您有 30 种不同的尺寸/形状,只需为所有 30 种尺寸/形状创建椭圆版本一次。 This significantly speeds up Direct2D.这显着加快了 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. Gdi 椭圆看起来非常糟糕,直接使用 Direct3D 相当复杂,尤其是对于椭圆、大多边形和更高级别的基元。 With proper use of Direct2D you should be able to get good speed and high quality rendering.通过正确使用 Direct2D,您应该能够获得良好的速度和高质量的渲染。

Some time ago I've refused migrating rendering code from GDI to Direct2D due to low performance.前段时间,由于性能低下,我拒绝将渲染代码从 GDI 迁移到 Direct2D。 As I understand from google, Direct2D performance depends on driver and hardware optimizations and you shouldn't expect the same speed on different hardware.正如我从谷歌了解到的,Direct2D 性能取决于驱动程序和硬件优化,您不应该期望不同硬件上的速度相同。 GDI is pretty old and works equally almost everywhere. GDI 已经很老了,几乎在任何地方都能正常工作。

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.必须说我已经尝试使用它来绘制简单的几何图元,而 Direct2D 似乎是更健壮的库,并且可能在复杂场景中可以提高性能,但这不是我的情况。

If you need GDI performance with better quality – try to use OpenGL or Direct3D directly.如果您需要更高质量的 GDI 性能 - 尝试直接使用 OpenGL 或 Direct3D。

This is a related question: Is TDirect2DCanvas slow or am I doing something wrong?这是一个相关的问题: TDirect2DCanvas 慢还是我做错了什么?

I'm working on d2d.我正在研究 d2d。 Basically it is around 2x or 3x more fast than gdi, if you stay on basic drawing.基本上它比 gdi 快 2 倍或 3 倍,如果你继续使用基本绘图。 Drawing line, rectangles, ellipses, .. Like RenderTarget->DrawLine.画线,矩形,椭圆,..像RenderTarget->DrawLine。 Globally, drawing directly on the render target is 2x 3x more fast than gdi.在全局范围内,直接在渲染目标上绘制比 gdi 快 2 倍 3 倍。 Also don't forget to draw on a d2d backbuffer instead of the hwnd.也不要忘记绘制 d2d 后台缓冲区而不是 hwnd。 It's exactly the same process as using a gdi bitmap backbuffer but using direct2d resources.这与使用 gdi 位图后台缓冲区完全相同,但使用 direct2d 资源。

If you want to use the advanced d2d objects, like geometry, this is another thing.如果你想使用高级 d2d 对象,比如几何,这是另一回事。

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. D2D 资源是不可变的,尽管它们在 cpu 级别进行管理,但您无法修改源形状并绘制它。 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.如果你使用像paint这样的应用程序绘图,这不是一个大问题......但是如果你想使用一个实时系统,具有大量的形状,鼠标点击测试,缩放,滚动等......那么可以给表演中的一个漏洞。

On my tests, it take me (in debug mode) around 0.5s to transform/translate a source geometry 1000000 times.在我的测试中,我(在调试模式下)大约需要 0.5 秒才能转换/平移源几何体 1000000 次。

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;
    }
}

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.在 1980x1080 上,大约有 300 个矩形,大约 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).最重要的是Direct2D 似乎不是最快的解决方案,至少因为 Direct2D 只是 d3d11(或者可能是 12)的包装器。 If You inspect Chromium (at least Edge) render window via Spy++, You will notice, that it's class is called “Intermediate D3D Window”.如果您通过 Spy++ 检查 Chromium(至少是 Edge)渲染 window,您会注意到,它的 class 被称为“中间 D3D 窗口”。

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.我确信(虽然它是开源的)Blink 的图形(Skia 似乎是)即使是像绘制图元这样的东西也是使用 D3D 而不是 Direct2D 编写的。 As well as, uwp apps, for example.以及 uwp 应用程序,例如。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM