Skip to content

Commit

Permalink
Allow looking up OpenGL extensions in the way appropriate for GL3+.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
icculus committed Oct 11, 2012
1 parent de4220b commit dee3418
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 35 deletions.
41 changes: 31 additions & 10 deletions mojoshader.h
Expand Up @@ -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.
Expand All @@ -2531,24 +2539,35 @@ 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);


/*
* Determine the best profile to use for the current system.
*
* 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.
Expand All @@ -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.
Expand Down
112 changes: 91 additions & 21 deletions mojoshader_opengl.c
Expand Up @@ -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;
Expand All @@ -193,6 +194,7 @@ struct MOJOSHADER_glContext

// Entry points...
PFNGLGETSTRINGPROC glGetString;
PFNGLGETSTRINGIPROC glGetStringi;
PFNGLGETERRORPROC glGetError;
PFNGLGETINTEGERVPROC glGetIntegerv;
PFNGLENABLEPROC glEnable;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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


Expand Down Expand Up @@ -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;
Expand All @@ -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)))
{
Expand All @@ -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);
Expand All @@ -1122,6 +1174,8 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d)

#undef VERIFY_EXT

stringcache_destroy(exts);

detect_glsl_version();
} // load_extensions

Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions utils/availableprofiles.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion utils/bestprofile.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion utils/finderrors.c
Expand Up @@ -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)
Expand Down

0 comments on commit dee3418

Please sign in to comment.