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

Reply via email to