Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libGL vs libOpenGL exported symbols #63

Closed
evelikov opened this issue Feb 4, 2016 · 30 comments
Closed

libGL vs libOpenGL exported symbols #63

evelikov opened this issue Feb 4, 2016 · 30 comments

Comments

@evelikov
Copy link
Contributor

evelikov commented Feb 4, 2016

It seems that the latter is 'missing' a few entry points. Afaics the core version is around while the extension (*ARB, *EXT, etc.) version is missing.

As we don't have any programs using libOpenGL.so I'm still wondering if we cannot expose the symbols only via glXGetProcAddressARB/eglGetProcAddress. Although if that's not an option can we have some merits behind the decision ("why choose symbol X or Y") and have it documented.

Thanks
Emil

@kbrenneman
Copy link
Collaborator

Exporting some set of functions directly is useful, because loading everything via glXGetProcAddress would be a major annoyance for most developers. For all practical purposes, that would require every developer to use something like GLEW.

As noted in #62, there are a lot of broken applications that depend on a larger set of exported functions in libGL.so, and there's no way to know what might break if we reduced that set.

Since libOpenGL.so is brand new, we can pick a set of functions to export and stick with it, so that developers don't have to deal with the flood of duplicate/unresolved symbol errors every time they link to a different library version.

So, libOpenGL.so exports the core functions for OpenGL up through version 4.5. That'll cover a lot of applications, and it keeps the definition simple and easy to remember. It's also something that's easy to keep consistent between versions.

Note that if an application developer wants to load everything with glXGetProcAddress, they can. You can link against libGLX.so, but not link against libOpenGL.so or libGL.so.

@evelikov
Copy link
Contributor Author

evelikov commented Feb 4, 2016

Exporting some set of functions directly is useful, because loading everything via glXGetProcAddress would be a major annoyance for most developers. For all practical purposes, that would require every developer to use something like GLEW.

And at the same time the current implementation inspires for yet another round of GLEW and alike libraries. As getting function pointers for extensions will be "annoying", developers will just opt for OpenGL "3.3" as opposed to 3.2 + extensions they want/need :-(

With my proposal resolving a symbol is a consistent and uniform practise - two line macro, used once for each function needed.

So, libOpenGL.so exports the core functions for OpenGL up through version 4.5.

I take it that 4.5 was chosen as "the safest bet", or there's a technical reason behind it ?

@kbrenneman
Copy link
Collaborator

Extension functions will always be there, and there will always be GLEW-like libraries to work with them. But keeping a simple subset of core functions means that a lot of applications can work without a support library, and it means that writing and maintaining a support library like GLEW becomes much simpler.

OpenGL 4.5 was selected because it's the most recent version that any existing driver supports.

@tfogal
Copy link

tfogal commented Feb 9, 2016

A long time ago at a university far, far away, I implemented basically the same idea of libglvnd, except in user-space inside GLEW. It was more pain than desired because GLEW separated out how it implemented OpenGL 1.4 and >1.4 functions, since the <=1.4 stuff was guaranteed to be there.

Honestly, speaking as an app developer, I wish OpenGL libraries would expose *GetProcAddress and nothing else. It would've made that work simpler and would've ameliorated other compile-against-one-GL-use-another hell.

I imagine the possibility is pretty low, but I'll put in my +1 (or +$0.02) that the export list be extremely restricted. One can always implement a "libGL" that has 'real' functions forwarding to VND to hack around even binary-only applications.

@evelikov
Copy link
Contributor Author

evelikov commented Feb 9, 2016

@tfogal that's precisely my line of thinking as well. Glad to see I'm not the only person thinking this way.

@anholt, considering you wrote libepoxy (which deals with the same OpenGL symbol mayhem), can we get your input on the topic; Should libOpenGL.so provide any (and if so which) GL symbols or should they be available only via *GetProcAddress ?

I guess we can reach out to the GLEW and SDL developers as well ?

@kbrenneman
Copy link
Collaborator

libOpenGL.so is an OpenGL entrypoint library. OpenGL symbols are the only things that it exports, so if you take those out it would be literally empty.

Remember, using libOpenGL.so is totally optional. If you don't want to deal with any exported OpenGL symbols, then you don't have to. You can link against libGLX.so by itself, and then you can call glXGetProcAddress to load everything else.

@evelikov
Copy link
Contributor Author

evelikov commented Feb 9, 2016

libOpenGL.so is an OpenGL entrypoint library. OpenGL symbols are the only things that it exports, so if you take those out it would be literally empty.

Not quite: after all we do have the ctor/dtor - __libGLInit + __libGLInit (or _init/_fini). Which for some interesting reason are missing from the final binary.

Remember, using libOpenGL.so is totally optional. If you don't want to deal with any exported OpenGL symbols, then you don't have to. You can link against libGLX.so by itself, and then you can call glXGetProcAddress to load everything else.

You will have to use libGLX/libEGL either way (linking against them or not), so that's not the question. One cannot use GL without any form of winsys interaction. The idea is that we're allowing for the awkward libGL.so situation to happen again.

@kbrenneman
Copy link
Collaborator

My point wasn't that you have to link against libGLX.so, my point is that you don't have to link against libOpenGL.so.

libGLX.so only includes GLX functions, so if you link against libGLX.so, and you don't link against libOpenGL.so, then you won't import any OpenGL functions at all. You can then use glXGetProcAddress to load them.

@evelikov
Copy link
Contributor Author

There is a lot of confusion about GL/EGL/GLX/etc. and what magic is required for one to use them in their application. A good example of that is this article [1] - can you spot the mistakes (bonus points if you can ping the author about them) ? By providing more than one way (incomplete list for static linking and *GetProcAddress()) we provide further cause for confusion.

Do you have a case (cases?) which require static linking, or this part of the libglvnd is not really open to discussion, or simply my point is not going across ?

In summary - yes we can leave the current approach, yet it's proven fragile and confusing. Thus it would be better to avoid it.

[1] https://devblogs.nvidia.com/parallelforall/egl-eye-opengl-visualization-without-x-server/

@rmorell
Copy link
Contributor

rmorell commented Feb 10, 2016

The most semver-compliant way to organize things would be to have several libraries, one for each core GL version:
libOpenGL.so.1.4
libOpenGL.so.2.0
[...]
libOpenGL.so.4.5
where each of these libraries only exports the core functions for that GL version. That way as new GL core versions come out, we can ship new libraries exporting those functions and still leave the old ones alone. Apps would still need to use glXGetProcAddress to get any extension functions (after checking the extension string, of course).

There are problems with this approach, though:
a) /usr/lib would be full of libOpenGL DSOs with a lot of duplication (although they don't have to actually implement the functions so they shouldn't be that large)
b) An app linking with '-lOpenGL' would not necessarily link against the version with the SONAME for the minimal OpenGL core version that it needs; it would by default link against the highest that the libglvnd install on that system supports (since ldconfig will presumably put that link in place).

