mesa/main: handle double uniform matrices properly
[mesa.git] / src / mesa / main / uniform_query.cpp
index cab5083e81b38e2babecbe98e091624a8bc32cff..997b0cb1c7e6fa52705b0eb875035288b0dd3f74 100644 (file)
 
 #include "main/core.h"
 #include "main/context.h"
-#include "ir.h"
-#include "ir_uniform.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"
+#include "main/uniforms.h"
+#include "compiler/glsl/ir.h"
+#include "compiler/glsl/ir_uniform.h"
+#include "compiler/glsl/glsl_parser_extras.h"
+#include "compiler/glsl/program.h"
+#include "program/hash_table.h"
 
 
 extern "C" void GLAPIENTRY
@@ -323,20 +322,20 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
    {
       unsigned elements = (uni->type->is_sampler())
         ? 1 : uni->type->components();
+      const int dmul = uni->type->base_type == GLSL_TYPE_DOUBLE ? 2 : 1;
+      const int rmul = returnType == GLSL_TYPE_DOUBLE ? 2 : 1;
 
       /* Calculate the source base address *BEFORE* modifying elements to
        * account for the size of the user's buffer.
        */
       const union gl_constant_value *const src =
-        &uni->storage[offset * elements];
+        &uni->storage[offset * elements * dmul];
 
       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;
+             returnType == GLSL_TYPE_UINT || returnType == GLSL_TYPE_DOUBLE);
+
+      /* doubles have a different size than the other 3 types */
+      unsigned bytes = sizeof(src[0]) * elements * rmul;
       if (bufSize < 0 || bytes > (unsigned) bufSize) {
         _mesa_error( ctx, GL_INVALID_OPERATION,
                     "glGetnUniform*vARB(out of bounds: bufSize is %d,"
@@ -360,32 +359,57 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
       } else {
         union gl_constant_value *const dst =
            (union gl_constant_value *) paramsOut;
-
         /* This code could be optimized by putting the loop inside the switch
          * statements.  However, this is not expected to be
          * performance-critical code.
          */
         for (unsigned i = 0; i < elements; i++) {
+          int sidx = i * dmul;
+          int didx = i * rmul;
+
            switch (returnType) {
            case GLSL_TYPE_FLOAT:
               switch (uni->type->base_type) {
               case GLSL_TYPE_UINT:
-                 dst[i].f = (float) src[i].u;
+                 dst[didx].f = (float) src[sidx].u;
                  break;
               case GLSL_TYPE_INT:
               case GLSL_TYPE_SAMPLER:
                case GLSL_TYPE_IMAGE:
-                 dst[i].f = (float) src[i].i;
+                 dst[didx].f = (float) src[sidx].i;
                  break;
               case GLSL_TYPE_BOOL:
-                 dst[i].f = src[i].i ? 1.0f : 0.0f;
+                 dst[didx].f = src[sidx].i ? 1.0f : 0.0f;
+                 break;
+              case GLSL_TYPE_DOUBLE:
+                 dst[didx].f = *(double *)&src[sidx].f;
+                 break;
+              default:
+                 assert(!"Should not get here.");
+                 break;
+              }
+              break;
+           case GLSL_TYPE_DOUBLE:
+              switch (uni->type->base_type) {
+              case GLSL_TYPE_UINT:
+                 *(double *)&dst[didx].f = (double) src[sidx].u;
+                 break;
+              case GLSL_TYPE_INT:
+              case GLSL_TYPE_SAMPLER:
+              case GLSL_TYPE_IMAGE:
+                 *(double *)&dst[didx].f = (double) src[sidx].i;
+                 break;
+              case GLSL_TYPE_BOOL:
+                 *(double *)&dst[didx].f = src[sidx].i ? 1.0f : 0.0f;
+                 break;
+              case GLSL_TYPE_FLOAT:
+                 *(double *)&dst[didx].f = (double) src[sidx].f;
                  break;
               default:
                  assert(!"Should not get here.");
                  break;
               }
               break;
-
            case GLSL_TYPE_INT:
            case GLSL_TYPE_UINT:
               switch (uni->type->base_type) {
@@ -407,10 +431,13 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location,
                   *      a floating-point value is rounded to the
                   *      nearest integer..."
                   */
-                 dst[i].i = IROUND(src[i].f);
+                 dst[didx].i = IROUND(src[sidx].f);
                  break;
               case GLSL_TYPE_BOOL:
-                 dst[i].i = src[i].i ? 1 : 0;
+                 dst[didx].i = src[sidx].i ? 1 : 0;
+                 break;
+              case GLSL_TYPE_DOUBLE:
+                 dst[didx].i = IROUNDD(*(double *)&src[sidx].f);
                  break;
               default:
                  assert(!"Should not get here.");
@@ -677,9 +704,11 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       match = (basicType != GLSL_TYPE_DOUBLE);
       break;
    case GLSL_TYPE_SAMPLER:
-   case GLSL_TYPE_IMAGE:
       match = (basicType == GLSL_TYPE_INT);
       break;
+   case GLSL_TYPE_IMAGE:
+      match = (basicType == GLSL_TYPE_INT && _mesa_is_desktop_gl(ctx));
+      break;
    default:
       match = (basicType == uni->type->base_type);
       break;
@@ -729,6 +758,10 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
             return;
          }
       }
+      /* We need to reset the validate flag on changes to samplers in case
+       * two different sampler types are set to the same texture unit.
+       */
+      ctx->_Shader->Validated = GL_FALSE;
    }
 
    if (uni->type->is_image()) {
@@ -782,8 +815,6 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
       }
    }
 
-   uni->initialized = true;
-
    _mesa_propagate_uniforms_to_driver_storage(uni, offset, count);
 
    /* If the uniform is a sampler, do the extra magic necessary to propagate
@@ -796,11 +827,11 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
 
         /* If the shader stage doesn't use the sampler uniform, skip this.
          */
-        if (sh == NULL || !uni->sampler[i].active)
+        if (sh == NULL || !uni->opaque[i].active)
            continue;
 
          for (int j = 0; j < count; j++) {
-            sh->SamplerUnits[uni->sampler[i].index + offset + j] =
+            sh->SamplerUnits[uni->opaque[i].index + offset + j] =
                ((unsigned *) values)[j];
          }
 
@@ -842,11 +873,11 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
     */
    if (uni->type->is_image()) {
       for (int i = 0; i < MESA_SHADER_STAGES; i++) {
-        if (uni->image[i].active) {
+        if (uni->opaque[i].active) {
             struct gl_shader *sh = shProg->_LinkedShaders[i];
 
             for (int j = 0; j < count; j++)
-               sh->ImageUnits[uni->image[i].index + offset + j] =
+               sh->ImageUnits[uni->opaque[i].index + offset + j] =
                   ((GLint *) values)[j];
          }
       }
@@ -864,7 +895,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
                     GLuint cols, GLuint rows,
                      GLint location, GLsizei count,
                      GLboolean transpose,
-                     const GLvoid *values, GLenum type)
+                     const GLvoid *values, enum glsl_base_type basicType)
 {
    unsigned offset;
    unsigned vectors;
@@ -883,8 +914,8 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       return;
    }
 
-   assert(type == GL_FLOAT || type == GL_DOUBLE);
-   size_mul = type == GL_DOUBLE ? 2 : 1;
+   assert(basicType == GLSL_TYPE_FLOAT || basicType == GLSL_TYPE_DOUBLE);
+   size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1;
 
    assert(!uni->type->is_sampler());
    vectors = uni->type->matrix_columns;
@@ -910,6 +941,31 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       }
    }
 
+   /* Section 2.11.7 (Uniform Variables) of the OpenGL 4.2 Core Profile spec
+    * says:
+    *
+    *     "If any of the following conditions occur, an INVALID_OPERATION
+    *     error is generated by the Uniform* commands, and no uniform values
+    *     are changed:
+    *
+    *     ...
+    *
+    *     - if the uniform declared in the shader is not of type boolean and
+    *       the type indicated in the name of the Uniform* command used does
+    *       not match the type of the uniform"
+    *
+    * There are no Boolean matrix types, so we do not need to allow
+    * GLSL_TYPE_BOOL here (as _mesa_uniform does).
+    */
+   if (uni->type->base_type != basicType) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glUniformMatrix%ux%u(\"%s\"@%d is %s, not %s)",
+                  cols, rows, uni->name, location,
+                  glsl_type_name(uni->type->base_type),
+                  glsl_type_name(basicType));
+      return;
+   }
+
    if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) {
       log_uniform(values, uni->type->base_type, components, vectors, count,
                  bool(transpose), shProg, location, uni);
@@ -937,9 +993,9 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
    elements = components * vectors;
 
    if (!transpose) {
-      memcpy(&uni->storage[elements * offset], values,
+      memcpy(&uni->storage[size_mul * elements * offset], values,
             sizeof(uni->storage[0]) * elements * count * size_mul);
-   } else if (type == GL_FLOAT) {
+   } else if (basicType == GLSL_TYPE_FLOAT) {
       /* Copy and transpose the matrix.
        */
       const float *src = (const float *)values;
@@ -956,7 +1012,7 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
         src += elements;
       }
    } else {
-      assert(type == GL_DOUBLE);
+      assert(basicType == GLSL_TYPE_DOUBLE);
       const double *src = (const double *)values;
       double *dst = (double *)&uni->storage[elements * offset].f;
 
@@ -972,87 +1028,10 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg,
       }
    }
 
