On 09/13/2012 12:09 AM, Andy Ritger wrote:
There was some recent Khronos discussion about updating the OpenGL
Implementer's Guide for Linux, to which Ian Romanick noted that this
topic will be discussed at XDC as part of the broad "Discuss the future
of EGL, GLX, and OpenGL ES on Linux" agenda item.

To help facilitate the XDC discussion, I've put together a straw-man
proposal for an updated Linux OpenGL ABI.  I'm sending this out now to
start generating feedback and ideas, to help make the XDC discussion as
productive as possible.

Wow. This is a really thorough proposal. Thank you for taking the time to put this together. Will you be at XDC? I look forward to talking to you more there.

I'm still working my way through this, but I have some comments now. I should have quite a bit more by XDC next week.

Thanks,
- Andy Ritger

________________________________________________________________________

2012 Linux OpenGL ABI Proposal

Background

     The current Linux OpenGL ABI [1] (the "2000 Linux OpenGL ABI") is
     twelve years old, and the industry has advanced significantly in
     the past decade:

     * EGL has emerged as a compelling alternative window system binding,
       for use both within and without the X Window System.

     * The OpenGL API has evolved, reducing pressure, in modern usage, on
       OpenGL API call overhead.

     * One of the major short comings of the current ABI is that it does
       not allow multiple vendors' implementations to coexist on the file
       system.  Each vendor provides libGL.so.1; this can lead to library
       collisions and installation fights.  Each Linux distributor has
       been forced to invent its own library search path mechanism to
       resolve these conflicts.

     * In OpenGL 3.1, portions of the OpenGL API were removed (with some
       vendors continuing to provide the removed portions, via the
       ARB_compatibility extension).  A new Linux OpenGL ABI should
       potentially give vendors the option to not provide those entry
       points.

Requirements

     * This document proposes a new Linux ABI for OpenGL, OpenGL ES, GLX,
       and EGL on desktop Linux systems.  While this is hopefully
       useful for other UNIX and UNIX-like platforms, none are called
       out explicitly.  Further, this document does not try to standardize
       with Android or embedded Linux sorts of scenarios.

     * This document defines two ABIs:

         * The ABI between applications and a vendor-independent layer.

         * The ABI between a vendor-independent layer and a vendor
           implementation.

     * The existing ABI to applications must be preserved, though perhaps
       marked deprecated.  I.e., /usr/lib/libGL.so.1 must continue to
       exist for the foreseeable future, and continue to provide the
       entry points and semantics guaranteed by the 2000 Linux OpenGL ABI.
       How libGL.so.1 is implemented, and how vendor implementations fit
       into libGL.so.1, could change relative to the 2000 Linux OpenGL ABI.

     * Whatever minimum versions of OpenGL, OpenGL ES, EGL, and GLX are
       established by this standardization document, vendors must be allowed
       to provide extensions and newer API versions, beyond what is defined
       in this standard.

     * Multiple vendors' implementations must be able to coexist on the
       filesystem without distribution-specific "alternatives"-style selection.

     * It would be nice, from a design standpoint, to allow multiple vendor
       implementations to co-exist within the same process, and for the API
       library to dispatch to the current vendor library with per-context
       granularity.

Glossary

     * API Library: The vendor-neutral library that contains the API entry
       points.  In most cases, this should be fairly thin and simply dispatch
       to the vendor implementation.

     * Vendor Library: The vendor-provided library that provides the actual
       implementation.  The vendor libraries get enumerated, selected, and 
loaded
       by the API libraries.

     * Window system API: one of EGL or GLX.  Conceivably other window system
       APIs could be added in the future.

     * Client API: any rendering API that is used with a window system
       API.  OpenGL, OpenGL ES, OpenVG, etc.