But on the plus side, if an app does choose to link against a specific versioned libOpenGL it should get compile-time checking that it doesn't use symbols outside of that core set, and load-time checking that that version is available on the system. (It would still need to create a GL context using the same version, though.)

@aritger
Copy link
Collaborator

aritger commented Feb 10, 2016

Some of this is addressed in:

https://github.com/aritger/linux-opengl-abi-proposal/blob/master/linux-opengl-abi-proposal.txt

The selection of what symbols are exposed in libOpenGL.so is somewhat arbitrary, and whatever set we choose I'm sure it will never make everyone happy.

When glvnd was proposed at several XDCs, some advocated revving libOpenGL.so frequently to keep up with new OpenGL versions. I think that is undesirable both because it means more maintenance work for libOpenGL.so, and for the reasons tfogal described.

I like the idea of not providing a libOpenGL.so at all (less code to support), if we can sell the community on it. This would be a good topic to bring up on the mesa-dev list.

@kbrenneman
Copy link
Collaborator

The problem with the current situation is that different implementations of libGL.so.1 export a different set of functions, with no real rule or pattern between them.

There's nothing we can do about libGL.so.1 itself anymore. Too many buggy applications depend on that, and the chance of getting all of those applications fixed is zero.

The point of libOpenGL.so is to provide a clean separation between the window-system functions (GLX and EGL) and the OpenGL functions. In addition, since we don't have any compatibility baggage, the export list can be simple and well-defined. Unlike libGL.so, we can ensure that libOpenGL.so keeps the same export list forever.

And in answer to the question of whether static linking is required, no, static linking of OpenGL functions is never strictly required. Static linking (or at least, static exports) of GLX or EGL are required, hence splitting the GLX and OpenGL functions into libGLX.so and libOpenGL.so -- libGLX.so is required, and libOpenGL.so is optional.

I don't see where the confusion would come from. libGLX.so provides GLX functions, including glXGetProcAddress. libOpenGL.so exports a simple list of OpenGL functions as a convenience. libGL.so exports a large enough set of OpenGL functions to not break existing programs.

If you're using (or writing) a library like GLEW to look up OpenGL functions, then that's fine. You don't need libOpenGL.so in that case, so don't use it.

@kbrenneman
Copy link
Collaborator

