GDI与Direct2D

时间:2012-11-08 03:28:53

标签: c++ winapi direct2d

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

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

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

我已经关闭了antialiasing,但它并没有真正帮助。有趣的是,与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);
}

在我的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);

提前谢谢。

3 个答案:

答案 0 :(得分:31)

首次尝试Direct2D时常见的错误是开发人员没有正确缓存D2D资源,而是经常创建和销毁资源。如果所有省略号的大小相似,则应创建并缓存此椭圆对象一次。如果您有30种不同的尺寸/形状,则只为所有30种尺寸/形状创建椭圆版本一次。这显着加速了Direct2D。矩形和所有其他基元也是如此。如果基元存在太多变体,则缩放缓存对象与重复创建/销毁也是一种解决方案,尽管使用其原始大小的资源是理想的,并且存储卡具有相当多的内存来存储资源。

Gdi椭圆看起来非常糟糕,直接使用Direct3D相当复杂,特别是对于椭圆,大多边形和更高级别的基元。正确使用Direct2D后,您应该能够获得良好的速度和高质量的渲染。

答案 1 :(得分:1)

前段时间,由于性能低下,我拒绝将渲染代码从GDI迁移到Direct2D。据我所知,谷歌的Direct2D性能取决于驱动程序和硬件优化,你不应期望在不同的硬件上具有相同的速度。 GDI非常古老,几乎无处不在。

必须说我已经尝试用它来绘制简单的几何图元,而Direct2D似乎是更强大的库,也许在复杂场景中可以提升性能,但这不是我的情况。

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

这是一个相关的问题: Is TDirect2DCanvas slow or am I doing something wrong?

答案 2 :(得分:0)

我正在研究d2d。如果您继续使用基本绘图,基本上它的速度比gdi快2倍或3倍。画线,矩形,椭圆形..类似于RenderTarget-> DrawLine。在全球范围内,直接在渲染目标上绘制的速度比gdi快2到3倍。同样不要忘记使用d2d后备缓冲区而不是hwnd。与使用gdi位图后备缓冲区完全相同,但使用的是Direct2d资源。

如果要使用高级d2d对象(例如几何图形),这是另一回事。

通过示例,您不能移动矩形而不创建该矩形的变换几何的实例。

D2D资源是不可变的,尽管它们是在cpu级别进行管理的,但是您不能修改源形状并只能绘制它。您必须为每个平移,旋转创建此形状的副本...

如果您使用诸如绘画之类的应用程序绘图,这不是什么大问题。但是,如果您想使用实时系统,需要进行大量的形状,鼠标点击测试,缩放,滚动等操作。那么会给表演带来漏洞。

在我的测试中,我花了大约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;
    }
}