【问题标题】:GDI versus Direct2DGDI 与 Direct2D
【发布时间】:2012-10-28 05:33:44
【问题描述】:

我现在正在编写一个模拟程序,我想将我的应用程序从使用 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);
}

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

提前谢谢你。

【问题讨论】:

  • 你没有显示D2D的绘图代码
  • 绘制代码是标准的Direct2D代码来绘制一个椭圆。我使用 FillEllipse 和 DrawEllipse 函数,没有别的,这就是我没有发布它的原因。
  • 引入 Direct2D 的主要目的是新的干净 API 和 DirectWrite 的基础框架。绘制文本是当今最昂贵的操作,DirectWrite 对高质量文本有巨大的速度提升。也许您应该考虑使用其他基本绘图原语进行测试。我怀疑您的应用只是在绘制省略号。

标签: c++ winapi direct2d


【解决方案1】:

第一次尝试 Direct2D 的一个常见错误是开发人员没有正确缓存 D2D 资源,而是过于频繁地创建和销毁资源。如果所有椭圆的大小都相似,则应创建并缓存此椭圆对象一次。如果您有 30 种不同的尺寸/形状,只需为所有 30 种尺寸/形状创建椭圆版本一次。这显着加快了 Direct2D。矩形和所有其他基元也是如此。如果原语存在太多变化,则缩放缓存对象与重复创建/销毁也是某些场景的解决方案,尽管使用其本机大小的资源是理想的,并且存储卡有相当多的内存来存储您的资源。

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

【讨论】:

  • ^ 这是真正的答案。
  • 我能否补充一点,Direct3d 不是答案,因为您必须在 Direct2d 中执行与缓存/预创建几何图形完全相同的操作,以从中获得最佳性能。 Direct2d 建立在 Direct3d 之上,在很大程度上消除了使用它制作 2d 图形的痛苦,尤其是画笔、AA、线条等。
  • D2D 和 D3D 完全不同。它们可以共享内存、创建复合渲染,但 D2D 场景与 D3D 场景的准备和渲染是不同的。虽然合成起来并不难。对场景的边界、文本、各种稀疏部分使用 D2D,然后为场景的大数据部分添加 D3D。用于 2D 的 D3D 是一种比完整 3D 更简单的渲染形式,但一切仍然是线或三角形拓扑,因此计划使用三角法在 D3D 世界中创建具有顶点的一切。 D2D 使您不必只使用顶点。
【解决方案2】:

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

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

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

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

【讨论】:

  • 您好,感谢您的回答。我考虑过使用 Direct3D,但我的项目时间非常有限。那就是我用最简单的方法画椭圆。在 Direct3D 中实现相同的结果要复杂得多。我想我只会使用 GDI 而不是接受椭圆的较低质量。尽管与 GDI 相比,应该利用您的显卡的东西要慢得多,但这仍然很奇怪。
  • 从 GDI 移植到 GDI+ 应该很简单。 GDI+ 支持抗锯齿输出,因此质量与 Direct2D 一样好,而 GDI+ 的性能仍远优于 Direct2D。
  • Direct2D 取决于 DirectX 10/11 驱动程序的存在。在它不存在的情况下,Direct2D 将回退到软件渲染,在这种情况下,它的性能最差与 gdi 相当。当硬件支持 DirectX10/11 时,Direct2D 远胜 gdi。 GDI+ 是一个很好的尝试,但是 GDI+ 的性能比 gdi 差。老实说,最好的答案是让 OP 使用他喜欢的东西,然后在他得到一个工作应用程序之后,如果它的性能不如他想要的,优化!
【解决方案3】:

我正在研究 d2d。基本上它比 gdi 快 2 倍或 3 倍,如果你坚持基本绘图的话。画线,矩形,椭圆,.. 像 RenderTarget->DrawLine。在全球范围内,直接在渲染目标上绘图比 gdi 快 2 倍 3 倍。也不要忘记使用 d2d 后备缓冲区而不是 hwnd。这与使用 gdi 位图 backbuffer 的过程完全相同,但使用的是 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;
    }
}

【讨论】:

    猜你喜欢
    • 2019-05-13
    • 2016-05-08
    • 1970-01-01
    • 2014-11-28
    • 1970-01-01
    • 2017-03-21
    • 2012-10-15
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多