【问题标题】:Why are functions duplicated between opengl32.dll and gdi32.dll?为什么 opengl32.dll 和 gdi32.dll 之间函数重复?
【发布时间】:2014-01-05 21:31:48
【问题描述】:

以下函数在 opengl32.dll 和 gdi32.dll 之间重复:

[opengl32.dll]         / [gdi32.dll]
wglChoosePixelFormat   / ChoosePixelFormat
wglDescribePixelFormat / DescribePixelFormat
wglGetPixelFormat      / GetPixelFormat
wglSetPixelFormat      / SetPixelFormat
wglSwapBuffers         / SwapBuffers

我一直在寻找答案很长时间,但似乎没有人知道为什么会这样以及它们的确切区别是什么。

OpenGL FAQ 第 5.190 节表明这些函数在功能上相同:

为确保 OpenGL 正确运行,请使用 ChoosePixelformat, DescribePixelformat、GetPixelformat、SetPixelformat 和 SwapBuffers, 而不是 wgl 等效项 wglChoosePixelformat, wglDescribePixelformat、wglGetPixelformat、wglSetPixelformat 和 wglSwapBuffers。在所有其他情况下,使用 wgl 函数,其中 可用的。使用五个 wgl 函数只对 开发人员运行时链接到 OpenGL 驱动程序。

“运行时链接到 OpenGL 驱动程序”是否意味着绕过 opengl32.dll 并直接加载 ICD?

一个名为 "Mesa3D does not like my context creation code" 的 stackoverflow 线程似乎强化了这一点。

另一个名为 wglCreateContext in C# failing but not in managed C++ 的 stackoverflow 线程建议在使用 GDI 函数时必须在 gdi32.dll 之前加载 opengl32.dll,否则可能会出现运行时失败(错误:2000)。

我自己的测试表明,如果调用这些函数的 opengl32/wgl 版本,一些系统(Nvidia,但不是 Intel 或 Parallels VM)上会出现“错误:2000”。更改为 GDI 版本可以解决此问题,但使用 LoadLibrary("opengl32.dll") 似乎没有任何改变。

有没有人研究过这些 WGL 和 GDI 函数之间的区别?很明显,存在某种形式的差异,我试图了解在什么情况下应该使用哪个版本,以及如果使用了错误的版本,可能会遇到什么问题。

编辑:wayback machine brings up a webpage 描述了直接加载 ICD 的工作原理。这显然是 Voodoo 1/2 天的要求,当时 2d 和 3d 加速器是两个不同的硬件,具有单独的 ICD(普通的 opengl32.dll+ICD 机制无法处理)。地震 1 和 2 显然会加载直接因为这个ICD。

但是,下面的帖子显示 AMD ICD 不导出 wgl 变体,这与此想法相矛盾。

必须有人或某个地方掌握着这些知识的钥匙。

编辑 2:从上面的网页中得到了最清晰的建议:

"因此,如果您使用的是名为 opengl32.dll 的 OpenGL 驱动程序,您 必须调用 GDI 函数,如果您不使用名为 opengl32.dll 的驱动程序,则必须调用 GDI 函数。 "

但这与 AMD ICD 不导出 wgl 函数这一事实有何关系?

编辑 2:显然 Mesa3d 导出 WGL 符号,如下所示:http://cgit.freedesktop.org/mesa/mesa/tree/src/mesa/drivers/windows/gdi

这是有道理的,因为 Mesa3d 不应该用作 ICD。这符合上面链接的 Mesa3d 线程中的模式:它们的调用没有通过 Microsoft 的 opengl32.dll 路由,因此 gdi 函数失败,但 Mesa3d 正在导出 wgl* 函数,因此这些函数仍然有效。但是,这是 Mesa3d 特有的 - 如果您尝试直接使用 AMD 的 ICD,该方法将失败。

【问题讨论】:

  • 有一个巨大的区别,即WGL函数的原型没有在任何系统头文件中定义。 OpenGL32.DLL 导出符号,但除非您手动导入函数,否则您永远不会知道这一点。我怀疑这就是“运行时链接”的意思,你必须做一些讨厌的LoadLibrary (...) 的东西。顺便问一下,您使用的是什么语言?大多数情况下,当这个问题出现时,它是在 C# 的上下文中,LoadLibrary (...) 是一种更常见的做法。在 C 和 C++ 中,大多数人链接到 opengl32.lib / gdi32.lib 并使用 GDI32 函数。
  • 我在开发 OpenTK 时第一次遇到 C# 的这个问题,但我目前正在使用 C++。这个问题与语言无关。使用 LoadLibrary("opengl32.dll") 的原因是为了维护 1.1 之前和 1.1 之后的 OpenGL 函数(GetProcAddress + wglGetProcAddress)的单一代码路径。
  • 这肯定不仅仅是早期/晚期绑定的差异。这些函数在 wgl.spec 中定义并记录在 MSDN 中,因此它们是否在特定的 gl.h 实现中导出并不重要。
  • 您提到的那些功能在 MSDN 上没有记录,您可能需要使用正确的前缀再次搜索。它们不是公开定义的,您必须手动加载 DLL 导入。为了获得最大的兼容性,您应该避免使用未记录的/私有函数,每当新版本的 Windows 出现时,它们通常是首先要破坏的。
  • opengl32.dll 不是 ICD。也许这将有助于清除一些事情。它使用不同的接口与 ICD 通信:Drv...,这就是 ICD 不导出 wgl... 函数的原因。就地震 1 和 2 而言,他们没有使用 ICD。他们有MiniGL drivers,那是很糟糕的事情;他们只实现了雷神之锤 1 和 2 所需的功能,通常不适用于任何其他游戏。

标签: opengl gdi wgl


【解决方案1】:

