mesa: Use MAX2 to calculate maximum uniform element
[mesa.git] / src / mesa / main / uniform_query.cpp
index 01be786c62172cc9d00c3e2c2485981d0c7dc009..2dc9f273b47d90dcb265223a01263397b4027ce6 100644 (file)
  * 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 OR COPYRIGHT HOLDERS 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.
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
  */
 
 #include <stdlib.h>
 #include "program/hash_table.h"
 #include "../glsl/program.h"
 #include "../glsl/ir_uniform.h"
+#include "../glsl/glsl_parser_extras.h"
 #include "main/shaderapi.h"
 #include "main/shaderobj.h"
 #include "uniforms.h"
 
 
 extern "C" void GLAPIENTRY
-_mesa_GetActiveUniform(GLhandleARB program, GLuint index,
-                          GLsizei maxLength, GLsizei *length, GLint *size,
-                          GLenum *type, GLcharARB *nameOut)
+_mesa_GetActiveUniform(GLuint program, GLuint index,
+                       GLsizei maxLength, GLsizei *length, GLint *size,
+                       GLenum *type, GLcharARB *nameOut)
 {
    GET_CURRENT_CONTEXT(ctx);
    struct gl_shader_program *shProg =
@@ -89,7 +91,7 @@ _mesa_GetActiveUniformsiv(GLuint program,
 
    if (uniformCount < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
-                 "glGetUniformIndices(uniformCount < 0)");
+                 "glGetActiveUniformsiv(uniformCount < 0)");
       return;
    }
 
@@ -152,25 +154,34 @@ _mesa_GetActiveUniformsiv(GLuint program,
         params[i] = uni->row_major;
         break;
 
+      case GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX:
+         if (!ctx->Extensions.ARB_shader_atomic_counters)
+            goto invalid_enum;
+         params[i] = uni->atomic_buffer_index;
+         break;
+
       default:
-        _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)");
-        return;
+         goto invalid_enum;
       }
    }
+
+   return;
+
+ invalid_enum:
+   _mesa_error(ctx, GL_INVALID_ENUM, "glGetActiveUniformsiv(pname)");
 }
 
-static bool
+static struct gl_uniform_storage *
 validate_uniform_parameters(struct gl_context *ctx,
                            struct gl_shader_program *shProg,
                            GLint location, GLsizei count,
-                           unsigned *loc,
                            unsigned *array_index,
                            const char *caller,
                            bool negative_one_is_not_valid)
 {
    if (!shProg || !shProg->LinkStatus) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)", caller);
-      return false;
+      return NULL;
    }
 
    if (location == -1) {
@@ -202,7 +213,7 @@ validate_uniform_parameters(struct gl_context *ctx,
                     caller, location);
       }
 
-      return false;
+      return NULL;
    }
 
    /* From page 12 (page 26 of the PDF) of the OpenGL 2.1 spec:
@@ -212,7 +223,7 @@ validate_uniform_parameters(struct gl_context *ctx,
     */
    if (count < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s(count < 0)", caller);
-      return false;
+      return NULL;
    }
 
    /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says:
@@ -231,37 +242,53 @@ validate_uniform_parameters(struct gl_context *ctx,
    if (location < -1) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
                   caller, location);
-      return false;
+      return NULL;
    }
 
-   _mesa_uniform_split_location_offset(location, loc, array_index);
-
-   if (*loc >= shProg->NumUserUniformStorage) {
+   /* Check that the given location is in bounds of uniform remap table. */
+   if (location >= (GLint) shProg->NumUniformRemapTable) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
                  caller, location);
-      return false;
+      return NULL;
    }
 
-   if (shProg->UniformStorage[*loc].array_elements == 0 && count > 1) {
+   /* If the driver storage pointer in remap table is -1, we ignore silently.
+    *
+    * GL_ARB_explicit_uniform_location spec says:
+    *     "What happens if Uniform* is called with an explicitly defined
+    *     uniform location, but that uniform is deemed inactive by the
+    *     linker?
+    *
+    *     RESOLVED: The call is ignored for inactive uniform variables and
+    *     no error is generated."
+    *
+    */
+   if (shProg->UniformRemapTable[location] ==
+       INACTIVE_UNIFORM_EXPLICIT_LOCATION)
+      return NULL;
+
+   unsigned loc;
+   _mesa_uniform_split_location_offset(shProg, location, &loc, array_index);
+   struct gl_uniform_storage *const uni = &shProg->UniformStorage[loc];
+
+   if (uni->array_elements == 0 && count > 1) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                  "%s(count > 1 for non-array, location=%d)",
                  caller, location);
-      return false;
+      return NULL;
    }
 
    /* If the uniform is an array, check that array_index is in bounds.
     * If not an array, check that array_index is zero.
     * array_index is unsigned so no need to check for less than zero.
     */
