* GL_ARB_separate_shader_objects extension.
*/
+#include <stdbool.h>
#include "main/glheader.h"
#include "main/context.h"
-#include "main/dispatch.h"
#include "main/enums.h"
#include "main/hash.h"
#include "main/mtypes.h"
#include "main/pipelineobj.h"
#include "main/shaderapi.h"
#include "main/shaderobj.h"
+#include "main/state.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"
+#include "util/bitscan.h"
/**
* Delete a pipeline object.
{
unsigned i;
- _mesa_reference_shader_program(ctx, &obj->_CurrentFragmentProgram, NULL);
-
- for (i = 0; i < MESA_SHADER_STAGES; i++)
- _mesa_reference_shader_program(ctx, &obj->CurrentProgram[i], NULL);
+ for (i = 0; i < MESA_SHADER_STAGES; i++) {
+ _mesa_reference_program(ctx, &obj->CurrentProgram[i], NULL);
+ _mesa_reference_shader_program(ctx, &obj->ReferencedPrograms[i], NULL);
+ }
_mesa_reference_shader_program(ctx, &obj->ActiveProgram, NULL);
- mtx_destroy(&obj->Mutex);
+ free(obj->Label);
ralloc_free(obj);
}
struct gl_pipeline_object *obj = rzalloc(NULL, struct gl_pipeline_object);
if (obj) {
obj->Name = name;
- mtx_init(&obj->Mutex, mtx_plain);
obj->RefCount = 1;
obj->Flags = _mesa_get_shader_flags();
obj->InfoLog = NULL;
* 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;
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);
-
}
/**
* 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;
else
return (struct gl_pipeline_object *)
- _mesa_HashLookup(ctx->Pipeline.Objects, id);
+ _mesa_HashLookupLocked(ctx->Pipeline.Objects, id);
}
/**
save_pipeline_object(struct gl_context *ctx, struct gl_pipeline_object *obj)
{
if (obj->Name > 0) {
- _mesa_HashInsert(ctx->Pipeline.Objects, obj->Name, obj);
+ _mesa_HashInsertLocked(ctx->Pipeline.Objects, obj->Name, obj);
}
}
remove_pipeline_object(struct gl_context *ctx, struct gl_pipeline_object *obj)
{
if (obj->Name > 0) {
- _mesa_HashRemove(ctx->Pipeline.Objects, obj->Name);
+ _mesa_HashRemoveLocked(ctx->Pipeline.Objects, obj->Name);
}
}
if (*ptr) {
/* Unreference the old pipeline object */
- GLboolean deleteFlag = GL_FALSE;
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);
- if (deleteFlag) {
+ if (oldObj->RefCount == 0) {
_mesa_delete_pipeline_object(ctx, oldObj);
}
*ptr = NULL;
}
- ASSERT(!*ptr);
+ assert(!*ptr);
if (obj) {
/* reference new pipeline object */
- mtx_lock(&obj->Mutex);
- if (obj->RefCount == 0) {
- /* this pipeline's being deleted (look just above) */
- /* Not sure this can ever really happen. Warn if it does. */
- _mesa_problem(NULL, "referencing deleted pipeline object");
- *ptr = NULL;
- }
- else {
- obj->RefCount++;
- *ptr = obj;
- }
- mtx_unlock(&obj->Mutex);
+ assert(obj->RefCount > 0);
+
+ obj->RefCount++;
+ *ptr = obj;
}
}
+static void
+use_program_stage(struct gl_context *ctx, GLenum type,
+ struct gl_shader_program *shProg,
+ struct gl_pipeline_object *pipe) {
+ gl_shader_stage stage = _mesa_shader_enum_to_shader_stage(type);
+ struct gl_program *prog = NULL;
+ if (shProg && shProg->_LinkedShaders[stage])
+ prog = shProg->_LinkedShaders[stage]->Program;
+
+ _mesa_use_program(ctx, stage, shProg, prog, pipe);
+}
+
+static void
+use_program_stages(struct gl_context *ctx, struct gl_shader_program *shProg,
+ GLbitfield stages, struct gl_pipeline_object *pipe) {
+
+ /* Enable individual stages from the program as requested by the
+ * application. If there is no shader for a requested stage in the
+ * program, _mesa_use_shader_program will enable fixed-function processing
+ * as dictated by the spec.
+ *
+ * Section 2.11.4 (Program Pipeline Objects) of the OpenGL 4.1 spec
+ * says:
+ *
+ * "If UseProgramStages is called with program set to zero or with a
+ * program object that contains no executable code for the given
+ * stages, it is as if the pipeline object has no programmable stage
+ * configured for the indicated shader stages."
+ */
+ if ((stages & GL_VERTEX_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_VERTEX_SHADER, shProg, pipe);
+
+ if ((stages & GL_FRAGMENT_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_FRAGMENT_SHADER, shProg, pipe);
+
+ if ((stages & GL_GEOMETRY_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_GEOMETRY_SHADER, shProg, pipe);
+
+ if ((stages & GL_TESS_CONTROL_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_TESS_CONTROL_SHADER, shProg, pipe);
+
+ if ((stages & GL_TESS_EVALUATION_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_TESS_EVALUATION_SHADER, shProg, pipe);
+
+ if ((stages & GL_COMPUTE_SHADER_BIT) != 0)
+ use_program_stage(ctx, GL_COMPUTE_SHADER, shProg, pipe);
+
+ pipe->Validated = false;
+}
+
+void GLAPIENTRY
+_mesa_UseProgramStages_no_error(GLuint pipeline, GLbitfield stages,
+ GLuint prog)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ struct gl_pipeline_object *pipe =
+ _mesa_lookup_pipeline_object(ctx, pipeline);
+ struct gl_shader_program *shProg = NULL;
+
+ if (prog)
+ shProg = _mesa_lookup_shader_program(ctx, prog);
+
+ /* Object is created by any Pipeline call but glGenProgramPipelines,
+ * glIsProgramPipeline and GetProgramPipelineInfoLog
+ */
+ pipe->EverBound = GL_TRUE;
+
+ use_program_stages(ctx, shProg, stages, pipe);
+}
+
/**
* Bound program to severals stages of the pipeline
*/
{
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)");
return;
*
* "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
*/
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)");
* shader stages in the pipeline program pipeline object are not
* modified."
*/
- if (!shProg->LinkStatus) {
+ if (!shProg->data->LinkStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glUseProgramStages(program not linked)");
return;
}
}
- /* Enable individual stages from the program as requested by the
- * application. If there is no shader for a requested stage in the
- * program, _mesa_use_shader_program will enable fixed-function processing
- * as dictated by the spec.
- *
- * Section 2.11.4 (Program Pipeline Objects) of the OpenGL 4.1 spec
- * says:
- *
- * "If UseProgramStages is called with program set to zero or with a
- * program object that contains no executable code for the given
- * stages, it is as if the pipeline object has no programmable stage
- * configured for the indicated shader stages."
- */
- if ((stages & GL_VERTEX_SHADER_BIT) != 0)
- _mesa_use_shader_program(ctx, GL_VERTEX_SHADER, shProg, pipe);
-
- if ((stages & GL_FRAGMENT_SHADER_BIT) != 0)
- _mesa_use_shader_program(ctx, GL_FRAGMENT_SHADER, shProg, pipe);
-
- if ((stages & GL_GEOMETRY_SHADER_BIT) != 0)
- _mesa_use_shader_program(ctx, GL_GEOMETRY_SHADER, shProg, pipe);
+ use_program_stages(ctx, shProg, stages, pipe);
}
-/**
- * Use the named shader program for subsequent glUniform calls (if pipeline
- * bound)
- */
-void GLAPIENTRY
-_mesa_ActiveShaderProgram(GLuint pipeline, GLuint program)
+static ALWAYS_INLINE void
+active_shader_program(struct gl_context *ctx, GLuint pipeline, GLuint program,
+ bool no_error)
{
- 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 (program != 0) {
- shProg = _mesa_lookup_shader_program_err(ctx, program,
- "glActiveShaderProgram(program)");
- if (shProg == NULL)
- return;
+ if (program) {
+ if (no_error) {
+ shProg = _mesa_lookup_shader_program(ctx, program);
+ } else {
+ shProg = _mesa_lookup_shader_program_err(ctx, program,
+ "glActiveShaderProgram(program)");
+ if (shProg == NULL)
+ return;
+ }
}
- if (!pipe) {
+ if (!no_error && !pipe) {
_mesa_error(ctx, GL_INVALID_OPERATION, "glActiveShaderProgram(pipeline)");
return;
}
*/
pipe->EverBound = GL_TRUE;
- if ((shProg != NULL) && !shProg->LinkStatus) {
+ if (!no_error && shProg != NULL && !shProg->data->LinkStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glActiveShaderProgram(program %u not linked)", shProg->Name);
return;
_mesa_reference_shader_program(ctx, &pipe->ActiveProgram, shProg);
}
+void GLAPIENTRY
+_mesa_ActiveShaderProgram_no_error(GLuint pipeline, GLuint program)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ active_shader_program(ctx, pipeline, program, true);
+}
+
/**
- * Make program of the pipeline current
+ * Use the named shader program for subsequent glUniform calls (if pipeline
+ * bound)
*/
void GLAPIENTRY
-_mesa_BindProgramPipeline(GLuint pipeline)
+_mesa_ActiveShaderProgram(GLuint pipeline, GLuint program)
{
GET_CURRENT_CONTEXT(ctx);
+
+ if (MESA_VERBOSE & VERBOSE_API)
+ _mesa_debug(ctx, "glActiveShaderProgram(%u, %u)\n", pipeline, program);
+
+ active_shader_program(ctx, pipeline, program, false);
+}
+
+static ALWAYS_INLINE void
+bind_program_pipeline(struct gl_context *ctx, GLuint pipeline, bool no_error)
+{
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)
* - by BindProgramPipeline if the current transform feedback
* object is active and not paused;
*/
- if (_mesa_is_xfb_active_and_unpaused(ctx)) {
+ if (!no_error && _mesa_is_xfb_active_and_unpaused(ctx)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBindProgramPipeline(transform feedback active)");
return;
*/
if (pipeline) {
/* non-default pipeline object */
- newObj = lookup_pipeline_object(ctx, pipeline);
- if (!newObj) {
+ newObj = _mesa_lookup_pipeline_object(ctx, pipeline);
+ if (!no_error && !newObj) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glBindProgramPipeline(non-gen name)");
return;
newObj->EverBound = GL_TRUE;
}
+ _mesa_bind_pipeline(ctx, newObj);
+}
+
+void GLAPIENTRY
+_mesa_BindProgramPipeline_no_error(GLuint pipeline)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ bind_program_pipeline(ctx, pipeline, true);
+}
+
+/**
+ * Make program of the pipeline current
+ */
+void GLAPIENTRY
+_mesa_BindProgramPipeline(GLuint pipeline)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ bind_program_pipeline(ctx, pipeline, false);
+}
+
+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, newObj);
+ _mesa_reference_pipeline_object(ctx, &ctx->Pipeline.Current, pipe);
/* Section 2.11.3 (Program Objects) of the OpenGL 4.1 spec says:
*
* considered current."
*/
if (&ctx->Shader != ctx->_Shader) {
- if (pipeline) {
+ FLUSH_VERTICES(ctx, _NEW_PROGRAM | _NEW_PROGRAM_CONSTANTS);
+
+ if (pipe != NULL) {
/* Bound the pipeline to the current program and
* restore the pipeline state
*/
- _mesa_reference_pipeline_object(ctx, &ctx->_Shader, newObj);
+ _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++) {
+ struct gl_program *prog = ctx->_Shader->CurrentProgram[i];
+ if (prog) {
+ _mesa_program_init_subroutine_defaults(ctx, prog);
+ }
+ }
- if (ctx->Driver.UseProgram)
- ctx->Driver.UseProgram(ctx, NULL);
+ _mesa_update_vertex_processing_mode(ctx);
+ _mesa_update_allow_draw_out_of_order(ctx);
}
}
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;
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
* \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 = dsa ? "glCreateProgramPipelines" : "glGenProgramPipelines";
GLuint first;
GLint i;
- if (n < 0) {
- _mesa_error(ctx, GL_INVALID_VALUE, "glGenProgramPipelines(n<0)");
+ if (!pipelines)
return;
- }
-
- if (!pipelines) {
- return;
- }
first = _mesa_HashFindFreeKeyBlock(ctx->Pipeline.Objects, n);
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;
}
+}
+
+static void
+create_program_pipelines_err(struct gl_context *ctx, GLsizei n,
+ GLuint *pipelines, bool dsa)
+{
+ const char *func = dsa ? "glCreateProgramPipelines" : "glGenProgramPipelines";
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s (n < 0)", func);
+ return;
+ }
+
+ create_program_pipelines(ctx, n, pipelines, dsa);
+}
+void GLAPIENTRY
+_mesa_GenProgramPipelines_no_error(GLsizei n, GLuint *pipelines)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ create_program_pipelines(ctx, n, pipelines, false);
+}
+
+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_err(ctx, n, pipelines, false);
+}
+
+void GLAPIENTRY
+_mesa_CreateProgramPipelines_no_error(GLsizei n, GLuint *pipelines)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ create_program_pipelines(ctx, n, pipelines, true);
+}
+
+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_err(ctx, n, pipelines, true);
}
/**
{
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;
_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,
*params = pipe->ActiveProgram ? pipe->ActiveProgram->Name : 0;
return;
case GL_INFO_LOG_LENGTH:
- *params = pipe->InfoLog ? strlen(pipe->InfoLog) + 1 : 0;
+ *params = (pipe->InfoLog && pipe->InfoLog[0] != '\0') ?
+ strlen(pipe->InfoLog) + 1 : 0;
return;
case GL_VALIDATE_STATUS:
*params = pipe->Validated;
return;
case GL_VERTEX_SHADER:
*params = pipe->CurrentProgram[MESA_SHADER_VERTEX]
- ? pipe->CurrentProgram[MESA_SHADER_VERTEX]->Name : 0;
+ ? pipe->CurrentProgram[MESA_SHADER_VERTEX]->Id : 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]->Id : 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]->Id : 0;
+ return;
case GL_GEOMETRY_SHADER:
if (!has_gs)
break;
*params = pipe->CurrentProgram[MESA_SHADER_GEOMETRY]
- ? pipe->CurrentProgram[MESA_SHADER_GEOMETRY]->Name : 0;
+ ? pipe->CurrentProgram[MESA_SHADER_GEOMETRY]->Id : 0;
return;
case GL_FRAGMENT_SHADER:
*params = pipe->CurrentProgram[MESA_SHADER_FRAGMENT]
- ? pipe->CurrentProgram[MESA_SHADER_FRAGMENT]->Name : 0;
+ ? pipe->CurrentProgram[MESA_SHADER_FRAGMENT]->Id : 0;
+ return;
+ case GL_COMPUTE_SHADER:
+ if (!_mesa_has_compute_shaders(ctx))
+ break;
+ *params = pipe->CurrentProgram[MESA_SHADER_COMPUTE]
+ ? pipe->CurrentProgram[MESA_SHADER_COMPUTE]->Id : 0;
return;
default:
break;
}
_mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramPipelineiv(pname=%s)",
- _mesa_lookup_enum_by_nr(pname));
+ _mesa_enum_to_string(pname));
}
/**
*/
static bool
program_stages_all_active(struct gl_pipeline_object *pipe,
- const struct gl_shader_program *prog)
+ const struct gl_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 {
+ unsigned mask = prog->sh.data->linked_stages;
+ while (mask) {
+ const int i = u_bit_scan(&mask);
+ if (pipe->CurrentProgram[i]) {
+ if (prog->Id != pipe->CurrentProgram[i]->Id) {
status = false;
}
+ } else {
+ status = false;
}
}
pipe->InfoLog = ralloc_asprintf(pipe,
"Program %d is not active for all "
"shaders that was linked",
- prog->Name);
+ prog->Id);
}
return status;
}
+static bool
+program_stages_interleaved_illegally(const struct gl_pipeline_object *pipe)
+{
+ unsigned prev_linked_stages = 0;
+
+ /* Look for programs bound to stages: A -> B -> A, with any intervening
+ * sequence of unrelated programs or empty stages.
+ */
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_program *cur = pipe->CurrentProgram[i];
+
+ /* Empty stages anywhere in the pipe are OK. Also we can be confident
+ * that if the linked_stages mask matches we are looking at the same
+ * linked program because a previous validation call to
+ * program_stages_all_active() will have already failed if two different
+ * programs with the sames stages linked are not active for all linked
+ * stages.
+ */
+ if (!cur || cur->sh.data->linked_stages == prev_linked_stages)
+ continue;
+
+ if (prev_linked_stages) {
+ /* We've seen an A -> B transition; look at the rest of the pipe
+ * to see if we ever see A again.
+ */
+ if (prev_linked_stages >> (i + 1))
+ return true;
+ }
+
+ prev_linked_stages = cur->sh.data->linked_stages;
+ }
+
+ return false;
+}
+
extern GLboolean
_mesa_validate_program_pipeline(struct gl_context* ctx,
- struct gl_pipeline_object *pipe,
- GLboolean IsBound)
+ struct gl_pipeline_object *pipe)
{
unsigned i;
+ bool program_empty = true;
pipe->Validated = GL_FALSE;
*/
for (i = 0; i < MESA_SHADER_STAGES; i++) {
if (!program_stages_all_active(pipe, pipe->CurrentProgram[i])) {
- goto err;
+ return GL_FALSE;
}
}
* - 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;
- }
+ 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
* executable vertex shader."
*/
if (!pipe->CurrentProgram[MESA_SHADER_VERTEX]
- && pipe->CurrentProgram[MESA_SHADER_GEOMETRY]) {
+ && (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");
- goto err;
+ return GL_FALSE;
}
/* Section 2.11.11 (Shader Execution), subheading "Validation," of the
* PROGRAM_SEPARABLE parameter set to FALSE.
*/
for (i = 0; i < MESA_SHADER_STAGES; i++) {
- if (pipe->CurrentProgram[i] && !pipe->CurrentProgram[i]->SeparateShader) {
+ if (pipe->CurrentProgram[i] &&
+ !pipe->CurrentProgram[i]->info.separate_shader) {
pipe->InfoLog = ralloc_asprintf(pipe,
"Program %d was relinked without "
"PROGRAM_SEPARABLE state",
- pipe->CurrentProgram[i]->Name);
- goto err;
+ pipe->CurrentProgram[i]->Id);
+ 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:
*
* maximum number of texture image units allowed."
*/
if (!_mesa_sampler_uniforms_pipeline_are_valid(pipe))
- goto err;
+ 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_debugf(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;
-
-err:
- if (IsBound)
- _mesa_error(ctx, GL_INVALID_OPERATION,
- "glValidateProgramPipeline failed to validate the pipeline");
-
- return GL_FALSE;
}
/**
{
GET_CURRENT_CONTEXT(ctx);
- struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+ 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,
return;
}
- _mesa_validate_program_pipeline(ctx, pipe,
- (ctx->_Shader->Name == pipe->Name));
+ _mesa_validate_program_pipeline(ctx, pipe);
}
void GLAPIENTRY
{
GET_CURRENT_CONTEXT(ctx);
- struct gl_pipeline_object *pipe = lookup_pipeline_object(ctx, pipeline);
+ 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,
return;
}
- if (pipe->InfoLog)
- _mesa_copy_string(infoLog, bufSize, length, pipe->InfoLog);
- else
- *length = 0;
+ _mesa_copy_string(infoLog, bufSize, length, pipe->InfoLog);
}