mesa: Add SYSTEM_VALUE_VERTEX_ID_ZERO_BASE
[mesa.git] / src / mesa / main / pipelineobj.c
index b639e9bc913532ff2f5c227fd340aaee86b79383..017d4257eb815ea122504acfbf708770040836ae 100644 (file)
@@ -44,7 +44,7 @@
 #include "main/uniforms.h"
 #include "program/program.h"
 #include "program/prog_parameter.h"
-#include "ralloc.h"
+#include "util/ralloc.h"
 #include <stdbool.h>
 #include "../glsl/glsl_parser_extras.h"
 #include "../glsl/ir_uniform.h"
@@ -80,6 +80,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;
@@ -226,6 +227,7 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
 
    struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
    struct gl_shader_program *shProg = NULL;
+   GLbitfield any_valid_stages;
 
    if (!pipe) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glUseProgramStages(pipeline)");
@@ -246,7 +248,7 @@ _mesa_UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
     * 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;
 
@@ -369,6 +371,82 @@ _mesa_ActiveShaderProgram(GLuint pipeline, GLuint program)
 void GLAPIENTRY
 _mesa_BindProgramPipeline(GLuint pipeline)
 {
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_pipeline_object *newObj = NULL;
+
+   /* 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 = 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)
+{
+   /* 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);
+
+      if (ctx->Driver.UseProgram)
+         ctx->Driver.UseProgram(ctx, NULL);
+   }
 }
 
 /**
@@ -503,14 +581,10 @@ _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 ? 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]
@@ -540,16 +614,223 @@ _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
 _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;
+   }
+
+   if (pipe->InfoLog)
+      _mesa_copy_string(infoLog, bufSize, length, pipe->InfoLog);
+   else
+      *length = 0;
 }