mesa: Free all shader program data before deleting all
[mesa.git] / src / mesa / shader / shader_api.c
index 3a54e68d0de8374fb31a2aca2dd6efc0b21fcfe0..01a237c525a231047b0f2d9e2936a0c82c806af4 100644 (file)
 #include "prog_parameter.h"
 #include "prog_print.h"
 #include "prog_statevars.h"
-#include "shader_api.h"
-
-#include "slang_compile.h"
-#include "slang_link.h"
+#include "shader/shader_api.h"
+#include "shader/slang/slang_compile.h"
+#include "shader/slang/slang_link.h"
 
 
 
@@ -80,7 +79,7 @@ _mesa_clear_shader_program_data(GLcontext *ctx,
          /* to prevent a double-free in the next call */
          shProg->VertexProgram->Base.Parameters = NULL;
       }
-      _mesa_delete_program(ctx, &shProg->VertexProgram->Base);
+      ctx->Driver.DeleteProgram(ctx, &shProg->VertexProgram->Base);
       shProg->VertexProgram = NULL;
    }
 
@@ -89,7 +88,7 @@ _mesa_clear_shader_program_data(GLcontext *ctx,
          /* to prevent a double-free in the next call */
          shProg->FragmentProgram->Base.Parameters = NULL;
       }
-      _mesa_delete_program(ctx, &shProg->FragmentProgram->Base);
+      ctx->Driver.DeleteProgram(ctx, &shProg->FragmentProgram->Base);
       shProg->FragmentProgram = NULL;
    }
 
@@ -128,6 +127,8 @@ _mesa_free_shader_program_data(GLcontext *ctx,
    for (i = 0; i < shProg->NumShaders; i++) {
       _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL);
    }
+   shProg->NumShaders = 0;
+
    if (shProg->Shaders) {
       _mesa_free(shProg->Shaders);
       shProg->Shaders = NULL;
@@ -247,7 +248,7 @@ _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh)
       _mesa_free(sh->InfoLog);
    for (i = 0; i < sh->NumPrograms; i++) {
       assert(sh->Programs[i]);
-      _mesa_delete_program(ctx, sh->Programs[i]);
+      ctx->Driver.DeleteProgram(ctx, sh->Programs[i]);
    }
    if (sh->Programs)
       _mesa_free(sh->Programs);
@@ -369,6 +370,54 @@ copy_string(GLchar *dst, GLsizei maxLength, GLsizei *length, const GLchar *src)
 }
 
 
+/**
+ * Return size (in floats) of the given GLSL type.
+ * See also _slang_sizeof_type_specifier().
+ */
+static GLint
+sizeof_glsl_type(GLenum type)
+{
+   switch (type) {
+   case GL_BOOL:
+   case GL_FLOAT:
+   case GL_INT:
+      return 1;
+   case GL_BOOL_VEC2:
+   case GL_FLOAT_VEC2:
+   case GL_INT_VEC2:
+      return 2;
+   case GL_BOOL_VEC3:
+   case GL_FLOAT_VEC3:
+   case GL_INT_VEC3:
+      return 3;
+   case GL_BOOL_VEC4:
+   case GL_FLOAT_VEC4:
+   case GL_INT_VEC4:
+      return 4;
+   case GL_FLOAT_MAT2:
+      return 8;  /* 2 rows of 4, actually */
+   case GL_FLOAT_MAT3:
+      return 12;  /* 3 rows of 4, actually */
+   case GL_FLOAT_MAT4:
+      return 16;
+   case GL_FLOAT_MAT2x3:
+      return 8;   /* 2 rows of 4, actually */
+   case GL_FLOAT_MAT2x4:
+      return 8;
+   case GL_FLOAT_MAT3x2:
+      return 12;  /* 3 rows of 4, actually */
+   case GL_FLOAT_MAT3x4:
+      return 12;
+   case GL_FLOAT_MAT4x2:
+      return 16;  /* 4 rows of 4, actually */
+   case GL_FLOAT_MAT4x3:
+      return 16;  /* 4 rows of 4, actually */
+   default:
+      return 0; /* error */
+   }
+}
+
+
 /**
  * Called via ctx->Driver.AttachShader()
  */
