X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fshader%2Fshader_api.c;h=0d65af9738f1237609f8552b5b940309a5bd38fc;hb=89ab66448e1bcd78caab6678261c2885dcff741c;hp=5ec03563ba2e4af807b6a9e3231466a05ca4b845;hpb=73c5505c13c42ba1ab95fa1d1003d5f9327428a5;p=mesa.git diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index 5ec03563ba2..0d65af9738f 100644 --- a/src/mesa/shader/shader_api.c +++ b/src/mesa/shader/shader_api.c @@ -1,8 +1,8 @@ /* * Mesa 3-D graphics library - * Version: 7.0 + * Version: 7.2 * - * Copyright (C) 2004-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2004-2008 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -35,10 +35,10 @@ */ -#include "glheader.h" -#include "context.h" -#include "hash.h" -#include "macros.h" +#include "main/glheader.h" +#include "main/context.h" +#include "main/hash.h" +#include "main/macros.h" #include "program.h" #include "prog_parameter.h" #include "prog_print.h" @@ -50,6 +50,11 @@ +#ifndef GL_PROGRAM_BINARY_LENGTH_OES +#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 +#endif + + /** * Allocate a new gl_shader_program object, initialize it. */ @@ -75,21 +80,8 @@ void _mesa_clear_shader_program_data(GLcontext *ctx, struct gl_shader_program *shProg) { - if (shProg->VertexProgram) { - /* Set ptr to NULL since the param list is shared with the - * original/unlinked program. - */ - shProg->VertexProgram->Base.Parameters = NULL; - _mesa_reference_vertprog(ctx, &shProg->VertexProgram, NULL); - } - - if (shProg->FragmentProgram) { - /* Set ptr to NULL since the param list is shared with the - * original/unlinked program. - */ - shProg->FragmentProgram->Base.Parameters = NULL; - _mesa_reference_fragprog(ctx, &shProg->FragmentProgram, NULL); - } + _mesa_reference_vertprog(ctx, &shProg->VertexProgram, NULL); + _mesa_reference_fragprog(ctx, &shProg->FragmentProgram, NULL); if (shProg->Uniforms) { _mesa_free_uniform_list(shProg->Uniforms); @@ -132,6 +124,11 @@ _mesa_free_shader_program_data(GLcontext *ctx, _mesa_free(shProg->Shaders); shProg->Shaders = NULL; } + + if (shProg->InfoLog) { + _mesa_free(shProg->InfoLog); + shProg->InfoLog = NULL; + } } @@ -142,10 +139,7 @@ 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); } @@ -174,8 +168,10 @@ _mesa_reference_shader_program(GLcontext *ctx, ASSERT(old->RefCount > 0); old->RefCount--; - /*printf("SHPROG DECR %p (%d) to %d\n", - (void*) old, old->Name, old->RefCount);*/ +#if 0 + printf("ShaderProgram %p ID=%u RefCount-- to %d\n", + (void *) old, old->Name, old->RefCount); +#endif deleteFlag = (old->RefCount == 0); if (deleteFlag) { @@ -189,8 +185,10 @@ _mesa_reference_shader_program(GLcontext *ctx, if (shProg) { shProg->RefCount++; - /*printf("SHPROG INCR %p (%d) to %d\n", - (void*) shProg, shProg->Name, shProg->RefCount);*/ +#if 0 + printf("ShaderProgram %p ID=%u RefCount++ to %d\n", + (void *) shProg, shProg->Name, shProg->RefCount); +#endif *ptr = shProg; } } @@ -219,6 +217,35 @@ _mesa_lookup_shader_program(GLcontext *ctx, GLuint name) } +/** + * As above, but record an error if program is not found. + */ +static struct gl_shader_program * +_mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name, + const char *caller) +{ + if (!name) { + _mesa_error(ctx, GL_INVALID_VALUE, caller); + return NULL; + } + else { + struct gl_shader_program *shProg = (struct gl_shader_program *) + _mesa_HashLookup(ctx->Shared->ShaderObjects, name); + if (!shProg) { + _mesa_error(ctx, GL_INVALID_VALUE, caller); + return NULL; + } + if (shProg->Type != GL_SHADER_PROGRAM_MESA) { + _mesa_error(ctx, GL_INVALID_OPERATION, caller); + return NULL; + } + return shProg; + } +} + + + + /** * Allocate a new gl_shader object, initialize it. */ @@ -240,17 +267,11 @@ _mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type) void _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh) { - GLuint i; if (sh->Source) _mesa_free((void *) sh->Source); if (sh->InfoLog) _mesa_free(sh->InfoLog); - for (i = 0; i < sh->NumPrograms; i++) { - assert(sh->Programs[i]); - ctx->Driver.DeleteProgram(ctx, sh->Programs[i]); - } - if (sh->Programs) - _mesa_free(sh->Programs); + _mesa_reference_program(ctx, &sh->Program, NULL); _mesa_free(sh); } @@ -323,6 +344,33 @@ _mesa_lookup_shader(GLcontext *ctx, GLuint name) } +/** + * As above, but record an error if shader is not found. + */ +static struct gl_shader * +_mesa_lookup_shader_err(GLcontext *ctx, GLuint name, const char *caller) +{ + if (!name) { + _mesa_error(ctx, GL_INVALID_VALUE, caller); + return NULL; + } + else { + struct gl_shader *sh = (struct gl_shader *) + _mesa_HashLookup(ctx->Shared->ShaderObjects, name); + if (!sh) { + _mesa_error(ctx, GL_INVALID_VALUE, caller); + return NULL; + } + if (sh->Type == GL_SHADER_PROGRAM_MESA) { + _mesa_error(ctx, GL_INVALID_OPERATION, caller); + return NULL; + } + return sh; + } +} + + + /** * Initialize context's shader state. */ @@ -369,24 +417,42 @@ copy_string(GLchar *dst, GLsizei maxLength, GLsizei *length, const GLchar *src) } +static GLboolean +_mesa_is_program(GLcontext *ctx, GLuint name) +{ + struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, name); + return shProg ? GL_TRUE : GL_FALSE; +} + + +static GLboolean +_mesa_is_shader(GLcontext *ctx, GLuint name) +{ + struct gl_shader *shader = _mesa_lookup_shader(ctx, name); + return shader ? GL_TRUE : GL_FALSE; +} + + /** * Called via ctx->Driver.AttachShader() */ static void _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 i; + struct gl_shader_program *shProg; + struct gl_shader *sh; + GLuint i, n; + + shProg = _mesa_lookup_shader_program_err(ctx, program, "glAttachShader"); + if (!shProg) + return; - if (!shProg || !sh) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glAttachShader(bad program or shader name)"); + sh = _mesa_lookup_shader_err(ctx, shader, "glAttachShader"); + if (!sh) { return; } + n = shProg->NumShaders; for (i = 0; i < n; i++) { if (shProg->Shaders[i] == sh) { /* already attached */ @@ -416,10 +482,9 @@ _mesa_get_attrib_location(GLcontext *ctx, GLuint program, const GLchar *name) { struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + = _mesa_lookup_shader_program_err(ctx, program, "glGetAttribLocation"); if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetAttribLocation"); return -1; } @@ -432,10 +497,14 @@ _mesa_get_attrib_location(GLcontext *ctx, GLuint program, if (!name) return -1; - if (shProg->Attributes) { - GLint i = _mesa_lookup_parameter_index(shProg->Attributes, -1, name); - if (i >= 0) { - return shProg->Attributes->Parameters[i].StateIndexes[0]; + if (shProg->VertexProgram) { + const struct gl_program_parameter_list *attribs = + shProg->VertexProgram->Base.Attributes; + if (attribs) { + GLint i = _mesa_lookup_parameter_index(attribs, -1, name); + if (i >= 0) { + return attribs->Parameters[i].StateIndexes[0]; + } } } return -1; @@ -446,13 +515,14 @@ static void _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index, const GLchar *name) { - struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + struct gl_shader_program *shProg; const GLint size = -1; /* unknown size */ - GLint i, oldIndex; + GLint i; + GLenum datatype = GL_FLOAT_VEC4; + shProg = _mesa_lookup_shader_program_err(ctx, program, + "glBindAttribLocation"); if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glBindAttribLocation(program)"); return; } @@ -465,26 +535,22 @@ _mesa_bind_attrib_location(GLcontext *ctx, GLuint program, GLuint index, return; } - if (shProg->LinkStatus) { - /* get current index/location for the attribute */ - oldIndex = _mesa_get_attrib_location(ctx, program, name); - } - else { - oldIndex = -1; + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, "glBindAttribLocation(index)"); + return; } /* this will replace the current value if it's already in the list */ - i = _mesa_add_attribute(shProg->Attributes, name, size, index); + i = _mesa_add_attribute(shProg->Attributes, name, size, datatype, index); if (i < 0) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindAttribLocation"); + return; } - 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); - } + /* + * Note that this attribute binding won't go into effect until + * glLinkProgram is called again. + */ } @@ -546,11 +612,9 @@ _mesa_delete_program2(GLcontext *ctx, GLuint name) */ struct gl_shader_program *shProg; - shProg = _mesa_lookup_shader_program(ctx, name); - if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteProgram(name)"); + shProg = _mesa_lookup_shader_program_err(ctx, name, "glDeleteProgram"); + if (!shProg) return; - } shProg->DeletePending = GL_TRUE; @@ -562,10 +626,11 @@ _mesa_delete_program2(GLcontext *ctx, GLuint name) static void _mesa_delete_shader(GLcontext *ctx, GLuint shader) { - struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); - if (!sh) { + struct gl_shader *sh; + + sh = _mesa_lookup_shader_err(ctx, shader, "glDeleteShader"); + if (!sh) return; - } sh->DeletePending = GL_TRUE; @@ -577,23 +642,22 @@ _mesa_delete_shader(GLcontext *ctx, GLuint shader) static void _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; + struct gl_shader_program *shProg; + GLuint n; GLuint i, j; - if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glDetachShader(bad program or shader name)"); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glDetachShader"); + if (!shProg) return; - } + + n = shProg->NumShaders; for (i = 0; i < n; i++) { if (shProg->Shaders[i]->Name == shader) { /* found it */ struct gl_shader **newList; - /* derefernce */ + /* release */ _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); /* alloc new, smaller array */ @@ -629,8 +693,97 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader) } /* not found */ - _mesa_error(ctx, GL_INVALID_VALUE, - "glDetachShader(shader not found)"); + { + GLenum err; + if (_mesa_is_shader(ctx, shader)) + err = GL_INVALID_OPERATION; + else if (_mesa_is_program(ctx, shader)) + err = GL_INVALID_OPERATION; + else + err = GL_INVALID_VALUE; + _mesa_error(ctx, err, "glDetachProgram(shader)"); + return; + } +} + + +static GLint +sizeof_glsl_type(GLenum type) +{ + switch (type) { + case GL_FLOAT: + case GL_INT: + case GL_BOOL: + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_SHADOW_ARB: + case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: + case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: + case GL_SAMPLER_CUBE_SHADOW_EXT: + return 1; + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_BOOL_VEC2: + return 2; + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_BOOL_VEC3: + return 3; + case GL_FLOAT_VEC4: + case GL_INT_VEC4: + case GL_BOOL_VEC4: + return 4; + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + return 8; /* two float[4] vectors */ + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + return 12; /* three float[4] vectors */ + case GL_FLOAT_MAT4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + return 16; /* four float[4] vectors */ + default: + _mesa_problem(NULL, "Invalid type in sizeof_glsl_type()"); + return 1; + } +} + + +static GLboolean +is_boolean_type(GLenum type) +{ + switch (type) { + case GL_BOOL: + case GL_BOOL_VEC2: + case GL_BOOL_VEC3: + case GL_BOOL_VEC4: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +static GLboolean +is_integer_type(GLenum type) +{ + switch (type) { + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + return GL_TRUE; + default: + return GL_FALSE; + } } @@ -639,30 +792,53 @@ _mesa_get_active_attrib(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; + const struct gl_program_parameter_list *attribs = NULL; + struct gl_shader_program *shProg; - if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib"); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveAttrib"); + if (!shProg) return; - } - if (!shProg->Attributes || index >= shProg->Attributes->NumParameters) { + if (shProg->VertexProgram) + attribs = shProg->VertexProgram->Base.Attributes; + + if (!attribs || index >= attribs->NumParameters) { _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveAttrib(index)"); return; } - copy_string(nameOut, maxLength, length, - shProg->Attributes->Parameters[index].Name); - sz = shProg->Attributes->Parameters[index].Size; + copy_string(nameOut, maxLength, length, attribs->Parameters[index].Name); + if (size) - *size = sz; + *size = attribs->Parameters[index].Size + / sizeof_glsl_type(attribs->Parameters[index].DataType); + if (type) - *type = vec_types[sz]; /* XXX this is a temporary hack */ + *type = attribs->Parameters[index].DataType; +} + + +static struct gl_program_parameter * +get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) +{ + const struct gl_program *prog; + GLint progPos; + + progPos = shProg->Uniforms->Uniforms[index].VertPos; + if (progPos >= 0) { + prog = &shProg->VertexProgram->Base; + } + else { + progPos = shProg->Uniforms->Uniforms[index].FragPos; + if (progPos >= 0) { + prog = &shProg->FragmentProgram->Base; + } + } + + if (!prog || progPos < 0) + return NULL; /* should never happen */ + + return &prog->Parameters->Parameters[progPos]; } @@ -674,15 +850,13 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLchar *nameOut) { - const struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + const struct gl_shader_program *shProg; const struct gl_program *prog; GLint progPos; - if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform"); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform"); + if (!shProg) return; - } if (!shProg->Uniforms || index >= shProg->Uniforms->NumUniforms) { _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniform(index)"); @@ -707,8 +881,8 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index, copy_string(nameOut, maxLength, length, prog->Parameters->Parameters[progPos].Name); if (size) - *size = prog->Parameters->Parameters[progPos].Size; - + *size = prog->Parameters->Parameters[progPos].Size + / sizeof_glsl_type(prog->Parameters->Parameters[progPos].DataType); if (type) *type = prog->Parameters->Parameters[progPos].DataType; } @@ -721,19 +895,16 @@ static void _mesa_get_attached_shaders(GLcontext *ctx, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj) { - struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + struct gl_shader_program *shProg = + _mesa_lookup_shader_program_err(ctx, program, "glGetAttachedShaders"); if (shProg) { - GLint i; - for (i = 0; i < maxCount && i < shProg->NumShaders; i++) { + GLuint i; + for (i = 0; i < (GLuint) maxCount && i < shProg->NumShaders; i++) { obj[i] = shProg->Shaders[i]->Name; } if (count) *count = i; } - else { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetAttachedShaders"); - } } @@ -765,6 +936,7 @@ static void _mesa_get_programiv(GLcontext *ctx, GLuint program, GLenum pname, GLint *params) { + const struct gl_program_parameter_list *attribs; struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, program); @@ -773,6 +945,11 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, return; } + if (shProg->VertexProgram) + attribs = shProg->VertexProgram->Base.Attributes; + else + attribs = NULL; + switch (pname) { case GL_DELETE_STATUS: *params = shProg->DeletePending; @@ -790,11 +967,10 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, *params = shProg->NumShaders; break; case GL_ACTIVE_ATTRIBUTES: - *params = shProg->Attributes ? shProg->Attributes->NumParameters : 0; + *params = attribs ? attribs->NumParameters : 0; break; case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: - *params = _mesa_longest_parameter_name(shProg->Attributes, - PROGRAM_INPUT) + 1; + *params = _mesa_longest_parameter_name(attribs, PROGRAM_INPUT) + 1; break; case GL_ACTIVE_UNIFORMS: *params = shProg->Uniforms ? shProg->Uniforms->NumUniforms : 0; @@ -804,6 +980,9 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, if (*params > 0) (*params)++; /* add one for terminating zero */ break; + case GL_PROGRAM_BINARY_LENGTH_OES: + *params = 0; + break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramiv(pname)"); return; @@ -814,10 +993,9 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program, static void _mesa_get_shaderiv(GLcontext *ctx, GLuint name, GLenum pname, GLint *params) { - struct gl_shader *shader = _mesa_lookup_shader(ctx, name); + struct gl_shader *shader = _mesa_lookup_shader_err(ctx, name, "glGetShaderiv"); if (!shader) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetShaderiv(shader)"); return; } @@ -878,28 +1056,97 @@ static void _mesa_get_shader_source(GLcontext *ctx, GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *sourceOut) { - struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); + struct gl_shader *sh; + sh = _mesa_lookup_shader_err(ctx, shader, "glGetShaderSource"); if (!sh) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetShaderSource(shader)"); return; } copy_string(sourceOut, maxLength, length, sh->Source); } +static void +get_matrix_dims(GLenum type, GLint *rows, GLint *cols) +{ + switch (type) { + case GL_FLOAT_MAT2: + *rows = *cols = 2; + break; + case GL_FLOAT_MAT2x3: + *rows = 3; + *cols = 2; + break; + case GL_FLOAT_MAT2x4: + *rows = 4; + *cols = 2; + break; + case GL_FLOAT_MAT3: + *rows = 3; + *cols = 3; + break; + case GL_FLOAT_MAT3x2: + *rows = 2; + *cols = 3; + break; + case GL_FLOAT_MAT3x4: + *rows = 4; + *cols = 3; + break; + case GL_FLOAT_MAT4: + *rows = 4; + *cols = 4; + break; + case GL_FLOAT_MAT4x2: + *rows = 2; + *cols = 4; + break; + case GL_FLOAT_MAT4x3: + *rows = 3; + *cols = 4; + break; + default: + *rows = *cols = 0; + } +} + + /** - * Called via ctx->Driver.GetUniformfv(). + * Determine the number of rows and columns occupied by a uniform + * according to its datatype. */ static void -_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, - GLfloat *params) +get_uniform_rows_cols(const struct gl_program_parameter *p, + GLint *rows, GLint *cols) +{ + get_matrix_dims(p->DataType, rows, cols); + if (*rows == 0 && *cols == 0) { + /* not a matrix type, probably a float or vector */ + *rows = p->Size / 4 + 1; + if (p->Size % 4 == 0) + *cols = 4; + else + *cols = p->Size % 4; + } +} + + +#define MAX_UNIFORM_ELEMENTS 16 + +/** + * Helper for GetUniformfv(), GetUniformiv() + * Returns number of elements written to 'params' output. + */ +static GLuint +get_uniformfv(GLcontext *ctx, GLuint program, GLint location, + GLfloat *params) { struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + = _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v"); if (shProg) { - if (location < shProg->Uniforms->NumUniforms) { - GLint progPos, i; - const struct gl_program *prog; + if (shProg->Uniforms && + location >= 0 && location < (GLint) shProg->Uniforms->NumUniforms) { + GLint progPos; + const struct gl_program *prog = NULL; progPos = shProg->Uniforms->Uniforms[location].VertPos; if (progPos >= 0) { @@ -912,48 +1159,162 @@ _mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, } } - for (i = 0; i < prog->Parameters->Parameters[progPos].Size; i++) { - params[i] = prog->Parameters->ParameterValues[progPos][i]; + ASSERT(prog); + if (prog) { + const struct gl_program_parameter *p = + &prog->Parameters->Parameters[progPos]; + GLint rows, cols, i, j, k; + + /* See uniformiv() below */ + assert(p->Size <= MAX_UNIFORM_ELEMENTS); + + get_uniform_rows_cols(p, &rows, &cols); + + k = 0; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++ ) { + params[k++] = prog->Parameters->ParameterValues[progPos+i][j]; + } + } + + return p->Size; } } else { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetUniformfv(location)"); + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(location)"); } } - else { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetUniformfv(program)"); + return 0; +} + + +/** + * Called via ctx->Driver.GetUniformfv(). + */ +static void +_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, + GLfloat *params) +{ + (void) get_uniformfv(ctx, program, location, params); +} + + +/** + * Called via ctx->Driver.GetUniformiv(). + */ +static void +_mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location, + GLint *params) +{ + GLfloat fparams[MAX_UNIFORM_ELEMENTS]; + GLuint n = get_uniformfv(ctx, program, location, fparams); + GLuint i; + assert(n <= MAX_UNIFORM_ELEMENTS); + for (i = 0; i < n; i++) { + params[i] = (GLint) fparams[i]; } } +/** + * The value returned by GetUniformLocation actually encodes two things: + * 1. the index into the prog->Uniforms[] array for the uniform + * 2. an offset in the prog->ParameterValues[] array for specifying array + * elements or structure fields. + * This function merges those two values. + */ +static void +merge_location_offset(GLint *location, GLint offset) +{ + *location = *location | (offset << 16); +} + + +/** + * Seperate the uniform location and parameter offset. See above. + */ +static void +split_location_offset(GLint *location, GLint *offset) +{ + *offset = (*location >> 16); + *location = *location & 0xffff; +} + + /** * Called via ctx->Driver.GetUniformLocation(). + * + * The return value will encode two values, the uniform location and an + * offset (used for arrays, structs). */ static GLint _mesa_get_uniform_location(GLcontext *ctx, GLuint program, const GLchar *name) { - struct gl_shader_program *shProg - = _mesa_lookup_shader_program(ctx, program); + GLint offset = 0, location = -1; + + struct gl_shader_program *shProg = + _mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); + if (!shProg) return -1; - return _mesa_lookup_uniform(shProg->Uniforms, name); -} + if (shProg->LinkStatus == GL_FALSE) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetUniformfv(program)"); + return -1; + } + /* XXX we should return -1 if the uniform was declared, but not + * actually used. + */ -static GLboolean -_mesa_is_program(GLcontext *ctx, GLuint name) -{ - struct gl_shader_program *shProg = _mesa_lookup_shader_program(ctx, name); - return shProg ? GL_TRUE : GL_FALSE; -} + /* XXX we need to be able to parse uniform names for structs and arrays + * such as: + * mymatrix[1] + * mystruct.field1 + */ + { + /* handle 1-dimension arrays here... */ + char *c = strchr(name, '['); + if (c) { + /* truncate name at [ */ + const GLint len = c - name; + GLchar *newName = _mesa_malloc(len + 1); + if (!newName) + return -1; /* out of mem */ + _mesa_memcpy(newName, name, len); + newName[len] = 0; + + location = _mesa_lookup_uniform(shProg->Uniforms, newName); + if (location >= 0) { + const GLint element = _mesa_atoi(c + 1); + if (element > 0) { + /* get type of the uniform array element */ + struct gl_program_parameter *p; + p = get_uniform_parameter(shProg, location); + if (p) { + GLint rows, cols; + get_matrix_dims(p->DataType, &rows, &cols); + if (rows < 1) + rows = 1; + offset = element * rows; + } + } + } -static GLboolean -_mesa_is_shader(GLcontext *ctx, GLuint name) -{ - struct gl_shader *shader = _mesa_lookup_shader(ctx, name); - return shader ? GL_TRUE : GL_FALSE; + _mesa_free(newName); + } + } + + if (location < 0) { + location = _mesa_lookup_uniform(shProg->Uniforms, name); + } + + if (location >= 0) { + merge_location_offset(&location, offset); + } + + return location; } @@ -964,11 +1325,11 @@ _mesa_is_shader(GLcontext *ctx, GLuint name) static void _mesa_shader_source(GLcontext *ctx, GLuint shader, const GLchar *source) { - struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); - if (!sh) { - _mesa_error(ctx, GL_INVALID_VALUE, "glShaderSource(shaderObj)"); + struct gl_shader *sh; + + sh = _mesa_lookup_shader_err(ctx, shader, "glShaderSource"); + if (!sh) return; - } /* free old shader source string and install new one */ if (sh->Source) { @@ -985,12 +1346,11 @@ _mesa_shader_source(GLcontext *ctx, GLuint shader, const GLchar *source) static void _mesa_compile_shader(GLcontext *ctx, GLuint shaderObj) { - struct gl_shader *sh = _mesa_lookup_shader(ctx, shaderObj); + struct gl_shader *sh; - if (!sh) { - _mesa_error(ctx, GL_INVALID_VALUE, "glCompileShader(shaderObj)"); + sh = _mesa_lookup_shader_err(ctx, shaderObj, "glCompileShader"); + if (!sh) return; - } sh->CompileStatus = _slang_compile(ctx, sh); } @@ -1004,11 +1364,9 @@ _mesa_link_program(GLcontext *ctx, GLuint program) { struct gl_shader_program *shProg; - shProg = _mesa_lookup_shader_program(ctx, program); - if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glLinkProgram(program)"); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glLinkProgram"); + if (!shProg) return; - } FLUSH_VERTICES(ctx, _NEW_PROGRAM); @@ -1033,10 +1391,12 @@ _mesa_use_program(GLcontext *ctx, GLuint program) FLUSH_VERTICES(ctx, _NEW_PROGRAM); if (program) { - shProg = _mesa_lookup_shader_program(ctx, program); + shProg = _mesa_lookup_shader_program_err(ctx, program, "glUseProgram"); if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glUseProgramObjectARB(programObj)"); + return; + } + if (!shProg->LinkStatus) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUseProgram"); return; } } @@ -1070,20 +1430,90 @@ update_textures_used(struct gl_program *prog) } +static GLboolean +is_sampler_type(GLenum type) +{ + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_SHADOW_ARB: + case GL_SAMPLER_1D_ARRAY_EXT: + case GL_SAMPLER_2D_ARRAY_EXT: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Check if the type given by userType is allowed to set a uniform of the + * target type. Generally, equivalence is required, but setting Boolean + * uniforms can be done with glUniformiv or glUniformfv. + */ +static GLboolean +compatible_types(GLenum userType, GLenum targetType) +{ + if (userType == targetType) + return GL_TRUE; + + if (targetType == GL_BOOL && (userType == GL_FLOAT || userType == GL_INT)) + return GL_TRUE; + + if (targetType == GL_BOOL_VEC2 && (userType == GL_FLOAT_VEC2 || + userType == GL_INT_VEC2)) + return GL_TRUE; + + if (targetType == GL_BOOL_VEC3 && (userType == GL_FLOAT_VEC3 || + userType == GL_INT_VEC3)) + return GL_TRUE; + + if (targetType == GL_BOOL_VEC4 && (userType == GL_FLOAT_VEC4 || + userType == GL_INT_VEC4)) + return GL_TRUE; + + if (is_sampler_type(targetType) && userType == GL_INT) + return GL_TRUE; + + return GL_FALSE; +} + + /** * Set the value of a program's uniform variable. * \param program the program whose uniform to update - * \param location the location/index of the uniform + * \param index the index of the program parameter for the uniform + * \param offset additional parameter slot offset (for arrays) * \param type the datatype of the uniform * \param count the number of uniforms to set * \param elems number of elements per uniform * \param values the new values */ static void -set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location, - GLenum type, GLint count, GLint elems, const void *values) +set_program_uniform(GLcontext *ctx, struct gl_program *program, + GLint index, GLint offset, + GLenum type, GLsizei count, GLint elems, + const void *values) { - if (program->Parameters->Parameters[location].Type == PROGRAM_SAMPLER) { + assert(offset >= 0); + + if (!compatible_types(type, + program->Parameters->Parameters[index].DataType)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); + return; + } + + if (index + offset > (GLint) program->Parameters->Size) { + /* out of bounds! */ + return; + } + + if (program->Parameters->Parameters[index].Type == PROGRAM_SAMPLER) { /* This controls which texture unit which is used by a sampler */ GLuint texUnit, sampler; @@ -1095,7 +1525,7 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location, return; } - sampler = (GLuint) program->Parameters->ParameterValues[location][0]; + sampler = (GLuint) program->Parameters->ParameterValues[index][0]; texUnit = ((GLuint *) values)[0]; /* check that the sampler (tex unit index) is legal */ @@ -1113,19 +1543,20 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location, } else { /* ordinary uniform variable */ - GLint k, i; + GLsizei k, i; + GLint slots = (program->Parameters->Parameters[index].Size + 3) / 4; - if (count * elems > program->Parameters->Parameters[location].Size) { + if (count * elems > (GLint) program->Parameters->Parameters[index].Size) { _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(count too large)"); return; } + if (count > slots) + count = slots; + for (k = 0; k < count; k++) { - GLfloat *uniformVal = program->Parameters->ParameterValues[location + k]; - if (type == GL_INT || - type == GL_INT_VEC2 || - type == GL_INT_VEC3 || - type == GL_INT_VEC4) { + GLfloat *uniformVal = program->Parameters->ParameterValues[index + offset + k]; + if (is_integer_type(type)) { const GLint *iValues = ((const GLint *) values) + k * elems; for (i = 0; i < elems; i++) { uniformVal[i] = (GLfloat) iValues[i]; @@ -1137,6 +1568,13 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program, GLint location, uniformVal[i] = fValues[i]; } } + + /* if the uniform is bool-valued, convert to 1.0 or 0.0 */ + if (is_boolean_type(program->Parameters->Parameters[index].DataType)) { + for (i = 0; i < elems; i++) { + uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; + } + } } } } @@ -1150,13 +1588,18 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, const GLvoid *values, GLenum type) { struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; - GLint elems; + GLint elems, offset; 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 */ + + split_location_offset(&location, &offset); + if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location)"); return; @@ -1195,69 +1638,103 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, * shader. We may need to update one or both shader's uniform here: */ if (shProg->VertexProgram) { - GLint loc = shProg->Uniforms->Uniforms[location].VertPos; - if (loc >= 0) { + /* convert uniform location to program parameter index */ + GLint index = shProg->Uniforms->Uniforms[location].VertPos; + if (index >= 0) { set_program_uniform(ctx, &shProg->VertexProgram->Base, - loc, type, count, elems, values); + index, offset, type, count, elems, values); } } if (shProg->FragmentProgram) { - GLint loc = shProg->Uniforms->Uniforms[location].FragPos; - if (loc >= 0) { + /* convert uniform location to program parameter index */ + GLint index = shProg->Uniforms->Uniforms[location].FragPos; + if (index >= 0) { set_program_uniform(ctx, &shProg->FragmentProgram->Base, - loc, type, count, elems, values); + index, offset, type, count, elems, values); } } } +/** + * Set a matrix-valued program parameter. + */ static void set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program, - GLuint location, GLuint rows, GLuint cols, + GLuint index, GLuint offset, + GLuint count, GLuint rows, GLuint cols, GLboolean transpose, const GLfloat *values) { + GLuint mat, row, col; + GLuint dst = index + offset, src = 0; + GLint nr, nc; + + /* check that the number of rows, columns is correct */ + get_matrix_dims(program->Parameters->Parameters[index].DataType, &nr, &nc); + if (rows != nr || cols != nc) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glUniformMatrix(matrix size mismatch)"); + return; + } + + if (index + offset > program->Parameters->Size) { + /* out of bounds! */ + return; + } + /* * Note: the _columns_ of a matrix are stored in program registers, not - * the rows. + * the rows. So, the loops below look a little funny. + * XXX could optimize this a bit... */ - /* XXXX need to test 3x3 and 2x2 matrices... */ - if (transpose) { - GLuint row, col; - for (col = 0; col < cols; col++) { - GLfloat *v = program->Parameters->ParameterValues[location + col]; - for (row = 0; row < rows; row++) { - v[row] = values[row * cols + col]; - } - } - } - else { - GLuint row, col; + + /* loop over matrices */ + for (mat = 0; mat < count; mat++) { + + /* each matrix: */ for (col = 0; col < cols; col++) { - GLfloat *v = program->Parameters->ParameterValues[location + col]; + GLfloat *v = program->Parameters->ParameterValues[dst]; for (row = 0; row < rows; row++) { - v[row] = values[col * rows + row]; + if (transpose) { + v[row] = values[src + row * cols + col]; + } + else { + v[row] = values[src + col * rows + row]; + } } + dst++; } + + src += rows * cols; /* next matrix */ } } /** * Called by ctx->Driver.UniformMatrix(). + * Note: cols=2, rows=4 ==> array[2] of vec4 */ static void _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, GLenum matrixType, GLint location, GLsizei count, GLboolean transpose, const GLfloat *values) { + GLint offset; 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->NumUniforms) { + + if (location == -1) + return; /* The standard specifies this as a no-op */ + + split_location_offset(&location, &offset); + + if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); return; } @@ -1269,18 +1746,22 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows, FLUSH_VERTICES(ctx, _NEW_PROGRAM); if (shProg->VertexProgram) { - GLint loc = shProg->Uniforms->Uniforms[location].VertPos; - if (loc >= 0) { + /* convert uniform location to program parameter index */ + GLint index = shProg->Uniforms->Uniforms[location].VertPos; + if (index >= 0) { set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, - loc, rows, cols, transpose, values); + index, offset, + count, rows, cols, transpose, values); } } if (shProg->FragmentProgram) { - GLint loc = shProg->Uniforms->Uniforms[location].FragPos; - if (loc >= 0) { + /* convert uniform location to program parameter index */ + GLint index = shProg->Uniforms->Uniforms[location].FragPos; + if (index >= 0) { set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, - loc, rows, cols, transpose, values); + index, offset, + count, rows, cols, transpose, values); } } } @@ -1290,15 +1771,19 @@ static void _mesa_validate_program(GLcontext *ctx, GLuint program) { struct gl_shader_program *shProg; - shProg = _mesa_lookup_shader_program(ctx, program); + + shProg = _mesa_lookup_shader_program_err(ctx, program, "glValidateProgram"); if (!shProg) { - _mesa_error(ctx, GL_INVALID_VALUE, "glValidateProgram(program)"); return; } - /* XXX temporary */ - shProg->Validated = GL_TRUE; - /* From the GL spec: + if (!shProg->LinkStatus) { + shProg->Validated = GL_FALSE; + return; + } + + /* From the GL spec, a program is invalid if any of these are true: + any two active samplers in the current program object are of different types, but refer to the same texture image unit, @@ -1311,6 +1796,8 @@ _mesa_validate_program(GLcontext *ctx, GLuint program) processing exceeds the combined limit on the total number of texture image units allowed. */ + + shProg->Validated = GL_TRUE; } @@ -1339,6 +1826,7 @@ _mesa_init_glsl_driver_functions(struct dd_function_table *driver) driver->GetShaderInfoLog = _mesa_get_shader_info_log; driver->GetShaderSource = _mesa_get_shader_source; driver->GetUniformfv = _mesa_get_uniformfv; + driver->GetUniformiv = _mesa_get_uniformiv; driver->GetUniformLocation = _mesa_get_uniform_location; driver->IsProgram = _mesa_is_program; driver->IsShader = _mesa_is_shader;