On Tue, Apr 25, 2000 at 11:54:35PM -0700, Jon Leech wrote:
| The re-vote on mandating inclusion of glext.h from gl.h is pretty
| much where the original vote ended: hung with equal totals for choices
| (A) and (C) (I think there's maybe 1 point in favor of (A) now that
| Brett voted, but if Blythe votes it might well slide back). We all seem
| to have taken a collective breather for a bit, although there is some
| behind the scenes activity to try and conclude this (will let the people
| involved say more when they're ready).
Jon's referring to a "shuttle diplomacy" effort that Michael Gold and
I have been pursuing for the last two weeks.
This topic has been surprisingly contentious, but it's vital to reach
agreement. If we don't, then not only will app developers be
sentenced to an eternity of writing ifdefs, but it's also possible
that some portability issues will be practically intractable. More on
this below.
Michael and I hoped to rally support for a solution that avoids the
worst-case scenario. We haven't achieved unanimity, but we've made
some progress. Please bear with me while I examine the options one
more time.
Option A is essentially to leave gl.h as it is, without any provisions
to include glext.h. However, multiple versions of gl.h have already
been deployed, and they include enumerant and function declarations
for different sets of extensions. This has three consequences:
1. The common idiom for declaring extension functions actually
can't be used reliably, because gl.h *might* contain
conflicting declarations for the same names. For example:
#include <GL/gl.h>
...
PFNGLEXTENSIONEXTPROC glExtensionEXT;
...
glExtensionEXT = (PFNGLEXTENSIONEXTPROC)
glXGetProcAddressARB("glExtensionEXT");
...
glExtensionExt();
This fails to compile on some systems because glExtensionEXT
is already declared in gl.h
GLAPI void GLAPIENTRY glExtensionExt();
and fails to compile on some other systems because
PFNGLEXTENSIONEXTPROC is *not* declared in gl.h.
This can't be handled readily with conditional compilation,
because there's no way to test for the existence of the
typedef or the function declaration. The existence of the
extension's compile-time-test macro does *not* guarantee the
existence of both the typedef and function declarations.
(There are existence proofs for several cases.)
At the moment, I see no way for an app developer to solve this
but to choose distinct names for extension typedefs and
functions, and re-declare them in the app. (glean uses a C++
namespace for this purpose.) This essentially bypasses the
shared header file, and the consequences of such a move are
usually bad.
2. Apps that call extension functions directly may compile
successfully, but fail at runtime when the libGL.so with which
they're used lacks entry points for those extension functions.
Or they may fail to compile on a different system, whose gl.h
lacks the extension function declarations. Both these
situations have been observed in the field.
3. The two problems noted above may also cause difficulties for
code in glext.h. Unlike the app code itself, the app
developer may be reluctant or unable to modify glext.h
to work around them.
For these reasons Michael and I have been arguing that Option A is
undesirable.
Option B resolves the difficulties with Option A by introducing new
conventions for the structure of extension declarations in gl.h and
glext.h. The rules are:
1. Declarations of extensions in gl.h are guarded by a new
conditional compilation flag (let's call it GL_PORTABLE_SDK
for the moment). When GL_PORTABLE_SDK is *not* defined,
extension declarations are preserved in the same form they
have today, whatever that may be for any given vendor's gl.h.
Compatibility is maintained to the greatest extent possible;
applications compiled with existing Makefiles and a fresh gl.h
from the same vendor will compile and behave just as they did
before.
2. When GL_PORTABLE_SDK is defined, all the extension
declarations are compiled out of gl.h. This yields a pristine
header file that covers just declarations for the OpenGL core.
This guarantees that gl.h contains no extension declarations
that might conflict with those in the app or in glext.h.
3. In addition, when GL_PORTABLE_SDK is defined, gl.h includes
glext.h. From the app developer's point of view, including
gl.h brings in all the declarations needed to use both the
core and the extensions, but from the system administrator's
point of view, it's possible to maintain gl.h and glext.h
independently.
4. glext.h declares enumerants and typedefs for extensions in the
usual way. However, it does *not* declare extension function
entry points. This allows naked references to extension
functions to be caught at compile time.
OK. With Option B, by default we maintain exactly the same behavior
as Option A, so in an important sense B subsumes A. In the
non-default case (when GL_PORTABLE_SDK is defined), Option B avoids
the problems of Option A; there are no name conflicts, and there are
no declarations of symbols that might be absent from libGL.so at
runtime.
On the downside, though, Option B will suffer from all the same
problems as Option A in the default case. Furthermore, it suffers
from one additional problem: New apps *must* define GL_PORTABLE_SDK,
either in their Makefiles or in their code, in order to avoid the
problems of Option A. A number of people feel that this is setting up
all developers of new OpenGL applications to be trapped when they
forget to define GL_PORTABLE_SDK. At worst it could perpetuate the
problems with gl.h forever.
Which brings us to Option C. This is the same as Option B, but with
the sense of the conditional compilation flag reversed -- defining
GL_NONPORTABLE_SDK (for instance) restores the compatibility-mode
behavior, but by default everything is based on the new semantics.
Under Option C, apps that depend on the (nonportable) extension
function declarations in their particular vendor's gl.h will need to
be modified. One approach is to define GL_NONPORTABLE_SDK in the app
Makefiles, which will provide complete compatibility but be subject to
all the problems of Option A. The other is to modify the app source
code so that it uses glXGetProcAddressARB() to fetch pointers to
extension functions, rather than calling those functions directly.
Fortunately, this can be relatively easy; since the function pointers
are context independent, they only need to be global variables that
are initialized once at app startup.
Michael and I feel that Option C is clearly preferable, as it resolves
the problems with Option A, has the best long-term behavior, and has a
low cost in the short term.
However, the votes have been split roughly evenly between Option A and
Option C. Therefore we've been negotiating with people offline. Our
thought was that perhaps not everyone would be willing to go with
Option C, but if we could at least agree on Option B, we'd be most of
the way toward a good solution.
I think we might have convinced enough people to go for Option C or B
that we can achieve consensus. But in fairness to all the folks we
haven't consulted directly, I thought I'd send this summary and invite
questions.
Have I persuaded anyone to switch from A to C or B? :-)
Allen