Libraries

     The libraries to be provided are:

     * libGL.so.1
         * Provides symbols for all OpenGL 1.2 entry points (as per [1]).
         * Provides symbols for all GLX 1.3 entry points (as per [1]).
         * This would be a vendor-neutral API library.
         * Hopefully would just pass through to libGLX.so.1 and libOpenGL.so.1
           (maybe using ELF DT_FILTER; see [2] and the GNU ld(1) man page).

     * libOpenGL.so.1
         * Provides symbols for all entry points in a TBD version of OpenGL.
         * Vendors can provide additional OpenGL entry points that can be
           retrieved via {egl,glX}GetProcAddress.
         * No EGL or GLX entry points are provided by this library; it
           is expected that libOpenGL.so.1 would be used in conjunction
           with one of the Window system libraries (libGLX.so.1 or
           libEGL.so.1).
         * Provides a function for vendor libraries to install a dispatch table.
         * Provides a function for vendor libraries to report their
           GetProcAddress-able functions.

     * libGLESv1_CM.so.1:
         * Provides symbols for all OpenGL ES 1 common profile entry points.

     * libGLESv2.so.1:
         * Provides symbols for all OpenGL ES 2 and 3 entry points.

     * libEGL.so.1
         * Provides symbols for all EGL 1.4 entry points.
         * Loads and dispatches to one or more vendor libraries.

     * libGLX.so.1
         * Provides symbols for all GLX 1.4 entry points.
         * Provides symbols for the GLX_ARB_create_context_profile extension.
         * Loads and dispatches to one or more vendor libraries.

     * libEGL_${VENDOR}.so.1
         * Provides a function that libEGL.so.1 can call to initialize and
           get the EGL dispatch table.
         * Expected to pull in the vendor's implementation of all the client
           APIs it supports, and register with the appropriate API library at
           MakeCurrent time.
         * Must not export symbols with names that collide with the namespace
           of EGL (^egl.*) or OpenGL (^gl.*).

     * libGLX_${VENDOR}.so.1
         * Provides a function that libGLX.so.1 can call to initialize and
           get the GLX dispatch table.
         * Expected to pull in the vendor's implementation of all the client
           APIs it supports, and register with the appropriate API library at
           MakeCurrent time.
         * Must not export symbols with names that collide with the namespace
           of GLX (^glX.*) or OpenGL (^gl.*).

     For more background, see [3].

     API libraries (i.e., everything other than the vendor libraries)
     are expected to change infrequently.  For maintenance reasons, they
     should be as simple/thin as reasonable and dispatch to vendors for as
     much as possible.  While Khronos members would author and maintain
     the API libraries, the source to them would presumably be hosted by
     Khronos, available to the general public.  We would recommend to Linux
     distributions to package the API libraries separately from vendor
     libraries (i.e., in separate packages from Mesa, NVIDIA, AMD, etc).

GLX

     The GLX API library should allow for a different vendor library per
     X screen, and dispatch to the correct vendor as early as possible
     (though, see Issue (9)).

     GLX 1.4 entry points fall into one of several categories:

     (1) Entry points that take an X Display pointer and an X screen number
         (or, an object, such as a GLXContect or GLXDrawable, that implies
         an X screen).  E.g.,

             Bool glXMakeCurrent(Display *dpy,
                                 GLXDrawable drawable,
                                 GLXContext ctx);

         Such functions could be implemented by dispatching to the appropriate
         vendor library based on Display and screen.

     (2) Entry points that operate on the current context.  E.g.,

             void glXWaitGL(void);

         Such functions could be implemented by dispatching to the appropriate
         vendor library based on the context of the current thread.

     (3) Entry points that are vendor-independent and return current state.
         E.g.,

             GLXContext glXGetCurrentContext(void);

         Such functions could be implemented entirely within the GLX
         API library.

     (4) "Special functions":

             void *glXGetProcAddress(const GLubyte *procName);
             const char *glXGetClientString(Display *dpy, int name);

         glXGetClientString() is addressed in the Issues section, and
         glXGetProcAddress is addressed in the Dispatching section.

     There would be an X protocol-based mechanism to map an X Display
     pointer and screen number to GLX vendor library, presumably using
     something like the existing X_DRI2Connect/DRI2DriverVDPAU request.
     This would be used similarly to how VDPAU determines the vendor
     library to load (see [4]).

     At MakeCurrent time, the vendor library would call a function in
     libOpenGL.so.1 to register its dispatch table.

