diff options
Diffstat (limited to 'src/mesa/shader')
| -rw-r--r-- | src/mesa/shader/shader_api.c | 861 | ||||
| -rw-r--r-- | src/mesa/shader/shader_api.h | 4 | ||||
| -rw-r--r-- | src/mesa/shader/uniforms.c | 913 | ||||
| -rw-r--r-- | src/mesa/shader/uniforms.h | 33 | 
4 files changed, 954 insertions, 857 deletions
diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index cebe899265..25853823a1 100644 --- a/src/mesa/shader/shader_api.c +++ b/src/mesa/shader/shader_api.c @@ -44,6 +44,7 @@  #include "shader/prog_statevars.h"  #include "shader/prog_uniform.h"  #include "shader/shader_api.h" +#include "shader/uniforms.h"  #include "shader/slang/slang_compile.h"  #include "shader/slang/slang_link.h"  #include "main/dispatch.h" @@ -222,7 +223,7 @@ _mesa_lookup_shader_program(GLcontext *ctx, GLuint name)  /**   * As above, but record an error if program is not found.   */ -static struct gl_shader_program * +struct gl_shader_program *  _mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name,                                  const char *caller)  { @@ -840,77 +841,6 @@ _mesa_sizeof_glsl_type(GLenum type)  } -static GLenum -base_uniform_type(GLenum type) -{ -   switch (type) { -#if 0 /* not needed, for now */ -   case GL_BOOL: -   case GL_BOOL_VEC2: -   case GL_BOOL_VEC3: -   case GL_BOOL_VEC4: -      return GL_BOOL; -#endif -   case GL_FLOAT: -   case GL_FLOAT_VEC2: -   case GL_FLOAT_VEC3: -   case GL_FLOAT_VEC4: -      return GL_FLOAT; -   case GL_UNSIGNED_INT: -   case GL_UNSIGNED_INT_VEC2: -   case GL_UNSIGNED_INT_VEC3: -   case GL_UNSIGNED_INT_VEC4: -      return GL_UNSIGNED_INT; -   case GL_INT: -   case GL_INT_VEC2: -   case GL_INT_VEC3: -   case GL_INT_VEC4: -      return GL_INT; -   default: -      _mesa_problem(NULL, "Invalid type in base_uniform_type()"); -      return GL_FLOAT; -   } -} - - -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_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: -   case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: -   case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: -      return GL_TRUE; -   default: -      return GL_FALSE; -   } -} - -  static void  _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index,                          GLsizei maxLength, GLsizei *length, GLint *size, @@ -943,93 +873,6 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index,  } -static struct gl_program_parameter * -get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) -{ -   const struct gl_program *prog = NULL; -   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]; -} - - -/** - * Called via ctx->Driver.GetActiveUniform(). - */ -static void -_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; -   const struct gl_program *prog = NULL; -   const struct gl_program_parameter *param; -   GLint progPos; - -   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)"); -      return; -   } - -   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; /* should never happen */ - -   ASSERT(progPos < prog->Parameters->NumParameters); -   param = &prog->Parameters->Parameters[progPos]; - -   if (nameOut) { -      _mesa_copy_string(nameOut, maxLength, length, param->Name); -   } - -   if (size) { -      GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); -      if ((GLint) param->Size > typeSize) { -         /* This is an array. -          * Array elements are placed on vector[4] boundaries so they're -          * a multiple of four floats.  We round typeSize up to next multiple -          * of four to get the right size below. -          */ -         typeSize = (typeSize + 3) & ~3; -      } -      /* Note that the returned size is in units of the <type>, not bytes */ -      *size = param->Size / typeSize; -   } - -   if (type) { -      *type = param->DataType; -   } -} - -  /**   * Called via ctx->Driver.GetAttachedShaders().   */ @@ -1209,282 +1052,6 @@ _mesa_get_shader_source(GLcontext *ctx, GLuint shader, GLsizei maxLength,  } -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; -   } -} - - -/** - * Determine the number of rows and columns occupied by a uniform - * according to its datatype.  For non-matrix types (such as GL_FLOAT_VEC4), - * the number of rows = 1 and cols = number of elements in the vector. - */ -static void -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 */ -      if (p->Size <= 4) { -         *rows = 1; -         *cols = p->Size; -      } -      else { -         *rows = p->Size / 4 + 1; -         if (p->Size % 4 == 0) -            *cols = 4; -         else -            *cols = p->Size % 4; -      } -   } -} - - -/** - * Helper for get_uniform[fi]v() functions. - * Given a shader program name and uniform location, return a pointer - * to the shader program and return the program parameter position. - */ -static void -lookup_uniform_parameter(GLcontext *ctx, GLuint program, GLint location, -                         struct gl_program **progOut, GLint *paramPosOut) -{ -   struct gl_shader_program *shProg -      = _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v"); -   struct gl_program *prog = NULL; -   GLint progPos = -1; - -   /* if shProg is NULL, we'll have already recorded an error */ - -   if (shProg) { -      if (!shProg->Uniforms || -          location < 0 || -          location >= (GLint) shProg->Uniforms->NumUniforms) { -         _mesa_error(ctx, GL_INVALID_OPERATION,  "glGetUniformfv(location)"); -      } -      else { -         /* OK, find the gl_program and program parameter location */ -         progPos = shProg->Uniforms->Uniforms[location].VertPos; -         if (progPos >= 0) { -            prog = &shProg->VertexProgram->Base; -         } -         else { -            progPos = shProg->Uniforms->Uniforms[location].FragPos; -            if (progPos >= 0) { -               prog = &shProg->FragmentProgram->Base; -            } -         } -      } -   } - -   *progOut = prog; -   *paramPosOut = progPos; -} - - -/** - * Called via ctx->Driver.GetUniformfv(). - */ -static void -_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, -                    GLfloat *params) -{ -   struct gl_program *prog; -   GLint paramPos; - -   lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); - -   if (prog) { -      const struct gl_program_parameter *p = -         &prog->Parameters->Parameters[paramPos]; -      GLint rows, cols, i, j, k; - -      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[paramPos+i][j]; -         } -      } -   } -} - - -/** - * Called via ctx->Driver.GetUniformiv(). - * \sa _mesa_get_uniformfv, only difference is a cast. - */ -static void -_mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location, -                    GLint *params) -{ -   struct gl_program *prog; -   GLint paramPos; - -   lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); - -   if (prog) { -      const struct gl_program_parameter *p = -         &prog->Parameters->Parameters[paramPos]; -      GLint rows, cols, i, j, k; - -      get_uniform_rows_cols(p, &rows, &cols); - -      k = 0; -      for (i = 0; i < rows; i++) { -         for (j = 0; j < cols; j++ ) { -            params[k++] = (GLint) prog->Parameters->ParameterValues[paramPos+i][j]; -         } -      } -   } -} - - -/** - * 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) -{ -   GLint offset = 0, location = -1; - -   struct gl_shader_program *shProg = -      _mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); - -   if (!shProg) -      return -1; - -   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. -    */ - -   /* 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 = malloc(len + 1); -         if (!newName) -            return -1; /* out of mem */ -         memcpy(newName, name, len); -         newName[len] = 0; - -         location = _mesa_lookup_uniform(shProg->Uniforms, newName); -         if (location >= 0) { -            const GLint element = 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; -               } -            } -         } - -         free(newName); -      } -   } - -   if (location < 0) { -      location = _mesa_lookup_uniform(shProg->Uniforms, name); -   } - -   if (location >= 0) { -      merge_location_offset(&location, offset); -   } - -   return location; -} - - -  /**   * Called via ctx->Driver.ShaderSource()   */ @@ -1692,422 +1259,6 @@ _mesa_update_shader_textures_used(struct gl_program *prog)  /** - * 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_UNSIGNED_INT || -                                 userType == GL_INT)) -      return GL_TRUE; - -   if (targetType == GL_BOOL_VEC2 && (userType == GL_FLOAT_VEC2 || -                                      userType == GL_UNSIGNED_INT_VEC2 || -                                      userType == GL_INT_VEC2)) -      return GL_TRUE; - -   if (targetType == GL_BOOL_VEC3 && (userType == GL_FLOAT_VEC3 || -                                      userType == GL_UNSIGNED_INT_VEC3 || -                                      userType == GL_INT_VEC3)) -      return GL_TRUE; - -   if (targetType == GL_BOOL_VEC4 && (userType == GL_FLOAT_VEC4 || -                                      userType == GL_UNSIGNED_INT_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 index  the index of the program parameter for the uniform - * \param offset  additional parameter slot offset (for arrays) - * \param type  the incoming datatype of 'values' - * \param count  the number of uniforms to set - * \param elems  number of elements per uniform (1, 2, 3 or 4) - * \param values  the new values, of datatype 'type' - */ -static void -set_program_uniform(GLcontext *ctx, struct gl_program *program, -                    GLint index, GLint offset, -                    GLenum type, GLsizei count, GLint elems, -                    const void *values) -{ -   const struct gl_program_parameter *param = -      &program->Parameters->Parameters[index]; - -   assert(offset >= 0); -   assert(elems >= 1); -   assert(elems <= 4); - -   if (!compatible_types(type, param->DataType)) { -      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); -      return; -   } - -   if (index + offset > (GLint) program->Parameters->Size) { -      /* out of bounds! */ -      return; -   } - -   if (param->Type == PROGRAM_SAMPLER) { -      /* This controls which texture unit which is used by a sampler */ -      GLboolean changed = GL_FALSE; -      GLint i; - -      /* this should have been caught by the compatible_types() check */ -      ASSERT(type == GL_INT); - -      /* loop over number of samplers to change */ -      for (i = 0; i < count; i++) { -         GLuint sampler = -            (GLuint) program->Parameters->ParameterValues[index + offset + i][0]; -         GLuint texUnit = ((GLuint *) values)[i]; - -         /* check that the sampler (tex unit index) is legal */ -         if (texUnit >= ctx->Const.MaxTextureImageUnits) { -            _mesa_error(ctx, GL_INVALID_VALUE, -                        "glUniform1(invalid sampler/tex unit index for '%s')", -                        param->Name); -            return; -         } - -         /* This maps a sampler to a texture unit: */ -         if (sampler < MAX_SAMPLERS) { -#if 0 -            printf("Set program %p sampler %d '%s' to unit %u\n", -		   program, sampler, param->Name, texUnit); -#endif -            if (program->SamplerUnits[sampler] != texUnit) { -               program->SamplerUnits[sampler] = texUnit; -               changed = GL_TRUE; -            } -         } -      } - -      if (changed) { -         /* When a sampler's value changes it usually requires rewriting -          * a GPU program's TEX instructions since there may not be a -          * sampler->texture lookup table.  We signal this with the -          * ProgramStringNotify() callback. -          */ -         FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM); -         _mesa_update_shader_textures_used(program); -         /* Do we need to care about the return value here? -          * This should not be the first time the driver was notified of -          * this program. -          */ -         (void) ctx->Driver.ProgramStringNotify(ctx, program->Target, program); -      } -   } -   else { -      /* ordinary uniform variable */ -      const GLboolean isUniformBool = is_boolean_type(param->DataType); -      const GLenum basicType = base_uniform_type(type); -      const GLint slots = (param->Size + 3) / 4; -      const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); -      GLsizei k, i; - -      if ((GLint) param->Size > typeSize) { -         /* an array */ -         /* we'll ignore extra data below */ -      } -      else { -         /* non-array: count must be at most one; count == 0 is handled by the loop below */ -         if (count > 1) { -            _mesa_error(ctx, GL_INVALID_OPERATION, -                        "glUniform(uniform '%s' is not an array)", -                        param->Name); -            return; -         } -      } - -      /* loop over number of array elements */ -      for (k = 0; k < count; k++) { -         GLfloat *uniformVal; - -         if (offset + k >= slots) { -            /* Extra array data is ignored */ -            break; -         } - -         /* uniformVal (the destination) is always float[4] */ -         uniformVal = program->Parameters->ParameterValues[index + offset + k]; - -         if (basicType == GL_INT) { -            /* convert user's ints to floats */ -            const GLint *iValues = ((const GLint *) values) + k * elems; -            for (i = 0; i < elems; i++) { -               uniformVal[i] = (GLfloat) iValues[i]; -            } -         } -         else if (basicType == GL_UNSIGNED_INT) { -            /* convert user's uints to floats */ -            const GLuint *iValues = ((const GLuint *) values) + k * elems; -            for (i = 0; i < elems; i++) { -               uniformVal[i] = (GLfloat) iValues[i]; -            } -         } -         else { -            const GLfloat *fValues = ((const GLfloat *) values) + k * elems; -            assert(basicType == GL_FLOAT); -            for (i = 0; i < elems; i++) { -               uniformVal[i] = fValues[i]; -            } -         } - -         /* if the uniform is bool-valued, convert to 1.0 or 0.0 */ -         if (isUniformBool) { -            for (i = 0; i < elems; i++) { -               uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; -            } -         } -      } -   } -} - - -/** - * Called via ctx->Driver.Uniform(). - */ -static void -_mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, -              const GLvoid *values, GLenum type) -{ -   struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; -   struct gl_uniform *uniform; -   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 */ - -   if (location < -1) { -      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location=%d)", -                  location); -      return; -   } - -   split_location_offset(&location, &offset); - -   if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { -      _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location=%d)", location); -      return; -   } - -   if (count < 0) { -      _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(count < 0)"); -      return; -   } - -   elems = _mesa_sizeof_glsl_type(type); - -   FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); - -   uniform = &shProg->Uniforms->Uniforms[location]; - -   if (ctx->Shader.Flags & GLSL_UNIFORMS) { -      const GLenum basicType = base_uniform_type(type); -      GLint i; -      printf("Mesa: set program %u uniform %s (loc %d) to: ", -	     shProg->Name, uniform->Name, location); -      if (basicType == GL_INT) { -         const GLint *v = (const GLint *) values; -         for (i = 0; i < count * elems; i++) { -            printf("%d ", v[i]); -         } -      } -      else if (basicType == GL_UNSIGNED_INT) { -         const GLuint *v = (const GLuint *) values; -         for (i = 0; i < count * elems; i++) { -            printf("%u ", v[i]); -         } -      } -      else { -         const GLfloat *v = (const GLfloat *) values; -         assert(basicType == GL_FLOAT); -         for (i = 0; i < count * elems; i++) { -            printf("%g ", v[i]); -         } -      } -      printf("\n"); -   } - -   /* A uniform var may be used by both a vertex shader and a fragment -    * shader.  We may need to update one or both shader's uniform here: -    */ -   if (shProg->VertexProgram) { -      /* convert uniform location to program parameter index */ -      GLint index = uniform->VertPos; -      if (index >= 0) { -         set_program_uniform(ctx, &shProg->VertexProgram->Base, -                             index, offset, type, count, elems, values); -      } -   } - -   if (shProg->FragmentProgram) { -      /* convert uniform location to program parameter index */ -      GLint index = uniform->FragPos; -      if (index >= 0) { -         set_program_uniform(ctx, &shProg->FragmentProgram->Base, -                             index, offset, type, count, elems, values); -      } -   } - -   uniform->Initialized = GL_TRUE; -} - - -/** - * Set a matrix-valued program parameter. - */ -static void -set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program, -                           GLuint index, GLuint offset, -                           GLuint count, GLuint rows, GLuint cols, -                           GLboolean transpose, const GLfloat *values) -{ -   GLuint mat, row, col; -   GLuint src = 0; -   const struct gl_program_parameter * param = &program->Parameters->Parameters[index]; -   const GLuint slots = (param->Size + 3) / 4; -   const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); -   GLint nr, nc; - -   /* check that the number of rows, columns is correct */ -   get_matrix_dims(param->DataType, &nr, &nc); -   if (rows != nr || cols != nc) { -      _mesa_error(ctx, GL_INVALID_OPERATION, -                  "glUniformMatrix(matrix size mismatch)"); -      return; -   } - -   if ((GLint) param->Size <= typeSize) { -      /* non-array: count must be at most one; count == 0 is handled by the loop below */ -      if (count > 1) { -         _mesa_error(ctx, GL_INVALID_OPERATION, -                     "glUniformMatrix(uniform is not an array)"); -         return; -      } -   } - -   /* -    * Note: the _columns_ of a matrix are stored in program registers, not -    * the rows.  So, the loops below look a little funny. -    * XXX could optimize this a bit... -    */ - -   /* loop over matrices */ -   for (mat = 0; mat < count; mat++) { - -      /* each matrix: */ -      for (col = 0; col < cols; col++) { -         GLfloat *v; -         if (offset >= slots) { -            /* Ignore writes beyond the end of (the used part of) an array */ -            return; -         } -         v = program->Parameters->ParameterValues[index + offset]; -         for (row = 0; row < rows; row++) { -            if (transpose) { -               v[row] = values[src + row * cols + col]; -            } -            else { -               v[row] = values[src + col * rows + row]; -            } -         } - -         offset++; -      } - -      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, -                     GLint location, GLsizei count, -                     GLboolean transpose, const GLfloat *values) -{ -   struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; -   struct gl_uniform *uniform; -   GLint offset; - -   if (!shProg || !shProg->LinkStatus) { -      _mesa_error(ctx, GL_INVALID_OPERATION, -         "glUniformMatrix(program not linked)"); -      return; -   } - -   if (location == -1) -      return;   /* The standard specifies this as a no-op */ - -   if (location < -1) { -      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)"); -      return; -   } - -   split_location_offset(&location, &offset); - -   if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { -      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); -      return; -   } -   if (values == NULL) { -      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix"); -      return; -   } - -   FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); - -   uniform = &shProg->Uniforms->Uniforms[location]; - -   if (shProg->VertexProgram) { -      /* convert uniform location to program parameter index */ -      GLint index = uniform->VertPos; -      if (index >= 0) { -         set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, -                                    index, offset, -                                    count, rows, cols, transpose, values); -      } -   } - -   if (shProg->FragmentProgram) { -      /* convert uniform location to program parameter index */ -      GLint index = uniform->FragPos; -      if (index >= 0) { -         set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, -                                    index, offset, -                                    count, rows, cols, transpose, values); -      } -   } - -   uniform->Initialized = GL_TRUE; -} - - -/**   * Validate a program's samplers.   * Specifically, check that there aren't two samplers of different types   * pointing to the same texture unit. @@ -2248,7 +1399,6 @@ _mesa_init_glsl_driver_functions(struct dd_function_table *driver)     driver->DeleteShader = _mesa_delete_shader;     driver->DetachShader = _mesa_detach_shader;     driver->GetActiveAttrib = _mesa_get_active_attrib; -   driver->GetActiveUniform = _mesa_get_active_uniform;     driver->GetAttachedShaders = _mesa_get_attached_shaders;     driver->GetAttribLocation = _mesa_get_attrib_location;     driver->GetHandle = _mesa_get_handle; @@ -2257,15 +1407,12 @@ _mesa_init_glsl_driver_functions(struct dd_function_table *driver)     driver->GetShaderiv = _mesa_get_shaderiv;     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;     driver->LinkProgram = _mesa_link_program;     driver->ShaderSource = _mesa_shader_source; -   driver->Uniform = _mesa_uniform; -   driver->UniformMatrix = _mesa_uniform_matrix;     driver->UseProgram = _mesa_use_program;     driver->ValidateProgram = _mesa_validate_program; + +   _mesa_init_uniform_functions(driver);  } diff --git a/src/mesa/shader/shader_api.h b/src/mesa/shader/shader_api.h index 597f0b8e75..9743a23ce6 100644 --- a/src/mesa/shader/shader_api.h +++ b/src/mesa/shader/shader_api.h @@ -75,6 +75,10 @@ extern struct gl_shader_program *  _mesa_lookup_shader_program(GLcontext *ctx, GLuint name); +extern struct gl_shader_program * +_mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name, +                                const char *caller); +  extern struct gl_shader *  _mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type); diff --git a/src/mesa/shader/uniforms.c b/src/mesa/shader/uniforms.c new file mode 100644 index 0000000000..7aa6c934de --- /dev/null +++ b/src/mesa/shader/uniforms.c @@ -0,0 +1,913 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 2004-2008  Brian Paul   All Rights Reserved. + * Copyright (C) 2009-2010  VMware, Inc.  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"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * \file uniforms.c + * Functions related to GLSL uniform variables. + * \author Brian Paul + */ + +/** + * XXX things to do: + * 1. Check that the right error code is generated for all _mesa_error() calls. + * 2. Insert FLUSH_VERTICES calls in various places + */ + + +#include "main/glheader.h" +#include "main/context.h" +#include "main/hash.h" +#include "shader/program.h" +#include "shader/prog_parameter.h" +#include "shader/prog_statevars.h" +#include "shader/prog_uniform.h" +#include "shader/shader_api.h" +#include "shader/slang/slang_compile.h" +#include "shader/slang/slang_link.h" +#include "main/dispatch.h" +#include "uniforms.h" + + + +static GLenum +base_uniform_type(GLenum type) +{ +   switch (type) { +#if 0 /* not needed, for now */ +   case GL_BOOL: +   case GL_BOOL_VEC2: +   case GL_BOOL_VEC3: +   case GL_BOOL_VEC4: +      return GL_BOOL; +#endif +   case GL_FLOAT: +   case GL_FLOAT_VEC2: +   case GL_FLOAT_VEC3: +   case GL_FLOAT_VEC4: +      return GL_FLOAT; +   case GL_UNSIGNED_INT: +   case GL_UNSIGNED_INT_VEC2: +   case GL_UNSIGNED_INT_VEC3: +   case GL_UNSIGNED_INT_VEC4: +      return GL_UNSIGNED_INT; +   case GL_INT: +   case GL_INT_VEC2: +   case GL_INT_VEC3: +   case GL_INT_VEC4: +      return GL_INT; +   default: +      _mesa_problem(NULL, "Invalid type in base_uniform_type()"); +      return GL_FLOAT; +   } +} + + +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_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: +   case GL_SAMPLER_1D_ARRAY_SHADOW_EXT: +   case GL_SAMPLER_2D_ARRAY_SHADOW_EXT: +      return GL_TRUE; +   default: +      return GL_FALSE; +   } +} + + +static struct gl_program_parameter * +get_uniform_parameter(const struct gl_shader_program *shProg, GLuint index) +{ +   const struct gl_program *prog = NULL; +   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]; +} + + +/** + * Called via ctx->Driver.GetActiveUniform(). + */ +static void +_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; +   const struct gl_program *prog = NULL; +   const struct gl_program_parameter *param; +   GLint progPos; + +   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)"); +      return; +   } + +   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; /* should never happen */ + +   ASSERT(progPos < prog->Parameters->NumParameters); +   param = &prog->Parameters->Parameters[progPos]; + +   if (nameOut) { +      _mesa_copy_string(nameOut, maxLength, length, param->Name); +   } + +   if (size) { +      GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); +      if ((GLint) param->Size > typeSize) { +         /* This is an array. +          * Array elements are placed on vector[4] boundaries so they're +          * a multiple of four floats.  We round typeSize up to next multiple +          * of four to get the right size below. +          */ +         typeSize = (typeSize + 3) & ~3; +      } +      /* Note that the returned size is in units of the <type>, not bytes */ +      *size = param->Size / typeSize; +   } + +   if (type) { +      *type = param->DataType; +   } +} + + + +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; +   } +} + + +/** + * Determine the number of rows and columns occupied by a uniform + * according to its datatype.  For non-matrix types (such as GL_FLOAT_VEC4), + * the number of rows = 1 and cols = number of elements in the vector. + */ +static void +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 */ +      if (p->Size <= 4) { +         *rows = 1; +         *cols = p->Size; +      } +      else { +         *rows = p->Size / 4 + 1; +         if (p->Size % 4 == 0) +            *cols = 4; +         else +            *cols = p->Size % 4; +      } +   } +} + + +/** + * Helper for get_uniform[fi]v() functions. + * Given a shader program name and uniform location, return a pointer + * to the shader program and return the program parameter position. + */ +static void +lookup_uniform_parameter(GLcontext *ctx, GLuint program, GLint location, +                         struct gl_program **progOut, GLint *paramPosOut) +{ +   struct gl_shader_program *shProg +      = _mesa_lookup_shader_program_err(ctx, program, "glGetUniform[if]v"); +   struct gl_program *prog = NULL; +   GLint progPos = -1; + +   /* if shProg is NULL, we'll have already recorded an error */ + +   if (shProg) { +      if (!shProg->Uniforms || +          location < 0 || +          location >= (GLint) shProg->Uniforms->NumUniforms) { +         _mesa_error(ctx, GL_INVALID_OPERATION,  "glGetUniformfv(location)"); +      } +      else { +         /* OK, find the gl_program and program parameter location */ +         progPos = shProg->Uniforms->Uniforms[location].VertPos; +         if (progPos >= 0) { +            prog = &shProg->VertexProgram->Base; +         } +         else { +            progPos = shProg->Uniforms->Uniforms[location].FragPos; +            if (progPos >= 0) { +               prog = &shProg->FragmentProgram->Base; +            } +         } +      } +   } + +   *progOut = prog; +   *paramPosOut = progPos; +} + + +/** + * Called via ctx->Driver.GetUniformfv(). + */ +static void +_mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location, +                    GLfloat *params) +{ +   struct gl_program *prog; +   GLint paramPos; + +   lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); + +   if (prog) { +      const struct gl_program_parameter *p = +         &prog->Parameters->Parameters[paramPos]; +      GLint rows, cols, i, j, k; + +      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[paramPos+i][j]; +         } +      } +   } +} + + +/** + * Called via ctx->Driver.GetUniformiv(). + * \sa _mesa_get_uniformfv, only difference is a cast. + */ +static void +_mesa_get_uniformiv(GLcontext *ctx, GLuint program, GLint location, +                    GLint *params) +{ +   struct gl_program *prog; +   GLint paramPos; + +   lookup_uniform_parameter(ctx, program, location, &prog, ¶mPos); + +   if (prog) { +      const struct gl_program_parameter *p = +         &prog->Parameters->Parameters[paramPos]; +      GLint rows, cols, i, j, k; + +      get_uniform_rows_cols(p, &rows, &cols); + +      k = 0; +      for (i = 0; i < rows; i++) { +         for (j = 0; j < cols; j++ ) { +            params[k++] = (GLint) prog->Parameters->ParameterValues[paramPos+i][j]; +         } +      } +   } +} + + +/** + * 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) +{ +   GLint offset = 0, location = -1; + +   struct gl_shader_program *shProg = +      _mesa_lookup_shader_program_err(ctx, program, "glGetUniformLocation"); + +   if (!shProg) +      return -1; + +   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. +    */ + +   /* 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 = malloc(len + 1); +         if (!newName) +            return -1; /* out of mem */ +         memcpy(newName, name, len); +         newName[len] = 0; + +         location = _mesa_lookup_uniform(shProg->Uniforms, newName); +         if (location >= 0) { +            const GLint element = 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; +               } +            } +         } + +         free(newName); +      } +   } + +   if (location < 0) { +      location = _mesa_lookup_uniform(shProg->Uniforms, name); +   } + +   if (location >= 0) { +      merge_location_offset(&location, offset); +   } + +   return location; +} + + +/** + * 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_UNSIGNED_INT || +                                 userType == GL_INT)) +      return GL_TRUE; + +   if (targetType == GL_BOOL_VEC2 && (userType == GL_FLOAT_VEC2 || +                                      userType == GL_UNSIGNED_INT_VEC2 || +                                      userType == GL_INT_VEC2)) +      return GL_TRUE; + +   if (targetType == GL_BOOL_VEC3 && (userType == GL_FLOAT_VEC3 || +                                      userType == GL_UNSIGNED_INT_VEC3 || +                                      userType == GL_INT_VEC3)) +      return GL_TRUE; + +   if (targetType == GL_BOOL_VEC4 && (userType == GL_FLOAT_VEC4 || +                                      userType == GL_UNSIGNED_INT_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 index  the index of the program parameter for the uniform + * \param offset  additional parameter slot offset (for arrays) + * \param type  the incoming datatype of 'values' + * \param count  the number of uniforms to set + * \param elems  number of elements per uniform (1, 2, 3 or 4) + * \param values  the new values, of datatype 'type' + */ +static void +set_program_uniform(GLcontext *ctx, struct gl_program *program, +                    GLint index, GLint offset, +                    GLenum type, GLsizei count, GLint elems, +                    const void *values) +{ +   const struct gl_program_parameter *param = +      &program->Parameters->Parameters[index]; + +   assert(offset >= 0); +   assert(elems >= 1); +   assert(elems <= 4); + +   if (!compatible_types(type, param->DataType)) { +      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)"); +      return; +   } + +   if (index + offset > (GLint) program->Parameters->Size) { +      /* out of bounds! */ +      return; +   } + +   if (param->Type == PROGRAM_SAMPLER) { +      /* This controls which texture unit which is used by a sampler */ +      GLboolean changed = GL_FALSE; +      GLint i; + +      /* this should have been caught by the compatible_types() check */ +      ASSERT(type == GL_INT); + +      /* loop over number of samplers to change */ +      for (i = 0; i < count; i++) { +         GLuint sampler = +            (GLuint) program->Parameters->ParameterValues[index + offset + i][0]; +         GLuint texUnit = ((GLuint *) values)[i]; + +         /* check that the sampler (tex unit index) is legal */ +         if (texUnit >= ctx->Const.MaxTextureImageUnits) { +            _mesa_error(ctx, GL_INVALID_VALUE, +                        "glUniform1(invalid sampler/tex unit index for '%s')", +                        param->Name); +            return; +         } + +         /* This maps a sampler to a texture unit: */ +         if (sampler < MAX_SAMPLERS) { +#if 0 +            printf("Set program %p sampler %d '%s' to unit %u\n", +		   program, sampler, param->Name, texUnit); +#endif +            if (program->SamplerUnits[sampler] != texUnit) { +               program->SamplerUnits[sampler] = texUnit; +               changed = GL_TRUE; +            } +         } +      } + +      if (changed) { +         /* When a sampler's value changes it usually requires rewriting +          * a GPU program's TEX instructions since there may not be a +          * sampler->texture lookup table.  We signal this with the +          * ProgramStringNotify() callback. +          */ +         FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM); +         _mesa_update_shader_textures_used(program); +         /* Do we need to care about the return value here? +          * This should not be the first time the driver was notified of +          * this program. +          */ +         (void) ctx->Driver.ProgramStringNotify(ctx, program->Target, program); +      } +   } +   else { +      /* ordinary uniform variable */ +      const GLboolean isUniformBool = is_boolean_type(param->DataType); +      const GLenum basicType = base_uniform_type(type); +      const GLint slots = (param->Size + 3) / 4; +      const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); +      GLsizei k, i; + +      if ((GLint) param->Size > typeSize) { +         /* an array */ +         /* we'll ignore extra data below */ +      } +      else { +         /* non-array: count must be at most one; count == 0 is handled by the loop below */ +         if (count > 1) { +            _mesa_error(ctx, GL_INVALID_OPERATION, +                        "glUniform(uniform '%s' is not an array)", +                        param->Name); +            return; +         } +      } + +      /* loop over number of array elements */ +      for (k = 0; k < count; k++) { +         GLfloat *uniformVal; + +         if (offset + k >= slots) { +            /* Extra array data is ignored */ +            break; +         } + +         /* uniformVal (the destination) is always float[4] */ +         uniformVal = program->Parameters->ParameterValues[index + offset + k]; + +         if (basicType == GL_INT) { +            /* convert user's ints to floats */ +            const GLint *iValues = ((const GLint *) values) + k * elems; +            for (i = 0; i < elems; i++) { +               uniformVal[i] = (GLfloat) iValues[i]; +            } +         } +         else if (basicType == GL_UNSIGNED_INT) { +            /* convert user's uints to floats */ +            const GLuint *iValues = ((const GLuint *) values) + k * elems; +            for (i = 0; i < elems; i++) { +               uniformVal[i] = (GLfloat) iValues[i]; +            } +         } +         else { +            const GLfloat *fValues = ((const GLfloat *) values) + k * elems; +            assert(basicType == GL_FLOAT); +            for (i = 0; i < elems; i++) { +               uniformVal[i] = fValues[i]; +            } +         } + +         /* if the uniform is bool-valued, convert to 1.0 or 0.0 */ +         if (isUniformBool) { +            for (i = 0; i < elems; i++) { +               uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f; +            } +         } +      } +   } +} + + +/** + * Called via ctx->Driver.Uniform(). + */ +static void +_mesa_uniform(GLcontext *ctx, GLint location, GLsizei count, +              const GLvoid *values, GLenum type) +{ +   struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; +   struct gl_uniform *uniform; +   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 */ + +   if (location < -1) { +      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location=%d)", +                  location); +      return; +   } + +   split_location_offset(&location, &offset); + +   if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { +      _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location=%d)", location); +      return; +   } + +   if (count < 0) { +      _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(count < 0)"); +      return; +   } + +   elems = _mesa_sizeof_glsl_type(type); + +   FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); + +   uniform = &shProg->Uniforms->Uniforms[location]; + +   if (ctx->Shader.Flags & GLSL_UNIFORMS) { +      const GLenum basicType = base_uniform_type(type); +      GLint i; +      printf("Mesa: set program %u uniform %s (loc %d) to: ", +	     shProg->Name, uniform->Name, location); +      if (basicType == GL_INT) { +         const GLint *v = (const GLint *) values; +         for (i = 0; i < count * elems; i++) { +            printf("%d ", v[i]); +         } +      } +      else if (basicType == GL_UNSIGNED_INT) { +         const GLuint *v = (const GLuint *) values; +         for (i = 0; i < count * elems; i++) { +            printf("%u ", v[i]); +         } +      } +      else { +         const GLfloat *v = (const GLfloat *) values; +         assert(basicType == GL_FLOAT); +         for (i = 0; i < count * elems; i++) { +            printf("%g ", v[i]); +         } +      } +      printf("\n"); +   } + +   /* A uniform var may be used by both a vertex shader and a fragment +    * shader.  We may need to update one or both shader's uniform here: +    */ +   if (shProg->VertexProgram) { +      /* convert uniform location to program parameter index */ +      GLint index = uniform->VertPos; +      if (index >= 0) { +         set_program_uniform(ctx, &shProg->VertexProgram->Base, +                             index, offset, type, count, elems, values); +      } +   } + +   if (shProg->FragmentProgram) { +      /* convert uniform location to program parameter index */ +      GLint index = uniform->FragPos; +      if (index >= 0) { +         set_program_uniform(ctx, &shProg->FragmentProgram->Base, +                             index, offset, type, count, elems, values); +      } +   } + +   uniform->Initialized = GL_TRUE; +} + + +/** + * Set a matrix-valued program parameter. + */ +static void +set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program, +                           GLuint index, GLuint offset, +                           GLuint count, GLuint rows, GLuint cols, +                           GLboolean transpose, const GLfloat *values) +{ +   GLuint mat, row, col; +   GLuint src = 0; +   const struct gl_program_parameter * param = &program->Parameters->Parameters[index]; +   const GLuint slots = (param->Size + 3) / 4; +   const GLint typeSize = _mesa_sizeof_glsl_type(param->DataType); +   GLint nr, nc; + +   /* check that the number of rows, columns is correct */ +   get_matrix_dims(param->DataType, &nr, &nc); +   if (rows != nr || cols != nc) { +      _mesa_error(ctx, GL_INVALID_OPERATION, +                  "glUniformMatrix(matrix size mismatch)"); +      return; +   } + +   if ((GLint) param->Size <= typeSize) { +      /* non-array: count must be at most one; count == 0 is handled by the loop below */ +      if (count > 1) { +         _mesa_error(ctx, GL_INVALID_OPERATION, +                     "glUniformMatrix(uniform is not an array)"); +         return; +      } +   } + +   /* +    * Note: the _columns_ of a matrix are stored in program registers, not +    * the rows.  So, the loops below look a little funny. +    * XXX could optimize this a bit... +    */ + +   /* loop over matrices */ +   for (mat = 0; mat < count; mat++) { + +      /* each matrix: */ +      for (col = 0; col < cols; col++) { +         GLfloat *v; +         if (offset >= slots) { +            /* Ignore writes beyond the end of (the used part of) an array */ +            return; +         } +         v = program->Parameters->ParameterValues[index + offset]; +         for (row = 0; row < rows; row++) { +            if (transpose) { +               v[row] = values[src + row * cols + col]; +            } +            else { +               v[row] = values[src + col * rows + row]; +            } +         } + +         offset++; +      } + +      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, +                     GLint location, GLsizei count, +                     GLboolean transpose, const GLfloat *values) +{ +   struct gl_shader_program *shProg = ctx->Shader.CurrentProgram; +   struct gl_uniform *uniform; +   GLint offset; + +   if (!shProg || !shProg->LinkStatus) { +      _mesa_error(ctx, GL_INVALID_OPERATION, +         "glUniformMatrix(program not linked)"); +      return; +   } + +   if (location == -1) +      return;   /* The standard specifies this as a no-op */ + +   if (location < -1) { +      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)"); +      return; +   } + +   split_location_offset(&location, &offset); + +   if (location < 0 || location >= (GLint) shProg->Uniforms->NumUniforms) { +      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)"); +      return; +   } +   if (values == NULL) { +      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix"); +      return; +   } + +   FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); + +   uniform = &shProg->Uniforms->Uniforms[location]; + +   if (shProg->VertexProgram) { +      /* convert uniform location to program parameter index */ +      GLint index = uniform->VertPos; +      if (index >= 0) { +         set_program_uniform_matrix(ctx, &shProg->VertexProgram->Base, +                                    index, offset, +                                    count, rows, cols, transpose, values); +      } +   } + +   if (shProg->FragmentProgram) { +      /* convert uniform location to program parameter index */ +      GLint index = uniform->FragPos; +      if (index >= 0) { +         set_program_uniform_matrix(ctx, &shProg->FragmentProgram->Base, +                                    index, offset, +                                    count, rows, cols, transpose, values); +      } +   } + +   uniform->Initialized = GL_TRUE; +} + + + +void +_mesa_init_uniform_functions(struct dd_function_table *driver) +{ +   driver->GetActiveUniform = _mesa_get_active_uniform; +   driver->GetUniformfv = _mesa_get_uniformfv; +   driver->GetUniformiv = _mesa_get_uniformiv; +   driver->GetUniformLocation = _mesa_get_uniform_location; +   driver->Uniform = _mesa_uniform; +   driver->UniformMatrix = _mesa_uniform_matrix; +} diff --git a/src/mesa/shader/uniforms.h b/src/mesa/shader/uniforms.h new file mode 100644 index 0000000000..52984dedad --- /dev/null +++ b/src/mesa/shader/uniforms.h @@ -0,0 +1,33 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 2010  VMware, Inc.  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"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef UNIFORMS_H +#define UNIFORMS_H + + +extern void +_mesa_init_uniform_functions(struct dd_function_table *driver); + + +#endif /* UNIFORMS_H */  | 
