[Mesa-dev] Proposal for an Updated Linux OpenGL ABI

Ian Romanick idr at freedesktop.org
Fri Sep 14 00:17:26 PDT 2012


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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>



More information about the mesa-dev mailing list