有一个巨大的区别,即 WGL 函数的原型没有在任何系统头文件中定义。 opengl32.dll 导出符号,但除非您手动导入函数,否则您永远不会知道这一点。

但是,WGL 可安装客户端驱动程序 (ICD) 实现的功能实际上是这样的前缀:DrvSwapBuffers (...)DrvSetPixelFormat (...)DrvGetProcAddress (...) 等......所以如果你肯定没有直接链接到 ICD调用wglChoosePixelFormat (...) 而不是ChoosePixelFormat (...)

opengl32.dll 基本上是微软的 OpenGL 的 GDI 实现和 ICD 的包装器。如果您查看Mesa,您甚至可以看到 ICD 的实现是什么样的;请注意没有任何函数以wgl 为前缀? ICD不导出任何以wgl为前缀的符号,它们实现的WGL函数都是扩展(例如wglSwapIntervalEXT (...)wglChoosePixelFormatARB (...)等),只能使用wglGetProcAddress (...)DrvGetProcAddress (...)加载。


看看 AMD 的 OpenGL ICD:

        

您会注意到 AMD 实际上在其 ICD 中完全实现了 EGL API(您可以获得必要的标头以在 AMD 硬件here 上使用 EGL),但不会导出 WGL 符号。


更新:

如 cmets 中所述,当您调用 ChoosePixelFormat (...) 时,gdi32.dll 实际上会调用 wglChoosePixelFormat (...)。该函数所做的第一件事是尝试加载 opengl32.dll 并调用 wglChoosePixelFormat (...):

.text:4D579CAC ; int __stdcall ChoosePixelFormat(HDC,const PIXELFORMATDESCRIPTOR *)
.text:4D579CAC                 public _ChoosePixelFormat@8
.text:4D579CAC _ChoosePixelFormat@8 proc near
.text:4D579CAC
.text:4D579CAC hLibModule      = dword ptr -4
.text:4D579CAC arg_0           = dword ptr  8
.text:4D579CAC arg_4           = dword ptr  0Ch
.text:4D579CAC
.text:4D579CAC                 mov     edi, edi
.text:4D579CAE                 push    ebp
.text:4D579CAF                 mov     ebp, esp
.text:4D579CB1                 push    ecx
.text:4D579CB2                 push    esi
.text:4D579CB3                 lea     eax, [ebp+hLibModule]
.text:4D579CB6                 push    eax             ; int
.text:4D579CB7                 push    offset aWglchoosepixel ; "wglChoosePixelFormat"
.text:4D579CBC                 call    _GetAPI@12      ; GetAPI(x,x,x)
.text:4D579CC1                 xor     esi, esi
.text:4D579CC3                 test    eax, eax
.text:4D579CC5                 jz      short loc_4D579CD1
.text:4D579CC7                 push    [ebp+arg_4]
.text:4D579CCA                 push    [ebp+arg_0]
.text:4D579CCD                 call    eax
.text:4D579CCF                 mov     esi, eax

这里是GetAPI(它所做的只是加载 opengl32.dll 并从中导入一个命名函数):

                              

现在,ICD 实际上并没有实现ChoosePixelFormat (...),因为它在所有实现中的功能都是相同的。这是一个简单的模式匹配功能。如果您想了解 opengl32.dll 如何在运行时将其 wgl... 函数之一分派到 ICD,请查看 wglSwapBuffers 的控制流:

  

红色左侧分支是安装 ICD 时出现的情况,绿色右侧分支是 wglSwapBuffers 的默认 GDI 实现。有趣的是,您可以看到 GDI 实现需要完整的glFinish (...)。大多数硬件驱动程序在交换缓冲区时倾向于刷新命令队列而不是完成,这可以实现更好的 CPU/GPU 并行性。

【讨论】:

  • 这与 OpenGL 常见问题解答中建议在直接加载 ICD 时使用 wgl 变体的评论相矛盾。再深入一点会找到一个interesting webpage,它解释了如何直接加载 ICD。这显然是在 Voodoo 1/2 天里需要的,当时 2d 和 3d 加速器是两个独立的硬件(普通的 opengl32.dll+ICD 机制无法处理)。
  • 我链接的stackoverflow线程“Mesa3d doesn't like my context creation code”也支持直接加载ICD时必须使用wgl变体。如果 ICD 不导出这些功能,那么它是如何工作的呢? (正如那个线程所证明的那样,它确实有效。)
  • @TheFiddler:就像我想告诉你的那样,opengl32.dll 包装了 ICD。例如,如果安装了任何 ICD,它导出的 wglSwapBuffers (...) 函数将调用 ICD 的 DrvSwapBuffers (...) 函数。但 ICD 本身并不包含这些功能。您列出的文章甚至解释了这一点,尽管没有那么详细。但是你永远不会在 ICD DLL 中找到 wglSwapBuffers (...) 作为导出函数,这根本不是微软为 ICD 设计的接口;所有 ICD 函数都以 Drv... 开头。
  • 几乎没有人直接链接到 ICD(甚至在您发布的示例中也没有),我认为这就是您感到困惑的地方。 ICD 由运行时加载:opengl32.dll。如果要显式使用 ICD,则必须加载系统注册表并查询 HKLM/System/CurrentControlSet/Control/Class/{Adapter GUID}/0000/OpenGLDriverName 的值。如果您绕过 OpenGL 运行时并直接链接到 ICD,您也将无权访问 ChoosePixelFormat (...),因为这是 ICD 无法实现的众多功能之一。
  • 我接受您的回答,因为它是迄今为止我所见过的关于该主题的最佳信息来源。非常感谢您抽出宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
  • 2012-01-18
  • 2013-05-25
  • 2017-10-20
  • 2017-05-28
  • 2018-02-22
相关资源
最近更新 更多