9

The following functions are duplicated between opengl32.dll and gdi32.dll:

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

I have been searching for an answer for a long time now, but noone appears to have any concrete information why that is and what their exact difference is.

The OpenGL FAQ, section 5.190, suggests that these functions are not functionally identical:

To ensure correct operation of OpenGL use ChoosePixelformat, DescribePixelformat, GetPixelformat, SetPixelformat, and SwapBuffers, instead of the wgl equivalents, wglChoosePixelformat, wglDescribePixelformat, wglGetPixelformat, wglSetPixelformat, and wglSwapBuffers. In all other cases use the wgl function where available. Using the five wgl functions is only of interest to developers run-time linking to an OpenGL driver.

Does "run-time linking to an OpenGL driver" imply bypassing opengl32.dll and loading an ICD directly?

A stackoverflow thread named "Mesa3D does not like my context creation code", appears to reinforce this.

Another stackoverflow thread, named wglCreateContext in C# failing but not in managed C++ suggests that opengl32.dll must be loaded before gdi32.dll when using the GDI functions, or risk runtime failure (error: 2000).

My own testing indicates that "error: 2000" occurs on some systems (Nvidia, but not Intel or a Parallels VM) if the opengl32/wgl version of these functions is called. Changing to the GDI version clears this issue, but using LoadLibrary("opengl32.dll") does not appear to change anything.

Has anyone ever investigated the difference between these WGL and GDI functions? It is clear that there is some form of difference, and I am trying to understand which version should be used under which circumstances and what are the potential pitfalls if the wrong version is used.

Edit: wayback machine brings up a webpage that describes how direct loading of an ICD works. This was apparently required back in the Voodoo 1/2 days when the 2d and 3d accelerators were two different pieces of hardware with separate ICDs (which the normal opengl32.dll+ICD mechanism couldn't handle.) Quake 1 and 2 would apparently load ICDs directly because of this.

However, a post below shows that the AMD ICD does not export the wgl variants, which contradicts this idea.

There has to be someone or some place out there that holds the keys to this knowledge.

Edit 2: from the webpage above comes the clearest suggestion yet:

"Therefore if you are using a OpenGL driver named opengl32.dll you must call the GDI functions, and if you are not using a driver named opengl32.dll you must NOT call the GDI functions. "

But how does this fit in with the fact that the AMD ICD does not export wgl functions?

Edit 2: apparently Mesa3d exports WGL symbols, as can be seen here: http://cgit.freedesktop.org/mesa/mesa/tree/src/mesa/drivers/windows/gdi

This makes sense, since Mesa3d is not supposed to be used as an ICD. This fits with the pattern in the Mesa3d thread linked above: their calls are not being routed through Microsoft's opengl32.dll, so gdi functions fail, but Mesa3d is exporting wgl* functions so these still work. However, that's specific to Mesa3d - that method would fail if you tried to use AMD's ICD directly.

4

1 回答 1

8

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

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

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


看看 AMD 的 OpenGL ICD:

        ICD 出口

您会注意到 AMD 实际上在其 ICD 中完全实现了 EGL API(您可以在此处获取在 AMD 硬件上使用 EGL 所需的头文件),但不会导出 WGL 符号。


更新:

正如评论中所解释的,当你调用gdi32.dll时,它实际上会调用. 该函数所做的第一件事是尝试加载opengl32.dll并调用:wglChoosePixelFormat (...)ChoosePixelFormat (...)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 实现需要完整的glFinish (...). 大多数硬件驱动程序在交换缓冲区时倾向于刷新命令队列而不是完成,这允许更好的 CPU/GPU 并行性。

于 2013-12-17T23:18:03.037 回答