GDI与Direct2D的比较

问题描述 投票:15回答:3

我现在正在编写一个仿真程序,我想把我的应用程序从使用GDI移植到使用Direct2D。但是我的Direct2D代码比我的GDI代码慢很多。

我在屏幕上渲染了很多椭圆。在我的GDI应用程序中,我绘制到一个内存设备上下文,然后使用BitBlt绘制到windows设备上下文。使用Direct2D,我画到ID2D1HwndRenderTarget上。

我的问题是,当使用GDI时,我可以轻松地绘制400多个椭圆,并且仍然有400 FPS。当我用Direct2D画同样数量的椭圆时,我的FPS降到了30FPS。

我已经关闭了抗锯齿,但这并没有什么帮助。有趣的是,在Direct2D中画几个椭圆比GDI快。我有什么办法可以提高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);
}

在Begin和End函数之间,我用标准的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);

先谢谢你。

c++ winapi direct2d
3个回答
4
投票

前段时间由于性能低下,我拒绝将渲染代码从GDI迁移到Direct2D。据我从google上了解,Direct2D的性能取决于驱动和硬件优化,你不应该期望在不同的硬件上有同样的速度。GDI已经很老了,几乎在任何地方都能同样工作。

不得不说,我曾尝试用它来绘制简单的几何基元,而Direct2D似乎是更强大的库,也许在复杂的情况下会有性能提升,但这不是我的情况。

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

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


34
投票

初次尝试Direct2D的一个常见错误是开发者没有正确地缓存D2D资源,而是过于频繁地创建和销毁资源。 如果你所有的椭圆大小相似,你应该创建并缓存一次这个椭圆对象。 如果你有30个不同大小的形状,那么只需要为所有30个大小的形状创建一次椭圆版本。 这样可以显著提高Direct2D的速度。 矩形和所有其他基元也是如此。如果一个基元存在太多的变化,那么缩放缓存对象与重复创建销毁也是某些情况下的解决方案,不过使用资源的原生尺寸是最理想的,内存卡有相当多的内存来存储你的资源。

Gdi椭圆看起来绝对糟糕,直接使用Direct3D相当复杂,特别是对于椭圆、大型多边形和更高级别的基元。如果适当使用Direct2D,你应该可以获得良好的速度和高质量的渲染。


0
投票

我正在研究d2d。基本上它的速度是gdi的2倍或3倍左右,如果你停留在基本的绘图上。绘制线条,矩形,椭圆,......比如RenderTarget->DrawLine。在全球范围内,直接在渲染目标上绘制比gdi快2倍3倍。另外,别忘了在d2d后置缓冲区上绘制,而不是在hwnd上绘制。这和使用gdi位图回缓冲区的过程完全一样,但使用的是direct2d资源。

如果你想使用高级的d2d对象,比如几何体,这是另外一回事。

举个例子,如果不创建一个这个矩形的变换几何体的实例,你就不能移动一个矩形。

D2D资源是不可变的,尽管它们是在cpu级别上管理的,你不能修改源形状并直接绘制它。你必须为每一次平移、旋转创建一个这个形状的副本。

如果你使用像paint这样的应用程序绘图,这不是一个大问题......。但如果你想使用一个实时系统,有大量的形状,鼠标点击测试,缩放,滚动等......。那就会在性能上出现漏洞。

在我的测试中,它需要我(在调试模式下)约0.5s的转化源几何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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.