I like the idea of not providing a libOpenGL.so at all (less code to support), if we can sell the community on it. This would be a good topic to bring up on the mesa-dev list.

Note that as long as libGL.so.1 and the libGLES*.so libraries exist, the additional maintenance for libOpenGL.so is zero. libOpenGL.so and libGLES*.so are built from the same sources and the same scripts. The only thing that separates them are hard-coded entries in a hashtable in one Python script.

@aritger
Copy link
Collaborator

aritger commented Feb 10, 2016

I think the point is that an application developer has to go through the thought process:

  • Which OpenGL API entry points do I want to use?
  • If any entry point is not provided by libOpenGL.so, use GLEW
  • Else, just call libOpenGL.so symbols directly

That is more work than just:

  • Use GLEW

libOpenGL.so is intended as a convenience for developers. As long as it is sufficiently useful for application developers, great. But, if everyone is just going to use GLEW, then libOpenGL.so is a potential point of confusion and extra code to maintain that we don't otherwise need. I don't write enough serious OpenGL applications myself to judge.

As an aside, the discussion here and in #59 reminded me of issue #7 in

https://github.com/aritger/linux-opengl-abi-proposal/blob/master/linux-opengl-abi-proposal.txt

(How should libOpenGL.so and libGLESv2.so coexist in the same process?). A long time ago, I wrote some of proof of concept stuff for symbol versioning to avoid symbol conflicts between libOpenGL.so and libGLESv2.so:

https://github.com/aritger/libgl-elf-tricks-demo

But, maybe we can avoid the problem more completely by just forcing developers to always go through GetProcAddress, instead.

@kbrenneman
Copy link
Collaborator

Having an entrypoint library or a loader library that's part of libglvnd still seems like it would be useful, at least for any simple OpenGL apps that don't need anything beyond the core library.

As for libOpenGL.so and libGLESv2.so coexisting, they already can. Calls to the same function in either library will go through the same entry in the same dispatch table, so they're all equivalent.

You can use any combination of libGL.so, libGLX.so, libOpenGL.so, and libGLES*.so in the same process and they all just work. That's also why libOpenGL.so is optional -- the functions that it exports are the same as the functions that you get from glXGetProcAddress or any of the others.

@evelikov
Copy link
Contributor Author

@kbrenneman I've already pinged Aaron on one of the downsides with the mentioned article. If you're kind enough to list any issues/confusions that you see, I would gladly elaborate who they relate here.

I believe Andy mentioned it perfectly - the list is arbitrary and it's impossible to have everyone happy. So why not just have no list ?

Additionally I'm wondering if by dropping the list we'll be able to avoid the DT_FILTER glibc bug, as mentioned in issues [1]

Edit: fixing up a butchered name
[1] https://github.com/NVIDIA/libglvnd#issues

@evelikov
Copy link
Contributor Author

Having an entrypoint library or a loader library that's part of libglvnd still seems like it would be useful, at least for any simple OpenGL apps that don't need anything beyond the core library.

I'm afraid that only goes for a very selected simple apps.

  • Want to do anything legacy related - though luck
  • I don't know that extension FOO got promoted to GL v.X+1 core, so how do I deal with extensions again ?
  • I had no idea that I can use GL v.X + extension FOO, GL v.X+1 had everything that I needed, so I went along with it.
  • All my GL v.X symbols linked fine, so can I omit the runtime check if GL v.X is truly available.

Apologies for the lame wording on the examples, although we've all seen these in one form or another.

@kbrenneman
Copy link
Collaborator

I believe Andy mentioned it perfectly - the list is arbitrary and it's impossible to have everyone happy. So why not just have no list ?

I guess I just don't see the advantage to removing it. The maintenance cost for it is zero. We have to supply libGL.so.1 and libGLES, and if we do that, then we get libOpenGL.so for free.

It might be useful to some (I've used it for a lot of random one-off test programs), and it won't affect anyone else.

Additionally I'm wondering if by dropping the list we'll be able to avoid the DT_FILTER glibc bug, as mentioned in issues [1]

It won't. We still have to provide libGL.so and libGLES, and the entrypoints work the same way for all of them. We'll get all the same problems there that we would in libOpenGL.so.

@evelikov
Copy link
Contributor Author

I believe Andy mentioned it perfectly - the list is arbitrary and it's impossible to have everyone happy. So why not just have no list ?

I guess I just don't see the advantage to removing it. The maintenance cost for it is zero. We have to supply libGL.so.1 and libGLES, and if we do that, then we get libOpenGL.so for free.

How about - no list, no contention point ;-) The other benefits of reduced "fragility, confusion".
One can discard the former if a) distros/third parties do not distribute modified version of glvnd and b) distros/third parties always distribute the latest glvnd. Which luckily is the case, generally.
On the confusion part I'll refer you to the lovely list above plus the devblogs article mentioned previously.

