mesa/sso: Implement ValidateProgramPipeline

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 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

V3 (idr):
* Actually fix the leak of the InfoLog. :)
* Directly generate logs in to gl_pipeline_object::InfoLog via
  ralloc_asprintf isntead of using a temporary buffer.
* Split out from previous uber patch.
* Change spec references to include section numbers, etc.
* Fix a bug in checking that a different program isn't active in a stage
  between two stages that have the same program.  Specifically,

 if (pipe->CurrentVertexProgram->Name == pipe->CurrentGeometryProgram->Name &&
     pipe->CurrentGeometryProgram->Name != pipe->CurrentVertexProgram->Name)

should have been

 if (pipe->CurrentVertexProgram->Name == pipe->CurrentFragmentProgram->Name &&
     pipe->CurrentGeometryProgram->Name != pipe->CurrentVertexProgram->Name)

v4 (idr): Rework to use CurrentProgram array in loops.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
Gregory Hainaut 2013-06-28 19:30:44 -07:00 committed by Ian Romanick
parent 95426b28ac
commit 1c29068074
4 changed files with 203 additions and 3 deletions

View file

@ -1841,6 +1841,7 @@ shader_linked_or_absent(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
*/
@ -1892,6 +1893,15 @@ _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);

View file

@ -2808,6 +2808,8 @@ struct gl_pipeline_object
GLboolean EverBound; /**< Has the pipeline object been created */
GLboolean Validated; /**< Pipeline Validation status */
GLchar *InfoLog;
};

View file

@ -576,9 +576,7 @@ _mesa_GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params)
*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]
@ -608,12 +606,199 @@ _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

View file

@ -59,6 +59,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);