mesa: fix empty program log length
[mesa.git] / src / mesa / main / pipelineobj.c
index b639e9bc913532ff2f5c227fd340aaee86b79383..310b745ebc8f0d9e9613c18031f707e391f72088 100644 (file)
@@ -31,6 +31,7 @@
  * GL_ARB_separate_shader_objects extension.
  */
 
+#include <stdbool.h>
 #include "main/glheader.h"
 #include "main/context.h"
 #include "main/dispatch.h"
 #include "main/shaderobj.h"
 #include "main/transformfeedback.h"
 #include "main/uniforms.h"
+#include "compiler/glsl/glsl_parser_extras.h"
+#include "compiler/glsl/ir_uniform.h"
 #include "program/program.h"
 #include "program/prog_parameter.h"
-#include "ralloc.h"
-#include <stdbool.h>
-#include "../glsl/glsl_parser_extras.h"
-#include "../glsl/ir_uniform.h"
+#include "util/ralloc.h"
 
 /**
  * Delete a pipeline object.
@@ -65,6 +65,7 @@ _mesa_delete_pipeline_object(struct gl_context *ctx,
 
    _mesa_reference_shader_program(ctx, &obj->ActiveProgram, NULL);
    mtx_destroy(&obj->Mutex);
+   free(obj->Label);
    ralloc_free(obj);
 }
 
@@ -80,6 +81,7 @@ _mesa_new_pipeline_object(struct gl_context *ctx, GLuint name)
       mtx_init(&obj->Mutex, mtx_plain);
       obj->RefCount = 1;
       obj->Flags = _mesa_get_shader_flags();
+      obj->InfoLog = NULL;
    }
 
    return obj;
@@ -105,7 +107,7 @@ _mesa_init_pipeline(struct gl_context *ctx)
  * Callback for deleting a pipeline object.  Called by _mesa_HashDeleteAll().
  */
 static void