@@ -378,7 +427,7 @@ _mesa_attach_shader(GLcontext *ctx, GLuint program, GLuint shader)
    struct gl_shader_program *shProg
       = _mesa_lookup_shader_program(ctx, program);
    struct gl_shader *sh = _mesa_lookup_shader(ctx, shader);
-   const GLuint n = shProg->NumShaders;
+   GLuint n;
    GLuint i;
 
    if (!shProg || !sh) {
@@ -387,6 +436,8 @@ _mesa_attach_shader(GLcontext *ctx, GLuint program, GLuint shader)
       return;
    }
 
+   n = shProg->NumShaders;
+
    for (i = 0; i < n; i++) {
       if (shProg->Shaders[i] == sh) {
          /* already attached */
@@ -548,7 +599,7 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader)
 {
    struct gl_shader_program *shProg
       = _mesa_lookup_shader_program(ctx, program);
-   const GLuint n = shProg->NumShaders;
+   GLuint n;
    GLuint i, j;
 
    if (!shProg) {
@@ -557,6 +608,8 @@ _mesa_detach_shader(GLcontext *ctx, GLuint program, GLuint shader)
       return;
    }
 
+   n = shProg->NumShaders;
+
    for (i = 0; i < n; i++) {
       if (shProg->Shaders[i]->Name == shader) {
          /* found it */
@@ -629,9 +682,9 @@ _mesa_get_active_attrib(GLcontext *ctx, GLuint program, GLuint index,
                shProg->Attributes->Parameters[index].Name);
    sz = shProg->Attributes->Parameters[index].Size;
    if (size)
-      *size = sz;
-   if (type)
-      *type = vec_types[sz]; /* XXX this is a temporary hack */
+      *size = 1;   /* attributes may not be arrays */
+   if (type && sz > 0 && sz <= 4)  /* XXX this is a temporary hack */
+      *type = vec_types[sz - 1];
 }
 
 
@@ -662,13 +715,17 @@ _mesa_get_active_uniform(GLcontext *ctx, GLuint program, GLuint index,
       if (shProg->Uniforms->Parameters[j].Type == PROGRAM_UNIFORM ||
           shProg->Uniforms->Parameters[j].Type == PROGRAM_SAMPLER) {
          if (ind == index) {
+            GLuint uSize = shProg->Uniforms->Parameters[j].Size;
+            GLenum uType = shProg->Uniforms->Parameters[j].DataType;
             /* found it */
             copy_string(nameOut, maxLength, length,
                         shProg->Uniforms->Parameters[j].Name);
-            if (size)
-               *size = shProg->Uniforms->Parameters[j].Size;
+            if (size) {
+               /* convert from floats to 'type' (eg: sizeof(mat4x4)=1) */
+               *size = uSize / sizeof_glsl_type(uType);
+            }
             if (type)
-               *type = shProg->Uniforms->Parameters[j].DataType;
+               *type = uType;
             return;
          }
          ind++;
@@ -780,7 +837,7 @@ _mesa_get_programiv(GLcontext *ctx, GLuint program,
       *params = shProg->Validated;
       break;
    case GL_INFO_LOG_LENGTH:
-      *params = shProg->InfoLog ? strlen(shProg->InfoLog) : 0;
+      *params = shProg->InfoLog ? strlen(shProg->InfoLog) + 1 : 0;
       break;
    case GL_ATTACHED_SHADERS:
       *params = shProg->NumShaders;
@@ -832,10 +889,10 @@ _mesa_get_shaderiv(GLcontext *ctx, GLuint name, GLenum pname, GLint *params)
       *params = shader->CompileStatus;
       break;
    case GL_INFO_LOG_LENGTH:
-      *params = shader->InfoLog ? strlen(shader->InfoLog) : 0;
+      *params = shader->InfoLog ? strlen(shader->InfoLog) + 1 : 0;
       break;
    case GL_SHADER_SOURCE_LENGTH:
-      *params = shader->Source ? strlen((char *) shader->Source) : 0;
+      *params = shader->Source ? strlen((char *) shader->Source) + 1 : 0;
       break;
    default:
       _mesa_error(ctx, GL_INVALID_ENUM, "glGetShaderiv(pname)");
@@ -899,9 +956,40 @@ _mesa_get_uniformfv(GLcontext *ctx, GLuint program, GLint location,
    if (shProg) {
       GLint i;
       if (location >= 0 && location < shProg->Uniforms->NumParameters) {
-         for (i = 0; i < shProg->Uniforms->Parameters[location].Size; i++) {
-            params[i] = shProg->Uniforms->ParameterValues[location][i];
+         GLuint uSize;
+         GLenum uType;
+         GLint rows = 0;
+         uType = shProg->Uniforms->Parameters[location].DataType;
+         uSize = sizeof_glsl_type(uType);
+         /* Matrix types need special handling, because they span several
+          * parameters, and may also not be fully packed.
+          */
+         switch (shProg->Uniforms->Parameters[location].DataType) {
+            case GL_FLOAT_MAT2:
+            case GL_FLOAT_MAT3x2:
+            case GL_FLOAT_MAT4x2:
+               rows = 2;
+               break;
+            case GL_FLOAT_MAT2x3:
+            case GL_FLOAT_MAT3:
+            case GL_FLOAT_MAT4x3:
+               rows = 3;
+               break;
+            case GL_FLOAT_MAT2x4:
+            case GL_FLOAT_MAT3x4:
+            case GL_FLOAT_MAT4:
+               rows = 4;
+         }
+         if (rows != 0) {
+            GLint r, c;
+            for (c = 0, i = 0; c * 4 < uSize; c++)
+               for (r = 0; r < rows; r++, i++)
+                  params[i] = shProg->Uniforms->ParameterValues[location + c][r];
          }
+         else
+            for (i = 0; i < uSize; i++) {
+               params[i] = shProg->Uniforms->ParameterValues[location][i];
+            }
       }
       else {
          _mesa_error(ctx, GL_INVALID_VALUE, "glGetUniformfv(location)");
@@ -1055,19 +1143,28 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,
 {
    struct gl_shader_program *shProg = ctx->Shader.CurrentProgram;
    GLint elems, i, k;
+   GLenum uType;
+   GLsizei maxCount;
 
    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 */
+
+   /* The spec says this is GL_INVALID_OPERATION, although it seems like it
+    * ought to be GL_INVALID_VALUE
+    */
    if (location < 0 || location >= (GLint) shProg->Uniforms->NumParameters) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "glUniform(location)");
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(location)");
       return;
    }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM);
 
+   uType = shProg->Uniforms->Parameters[location].DataType;
    /*
     * If we're setting a sampler, we must use glUniformi1()!
     */
@@ -1115,11 +1212,37 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,
       return;
    }
 
-   if (count * elems > shProg->Uniforms->Parameters[location].Size) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(count too large)");
-      return;
+   /* OpenGL requires types to match exactly, except that one can convert
+    * float or int array to boolean array.
+    */
+   switch (uType)
+   {
+      case GL_BOOL:
+      case GL_BOOL_VEC2:
+      case GL_BOOL_VEC3:
+      case GL_BOOL_VEC4:
+         if (elems != sizeof_glsl_type(uType)) {
+            _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(count mismatch)");
+         }
+         break;
+      case PROGRAM_SAMPLER:
+         break;
+      default:
+         if (shProg->Uniforms->Parameters[location].Type != PROGRAM_SAMPLER 
+             && uType != type) {
+            _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform(type mismatch)");
+         }
+         break;
    }
 
+   /* XXX if this is a base type, then count must equal 1. However, we
+    * don't have enough information from the compiler to distinguish a
+    * base type from a 1-element array of that type. The standard allows
+    * count to overrun an array, in which case the overflow is ignored.
+    */
+   maxCount = shProg->Uniforms->Parameters[location].Size / elems;
+   if (count > maxCount) count = maxCount;
+
    for (k = 0; k < count; k++) {
       GLfloat *uniformVal = shProg->Uniforms->ParameterValues[location + k];
       if (type == GL_INT ||
@@ -1137,6 +1260,13 @@ _mesa_uniform(GLcontext *ctx, GLint location, GLsizei count,
             uniformVal[i] = fValues[i];
          }
       }
+      if (uType == GL_BOOL ||
+          uType == GL_BOOL_VEC2 ||
+          uType == GL_BOOL_VEC3 ||
+          uType == GL_BOOL_VEC4) {
+          for (i = 0; i < elems; i++)
+              uniformVal[i] = uniformVal[i] ? 1.0f : 0.0f;
+      }
    }
 
    if (shProg->Uniforms->Parameters[location].Type == PROGRAM_SAMPLER) {
@@ -1157,20 +1287,30 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,
                      GLenum matrixType, GLint location, GLsizei count,
                      GLboolean transpose, const GLfloat *values)
 {
+   GLsizei maxCount, i;
    struct gl_shader_program *shProg = ctx->Shader.CurrentProgram;
    if (!shProg || !shProg->LinkStatus) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
          "glUniformMatrix(program not linked)");
       return;
    }
-   if (location < 0 || location >= shProg->Uniforms->NumParameters) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(location)");
+   if (location == -1)
+      return;   /* The standard specifies this as a no-op */
+   /* The spec says this is GL_INVALID_OPERATION, although it seems like it
+    * ought to be GL_INVALID_VALUE
+    */
+   if (location < 0 || location >= (GLint) shProg->Uniforms->NumParameters) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glUniformMatrix(location)");
       return;
    }
    if (values == NULL) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix");
       return;
    }
+   if (count < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glUniformMatrix(count < 0)");
+      return;
+   }
 
    FLUSH_VERTICES(ctx, _NEW_PROGRAM);
 
@@ -1179,23 +1319,30 @@ _mesa_uniform_matrix(GLcontext *ctx, GLint cols, GLint rows,
     * the rows.
     */
    /* XXXX need to test 3x3 and 2x2 matrices... */
-   if (transpose) {
-      GLuint row, col;
-      for (col = 0; col < cols; col++) {
-         GLfloat *v = shProg->Uniforms->ParameterValues[location + col];
-         for (row = 0; row < rows; row++) {
-            v[row] = values[row * cols + col];
+   maxCount = shProg->Uniforms->Parameters[location].Size / (4 * cols);
+   if (count > maxCount)
+      count = maxCount;
+   for (i = 0; i < count; i++) {
+      if (transpose) {
+         GLuint row, col;
+         for (col = 0; col < cols; col++) {
+            GLfloat *v = shProg->Uniforms->ParameterValues[location + col];
+            for (row = 0; row < rows; row++) {
+               v[row] = values[row * cols + col];
+            }
          }
       }
-   }
-   else {
-      GLuint row, col;
-      for (col = 0; col < cols; col++) {
-         GLfloat *v = shProg->Uniforms->ParameterValues[location + col];
-         for (row = 0; row < rows; row++) {
-            v[row] = values[col * rows + row];
+      else {
+         GLuint row, col;
+         for (col = 0; col < cols; col++) {
+            GLfloat *v = shProg->Uniforms->ParameterValues[location + col];
+            for (row = 0; row < rows; row++) {
+               v[row] = values[col * rows + row];
+            }
          }
       }
+      location += cols;
+      values += rows * cols;
    }
 }