/** * MojoShader; generate shader programs from bytecode of compiled * Direct3D shaders. * * Please see the file LICENSE.txt in the source's root directory. * * This file written by Ryan C. Gordon. */ #include #include #include #include #include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN 1 #include // GL headers need this for WINGDIAPI definition. #include // for alloca(). #endif #if (defined(__APPLE__) && defined(__MACH__)) #define PLATFORM_MACOSX 1 #endif #if PLATFORM_MACOSX #include #endif #define __MOJOSHADER_INTERNAL__ 1 #include "mojoshader_internal.h" #define GL_GLEXT_LEGACY 1 #include "GL/gl.h" #include "GL/glext.h" #ifndef GL_HALF_FLOAT #define GL_HALF_FLOAT 0x140B #endif #ifndef GL_HALF_FLOAT_OES #define GL_HALF_FLOAT_OES 0x8D61 #endif struct MOJOSHADER_glShader { const MOJOSHADER_parseData *parseData; GLuint handle; uint32 refcount; }; typedef struct { MOJOSHADER_shaderType shader_type; const MOJOSHADER_uniform *uniform; GLuint location; union { GLint b; GLint i[4]; GLfloat f[4]; } value; GLfloat *uniform_array_buffer; } UniformMap; typedef struct { MOJOSHADER_shaderType shader_type; const MOJOSHADER_sampler *sampler; GLuint location; GLint value; } SamplerMap; typedef struct { const MOJOSHADER_attribute *attribute; GLuint location; } AttributeMap; struct MOJOSHADER_glProgram { MOJOSHADER_glShader *vertex; MOJOSHADER_glShader *fragment; GLuint handle; uint32 uniform_count; UniformMap *uniforms; uint32 sampler_count; SamplerMap *samplers; uint32 attribute_count; AttributeMap *attributes; uint32 refcount; }; #ifndef WINGDIAPI #define WINGDIAPI #endif // Entry points in base OpenGL that lack function pointer prototypes... typedef WINGDIAPI void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *params); typedef WINGDIAPI const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); typedef WINGDIAPI GLenum (APIENTRYP PFNGLGETERRORPROC) (void); typedef WINGDIAPI void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); typedef WINGDIAPI void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); struct MOJOSHADER_glContext { // Allocators... MOJOSHADER_malloc malloc_fn; MOJOSHADER_free free_fn; void *malloc_data; // The constant register files... // Man, it kills me how much memory this takes... GLfloat vs_reg_file_f[8192 * 4]; GLint vs_reg_file_i[2047 * 4]; GLint vs_reg_file_b[2047]; GLfloat ps_reg_file_f[8192 * 4]; GLint ps_reg_file_i[2047 * 4]; GLint ps_reg_file_b[2047]; GLuint sampler_reg_file[16]; // GL stuff... int opengl_major; int opengl_minor; MOJOSHADER_glProgram *bound_program; char profile[16]; // Extensions... int have_base_opengl; int have_GL_ARB_vertex_program; int have_GL_ARB_fragment_program; int have_GL_NV_vertex_program2_option; int have_GL_NV_fragment_program2; int have_GL_NV_vertex_program3; int have_GL_NV_gpu_program4; int have_GL_ARB_shader_objects; int have_GL_ARB_vertex_shader; int have_GL_ARB_fragment_shader; int have_GL_ARB_shading_language_100; int have_GL_NV_half_float; int have_GL_ARB_half_float_vertex; int have_GL_OES_vertex_half_float; // Entry points... PFNGLGETSTRINGPROC glGetString; PFNGLGETERRORPROC glGetError; PFNGLGETINTEGERVPROC glGetIntegerv; PFNGLENABLEPROC glEnable; PFNGLDISABLEPROC glDisable; PFNGLDELETEOBJECTARBPROC glDeleteObject; PFNGLATTACHOBJECTARBPROC glAttachObject; PFNGLCOMPILESHADERARBPROC glCompileShader; PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObject; PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObject; PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArray; PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArray; PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocation; PFNGLGETINFOLOGARBPROC glGetInfoLog; PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameteriv; PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation; PFNGLLINKPROGRAMARBPROC glLinkProgram; PFNGLSHADERSOURCEARBPROC glShaderSource; PFNGLUNIFORM1IARBPROC glUniform1i; PFNGLUNIFORM4FVARBPROC glUniform4fv; PFNGLUNIFORM4IVARBPROC glUniform4iv; PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObject; PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointer; PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glProgramLocalParameterI4ivNV; PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; PFNGLGENPROGRAMSARBPROC glGenProgramsARB; PFNGLBINDPROGRAMARBPROC glBindProgramARB; PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; // interface for profile-specific things. int (*profileMaxUniforms)(MOJOSHADER_shaderType shader_type); int (*profileCompileShader)(const MOJOSHADER_parseData *pd, GLuint *s); void (*profileDeleteShader)(const GLuint shader); void (*profileDeleteProgram)(const GLuint program); GLint (*profileGetAttribLocation)(MOJOSHADER_glProgram *program, int idx); GLint (*profileGetUniformLocation)(MOJOSHADER_glProgram *, MOJOSHADER_glShader *, int); GLint (*profileGetSamplerLocation)(MOJOSHADER_glProgram *, MOJOSHADER_glShader *, int); GLuint (*profileLinkProgram)(MOJOSHADER_glShader *, MOJOSHADER_glShader *); void (*profileUseProgramObject)(MOJOSHADER_glProgram *program); void (*profileUniform4fv)(const MOJOSHADER_parseData *, GLint, GLsizei, GLfloat *); void (*profileUniform4iv)(const MOJOSHADER_parseData *, GLint, GLsizei, GLint *); void (*profileUniform1i)(const MOJOSHADER_parseData *, GLint, GLint); void (*profileSetSampler)(GLint loc, GLuint sampler); int (*profileMustLoadConstantArrays)(void); }; static MOJOSHADER_glContext *ctx = NULL; // Error state... static char error_buffer[1024] = { '\0' }; static void set_error(const char *str) { snprintf(error_buffer, sizeof (error_buffer), "%s", str); } // set_error #if PLATFORM_MACOSX static inline int macosx_version_atleast(int x, int y, int z) { static int checked = 0; static int combined = 0; if (!checked) { long ver, major, minor, patch; int convert = 0; if (Gestalt(gestaltSystemVersion, &ver) != noErr) ver = 0x1000; // oh well. else if (ver < 0x1030) convert = 1; // split (ver) into (major),(minor),(patch). else { // presumably this won't fail. But if it does, we'll just use the // original version value. This might cut the value--10.12.11 will // come out to 10.9.9, for example--but it's better than nothing. if (Gestalt(gestaltSystemVersionMajor, &major) != noErr) convert = 1; else if (Gestalt(gestaltSystemVersionMinor, &minor) != noErr) convert = 1; else if (Gestalt(gestaltSystemVersionBugFix, &patch) != noErr) convert = 1; } // else if (convert) { major = ((ver & 0xFF00) >> 8); major = (((major / 16) * 10) + (major % 16)); minor = ((ver & 0xF0) >> 4); patch = (ver & 0xF); } // if combined = (major << 16) | (minor << 8) | patch; checked = 1; } // if return (combined >= ((x << 16) | (y << 8) | z)); } // macosx_version_atleast #endif static inline void *Malloc(const size_t len) { void *retval = ctx->malloc_fn((int) len, ctx->malloc_data); if (retval == NULL) set_error("out of memory"); return retval; } // Malloc static inline void Free(void *ptr) { if (ptr != NULL) ctx->free_fn(ptr, ctx->malloc_data); } // Free static inline void toggle_gl_state(GLenum state, int val) { if (val) ctx->glEnable(state); else ctx->glDisable(state); } // toggle_gl_state // profile-specific implementations... #if SUPPORT_PROFILE_GLSL static inline GLenum glsl_shader_type(const MOJOSHADER_shaderType t) { if (t == MOJOSHADER_TYPE_VERTEX) return GL_VERTEX_SHADER; else if (t == MOJOSHADER_TYPE_PIXEL) return GL_FRAGMENT_SHADER; // !!! FIXME: geometry shaders? return GL_NONE; } // glsl_shader_type static int impl_GLSL_MustLoadConstantArrays(void) { return 1; } static int impl_GLSL_MaxUniforms(MOJOSHADER_shaderType shader_type) { GLenum pname = GL_NONE; GLint val = 0; if (shader_type == MOJOSHADER_TYPE_VERTEX) pname = GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB; else if (shader_type == MOJOSHADER_TYPE_PIXEL) pname = GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB; else return -1; ctx->glGetIntegerv(pname, &val); return (int) val; } // impl_GLSL_MaxUniforms static int impl_GLSL_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s) { GLint ok = 0; GLint shaderlen = (GLint) pd->output_len; const GLenum shader_type = glsl_shader_type(pd->shader_type); GLuint shader = ctx->glCreateShaderObject(shader_type); ctx->glShaderSource(shader, 1, (const GLchar **) &pd->output, &shaderlen); ctx->glCompileShader(shader); ctx->glGetObjectParameteriv(shader, GL_OBJECT_COMPILE_STATUS_ARB, &ok); if (!ok) { GLsizei len = 0; ctx->glGetInfoLog(shader, sizeof (error_buffer), &len, (GLchar *) error_buffer); *s = 0; return 0; } // if *s = shader; return 1; } // impl_GLSL_CompileShader static void impl_GLSL_DeleteShader(const GLuint shader) { ctx->glDeleteObject(shader); } // impl_GLSL_DeleteShader static void impl_GLSL_DeleteProgram(const GLuint program) { ctx->glDeleteObject(program); } // impl_GLSL_DeleteProgram static GLint impl_GLSL_GetUniformLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) { return ctx->glGetUniformLocation(program->handle, shader->parseData->uniforms[idx].name); } // impl_GLSL_GetUniformLocation static GLint impl_GLSL_GetSamplerLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) { return ctx->glGetUniformLocation(program->handle, shader->parseData->samplers[idx].name); } // impl_GLSL_GetSamplerLocation static GLint impl_GLSL_GetAttribLocation(MOJOSHADER_glProgram *program, int idx) { const MOJOSHADER_parseData *pd = program->vertex->parseData; const MOJOSHADER_attribute *a = pd->attributes; return ctx->glGetAttribLocation(program->handle, a[idx].name); } // impl_GLSL_GetAttribLocation static GLuint impl_GLSL_LinkProgram(MOJOSHADER_glShader *vshader, MOJOSHADER_glShader *pshader) { const GLuint program = ctx->glCreateProgramObject(); if (vshader != NULL) ctx->glAttachObject(program, vshader->handle); if (pshader != NULL) ctx->glAttachObject(program, pshader->handle); ctx->glLinkProgram(program); GLint ok = 0; ctx->glGetObjectParameteriv(program, GL_OBJECT_LINK_STATUS_ARB, &ok); if (!ok) { GLsizei len = 0; ctx->glGetInfoLog(program, sizeof (error_buffer), &len, (GLchar *) error_buffer); ctx->glDeleteObject(program); return 0; } // if return program; } // impl_GLSL_LinkProgram static void impl_GLSL_UseProgramObject(MOJOSHADER_glProgram *program) { ctx->glUseProgramObject((program != NULL) ? program->handle : 0); } // impl_GLSL_UseProgramObject static void impl_GLSL_Uniform4fv(const MOJOSHADER_parseData *pd, GLint loc, GLsizei siz, GLfloat *v) { ctx->glUniform4fv(loc, siz, v); } // impl_GLSL_Uniform4fv static void impl_GLSL_Uniform4iv(const MOJOSHADER_parseData *pd, GLint loc, GLsizei siz, GLint *v) { ctx->glUniform4iv(loc, siz, v); } // impl_GLSL_Uniform4iv static void impl_GLSL_Uniform1i(const MOJOSHADER_parseData *pd, GLint loc, GLint v) { ctx->glUniform1i(loc, v); } // impl_GLSL_Uniform1i static void impl_GLSL_SetSampler(GLint loc, GLuint sampler) { ctx->glUniform1i(loc, sampler); } // impl_GLSL_SetSampler #endif // SUPPORT_PROFILE_GLSL #if SUPPORT_PROFILE_ARB1 static inline GLenum arb1_shader_type(const MOJOSHADER_shaderType t) { if (t == MOJOSHADER_TYPE_VERTEX) return GL_VERTEX_PROGRAM_ARB; else if (t == MOJOSHADER_TYPE_PIXEL) return GL_FRAGMENT_PROGRAM_ARB; // !!! FIXME: geometry shaders? return GL_NONE; } // arb1_shader_type static int impl_ARB1_MustLoadConstantArrays(void) { return 0; } static int impl_ARB1_MaxUniforms(MOJOSHADER_shaderType shader_type) { GLint retval = 0; const GLenum program_type = arb1_shader_type(shader_type); if (program_type == GL_NONE) return -1; ctx->glGetProgramivARB(program_type, GL_MAX_PROGRAM_PARAMETERS_ARB, &retval); return (int) retval; // !!! FIXME: times four? } // impl_ARB1_MaxUniforms static int impl_ARB1_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s) { GLint shaderlen = (GLint) pd->output_len; const GLenum shader_type = arb1_shader_type(pd->shader_type); GLuint shader = 0; ctx->glGenProgramsARB(1, &shader); ctx->glGetError(); // flush any existing error state. ctx->glBindProgramARB(shader_type, shader); ctx->glProgramStringARB(shader_type, GL_PROGRAM_FORMAT_ASCII_ARB, shaderlen, pd->output); if (ctx->glGetError() == GL_INVALID_OPERATION) { GLint pos = 0; ctx->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); const GLubyte *errstr = ctx->glGetString(GL_PROGRAM_ERROR_STRING_ARB); snprintf(error_buffer, sizeof (error_buffer), "ARB1 compile error at position %d: %s", (int) pos, (const char *) errstr); ctx->glBindProgramARB(shader_type, 0); ctx->glDeleteProgramsARB(1, &shader); *s = 0; return 0; } // if *s = shader; return 1; } // impl_ARB1_CompileShader static void impl_ARB1_DeleteShader(const GLuint _shader) { GLuint shader = _shader; // const removal. ctx->glDeleteProgramsARB(1, &shader); } // impl_ARB1_DeleteShader static void impl_ARB1_DeleteProgram(const GLuint program) { // no-op. ARB1 doesn't have real linked programs. } // impl_GLSL_DeleteProgram static GLint impl_ARB1_GetUniformLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) { assert(shader->parseData->uniforms[idx].type == MOJOSHADER_UNIFORM_FLOAT); return shader->parseData->uniforms[idx].index; // !!! FIXME: doesn't work if there are int or bool uniforms! } // impl_ARB1_GetUniformLocation static GLint impl_ARB1_GetSamplerLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) { return shader->parseData->samplers[idx].index; } // impl_ARB1_GetSamplerLocation static GLint impl_ARB1_GetAttribLocation(MOJOSHADER_glProgram *program, int idx) { return idx; // map to vertex arrays in the same order as the parseData. } // impl_ARB1_GetAttribLocation static GLuint impl_ARB1_LinkProgram(MOJOSHADER_glShader *vshader, MOJOSHADER_glShader *pshader) { // there is no formal linking in ARB1...just return a unique value. static GLuint retval = 1; return retval++; } // impl_ARB1_LinkProgram static void impl_ARB1_UseProgramObject(MOJOSHADER_glProgram *program) { GLuint vhandle = 0; GLuint phandle = 0; if (program != NULL) { if (program->vertex != NULL) vhandle = program->vertex->handle; if (program->fragment != NULL) phandle = program->fragment->handle; } // if toggle_gl_state(GL_VERTEX_PROGRAM_ARB, vhandle != 0); toggle_gl_state(GL_FRAGMENT_PROGRAM_ARB, phandle != 0); ctx->glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vhandle); ctx->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, phandle); } // impl_ARB1_UseProgramObject static void impl_ARB1_Uniform4fv(const MOJOSHADER_parseData *pd, GLint loc, GLsizei siz, GLfloat *v) { int i; const GLenum shader_type = arb1_shader_type(pd->shader_type); for (i = 0; i < siz; i++, v += 4) ctx->glProgramLocalParameter4fvARB(shader_type, loc + i, v); } // impl_ARB1_Uniform4fv static void impl_ARB1_Uniform4iv(const MOJOSHADER_parseData *pd, GLint loc, GLsizei siz, GLint *v) { int i; const GLenum shader_type = arb1_shader_type(pd->shader_type); for (i = 0; i < siz; i++, v += 4) { GLfloat f[4] = { (GLfloat) v[0], (GLfloat) v[1], (GLfloat) v[2], (GLfloat) v[3] }; ctx->glProgramLocalParameter4fvARB(shader_type, loc + i, f); } // for } // impl_ARB1_Uniform4iv static void impl_ARB1_Uniform1i(const MOJOSHADER_parseData *pd, GLint loc, GLint _v) { const GLenum shader_type = arb1_shader_type(pd->shader_type); const GLfloat v = (GLfloat) _v; GLfloat f[4] = { v, v, v, v }; ctx->glProgramLocalParameter4fvARB(shader_type, loc, f); } // impl_ARB1_Uniform1i static void impl_NV4_Uniform4iv(const MOJOSHADER_parseData *pd, GLint loc, GLsizei siz, GLint *v) { int i; const GLenum shader_type = arb1_shader_type(pd->shader_type); for (i = 0; i < siz; i++, v += 4) ctx->glProgramLocalParameterI4ivNV(shader_type, loc + i, v); } // impl_NV4_Uniform4iv static void impl_NV4_Uniform1i(const MOJOSHADER_parseData *pd, GLint loc, GLint _v) { const GLenum shader_type = arb1_shader_type(pd->shader_type); GLint v[4] = { _v, _v, _v, _v }; ctx->glProgramLocalParameterI4ivNV(shader_type, loc, v); } // impl_NV4_Uniform1i static void impl_ARB1_SetSampler(GLint loc, GLuint sampler) { // no-op in this profile...arb1 uses the texture units as-is. assert(loc == (GLint) sampler); } // impl_ARB1_SetSampler #endif // SUPPORT_PROFILE_ARB1 const char *MOJOSHADER_glGetError(void) { return error_buffer; } // MOJOSHADER_glGetError static void *loadsym(void *(*lookup)(const char *fn), const char *fn, int *ext) { void *retval = NULL; if (lookup != NULL) { retval = lookup(fn); if (retval == NULL) { char arbfn[64]; snprintf(arbfn, sizeof (arbfn), "%sARB", fn); retval = lookup(arbfn); } // if } // if if (retval == NULL) *ext = 0; return retval; } // loadsym static void lookup_entry_points(void *(*lookup)(const char *fnname)) { #define DO_LOOKUP(ext, typ, fn) { \ int exist = ctx->have_##ext; \ ctx->fn = (typ) loadsym(lookup, #fn, &exist); \ ctx->have_##ext = exist; \ } DO_LOOKUP(base_opengl, PFNGLGETSTRINGPROC, glGetString); DO_LOOKUP(base_opengl, PFNGLGETERRORPROC, glGetError); DO_LOOKUP(base_opengl, PFNGLGETINTEGERVPROC, glGetIntegerv); DO_LOOKUP(base_opengl, PFNGLENABLEPROC, glEnable); DO_LOOKUP(base_opengl, PFNGLDISABLEPROC, glDisable); DO_LOOKUP(GL_ARB_shader_objects, PFNGLDELETEOBJECTARBPROC, glDeleteObject); DO_LOOKUP(GL_ARB_shader_objects, PFNGLATTACHOBJECTARBPROC, glAttachObject); DO_LOOKUP(GL_ARB_shader_objects, PFNGLCOMPILESHADERARBPROC, glCompileShader); DO_LOOKUP(GL_ARB_shader_objects, PFNGLCREATEPROGRAMOBJECTARBPROC, glCreateProgramObject); DO_LOOKUP(GL_ARB_shader_objects, PFNGLCREATESHADEROBJECTARBPROC, glCreateShaderObject); DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETINFOLOGARBPROC, glGetInfoLog); DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETOBJECTPARAMETERIVARBPROC, glGetObjectParameteriv); DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETUNIFORMLOCATIONARBPROC, glGetUniformLocation); DO_LOOKUP(GL_ARB_shader_objects, PFNGLLINKPROGRAMARBPROC, glLinkProgram); DO_LOOKUP(GL_ARB_shader_objects, PFNGLSHADERSOURCEARBPROC, glShaderSource); DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM1IARBPROC, glUniform1i); DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM4FVARBPROC, glUniform4fv); DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM4IVARBPROC, glUniform4iv); DO_LOOKUP(GL_ARB_shader_objects, PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgramObject); DO_LOOKUP(GL_ARB_vertex_shader, PFNGLDISABLEVERTEXATTRIBARRAYARBPROC, glDisableVertexAttribArray); DO_LOOKUP(GL_ARB_vertex_shader, PFNGLENABLEVERTEXATTRIBARRAYARBPROC, glEnableVertexAttribArray); DO_LOOKUP(GL_ARB_vertex_shader, PFNGLGETATTRIBLOCATIONARBPROC, glGetAttribLocation); DO_LOOKUP(GL_ARB_vertex_shader, PFNGLVERTEXATTRIBPOINTERARBPROC, glVertexAttribPointer); DO_LOOKUP(GL_ARB_vertex_program, PFNGLVERTEXATTRIBPOINTERARBPROC, glVertexAttribPointer); DO_LOOKUP(GL_ARB_vertex_program, PFNGLGETPROGRAMIVARBPROC, glGetProgramivARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLGETPROGRAMSTRINGARBPROC, glGetProgramStringARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLPROGRAMLOCALPARAMETER4FVARBPROC, glProgramLocalParameter4fvARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLDELETEPROGRAMSARBPROC, glDeleteProgramsARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLGENPROGRAMSARBPROC, glGenProgramsARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLBINDPROGRAMARBPROC, glBindProgramARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLPROGRAMSTRINGARBPROC, glProgramStringARB); DO_LOOKUP(GL_NV_gpu_program4, PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC, glProgramLocalParameterI4ivNV); #undef DO_LOOKUP } // lookup_entry_points static int verify_extension(const char *ext, int have, const char *extlist, int major, int minor) { if (have == 0) return 0; // don't bother checking, we're missing an entry point. else if (!ctx->have_base_opengl) return 0; // don't bother checking, we're missing basic functionality. // See if it's in the spec for this GL implementation's version. if (major >= 0) { if ( ((ctx->opengl_major << 16) | (ctx->opengl_minor & 0xFFFF)) >= ((major << 16) | (minor & 0xFFFF)) ) return 1; } // if // 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. } // verify_extension static void parse_opengl_version_str(const char *verstr, int *maj, int *min) { if (verstr == NULL) *maj = *min = 0; else sscanf(verstr, "%d.%d", maj, min); } // parse_opengl_version_str static inline void parse_opengl_version(const char *verstr) { parse_opengl_version_str(verstr, &ctx->opengl_major, &ctx->opengl_minor); } // parse_opengl_version static int glsl_version_atleast(int maj, int min) { int glslmin = 0; int glslmaj = 0; ctx->glGetError(); // flush any existing error state. const GLenum enumval = GL_SHADING_LANGUAGE_VERSION_ARB; const char *str = (const char *) ctx->glGetString(enumval); if (ctx->glGetError() == GL_INVALID_ENUM) return 0; // this is a basic, 1.0-compliant implementation. parse_opengl_version_str(str, &glslmaj, &glslmin); return ( (glslmaj > maj) || ((glslmaj == maj) && (glslmin >= min)) ); } // glsl_version_atleast static void load_extensions(void *(*lookup)(const char *fnname)) { const char *extlist = NULL; ctx->have_base_opengl = 1; ctx->have_GL_ARB_vertex_program = 1; ctx->have_GL_ARB_fragment_program = 1; ctx->have_GL_NV_vertex_program2_option = 1; ctx->have_GL_NV_fragment_program2 = 1; ctx->have_GL_NV_vertex_program3 = 1; ctx->have_GL_NV_gpu_program4 = 1; ctx->have_GL_ARB_shader_objects = 1; ctx->have_GL_ARB_vertex_shader = 1; ctx->have_GL_ARB_fragment_shader = 1; ctx->have_GL_ARB_shading_language_100 = 1; ctx->have_GL_NV_half_float = 1; ctx->have_GL_ARB_half_float_vertex = 1; ctx->have_GL_OES_vertex_half_float = 1; lookup_entry_points(lookup); if (!ctx->have_base_opengl) set_error("missing basic OpenGL entry points"); else { parse_opengl_version((const char *) ctx->glGetString(GL_VERSION)); extlist = (const char *) ctx->glGetString(GL_EXTENSIONS); } // else if (extlist == NULL) extlist = ""; // just in case. #define VERIFY_EXT(ext, major, minor) \ ctx->have_##ext = verify_extension(#ext, ctx->have_##ext, extlist, major, minor) VERIFY_EXT(GL_ARB_vertex_program, -1, -1); VERIFY_EXT(GL_ARB_fragment_program, -1, -1); VERIFY_EXT(GL_ARB_shader_objects, 2, 0); VERIFY_EXT(GL_ARB_vertex_shader, 2, 0); VERIFY_EXT(GL_ARB_fragment_shader, 2, 0); VERIFY_EXT(GL_ARB_shading_language_100, 2, 0); VERIFY_EXT(GL_NV_vertex_program2_option, -1, -1); VERIFY_EXT(GL_NV_fragment_program2, -1, -1); VERIFY_EXT(GL_NV_vertex_program3, -1, -1); VERIFY_EXT(GL_NV_half_float, -1, -1); VERIFY_EXT(GL_ARB_half_float_vertex, 3, 0); VERIFY_EXT(GL_OES_vertex_half_float, -1, -1); #undef VERIFY_EXT } // load_extensions static int valid_profile(const char *profile) { // If running on Mac OS X <= 10.4, don't ever pick GLSL, even if // the system claims it is available. #if PLATFORM_MACOSX const int allow_glsl = macosx_version_atleast(10, 5, 0); #else const int allow_glsl = 1; #endif if (!ctx->have_base_opengl) return 0; #define MUST_HAVE(p, x) \ if (!ctx->have_##x) { set_error(#p " profile needs " #x); return 0; } if (profile == NULL) { set_error("NULL profile"); return 0; } // if #if SUPPORT_PROFILE_ARB1 else if (strcmp(profile, MOJOSHADER_PROFILE_ARB1) == 0) { MUST_HAVE(MOJOSHADER_PROFILE_ARB1, GL_ARB_vertex_program); MUST_HAVE(MOJOSHADER_PROFILE_ARB1, GL_ARB_fragment_program); } // else if else if (strcmp(profile, MOJOSHADER_PROFILE_NV2) == 0) { MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_ARB_vertex_program); MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_ARB_fragment_program); MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_NV_vertex_program2_option); MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_NV_fragment_program2); } // else if else if (strcmp(profile, MOJOSHADER_PROFILE_NV3) == 0) { MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_ARB_vertex_program); MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_ARB_fragment_program); MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_NV_vertex_program3); MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_NV_fragment_program2); } // else if else if (strcmp(profile, MOJOSHADER_PROFILE_NV4) == 0) { MUST_HAVE(MOJOSHADER_PROFILE_NV4, GL_NV_gpu_program4); } // else if #endif #if SUPPORT_PROFILE_GLSL else if ((allow_glsl) && (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0)) { MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_shader_objects); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_vertex_shader); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_fragment_shader); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_shading_language_100); // if you got here, you have all the extensions. if (!glsl_version_atleast(1, 20)) return 0; } // else if else if ((allow_glsl) && (strcmp(profile, MOJOSHADER_PROFILE_GLSL) == 0)) { MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_shader_objects); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_vertex_shader); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_fragment_shader); MUST_HAVE(MOJOSHADER_PROFILE_GLSL, GL_ARB_shading_language_100); } // else if #endif else { set_error("unknown or unsupported profile"); return 0; } // else #undef MUST_HAVE return 1; } // valid_profile static const char *profile_priorities[] = { #if SUPPORT_PROFILE_GLSL MOJOSHADER_PROFILE_GLSL120, MOJOSHADER_PROFILE_GLSL, #endif #if SUPPORT_PROFILE_ARB1 MOJOSHADER_PROFILE_NV4, MOJOSHADER_PROFILE_NV3, MOJOSHADER_PROFILE_NV2, MOJOSHADER_PROFILE_ARB1, #endif }; int MOJOSHADER_glAvailableProfiles(void *(*lookup)(const char *fnname), const char **profs, const int size) { int retval = 0; MOJOSHADER_glContext _ctx; MOJOSHADER_glContext *current_ctx = ctx; ctx = &_ctx; memset(ctx, '\0', sizeof (MOJOSHADER_glContext)); load_extensions(lookup); if (ctx->have_base_opengl) { int i; for (i = 0; i < STATICARRAYLEN(profile_priorities); i++) { const char *profile = profile_priorities[i]; if (valid_profile(profile)) { if (retval < size) profs[retval] = profile; retval++; } // if } // for } // if ctx = current_ctx; return retval; } // MOJOSHADER_glAvailableProfiles const char *MOJOSHADER_glBestProfile(void *(*lookup)(const char *fnname)) { const char *prof[STATICARRAYLEN(profile_priorities)]; if (MOJOSHADER_glAvailableProfiles(lookup, prof, STATICARRAYLEN(prof)) <= 0) { set_error("no profiles available"); return NULL; } // if return prof[0]; // profiles are sorted "best" to "worst." } // MOJOSHADER_glBestProfile MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, void *(*lookup)(const char *fnname), MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) { MOJOSHADER_glContext *retval = NULL; MOJOSHADER_glContext *current_ctx = ctx; ctx = NULL; if (m == NULL) m = internal_malloc; if (f == NULL) f = internal_free; ctx = (MOJOSHADER_glContext *) m(sizeof (MOJOSHADER_glContext), d); if (ctx == NULL) { set_error("out of memory"); goto init_fail; } // if memset(ctx, '\0', sizeof (MOJOSHADER_glContext)); ctx->malloc_fn = m; ctx->free_fn = f; ctx->malloc_data = d; snprintf(ctx->profile, sizeof (ctx->profile), "%s", profile); load_extensions(lookup); if (!valid_profile(profile)) goto init_fail; MOJOSHADER_glBindProgram(NULL); // !!! FIXME: generalize this part. if (profile == NULL) {} #if SUPPORT_PROFILE_GLSL else if ( (strcmp(profile, MOJOSHADER_PROFILE_GLSL) == 0) || (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) ) { ctx->profileMaxUniforms = impl_GLSL_MaxUniforms; ctx->profileCompileShader = impl_GLSL_CompileShader; ctx->profileDeleteShader = impl_GLSL_DeleteShader; ctx->profileDeleteProgram = impl_GLSL_DeleteProgram; ctx->profileGetAttribLocation = impl_GLSL_GetAttribLocation; ctx->profileGetUniformLocation = impl_GLSL_GetUniformLocation; ctx->profileGetSamplerLocation = impl_GLSL_GetSamplerLocation; ctx->profileLinkProgram = impl_GLSL_LinkProgram; ctx->profileUseProgramObject = impl_GLSL_UseProgramObject; ctx->profileUniform4fv = impl_GLSL_Uniform4fv; ctx->profileUniform4iv = impl_GLSL_Uniform4iv; ctx->profileUniform1i = impl_GLSL_Uniform1i; ctx->profileSetSampler = impl_GLSL_SetSampler; ctx->profileMustLoadConstantArrays = impl_GLSL_MustLoadConstantArrays; } // if #endif #if SUPPORT_PROFILE_ARB1 else if ( (strcmp(profile, MOJOSHADER_PROFILE_ARB1) == 0) || (strcmp(profile, MOJOSHADER_PROFILE_NV2) == 0) || (strcmp(profile, MOJOSHADER_PROFILE_NV3) == 0) || (strcmp(profile, MOJOSHADER_PROFILE_NV4) == 0) ) { ctx->profileMaxUniforms = impl_ARB1_MaxUniforms; ctx->profileCompileShader = impl_ARB1_CompileShader; ctx->profileDeleteShader = impl_ARB1_DeleteShader; ctx->profileDeleteProgram = impl_ARB1_DeleteProgram; ctx->profileGetAttribLocation = impl_ARB1_GetAttribLocation; ctx->profileGetUniformLocation = impl_ARB1_GetUniformLocation; ctx->profileGetSamplerLocation = impl_ARB1_GetSamplerLocation; ctx->profileLinkProgram = impl_ARB1_LinkProgram; ctx->profileUseProgramObject = impl_ARB1_UseProgramObject; ctx->profileUniform4fv = impl_ARB1_Uniform4fv; ctx->profileUniform4iv = impl_ARB1_Uniform4iv; ctx->profileUniform1i = impl_ARB1_Uniform1i; ctx->profileSetSampler = impl_ARB1_SetSampler; ctx->profileMustLoadConstantArrays = impl_ARB1_MustLoadConstantArrays; // GL_NV_gpu_program4 has integer uniform loading support. if (strcmp(profile, MOJOSHADER_PROFILE_NV4) == 0) { ctx->profileUniform4iv = impl_NV4_Uniform4iv; ctx->profileUniform1i = impl_NV4_Uniform1i; } // if } // if #endif assert(ctx->profileMaxUniforms != NULL); assert(ctx->profileCompileShader != NULL); assert(ctx->profileDeleteShader != NULL); assert(ctx->profileDeleteProgram != NULL); assert(ctx->profileMaxUniforms != NULL); assert(ctx->profileGetAttribLocation != NULL); assert(ctx->profileGetUniformLocation != NULL); assert(ctx->profileGetSamplerLocation != NULL); assert(ctx->profileLinkProgram != NULL); assert(ctx->profileUseProgramObject != NULL); assert(ctx->profileUniform4fv != NULL); assert(ctx->profileUniform4iv != NULL); assert(ctx->profileUniform1i != NULL); assert(ctx->profileSetSampler != NULL); assert(ctx->profileMustLoadConstantArrays != NULL); retval = ctx; ctx = current_ctx; return retval; init_fail: if (ctx != NULL) f(ctx, d); ctx = current_ctx; return NULL; } // MOJOSHADER_glCreateContext void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *_ctx) { ctx = _ctx; } // MOJOSHADER_glMakeContextCurrent int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type) { return ctx->profileMaxUniforms(shader_type); } // MOJOSHADER_glMaxUniforms MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf, const unsigned int bufsize, const MOJOSHADER_swizzle *swiz, const unsigned int swizcount) { MOJOSHADER_glShader *retval = NULL; GLuint shader = 0; const MOJOSHADER_parseData *pd = MOJOSHADER_parse(ctx->profile, tokenbuf, bufsize, swiz, swizcount, ctx->malloc_fn, ctx->free_fn, ctx->malloc_data); if (pd->error_count > 0) { set_error(pd->errors[0].error); goto compile_shader_fail; } // if retval = (MOJOSHADER_glShader *) Malloc(sizeof (MOJOSHADER_glShader)); if (retval == NULL) goto compile_shader_fail; if (!ctx->profileCompileShader(pd, &shader)) goto compile_shader_fail; retval->parseData = pd; retval->handle = shader; retval->refcount = 1; return retval; compile_shader_fail: MOJOSHADER_freeParseData(pd); Free(retval); if (shader != 0) ctx->profileDeleteShader(shader); return NULL; } // MOJOSHADER_glCompileShader const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData( MOJOSHADER_glShader *shader) { return (shader != NULL) ? shader->parseData : NULL; } // MOJOSHADER_glGetShaderParseData static void shader_unref(MOJOSHADER_glShader *shader) { if (shader != NULL) { const uint32 refcount = shader->refcount; if (refcount > 1) shader->refcount--; else { ctx->profileDeleteShader(shader->handle); MOJOSHADER_freeParseData(shader->parseData); Free(shader); } // else } // if } // shader_unref static void program_unref(MOJOSHADER_glProgram *program) { if (program != NULL) { uint32 i; const uint32 refcount = program->refcount; if (refcount > 1) program->refcount--; else { ctx->profileDeleteProgram(program->handle); shader_unref(program->vertex); shader_unref(program->fragment); for (i = 0; i < program->uniform_count; i++) Free(program->uniforms[i].uniform_array_buffer); Free(program->samplers); Free(program->uniforms); Free(program->attributes); Free(program); } // else } // if } // program_unref static void fill_constant_array(GLfloat *f, const int base, const int size, const MOJOSHADER_parseData *pd) { int i; int filled = 0; for (i = 0; i < pd->constant_count; i++) { const MOJOSHADER_constant *c = &pd->constants[i]; if (c->type != MOJOSHADER_UNIFORM_FLOAT) continue; else if (c->index < base) continue; else if (c->index >= (base+size)) continue; memcpy(&f[(c->index-base) * 4], &c->value.f, sizeof (c->value.f)); filled++; } // for assert(filled == size); } // fill_constant_array static void lookup_uniforms(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader) { const MOJOSHADER_parseData *pd = shader->parseData; const MOJOSHADER_shaderType shader_type = pd->shader_type; int i; for (i = 0; i < pd->uniform_count; i++) { const MOJOSHADER_uniform *u = &pd->uniforms[i]; const GLint loc = ctx->profileGetUniformLocation(program, shader, i); if (loc != -1) // maybe the Uniform was optimized out? { // only do constants once, at link time. These aren't changed ever. if (u->constant) { if (ctx->profileMustLoadConstantArrays()) { const int base = u->index; const int size = u->array_count; GLfloat *f = (GLfloat *) alloca(sizeof (GLfloat)*(size*4)); fill_constant_array(f, base, size, pd); ctx->profileUseProgramObject(program); ctx->profileUniform4fv(pd, loc, size, f); ctx->profileUseProgramObject(ctx->bound_program); } // if } // if else { UniformMap *map = &program->uniforms[program->uniform_count]; map->shader_type = shader_type; map->uniform = u; map->location = (GLuint) loc; program->uniform_count++; } // else } // if } // for } // lookup_uniforms static void lookup_samplers(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader) { int i; const MOJOSHADER_parseData *pd = shader->parseData; const MOJOSHADER_sampler *s = pd->samplers; const MOJOSHADER_shaderType shader_type = pd->shader_type; for (i = 0; i < pd->sampler_count; i++) { const GLint loc = ctx->profileGetSamplerLocation(program, shader, i); if (loc != -1) // maybe the Sampler was optimized out? { SamplerMap *map = &program->samplers[program->sampler_count]; map->shader_type = shader_type; map->sampler = &s[i]; map->location = (GLuint) loc; map->value = -1; // hopefully not a valid texture unit. program->sampler_count++; } // if } // for } // lookup_samplers static void lookup_attributes(MOJOSHADER_glProgram *program) { int i; const MOJOSHADER_parseData *pd = program->vertex->parseData; const MOJOSHADER_attribute *a = pd->attributes; for (i = 0; i < pd->attribute_count; i++) { const GLint loc = ctx->profileGetAttribLocation(program, i); if (loc != -1) // maybe the Attribute was optimized out? { AttributeMap *map = &program->attributes[program->attribute_count]; map->attribute = &a[i]; map->location = (GLuint) loc; program->attribute_count++; } // if } // for } // lookup_attributes // !!! FIXME: misnamed // build a list of indexes that need to be overwritten with constant values // when pushing a uniform array to the GL. static int build_constants_lists(MOJOSHADER_glProgram *program) { int i; const int count = program->uniform_count; for (i = 0; i < count; i++) { UniformMap *map = &program->uniforms[i]; const MOJOSHADER_uniform *u = map->uniform; const int size = u->array_count; assert(!u->constant); if (size == 0) continue; // nothing to see here. // only use arrays for 'c' registers. const MOJOSHADER_uniformType type = u->type; assert(type == MOJOSHADER_UNIFORM_FLOAT); const size_t len = size * sizeof (GLfloat) * 4; map->uniform_array_buffer = (GLfloat *) Malloc(len); if (map->uniform_array_buffer == NULL) return 0; memset(map->uniform_array_buffer, 0, len); } // for return 1; } // build_constants_lists MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, MOJOSHADER_glShader *pshader) { if ((vshader == NULL) && (pshader == NULL)) return NULL; int numregs = 0; MOJOSHADER_glProgram *retval = NULL; const GLuint program = ctx->profileLinkProgram(vshader, pshader); if (program == 0) goto link_program_fail; retval = (MOJOSHADER_glProgram *) Malloc(sizeof (MOJOSHADER_glProgram)); if (retval == NULL) goto link_program_fail; memset(retval, '\0', sizeof (MOJOSHADER_glProgram)); numregs = 0; if (vshader != NULL) numregs += vshader->parseData->uniform_count; if (pshader != NULL) numregs += pshader->parseData->uniform_count; if (numregs > 0) { const size_t len = sizeof (UniformMap) * numregs; retval->uniforms = (UniformMap *) Malloc(len); if (retval->uniforms == NULL) goto link_program_fail; memset(retval->uniforms, '\0', len); } // if numregs = 0; if (vshader != NULL) numregs += vshader->parseData->sampler_count; if (pshader != NULL) numregs += pshader->parseData->sampler_count; if (numregs > 0) { const size_t len = sizeof (SamplerMap) * numregs; retval->samplers = (SamplerMap *) Malloc(len); if (retval->samplers == NULL) goto link_program_fail; memset(retval->samplers, '\0', len); } // if retval->handle = program; retval->vertex = vshader; retval->fragment = pshader; retval->refcount = 1; if (vshader != NULL) { if (vshader->parseData->attribute_count > 0) { const int count = vshader->parseData->attribute_count; const size_t len = sizeof (AttributeMap) * count; retval->attributes = (AttributeMap *) Malloc(len); if (retval->attributes == NULL) goto link_program_fail; memset(retval->attributes, '\0', len); lookup_attributes(retval); } // if lookup_uniforms(retval, vshader); lookup_samplers(retval, vshader); vshader->refcount++; } // if if (pshader != NULL) { lookup_uniforms(retval, pshader); lookup_samplers(retval, pshader); pshader->refcount++; } // if if (!build_constants_lists(retval)) goto link_program_fail; return retval; link_program_fail: if (retval != NULL) { uint32 i; for (i = 0; i < retval->uniform_count; i++) Free(retval->uniforms[i].uniform_array_buffer); Free(retval->samplers); Free(retval->uniforms); Free(retval->attributes); Free(retval); } // if if (program != 0) ctx->profileDeleteProgram(program); return NULL; } // MOJOSHADER_glLinkProgram void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program) { GLuint handle = 0; int i; if (program == ctx->bound_program) return; // nothing to do. // Disable any client-side arrays the current program could have used. // !!! FIXME: don't disable yet...see which ones get reused, and disable // !!! FIXME: only what we don't need in MOJOSHADER_glProgramReady(). if (ctx->bound_program != NULL) { const int count = ctx->bound_program->attribute_count; for (i = 0; i < count; i++) { const AttributeMap *map = &ctx->bound_program->attributes[i]; ctx->glDisableVertexAttribArray(map->location); } // if } // for if (program != NULL) { handle = program->handle; program->refcount++; } // if ctx->profileUseProgramObject(program); program_unref(ctx->bound_program); ctx->bound_program = program; } // MOJOSHADER_glBindProgram static inline uint minuint(const uint a, const uint b) { return ((a < b) ? a : b); } // minuint void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data, unsigned int vec4n) { const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_f) / 4; if (idx < maxregs) { assert(sizeof (GLfloat) == sizeof (float)); const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; memcpy(ctx->vs_reg_file_f + (idx * 4), data, cpy); } // if } // MOJOSHADER_glSetVertexShaderUniformF void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data, unsigned int ivec4n) { const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_i) / 4; if (idx < maxregs) { assert(sizeof (GLint) == sizeof (int)); const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; memcpy(ctx->vs_reg_file_i + (idx * 4), data, cpy); } // if } // MOJOSHADER_glSetVertexShaderUniformI void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data, unsigned int bcount) { const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_f) / 4; if (idx < maxregs) { GLint *wptr = ctx->vs_reg_file_b + idx; GLint *endptr = wptr + minuint(maxregs - idx, bcount); while (wptr != endptr) *(wptr++) = *(data++) ? 1 : 0; } // if } // MOJOSHADER_glSetVertexShaderUniformB void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data, unsigned int vec4n) { const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_f) / 4; if (idx < maxregs) { assert(sizeof (GLfloat) == sizeof (float)); const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; memcpy(ctx->ps_reg_file_f + (idx * 4), data, cpy); } // if } // MOJOSHADER_glSetPixelShaderUniformF void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data, unsigned int ivec4n) { const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_i) / 4; if (idx < maxregs) { assert(sizeof (GLint) == sizeof (int)); const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; memcpy(ctx->ps_reg_file_i + (idx * 4), data, cpy); } // if } // MOJOSHADER_glSetPixelShaderUniformI void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data, unsigned int bcount) { const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_f) / 4; if (idx < maxregs) { GLint *wptr = ctx->ps_reg_file_b + idx; GLint *endptr = wptr + minuint(maxregs - idx, bcount); while (wptr != endptr) *(wptr++) = *(data++) ? 1 : 0; } // if } // MOJOSHADER_glSetPixelShaderUniformB static inline GLenum opengl_attr_type(const MOJOSHADER_attributeType type) { switch (type) { case MOJOSHADER_ATTRIBUTE_UNKNOWN: return GL_NONE; // oh well. case MOJOSHADER_ATTRIBUTE_BYTE: return GL_BYTE; case MOJOSHADER_ATTRIBUTE_UBYTE: return GL_UNSIGNED_BYTE; case MOJOSHADER_ATTRIBUTE_SHORT: return GL_SHORT; case MOJOSHADER_ATTRIBUTE_USHORT: return GL_UNSIGNED_SHORT; case MOJOSHADER_ATTRIBUTE_INT: return GL_INT; case MOJOSHADER_ATTRIBUTE_UINT: return GL_UNSIGNED_INT; case MOJOSHADER_ATTRIBUTE_FLOAT: return GL_FLOAT; case MOJOSHADER_ATTRIBUTE_DOUBLE: return GL_DOUBLE; case MOJOSHADER_ATTRIBUTE_HALF_FLOAT: if (ctx->have_GL_NV_half_float) return GL_HALF_FLOAT_NV; else if (ctx->have_GL_ARB_half_float_vertex) return GL_HALF_FLOAT; else if (ctx->have_GL_OES_vertex_half_float) return GL_HALF_FLOAT; break; } // switch return GL_NONE; // oh well. Raises a GL error later. } // opengl_attr_type // !!! FIXME: shouldn't (index) be unsigned? void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, int index, unsigned int size, MOJOSHADER_attributeType type, int normalized, unsigned int stride, const void *ptr) { if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL)) return; const GLenum gl_type = opengl_attr_type(type); const GLboolean norm = (normalized) ? GL_TRUE : GL_FALSE; const int count = ctx->bound_program->attribute_count; GLuint gl_index = 0; int i; for (i = 0; i < count; i++) { const AttributeMap *map = &ctx->bound_program->attributes[i]; const MOJOSHADER_attribute *a = map->attribute; // !!! FIXME: is this array guaranteed to be sorted by usage? // !!! FIXME: if so, we can break if a->usage > usage. if ((a->usage == usage) && (a->index == index)) { gl_index = map->location; break; } // if } // for if (i == count) return; // nothing to do, this shader doesn't use this stream. // these happen to work in both ARB1 and GLSL, but if something alien // shows up, we'll have to split these into profile*() functions. ctx->glVertexAttribPointer(gl_index, size, gl_type, norm, stride, ptr); ctx->glEnableVertexAttribArray(gl_index); } // MOJOSHADER_glSetVertexAttribute void MOJOSHADER_glProgramReady(void) { int i; int count; if (ctx->bound_program == NULL) return; // nothing to do. // push Uniforms to the program from our register files... count = ctx->bound_program->uniform_count; for (i = 0; i < count; i++) { UniformMap *map = &ctx->bound_program->uniforms[i]; const MOJOSHADER_uniform *u = map->uniform; const MOJOSHADER_uniformType type = u->type; const MOJOSHADER_shaderType shader_type = map->shader_type; const int index = u->index; const int size = u->array_count; const GLint location = map->location; const MOJOSHADER_parseData *pd; GLfloat *regf; GLint *regi; GLint *regb; assert(!u->constant); if (shader_type == MOJOSHADER_TYPE_VERTEX) { pd = ctx->bound_program->vertex->parseData; regf = ctx->vs_reg_file_f; regi = ctx->vs_reg_file_i; regb = ctx->vs_reg_file_b; } // if else if (shader_type == MOJOSHADER_TYPE_PIXEL) { pd = ctx->bound_program->fragment->parseData; regf = ctx->ps_reg_file_f; regi = ctx->ps_reg_file_i; regb = ctx->ps_reg_file_b; } // else if else { assert(0); // !!! FIXME: geometry shaders? } // else if (size != 0) // uniform array? { GLfloat *f = ®f[index * 4]; const size_t len = size * sizeof (GLfloat) * 4; GLfloat *current = map->uniform_array_buffer; //if (memcmp(current, f, len) != 0) { // array has changed, upload it. ctx->profileUniform4fv(pd, location, size, f); memcpy(current, f, len); } // if } // if else { if (type == MOJOSHADER_UNIFORM_FLOAT) { GLfloat *f = ®f[index * 4]; //if (memcmp(map->value.f, f, sizeof (map->value.f)) != 0) { memcpy(map->value.f, f, sizeof (map->value.f)); ctx->profileUniform4fv(pd, location, 1, f); } // if } // if else if (type == MOJOSHADER_UNIFORM_INT) { GLint *i = ®i[index * 4]; //if (memcmp(map->value.i, i, sizeof (map->value.i)) != 0) { memcpy(map->value.i, i, sizeof (map->value.i)); ctx->profileUniform4iv(pd, location, 1, i); } // if } // else if else if (type == MOJOSHADER_UNIFORM_BOOL) { const GLint b = regb[index]; //if (b != map->value.b) { map->value.b = b; ctx->profileUniform1i(pd, location, b); } // if } // else if } // if } // for // push Samplers to the program from our register files... // !!! FIXME: just push these once at link time...they never change, since // !!! FIXME: they are meant to be constant texture unit ids and not // !!! FIXME: textures. count = ctx->bound_program->sampler_count; for (i = 0; i < count; i++) { SamplerMap *map = &ctx->bound_program->samplers[i]; const MOJOSHADER_sampler *s = map->sampler; if (s->index != map->value) { map->value = s->index; ctx->profileSetSampler(map->location, s->index); } // if } // for } // MOJOSHADER_glProgramReady void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program) { program_unref(program); } // MOJOSHADER_glDeleteProgram void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader) { shader_unref(shader); } // MOJOSHADER_glDeleteShader void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *_ctx) { MOJOSHADER_glContext *current_ctx = ctx; ctx = _ctx; MOJOSHADER_glBindProgram(NULL); lookup_entry_points(NULL); Free(ctx); ctx = ((current_ctx == _ctx) ? NULL : current_ctx); } // MOJOSHADER_glDestroyContext // end of mojoshader_opengl.c ...