【问题标题】:Why not use GDI to repeatedly fill a window with RGB data from an array?为什么不使用 GDI 用数组中的 RGB 数据重复填充窗口?
【发布时间】:2011-05-06 22:04:17
【问题描述】:

这是this question 的后续行动。我目前正在编写一个简单的游戏,并且正在寻找在 Win32 窗口中(重复)显示 RGB 数据数组的最快方法,而不会出现闪烁或其他伪影。

在上一个问题的答案中推荐了几种不同的方法,但对于哪种方法最快没有达成共识。所以,我拼凑了一个测试程序。该代码只是尽可能快地在屏幕上重复显示一个帧缓冲区。

这些是我获得的结果,对于在 32 位视频模式下运行的 32 位数据 - 它们可能会让一些人感到惊讶:

- Direct3D (1):             500 fps
- Direct3D (2):             650 fps
- DirectDraw (3):          1100 fps
- DirectDraw (4):           800 fps
- GDI (SetDIBitsToDevice): 2000 fps

鉴于这些数字:

  • 为什么很多人坚持认为 GDI 对这个操作来说太慢了?
  • 是否有任何理由更喜欢 DirectDraw 或 Direct3D 而不是 SetDIBitsToDevice?

以下是每个 Direct* 代码路径调用的简要摘要。如果有人知道使用 DirectDraw/Direct3D 的更有效方法,请发表评论。

1. CreateTexture(D3DUSAGE_DYNAMIC, D3DPOOL_DEFAULT);
       LockRect(); memcpy(); UnlockRect(); DrawPrimitive()

2. CreateTexture(0, D3DPOOL_SYSTEMMEM); CreateTexture(0, D3DPOOL_DEFAULT);
       LockRect(); memcpy(); UnlockRect(); UpdateTexture(); DrawPrimitive()

3. CreateSurface(); SetSurfaceDesc(lpSurface = &frameBuffer[0]);
       memcpy(); primarySurface->Blt();

4. CreateSurface();
       Lock(); memcpy(); Unlock(); primarySurface->Blt();

【问题讨论】:

  • 我不是这个领域的专家,但我肯定会在几种不同类型的图形硬件上运行这个实验,然后再得出任何结论。
  • 你是如何合成 RGB 数据的?除非您需要硬件加速来合成图像,否则我不希望 DirectX 更快。它需要以一种或另一种方式发送到视频硬件才能显示,即使是通过 GDI。使用 directx 方法,您将添加创建表面或纹理的额外工作。 GDI 一定会变慢是不合理的。
  • GDI 做得很好并且已经优化了 20 多年的一件事是移动位。现在似乎有某种传统的“智慧”,任何有 20 年历史的 API 都必须以某种方式过时。
  • 大家好!保罗,你最终选择了什么,为什么?我正在实时显示和更新表示来自进程的原始内存的大量 RGB 数据数组,而 Direct2D 似乎不适合这种操作,因为我的帧速率非常低。
  • FWIW,你的 DirectX 测试循环都有一个巨大的缺陷,这可能解释了性能问题。了解 DirectX 已针对 SETTING STUFF UP ONCE 进行了优化,然后在多个帧上重用,只更改需要更改的内容。正确的用法是 CreateSurface 和 CreateTexture ONCE,然后在框架上重新使用这些实体。也就是说,这些调用不应该在你的计时循环中。

标签: winapi directx gdi directdraw


【解决方案1】:

这里有几件事要记住。首先,很多“常识”都是基于一些不再适用的事实。

在 AGP 时代,当 CPU 直接与 GPU 通信时,它总是使用基本的 PCI 协议,这以“1x”速率发生(总是且不可避免地)。 AGX 2x/4x/8x 在 GPU 直接进入 内存控制器 时应用。换句话说,根据您查看的时间,GPU 从内存加载纹理的速度是 CPU 将相同数据直接发送到 GPU 的速度的 8 倍。当然,CPU 的内存带宽也比支持的 PCI 总线多得多。

然而,当事情切换到 PCI-E 时,情况就完全改变了。虽然带宽可能因路径而异,但没有一般规则表明内存->GPU 会比 CPU->GPU 更快。一个(大部分)安全的概括是如果你有一个专用的显卡,那么 GPU 几乎总是有更多的带宽到显卡上的内存比它在主板上的主内存上.

在您的情况下,这并不重要 - 您正在谈论将数据从 CPU 空间移动到 GPU 空间,无论如何。使用 DirectX(或 OpenGL)的主要速度差异发生在您将所有(或大部分)计算保留在 GPU 上,并完全避免使用 CPU(或主内存)时。它们没有(现在 AGP 已成为历史)在内存->显示带宽方面提供任何实质性改进。

