Allow looking up OpenGL extensions in the way appropriate for GL3+.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 10 Oct 2012 21:35:16 -0400
changeset 1117 f6712bf72c19
parent 1116 0ef3d106dab9
child 1118 c383aebc9f13
Allow looking up OpenGL extensions in the way appropriate for GL3+. Note that we use a StringCache for this now, which means we need to allocate memory, which means the API changed to allow custom allocators on some entry points.
mojoshader.h
mojoshader_opengl.c
utils/availableprofiles.c
utils/bestprofile.c
utils/finderrors.c
--- a/mojoshader.h	Wed Oct 10 21:33:36 2012 -0400
+++ b/mojoshader.h	Wed Oct 10 21:35:16 2012 -0400
@@ -2520,9 +2520,17 @@
  *
  * You can only call this AFTER you have successfully built your GL context
  *  and made it current. This function will lookup the GL functions it needs
- *  through the callback you supply, via (lookup) and (d). The lookup function
- *  is neither stored nor used by MojoShader after this function returns, nor
- *  are the functions it might look up.
+ *  through the callback you supply, via (lookup) and (lookup_d). The lookup
+ *  function is neither stored nor used by MojoShader after this function
+ *  returns, nor are the functions it might look up.
+ *
+ * As MojoShader requires some memory to be allocated, you may provide a
+ *  custom allocator to this function, which will be used to allocate/free
+ *  memory. They function just like malloc() and free(). We do not use
+ *  realloc(). If you don't care, pass NULL in for the allocator functions.
+ *  If your allocator needs instance-specific data, you may supply it with the
+ *  (malloc_d) parameter. This pointer is passed as-is to your (m) and (f)
+ *  functions.
  *
  * You should not free any strings returned from this function; they are
  *  pointers to internal, probably static, memory.
@@ -2531,8 +2539,11 @@
  *  safe, you should probably only call this from the same thread that created
  *  the GL context.
  */
-int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d,
-                                   const char **profs, const int size);
+int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup,
+                                   void *lookup_d,
+                                   const char **profs, const int size,
+                                   MOJOSHADER_malloc m, MOJOSHADER_free f,
+                                   void *malloc_d);
 
 
 /*
@@ -2540,15 +2551,23 @@
  *
  * You can only call this AFTER you have successfully built your GL context
  *  and made it current. This function will lookup the GL functions it needs
- *  through the callback you supply via (lookup) and (d). The lookup function
- *  is neither stored nor used by MojoShader after this function returns, nor
- *  are the functions it might look up.
+ *  through the callback you supply via (lookup) and (lookup_d). The lookup
+ *  function is neither stored nor used by MojoShader after this function
+ *  returns, nor are the functions it might look up.
  *
  * Returns the name of the "best" profile on success, NULL if none of the
  *  available profiles will work on this system. "Best" is a relative term,
  *  but it generally means the best trade off between feature set and
  *  performance. The selection algorithm may be arbitrary and complex.
  *
+ * As MojoShader requires some memory to be allocated, you may provide a
+ *  custom allocator to this function, which will be used to allocate/free
+ *  memory. They function just like malloc() and free(). We do not use
+ *  realloc(). If you don't care, pass NULL in for the allocator functions.
+ *  If your allocator needs instance-specific data, you may supply it with the
+ *  (malloc_d) parameter. This pointer is passed as-is to your (m) and (f)
+ *  functions.
+ *
  * The returned value is an internal static string, and should not be free()'d
  *  by the caller. If you get a NULL, calling MOJOSHADER_glGetError() might
  *  shed some light on why.
@@ -2557,8 +2576,10 @@
  *  safe, you should probably only call this from the same thread that created
  *  the GL context.
  */