-delete_pipelineobj_cb(GLuint id, void *data, void *userData)
+delete_pipelineobj_cb(UNUSED GLuint id, void *data, void *userData)
 {
    struct gl_pipeline_object *obj = (struct gl_pipeline_object *) data;
    struct gl_context *ctx = (struct gl_context *) userData;
@@ -119,12 +121,12 @@ delete_pipelineobj_cb(GLuint id, void *data, void *userData)
 void
 _mesa_free_pipeline_data(struct gl_context *ctx)
 {
+   _mesa_reference_pipeline_object(ctx, &ctx->_Shader, NULL);
+
    _mesa_HashDeleteAll(ctx->Pipeline.Objects, delete_pipelineobj_cb, ctx);
    _mesa_DeleteHashTable(ctx->Pipeline.Objects);
 
-   _mesa_reference_pipeline_object(ctx, &ctx->_Shader, NULL);
    _mesa_delete_pipeline_object(ctx, ctx->Pipeline.Default);
-
 }
 
 /**
@@ -135,8 +137,8 @@ _mesa_free_pipeline_data(struct gl_context *ctx)
  * a non-existent ID.  The spec defines ID 0 as being technically
  * non-existent.
  */
-static inline struct gl_pipeline_object *
-lookup_pipeline_object(struct gl_context *ctx, GLuint id)
+struct gl_pipeline_object *
+_mesa_lookup_pipeline_object(struct gl_context *ctx, GLuint id)
 {
    if (id == 0)
       return NULL;
@@ -186,7 +188,7 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx,
       struct gl_pipeline_object *oldObj = *ptr;
 
       mtx_lock(&oldObj->Mutex);
-      ASSERT(oldObj->RefCount > 0);
+      assert(oldObj->RefCount > 0);
       oldObj->RefCount--;
       deleteFlag = (oldObj->RefCount == 0);
       mtx_unlock(&oldObj->Mutex);
@@ -197,7 +199,7 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx,
 
       *ptr = NULL;
    }
-   ASSERT(!*ptr);
+   assert(!*ptr);
 
    if (obj) {
       /* reference new pipeline object */
@@ -224,8 +226,13 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
 {
    GET_CURRENT_CONTEXT(ctx);
 
-   struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+   struct gl_pipeline_object *pipe = _mesa_lookup_pipeline_object(ctx, pipeline);
    struct gl_shader_program *shProg = NULL;
+   GLbitfield any_valid_stages;
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glUseProgramStages(%u, 0x%x, %u)\n",
+                  pipeline, stages, program);
 
    if (!pipe) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glUseProgramStages(pipeline)");
@@ -241,14 +248,15 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
     *
     *     "If stages is not the special value ALL_SHADER_BITS, and has a bit
     *     set that is not recognized, the error INVALID_VALUE is generated."
-    *
-    * NOT YET SUPPORTED:
-    * GL_TESS_CONTROL_SHADER_BIT
-    * GL_TESS_EVALUATION_SHADER_BIT
     */
-   GLbitfield any_valid_stages = GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT;
+   any_valid_stages = GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT;
    if (_mesa_has_geometry_shaders(ctx))
       any_valid_stages |= GL_GEOMETRY_SHADER_BIT;
+   if (_mesa_has_tessellation(ctx))
+      any_valid_stages |= GL_TESS_CONTROL_SHADER_BIT |
+                          GL_TESS_EVALUATION_SHADER_BIT;
+   if (_mesa_has_compute_shaders(ctx))
+      any_valid_stages |= GL_COMPUTE_SHADER_BIT;
 
    if (stages != GL_ALL_SHADER_BITS && (stages & ~any_valid_stages) != 0) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glUseProgramStages(Stages)");
@@ -324,6 +332,17 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
 
    if ((stages & GL_GEOMETRY_SHADER_BIT) != 0)
       _mesa_use_shader_program(ctx, GL_GEOMETRY_SHADER, shProg, pipe);
+
+   if ((stages & GL_TESS_CONTROL_SHADER_BIT) != 0)
+      _mesa_use_shader_program(ctx, GL_TESS_CONTROL_SHADER, shProg, pipe);
+
+   if ((stages & GL_TESS_EVALUATION_SHADER_BIT) != 0)
+      _mesa_use_shader_program(ctx, GL_TESS_EVALUATION_SHADER, shProg, pipe);
+
+   if ((stages & GL_COMPUTE_SHADER_BIT) != 0)
+      _mesa_use_shader_program(ctx, GL_COMPUTE_SHADER, shProg, pipe);
+
+   pipe->Validated = false;
 }
 
 /**
@@ -335,7 +354,10 @@ _mesa_ActiveShaderProgram(GLuint pipeline, GLuint program)
 {
    GET_CURRENT_CONTEXT(ctx);
    struct gl_shader_program *shProg = NULL;
-   struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+   struct gl_pipeline_object *pipe = _mesa_lookup_pipeline_object(ctx, pipeline);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glActiveShaderProgram(%u, %u)\n", pipeline, program);
 
    if (program != 0) {
       shProg = _mesa_lookup_shader_program_err(ctx, program,
@@ -369,6 +391,86 @@ _mesa_ActiveShaderProgram(GLuint pipeline, GLuint program)
 void GLAPIENTRY
 _mesa_BindProgramPipeline(GLuint pipeline)
 {
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_pipeline_object *newObj = NULL;
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glBindProgramPipeline(%u)\n", pipeline);
+
+   /* Rebinding the same pipeline object: no change.
+    */
+   if (ctx->_Shader->Name == pipeline)
+      return;
+
+   /* Section 2.17.2 (Transform Feedback Primitive Capture) of the OpenGL 4.1
+    * spec says:
+    *
+    *     "The error INVALID_OPERATION is generated:
+    *
+    *      ...
+    *
+    *         - by BindProgramPipeline if the current transform feedback
+    *           object is active and not paused;
+    */
+   if (_mesa_is_xfb_active_and_unpaused(ctx)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+            "glBindProgramPipeline(transform feedback active)");
+      return;
+   }
+
+   /* Get pointer to new pipeline object (newObj)
+    */
+   if (pipeline) {
+      /* non-default pipeline object */
+      newObj = _mesa_lookup_pipeline_object(ctx, pipeline);
+      if (!newObj) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glBindProgramPipeline(non-gen name)");
+         return;
+      }
+
+      /* Object is created by any Pipeline call but glGenProgramPipelines,
+       * glIsProgramPipeline and GetProgramPipelineInfoLog
+       */
+      newObj->EverBound = GL_TRUE;
+   }
+
+   _mesa_bind_pipeline(ctx, newObj);
+}
+
+void
+_mesa_bind_pipeline(struct gl_context *ctx,
+                    struct gl_pipeline_object *pipe)
+{
+   int i;
+   /* First bind the Pipeline to pipeline binding point */
+   _mesa_reference_pipeline_object(ctx, &ctx->Pipeline.Current, pipe);
+
+   /* Section 2.11.3 (Program Objects) of the OpenGL 4.1 spec says:
+    *
+    *     "If there is a current program object established by UseProgram,
+    *     that program is considered current for all stages. Otherwise, if
+    *     there is a bound program pipeline object (see section 2.11.4), the
+    *     program bound to the appropriate stage of the pipeline object is
+    *     considered current."
+    */
+   if (&ctx->Shader != ctx->_Shader) {
+      if (pipe != NULL) {
+         /* Bound the pipeline to the current program and
+          * restore the pipeline state
+          */
+         _mesa_reference_pipeline_object(ctx, &ctx->_Shader, pipe);
+      } else {
+         /* Unbind the pipeline */
+         _mesa_reference_pipeline_object(ctx, &ctx->_Shader,
+                                         ctx->Pipeline.Default);
+      }
+
+      FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
+
+      for (i = 0; i < MESA_SHADER_STAGES; i++)
+         _mesa_shader_program_init_subroutine_defaults(ctx, ctx->_Shader->CurrentProgram[i]);
+   }
 }
 
 /**
@@ -383,6 +485,9 @@ _mesa_DeleteProgramPipelines(GLsizei n, const GLuint *pipelines)
    GET_CURRENT_CONTEXT(ctx);
    GLsizei i;
 
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glDeleteProgramPipelines(%d, %p)\n", n, pipelines);
+
    if (n < 0) {
       _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteProgramPipelines(n<0)");
       return;
@@ -390,10 +495,10 @@ _mesa_DeleteProgramPipelines(GLsizei n, const GLuint *pipelines)
 
    for (i = 0; i < n; i++) {
       struct gl_pipeline_object *obj =
-         lookup_pipeline_object(ctx, pipelines[i]);
+         _mesa_lookup_pipeline_object(ctx, pipelines[i]);
 
       if (obj) {
-         ASSERT(obj->Name == pipelines[i]);
+         assert(obj->Name == pipelines[i]);
 
          /* If the pipeline object is currently bound, the spec says "If an
           * object that is currently bound is deleted, the binding for that
@@ -420,16 +525,18 @@ _mesa_DeleteProgramPipelines(GLsizei n, const GLuint *pipelines)
  * \param n       Number of IDs to generate.
  * \param pipelines  pipeline of \c n locations to store the IDs.
  */
