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

gregory gregory.hainaut at gmail.com
Fri Apr 5 14:27:44 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.
---
 src/mesa/main/context.c         |    9 ++
 src/mesa/main/mtypes.h          |    2 +
 src/mesa/main/pipelineobj.c     |  202 ++++++++++++++++++++++++++++++++++++++-
 src/mesa/main/pipelineobj.h     |    3 +
 src/mesa/main/uniform_query.cpp |   71 ++++++++++++++
 src/mesa/main/uniforms.h        |    3 +
 6 files changed, 287 insertions(+), 3 deletions(-)

diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index 6a0619a..243f957 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->ValidationStatus) {
+      /* 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 dc54f3d..c551273 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -2423,6 +2423,8 @@ struct gl_shader_state
    GLboolean ValidationStatus;          /**< 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 7351d45..d90ef9b 100644
--- a/src/mesa/main/pipelineobj.c
+++ b/src/mesa/main/pipelineobj.c
@@ -66,6 +66,7 @@ _mesa_delete_pipeline_object( struct gl_context *ctx, struct gl_shader_state *ob
    _mesa_reference_shader_program(ctx, &obj->CurrentVertexProgram, NULL);
    _mesa_reference_shader_program(ctx, &obj->CurrentGeometryProgram, NULL);
    _mesa_reference_shader_program(ctx, &obj->ActiveProgram, NULL);
+   ralloc_free(obj->InfoLog);
    _glthread_DESTROY_MUTEX(obj->Mutex);
    free(obj);
 }
@@ -83,6 +84,7 @@ _mesa_new_pipeline_object( struct gl_context *ctx, GLuint name )
       _glthread_INIT_MUTEX(obj->Mutex);
       obj->RefCount = 1;
       obj->Flags = get_shader_flags();
+      obj->InfoLog = ralloc_strdup(NULL, "");
    }
 
    return obj;
@@ -329,13 +331,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->ValidationStatus = GL_FALSE;
 }
 
 /**
@@ -557,8 +561,7 @@ _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->InfoLog ? strlen(pipe->InfoLog) + 1 : 0;
          return;
       case GL_VALIDATE_STATUS:
          *params = pipe->ValidationStatus;
@@ -590,15 +593,208 @@ _mesa_GetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params)
          _mesa_lookup_enum_by_nr(pname));
 }
 
+static GLboolean
+ProgramEnabledEverywhere(struct gl_shader_state *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_shader_state *pipe, GLboolean IsBound)
+{
+   char errMsg[200] = "";
+   const GLuint errMsgLength = 200;
+
+   pipe->ValidationStatus = GL_FALSE;
+   pipe->InfoLog = ralloc_strdup(NULL, "");
+
+   /*
+    * 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->ValidationStatus = GL_TRUE;
+
+err:
+   if (!pipe->ValidationStatus) {
+      /* update info log */
+      if (pipe->InfoLog) {
+         ralloc_free(pipe->InfoLog);
+      }
+      pipe->InfoLog = ralloc_strdup(NULL, errMsg);
+   }
+
+   return pipe->ValidationStatus;
+}
+
 /**
  * Check compatibility of pipeline's program
  */
 void GLAPIENTRY
 _mesa_ValidateProgramPipeline (GLuint pipeline)
 {
+   GET_CURRENT_CONTEXT(ctx);
+
+   struct gl_shader_state *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_shader_state *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 b0ee233..7e7f621 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, PipelineObj);
 }
 
+extern GLboolean
+_mesa_validate_program_pipeline(struct gl_context * ctx, struct gl_shader_state *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..777fdaf 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_shader_state *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 7b55246..f3b980c 100644
--- a/src/mesa/main/uniforms.h
+++ b/src/mesa/main/uniforms.h
@@ -287,6 +287,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_shader_state *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