Note: I'm not trying to make anyone look bad, just giving examples that things are confusing even for people with experience in using OpenGL.

@tfogal
Copy link

tfogal commented Feb 11, 2016

In visualization at least, everybody decided long ago to "just use GLEW." Pinging the GLEW lead @nigels-com to see if he has comments.

If there are going to be issues when one library links against libOpenGL and a second links against, say, libEGL or libGLES or whatever, then I urge GLVND to axe libOpenGL.so. If it's possible to happen two ways, then it will happen. Modern supercomputing software has 100+ project dependency graphs that could be used to frighten small children, and are often compiled in environments where 5+ versions of each of the base libraries coexist. Any possible linking mistake will occur. Sometimes a few of the impossible ones, too.

@nigels-com
Copy link

Hi Tom, thanks for the ping. I've been recently grappling with EGL support for GLEW, and getting concerned that it's going to turn into a can of worms. Rightly or wrongly, for compatibility reasons GLEW is fairly constrained in terms of it's assumptions, and at least there is no risk of GLEW symbols resolving to libOpenGL.so (unless someone get's creative with macros in glew.h).

I'm really not clear about libOpenGL.so being intended as "the way of the future", or an opt-in solution for certain use cases.

As for the question at hand - one thing I really do like about GLEW and Regal is the simplicity of not needing to load entry points manually. #include the header, link the library, end of story. And it's arguable that anything in between "all or nothing" is just a committee decision that everyone will criticise, rightly or wrongly, for one reason or another. The rationale that core functions are "forever" makes me giggle, personally.

--- Nigel

@evelikov
Copy link
Contributor Author

FTR we had a few people over at #dri-devel at freenode who were struggling with mesa and symbol resolution ("mesa does not export statically symbol X" or "symbol X is exported but I forgot to check if the extension is available before using the function"). A fair few of these cases were using SDL and GLEW. I'm afraid I don't recall the details - perhaps they were using an old version or simply misusing the library?

@nigels-com have you seen libepoxy [1] ? It has some details about its origin and relation to GLEW.

[1] https://github.com/anholt/libepoxy

@kbrenneman
Copy link
Collaborator

I'm really not clear about libOpenGL.so being intended as "the way of the future", or an opt-in solution for certain use cases.

GLEW itself could avoid the problem entirely by just ignoring libOpenGL.so and using libGLX.so (or libEGL.so) by itself. At that point, you can just load all OpenGL functions using glXGetProcAddress and not worry about what might be exported from any of the other libraries.

If the app does somehow resolve a function as something in libOpenGL.so instead of from GLEW, then it doesn't matter because they'll be the same function. Either one will be a dispatch stub, so they'll both just go to the vendor library implementation.

The rationale that core functions are "forever" makes me giggle, personally.

This is easier with GLVND, because all OpenGL functions are nothing more than dispatch stubs. They're all dispatched the same way (based on the current context), and so none of the GLVND libraries know or care what any of the OpenGL functions do. It's not a question of what's "core" in a driver or in some version of the OpenGL spec, it's just a list of symbol names to export.

"symbol X is exported but I forgot to check if the extension is available before using the function"

There's nothing that GLVND can do about that. Whether or not you use libOpenGL.so, libGLX.so doesn't know what vendor libraries are going to be used or what functions they might support. When you call glXGetProcAddress, it'll return -- and if necessary, generate -- a dispatch stub for whatever GL or GLX function you requested.

If the vendor library for the current context doesn't provide an implementation, then libGLdispatch.so will plug a no-op stub into that part of the dispatch table.

@kbrenneman
Copy link
Collaborator

If there are going to be issues when one library links against libOpenGL and a second links against, say, libEGL or libGLES or whatever, then I urge GLVND to axe libOpenGL.so.

That won't be a problem with GLVND, for the same reason as above. You can safely mix and match glXGetProcAddress, libOpenGL.so, libGL.so, and the libGLES libraries, in any combination and it all works out. Because they're all dispatch stubs, it doesn't matter which stub you call through.

@tfogal
Copy link

tfogal commented Feb 11, 2016

I'm really not clear about libOpenGL.so being intended as "the way of the future", or an opt-in solution for certain use cases.

GLEW itself could avoid the problem entirely by just ignoring libOpenGL.so and using libGLX.so (or libEGL.so) by itself. At that point, you can just load all OpenGL functions using glXGetProcAddress and not worry about what might be exported from any of the other libraries.

