mesa: Include compiler.h in hash_table.h.
[mesa.git] / src / mesa / main / uniform_query.cpp
index 5371d6a176bb3d5454f6f7d9f674cfabe789939a..c71577c6ed165f688dba16d818b026fb199b2409 100644 (file)
@@ -38,7 +38,7 @@
 
 
 extern "C" void GLAPIENTRY
-_mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
+_mesa_GetActiveUniform(GLhandleARB program, GLuint index,
                           GLsizei maxLength, GLsizei *length, GLint *size,
                           GLenum *type, GLcharARB *nameOut)
 {
@@ -46,6 +46,8 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
    struct gl_shader_program *shProg =
       _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
 
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
    if (!shProg)
       return;
 
@@ -72,6 +74,79 @@ _mesa_GetActiveUniformARB(GLhandleARB program, GLuint index,
    }
 }
 
+extern "C" void GLAPIENTRY
+_mesa_GetActiveUniformsiv(GLuint program,
+                         GLsizei uniformCount,
+                         const GLuint *uniformIndices,
+                         GLenum pname,
+                         GLint *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_shader_program *shProg;
+   GLsizei i;
+
+   shProg = _mesa_lookup_shader_program_err(ctx, program, "glGetActiveUniform");
+   if (!shProg)
+      return;
+
+   if (uniformCount < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                 "glGetUniformIndices(uniformCount < 0)");
+      return;
+   }
+
+   for (i = 0; i < uniformCount; i++) {
+      GLuint index = uniformIndices[i];
+      const struct gl_uniform_storage *uni = &shProg->UniformStorage[index];
+
+      if (index >= shProg->NumUserUniformStorage) {
+        _mesa_error(ctx, GL_INVALID_VALUE, "glGetActiveUniformsiv(index)");
+        return;
+      }
+
+      switch (pname) {
+      case GL_UNIFORM_TYPE:
+        params[i] = uni->type->gl_type;
+        break;
+
+      case GL_UNIFORM_SIZE:
+        /* array_elements is zero for non-arrays, but the API requires that 1 be
+         * returned.
+         */
+        params[i] = MAX2(1, uni->array_elements);
+        break;
+
+      case GL_UNIFORM_NAME_LENGTH:
+        params[i] = strlen(uni->name) + 1;
+        break;
+
+      case GL_UNIFORM_BLOCK_INDEX:
+        params[i] = uni->block_index;
+        break;
+
+      case GL_UNIFORM_OFFSET:
+        params[i] = uni->offset;
+        break;
+
+      case GL_UNIFORM_ARRAY_STRIDE:
+        params[i] = uni->array_stride;
+        break;
+
+      case GL_UNIFORM_MATRIX_STRIDE:
+        params[i] = uni->matrix_stride;
+        break;
+
+      case GL_UNIFORM_IS_ROW_MAJOR:
+        params[i] = uni->row_major;
+        break;
+
+      default:
+        _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)");
+        return;
+      }
+   }
+}
+
 static bool
 validate_uniform_parameters(struct gl_context *ctx,
                            struct gl_shader_program *shProg,
@@ -203,10 +278,18 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
       const union gl_constant_value *const src =
         &uni->storage[offset * elements];
 
-      unsigned bytes = sizeof(uni->storage[0]) * elements;
-      if (bytes > (unsigned) bufSize) {
-        elements = bufSize / sizeof(uni->storage[0]);
-        bytes = bufSize;
+      assert(returnType == GLSL_TYPE_FLOAT || returnType == GLSL_TYPE_INT ||
+             returnType == GLSL_TYPE_UINT);
+      /* The three (currently) supported types all have the same size,
+       * which is of course the same as their union. That'll change
+       * with glGetUniformdv()...
+       */
+      unsigned bytes = sizeof(src[0]) * elements;
+      if (bufSize < 0 || bytes > (unsigned) bufSize) {
+        _mesa_error( ctx, GL_INVALID_OPERATION,
+                    "glGetnUniform*vARB(out of bounds: bufSize is %d,"
+                    " but %u bytes are required)", bufSize, bytes );
+        return;
       }
 
       /* If the return type and the uniform's native type are "compatible,"
@@ -648,7 +731,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       if (offset >= uni->array_elements)
         return;
 
-      count = MIN2(count, (uni->array_elements - offset));
+      count = MIN2(count, (int) (uni->array_elements - offset));
    }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS);
@@ -691,18 +774,31 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
 
       bool flushed = false;
       for (i = 0; i < MESA_SHADER_TYPES; i++) {
-        struct gl_program *prog;
+        struct gl_shader *const sh = shProg->_LinkedShaders[i];
 
-        if (shProg->_LinkedShaders[i] == NULL)
+        /* If the shader stage doesn't use any samplers, don't bother
+         * checking if any samplers have changed.
+         */
+        if (sh == NULL || sh->active_samplers == 0)
            continue;
 
-        prog = shProg->_LinkedShaders[i]->Program;
+        struct gl_program *const prog = sh->Program;
 
         assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits));
 