-const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup, void *d);
-
+const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup,
+                                   void *lookup_d,
+                                   MOJOSHADER_malloc m, MOJOSHADER_free f,
+                                   void *malloc_d);
 
 /*
  * Prepare MojoShader to manage OpenGL shaders.
--- a/mojoshader_opengl.c	Wed Oct 10 21:33:36 2012 -0400
+++ b/mojoshader_opengl.c	Wed Oct 10 21:35:16 2012 -0400
@@ -177,6 +177,7 @@
     // Extensions...
     int have_core_opengl;
     int have_opengl_2;  // different entry points than ARB extensions.
+    int have_opengl_3;  // different extension query.
     int have_GL_ARB_vertex_program;
     int have_GL_ARB_fragment_program;
     int have_GL_NV_vertex_program2_option;
@@ -193,6 +194,7 @@
 
     // Entry points...
     PFNGLGETSTRINGPROC glGetString;
+    PFNGLGETSTRINGIPROC glGetStringi;
     PFNGLGETERRORPROC glGetError;
     PFNGLGETINTEGERVPROC glGetIntegerv;
     PFNGLENABLEPROC glEnable;
@@ -919,6 +921,7 @@
     DO_LOOKUP(core_opengl, PFNGLGETINTEGERVPROC, glGetIntegerv);
     DO_LOOKUP(core_opengl, PFNGLENABLEPROC, glEnable);
     DO_LOOKUP(core_opengl, PFNGLDISABLEPROC, glDisable);
+    DO_LOOKUP(opengl_3, PFNGLGETSTRINGIPROC, glGetStringi);
     DO_LOOKUP(opengl_2, PFNGLDELETESHADERPROC, glDeleteShader);
     DO_LOOKUP(opengl_2, PFNGLDELETEPROGRAMPROC, glDeleteProgram);
     DO_LOOKUP(opengl_2, PFNGLATTACHSHADERPROC, glAttachShader);
@@ -977,7 +980,7 @@
              ((major << 16) | (minor & 0xFFFF)) );
 } // opengl_version_atleast
 
-static int verify_extension(const char *ext, int have, const char *extlist,
+static int verify_extension(const char *ext, int have, StringCache *exts,
                             int major, int minor)
 {
     if (have == 0)
@@ -991,15 +994,7 @@
         return 1;
 
     // Not available in the GL version, check the extension list.
-    const char *ptr = strstr(extlist, ext);
-    if (ptr == NULL)
-        return 0;
-
-    const char endchar = ptr[strlen(ext)];
-    if ((endchar == '\0') || (endchar == ' '))
-        return 1;  // extension is in the list.
-
-    return 0;  // just not supported, fail.
+    return stringcache_iscached(exts, ext);
 } // verify_extension
 
 
@@ -1053,12 +1048,28 @@
 } // detect_glsl_version
 
 
+static int iswhitespace(const char ch)
+{
+    switch (ch)
+    {
+        case ' ': case '\t': case '\r': case '\n': return 1;
+        default: return 0;
+    } // switch
+} // iswhitespace
+
+
 static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d)
 {
-    const char *extlist = NULL;
+    StringCache *exts = stringcache_create(ctx->malloc_fn, ctx->free_fn, ctx->malloc_data);
+    if (!exts)
+    {
+        out_of_memory();
+        return;
+    } // if
 
     ctx->have_core_opengl = 1;
     ctx->have_opengl_2 = 1;
+    ctx->have_opengl_3 = 1;
     ctx->have_GL_ARB_vertex_program = 1;
     ctx->have_GL_ARB_fragment_program = 1;
     ctx->have_GL_NV_vertex_program2_option = 1;
@@ -1081,12 +1092,53 @@
     {
         const char *str = (const char *) ctx->glGetString(GL_VERSION);
         parse_opengl_version_str(str, &ctx->opengl_major, &ctx->opengl_minor);
-        extlist = (const char *) ctx->glGetString(GL_EXTENSIONS);
+
+        if ((ctx->have_opengl_3) && (opengl_version_atleast(3, 0)))
+        {
+            GLint i;
+            GLint num_exts = 0;
+            ctx->glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts);
+            for (i = 0; i < num_exts; i++)
+            {
+                if (!stringcache(exts, (const char *) ctx->glGetStringi(GL_EXTENSIONS, i)))
+                    out_of_memory();
+            } // for
+        } // if
+        else
+        {
+            const char *str = (const char *) ctx->glGetString(GL_EXTENSIONS);
+            const char *ext;
+            ctx->have_opengl_3 = 0;
+
+            while (*str && iswhitespace(*str))
+                str++;
+            ext = str;
+
+            while (1)
+            {
+                const char ch = *str;
+                if (ch == '\0')
+                    break;
+                else if (!iswhitespace(ch))
+                {
+                    str++;
+                    continue;
+                } // else if
+
+                if (!stringcache_len(exts, ext, (unsigned int) (str - ext)))
+                {
+                    out_of_memory();
+                    break;
+                } // if
+
+                str++;
+                while (*str && iswhitespace(*str))
+                    str++;
+                ext = str;
+            } // while
+        } // else
     } // else
 
-    if (extlist == NULL)
-        extlist = "";  // just in case.
-
     if ((ctx->have_opengl_2) && (!opengl_version_atleast(2, 0)))
     {
         ctx->have_opengl_2 = 0;  // Not GL2! must have the ARB extensions!
@@ -1105,7 +1157,7 @@
     } // if
 
     #define VERIFY_EXT(ext, major, minor) \
-        ctx->have_##ext = verify_extension(#ext, ctx->have_##ext, extlist, major, minor)
+        ctx->have_##ext = verify_extension(#ext, ctx->have_##ext, exts, major, minor)
 
     VERIFY_EXT(GL_ARB_vertex_program, -1, -1);
     VERIFY_EXT(GL_ARB_fragment_program, -1, -1);
@@ -1122,6 +1174,8 @@
 
     #undef VERIFY_EXT
 
+    stringcache_destroy(exts);
+
     detect_glsl_version();
 } // load_extensions
 
@@ -1220,16 +1274,26 @@
 #endif
 };
 
-int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d,
-                                   const char **profs, const int size)
+int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup,
+                                   void *lookup_d,
+                                   const char **profs, const int size,
+                                   MOJOSHADER_malloc m, MOJOSHADER_free f,
+                                   void *malloc_d)
 {
     int retval = 0;
     MOJOSHADER_glContext _ctx;
     MOJOSHADER_glContext *current_ctx = ctx;
 
+    if (m == NULL) m = MOJOSHADER_internal_malloc;
+    if (f == NULL) f = MOJOSHADER_internal_free;
+
     ctx = &_ctx;
     memset(ctx, '\0', sizeof (MOJOSHADER_glContext));
-    load_extensions(lookup, d);
+    ctx->malloc_fn = m;
+    ctx->free_fn = f;
+    ctx->malloc_data = malloc_d;
+
+    load_extensions(lookup, lookup_d);
 
     if (ctx->have_core_opengl)
     {
@@ -1251,10 +1315,16 @@
 } // MOJOSHADER_glAvailableProfiles
 
 
-const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress gpa, void *d)
+const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress gpa,
+                                     void *lookup_d,
+                                     MOJOSHADER_malloc m, MOJOSHADER_free f,
+                                     void *malloc_d)
 {
     const char *prof[STATICARRAYLEN(profile_priorities)];
-    if (MOJOSHADER_glAvailableProfiles(gpa, d, prof, STATICARRAYLEN(prof)) <= 0)
+    const int avail = MOJOSHADER_glAvailableProfiles(gpa, lookup_d, prof,
+                                                     STATICARRAYLEN(prof),
+                                                     m, f, malloc_d);
+    if (avail <= 0)
     {
         set_error("no profiles available");
         return NULL;
--- a/utils/availableprofiles.c	Wed Oct 10 21:33:36 2012 -0400
+++ b/utils/availableprofiles.c	Wed Oct 10 21:35:16 2012 -0400
@@ -20,11 +20,11 @@
 static int check_available(void)
 {
     const char **avail = NULL;
-    int total = MOJOSHADER_glAvailableProfiles(lookup, NULL, NULL, 0);
+    int total = MOJOSHADER_glAvailableProfiles(lookup, NULL, NULL, 0, NULL, NULL, NULL);
     if (total > 0)
     {
         avail = (const char **) alloca(sizeof (const char *) * total);
-        total = MOJOSHADER_glAvailableProfiles(lookup, NULL, avail, total);
+        total = MOJOSHADER_glAvailableProfiles(lookup, NULL, avail, total, NULL, NULL, NULL);
     } // if
 
     if (total <= 0)
--- a/utils/bestprofile.c	Wed Oct 10 21:33:36 2012 -0400
+++ b/utils/bestprofile.c	Wed Oct 10 21:35:16 2012 -0400
@@ -37,7 +37,7 @@
             fprintf(stderr, "SDL_SetVideoMode() error: %s\n", SDL_GetError());
         else
         {
-            const char *best = MOJOSHADER_glBestProfile(lookup, NULL);
+            const char *best = MOJOSHADER_glBestProfile(lookup, NULL, NULL, NULL, NULL);
             MOJOSHADER_glContext *ctx;
             ctx = MOJOSHADER_glCreateContext(best, lookup, 0, 0, 0, 0);
             if (ctx == NULL)
--- a/utils/finderrors.c	Wed Oct 10 21:33:36 2012 -0400
+++ b/utils/finderrors.c	Wed Oct 10 21:35:16 2012 -0400
@@ -204,7 +204,7 @@
         SDL_Init(SDL_INIT_VIDEO);
         SDL_GL_LoadLibrary(NULL);
         SDL_SetVideoMode(640, 480, 0, SDL_OPENGL);
-        printf("Best profile is '%s'\n", MOJOSHADER_glBestProfile(lookup, 0));
+        printf("Best profile is '%s'\n", MOJOSHADER_glBestProfile(lookup, 0, NULL, NULL, NULL));
         MOJOSHADER_glContext *ctx;
         ctx = MOJOSHADER_glCreateContext(profile, lookup, 0, 0, 0, 0);
         if (ctx == NULL)