-   unsigned limit = shProg->UniformStorage[*loc].array_elements;
-   if (limit == 0)
-      limit = 1;
+   const unsigned limit = MAX2(uni->array_elements, 1);
    if (*array_index >= limit) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "%s(location=%d)",
                  caller, location);
-      return false;
+      return NULL;
    }
-   return true;
+   return uni;
 }
 
 /**
@@ -274,15 +301,14 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
 {
    struct gl_shader_program *shProg =
       _mesa_lookup_shader_program_err(ctx, program, "glGetUniformfv");
-   struct gl_uniform_storage *uni;
-   unsigned loc, offset;
+   unsigned offset;
 
-   if (!validate_uniform_parameters(ctx, shProg, location, 1,
-                                   &loc, &offset, "glGetUniform", true))
+   struct gl_uniform_storage *const uni =
+      validate_uniform_parameters(ctx, shProg, location, 1,
+                                  &offset, "glGetUniform", true);
+   if (uni == NULL)
       return;
 
-   uni = &shProg->UniformStorage[loc];
-
    {
       unsigned elements = (uni->type->is_sampler())
         ? 1 : uni->type->components();
@@ -433,20 +459,14 @@ log_uniform(const void *values, enum glsl_base_type basicType,
 static void
 log_program_parameters(const struct gl_shader_program *shProg)
 {
-   static const char *stages[] = {
-      "vertex", "fragment", "geometry"
-   };
-
-   assert(Elements(stages) == MESA_SHADER_TYPES);
-
-   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
       if (shProg->_LinkedShaders[i] == NULL)
         continue;
 
       const struct gl_program *const prog = shProg->_LinkedShaders[i]->Program;
 
       printf("Program %d %s shader parameters:\n",
-            shProg->Name, stages[i]);
+             shProg->Name, _mesa_shader_stage_to_string(i));
       for (unsigned j = 0; j < prog->Parameters->NumParameters; j++) {
         printf("%s: %p %f %f %f %f\n",
                prog->Parameters->Parameters[j].Name,
@@ -585,18 +605,17 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
              GLint location, GLsizei count,
               const GLvoid *values, GLenum type)
 {
-   unsigned loc, offset;
+   unsigned offset;
    unsigned components;
    unsigned src_components;
    enum glsl_base_type basicType;
-   struct gl_uniform_storage *uni;
 
-   if (!validate_uniform_parameters(ctx, shProg, location, count,
-                                   &loc, &offset, "glUniform", false))
+   struct gl_uniform_storage *const uni =
+      validate_uniform_parameters(ctx, shProg, location, count,
+                                  &offset, "glUniform", false);
+   if (uni == NULL)
       return;
 
-   uni = &shProg->UniformStorage[loc];
-
    /* Verify that the types are compatible.
     */
    switch (type) {
@@ -678,6 +697,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       match = true;
       break;
    case GLSL_TYPE_SAMPLER:
+   case GLSL_TYPE_IMAGE:
       match = (basicType == GLSL_TYPE_INT);
       break;
    default:
@@ -690,7 +710,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       return;
    }
 
-   if (ctx->Shader.Flags & GLSL_UNIFORMS) {
+   if (ctx->_Shader->Flags & GLSL_UNIFORMS) {
       log_uniform(values, basicType, components, 1, count,
                  false, shProg, location, uni);
    }
@@ -729,6 +749,22 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       }
    }
 
+   if (uni->type->is_image()) {
+      int i;
+
+      for (i = 0; i < count; i++) {
+         const int unit = ((GLint *) values)[i];
+
+         /* check that the image unit is legal */
+         if (unit < 0 || unit >= (int)ctx->Const.MaxImageUnits) {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glUniform1i(invalid image unit index for uniform %d)",
+                        location);
+            return;
+         }
+      }
+   }
+
    /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says:
     *
     *     "When loading N elements starting at an arbitrary position k in a
