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

The*_*ler 9 opengl gdi wgl

opengl32.dll和gdi32.dll之间重复以下功能:

[opengl32.dll]         / [gdi32.dll]
wglChoosePixelFormat   / ChoosePixelFormat
wglDescribePixelFormat / DescribePixelFormat
wglGetPixelFormat      / GetPixelFormat
wglSetPixelFormat      / SetPixelFormat
wglSwapBuffers         / SwapBuffers
Run Code Online (Sandbox Code Playgroud)

我一直在寻找一个答案很长一段时间,但似乎没有任何具体的信息,为什么会这样,他们的确切区别是什么.

OpenGL的常见问题,节5.190,表明这些功能都没有功能上相同的:

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

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

名为"Mesa3D不喜欢我的上下文创建代码"的stackoverflow线程似乎强化了这一点.

另一个在C#中名为wglCreateContext但不在托管C++中的 stackoverflow线程表明,在使用GDI函数时,必须在gdi32.dll之前加载opengl32.dll,否则可能会导致运行时失败(错误:2000).

我自己的测试表明,如果调用这些函数的opengl32/wgl版本,某些系统(Nvidia,但不是Intel或Parallels VM)会出现"error:2000" .更改为GDI版本会清除此问题,但使用LoadLibrary("opengl32.dll")似乎不会更改任何内容.

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

编辑:wayback机器打开一个网页,描述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,该方法将失败.

And*_*man 7

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

然而,WGL安装客户端驱动器(ICD)实现的功能实际上是前缀是这样的:DrvSwapBuffers (...),DrvSetPixelFormat (...),DrvGetProcAddress (...),等...所以你绝对不会直接,如果你调用链接到ICD wglChoosePixelFormat (...)代替ChoosePixelFormat (...).

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


看看AMD的OpenGL ICD:

        ICD出口

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


更新:

正如评论中所解释的那样,gdi32.dllwglChoosePixelFormat (...)在您调用时实际调用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
Run Code Online (Sandbox Code Playgroud)

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

                               GetAPI  -  gdi32.dll

现在,ICD实际上并没有实现ChoosePixelFormat (...),因为它在所有实现中功能相同.它是一种简单的模式匹配功能.如果你想看看opengl32.dll如何在运行时将其中一个wgl...函数调度到ICD,请查看以下控件流wglSwapBuffers:

  wglSwapBuffers  -  opengl32.dll

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