EGL

     Similar to GLX, EGL API entry points fall into one of several categories:

     (1) Entry points that take an EGLDisplay.  E.g.,

             EGLSurface eglCreateWindowSurface(EGLDisplay dpy,
                                               EGLConfig config,
                                               EGLNativeWindowType win,
                                               const EGLint *attrib_list);

         Such functions could be implemented by dispatching to the appropriate
         vendor library based on EGLDisplay.

     (2) Entry points that operate on the current context.  E.g.,

             EGLBoolean eglWaitGL(void);

         Such functions could be implemented by dispatching to the appropriate
         vendor library based on the context of the current thread.

     (3) Entry points that are vendor-independent and return current state.  
E.g.,

             EGLContext eglGetCurrentContext(void);

         Such functions could be implemented entirely within the EGL API 
library.

     (4) "Special functions":

             EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
             EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint 
*minor);
             void *eglGetProcAddress(const char *procname);

     The general dispatching approach in EGL would be the same as described
     above for GLX.  The vendor selection, done during eglInitialize(),
     would be more sophisticated that the GLX equivalent.

     The Mesa EGL implementation appears to be almost exactly this.

Dispatching

     All client API library (OpenGL, OpenGL ES) entry points are
     context-dependent.  All the entry points provided by these libraries
     would retrieve the current dispatch table from TLS and jump to the
     vendor implementation.

     There are two categories of client API library entry points:

     (1) A fixed list of entry points that all vendors must provide per
         this document.

     (2) A dynamic list of entry points (for extensions, newer versions
         of OpenGL, etc) which would only be accessible to applications via
         {glX,egl}GetProcAddress.  This list will vary by vendor.

     For category (1), the vendor would just provide an array of function
     pointers as the dispatch table.  The ABI standard would define a
     dispatch table index to each entry point in this category.  The ABI
     library would need to provide a function to the vendor library that
     allowed the vendor to change the dispatch table pointer.

     For category (2), since GetProcAddress is context-independent, the
     API library would need to provide the union of entry points for all
     vendor implementations.  It seems like this could be handled in
     several different ways:

     Option (a): Define the possible list, or at least the indices,
     statically: make the dispatch table for category (2) fixed size,
     and have some sort of centralized registry to manage indices into
     the dispatch table.  Allocation into this table could be handled in
     a way similar to OpenGL enum assignment (see [5]).

         Pros:
             * Easier for vendors to define this dispatch table statically
               at compile time.
         Cons:
             * Need to coordinate for dispatch table index assignment.
             * As more entry points are added, a fixed size would
               eventually be exhausted.
             * Potentially large and sparse dispatch tables would be
               inefficient for memory usage.

     Option (b): At run time, the API library could:
         * Get the list of GetProcAddress-able functions from each
           vendor library.
         * Build the union of GetProcAddress-able functions.
         * Assign dispatch table indices for each function.
         * Tell the vendor libraries what dispatch table index was
           assigned for each of their functions.

         Pros:
             * Flexible to handle an arbitrary number of functions.
         Cons:
             * Vendors have to build their dispatch tables at run time
               to accommodate the dynamically assigned dispatch table indices.
             * Need to query all vendor libraries upfront; this seems a
               little unfortunate, since there potentially could be many
               vendor implementations on the file system, and only a few
               are likely to be used within the context of any one process.

     Option (c): Return entry points for all GetProcAddress'ed strings.  In this
         model, the API library would use logic like this:

         * Does a dispatch function already exist for the string passed
           into GetProcAddress?  If so, return that.
         * Else, online generate a dispatch function for the given string.
           Default the dispatch table to a noop function.
         * At MakeCurrent time, the vendor library would provide all
           the strings/function pointers that it supports and the API
           library would need to plug those vendor functions into the
           online-generated entry points.  I.e., effectively "lazily
           resolve" at MakeCurrent time.

         Pros:
             * Does not require loading vendor libraries before the vendor
               library would be otherwise needed.
         Cons:
             * Some complexity at MakeCurrent time to map the
               GetProcAddress'ed entry points to the vendor functions.
             * This would break apps who determine if a feature is
               available via GetProcAddress (though such apps are broken:
               they are supposed to check the extension string, per [6]).

This is the model we have now. If we make libOpenGL largely fixed in time, I think this is the only model that could work. Right? If we ship a libOpenGL now, how can we know what extensions some vendor library will have in 2 months?

My experience with this model has also shown me that there is almost no benefit to having any functions at fixed offsets in the dispatch table. The vendor library already has to support some functions with dynamic locations, so it makes the code more complex to have some functions with fixed offsets.

