4

I'm a bit confused about the nature of OpenGL 1.0 and 1.1 function pointers on Windows. I think I have it down, but I'm not 100% sure, so I'm hoping that someone will be able to confirm or comment on my current understanding:

My understanding at the moment is that you're supposed to load 1.2+ functions with wglGetProcAddress(), but that 1.0 and 1.1 function have to be loaded with GetProcAddress() through opengl32.dll. What catches my attention, however, is that wglGetProcAddress() supposedly returns different function pointers depending on which HGLRC context is current. Yet presumably the 1.0 and 1.1 pointers from GetProcAddress() are always the same. This disparity in behavior feels unusual.

So, let's say that I have a situation where I have multiple HGLRC objects, we'll call them A and B. I call wglGetProcAddress() and I keep the results in separate pointer pools, one for A and another for B. Yet I also have to load 1.0 and 1.1 functions into these pointer pools, and in this case it seems that the pointers for A and B will always be the same.

What surprises me is that the 1.0 and 1.1 functions must therefore be thin wrappers which redirect OpenGL calls to whichever driver is associated with the current HGLRC. Yet if such a redirect mechanism is already in place on Windows then I wonder why wglGetProcAddress() can't also use it, because doing so would alleviate the danger of it returning context-dependent pointers. I don't necessarily even need to know the answer to this question, but the very presence of the question is what makes me wonder whether or not I understand things correctly at all to begin with.

fieldtensor
  • 3,972
  • 4
  • 27
  • 43

1 Answers1

4

The reason for this is a simple one: The symbols for OpenGL-1.1 are a fixed set, so the interface DLL opengl32.dll can contain both the fallback implementation and the trampolines into the full blown OpenGL implementation provided by the graphics driver (ICD). Those symbols are exposed by the interface DLL in the symbol table and hence are reachable with GetProcAddress.

Anything that goes beyond version OpenGL-1.2 is unknown to the interface library. Hence opengl32.dll does neither contain fallbacks nor trampolines into the ICD for those functions. Instead it acts as a proxy that passes the call to wglGetProcAddress to the actual OpenGL implementation in the graphics driver (ICD). However since different OpenGL contexts may be served by different graphics drivers (ICD) the result may be symbols residing at different addresses. wglGetProcAddress does not look at the symbol table of some DLL but may be implemented in whatever the ICD sees fit.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Interesting. So then I think my initial assessment was correct: In my pools of otherwise context-dependent function pointers the function pointers to 1.0 and 1.1 calls will always be the same (and there's nothing odd or broken about that; well, maybe odd, but not broken). – fieldtensor Aug 09 '14 at 02:16
  • 1
    @patrick-rutkowski: Yes, this is indeed the case. IMHO the interface DLL `opengl32.dll`should never have exported any OpenGL symbols whatsoever, but only the `wgl…` ones. This would have forced all programs to dynamically load all OpenGL symbols dynamically through `wglGetProcAddress`; tedious yes, but much more consistent. Also it whould have been trivial to make all the returned pointers to point to entries in a dynamically managed `JMP` table in an TLS area, that gets updated whenever the OpenGL context is switched. – datenwolf Aug 09 '14 at 02:44
  • That's interesting that you mention TLS jump tables. I'm currently trying to write trampoline wrappers for GL 1.2+ functions. The trampoline functions would have to keep track of which func-pointer pool was "active" in a given thread. Every one of my trampoline functions would have to make a call to `TlsGetValue()` to get the active func-pointer pool before calling the GL function from the actual ICD driver. I'm currently looking into it, but I suspect that it might be too much of a performance overhead, and I'm not quite sure how to proceed. – fieldtensor Aug 09 '14 at 02:55
  • @patrick-rutkowski: Well, yes, there is a certain penality. That being said TlsGetValue has been optimized for speed (according to the MSDN). Also don't forget that modern OpenGL is all about reducing the number of function calls that are necessary to get a large number of primitives drawn. Note that only Windows is that messed up. In GLX the function pointers are specified to be the same across all contexts and threads (it still leaves the problem that different contexts can be bount to different threads which requires dispatching, but thats a far more benign problem for the implementation.) – datenwolf Aug 09 '14 at 12:27
  • @patrick-rutkowski: You might want to look at how Mesa tackles the problem of juggling with dispatch tables: http://www.mesa3d.org/dispatch.html#overview – datenwolf Aug 09 '14 at 12:31
  • Right, that's a very good point about minimizing the number of function calls. I've also done some research on TLS in Windows since posting my previous comment, and I found this: http://www.nynaeve.net/?p=181 So it looks like TlsGetValue() is essentially a zero-overhead call (unless a pointer deference or two is considered overhead). – fieldtensor Aug 09 '14 at 12:34
  • At the moment I finally have my GL loader and dispatcher fully working. Every thread uses TlsSetValue() to set an HGLRC as well as a struct holding all of that HGLRC's function-pointers. Then I have wrappers around every OpenGL function that uses TlsGetValue() to fetch the current function-pointer-table for the thread's current HGLRC and trampolines the GL call. I haven't benchmarked it yet, but the method should work great assuming TlsGetValue() really is basically free like the internet says. It took some work parsing the GL registry's gl.xml file, but it was worth it. – fieldtensor Aug 09 '14 at 12:35