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. 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]). 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. (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. (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