[Mesa-dev] [PATCH 11/12] sso: implement ValidateProgramPipeline and GetProgramPipelineInfoLog

Gregory Hainaut gregory.hainaut at gmail.com
Fri May 3 10:44:15 PDT 2013


Implementation note:
I don't use context for ralloc (don't know how).

The check on PROGRAM_SEPARABLE flags is also done when the pipeline isn't bound.
It doesn't make any sense in a DSA style API.

Maybe we could replace _mesa_validate_program_pipeline by  _mesa_validate_program_pipeline.
For example we could recreate a dummy pipeline object. However the new function checks
also the TEXTURE_IMAGE_UNIT number not sure of the impact.

V2:
Fix memory leak with ralloc_strdup
Formatting improvement
---
 src/mesa/main/context.c         |    9 ++
 src/mesa/main/mtypes.h          |    2 +
 src/mesa/main/pipelineobj.c     |  221 ++++++++++++++++++++++++++++++++++++++-
 src/mesa/main/pipelineobj.h     |    3 +
 src/mesa/main/uniform_query.cpp |   71 +++++++++++++
 src/mesa/main/uniforms.h        |    3 +
 6 files changed, 305 insertions(+), 4 deletions(-)

diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index 6a0619a..559e21f 100644
--- a/src/mesa/main/context.c
+++ b/src/mesa/main/context.c
@@ -1767,6 +1767,7 @@ _mesa_check_blend_func_error(struct gl_context *ctx)
  * Prior to drawing anything with glBegin, glDrawArrays, etc. this function
  * is called to see if it's valid to render.  This involves checking that
  * the current shader is valid and the framebuffer is complete.
+ * It also check the current pipeline object is valid if any.
  * If an error is detected it'll be recorded here.
  * \return GL_TRUE if OK to render, GL_FALSE if not
  */
@@ -1876,6 +1877,14 @@ _mesa_valid_to_render(struct gl_context *ctx, const char *where)
       }
    }
 
+   /* A pipeline object is bound */
+   if (ctx->_Shader->Name && !ctx->_Shader->Validated) {
+      /* Error message will be printed inside _mesa_validate_program_pipeline */
+      if (!_mesa_validate_program_pipeline(ctx, ctx->_Shader, GL_TRUE)) {
+         return GL_FALSE;
+      }
+   }
+
    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
                   "%s(incomplete framebuffer)", where);
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index adf518b..ce88860 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -2424,6 +2424,8 @@ struct gl_pipeline_object
    GLboolean Validated;                 /**< Pipeline Validation status */
 
    GLboolean EverBound;                 /**< Has the pipeline object been created */
+
+   GLchar *InfoLog;
 };
 
 /**
diff --git a/src/mesa/main/pipelineobj.c b/src/mesa/main/pipelineobj.c
index 2d18192..d7948ff 100644
--- a/src/mesa/main/pipelineobj.c
+++ b/src/mesa/main/pipelineobj.c
@@ -87,6 +87,7 @@ _mesa_new_pipeline_object(struct gl_context *ctx, GLuint name)
       _glthread_INIT_MUTEX(obj->Mutex);
       obj->RefCount = 1;
       obj->Flags = _mesa_get_shader_flags();
+      obj->InfoLog = ralloc_strdup(obj, "");
    }
 
    return obj;
@@ -339,13 +340,15 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
     *    disabled (tessellation control and evaluation, geometry), or have
     *    undefined results (core profile vertex and fragment).
     */
-
    if (stages & GL_VERTEX_SHADER_BIT)
       _mesa_use_shader_program(ctx, GL_VERTEX_SHADER, shProg, pipe);
    if (stages & GL_FRAGMENT_SHADER_BIT)
       _mesa_use_shader_program(ctx, GL_FRAGMENT_SHADER, shProg, pipe);
    if (stages & GL_GEOMETRY_SHADER_BIT)
       _mesa_use_shader_program(ctx, GL_GEOMETRY_SHADER_ARB, shProg, pipe);
