X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fshader%2Fshader_api.c;h=01a237c525a231047b0f2d9e2936a0c82c806af4;hb=63d8a8417d68365cd10c11178516378411c09f87;hp=e2fedf7fa647410d5a092043129b159a87c2fb93;hpb=716239877823e13a6111b5cff1bf2b446a37a537;p=mesa.git diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index e2fedf7fa64..01a237c525a 100644 --- a/src/mesa/shader/shader_api.c +++ b/src/mesa/shader/shader_api.c @@ -1,6 +1,6 @@ /* * Mesa 3-D graphics library - * Version: 6.5.3 + * Version: 7.0 * * Copyright (C) 2004-2007 Brian Paul All Rights Reserved. * @@ -38,14 +38,14 @@ #include "glheader.h" #include "context.h" #include "hash.h" +#include "macros.h" #include "program.h" #include "prog_parameter.h" #include "prog_print.h" #include "prog_statevars.h" -#include "shader_api.h" - -#include "slang_compile.h" -#include "slang_link.h" +#include "shader/shader_api.h" +#include "shader/slang/slang_compile.h" +#include "shader/slang/slang_link.h" @@ -58,7 +58,7 @@ _mesa_new_shader_program(GLcontext *ctx, GLuint name) struct gl_shader_program *shProg; shProg = CALLOC_STRUCT(gl_shader_program); if (shProg) { - shProg->Type = GL_SHADER_PROGRAM; + shProg->Type = GL_SHADER_PROGRAM_MESA; shProg->Name = name; shProg->RefCount = 1; shProg->Attributes = _mesa_new_parameter_list(); @@ -67,18 +67,19 @@ _mesa_new_shader_program(GLcontext *ctx, GLuint name) } +/** + * Clear (free) the shader program state that gets produced by linking. + */ void -_mesa_free_shader_program_data(GLcontext *ctx, - struct gl_shader_program *shProg) +_mesa_clear_shader_program_data(GLcontext *ctx, + struct gl_shader_program *shProg) { - assert(shProg->Type == GL_SHADER_PROGRAM); - if (shProg->VertexProgram) { if (shProg->VertexProgram->Base.Parameters == shProg->Uniforms) { /* to prevent a double-free in the next call */ shProg->VertexProgram->Base.Parameters = NULL; } - _mesa_delete_program(ctx, &shProg->VertexProgram->Base); + ctx->Driver.DeleteProgram(ctx, &shProg->VertexProgram->Base); shProg->VertexProgram = NULL; } @@ -87,11 +88,10 @@ _mesa_free_shader_program_data(GLcontext *ctx, /* to prevent a double-free in the next call */ shProg->FragmentProgram->Base.Parameters = NULL; } - _mesa_delete_program(ctx, &shProg->FragmentProgram->Base); + ctx->Driver.DeleteProgram(ctx, &shProg->FragmentProgram->Base); shProg->FragmentProgram = NULL; } - if (shProg->Uniforms) { _mesa_free_parameter_list(shProg->Uniforms); shProg->Uniforms = NULL; @@ -104,15 +104,99 @@ _mesa_free_shader_program_data(GLcontext *ctx, } +/** + * Free all the data that hangs off a shader program object, but not the + * object itself. + */ +void +_mesa_free_shader_program_data(GLcontext *ctx, + struct gl_shader_program *shProg) +{ + GLuint i; + + assert(shProg->Type == GL_SHADER_PROGRAM_MESA); + + _mesa_clear_shader_program_data(ctx, shProg); + + if (shProg->Attributes) { + _mesa_free_parameter_list(shProg->Attributes); + shProg->Attributes = NULL; + } + + /* detach shaders */ + for (i = 0; i < shProg->NumShaders; i++) { + _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); + } + shProg->NumShaders = 0; + + if (shProg->Shaders) { + _mesa_free(shProg->Shaders); + shProg->Shaders = NULL; + } +} + +/** + * Free/delete a shader program object. + */ void _mesa_free_shader_program(GLcontext *ctx, struct gl_shader_program *shProg) { _mesa_free_shader_program_data(ctx, shProg); + if (shProg->Shaders) { + _mesa_free(shProg->Shaders); + shProg->Shaders = NULL; + } _mesa_free(shProg); } +/** + * Set ptr to point to shProg. + * If ptr is pointing to another object, decrement its refcount (and delete + * if refcount hits zero). + * Then set ptr to point to shProg, incrementing its refcount. + */ +/* XXX this could be static */ +void +_mesa_reference_shader_program(GLcontext *ctx, + struct gl_shader_program **ptr, + struct gl_shader_program *shProg) +{ + assert(ptr); + if (*ptr == shProg) { + /* no-op */ + return; + } + if (*ptr) { + /* Unreference the old shader program */ + GLboolean deleteFlag = GL_FALSE; + struct gl_shader_program *old = *ptr; + + ASSERT(old->RefCount > 0); + old->RefCount--; + /*printf("SHPROG DECR %p (%d) to %d\n", + (void*) old, old->Name, old->RefCount);*/ + deleteFlag = (old->RefCount == 0); + + if (deleteFlag) { + _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name); + _mesa_free_shader_program(ctx, old); + } + + *ptr = NULL; + } + assert(!*ptr); + + if (shProg) { + shProg->RefCount++; + /*printf("SHPROG INCR %p (%d) to %d\n", + (void*) shProg, shProg->Name, shProg->RefCount);*/ + *ptr = shProg; + } +} + + /** * Lookup a GLSL program object. */ @@ -127,7 +211,7 @@ _mesa_lookup_shader_program(GLcontext *ctx, GLuint name) * in the same hash table. Check the object's type to be sure it's * what we're expecting. */ - if (shProg && shProg->Type != GL_SHADER_PROGRAM) { + if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) { return NULL; } return shProg; @@ -164,7 +248,7 @@ _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh) _mesa_free(sh->InfoLog); for (i = 0; i < sh->NumPrograms; i++) { assert(sh->Programs[i]); - _mesa_delete_program(ctx, sh->Programs[i]); + ctx->Driver.DeleteProgram(ctx, sh->Programs[i]); } if (sh->Programs) _mesa_free(sh->Programs); @@ -172,6 +256,52 @@ _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh) } +/** + * Set ptr to point to sh. + * If ptr is pointing to another shader, decrement its refcount (and delete + * if refcount hits zero). + * Then set ptr to point to sh, incrementing its refcount. + */ +/* XXX this could be static */ +void +_mesa_reference_shader(GLcontext *ctx, struct gl_shader **ptr, + struct gl_shader *sh) +{ + assert(ptr); + if (*ptr == sh) { + /* no-op */ + return; + } + if (*ptr) { + /* Unreference the old shader */ + GLboolean deleteFlag = GL_FALSE; + struct gl_shader *old = *ptr; + + ASSERT(old->RefCount > 0); + old->RefCount--; + /*printf("SHADER DECR %p (%d) to %d\n", + (void*) old, old->Name, old->RefCount);*/ + deleteFlag = (old->RefCount == 0); + + if (deleteFlag) { + _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name); + _mesa_free_shader(ctx, old); + } + + *ptr = NULL; + } + assert(!*ptr); + + if (sh) { + /* reference new */ + sh->RefCount++; + /*printf("SHADER INCR %p (%d) to %d\n", + (void*) sh, sh->Name, sh->RefCount);*/ + *ptr = sh; + } +} + + /** * Lookup a GLSL shader object. */ @@ -185,9 +315,7 @@ _mesa_lookup_shader(GLcontext *ctx, GLuint name) * in the same hash table. Check the object's type to be sure it's * what we're expecting. */ - if (sh && sh->Type == GL_SHADER_PROGRAM) { - assert(sh->Type == GL_VERTEX_SHADER || - sh->Type == GL_FRAGMENT_SHADER); + if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) { return NULL; } return sh; @@ -196,15 +324,29 @@ _mesa_lookup_shader(GLcontext *ctx, GLuint name) } +/** + * Initialize context's shader state. + */ void _mesa_init_shader_state(GLcontext * ctx) { - ctx->Shader._FragmentShaderPresent = GL_FALSE; - ctx->Shader._VertexShaderPresent = GL_FALSE; + /* Device drivers may override these to control what kind of instructions + * are generated by the GLSL compiler. + */ + ctx->Shader.EmitHighLevelInstructions = GL_TRUE; + ctx->Shader.EmitCondCodes = GL_TRUE; /* XXX probably want GL_FALSE... */ + ctx->Shader.EmitComments = GL_FALSE; } - +/** + * Free the per-context shader-related state. + */ +void +_mesa_free_shader_state(GLcontext *ctx) +{ + _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram, NULL); +} /** @@ -228,6 +370,52 @@ copy_string(GLchar *dst, GLsizei maxLength, GLsizei *length, const GLchar *src) } +/** + * Return size (in floats) of the given GLSL type. + * See also _slang_sizeof_type_specifier(). + */ +static GLint +sizeof_glsl_type(GLenum type) +{ + switch (type) { + case GL_BOOL: + case GL_FLOAT: + case GL_INT: + return 1; + case GL_BOOL_VEC2: + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + return 2; + case GL_BOOL_VEC3: + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + return 3; + case GL_BOOL_VEC4: + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + return 4; + case GL_FLOAT_MAT2: + return 8; /* 2 rows of 4, actually */ + case GL_FLOAT_MAT3: + return 12; /* 3 rows of 4, actually */ + case GL_FLOAT_MAT4: + return 16; + case GL_FLOAT_MAT2x3: + return 8; /* 2 rows of 4, actually */ + case GL_FLOAT_MAT2x4: + return 8; + case GL_FLOAT_MAT3x2: + return 12; /* 3 rows of 4, actually */ + case GL_FLOAT_MAT3x4: + return 12; + case GL_FLOAT_MAT4x2: + return 16; /* 4 rows of 4, actually */ + case GL_FLOAT_MAT4x3: + return 16; /* 4 rows of 4, actually */ + default: + return 0; /* error */ + } +} /** @@ -239,7 +427,7 @@ _mesa_attach_shader(GLcontext *ctx, GLuint program, GLuint shader) struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); - const GLuint n = shProg->NumShaders; + GLuint n; GLuint i; if (!shProg || !sh) { @@ -248,6 +436,8 @@ _mesa_attach_shader(GLcontext *ctx, GLuint program, GLuint shader) return; } + n = shProg->NumShaders; + for (i = 0; i < n; i++) { if (shProg->Shaders[i] == sh) { /* already attached */ @@ -266,8 +456,8 @@ _mesa_attach_shader(GLcontext *ctx, GLuint program, GLuint shader) } /* append */ - shProg->Shaders[n] = sh; - sh->RefCount++; + shProg->Shaders[n] = NULL; /* since realloc() didn't zero the new space */ + _mesa_reference_shader(ctx, &shProg->Shaders[n], sh); shProg->NumShaders++; } @@ -295,7 +485,13 @@ _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index, return; } - oldIndex = _mesa_get_attrib_location(ctx, program, name); + if (shProg->LinkStatus) { + /* get current index/location for the attribute */ + oldIndex = _mesa_get_attrib_location(ctx, program, name); + } + else { + oldIndex = -1; + } /* this will replace the current value if it's already in the list */ i = _mesa_add_attribute(shProg->Attributes, name, size, index); @@ -303,14 +499,12 @@ _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index, _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindAttribLocation"); } - if (shProg->VertexProgram && oldIndex >= 0) { + if (shProg->VertexProgram && oldIndex >= 0 && oldIndex != index) { + /* If the index changed, need to search/replace references to that attribute + * in the vertex program. + */ _slang_remap_attribute(&shProg->VertexProgram->Base, oldIndex, index); } - -#if 0 - printf("===== post BindAttrib:\n"); - _mesa_print_program(&shProg->VertexProgram->Base); -#endif } @@ -349,13 +543,27 @@ _mesa_create_program(GLcontext *ctx) _mesa_HashInsert(ctx->Shared->ShaderObjects, name, shProg); + assert(shProg->RefCount == 1); + return name; } +/** + * Named w/ "2" to indicate OpenGL 2.x vs GL_ARB_fragment_programs's + * DeleteProgramARB. + */ void _mesa_delete_program2(GLcontext *ctx, GLuint name) { + /* + * NOTE: deleting shaders/programs works a bit differently than + * texture objects (and buffer objects, etc). Shader/program + * handles/IDs exist in the hash table until the object is really + * deleted (refcount==0). With texture objects, the handle/ID is + * removed from the hash table in glDeleteTextures() while the tex + * object itself might linger until its refcount goes to zero. + */ struct gl_shader_program *shProg; shProg = _mesa_lookup_shader_program(ctx, name); @@ -364,16 +572,10 @@ _mesa_delete_program2(GLcontext *ctx, GLuint name) return; } - /* always remove from hash table */ - _mesa_HashRemove(ctx->Shared->ShaderObjects, name); - shProg->DeletePending = GL_TRUE; - /* decrement refcount, delete if zero */ - shProg->RefCount--; - if (shProg->RefCount <= 0) { - _mesa_free_shader_program(ctx, shProg); - } + /* effectively, decr shProg's refcount */ + _mesa_reference_shader_program(ctx, &shProg, NULL); } @@ -386,10 +588,9 @@ _mesa_delete_shader(GLcontext *ctx, GLuint shader) } sh->DeletePending = GL_TRUE; - sh->RefCount--; - if (sh->RefCount <= 0) { - _mesa_free_shader(ctx, sh); - } + + /* effectively, decr sh's refcount */ + _mesa_reference_shader(ctx, &sh, NULL); } @@ -398,7 +599,7 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader) { struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); - const GLuint n = shProg->NumShaders; + GLuint n; GLuint i, j; if (!shProg) { @@ -407,12 +608,15 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader) return; } + n = shProg->NumShaders; + for (i = 0; i < n; i++) { if (shProg->Shaders[i]->Name == shader) { - struct gl_shader **newList; /* found it */ + struct gl_shader **newList; - shProg->Shaders[i]->RefCount--; + /* derefernce */ + _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); /* alloc new, smaller array */ newList = (struct gl_shader **) @@ -428,9 +632,20 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader) newList[j++] = shProg->Shaders[i]; _mesa_free(shProg->Shaders); - /* XXX refcounting! */ - shProg->Shaders = newList; + shProg->NumShaders = n - 1; + +#ifdef DEBUG + /* sanity check */ + { + for (j = 0; j < shProg->NumShaders; j++) { + assert(shProg->Shaders[j]->Type == GL_VERTEX_SHADER || + shProg->Shaders[j]->Type == GL_FRAGMENT_SHADER); + assert(shProg->Shaders[j]->RefCount > 0); + } + } +#endif + return; } } @@ -454,12 +669,12 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index, GLint sz; if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform"); + _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib"); return; } if (!shProg->Attributes || index >= shProg->Attributes->NumParameters) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)"); + _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)"); return; } @@ -467,9 +682,9 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index, shProg->Attributes->Parameters[index].Name); sz = shProg->Attributes->Parameters[index].Size; if (size) - *size = sz; - if (type) - *type = vec_types[sz]; /* XXX this is a temporary hack */ + *size = 1; /* attributes may not be arrays */ + if (type && sz > 0 && sz <= 4) /* XXX this is a temporary hack */ + *type = vec_types[sz - 1]; } @@ -481,12 +696,9 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLchar *nameOut) { - static const GLenum vec_types[] = { - GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4 - }; struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); - GLint sz; + GLuint ind, j; if (!shProg) { _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform"); @@ -498,13 +710,29 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, return; } - copy_string(nameOut, maxLength, length, - shProg->Uniforms->Parameters[index].Name); - sz = shProg->Uniforms->Parameters[index].Size; - if (size) - *size = sz; - if (type) - *type = vec_types[sz]; /* XXX this is a temporary hack */ + ind = 0; + for (j = 0; j < shProg->Uniforms->NumParameters; j++) { + if (shProg->Uniforms->Parameters[j].Type == PROGRAM_UNIFORM || + shProg->Uniforms->Parameters[j].Type == PROGRAM_SAMPLER) { + if (ind == index) { + GLuint uSize = shProg->Uniforms->Parameters[j].Size; + GLenum uType = shProg->Uniforms->Parameters[j].DataType; + /* found it */ + copy_string(nameOut, maxLength, length, + shProg->Uniforms->Parameters[j].Name); + if (size) { + /* convert from floats to 'type' (eg: sizeof(mat4x4)=1) */ + *size = uSize / sizeof_glsl_type(uType); + } + if (type) + *type = uType; + return; + } + ind++; + } + } + + _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)"); } @@ -609,7 +837,7 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, *params = shProg->Validated; break; case GL_INFO_LOG_LENGTH: - *params = shProg->InfoLog ? strlen(shProg->InfoLog) : 0; + *params = shProg->InfoLog ? strlen(shProg->InfoLog) + 1 : 0; break; case GL_ATTACHED_SHADERS: *params = shProg->NumShaders; @@ -618,13 +846,20 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, *params = shProg->Attributes ? shProg->Attributes->NumParameters : 0; break; case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: - *params = _mesa_parameter_longest_name(shProg->Attributes); + *params = _mesa_longest_parameter_name(shProg->Attributes, + PROGRAM_INPUT) + 1; break; case GL_ACTIVE_UNIFORMS: - *params = shProg->Uniforms ? shProg->Uniforms->NumParameters : 0; + *params + = _mesa_num_parameters_of_type(shProg->Uniforms, PROGRAM_UNIFORM) + + _mesa_num_parameters_of_type(shProg->Uniforms, PROGRAM_SAMPLER); break; case GL_ACTIVE_UNIFORM_MAX_LENGTH: - *params = _mesa_parameter_longest_name(shProg->Uniforms); + *params = MAX2( + _mesa_longest_parameter_name(shProg->Uniforms, PROGRAM_UNIFORM), + _mesa_longest_parameter_name(shProg->Uniforms, PROGRAM_SAMPLER)); + if (*params > 0) + (*params)++; /* add one for terminating zero */ break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramiv(pname)"); @@ -654,10 +889,10 @@ _mesa_get_shaderiv(GLcontext *ctx, GLuint name, GLenum pname, GLint *params) *params = shader->CompileStatus; break; case GL_INFO_LOG_LENGTH: - *params = shader->InfoLog ? strlen(shader->InfoLog) : 0; + *params = shader->InfoLog ? strlen(shader->InfoLog) + 1 : 0; break; case GL_SHADER_SOURCE_LENGTH: - *params = shader->Source ? strlen((char *) shader->Source) : 0; + *params = shader->Source ? strlen((char *) shader->Source) + 1 : 0; break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetShaderiv(pname)"); @@ -721,9 +956,40 @@ _mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, if (shProg) { GLint i; if (location >= 0 && location < shProg->Uniforms->NumParameters) { - for (i = 0; i < shProg->Uniforms->Parameters[location].Size; i++) { - params[i] = shProg->Uniforms->ParameterValues[location][i]; + GLuint uSize; + GLenum uType; + GLint rows = 0; + uType = shProg->Uniforms->Parameters[location].DataType; + uSize = sizeof_glsl_type(uType); + /* Matrix types need special handling, because they span several + * parameters, and may also not be fully packed. + */ + switch (shProg->Uniforms->Parameters[location].DataType) { + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT4x2: + rows = 2; + break; + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT4x3: + rows = 3; + break; + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4: + rows = 4; + } + if (rows != 0) { + GLint r, c; + for (c = 0, i = 0; c * 4 < uSize; c++) + for (r = 0; r < rows; r++, i++) + params[i] = shProg->Uniforms->ParameterValues[location + c][r]; } + else + for (i = 0; i < uSize; i++) { + params[i] = shProg->Uniforms->ParameterValues[location][i]; + } } else { _mesa_error(ctx, GL_INVALID_VALUE, "glGetUniformfv(location)"); @@ -832,7 +1098,7 @@ _mesa_link_program(GLcontext *ctx, GLuint program) return; } - _slang_link2(ctx, program, shProg); + _slang_link(ctx, program, shProg); } @@ -842,30 +1108,29 @@ _mesa_link_program(GLcontext *ctx, GLuint program) void _mesa_use_program(GLcontext *ctx, GLuint program) { - /* unbind old */ - if (ctx->Shader.CurrentProgram) { - ctx->Shader.CurrentProgram->RefCount--; - if (ctx->Shader.CurrentProgram->RefCount <= 0) { - _mesa_free_shader_program(ctx, ctx->Shader.CurrentProgram); - } - ctx->Shader.CurrentProgram = NULL; + struct gl_shader_program *shProg; + + if (ctx->Shader.CurrentProgram && + ctx->Shader.CurrentProgram->Name == program) { + /* no-op */ + return; } + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + if (program) { - struct gl_shader_program *shProg; shProg = _mesa_lookup_shader_program(ctx, program); if (!shProg) { _mesa_error(ctx, GL_INVALID_VALUE, "glUseProgramObjectARB(programObj)"); return; } - ctx->Shader.CurrentProgram = shProg; - shProg->RefCount++; } else { - /* don't use a shader program */ - ctx->Shader.CurrentProgram = NULL; - } + shProg = NULL; + } + + _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram, shProg); } @@ -877,69 +1142,138 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, const GLvoid *values, GLenum type) { struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; - GLfloat *uniformVal; + GLint elems, i, k; + GLenum uType; + GLsizei maxCount; if (!shProg || !shProg->LinkStatus) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(program not linked)"); return; } + if (location == -1) + return; /* The standard specifies this as a no-op */ + + /* The spec says this is GL_INVALID_OPERATION, although it seems like it + * ought to be GL_INVALID_VALUE + */ if (location < 0 || location >= (GLint) shProg->Uniforms->NumParameters) { - _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location)"); + _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location)"); return; } FLUSH_VERTICES(ctx, _NEW_PROGRAM); - uniformVal = shProg->Uniforms->ParameterValues[location]; - - if (type == GL_INT || - type == GL_INT_VEC2 || - type == GL_INT_VEC3 || - type == GL_INT_VEC4) { - const GLint *iValues = (const GLint *) values; - switch (type) { - case GL_INT_VEC4: - uniformVal[3] = (GLfloat) iValues[3]; - /* fall-through */ - case GL_INT_VEC3: - uniformVal[2] = (GLfloat) iValues[2]; - /* fall-through */ - case GL_INT_VEC2: - uniformVal[1] = (GLfloat) iValues[1]; - /* fall-through */ - case GL_INT: - uniformVal[0] = (GLfloat) iValues[0]; - break; - default: - _mesa_problem(ctx, "Invalid type in _mesa_uniform"); + uType = shProg->Uniforms->Parameters[location].DataType; + /* + * If we're setting a sampler, we must use glUniformi1()! + */ + if (shProg->Uniforms->Parameters[location].Type == PROGRAM_SAMPLER) { + GLint unit; + if (type != GL_INT || count != 1) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glUniform(only glUniform1i can be used " + "to set sampler uniforms)"); + return; + } + /* check that the sampler (tex unit index) is legal */ + unit = ((GLint *) values)[0]; + if (unit >= ctx->Const.MaxTextureImageUnits) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glUniform1(invalid sampler/tex unit index)"); return; } } - else { - const GLfloat *fValues = (const GLfloat *) values; - switch (type) { - case GL_FLOAT_VEC4: - uniformVal[3] = fValues[3]; - /* fall-through */ - case GL_FLOAT_VEC3: - uniformVal[2] = fValues[2]; - /* fall-through */ - case GL_FLOAT_VEC2: - uniformVal[1] = fValues[1]; - /* fall-through */ - case GL_FLOAT: - uniformVal[0] = fValues[0]; + + if (count < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(count < 0)"); + return; + } + + switch (type) { + case GL_FLOAT: + case GL_INT: + elems = 1; + break; + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + elems = 2; + break; + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + elems = 3; + break; + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + elems = 4; + break; + default: + _mesa_problem(ctx, "Invalid type in _mesa_uniform"); + return; + } + + /* OpenGL requires types to match exactly, except that one can convert + * float or int array to boolean array. + */ + switch (uType) + { + case GL_BOOL: + case GL_BOOL_VEC2: + case GL_BOOL_VEC3: + case GL_BOOL_VEC4: + if (elems != sizeof_glsl_type(uType)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(count mismatch)"); + } + break; + case PROGRAM_SAMPLER: break; default: - _mesa_problem(ctx, "Invalid type in _mesa_uniform"); - return; + if (shProg->Uniforms->Parameters[location].Type != PROGRAM_SAMPLER + && uType != type) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); + } + break; + } + + /* XXX if this is a base type, then count must equal 1. However, we + * don't have enough information from the compiler to distinguish a + * base type from a 1-element array of that type. The standard allows + * count to overrun an array, in which case the overflow is ignored. + */ + maxCount = shProg->Uniforms->Parameters[location].Size / elems; + if (count > maxCount) count = maxCount; + + for (k = 0; k < count; k++) { + GLfloat *uniformVal = shProg->Uniforms->ParameterValues[location + k]; + if (type == GL_INT || + type == GL_INT_VEC2 || + type == GL_INT_VEC3 || + type == GL_INT_VEC4) { + const GLint *iValues = ((const GLint *) values) + k * elems; + for (i = 0; i < elems; i++) { + uniformVal[i] = (GLfloat) iValues[i]; + } + } + else { + const GLfloat *fValues = ((const GLfloat *) values) + k * elems; + for (i = 0; i < elems; i++) { + uniformVal[i] = fValues[i]; + } + } + if (uType == GL_BOOL || + uType == GL_BOOL_VEC2 || + uType == GL_BOOL_VEC3 || + uType == GL_BOOL_VEC4) { + for (i = 0; i < elems; i++) + uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; } } if (shProg->Uniforms->Parameters[location].Type == PROGRAM_SAMPLER) { - _slang_resolve_samplers(shProg, &shProg->VertexProgram->Base); - _slang_resolve_samplers(shProg, &shProg->FragmentProgram->Base); + if (shProg->VertexProgram) + _slang_resolve_samplers(shProg, &shProg->VertexProgram->Base); + if (shProg->FragmentProgram) + _slang_resolve_samplers(shProg, &shProg->FragmentProgram->Base); FLUSH_VERTICES(ctx, _NEW_TEXTURE); } } @@ -953,20 +1287,30 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, GLenum matrixType, GLint location, GLsizei count, GLboolean transpose, const GLfloat *values) { + GLsizei maxCount, i; struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; if (!shProg || !shProg->LinkStatus) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(program not linked)"); return; } - if (location < 0 || location >= shProg->Uniforms->NumParameters) { - _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); + if (location == -1) + return; /* The standard specifies this as a no-op */ + /* The spec says this is GL_INVALID_OPERATION, although it seems like it + * ought to be GL_INVALID_VALUE + */ + if (location < 0 || location >= (GLint) shProg->Uniforms->NumParameters) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)"); return; } if (values == NULL) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix"); return; } + if (count < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(count < 0)"); + return; + } FLUSH_VERTICES(ctx, _NEW_PROGRAM); @@ -975,23 +1319,30 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, * the rows. */ /* XXXX need to test 3x3 and 2x2 matrices... */ - if (transpose) { - GLuint row, col; - for (col = 0; col < cols; col++) { - GLfloat *v = shProg->Uniforms->ParameterValues[location + col]; - for (row = 0; row < rows; row++) { - v[row] = values[col * rows + row]; + maxCount = shProg->Uniforms->Parameters[location].Size / (4 * cols); + if (count > maxCount) + count = maxCount; + for (i = 0; i < count; i++) { + if (transpose) { + GLuint row, col; + for (col = 0; col < cols; col++) { + GLfloat *v = shProg->Uniforms->ParameterValues[location + col]; + for (row = 0; row < rows; row++) { + v[row] = values[row * cols + col]; + } } } - } - else { - GLuint row, col; - for (col = 0; col < cols; col++) { - GLfloat *v = shProg->Uniforms->ParameterValues[location + col]; - for (row = 0; row < rows; row++) { - v[row] = values[row * cols + col]; + else { + GLuint row, col; + for (col = 0; col < cols; col++) { + GLfloat *v = shProg->Uniforms->ParameterValues[location + col]; + for (row = 0; row < rows; row++) { + v[row] = values[col * rows + row]; + } } } + location += cols; + values += rows * cols; } }