@@ -777,24 +813,24 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
    if (uni->type->is_sampler()) {
       int i;
 
-      for (i = 0; i < count; i++) {
-        shProg->SamplerUnits[uni->sampler + offset + i] =
-           ((unsigned *) values)[i];
-      }
-
       bool flushed = false;
-      for (i = 0; i < MESA_SHADER_TYPES; i++) {
+      for (i = 0; i < MESA_SHADER_STAGES; i++) {
         struct gl_shader *const sh = shProg->_LinkedShaders[i];
+         int j;
 
-        /* If the shader stage doesn't use any samplers, don't bother
-         * checking if any samplers have changed.
+        /* If the shader stage doesn't use the sampler uniform, skip this.
          */
-        if (sh == NULL || sh->active_samplers == 0)
+        if (sh == NULL || !uni->sampler[i].active)
            continue;
 
+         for (j = 0; j < count; j++) {
+            sh->SamplerUnits[uni->sampler[i].index + offset + j] =
+               ((unsigned *) values)[j];
+         }
+
         struct gl_program *const prog = sh->Program;
 
-        assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits));
+        assert(sizeof(prog->SamplerUnits) == sizeof(sh->SamplerUnits));
 
         /* Determine if any of the samplers used by this shader stage have
          * been modified.
@@ -802,7 +838,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
         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])) {
+               && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) {
               changed = true;
               break;
            }
@@ -815,8 +851,8 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
            }
 
            memcpy(prog->SamplerUnits,
-                  shProg->SamplerUnits,
-                  sizeof(shProg->SamplerUnits));
+                  sh->SamplerUnits,
+                  sizeof(sh->SamplerUnits));
 
            _mesa_update_shader_textures_used(shProg, prog);
             if (ctx->Driver.SamplerUniformChange)
@@ -824,6 +860,25 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
         }
       }
    }
+
+   /* If the uniform is an image, update the mapping from image
+    * uniforms to image units present in the shader data structure.
+    */
+   if (uni->type->is_image()) {
+      int i, j;
+
+      for (i = 0; i < MESA_SHADER_STAGES; i++) {
+        if (uni->image[i].active) {
+            struct gl_shader *sh = shProg->_LinkedShaders[i];
+
+            for (j = 0; j < count; j++)
+               sh->ImageUnits[uni->image[i].index + offset + j] =
+                  ((GLint *) values)[j];
+         }
+      }
+
+      ctx->NewDriverState |= ctx->DriverFlags.NewImageUnits;
+   }
 }
 
 /**
@@ -836,17 +891,17 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
                      GLint location, GLsizei count,
                      GLboolean transpose, const GLfloat *values)
 {
-   unsigned loc, offset;
+   unsigned offset;
    unsigned vectors;
    unsigned components;
    unsigned elements;
-   struct gl_uniform_storage *uni;
 
-   if (!validate_uniform_parameters(ctx, shProg, location, count,
-                                   &loc, &offset, "glUniformMatrix", false))
+   struct gl_uniform_storage *const uni =
+      validate_uniform_parameters(ctx, shProg, location, count,
+                                  &offset, "glUniformMatrix", false);
+   if (uni == NULL)
       return;
 
-   uni = &shProg->UniformStorage[loc];
    if (!uni->type->is_matrix()) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                  "glUniformMatrix(non-matrix uniform)");
@@ -877,7 +932,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       }
    }
 
-   if (ctx->Shader.Flags & GLSL_UNIFORMS) {
+   if (ctx->_Shader->Flags & GLSL_UNIFORMS) {
       log_uniform(values, GLSL_TYPE_FLOAT, components, vectors, count,
                  bool(transpose), shProg, location, uni);
    }
@@ -1046,3 +1101,80 @@ _mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg,
 
    return true;
 }
+
+extern "C" bool
+_mesa_sampler_uniforms_pipeline_are_valid(struct gl_pipeline_object *pipeline)
+{
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         ...
+    *
+    *         - Any two active samplers in the current program object are of
+    *           different types, but refer to the same texture image unit.
+    *
+    *         - The number of active samplers in the program exceeds the
+    *           maximum number of texture image units allowed."
+    */
+   unsigned active_samplers = 0;
+   const struct gl_shader_program **shProg =
+      (const struct gl_shader_program **) pipeline->CurrentProgram;
+
+   const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+   memset(unit_types, 0, sizeof(unit_types));
+
+   for (unsigned idx = 0; idx < ARRAY_SIZE(pipeline->CurrentProgram); idx++) {
+      if (!shProg[idx])
+         continue;
+
+      for (unsigned i = 0; i < shProg[idx]->NumUserUniformStorage; i++) {
+         const struct gl_uniform_storage *const storage =
+            &shProg[idx]->UniformStorage[i];
+         const glsl_type *const t = (storage->type->is_array())
+            ? storage->type->fields.array : storage->type;
+
+         if (!t->is_sampler())
+            continue;
+
+         active_samplers++;
+
+         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) {
+               pipeline->InfoLog =
+                  ralloc_asprintf(pipeline,
+                                  "Texture unit %d is accessed both as %s "
+                                  "and %s",
+                                  unit, unit_types[unit]->name, t->name);
+               return false;
+            }
+         }
+      }
+   }
+
+   if (active_samplers > MAX_COMBINED_TEXTURE_IMAGE_UNITS) {
+      pipeline->InfoLog =
+         ralloc_asprintf(pipeline,
+                         "the number of active samplers %d exceed the "
+                         "maximum %d",
+                         active_samplers, MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+      return false;
+   }
+
+   return true;
+}