From dee34189924fd6fb126c0f52e658395ade0942c3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 10 Oct 2012 21:35:16 -0400 Subject: [PATCH] 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 | 41 ++++++++++---- mojoshader_opengl.c | 112 +++++++++++++++++++++++++++++++------- utils/availableprofiles.c | 4 +- utils/bestprofile.c | 2 +- utils/finderrors.c | 2 +- 5 files changed, 126 insertions(+), 35 deletions(-) diff --git a/mojoshader.h b/mojoshader.h index 87407d4c..6d524fb6 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -2520,9 +2520,17 @@ typedef struct MOJOSHADER_glProgram MOJOSHADER_glProgram; * * 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 @@ typedef struct MOJOSHADER_glProgram MOJOSHADER_glProgram; * 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 @@ int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d, * * 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 @@ int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d, * 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. diff --git a/mojoshader_opengl.c b/mojoshader_opengl.c index ac2ad5ae..b9d3d570 100644 --- a/mojoshader_opengl.c +++ b/mojoshader_opengl.c @@ -177,6 +177,7 @@ struct MOJOSHADER_glContext // 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 @@ struct MOJOSHADER_glContext // Entry points... PFNGLGETSTRINGPROC glGetString; + PFNGLGETSTRINGIPROC glGetStringi; PFNGLGETERRORPROC glGetError; PFNGLGETINTEGERVPROC glGetIntegerv; PFNGLENABLEPROC glEnable; @@ -919,6 +921,7 @@ static void lookup_entry_points(MOJOSHADER_glGetProcAddress lookup, void *d) 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 @@ static inline int opengl_version_atleast(const int major, const int minor) ((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 @@ static int verify_extension(const char *ext, int have, const char *extlist, 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 @@ static void detect_glsl_version(void) } // 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,11 +1092,52 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) { 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); - } // else - if (extlist == NULL) - extlist = ""; // just in case. + 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 ((ctx->have_opengl_2) && (!opengl_version_atleast(2, 0))) { @@ -1105,7 +1157,7 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) } // 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 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) #undef VERIFY_EXT + stringcache_destroy(exts); + detect_glsl_version(); } // load_extensions @@ -1220,16 +1274,26 @@ static const char *profile_priorities[] = { #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 @@ int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d, } // 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; diff --git a/utils/availableprofiles.c b/utils/availableprofiles.c index 55b63eb9..6e280606 100644 --- a/utils/availableprofiles.c +++ b/utils/availableprofiles.c @@ -20,11 +20,11 @@ static void *lookup(const char *fnname, void *unused) 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) diff --git a/utils/bestprofile.c b/utils/bestprofile.c index 7339c4a9..4a17a2db 100644 --- a/utils/bestprofile.c +++ b/utils/bestprofile.c @@ -37,7 +37,7 @@ int main(int argc, char **argv) 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) diff --git a/utils/finderrors.c b/utils/finderrors.c index d90f34e4..761ce3e6 100644 --- a/utils/finderrors.c +++ b/utils/finderrors.c @@ -204,7 +204,7 @@ int main(int argc, char **argv) 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)