diff options
Diffstat (limited to 'Externals/mojoshader/1067/mojoshader_opengl.c')
| -rw-r--r-- | Externals/mojoshader/1067/mojoshader_opengl.c | 2308 |
1 files changed, 2308 insertions, 0 deletions
diff --git a/Externals/mojoshader/1067/mojoshader_opengl.c b/Externals/mojoshader/1067/mojoshader_opengl.c new file mode 100644 index 00000000..b8b74e43 --- /dev/null +++ b/Externals/mojoshader/1067/mojoshader_opengl.c @@ -0,0 +1,2308 @@ +/** + * 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. + */ + +// !!! FIXME: preshaders shouldn't be handled in here at all. This should +// !!! FIXME: be in the Effects API, once that's actually written. + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> + +// !!! FIXME: most of these _MSC_VER should probably be _WINDOWS? +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> // GL headers need this for WINGDIAPI definition. +#endif + +#if (defined(__APPLE__) && defined(__MACH__) && !defined(__arm__) && !defined(__arm64__)) +#define PLATFORM_MACOSX 1 +#endif + +#if PLATFORM_MACOSX +#include <Carbon/Carbon.h> +#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 + +// this happens to be the same value for ARB1 and GLSL. +#ifndef GL_PROGRAM_POINT_SIZE +#define GL_PROGRAM_POINT_SIZE 0x8642 +#endif + +struct MOJOSHADER_glShader +{ + const MOJOSHADER_parseData *parseData; + GLuint handle; + uint32 refcount; +}; + +typedef struct +{ + MOJOSHADER_shaderType shader_type; + const MOJOSHADER_uniform *uniform; + GLint location; +} UniformMap; + +typedef struct +{ + const MOJOSHADER_attribute *attribute; + GLint location; +} AttributeMap; + +struct MOJOSHADER_glProgram +{ + MOJOSHADER_glShader *vertex; + MOJOSHADER_glShader *fragment; + GLuint handle; + uint32 generation; + uint32 uniform_count; + UniformMap *uniforms; + uint32 attribute_count; + AttributeMap *attributes; + size_t vs_uniforms_float4_count; + GLfloat *vs_uniforms_float4; + size_t vs_uniforms_int4_count; + GLint *vs_uniforms_int4; + size_t vs_uniforms_bool_count; + GLint *vs_uniforms_bool; + size_t ps_uniforms_float4_count; + GLfloat *ps_uniforms_float4; + size_t ps_uniforms_int4_count; + GLint *ps_uniforms_int4; + size_t ps_uniforms_bool_count; + GLint *ps_uniforms_bool; + + size_t vs_preshader_reg_count; + GLfloat *vs_preshader_regs; + size_t ps_preshader_reg_count; + GLfloat *ps_preshader_regs; + + uint32 refcount; + + int uses_pointsize; + + // GLSL uses these...location of uniform arrays. + GLint vs_float4_loc; + GLint vs_int4_loc; + GLint vs_bool_loc; + GLint ps_float4_loc; + GLint ps_int4_loc; + GLint ps_bool_loc; +}; + +#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); + +// Max entries for each register file type... +#define MAX_REG_FILE_F 8192 +#define MAX_REG_FILE_I 2047 +#define MAX_REG_FILE_B 2047 + +struct MOJOSHADER_glContext +{ + // Allocators... + MOJOSHADER_malloc malloc_fn; + MOJOSHADER_free free_fn; + void *malloc_data; + + // The constant register files... + // !!! FIXME: Man, it kills me how much memory this takes... + // !!! FIXME: ... make this dynamically allocated on demand. + GLfloat vs_reg_file_f[MAX_REG_FILE_F * 4]; + GLint vs_reg_file_i[MAX_REG_FILE_I * 4]; + uint8 vs_reg_file_b[MAX_REG_FILE_B]; + GLfloat ps_reg_file_f[MAX_REG_FILE_F * 4]; + GLint ps_reg_file_i[MAX_REG_FILE_I * 4]; + uint8 ps_reg_file_b[MAX_REG_FILE_B]; + GLuint sampler_reg_file[16]; + + // This increments every time we change the register files. + uint32 generation; + + // This keeps track of implicitly linked programs. + HashTable *linker_cache; + + // This tells us which vertex attribute arrays we have enabled. + GLint max_attrs; + uint8 want_attr[32]; + uint8 have_attr[32]; + + // rarely used, so we don't touch when we don't have to. + int pointsize_enabled; + + // 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; + PFNGLUNIFORM1IVARBPROC glUniform1iv; + 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 *program, MOJOSHADER_glShader *shader, int idx); + GLint (*profileGetSamplerLocation)(MOJOSHADER_glProgram *, MOJOSHADER_glShader *, int); + GLuint (*profileLinkProgram)(MOJOSHADER_glShader *, MOJOSHADER_glShader *); + void (*profileFinalInitProgram)(MOJOSHADER_glProgram *program); + void (*profileUseProgramObject)(MOJOSHADER_glProgram *program); + void (*profilePushConstantArray)(MOJOSHADER_glProgram *, const MOJOSHADER_uniform *, const GLfloat *); + void (*profilePushUniforms)(void); + void (*profilePushSampler)(GLint loc, GLuint sampler); + int (*profileMustPushConstantArrays)(void); + int (*profileMustPushSamplers)(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) + { + SInt32 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 out_of_memory(void) +{ + set_error("out of memory"); +} // out_of_memory + +static inline void *Malloc(const size_t len) +{ + void *retval = ctx->malloc_fn((int) len, ctx->malloc_data); + if (retval == NULL) + 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_MustPushConstantArrays(void) { return 1; } +static int impl_GLSL_MustPushSamplers(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 0; // no-op, we push this as one big-ass array now. +} // 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_FinalInitProgram(MOJOSHADER_glProgram *program) +{ + program->vs_float4_loc = + ctx->glGetUniformLocation(program->handle, "vs_uniforms_vec4"); + program->vs_int4_loc = + ctx->glGetUniformLocation(program->handle, "vs_uniforms_ivec4"); + program->vs_bool_loc = + ctx->glGetUniformLocation(program->handle, "vs_uniforms_bool"); + program->ps_float4_loc = + ctx->glGetUniformLocation(program->handle, "ps_uniforms_vec4"); + program->ps_int4_loc = + ctx->glGetUniformLocation(program->handle, "ps_uniforms_ivec4"); + program->ps_bool_loc = + ctx->glGetUniformLocation(program->handle, "ps_uniforms_bool"); +} // impl_GLSL_FinalInitProgram + + +static void impl_GLSL_UseProgramObject(MOJOSHADER_glProgram *program) +{ + ctx->glUseProgramObject((program != NULL) ? program->handle : 0); +} // impl_GLSL_UseProgramObject + + +static void impl_GLSL_PushConstantArray(MOJOSHADER_glProgram *program, + const MOJOSHADER_uniform *u, + const GLfloat *f) +{ + const GLint loc = ctx->glGetUniformLocation(program->handle, u->name); + if (loc >= 0) // not optimized out? + ctx->glUniform4fv(loc, u->array_count, f); +} // impl_GLSL_PushConstantArray + + +static void impl_GLSL_PushUniforms(void) +{ + const MOJOSHADER_glProgram *program = ctx->bound_program; + + assert(program->uniform_count > 0); // don't call with nothing to do! + + if (program->vs_float4_loc != -1) + { + ctx->glUniform4fv(program->vs_float4_loc, + program->vs_uniforms_float4_count, + program->vs_uniforms_float4); + } // if + + if (program->vs_int4_loc != -1) + { + ctx->glUniform4iv(program->vs_int4_loc, + program->vs_uniforms_int4_count, + program->vs_uniforms_int4); + } // if + + if (program->vs_bool_loc != -1) + { + ctx->glUniform1iv(program->vs_bool_loc, + program->vs_uniforms_bool_count, + program->vs_uniforms_bool); + } // if + + if (program->ps_float4_loc != -1) + { + ctx->glUniform4fv(program->ps_float4_loc, + program->ps_uniforms_float4_count, + program->ps_uniforms_float4); + } // if + + if (program->ps_int4_loc != -1) + { + ctx->glUniform4iv(program->ps_int4_loc, + program->ps_uniforms_int4_count, + program->ps_uniforms_int4); + } // if + + if (program->ps_bool_loc != -1) + { + ctx->glUniform1iv(program->ps_bool_loc, + program->ps_uniforms_bool_count, + program->ps_uniforms_bool); + } // if +} // impl_GLSL_PushUniforms + + +static void impl_GLSL_PushSampler(GLint loc, GLuint sampler) +{ + ctx->glUniform1i(loc, sampler); +} // impl_GLSL_PushSampler + +#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_MustPushConstantArrays(void) { return 0; } +static int impl_ARB1_MustPushSamplers(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) +{ + return 0; // no-op, we push this as one big-ass array now. +} // 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_FinalInitProgram(MOJOSHADER_glProgram *program) +{ + // no-op. +} // impl_ARB1_FinalInitProgram + + +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_PushConstantArray(MOJOSHADER_glProgram *program, + const MOJOSHADER_uniform *u, + const GLfloat *f) +{ + // no-op. Constant arrays are defined in source code for arb1. +} // impl_ARB1_PushConstantArray + + +static void impl_ARB1_PushUniforms(void) +{ + // vertex shader uniforms come first in program->uniforms array. + MOJOSHADER_shaderType shader_type = MOJOSHADER_TYPE_VERTEX; + GLenum arb_shader_type = arb1_shader_type(shader_type); + const MOJOSHADER_glProgram *program = ctx->bound_program; + const uint32 count = program->uniform_count; + const GLfloat *srcf = program->vs_uniforms_float4; + const GLint *srci = program->vs_uniforms_int4; + const GLint *srcb = program->vs_uniforms_bool; + GLint loc = 0; + uint32 i; + + assert(count > 0); // shouldn't call this with nothing to do! + + for (i = 0; i < count; i++) + { + UniformMap *map = &program->uniforms[i]; + const MOJOSHADER_shaderType uniform_shader_type = map->shader_type; + const MOJOSHADER_uniform *u = map->uniform; + const MOJOSHADER_uniformType type = u->type; + const int size = u->array_count ? u->array_count : 1; + + assert(!u->constant); + + // Did we switch from vertex to pixel (to geometry, etc)? + if (shader_type != uniform_shader_type) + { + // we start with vertex, move to pixel, then to geometry, etc. + // The array should always be sorted as such. + if (uniform_shader_type == MOJOSHADER_TYPE_PIXEL) + { + assert(shader_type == MOJOSHADER_TYPE_VERTEX); + srcf = program->ps_uniforms_float4; + srci = program->ps_uniforms_int4; + srcb = program->ps_uniforms_bool; + loc = 0; + } // if + else + { + // These should be ordered vertex, then pixel, then geometry. + assert(0 && "Unexpected shader type"); + } // else + + shader_type = uniform_shader_type; + arb_shader_type = arb1_shader_type(uniform_shader_type); + } // if + + if (type == MOJOSHADER_UNIFORM_FLOAT) + { + int i; + for (i = 0; i < size; i++, srcf += 4, loc++) + ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, srcf); + } // if + else if (type == MOJOSHADER_UNIFORM_INT) + { + int i; + if (ctx->have_GL_NV_gpu_program4) + { + // GL_NV_gpu_program4 has integer uniform loading support. + for (i = 0; i < size; i++, srci += 4, loc++) + ctx->glProgramLocalParameterI4ivNV(arb_shader_type, loc, srci); + } // if + else + { + for (i = 0; i < size; i++, srci += 4, loc++) + { + const GLfloat fv[4] = { + (GLfloat) srci[0], (GLfloat) srci[1], + (GLfloat) srci[2], (GLfloat) srci[3] + }; + ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, fv); + } // for + } // else + } // else if + else if (type == MOJOSHADER_UNIFORM_BOOL) + { + int i; + if (ctx->have_GL_NV_gpu_program4) + { + // GL_NV_gpu_program4 has integer uniform loading support. + for (i = 0; i < size; i++, srcb++, loc++) + { + const GLint ib = (GLint) ((*srcb) ? 1 : 0); + const GLint iv[4] = { ib, ib, ib, ib }; + ctx->glProgramLocalParameterI4ivNV(arb_shader_type, loc, iv); + } // for + } // if + else + { + for (i = 0; i < size; i++, srcb++, loc++) + { + const GLfloat fb = (GLfloat) ((*srcb) ? 1.0f : 0.0f); + const GLfloat fv[4] = { fb, fb, fb, fb }; + ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, fv); + } // for + } // else + } // else if + } // for +} // impl_ARB1_PushUniforms + +static void impl_ARB1_PushSampler(GLint loc, GLuint sampler) +{ + // no-op in this profile...arb1 uses the texture units as-is. + assert(loc == (GLint) sampler); +} // impl_ARB1_PushSampler + +#endif // SUPPORT_PROFILE_ARB1 + + +const char *MOJOSHADER_glGetError(void) +{ + return error_buffer; +} // MOJOSHADER_glGetError + + +static void *loadsym(MOJOSHADER_glGetProcAddress lookup, void *d, + const char *fn, int *ext) +{ + void *retval = NULL; + if (lookup != NULL) + { + retval = lookup(fn, d); + if (retval == NULL) + { + char arbfn[64]; + snprintf(arbfn, sizeof (arbfn), "%sARB", fn); + retval = lookup(arbfn, d); + } // if + } // if + + if (retval == NULL) + *ext = 0; + + return retval; +} // loadsym + +static void lookup_entry_points(MOJOSHADER_glGetProcAddress lookup, void *d) +{ + #define DO_LOOKUP(ext, typ, fn) { \ + ctx->fn = (typ) loadsym(lookup, d, #fn, &ctx->have_##ext); \ + } + + 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, PFNGLUNIFORM1IVARBPROC, glUniform1iv); + 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 + + +#if SUPPORT_PROFILE_GLSL +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 +#endif + + +static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) +{ + 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, d); + + 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 SUPPORT_PROFILE_GLSL + // 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 +#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 + #endif + + #if SUPPORT_PROFILE_ARB1_NV + 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_GLSL120 + 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 + #endif + + #if SUPPORT_PROFILE_GLSL + 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_GLSL120 + MOJOSHADER_PROFILE_GLSL120, +#endif +#if SUPPORT_PROFILE_GLSL + MOJOSHADER_PROFILE_GLSL, +#endif +#if SUPPORT_PROFILE_ARB1_NV + MOJOSHADER_PROFILE_NV4, + MOJOSHADER_PROFILE_NV3, + MOJOSHADER_PROFILE_NV2, +#endif +#if SUPPORT_PROFILE_ARB1 + MOJOSHADER_PROFILE_ARB1, +#endif +}; + +int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, void *d, + 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, d); + + if (ctx->have_base_opengl) + { + size_t 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(MOJOSHADER_glGetProcAddress gpa, void *d) +{ + const char *prof[STATICARRAYLEN(profile_priorities)]; + if (MOJOSHADER_glAvailableProfiles(gpa, d, 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, + MOJOSHADER_glGetProcAddress lookup, + void *lookup_d, + MOJOSHADER_malloc m, MOJOSHADER_free f, + void *malloc_d) +{ + MOJOSHADER_glContext *retval = NULL; + MOJOSHADER_glContext *current_ctx = ctx; + + ctx = NULL; + + if (m == NULL) m = MOJOSHADER_internal_malloc; + if (f == NULL) f = MOJOSHADER_internal_free; + + ctx = (MOJOSHADER_glContext *) m(sizeof (MOJOSHADER_glContext), malloc_d); + if (ctx == NULL) + { + out_of_memory(); + goto init_fail; + } // if + + memset(ctx, '\0', sizeof (MOJOSHADER_glContext)); + ctx->malloc_fn = m; + ctx->free_fn = f; + ctx->malloc_data = malloc_d; + snprintf(ctx->profile, sizeof (ctx->profile), "%s", profile); + + load_extensions(lookup, lookup_d); + if (!valid_profile(profile)) + goto init_fail; + + MOJOSHADER_glBindProgram(NULL); + + // !!! FIXME: generalize this part. + if (profile == NULL) {} + + // We don't check SUPPORT_PROFILE_GLSL120 here, since valid_profile() does. +#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->profileFinalInitProgram = impl_GLSL_FinalInitProgram; + ctx->profileUseProgramObject = impl_GLSL_UseProgramObject; + ctx->profilePushConstantArray = impl_GLSL_PushConstantArray; + ctx->profilePushUniforms = impl_GLSL_PushUniforms; + ctx->profilePushSampler = impl_GLSL_PushSampler; + ctx->profileMustPushConstantArrays = impl_GLSL_MustPushConstantArrays; + ctx->profileMustPushSamplers = impl_GLSL_MustPushSamplers; + } // if +#endif + + // We don't check SUPPORT_PROFILE_ARB1_NV here, since valid_profile() does. +#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->profileFinalInitProgram = impl_ARB1_FinalInitProgram; + ctx->profileUseProgramObject = impl_ARB1_UseProgramObject; + ctx->profilePushConstantArray = impl_ARB1_PushConstantArray; + ctx->profilePushUniforms = impl_ARB1_PushUniforms; + ctx->profilePushSampler = impl_ARB1_PushSampler; + ctx->profileMustPushConstantArrays = impl_ARB1_MustPushConstantArrays; + ctx->profileMustPushSamplers = impl_ARB1_MustPushSamplers; + } // 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->profileFinalInitProgram != NULL); + assert(ctx->profileUseProgramObject != NULL); + assert(ctx->profilePushConstantArray != NULL); + assert(ctx->profilePushUniforms != NULL); + assert(ctx->profilePushSampler != NULL); + assert(ctx->profileMustPushConstantArrays != NULL); + assert(ctx->profileMustPushSamplers != NULL); + + retval = ctx; + ctx = current_ctx; + return retval; + +init_fail: + if (ctx != NULL) + f(ctx, malloc_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) + { + // !!! FIXME: put multiple errors in the buffer? Don't use + // !!! FIXME: MOJOSHADER_glGetError() for this? + 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) + { + const uint32 refcount = program->refcount; + if (refcount > 1) + program->refcount--; + else + { + ctx->profileDeleteProgram(program->handle); + shader_unref(program->vertex); + shader_unref(program->fragment); + Free(program->vs_preshader_regs); + Free(program->ps_preshader_regs); + Free(program->vs_uniforms_float4); + Free(program->vs_uniforms_int4); + Free(program->vs_uniforms_bool); + Free(program->ps_uniforms_float4); + Free(program->ps_uniforms_int4); + Free(program->ps_uniforms_bool); + 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 int lookup_uniforms(MOJOSHADER_glProgram *program, + MOJOSHADER_glShader *shader, int *bound) +{ + const MOJOSHADER_parseData *pd = shader->parseData; + const MOJOSHADER_shaderType shader_type = pd->shader_type; + uint32 float4_count = 0; + uint32 int4_count = 0; + uint32 bool_count = 0; + int i; + + for (i = 0; i < pd->uniform_count; i++) + { + const MOJOSHADER_uniform *u = &pd->uniforms[i]; + + if (u->constant) + { + // only do constants once, at link time. These aren't changed ever. + if (ctx->profileMustPushConstantArrays()) + { + 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); + if (!(*bound)) + { + ctx->profileUseProgramObject(program); + *bound = 1; + } // if + ctx->profilePushConstantArray(program, u, f); + } // if + } // if + + else + { + const GLint loc = ctx->profileGetUniformLocation(program, shader, i); + if (loc != -1) // -1 means it was optimized out, or failure. + { + const int regcount = u->array_count; + UniformMap *map = &program->uniforms[program->uniform_count]; + map->shader_type = shader_type; + map->uniform = u; + map->location = (GLuint) loc; + program->uniform_count++; + + if (u->type == MOJOSHADER_UNIFORM_FLOAT) + float4_count += regcount ? regcount : 1; + else if (u->type == MOJOSHADER_UNIFORM_INT) + int4_count += regcount ? regcount : 1; + else if (u->type == MOJOSHADER_UNIFORM_BOOL) + bool_count += regcount ? regcount : 1; + else + assert(0 && "Unexpected register type"); + } // if + } // else + } // for + + #define MAKE_ARRAY(typ, gltyp, siz, count) \ + if (count) { \ + const size_t buflen = sizeof (gltyp) * siz * count; \ + gltyp *ptr = (gltyp *) Malloc(buflen); \ + if (ptr == NULL) { \ + return 0; \ + } else if (shader_type == MOJOSHADER_TYPE_VERTEX) { \ + program->vs_uniforms_##typ = ptr; \ + program->vs_uniforms_##typ##_count = count; \ + } else if (shader_type == MOJOSHADER_TYPE_PIXEL) { \ + program->ps_uniforms_##typ = ptr; \ + program->ps_uniforms_##typ##_count = count; \ + } else { \ + assert(0 && "unsupported shader type"); \ + } \ + memset(ptr, '\0', buflen); \ + } + + MAKE_ARRAY(float4, GLfloat, 4, float4_count); + MAKE_ARRAY(int4, GLint, 4, int4_count); + MAKE_ARRAY(bool, GLint, 1, bool_count); + + #undef MAKE_ARRAY + + if (pd->preshader) + { + unsigned int largest = 0; + const MOJOSHADER_symbol *sym = pd->preshader->symbols; + for (i = 0; i < pd->preshader->symbol_count; i++, sym++) + { + const unsigned int val = sym->register_index + sym->register_count; + if (val > largest) + largest = val; + } // for + + if (largest > 0) + { + const size_t len = largest * sizeof (GLfloat) * 4; + GLfloat *buf = (GLfloat *) Malloc(len); + if (buf == NULL) + return 0; + memset(buf, '\0', len); + + if (shader_type == MOJOSHADER_TYPE_VERTEX) + { + program->vs_preshader_reg_count = largest; + program->vs_preshader_regs = buf; + } // if + else if (shader_type == MOJOSHADER_TYPE_PIXEL) + { + program->ps_preshader_reg_count = largest; + program->ps_preshader_regs = buf; + } // else if + } // if + } // if + + return 1; +} // lookup_uniforms + + +static void lookup_samplers(MOJOSHADER_glProgram *program, + MOJOSHADER_glShader *shader, int *bound) +{ + const MOJOSHADER_parseData *pd = shader->parseData; + const MOJOSHADER_sampler *s = pd->samplers; + int i; + + if ((pd->sampler_count == 0) || (!ctx->profileMustPushSamplers())) + return; // nothing to do here, so don't bother binding, etc. + + // Link up the Samplers. These never change after link time, since they + // are meant to be constant texture unit ids and not textures. + + if (!(*bound)) + { + ctx->profileUseProgramObject(program); + *bound = 1; + } // if + + for (i = 0; i < pd->sampler_count; i++) + { + const GLint loc = ctx->profileGetSamplerLocation(program, shader, i); + if (loc >= 0) // maybe the Sampler was optimized out? + ctx->profilePushSampler(loc, s[i].index); + } // for +} // lookup_samplers + + +// Right now, this just decides if we have to toggle pointsize support later. +static void lookup_outputs(MOJOSHADER_glProgram *program, + MOJOSHADER_glShader *shader) +{ + const MOJOSHADER_parseData *pd = shader->parseData; + int i; + + for (i = 0; i < pd->output_count; i++) + { + if (pd->outputs[i].usage == MOJOSHADER_USAGE_POINTSIZE) + { + program->uses_pointsize = 1; + break; + } // if + } // for +} // lookup_outputs + + +static int 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 >= 0) // maybe the Attribute was optimized out? + { + AttributeMap *map = &program->attributes[program->attribute_count]; + map->attribute = &a[i]; + map->location = loc; + program->attribute_count++; + + if (((size_t)loc) > STATICARRAYLEN(ctx->want_attr)) + { + assert(0 && "Static array is too small."); // laziness fail. + return 0; + } // if + } // if + } // for + + return 1; +} // 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. + assert(u->type == MOJOSHADER_UNIFORM_FLOAT); + + // !!! FIXME: deal with this. + } // for + + return 1; +} // build_constants_lists + + +MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, + MOJOSHADER_glShader *pshader) +{ + int bound = 0; + + 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 + + retval->handle = program; + retval->vertex = vshader; + retval->fragment = pshader; + retval->generation = ctx->generation - 1; + 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); + if (!lookup_attributes(retval)) + goto link_program_fail; + } // if + + if (!lookup_uniforms(retval, vshader, &bound)) + goto link_program_fail; + lookup_samplers(retval, vshader, &bound); + lookup_outputs(retval, pshader); + vshader->refcount++; + } // if + + if (pshader != NULL) + { + if (!lookup_uniforms(retval, pshader, &bound)) + goto link_program_fail; + lookup_samplers(retval, pshader, &bound); + lookup_outputs(retval, pshader); + pshader->refcount++; + } // if + + if (!build_constants_lists(retval)) + goto link_program_fail; + + if (bound) // reset the old binding. + ctx->profileUseProgramObject(ctx->bound_program); + + ctx->profileFinalInitProgram(retval); + + return retval; + +link_program_fail: + if (retval != NULL) + { + Free(retval->vs_uniforms_float4); + Free(retval->vs_uniforms_int4); + Free(retval->vs_uniforms_bool); + Free(retval->ps_uniforms_float4); + Free(retval->ps_uniforms_int4); + Free(retval->ps_uniforms_bool); + Free(retval->uniforms); + Free(retval->attributes); + Free(retval); + } // if + + if (program != 0) + ctx->profileDeleteProgram(program); + + if (bound) + ctx->profileUseProgramObject(ctx->bound_program); + + return NULL; +} // MOJOSHADER_glLinkProgram + + +static void update_enabled_arrays(void) +{ + int highest_enabled = 0; + int i; + + // Enable/disable vertex arrays to match our needs. + // this happens to work in both ARB1 and GLSL, but if something alien + // shows up, we'll have to split these into profile*() functions. + for (i = 0; i < ctx->max_attrs; i++) + { + const int want = (const int) ctx->want_attr[i]; + const int have = (const int) ctx->have_attr[i]; + if (want != have) + { + if (want) + ctx->glEnableVertexAttribArray(i); + else + ctx->glDisableVertexAttribArray(i); + ctx->have_attr[i] = ctx->want_attr[i]; + } // if + + if (want) + highest_enabled = i + 1; + } // for + + ctx->max_attrs = highest_enabled; // trim unneeded iterations next time. +} // update_enabled_arrays + + +void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program) +{ + if (program == ctx->bound_program) + return; // nothing to do. + + if (program != NULL) + program->refcount++; + + memset(ctx->want_attr, '\0', sizeof (ctx->want_attr[0]) * ctx->max_attrs); + + // If no program bound, disable all arrays, in case we're switching to + // fixed function pipeline. Otherwise, we try to minimize state changes + // by toggling just the changed set of needed arrays in ProgramReady(). + if (program == NULL) + update_enabled_arrays(); + + ctx->profileUseProgramObject(program); + program_unref(ctx->bound_program); + ctx->bound_program = program; +} // MOJOSHADER_glBindProgram + + +typedef struct +{ + MOJOSHADER_glShader *vertex; + MOJOSHADER_glShader *fragment; +} BoundShaders; + +static uint32 hash_shaders(const void *sym, void *data) +{ + (void) data; + const BoundShaders *s = (const BoundShaders *) sym; + const uint32 v = (s->vertex) ? (uint32) s->vertex->handle : 0; + const uint32 f = (s->fragment) ? (uint32) s->fragment->handle : 0; + return ((v & 0xFFFF) << 16) | (f & 0xFFFF); +} // hash_shaders + +static int match_shaders(const void *_a, const void *_b, void *data) +{ + (void) data; + const BoundShaders *a = (const BoundShaders *) _a; + const BoundShaders *b = (const BoundShaders *) _b; + + const GLuint av = (a->vertex) ? a->vertex->handle : 0; + const GLuint bv = (b->vertex) ? b->vertex->handle : 0; + if (av != bv) + return 0; + + const GLuint af = (a->fragment) ? a->fragment->handle : 0; + const GLuint bf = (b->fragment) ? b->fragment->handle : 0; + if (af != bf) + return 0; + + return 1; +} // match_shaders + +static void nuke_shaders(const void *key, const void *value, void *data) +{ + (void) data; + Free((void *) key); // this was a BoundShaders struct. + MOJOSHADER_glDeleteProgram((MOJOSHADER_glProgram *) value); +} // nuke_shaders + +void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *v, MOJOSHADER_glShader *p) +{ + if ((v == NULL) && (p == NULL)) + { + MOJOSHADER_glBindProgram(NULL); + return; + } // if + + // !!! FIXME: eventually support GL_EXT_separate_shader_objects. + if (ctx->linker_cache == NULL) + { + ctx->linker_cache = hash_create(NULL, hash_shaders, match_shaders, + nuke_shaders, 0, ctx->malloc_fn, + ctx->free_fn, ctx->malloc_data); + + if (ctx->linker_cache == NULL) + { + out_of_memory(); + return; + } // if + } // if + + MOJOSHADER_glProgram *program = NULL; + BoundShaders shaders; + shaders.vertex = v; + shaders.fragment = p; + + const void *val = NULL; + if (hash_find(ctx->linker_cache, &shaders, &val)) + program = (MOJOSHADER_glProgram *) val; + else + { + program = MOJOSHADER_glLinkProgram(v, p); + if (program == NULL) + return; + + BoundShaders *item = (BoundShaders *) Malloc(sizeof (BoundShaders)); + if (item == NULL) + { + MOJOSHADER_glDeleteProgram(program); + return; + } // if + + memcpy(item, &shaders, sizeof (BoundShaders)); + if (hash_insert(ctx->linker_cache, item, program) != 1) + { + Free(item); + MOJOSHADER_glDeleteProgram(program); + out_of_memory(); + return; + } // if + } // else + + assert(program != NULL); + MOJOSHADER_glBindProgram(program); +} // MOJOSHADER_glBindShaders + + +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); + ctx->generation++; + } // if +} // MOJOSHADER_glSetVertexShaderUniformF + + +void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, 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(data, ctx->vs_reg_file_f + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetVertexShaderUniformF + + +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); + ctx->generation++; + } // if +} // MOJOSHADER_glSetVertexShaderUniformI + + +void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, 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(data, ctx->vs_reg_file_i + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetVertexShaderUniformI + + +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) + { + uint8 *wptr = ctx->vs_reg_file_b + idx; + uint8 *endptr = wptr + minuint(maxregs - idx, bcount); + while (wptr != endptr) + *(wptr++) = *(data++) ? 1 : 0; + ctx->generation++; + } // if +} // MOJOSHADER_glSetVertexShaderUniformB + + +void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data, + unsigned int bcount) +{ + const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_f) / 4; + if (idx < maxregs) + { + uint8 *rptr = ctx->vs_reg_file_b + idx; + uint8 *endptr = rptr + minuint(maxregs - idx, bcount); + while (rptr != endptr) + *(data++) = (int) *(rptr++); + } // if +} // MOJOSHADER_glGetVertexShaderUniformB + + +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); + ctx->generation++; + } // if +} // MOJOSHADER_glSetPixelShaderUniformF + + +void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, 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(data, ctx->ps_reg_file_f + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetPixelShaderUniformF + + +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); + ctx->generation++; + } // if +} // MOJOSHADER_glSetPixelShaderUniformI + + +void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, 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(data, ctx->ps_reg_file_i + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetPixelShaderUniformI + + +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) + { + uint8 *wptr = ctx->ps_reg_file_b + idx; + uint8 *endptr = wptr + minuint(maxregs - idx, bcount); + while (wptr != endptr) + *(wptr++) = *(data++) ? 1 : 0; + ctx->generation++; + } // if +} // MOJOSHADER_glSetPixelShaderUniformB + + +void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data, + unsigned int bcount) +{ + const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_f) / 4; + if (idx < maxregs) + { + uint8 *rptr = ctx->ps_reg_file_b + idx; + uint8 *endptr = rptr + minuint(maxregs - idx, bcount); + while (rptr != endptr) + *(data++) = (int) *(rptr++); + } // if +} // MOJOSHADER_glGetPixelShaderUniformB + + +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; + GLint 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. + + // this happens 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); + + // flag this array as in use, so we can enable it later. + ctx->want_attr[gl_index] = 1; + if (ctx->max_attrs < (gl_index + 1)) + ctx->max_attrs = gl_index + 1; +} // MOJOSHADER_glSetVertexAttribute + + +void MOJOSHADER_glSetVertexPreshaderUniformF(unsigned int idx, + const float *data, + unsigned int vec4n) +{ + MOJOSHADER_glProgram *program = ctx->bound_program; + if (program == NULL) + return; // nothing to do. + + const uint maxregs = program->vs_preshader_reg_count; + if (idx < maxregs) + { + assert(sizeof (GLfloat) == sizeof (float)); + const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; + memcpy(program->vs_preshader_regs + (idx * 4), data, cpy); + program->generation = ctx->generation-1; + } // if +} // MOJOSHADER_glSetVertexPreshaderUniformF + + +void MOJOSHADER_glGetVertexPreshaderUniformF(unsigned int idx, float *data, + unsigned int vec4n) +{ + MOJOSHADER_glProgram *program = ctx->bound_program; + if (program == NULL) + return; // nothing to do. + + const uint maxregs = program->vs_preshader_reg_count; + if (idx < maxregs) + { + assert(sizeof (GLfloat) == sizeof (float)); + const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; + memcpy(data, program->vs_preshader_regs + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetVertexPreshaderUniformF + + +void MOJOSHADER_glSetPixelPreshaderUniformF(unsigned int idx, + const float *data, + unsigned int vec4n) +{ + MOJOSHADER_glProgram *program = ctx->bound_program; + if (program == NULL) + return; // nothing to do. + + const uint maxregs = program->ps_preshader_reg_count; + if (idx < maxregs) + { + assert(sizeof (GLfloat) == sizeof (float)); + const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; + memcpy(program->ps_preshader_regs + (idx * 4), data, cpy); + program->generation = ctx->generation-1; + } // if +} // MOJOSHADER_glSetPixelPreshaderUniformF + + +void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data, + unsigned int vec4n) +{ + MOJOSHADER_glProgram *program = ctx->bound_program; + if (program == NULL) + return; // nothing to do. + + const uint maxregs = program->ps_preshader_reg_count; + if (idx < maxregs) + { + assert(sizeof (GLfloat) == sizeof (float)); + const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; + memcpy(data, program->ps_preshader_regs + (idx * 4), cpy); + } // if +} // MOJOSHADER_glGetPixelPreshaderUniformF + + +void MOJOSHADER_glProgramReady(void) +{ + MOJOSHADER_glProgram *program = ctx->bound_program; + + if (program == NULL) + return; // nothing to do. + + // Toggle vertex attribute arrays on/off, based on our needs. + update_enabled_arrays(); + + if (program->uses_pointsize != ctx->pointsize_enabled) + { + if (program->uses_pointsize) + ctx->glEnable(GL_PROGRAM_POINT_SIZE); + else + ctx->glDisable(GL_PROGRAM_POINT_SIZE); + ctx->pointsize_enabled = program->uses_pointsize; + } // if + + // push Uniforms to the program from our register files... + if ((program->uniform_count) && (program->generation != ctx->generation)) + { + // vertex shader uniforms come first in program->uniforms array. + const uint32 count = program->uniform_count; + const GLfloat *srcf = ctx->vs_reg_file_f; + const GLint *srci = ctx->vs_reg_file_i; + const uint8 *srcb = ctx->vs_reg_file_b; + MOJOSHADER_shaderType shader_type = MOJOSHADER_TYPE_VERTEX; + GLfloat *dstf = program->vs_uniforms_float4; + GLint *dsti = program->vs_uniforms_int4; + GLint *dstb = program->vs_uniforms_bool; + const MOJOSHADER_preshader *preshader = NULL; + uint32 i; + + #if SUPPORT_PRESHADERS + int ran_preshader = 0; + if (program->vertex) + { + preshader = program->vertex->parseData->preshader; + if (preshader) + { + MOJOSHADER_runPreshader(preshader, program->vs_preshader_regs, + ctx->vs_reg_file_f); + ran_preshader = 1; + } // if + } // if + + if (program->fragment) + { + preshader = program->fragment->parseData->preshader; + if (preshader) + { + MOJOSHADER_runPreshader(preshader, program->ps_preshader_regs, + ctx->ps_reg_file_f); + ran_preshader = 1; + } // if + } // if + + if (ran_preshader) + ctx->generation++; + #endif + + for (i = 0; i < count; i++) + { + UniformMap *map = &program->uniforms[i]; + const MOJOSHADER_shaderType uniform_shader_type = map->shader_type; + const MOJOSHADER_uniform *u = map->uniform; + const MOJOSHADER_uniformType type = u->type; + const int index = u->index; + const int size = u->array_count ? u->array_count : 1; + + assert(!u->constant); + + // Did we switch from vertex to pixel (to geometry, etc)? + if (shader_type != uniform_shader_type) + { + // we start with vertex, move to pixel, then to geometry, etc. + // The array should always be sorted as such. + if (uniform_shader_type == MOJOSHADER_TYPE_PIXEL) + { + assert(shader_type == MOJOSHADER_TYPE_VERTEX); + srcf = ctx->ps_reg_file_f; + srci = ctx->ps_reg_file_i; + srcb = ctx->ps_reg_file_b; + dstf = program->ps_uniforms_float4; + dsti = program->ps_uniforms_int4; + dstb = program->ps_uniforms_bool; + } // if + else + { + // Should be ordered vertex, then pixel, then geometry. + assert(0 && "Unexpected shader type"); + } // else + + shader_type = uniform_shader_type; + } // if + + if (type == MOJOSHADER_UNIFORM_FLOAT) + { + const size_t count = 4 * size; + const GLfloat *f = &srcf[index * 4]; + memcpy(dstf, f, sizeof (GLfloat) * count); + dstf += count; + } // if + else if (type == MOJOSHADER_UNIFORM_INT) + { + const size_t count = 4 * size; + const GLint *i = &srci[index * 4]; + memcpy(dsti, i, sizeof (GLint) * count); + dsti += count; + } // else if + else if (type == MOJOSHADER_UNIFORM_BOOL) + { + const size_t count = size; + const uint8 *b = &srcb[index]; + size_t i; + for (i = 0; i < count; i++) + dstb[i] = (GLint) b[i]; + dstb += count; + } // else if + + // !!! FIXME: set constants that overlap the array. + } // for + + program->generation = ctx->generation; + + ctx->profilePushUniforms(); + } // if +} // MOJOSHADER_glProgramReady + + +void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program) +{ + program_unref(program); +} // MOJOSHADER_glDeleteProgram + + +void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader) +{ + // See if this was bound as an unlinked program anywhere... + if (ctx->linker_cache) + { + const void *key = NULL; + void *iter = NULL; + int morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); + while (morekeys) + { + const BoundShaders *shaders = (const BoundShaders *) key; + // Do this here so we don't confuse the iteration by removing... + morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); + if ((shaders->vertex == shader) || (shaders->fragment == shader)) + { + // Deletes the linked program, which will unref the shader. + hash_remove(ctx->linker_cache, shaders); + } // if + } // while + } // if + + 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, NULL); // !!! FIXME: is there a value to this? + if (ctx->linker_cache) + hash_destroy(ctx->linker_cache); + Free(ctx); + ctx = ((current_ctx == _ctx) ? NULL : current_ctx); +} // MOJOSHADER_glDestroyContext + +// end of mojoshader_opengl.c ... + |