GDI versus Direct2D

GDI versus Direct2D

本文关键字:Direct2D versus GDI      更新时间:2023-10-16

我目前正在编程模拟,我想将应用程序从使用GDI移植到使用Direct2D。但是我的Direct2D代码比我的GDI代码慢得多。

我在屏幕上渲染了很多椭圆。在我的GDI应用程序中,我绘制到存储设备上下文中,然后使用BITBLT在Windows设备上下文上绘制。使用Direct2D,我将其绘制到ID2D1HWNDRENDERTARGET上。

我的问题是,当使用GDI时,我可以轻松绘制400多个椭圆形,并且仍然有400 fps。当我使用Direct2D进行相同数量的椭圆时,我的FPS下降到30fps。

我已经关闭了抗血液,但这并没有真正的帮助。有趣的是,与GDI相比,Direct2D仅绘制几个椭圆的速度更快。我可以做些什么来提高Direct2D的性能,还是应该使用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);
}

在我的开始功能和结束功能之间,我绘制椭圆形的标准GDI方式。

这是我使用Direct2D的开始和结束功能:

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

这是我设置Direct2D接口的方式。这一切都包裹在课堂上;这就是为什么我不能发布完整代码:

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

预先感谢您。

direct2D首次尝试的常见错误是开发人员没有正确地缓存D2D资源,而是创建和破坏资源太频繁。如果您的所有椭圆都相似,则应创建和缓存此椭圆对象一次。如果您有30种不同的尺寸/形状,请仅一次为所有30个尺寸/形状创建椭圆版本。这大大加速了Direct2D。矩形和所有其他原语的也是如此。如果存在太多的变化,则缩放缓存的对象与重复创建/破坏也是某些情况的解决方案,尽管对于原始构图,但使用本机大小的资源是理想的,并且存储卡具有很多存储器来存储您的资源。

gdi椭圆形看起来绝对可怕,直接使用Direct3D非常复杂,尤其是对于椭圆,大多边形和更高级别的原语。通过适当使用Direct2D,您应该能够获得良好的速度和高质量的渲染。

我拒绝了从GDI迁移渲染代码Direct2D由于性能低。我从Google了解到,Direct2D性能取决于驱动程序和硬件优化,您不应该期望在不同的硬件上具有相同的速度。GDI很老,几乎到处都可以工作。

必须说,我已经尝试使用它来绘制简单的几何原始图,而direct2d似乎更强大,并且也许在复杂方案上可能会提高性能,但这不是我的情况。

如果您需要质量更高的GDI性能 - 尝试直接使用OpenGL或Direct3D。

这是一个相关的问题:Tdirect2dcanvas是慢还是我做错了什么?

我正在从事D2D。基本上,如果您留在基本图纸上,它比GDI快2倍或3倍。图线,矩形,椭圆形,例如rendertarget-> drawline。在全球范围内,直接在渲染目标上绘制比GDI快2x 3倍。另外,不要忘记绘制D2D backuffer而不是HWND。这与使用GDI位图背板完全相同,但使用Direct2D资源。

如果要使用高级D2D对象,例如几何,这是另一回事。

示例,如果不创建该矩形的转换几何形状的实例,就无法移动矩形。

d2d资源是不可药品的,尽管它们是在CPU级别进行管理的,但您无法修改源形状并绘制它。您必须为每次翻译创建此形状的副本,旋转...

如果您使用诸如PAINT之类的应用图绘图,这不是一个大问题...但是,如果您想使用一些形状,鼠标命中测试,缩放,滚动等...然后...然后...然后

在我的测试中,我(在调试模式下)需要0.5秒(在调试模式下)进行1000000次转换/转换源几何形状。

代码测试示例:

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

当然,正如其他海报所述,在硬件加速图形中,重要的是缓存资源,尽管有复杂的情况。

我编写了一些解决方案,这些解决方案用一些填充物绘制矩形,并填充屏幕。在1980x1080上,大约有300个矩形,大约有40 fps。

最重要的是, direct2d似乎不是最快的解决方案,至少是因为Direct2D只是D3D11上的包装器(或12)。如果您通过间谍 检查Chromium(至少边缘)渲染窗口,您会注意到,它的类称为"中间D3D窗口"。

我确定(虽然是开源的),即使诸如绘制图形之类的东西闪烁的图形(Skia似乎是),也可以使用恰好使用D3D而不是Direct2D编写。以及UWP应用程序。