What if an app wants to delay the choice of EGL versus GLX until runtime? Would it still work to have GLEW (or whatever GLEW-like library) link against libGLX (and only libGLX)?

@kbrenneman
Copy link
Collaborator

What if an app wants to delay the choice of EGL versus GLX until runtime? Would it still work to have GLEW (or whatever GLEW-like library) link against libGLX (and only libGLX)?

Yes. The dispatch stubs are all either part of libGLdispatch.so or wrappers around libGLdispatch.so, which is shared between EGL and GLX. Calling either eglMakeCurrent or glXMakeCurrent will plug a dispatch table into libGLdispatch.so, and that dispatch table is the only thing libGLdispatch.so cares about.

That was also one of the original reasons to split the OpenGL functions into a separate libOpenGL.so library. You can use it with EGL or GLX.

If you had reason to, you could even have an EGL context current on one thread and a GLX context current on the another.

In theory, libglvnd could also provide a generic GetProcAddress-style function that just goes directly to libGLdispatch.so, and doesn't depend on GLX or EGL at all. It wouldn't be able to hand back GLX or EGL extensions, of course, but it would hand back any OpenGL functions.

@tfogal
Copy link

tfogal commented Feb 11, 2016

Okay, great. My understanding is then:

  • it then doesn't matter what users link against
  • in a large dependency stack, different libraries can link against different lib*GL*so's and it will all work out. For example, if library A links against libGLX, "standard" GL functions (i.e. glCompileShader) called in library A will still reliably work, even when the application is using EGL with a context created by library B that linked against libOpenGL or libEGL.

If that's true, then this is pretty easy (and wonderful!) for visualization's purposes. I just need to convince @nigels-com that the GLEW GPA be done via runtime setup as opposed to #ifdefs (maybe it already does do that, it's been a while now ...).

@kbrenneman
Copy link
Collaborator

Your understanding is correct.

You'll still have to be careful of any cases where a library assumes that EGL or GLX is in use, of course. The libEGL.so and libGLX.so libraries themselves won't conflict with each other, though.

The only exception to that is the MakeCurrent calls, since each thread can only have one current context. If you try to call glXMakeCurrent when you already have a current EGL context or vice-versa will simply fail.

@evelikov
Copy link
Contributor Author

I get the feeling that we're drifting from the discussion a bit here, so I'll just mention this final piece:

@tfogal ftr, waffle can provide you with an abstraction layer of the winsys - just toggle the platform type [1] and enjoy. You can even iterate over different platforms :-)
While libepoxy mostly provides you with a simplification of the dispatch itself (core vs extension functions, dl_sym vs GetProcAddress). The symbols can be used directly, like
if epoxy_has_gl_extension("foo") glHamSandwitch()

[1] https://github.com/waffle-gl/waffle/blob/master/examples/simple-x11-egl.c#L77

@evelikov
Copy link
Contributor Author

@kbrenneman so it seems that I cannot convince you that the "lets export some arbitrary list of symbols" design makes things a bit more confusing and fragile ? It'll be a shame if we have to do "The new^2 Linux OpenGL ABI" in a few years ... Hope that you'll be around joking about it over a few beers :)

Let's close this ticket.

vlc-altair pushed a commit to videolan/vlc that referenced this issue Dec 6, 2017
Use GetProcAddress to fetch GL core symbols instead of using symbols from the
libGL linked with the vlc gl plugin (but not for GLES2/Windows/macOS).

This fixes a crash with nvidia drivers when using EGL but can also fix some
others issues if a vendor/distrib ship more than one openGL libs.

cf. NVIDIA/libglvnd#63

Symbols from the old libGL.so.1 were used instead of symbols from the new
libOpenGL.so (that are fetched via eglGetProcAddress()).

I guess that libGL.so.1 can't work with EGL with recent drivers.

Fixes #19093
tguillem added a commit to tguillem/vlc that referenced this issue Jan 9, 2018
Use GetProcAddress to fetch GL core symbols instead of using symbols from the
libGL linked with the vlc gl plugin (but not for GLES2/Windows/macOS).

This fixes a crash with nvidia drivers when using EGL but can also fix some
others issues if a vendor/distrib ship more than one openGL libs.

cf. NVIDIA/libglvnd#63

Symbols from the old libGL.so.1 were used instead of symbols from the new
libOpenGL.so (that are fetched via eglGetProcAddress()).

I guess that libGL.so.1 can't work with EGL with recent drivers.

Fixes #19093

(cherry picked from commit 744ac6e)
Signed-off-by: Thomas Guillem <thomas@gllm.fr>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants