mesa: added _mesa_print_vp/p_inputs() functions (debug aids)
[mesa.git] / src / mesa / shader / shader_api.c
index cf0a9023894a7df8da0fe4a065671edbc50d447d..d53580f5f6940f3a40e464c834df3b768a3beff7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.5
+ * Version:  7.6
  *
  * Copyright (C) 2004-2008  Brian Paul   All Rights Reserved.
  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
 #include "main/glheader.h"
 #include "main/context.h"
 #include "main/hash.h"
-#include "main/macros.h"
 #include "shader/program.h"
 #include "shader/prog_parameter.h"
-#include "shader/prog_print.h"
 #include "shader/prog_statevars.h"
 #include "shader/prog_uniform.h"
 #include "shader/shader_api.h"
@@ -380,12 +378,18 @@ get_shader_flags(void)
          flags |= GLSL_DUMP;
       if (_mesa_strstr(env, "log"))
          flags |= GLSL_LOG;
+      if (_mesa_strstr(env, "nopvert"))
+         flags |= GLSL_NOP_VERT;
+      if (_mesa_strstr(env, "nopfrag"))
+         flags |= GLSL_NOP_FRAG;
       if (_mesa_strstr(env, "nopt"))
          flags |= GLSL_NO_OPT;
       else if (_mesa_strstr(env, "opt"))
          flags |= GLSL_OPT;
       if (_mesa_strstr(env, "uniform"))
          flags |= GLSL_UNIFORMS;
+      if (_mesa_strstr(env, "useprog"))
+         flags |= GLSL_USE_PROG;
    }
 
    return flags;
@@ -951,7 +955,7 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index,
 
    if (size) {
       GLint typeSize = sizeof_glsl_type(param->DataType);
-      if (param->Size > typeSize) {
+      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
@@ -1474,6 +1478,56 @@ _mesa_link_program(GLcontext *ctx, GLuint program)
    FLUSH_VERTICES(ctx, _NEW_PROGRAM);
 
    _slang_link(ctx, program, shProg);
+
+   /* debug code */
+   if (0) {
+      GLuint i;
+
+      _mesa_printf("Link %u shaders in program %u: %s\n",
+                   shProg->NumShaders, shProg->Name,
+                   shProg->LinkStatus ? "Success" : "Failed");
+
+      for (i = 0; i < shProg->NumShaders; i++) {
+         _mesa_printf(" shader %u, type 0x%x\n",
+                      shProg->Shaders[i]->Name,
+                      shProg->Shaders[i]->Type);
+      }
+   }
+}
+
+
+/**
+ * Print basic shader info (for debug).
+ */
+static void
+print_shader_info(const struct gl_shader_program *shProg)
+{
+   GLuint i;
+
+   _mesa_printf("Mesa: glUseProgram(%u)\n", shProg->Name);
+   for (i = 0; i < shProg->NumShaders; i++) {
+      const char *s;
+      switch (shProg->Shaders[i]->Type) {
+      case GL_VERTEX_SHADER:
+         s = "vertex";
+         break;
+      case GL_FRAGMENT_SHADER:
+         s = "fragment";
+         break;
+      case GL_GEOMETRY_SHADER:
+         s = "geometry";
+         break;
+      default:
+         s = "";
+      }
+      _mesa_printf("  %s shader %u, checksum %u\n", s, 
+                   shProg->Shaders[i]->Name,
+                   shProg->Shaders[i]->SourceChecksum);
+   }
+   if (shProg->VertexProgram)
+      _mesa_printf("  vert prog %u\n", shProg->VertexProgram->Base.Id);
+   if (shProg->FragmentProgram)
+      _mesa_printf("  frag prog %u\n", shProg->FragmentProgram->Base.Id);
 }
 
 
@@ -1491,8 +1545,6 @@ _mesa_use_program(GLcontext *ctx, GLuint program)
       return;
    }
 
-   FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
-
    if (program) {
       shProg = _mesa_lookup_shader_program_err(ctx, program, "glUseProgram");
       if (!shProg) {
@@ -1505,26 +1557,18 @@ _mesa_use_program(GLcontext *ctx, GLuint program)
       }
 
       /* debug code */
-      if (0) {
-         GLuint i;
-         _mesa_printf("Use Shader %u\n", shProg->Name);
-         for (i = 0; i < shProg->NumShaders; i++) {
-            _mesa_printf(" shader %u, type 0x%x, checksum %u\n",
-                         shProg->Shaders[i]->Name,
-                         shProg->Shaders[i]->Type,
-                         shProg->Shaders[i]->SourceChecksum);
-         }
-         if (shProg->VertexProgram)
-            printf(" vert prog %u\n", shProg->VertexProgram->Base.Id);
-         if (shProg->FragmentProgram)
-            printf(" frag prog %u\n", shProg->FragmentProgram->Base.Id);
+      if (ctx->Shader.Flags & GLSL_USE_PROG) {
+         print_shader_info(shProg);
       }
    }
    else {
       shProg = NULL;
    }
 
-   _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram, shProg);
+   if (ctx->Shader.CurrentProgram != shProg) {
+      FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
+      _mesa_reference_shader_program(ctx, &ctx->Shader.CurrentProgram, shProg);
+   }
 }
 
 
@@ -1553,10 +1597,11 @@ _mesa_update_shader_textures_used(struct gl_program *prog)
 
    for (s = 0; s < MAX_SAMPLERS; s++) {
       if (prog->SamplersUsed & (1 << s)) {
-         GLuint u = prog->SamplerUnits[s];
-         GLuint t = prog->SamplerTargets[s];
-         assert(u < MAX_TEXTURE_IMAGE_UNITS);
-         prog->TexturesUsed[u] |= (1 << t);
+         GLuint unit = prog->SamplerUnits[s];
+         GLuint tgt = prog->SamplerTargets[s];
+         assert(unit < MAX_TEXTURE_IMAGE_UNITS);
+         assert(tgt < NUM_TEXTURE_TARGETS);
+         prog->TexturesUsed[unit] |= (1 << tgt);
       }
    }
 }
@@ -1611,10 +1656,8 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program,
                     GLenum type, GLsizei count, GLint elems,
                     const void *values)
 {
-   struct gl_program_parameter *param =
+   const struct gl_program_parameter *param =
       &program->Parameters->Parameters[index];
-   const GLboolean isUniformBool = is_boolean_type(param->DataType);
-   const GLboolean areIntValues = is_integer_type(type);
 
    assert(offset >= 0);
    assert(elems >= 1);
@@ -1632,23 +1675,17 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program,
 
    if (param->Type == PROGRAM_SAMPLER) {
       /* This controls which texture unit which is used by a sampler */
-      GLuint texUnit, sampler;
+      GLboolean changed = GL_FALSE;
       GLint i;
 
-      /* data type for setting samplers must be int */
-      if (type != GL_INT) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glUniform(only glUniform1i can be used "
-                     "to set sampler uniforms)");
-         return;
-      }
+      /* this should have been caught by the compatible_types() check */
+      ASSERT(type == GL_INT);
 
-      /* XXX arrays of samplers haven't been tested much, but it's not a
-       * common thing...
-       */
+      /* loop over number of samplers to change */
       for (i = 0; i < count; i++) {
-         sampler = (GLuint) program->Parameters->ParameterValues[index + i][0];
-         texUnit = ((GLuint *) values)[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) {
@@ -1659,27 +1696,43 @@ set_program_uniform(GLcontext *ctx, struct gl_program *program,
 
          /* This maps a sampler to a texture unit: */
          if (sampler < MAX_SAMPLERS) {
-            program->SamplerUnits[sampler] = texUnit;
+#if 0
+            _mesa_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;
+            }
          }
       }
 
-      _mesa_update_shader_textures_used(program);
-
-      FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+      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);
+         ctx->Driver.ProgramStringNotify(ctx, program->Target, program);
+      }
    }
    else {
       /* ordinary uniform variable */
-      GLsizei k, i;
+      const GLboolean isUniformBool = is_boolean_type(param->DataType);
+      const GLboolean areIntValues = is_integer_type(type);
       const GLint slots = (param->Size + 3) / 4;
       const GLint typeSize = sizeof_glsl_type(param->DataType);
+      GLsizei k, i;
 
-      if (param->Size > typeSize) {
+      if ((GLint) param->Size > typeSize) {
          /* an array */
          /* we'll ignore extra data below */
       }
       else {
-         /* non-array: count must be one */
-         if (count != 1) {
+         /* 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 is not an array)");
             return;
@@ -1856,20 +1909,27 @@ set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program,
                            GLboolean transpose, const GLfloat *values)
 {
    GLuint mat, row, col;
-   GLuint dst = index + offset, src = 0;
+   GLuint src = 0;
+   const struct gl_program_parameter * param = &program->Parameters->Parameters[index];
+   const GLuint slots = (param->Size + 3) / 4;
+   const GLint typeSize = sizeof_glsl_type(param->DataType);
    GLint nr, nc;
 
    /* check that the number of rows, columns is correct */
-   get_matrix_dims(program->Parameters->Parameters[index].DataType, &nr, &nc);
+   get_matrix_dims(param->DataType, &nr, &nc);
    if (rows != nr || cols != nc) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glUniformMatrix(matrix size mismatch)");
       return;
    }
 
-   if (index + offset > program->Parameters->Size) {
-      /* out of bounds! */
-      return;
+   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;
+      }
    }
 
    /*
@@ -1883,7 +1943,12 @@ set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program,
 
       /* each matrix: */
       for (col = 0; col < cols; col++) {
-         GLfloat *v = program->Parameters->ParameterValues[dst];
+         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];
@@ -1892,7 +1957,8 @@ set_program_uniform_matrix(GLcontext *ctx, struct gl_program *program,
                v[row] = values[src + col * rows + row];
             }
          }
-         dst++;
+
+         offset++;
       }
 
       src += rows * cols;  /* next matrix */
@@ -1966,19 +2032,74 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,
 }
 
 
-static void
-_mesa_validate_program(GLcontext *ctx, GLuint program)
+/**
+ * Validate a program's samplers.
+ * Specifically, check that there aren't two samplers of different types
+ * pointing to the same texture unit.
+ * \return GL_TRUE if valid, GL_FALSE if invalid
+ */
+static GLboolean
+validate_samplers(GLcontext *ctx, const struct gl_program *prog, char *errMsg)
 {
-   struct gl_shader_program *shProg;
+   static const char *targetName[] = {
+      "TEXTURE_2D_ARRAY",
+      "TEXTURE_1D_ARRAY",
+      "TEXTURE_CUBE",
+      "TEXTURE_3D",
+      "TEXTURE_RECT",
+      "TEXTURE_2D",
+      "TEXTURE_1D",
+   };
+   GLint targetUsed[MAX_TEXTURE_IMAGE_UNITS];
+   GLbitfield samplersUsed = prog->SamplersUsed;
+   GLuint i;
 
-   shProg = _mesa_lookup_shader_program_err(ctx, program, "glValidateProgram");
-   if (!shProg) {
-      return;
+   assert(Elements(targetName) == NUM_TEXTURE_TARGETS);
+
+   if (samplersUsed == 0x0)
+      return GL_TRUE;
+
+   for (i = 0; i < Elements(targetUsed); i++)
+      targetUsed[i] = -1;
+
+   /* walk over bits which are set in 'samplers' */
+   while (samplersUsed) {
+      GLuint unit;
+      gl_texture_index target;
+      GLint sampler = _mesa_ffs(samplersUsed) - 1;
+      assert(sampler >= 0);
+      assert(sampler < MAX_TEXTURE_IMAGE_UNITS);
+      unit = prog->SamplerUnits[sampler];
+      target = prog->SamplerTargets[sampler];
+      if (targetUsed[unit] != -1 && targetUsed[unit] != target) {
+         _mesa_snprintf(errMsg, 100,
+                       "Texture unit %d is accessed both as %s and %s",
+                       unit, targetName[targetUsed[unit]], targetName[target]);
+         return GL_FALSE;
+      }
+      targetUsed[unit] = target;
+      samplersUsed ^= (1 << sampler);
    }
 
+   return GL_TRUE;
+}
+
+
+/**
+ * Do validation of the given shader program.
+ * \param errMsg  returns error message if validation fails.
+ * \return GL_TRUE if valid, GL_FALSE if invalid (and set errMsg)
+ */
+GLboolean
+_mesa_validate_shader_program(GLcontext *ctx,
+                              const struct gl_shader_program *shProg,
+                              char *errMsg)
+{
+   const struct gl_vertex_program *vp = shProg->VertexProgram;
+   const struct gl_fragment_program *fp = shProg->FragmentProgram;
+
    if (!shProg->LinkStatus) {
-      shProg->Validated = GL_FALSE;
-      return;
+      return GL_FALSE;
    }
 
    /* From the GL spec, a program is invalid if any of these are true:
@@ -1996,7 +2117,44 @@ _mesa_validate_program(GLcontext *ctx, GLuint program)
      image units allowed.
    */
 
-   shProg->Validated = GL_TRUE;
+
+   /*
+    * Check: any two active samplers in the current program object are of
+    * different types, but refer to the same texture image unit,
+    */
+   if (vp && !validate_samplers(ctx, &vp->Base, errMsg)) {
+      return GL_FALSE;
+   }
+   if (fp && !validate_samplers(ctx, &fp->Base, errMsg)) {
+      return GL_FALSE;
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Called via glValidateProgram()
+ */
+static void
+_mesa_validate_program(GLcontext *ctx, GLuint program)
+{
+   struct gl_shader_program *shProg;
+   char errMsg[100];
+
+   shProg = _mesa_lookup_shader_program_err(ctx, program, "glValidateProgram");
+   if (!shProg) {
+      return;
+   }
+
+   shProg->Validated = _mesa_validate_shader_program(ctx, shProg, errMsg);
+   if (!shProg->Validated) {
+      /* update info log */
+      if (shProg->InfoLog) {
+         _mesa_free(shProg->InfoLog);
+      }
+      shProg->InfoLog = _mesa_strdup(errMsg);
+   }
 }