+
+   /* Validation would need to be redone */
+   pipe->Validated = GL_FALSE;
 }
 
 /**
@@ -573,11 +576,10 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
       *params = pipe->ActiveProgram ? pipe->ActiveProgram->Name : 0;
       return;
    case GL_INFO_LOG_LENGTH:
-      // TODO
-      *params = 0;
+      *params = pipe->Validated;
       return;
    case GL_VALIDATE_STATUS:
-      *params = pipe->ValidationStatus;
+      *params = pipe->Validated;
       return;
    case GL_VERTEX_SHADER:
       *params = pipe->CurrentVertexProgram ? pipe->CurrentVertexProgram->Name : 0;
@@ -606,16 +608,227 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
          _mesa_lookup_enum_by_nr(pname));
 }
 
+static GLboolean
+ProgramEnabledEverywhere(struct gl_pipeline_object *pipe,
+                         struct gl_shader_program *prog,
+                         char *errMsg, size_t errMsgLength)
+{
+   if (!prog) return GL_TRUE;
+
+   GLboolean status = GL_TRUE;
+
+   if (prog->_LinkedShaders[MESA_SHADER_VERTEX]) {
+      if (pipe->CurrentVertexProgram) {
+         if (prog->Name != pipe->CurrentVertexProgram->Name) {
+            status = GL_FALSE;
+         }
+      } else {
+         status = GL_FALSE;
+      }
+   }
+
+   if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT]) {
+      if (pipe->CurrentFragmentProgram) {
+         if (prog->Name != pipe->CurrentFragmentProgram->Name) {
+            status = GL_FALSE;
+         }
+      } else {
+         status = GL_FALSE;
+      }
+   }
+
+   if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY]) {
+      if (pipe->CurrentGeometryProgram) {
+         if (prog->Name != pipe->CurrentGeometryProgram->Name) {
+            status = GL_FALSE;
+         }
+      } else {
+         status = GL_FALSE;
+      }
+   }
+
+   if (!status) {
+      _mesa_snprintf(errMsg, errMsgLength,
+            "Program %d is not active for all shaders that was linked",
+            prog->Name);
+   }
+
+   return status;
+}
+
+extern GLboolean
+_mesa_validate_program_pipeline(struct gl_context* ctx,
+                                struct gl_pipeline_object *pipe,
+                                GLboolean IsBound)
+{
+   char errMsg[200] = "";
+   const GLuint errMsgLength = 200;
+
+   pipe->Validated = GL_FALSE;
+   pipe->InfoLog = ralloc_strdup(pipe, "");
+
+   /*
+    * A program object is active for at least one, but not all of the shader
+    * stages that were present when the program was linked.
+    */
+   if (!ProgramEnabledEverywhere(pipe, pipe->CurrentVertexProgram, errMsg, errMsgLength)) {
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+   if (!ProgramEnabledEverywhere(pipe, pipe->CurrentGeometryProgram, errMsg, errMsgLength)) {
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+   if (!ProgramEnabledEverywhere(pipe, pipe->CurrentFragmentProgram, errMsg, errMsgLength)) {
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+
+   /*
+    * One program object is active for at least two shader stages and a second
+    * program is active for a shader stage between two stages for which the
+    * first program was active. The active compute shader is ignored for the
+    * purposes of this test.
+    */
+   /* Without Tesselation, the only case is geometry shader between Fragment and Vertex */
+   if (pipe->CurrentGeometryProgram && pipe->CurrentFragmentProgram
+         && pipe->CurrentVertexProgram) {
+      if (pipe->CurrentVertexProgram->Name == pipe->CurrentGeometryProgram->Name &&
+          pipe->CurrentGeometryProgram->Name != pipe->CurrentVertexProgram->Name) {
+         _mesa_snprintf(errMsg, errMsgLength,
+               "Program %d is active for geometry stage between two"
+               "stages for which another program %d is active",
+               pipe->CurrentGeometryProgram->Name, pipe->CurrentVertexProgram->Name);
+         if (IsBound)
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glValidateProgramPipeline failed to validate the pipeline");
+         goto err;
+      }
+   }
+
+   /*
+    * There is an active program for tessellation control, tessellation evaluation, or
+    * geometry stages with corresponding executable shader, but there is no active
+    * program with executable vertex shader.
+    */
+   if (!pipe->CurrentVertexProgram && pipe->CurrentGeometryProgram) {
+         _mesa_snprintf(errMsg, errMsgLength, "Program miss a vertex shader");
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+
+   /*
+    * There is no current program object specified by UseProgram, there is a cur-
+    * rent program pipeline object, and the current program for any shader stage
+    * has been relinked since being applied to the pipeline object via UsePro-
+    * gramStages with the PROGRAM_SEPARABLE parameter set to FALSE.
+    */
+   if (pipe->CurrentVertexProgram && !pipe->CurrentVertexProgram->SeparateShader) {
+      _mesa_snprintf(errMsg, errMsgLength,
+            "Program %d was relinked without PROGRAM_SEPARABLE state",
+            pipe->CurrentVertexProgram->Name);
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+   if (pipe->CurrentFragmentProgram && !pipe->CurrentFragmentProgram->SeparateShader) {
+      _mesa_snprintf(errMsg, errMsgLength,
+            "Program %d was relinked without PROGRAM_SEPARABLE state",
+            pipe->CurrentFragmentProgram->Name);
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+   if (pipe->CurrentGeometryProgram && !pipe->CurrentGeometryProgram->SeparateShader) {
+      _mesa_snprintf(errMsg, errMsgLength,
+            "Program %d was relinked without PROGRAM_SEPARABLE state",
+            pipe->CurrentGeometryProgram->Name);
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+
+   /*
+    * The sum of the number of active samplers for each active program exceeds
+    * the maximum number of texture image units allowed.
+    *
+    * Any two active samplers in the set of active program objects are of different
+    * types, but refer to the same texture image unit.
+    */
+   if (!_mesa_sampler_uniforms_pipeline_are_valid(pipe, errMsg, errMsgLength)) {
+      if (IsBound)
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glValidateProgramPipeline failed to validate the pipeline");
+      goto err;
+   }
+
+   /*
+    * The sum of the number of active shader storage blocks used by the current
+    * program objects exceeds the combined limit on the number of active shader
+    * storage blocks (the value of MAX_COMBINED_SHADER_STORAGE_BLOCKS).
+    */
+   /* NOT YET SUPPORTED */
+
+   pipe->Validated = GL_TRUE;
+
+err:
+   if (!pipe->Validated) {
+      /* update info log */
+      if (pipe->InfoLog) {
+         ralloc_free(pipe->InfoLog);
+      }
+      pipe->InfoLog = ralloc_strdup(pipe, errMsg);
+   }
+
+   return pipe->Validated;
+}
+
 /**
  * Check compatibility of pipeline's program
  */
 void GLAPIENTRY
 _mesa_ValidateProgramPipeline(GLuint pipeline)
 {
+   GET_CURRENT_CONTEXT(ctx);
+
+   struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+
+   if (!pipe) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glValidateProgramPipeline(pipeline)");
+      return;
+   }
+
+   _mesa_validate_program_pipeline(ctx, pipe, (ctx->_Shader->Name == pipe->Name));
 }
 
 void GLAPIENTRY
 _mesa_GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize,
       GLsizei *length, GLchar *infoLog)
 {
+   GET_CURRENT_CONTEXT(ctx);
+
+   struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+
+   if (!pipe) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramPipelineInfoLog(pipeline)");
+      return;
+   }
+
+   if (bufSize < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramPipelineInfoLog(bufSize)");
+      return;
+   }
+
+   _mesa_copy_string(infoLog, bufSize, length, pipe->InfoLog);
 }
diff --git a/src/mesa/main/pipelineobj.h b/src/mesa/main/pipelineobj.h
index 8a38aab..48b03cc 100644
--- a/src/mesa/main/pipelineobj.h
+++ b/src/mesa/main/pipelineobj.h
@@ -62,6 +62,9 @@ _mesa_reference_pipeline_object(struct gl_context *ctx,
       _mesa_reference_pipeline_object_(ctx, ptr, obj);
 }
 
+extern GLboolean
+_mesa_validate_program_pipeline(struct gl_context * ctx, struct gl_pipeline_object *pipe, GLboolean IsBound);
+
 
 extern void GLAPIENTRY
 _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program);
diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp
index 59f3ad1..4dba920 100644
--- a/src/mesa/main/uniform_query.cpp
+++ b/src/mesa/main/uniform_query.cpp
@@ -1046,3 +1046,74 @@ _mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg,
 
    return true;
 }
+
+extern "C" bool
+_mesa_sampler_uniforms_pipeline_are_valid(const struct gl_pipeline_object *pipeline,
+                                          char *errMsg, size_t errMsgLength)
+{
+   /*
+    * An INVALID_OPERATION error is generated by any command that transfers
+    * vertices to the GL or launches compute work if the current set of active
+    * program objects cannot be executed, for reasons including:
+    *
+    * The sum of the number of active samplers for each active program exceeds
+    * the maximum number of texture image units allowed.
+    *
+    * Any two active samplers in the set of active program objects are of
+    * different types, but refer to the same texture image unit.
+    */
+   GLuint active_samplers = 0;
+   const struct gl_shader_program* shProg[3] = { pipeline->CurrentVertexProgram,
+                                                pipeline->CurrentFragmentProgram,
+                                                pipeline->CurrentGeometryProgram };
+
+   const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+   memset(unit_types, 0, sizeof(unit_types));
+
+   for (unsigned idx = 0; idx < 3; 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) {
+               _mesa_snprintf(errMsg, errMsgLength,
+                     "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) {
+      _mesa_snprintf(errMsg, errMsgLength,
+            "the number of active samplers %d exceed the maximum %d",
+            active_samplers, MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+      return false;
+   }
+
+   return true;
+}
diff --git a/src/mesa/main/uniforms.h b/src/mesa/main/uniforms.h
index 8c67697..7aae7ae 100644
--- a/src/mesa/main/uniforms.h
+++ b/src/mesa/main/uniforms.h
@@ -296,6 +296,9 @@ _mesa_update_shader_textures_used(struct gl_shader_program *shProg,
 extern bool
 _mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg,
 				 char *errMsg, size_t errMsgLength);
+extern bool
+_mesa_sampler_uniforms_pipeline_are_valid(const struct gl_pipeline_object *pipeline,
+				 char *errMsg, size_t errMsgLength);
 
 extern const struct gl_program_parameter *
 get_uniform_parameter(struct gl_shader_program *shProg, GLint index);
-- 
1.7.10.4



More information about the mesa-dev mailing list