为什么opengl32.dll和gdi32.dll之间的函数重复?

时间:2013-12-17 22:06:42

标签: opengl gdi wgl

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函数where   可用。使用五个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)上会出现“error:2000”。更改为GDI版本会清除此问题,但使用LoadLibrary(“opengl32.dll”)似乎不会更改任何内容。

有没有人研究过这些WGL和GDI功能之间的区别?显然存在某些形式的差异,我试图了解在哪种情况下应该使用哪个版本以及如果使用了错误的版本会有什么潜在的陷阱。

编辑:Wayback machine brings up a webpage,描述了ICD的直接加载如何工作。这显然需要在Voodoo 1/2天内,当2d和3d加速器是具有单独ICD的两个不同硬件时(正常的opengl32.dll + ICD机制无法处理。)Quake 1和2显然会加载因此直接导致ICD。

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

成为某人或某个地方,掌握着这些知识的关键。

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

  

“因此,如果您使用的是名为opengl32.dll的OpenGL驱动程序,那么   必须调用GDI函数,如果您没有使用名为opengl32.dll的驱动程序,则必须 NOT 调用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,该方法将失败。

1 个答案:

答案 0 :(得分:7)

存在巨大差异,即WGL函数的原型未在任何系统头中定义。 opengl32.dll 导出符号,但除非您手动导入函数,否则您将永远不会知道这些。

但是,WGL可安装客户端驱动程序(ICD)实现的功能实际上是这样的前缀:DrvSwapBuffers (...)DrvSetPixelFormat (...)DrvGetProcAddress (...)等...所以你绝对不会链接如果您拨打wglChoosePixelFormat (...)而不是ChoosePixelFormat (...),则直接转到ICD。

OPENGL32.DLL 基本上是Microsoft的OpenGL的GDI实现和ICD的包装器。如果你看一下Mesa,你甚至可以看到ICD的实现是什么样的。注意没有一个函数以wgl为前缀? ICD不会导出任何带有wgl前缀的符号,它们实现的WGL函数都是扩展名(例如wglSwapIntervalEXT (...)wglChoosePixelFormatARB (...)等),并且只能使用wglGetProcAddress (...)或{{DrvGetProcAddress (...)加载1}}。


看一下AMD的OpenGL ICD:

ICD Exports

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


更新

正如评论中所解释的,当您致电wglChoosePixelFormat (...)时, gdi32.dll 实际上会调用ChoosePixelFormat (...)。该函数的第一件事是尝试加载 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 并从中导入命名函数):

GetAPI - gdi32.dll

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

wglSwapBuffers - opengl32.dll

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