-        if (memcmp(prog->SamplerUnits,
-                   shProg->SamplerUnits,
-                   sizeof(shProg->SamplerUnits)) != 0) {
+        /* Determine if any of the samplers used by this shader stage have
+         * been modified.
+         */
+        bool changed = false;
+        for (unsigned j = 0; j < Elements(prog->SamplerUnits); j++) {
+           if ((sh->active_samplers & (1U << j)) != 0
+               && (prog->SamplerUnits[j] != shProg->SamplerUnits[j])) {
+              changed = true;
+              break;
+           }
+        }
+
+        if (changed) {
            if (!flushed) {
               FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM);
               flushed = true;
@@ -712,8 +808,9 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
                   shProg->SamplerUnits,
                   sizeof(shProg->SamplerUnits));
 
-           _mesa_update_shader_textures_used(prog);
-           (void) ctx->Driver.ProgramStringNotify(ctx, prog->Target, prog);
+           _mesa_update_shader_textures_used(shProg, prog);
+            if (ctx->Driver.SamplerUniformChange)
+              ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog);
         }
       }
    }
@@ -761,6 +858,16 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       return;
    }
 
+   /* GL_INVALID_VALUE is generated if `transpose' is not GL_FALSE.
+    * http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml */
+   if (ctx->API == API_OPENGLES || ctx->API == API_OPENGLES2) {
+      if (transpose) {
+        _mesa_error(ctx, GL_INVALID_VALUE,
+                    "glUniformMatrix(matrix transpose is not GL_FALSE)");
+        return;
+      }
+   }
+
    if (ctx->Shader.Flags & GLSL_UNIFORMS) {
       log_uniform(values, GLSL_TYPE_FLOAT, components, vectors, count,
                  bool(transpose), shProg, location, uni);
@@ -781,7 +888,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       if (offset >= uni->array_elements)
         return;
 
-      count = MIN2(count, (uni->array_elements - offset));
+      count = MIN2(count, (int) (uni->array_elements - offset));
    }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS);
@@ -819,13 +926,17 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
 /**
  * Called via glGetUniformLocation().
  *
- * The return value will encode two values, the uniform location and an
- * offset (used for arrays, structs).
+ * Returns the uniform index into UniformStorage (also the
+ * glGetActiveUniformsiv uniform index), and stores the referenced
+ * array offset in *offset, or GL_INVALID_INDEX (-1).  Those two
+ * return values can be encoded into a uniform location for
+ * glUniform* using _mesa_uniform_merge_location_offset(index, offset).
  */
-extern "C" GLint
+extern "C" unsigned
 _mesa_get_uniform_location(struct gl_context *ctx,
                            struct gl_shader_program *shProg,
-                          const GLchar *name)
+                           const GLchar *name,
+                           unsigned *out_offset)
 {
    const size_t len = strlen(name);
    long offset;
@@ -868,13 +979,13 @@ _mesa_get_uniform_location(struct gl_context *ctx,
        * (or other non-digit characters) before the opening '['.
        */
       if ((i == 0) || name[i-1] != '[')
-        return -1;
+        return GL_INVALID_INDEX;
 
       /* Return an error if there are no digits between the opening '[' to
        * match the closing ']'.
        */
       if (i == (len - 1))
-        return -1;
+        return GL_INVALID_INDEX;
 
       /* Make a new string that is a copy of the old string up to (but not
        * including) the '[' character.
@@ -884,8 +995,10 @@ _mesa_get_uniform_location(struct gl_context *ctx,
       name_copy[i-1] = '\0';
 
       offset = strtol(&name[i], NULL, 10);
-      if (offset < 0)
-        return -1;
+      if (offset < 0) {
+        free(name_copy);
+        return GL_INVALID_INDEX;
+      }
 
       array_lookup = true;
    } else {
@@ -906,14 +1019,58 @@ _mesa_get_uniform_location(struct gl_context *ctx,
       free(name_copy);
 
    if (!found)
-      return -1;
+      return GL_INVALID_INDEX;
 
    /* Since array_elements is 0 for non-arrays, this causes look-ups of 'a[0]'
     * to (correctly) fail if 'a' is not an array.
     */
    if (array_lookup && shProg->UniformStorage[location].array_elements == 0) {
-      return -1;
+      return GL_INVALID_INDEX;
+   }
+
+   *out_offset = offset;
+   return location;
+}
+
+extern "C" bool
+_mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg,
+                                char *errMsg, size_t errMsgLength)
+{
+   const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+
+   memset(unit_types, 0, sizeof(unit_types));
+
+   for (unsigned i = 0; i < shProg->NumUserUniformStorage; i++) {
+      const struct gl_uniform_storage *const storage =
+        &shProg->UniformStorage[i];
+      const glsl_type *const t = (storage->type->is_array())
+        ? storage->type->fields.array : storage->type;
+
+      if (!t->is_sampler())
+        continue;
+
+      const unsigned count = MAX2(1, storage->type->array_size());
+      for (unsigned j = 0; j < count; j++) {
+        const unsigned unit = storage->storage[j].i;
+
+        /* The types of the samplers associated with a particular texture
+         * unit must be an exact match.  Page 74 (page 89 of the PDF) of the
+         * OpenGL 3.3 core spec says:
+         *
+         *     "It is not allowed to have variables of different sampler
+         *     types pointing to the same texture image unit within a program
+         *     object."
+         */
+        if (unit_types[unit] == NULL) {
+           unit_types[unit] = t;
+        } else if (unit_types[unit] != t) {
+           _mesa_snprintf(errMsg, errMsgLength,
+                          "Texture unit %d is accessed both as %s and %s",
+                          unit, unit_types[unit]->name, t->name);
+           return false;
+        }
+      }
    }
 
-   return _mesa_uniform_merge_location_offset(location, offset);
+   return true;
 }