Example Control Flow

     Scenario 1: Application links against libGLX.so.1 and libOpenGL.so.1:

         * libOpenGL.so.1's entry points default to a dispatch table filled
           with noop functions.
         * Application calls any GLX entry point; libGLX.so.1 queries
           the X server for the vendor name to use with each X screen.
         * When application calls entry point that should be dispatched
           to vendor, libGLX.so.1 searches defined search paths to find
           the correct vendor library.
         * libGLX.so.1 loads vendor library and gets its table of GLX
           dispatch functions.
         * libGLX.so.1 dispatches to vendor library for appropriate GLX
           entry points.
         * Application calls MakeCurrent, which gets dispatched to
           vendor library.
         * Vendor library's MakeCurrent calls libOpenGL.so.1 to plug
           dispatch table(s) into TLS.
         * Application calls OpenGL entry points in libOpenGL.so.1;
           these dispatch to vendor library.

     Scenario 2: Application links against libEGL.so.1 and libOpenGL.so.1:

         * libOpenGL.so.1's entry points default to a dispatch table filled
           with noop functions.
         * Application calls eglInitialize(); EGL API library uses
           configuration magic to select appropriate vendor driver.
         * libEGL.so.1 loads vendor library and gets its table of EGL
           dispatch functions.
         * libEGL.so.1 dispatches to vendor library for appropriate EGL
           entry points.
         * Application calls MakeCurrent, which gets dispatched to
           vendor library.
         * Vendor library's MakeCurrent calls libOpenGL.so.1 to plug
           dispatch table(s) into TLS.
         * Application calls OpenGL entry points in libOpenGL.so.1;
           these dispatch to vendor library.

     Scenario 3: Application links against libGL.so.1

         * Should be same as the libGLX.so.1 + libOpenGL.so.1 scenario.

Issues

     (1) Should this document mandate particular filesystem paths for
         any of the libraries?

         As long as the API libraries are in the link-time and load-time
         search paths, leaving API library path up to distributions seems
         acceptable.  But we ought to standardize where vendor libraries
         should get installed (since they might be installed by the vendor,
         rather than the distribution).

     (2) glXGetClientString() is vendor-specific, but does not take a
         screen argument (though it does take a Display pointer).  How should
         it be implemented?

         PROPOSED: The best libGLX.so.1 can do is probably the following:
         * Enumerate all of the X screens on the X server.
         * Query the vendor for each X screen.
         * Get the client string for each vendor.
         * Take the union of client strings across all vendors.