-   uni->initialized = true;
-
    _mesa_propagate_uniforms_to_driver_storage(uni, offset, count);
 }
 
 
-/**
- * Called via glGetUniformLocation().
- *
- * Returns the uniform index into UniformStorage (also the
- * glGetActiveUniformsiv uniform index), and stores the referenced
- * array offset in *offset, or GL_INVALID_INDEX (-1).
- */
-extern "C" unsigned
-_mesa_get_uniform_location(struct gl_shader_program *shProg,
-                           const GLchar *name,
-                           unsigned *out_offset)
-{
-   /* Page 80 (page 94 of the PDF) of the OpenGL 2.1 spec says:
-    *
-    *     "The first element of a uniform array is identified using the
-    *     name of the uniform array appended with "[0]". Except if the last
-    *     part of the string name indicates a uniform array, then the
-    *     location of the first element of that array can be retrieved by
-    *     either using the name of the uniform array, or the name of the
-    *     uniform array appended with "[0]"."
-    *
-    * Note: since uniform names are not allowed to use whitespace, and array
-    * indices within uniform names are not allowed to use "+", "-", or leading
-    * zeros, it follows that each uniform has a unique name up to the possible
-    * ambiguity with "[0]" noted above.  Therefore we don't need to worry
-    * about mal-formed inputs--they will properly fail when we try to look up
-    * the uniform name in shProg->UniformHash.
-    */
-
-   const GLchar *base_name_end;
-   long offset = parse_program_resource_name(name, &base_name_end);
-   bool array_lookup = offset >= 0;
-   char *name_copy;
-
-   if (array_lookup) {
-      name_copy = (char *) malloc(base_name_end - name + 1);
-      memcpy(name_copy, name, base_name_end - name);
-      name_copy[base_name_end - name] = '\0';
-   } else {
-      name_copy = (char *) name;
-      offset = 0;
-   }
-
-   unsigned location = 0;
-   const bool found = shProg->UniformHash->get(location, name_copy);
-
-   assert(!found
-         || strcmp(name_copy, shProg->UniformStorage[location].name) == 0);
-
-   /* Free the temporary buffer *before* possibly returning an error.
-    */
-   if (name_copy != name)
-      free(name_copy);
-
-   if (!found)
-      return GL_INVALID_INDEX;
-
-   /* If the uniform is built-in, fail. */
-   if (shProg->UniformStorage[location].builtin)
-      return GL_INVALID_INDEX;
-
-   /* If the uniform is an array, fail if the index is out of bounds.
-    * (A negative index is caught above.)  This also fails if the uniform
-    * is not an array, but the user is trying to index it, because
-    * array_elements is zero and offset >= 0.
-    */
-   if (array_lookup
-       && offset >= (long) shProg->UniformStorage[location].array_elements) {
-      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)
@@ -1101,18 +1080,23 @@ _mesa_sampler_uniforms_pipeline_are_valid(struct gl_pipeline_object *pipeline)
       for (unsigned i = 0; i < shProg[idx]->NumUniformStorage; 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())
+         if (!storage->type->is_sampler())
             continue;
 
          active_samplers++;
 
-         const unsigned count = MAX2(1, storage->type->array_size());
+         const unsigned count = MAX2(1, storage->array_elements);
          for (unsigned j = 0; j < count; j++) {
             const unsigned unit = storage->storage[j].i;
 
+            /* FIXME: Samplers are initialized to 0 and Mesa doesn't do a
+             * great job of eliminating unused uniforms currently so for now
+             * don't throw an error if two sampler types both point to 0.
+             */
+            if (unit == 0)
+               continue;
+
             /* 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:
@@ -1122,13 +1106,14 @@ _mesa_sampler_uniforms_pipeline_are_valid(struct gl_pipeline_object *pipeline)
              *     program object."
              */
             if (unit_types[unit] == NULL) {
-               unit_types[unit] = t;
-            } else if (unit_types[unit] != t) {
+               unit_types[unit] = storage->type;
+            } else if (unit_types[unit] != storage->type) {
                pipeline->InfoLog =
                   ralloc_asprintf(pipeline,
                                   "Texture unit %d is accessed both as %s "
                                   "and %s",
-                                  unit, unit_types[unit]->name, t->name);
+                                  unit, unit_types[unit]->name,
+                                  storage->type->name);
                return false;
             }
          }