-void GLAPIENTRY
-_mesa_GenProgramPipelines(GLsizei n, GLuint *pipelines)
+static void
+create_program_pipelines(struct gl_context *ctx, GLsizei n, GLuint *pipelines,
+                         bool dsa)
 {
-   GET_CURRENT_CONTEXT(ctx);
-
+   const char *func;
    GLuint first;
    GLint i;
 
+   func = dsa ? "glCreateProgramPipelines" : "glGenProgramPipelines";
+
    if (n < 0) {
-      _mesa_error(ctx, GL_INVALID_VALUE, "glGenProgramPipelines(n<0)");
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s (n < 0)", func);
       return;
    }
 
@@ -445,16 +552,43 @@ _mesa_GenProgramPipelines(GLsizei n, GLuint *pipelines)
 
       obj = _mesa_new_pipeline_object(ctx, name);
       if (!obj) {
-         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenProgramPipelines");
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", func);
          return;
       }
 
+      if (dsa) {
+         /* make dsa-allocated objects behave like program objects */
+         obj->EverBound = GL_TRUE;
+      }
+
       save_pipeline_object(ctx, obj);
       pipelines[i] = first + i;
    }
 
 }
 
+void GLAPIENTRY
+_mesa_GenProgramPipelines(GLsizei n, GLuint *pipelines)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glGenProgramPipelines(%d, %p)\n", n, pipelines);
+
+   create_program_pipelines(ctx, n, pipelines, false);
+}
+
+void GLAPIENTRY
+_mesa_CreateProgramPipelines(GLsizei n, GLuint *pipelines)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glCreateProgramPipelines(%d, %p)\n", n, pipelines);
+
+   create_program_pipelines(ctx, n, pipelines, true);
+}
+
 /**
  * Determine if ID is the name of an pipeline object.
  *
@@ -467,7 +601,10 @@ _mesa_IsProgramPipeline(GLuint pipeline)
 {
    GET_CURRENT_CONTEXT(ctx);
 
-   struct gl_pipeline_object *obj = lookup_pipeline_object(ctx, pipeline);
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glIsProgramPipeline(%u)\n", pipeline);
+
+   struct gl_pipeline_object *obj = _mesa_lookup_pipeline_object(ctx, pipeline);
    if (obj == NULL)
       return GL_FALSE;
 
@@ -481,11 +618,16 @@ void GLAPIENTRY
 _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
 {
    GET_CURRENT_CONTEXT(ctx);
-   struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+   struct gl_pipeline_object *pipe = _mesa_lookup_pipeline_object(ctx, pipeline);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glGetProgramPipelineiv(%u, %d, %p)\n",
+                  pipeline, pname, params);
 
    /* Are geometry shaders available in this context?
     */
    const bool has_gs = _mesa_has_geometry_shaders(ctx);
+   const bool has_tess = _mesa_has_tessellation(ctx);
 
    if (!pipe) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
@@ -503,25 +645,28 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
       *params = pipe->ActiveProgram ? pipe->ActiveProgram->Name : 0;
       return;
    case GL_INFO_LOG_LENGTH:
-      /* FINISHME: Implement the info log.
-       */
-      *params = 0;
+      *params = (pipe->InfoLog && pipe->InfoLog[0] != '\0') ?
+         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]
          ? pipe->CurrentProgram[MESA_SHADER_VERTEX]->Name : 0;
       return;
    case GL_TESS_EVALUATION_SHADER:
-      /* NOT YET SUPPORTED */
-      break;
+      if (!has_tess)
+         break;
+      *params = pipe->CurrentProgram[MESA_SHADER_TESS_EVAL]
+         ? pipe->CurrentProgram[MESA_SHADER_TESS_EVAL]->Name : 0;
+      return;
    case GL_TESS_CONTROL_SHADER:
-      /* NOT YET SUPPORTED */
-      break;
+      if (!has_tess)
+         break;
+      *params = pipe->CurrentProgram[MESA_SHADER_TESS_CTRL]
+         ? pipe->CurrentProgram[MESA_SHADER_TESS_CTRL]->Name : 0;
+      return;
    case GL_GEOMETRY_SHADER:
       if (!has_gs)
          break;
@@ -532,12 +677,274 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
       *params = pipe->CurrentProgram[MESA_SHADER_FRAGMENT]
          ? pipe->CurrentProgram[MESA_SHADER_FRAGMENT]->Name : 0;
       return;
+   case GL_COMPUTE_SHADER:
+      if (!_mesa_has_compute_shaders(ctx))
+         break;
+      *params = pipe->CurrentProgram[MESA_SHADER_COMPUTE]
+         ? pipe->CurrentProgram[MESA_SHADER_COMPUTE]->Name : 0;
+      return;
    default:
       break;
    }
 
    _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramPipelineiv(pname=%s)",
-               _mesa_lookup_enum_by_nr(pname));
+               _mesa_enum_to_string(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;
+}
+
+static bool
+program_stages_interleaved_illegally(const struct gl_pipeline_object *pipe)
+{
+   struct gl_shader_program *prev = NULL;
+   unsigned i, j;
+
+   /* Look for programs bound to stages: A -> B -> A, with any intervening
+    * sequence of unrelated programs or empty stages.
+    */
+   for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      struct gl_shader_program *cur = pipe->CurrentProgram[i];
+
+      /* Empty stages anywhere in the pipe are OK */
+      if (!cur || cur == prev)
+         continue;
+
+      if (prev) {
+         /* We've seen an A -> B transition; look at the rest of the pipe
+          * to see if we ever see A again.
+          */
+         for (j = i + 1; j < MESA_SHADER_STAGES; j++) {
+            if (pipe->CurrentProgram[j] == prev)
+               return true;
+         }
+      }
+
+      prev = cur;
+   }
+
+   return false;
+}
+
+extern GLboolean
+_mesa_validate_program_pipeline(struct gl_context* ctx,
+                                struct gl_pipeline_object *pipe)
+{
+   unsigned i;
+   bool program_empty = true;
+
+   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])) {
+         return GL_FALSE;
+      }
+   }
+
+   /* 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."
+    */
+   if (program_stages_interleaved_illegally(pipe)) {
+      pipe->InfoLog =
+         ralloc_strdup(pipe,
+                       "Program is active for multiple shader stages with an "
+                       "intervening stage provided by another program");
+      return GL_FALSE;
+   }
+
+   /* 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->CurrentProgram[MESA_SHADER_TESS_CTRL] ||
+           pipe->CurrentProgram[MESA_SHADER_TESS_EVAL])) {
+      pipe->InfoLog = ralloc_strdup(pipe, "Program lacks a vertex shader");
+      return GL_FALSE;
+   }
+
+   /* 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);
+         return GL_FALSE;
+      }
+   }
+
+   /* Section 11.1.3.11 (Validation) of the OpenGL 4.5 spec says:
+    *
+    *    "An INVALID_OPERATION error is generated by any command that trans-
+    *    fers vertices to the GL or launches compute work if the current set
+    *    of active program objects cannot be executed, for reasons including:
+    *
+    *       ...
+    *
+    *       - There is no current program object specified by UseProgram,
+    *         there is a current program pipeline object, and that object is
+    *         empty (no executable code is installed for any stage).
+    */
+   for (i = 0; i < MESA_SHADER_STAGES; i++) {
+      if (pipe->CurrentProgram[i]) {
+         program_empty = false;
+         break;
+      }
+   }
+
+   if (program_empty) {
+      return GL_FALSE;
+   }
+
+   /* 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))
+      return GL_FALSE;
+
+   /* Validate inputs against outputs, this cannot be done during linking
+    * since programs have been linked separately from each other.
+    *
+    * Section 11.1.3.11 (Validation) of the OpenGL 4.5 Core Profile spec says:
+    *
+    *     "Separable program objects may have validation failures that cannot be
+    *     detected without the complete program pipeline. Mismatched interfaces,
+    *     improper usage of program objects together, and the same
+    *     state-dependent failures can result in validation errors for such
+    *     program objects."
+    *
+    * OpenGL ES 3.1 specification has the same text.
+    *
+    * Section 11.1.3.11 (Validation) of the OpenGL ES spec also says:
+    *
+    *    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 current program pipeline object contains a shader interface
+    *      that doesn't have an exact match (see section 7.4.1)
+    *
+    * Based on this, only perform the most-strict checking on ES or when the
+    * application has created a debug context.
+    */
+   if ((_mesa_is_gles(ctx) || (ctx->Const.ContextFlags & GL_CONTEXT_FLAG_DEBUG_BIT)) &&
+       !_mesa_validate_pipeline_io(pipe)) {
+      if (_mesa_is_gles(ctx))
+         return GL_FALSE;
+
+      static GLuint msg_id = 0;
+
+      _mesa_gl_debug(ctx, &msg_id,
+                     MESA_DEBUG_SOURCE_API,
+                     MESA_DEBUG_TYPE_PORTABILITY,
+                     MESA_DEBUG_SEVERITY_MEDIUM,
+                     "glValidateProgramPipeline: pipeline %u does not meet "
+                     "strict OpenGL ES 3.1 requirements and may not be "
+                     "portable across desktop hardware\n",
+                     pipe->Name);
+   }
+
+   pipe->Validated = GL_TRUE;
+   return GL_TRUE;
 }
 
 /**
@@ -546,10 +953,45 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
 void GLAPIENTRY
 _mesa_ValidateProgramPipeline(GLuint pipeline)
 {
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glValidateProgramPipeline(%u)\n", pipeline);
+
+   struct gl_pipeline_object *pipe = _mesa_lookup_pipeline_object(ctx, pipeline);
+
+   if (!pipe) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glValidateProgramPipeline(pipeline)");
+      return;
+   }
+
+   _mesa_validate_program_pipeline(ctx, pipe);
 }
 
 void GLAPIENTRY
 _mesa_GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize,
                                 GLsizei *length, GLchar *infoLog)
 {
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (MESA_VERBOSE & VERBOSE_API)
+      _mesa_debug(ctx, "glGetProgramPipelineInfoLog(%u, %d, %p, %p)\n",
+                  pipeline, bufSize, length, infoLog);
+
+   struct gl_pipeline_object *pipe = _mesa_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);
 }