It should be safe for the client string to be the union. The client string is such a weird, almost useless thing. A lot of apps mistakenly think that the extensions listed in the client string are the ones available for use. :(

     (3) How should Window System (GLX,EGL) API functions work with
         {glX,egl}GetProcAddress?  GetProcAddress is supposed to return
         context-independent functions.  For client-API functions,
         dispatching can always be done based on the current context,
         but window system layer functions must be dispatched differently
         depending on the arguments they take.  It isn't clear how to
         dispatch a window system layer function without knowledge of
         its parameters.

This has always been a trouble spot for Mesa. A lot of the guts of GLX data structures are shared, so having per-vendor library functions has been all but impossible. In practice, I'm not really sure this is a problem. The part of the implementation in the GLX (or EGL) library is generally very thin, and it is usually just a bunch of GLX protocol handling code.

We ought to be able to define shared interfaces between the API libraries and the vendor libraries in a similar way. This means that users may need to get updated API libraries to have access to all the functionality in the vendor library, but it should simplify the implementations of each enough to make it worth it.

     (4) What dispatching model should be used for GetProcAddress-able
         entry points?

         PROPOSED: Option (c) from the Dispatching section: online generate
         entry points for every string passed to GetProcAddress, and map
         to the actual vendor's functions at MakeCurrent time.

     (5) Even though it is 2012, some important OpenGL applications still
         use immediate mode OpenGL in very API-heavy ways.  In such cases,
         even just minimal dispatching overhead has a significant impact
         on performance.  How can we mitigate the performance impact in
         such scenarios?

         PROPOSED: The performant solution is to online-generate code
         directly into the top-level function of the API library.  The API
         library should provide a function that vendors can call, when
         they deem it thread-safe to do so, that replaces the code in an
         API library function with vendor-provided code.

     (6) How should libEGL.so select the vendor?

         Mesa's EGL implementation seems like at least a good starting
         point for these heuristics.

     (7) Multiple client API libraries (libOpenGL.so.1, libGLESv2.so.1,
         etc) provide symbols with the same name.  It is conceivable that
         a single process may have multiple libraries loaded (either
         explicitly loaded by an application, or chains of library
         dependencies cause multiple of these libraries to be loaded).
         How should symbol collisions be resolved?

         As I understand it, Mesa has resolved this problem by making it
         not matter which conflicting symbol gets loaded: the dispatch
         table contains the union of entry points exposed by GLESv1,
         GLESv2, and GL, and the dispatch function in each client API
         library jumps through the same dispatch table.

         Is the Mesa solution a reasonable requirement to make of all
         vendors?  It would be fine for NVIDIA.

         Alternatively, ELF symbol versioning could be used to distinguish
         between symbols of the same name in each of the client API
         libraries.  See "Maintaining APIs and ABIs" in Ulrich Drepper's
         DSO Howto [7].  I've coded up a demonstration of how that might
         work here: [8].

     (8) How should OpenGL deprecation impact the Linux OpenGL ABI?

         NVIDIA intends to support the OpenGL ARB_compatibility context
         indefinitely, but other vendors may want to omit that.  In that
         case, perhaps it would make sense to split libOpenGL.so.1 into
         separate libraries; e.g.,

             * libOpenGLv31.so.1 provides all entry points available in
               the core profile of OpenGL 3.1.

             * libOpenGLv10.so.1 provides entry points that were removed
               in OpenGL 3.1.

         The two digits identify the major.minor number of the first GL
         version the library supports.

         If an application wants to use a compatibility profile, utilizing
         entry points in OpenGL 3.1 and any that were removed in OpenGL
         3.1, then link against both libraries:

            -lOpenGLv31 -lOpenGLv10

         It would be intended that there are no symbol collisions between
         libOpenGLv31.so.1 and libOpenGLv10.so.1.

         But what happens if a future OpenGL version (M.N) removes another
         set of entry points?  It would sort of make sense to organize
         the libraries like this:

         * libOpenGLvMN.so.1 provides all entry points available in the
           core profile of OpenGL M.N.
         * libOpenGLv31.so.1 provides all entry points that were removed
           in OpenGL M.N.
         * libOpenGLv10.so.1 provides all entry points that were removed
           in OpenGL 3.1.

         But, we should not remove entry points from libOpenGLv31.so.1.

         Perhaps multiple libraries are more complicated than they are
         worth, and we should use a single libOpenGL.so.1?

     (9) How should server-side GLX be handled with multiple vendors?
         X extensions are registered with server, not screen, scope.
         Thus, there is not a good way for different vendors to provide
         their server-side GLX implementation per-screen.

         EGL, because it does not define any X server extension, may be
         an easier way to allow multiple vendors to execute simultaneously
         on different X screens.

         It seems conceivable that a vendor neutral GLX server module
         could be defined, which would dispatch to vendors' server-side
         GLX implementations with X screen granularity.

         However, that standardization effort is deferred for now: this
         proposal is already large enough.  Plus, with the growing interest
         in EGL, there may not be sufficient motivation to standardize
         server-side GLX multi-vendor support.

         It still seems prudent to design the client-side libGLX.so.1
         API library to allow multiple simultaneous vendors.

     (10) How should any of the vendor selection mechanisms in libEGL.so.1
         or libGLX.so.1 interact with Dave Airlie's Prime work?

References

     [1] http://www.opengl.org/registry/ABI/
     [2] http://docs.oracle.com/cd/E23824_01/html/819-0690/chapter4-4.html
     [3] http://www.khronos.org/registry/implementers_guide.html
     [4] http://cgit.freedesktop.org/~aplattner/libvdpau/
     [5] http://www.opengl.org/registry/doc/enums.html
     [6] http://www.opengl.org/registry/doc/rules.html#using
     [7] http://www.akkadia.org/drepper/dsohowto.pdf
     [8] http://github.com/aritger/libgl-elf-tricks-demo

Revision History

     #1 September 12, 2012: aritger
         - initial version

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to