【讨论】:

  • 我是否理解正确:在 AGP 机器上,“CPU => RAM => GPU”可能比“CPU => GPU”快 8 倍?所以有理由在这些机器上更喜欢 Direct* 而不是 GDI - 因为 DirectDraw 和 Direct3D 使用前一种方法,而 GDI 使用后者?
  • @Paul:仔细想想,乘数可能超过 8 倍——IIRC、1 倍 AGP 已经比 PCI 快了。无论如何,是的,这是一般的想法(尽管我认为至少对于某些驱动程序,GDI 可能也通过内存完成了这项工作)。
  • 顺便说一句,AGP 比 PCI 快得多(即使是 1x)的原因是因为它以 66Mhz 而不是 PCI 的 33Mhz 运行,从而提供了双倍的带宽)。因此,对于 1x,总共提供 266MB/秒(66Mhz * 32 位)。以 2x 532MB/秒(66Mhz * 2 * 32 位)等速度。存在一定程度的协议开销,因此无法完全实现这些数字。 PCI-Express 1x v1 以与 AGP 1x 相同的带宽运行。但可以组合高达 16 倍,从而获得比 AGP 更高的性能奖励。每个版本都有两倍的基本速率,这意味着 PCIe 1x v3 比 AGP 1x 快 4 倍...
【解决方案2】:

您会听到人们反对 GDI 的原因是它曾经只是旧的 Windows API 调用。它的较新版本(我上次查看 em 时称为 GDI+)实际上只是放置在 DirectX 调用之上的 API。因此,有时使用 GDI 可能看起来相当简单,但在事物之间添加一层总是会减慢速度。正如 Jerry Coffin 的回复中提到的,您的示例是关于移动数据的,这就是缓慢的时间。我有点惊讶 DirectX 的速度要慢得多,但我无法通过挖掘 DirectX 文档来获得更多帮助(这在相当长一段时间内真的非常棒......可能想看看 www.codesampler.com . 我总是从他那里找到很好的起点,实际上,虽然我可能会因为这样说而发疯,但我发誓在文档中对 DirectX SDK 的改进和示例都是基于这些人的工作完成的!)

关于 DirectDraw 与 Direct3D(而不是 GDI 调用)的讨论。我会说去 Direct3D。我相信 DirectDraw 自 8.0 左右就已被弃用,而 9.0 已经存在了很长时间。归根结底,所有 DirectX 都是 3D 的,它只是根据周围有用的 2D api 的级别而有所不同,但是当您实际使用 3D 空间时,您可能会发现您可以在 2D 环境中做一些非常有趣的事情。 (我曾经有一个非常整洁的随机生成的闪电武器,用于太空入侵者克隆:))

任何人,希望这会有所帮助!

PS:需要注意的是,DirectX 并不总是最快的。对于键盘输入(除非在 10 或 11 中发生了变化),几乎总是建议使用 windows 事件。因为 DirectInput 实际上只是该系统的包装器!.. XInput 然而 - 太棒了 - !!

【讨论】:

    【解决方案3】:

    Jerry Coffin 提出了一些很好的观点。要记住的是 DI 在 SetDIBitsToDevice 中代表什么。它代表设备独立。这意味着你总是受司机的摆布。一些驱动程序过去完全是垃圾,它极大地影响了性能。 DirectDraw 也遇到了类似的问题……但您也可以访问硬件 blitter,因此它通常更有用。由于 DirectDraw 的游戏关联性,IHV 也倾向于投入更多时间为 DirectDraw 编写适当的驱动程序。当硬件完全有能力做得更好时,谁想成为性能堆的底部?

    如今,许多显卡可以直接接受位数据,因此不会发生转换。如果确实需要搅拌,这在当今时代也非常快。

    相比之下,您的 Direct3D 性能如此糟糕的原因是 Direct3D 本质上是完全在 GPU 内部使用的,它使用奇怪和复杂的格式来提高缓存性能等等。

    再加上您不是通过创建纹理/表面、锁定、复制、解锁然后在后台缓冲区上绘制(通过各种方法)来测试类似(使用 DDraw 和 D3D)的事实。为了获得最佳性能,您最好使用 DISCARD 锁直接锁定后台缓冲区,然后在解锁之前直接将 memcpy'ing 到返回的缓冲区中。这将使您的性能更接近 SetDIBitsToDevice。但是,出于上述原因,我仍然希望 D3D 比 DDraw 慢。

    【讨论】:

    • 感谢您的回答。重新“锁定后台缓冲区” - 我知道这可以通过 D3D 完成,我将尝试在我的测试程序中添加对此的支持。你知道是否也可以将后台缓冲区锁定在(窗口)DDraw 中?
    • 另外,您说在 DirectDraw 中可以“访问硬件 blitters”。除了简单地调用“IDirectDrawSurface7::Blt”之外,这是否需要任何额外的工作?
    • 是的,这是可能的。如果您访问 www.trueharmoniccolours.co.uk(在 Coding->DirectX 下),您可以看到我 13 年前编写的一些 DirectDraw 代码。在那我直接锁定后台缓冲区,然后我直接写入后台缓冲区(注意:这些天我很清楚我在该示例中实际上正在写入未缓存的内存,因此它比它需要的要慢得多)。是的,这正是您访问硬件 blitter 的方式......如果它们不可用,那么它是软件模拟的。现在的 CPU 速度如此之快,但您可能会发现软件和硬件 blitting 之间几乎没有区别。
    • 我无法从 www.trueharmoniccolours.co.uk 获取样本以在我的机器上运行,但是查看源代码,它们不是仅全屏显示吗?
    • @Paul:嗯,它是用 DX6 编写的 .. 所以它可能需要进行一些修改。您可能只在全屏上正确.. 自从我写它以来已经很长时间了;)我不明白为什么这很重要,因为您总是锁定屏幕外(即后退)缓冲区不是吗?
    猜你喜欢
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多