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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev