[Mesa-dev] [PATCH 11/12] sso: implement ValidateProgramPipeline and GetProgramPipelineInfoLog
Gregory Hainaut
gregory.hainaut at gmail.com
Fri May 3 10:44:15 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.
V2:
Fix memory leak with ralloc_strdup
Formatting improvement
---
src/mesa/main/context.c | 9 ++
src/mesa/main/mtypes.h | 2 +
src/mesa/main/pipelineobj.c | 221 ++++++++++++++++++++++++++++++++++++++-
src/mesa/main/pipelineobj.h | 3 +
src/mesa/main/uniform_query.cpp | 71 +++++++++++++
src/mesa/main/uniforms.h | 3 +
6 files changed, 305 insertions(+), 4 deletions(-)
diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c
index 6a0619a..559e21f 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->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);
diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
index adf518b..ce88860 100644
--- a/src/mesa/main/mtypes.h
+++ b/src/mesa/main/mtypes.h
@@ -2424,6 +2424,8 @@ struct gl_pipeline_object
GLboolean Validated; /**< 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 2d18192..d7948ff 100644
--- a/src/mesa/main/pipelineobj.c
+++ b/src/mesa/main/pipelineobj.c
@@ -87,6 +87,7 @@ _mesa_new_pipeline_object(struct gl_context *ctx, GLuint name)
_glthread_INIT_MUTEX(obj->Mutex);
obj->RefCount = 1;
obj->Flags = _mesa_get_shader_flags();
+ obj->InfoLog = ralloc_strdup(obj, "");
}
return obj;
@@ -339,13 +340,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->Validated = GL_FALSE;
}
/**
@@ -573,11 +576,10 @@ _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->Validated;
return;
case GL_VALIDATE_STATUS:
- *params = pipe->ValidationStatus;
+ *params = pipe->Validated;
return;
case GL_VERTEX_SHADER:
*params = pipe->CurrentVertexProgram ? pipe->CurrentVertexProgram->Name : 0;
@@ -606,16 +608,227 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
_mesa_lookup_enum_by_nr(pname));
}
+static GLboolean
+ProgramEnabledEverywhere(struct gl_pipeline_object *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_pipeline_object *pipe,
+ GLboolean IsBound)
+{
+ char errMsg[200] = "";
+ const GLuint errMsgLength = 200;
+
+ pipe->Validated = GL_FALSE;
+ pipe->InfoLog = ralloc_strdup(pipe, "");
+
+ /*
+ * 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->Validated = GL_TRUE;
+
+err:
+ if (!pipe->Validated) {
+ /* update info log */
+ if (pipe->InfoLog) {
+ ralloc_free(pipe->InfoLog);
+ }
+ pipe->InfoLog = ralloc_strdup(pipe, errMsg);
+ }
+
+ return pipe->Validated;
+}
+
/**
* 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;
+ }
+
+ _mesa_copy_string(infoLog, bufSize, length, pipe->InfoLog);
}
diff --git a/src/mesa/main/pipelineobj.h b/src/mesa/main/pipelineobj.h
index 8a38aab..48b03cc 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, 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);
diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp
index 59f3ad1..4dba920 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_pipeline_object *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 8c67697..7aae7ae 100644
--- a/src/mesa/main/uniforms.h
+++ b/src/mesa/main/uniforms.h
@@ -296,6 +296,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_pipeline_object *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