mesa/sso: Implement ValidateProgramPipeline
authorGregory Hainaut <gregory.hainaut@gmail.com>
Sat, 29 Jun 2013 02:30:44 +0000 (19:30 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Tue, 25 Mar 2014 17:25:26 +0000 (10:25 -0700)
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 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

V3 (idr):
* Actually fix the leak of the InfoLog. :)
* Directly generate logs in to gl_pipeline_object::InfoLog via
  ralloc_asprintf isntead of using a temporary buffer.
* Split out from previous uber patch.
* Change spec references to include section numbers, etc.
* Fix a bug in checking that a different program isn't active in a stage
  between two stages that have the same program.  Specifically,

 if (pipe->CurrentVertexProgram->Name == pipe->CurrentGeometryProgram->Name &&
     pipe->CurrentGeometryProgram->Name != pipe->CurrentVertexProgram->Name)

should have been

 if (pipe->CurrentVertexProgram->Name == pipe->CurrentFragmentProgram->Name &&
     pipe->CurrentGeometryProgram->Name != pipe->CurrentVertexProgram->Name)

v4 (idr): Rework to use CurrentProgram array in loops.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
src/mesa/main/context.c
src/mesa/main/mtypes.h
src/mesa/main/pipelineobj.c
src/mesa/main/pipelineobj.h

index 5d580fab78695b25208895ecb11efa1f67b18505..860ae8605314766f04d90ed9ac3ed4aad4a52874 100644 (file)
@@ -1841,6 +1841,7 @@ shader_linked_or_absent(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
  */
@@ -1892,6 +1893,15 @@ _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);
index c18ff1edab7a7529e8c10e2416934ffec1b5618e..33cb88881364ba63653d5e43d6516307bbb81a6d 100644 (file)
@@ -2808,6 +2808,8 @@ struct gl_pipeline_object
 
    GLboolean EverBound;                 /**< Has the pipeline object been created */
 
+   GLboolean Validated;                 /**< Pipeline Validation status */
+
    GLchar *InfoLog;
 };
 
index 2d079cc75452ab5a2a958a56e7a61f9efa965f69..49fb64151a53bfca1cc4cfc406c587618a552311 100644 (file)
@@ -576,9 +576,7 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
       *params = pipe->InfoLog ? strlen(pipe->InfoLog) + 1 : 0;
       return;
    case GL_VALIDATE_STATUS:
-      /* FINISHME: Implement validation status.
-       */
-      *params = 0;
+      *params = pipe->Validated;
       return;
    case GL_VERTEX_SHADER:
       *params = pipe->CurrentProgram[MESA_SHADER_VERTEX]
@@ -608,12 +606,199 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
                _mesa_lookup_enum_by_nr(pname));
 }
 
+/**
+ * Determines whether every stage in a linked program is active in the
+ * specified pipeline.
+ */
+static bool
+program_stages_all_active(struct gl_pipeline_object *pipe,
+                          const struct gl_shader_program *prog)
+{
+   unsigned i;
+   bool status = true;
+
+   if (!prog)
+      return true;
+
+   for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      if (prog->_LinkedShaders[i]) {
+         if (pipe->CurrentProgram[i]) {
+            if (prog->Name != pipe->CurrentProgram[i]->Name) {
+               status = false;
+            }
+         } else {
+            status = false;
+         }
+      }
+   }
+
+   if (!status) {
+      pipe->InfoLog = ralloc_asprintf(pipe,
+                                      "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)
+{
+   unsigned i;
+
+   pipe->Validated = GL_FALSE;
+
+   /* Release and reset the info log.
+    */
+   if (pipe->InfoLog != NULL)
+      ralloc_free(pipe->InfoLog);
+
+   pipe->InfoLog = NULL;
+
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         - A program object is active for at least one, but not all of
+    *           the shader stages that were present when the program was
+    *           linked."
+    *
+    * For each possible program stage, verify that the program bound to that
+    * stage has all of its stages active.  In other words, if the program
+    * bound to the vertex stage also has a fragment shader, the fragment
+    * shader must also be bound to the fragment stage.
+    */
+   for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      if (!program_stages_all_active(pipe, pipe->CurrentProgram[i])) {
+         goto err;
+      }
+   }
+
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         ...
+    *
+    *         - 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."
+    *
+    * Without Tesselation, the only case where this can occur is the geometry
+    * shader between the fragment shader and vertex shader.
+    */
+   if (pipe->CurrentProgram[MESA_SHADER_GEOMETRY]
+       && pipe->CurrentProgram[MESA_SHADER_FRAGMENT]
+       && pipe->CurrentProgram[MESA_SHADER_VERTEX]) {
+      if (pipe->CurrentProgram[MESA_SHADER_VERTEX]->Name == pipe->CurrentProgram[MESA_SHADER_FRAGMENT]->Name &&
+          pipe->CurrentProgram[MESA_SHADER_GEOMETRY]->Name != pipe->CurrentProgram[MESA_SHADER_VERTEX]->Name) {
+         pipe->InfoLog =
+            ralloc_asprintf(pipe,
+                            "Program %d is active for geometry stage between "
+                            "two stages for which another program %d is "
+                            "active",
+                            pipe->CurrentProgram[MESA_SHADER_GEOMETRY]->Name,
+                            pipe->CurrentProgram[MESA_SHADER_VERTEX]->Name);
+         goto err;
+      }
+   }
+
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         ...
+    *
+    *         - 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->CurrentProgram[MESA_SHADER_VERTEX]
+       && pipe->CurrentProgram[MESA_SHADER_GEOMETRY]) {
+      pipe->InfoLog = ralloc_strdup(pipe, "Program lacks a vertex shader");
+      goto err;
+   }
+
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         ...
+    *
+    *         - There is no current program object specified by UseProgram,
+    *           there is a current program pipeline object, and the current
+    *           program for any shader stage has been relinked since being
+    *           applied to the pipeline object via UseProgramStages with the
+    *           PROGRAM_SEPARABLE parameter set to FALSE.
+    */
+   for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      if (pipe->CurrentProgram[i] && !pipe->CurrentProgram[i]->SeparateShader) {
+         pipe->InfoLog = ralloc_asprintf(pipe,
+                                         "Program %d was relinked without "
+                                         "PROGRAM_SEPARABLE state",
+                                         pipe->CurrentProgram[i]->Name);
+         goto err;
+      }
+   }
+
+   /* Section 2.11.11 (Shader Execution), subheading "Validation," of the
+    * OpenGL 4.1 spec says:
+    *
+    *     "[INVALID_OPERATION] is generated by any command that transfers
+    *     vertices to the GL if:
+    *
+    *         ...
+    *
+    *         - Any two active samplers in the current program object are of
+    *           different types, but refer to the same texture image unit.
+    *
+    *         - The number of active samplers in the program exceeds the
+    *           maximum number of texture image units allowed."
+    */
+   if (!_mesa_sampler_uniforms_pipeline_are_valid(pipe))
+      goto err;
+
+   pipe->Validated = GL_TRUE;
+   return GL_TRUE;
+
+err:
+   if (IsBound)
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glValidateProgramPipeline failed to validate the pipeline");
+
+   return GL_FALSE;
+}
+
 /**
  * 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
index 46d5fab4230a5fc168f08235afa1f1c41c33c6f4..ceaf4f14ce98b02cbcf52e72986b7bc509a563cb 100644 (file)
@@ -59,6 +59,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);