mesa: Disallow relinking if a program is used by an active XFB object.

Paused transform feedback objects may refer to a program other than the
current program.  If any active objects refer to a program, LinkProgram
must reject the request to relink.

The code to detect this is ugly since _mesa_HashWalk is awkward to use,
but unfortunately we can't use hash_table_foreach since there's no way
to get at the underlying struct hash_table (and even then, we'd need to
handle locking somehow).

Fixes the last subcase of Piglit's new ARB_transform_feedback2
api-errors test.

Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
This commit is contained in:
Kenneth Graunke 2013-09-06 15:41:19 -07:00
parent 9cc74c93f8
commit 2b71b3d466
3 changed files with 47 additions and 7 deletions

View File

@ -42,6 +42,7 @@
#include "main/dispatch.h"
#include "main/enums.h"
#include "main/hash.h"
#include "main/hash_table.h"
#include "main/mtypes.h"
#include "main/shaderapi.h"
#include "main/shaderobj.h"
@ -812,19 +813,19 @@ static void
link_program(struct gl_context *ctx, GLuint program)
{
struct gl_shader_program *shProg;
struct gl_transform_feedback_object *obj =
ctx->TransformFeedback.CurrentObject;
shProg = _mesa_lookup_shader_program_err(ctx, program, "glLinkProgram");
if (!shProg)
return;
if (obj->Active
&& (shProg == ctx->Shader.CurrentVertexProgram
|| shProg == ctx->Shader.CurrentGeometryProgram
|| shProg == ctx->Shader.CurrentFragmentProgram)) {
/* From the ARB_transform_feedback2 specification:
* "The error INVALID_OPERATION is generated by LinkProgram if <program> is
* the name of a program being used by one or more transform feedback
* objects, even if the objects are not currently bound or are paused."
*/
if (_mesa_transform_feedback_is_using_program(ctx, shProg)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glLinkProgram(transform feedback active)");
"glLinkProgram(transform feedback is using the program)");
return;
}

View File

@ -44,6 +44,41 @@
#include "program/prog_parameter.h"
struct using_program_tuple
{
struct gl_shader_program *shProg;
bool found;
};
static void
active_xfb_object_references_program(GLuint key, void *data, void *user_data)
{
struct using_program_tuple *callback_data = user_data;
struct gl_transform_feedback_object *obj = data;
if (obj->Active && obj->shader_program == callback_data->shProg)
callback_data->found = true;
}
/**
* Return true if any active transform feedback object is using a program.
*/
bool
_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
struct gl_shader_program *shProg)
{
struct using_program_tuple callback_data;
callback_data.shProg = shProg;
callback_data.found = false;
_mesa_HashWalk(ctx->TransformFeedback.Objects,
active_xfb_object_references_program, &callback_data);
/* Also check DefaultObject, as it's not in the Objects hash table. */
active_xfb_object_references_program(0, ctx->TransformFeedback.DefaultObject,
&callback_data);
return callback_data.found;
}
/**
* Do reference counting of transform feedback buffers.

View File

@ -120,4 +120,8 @@ _mesa_is_xfb_active_and_unpaused(const struct gl_context *ctx)
!ctx->TransformFeedback.CurrentObject->Paused;
}
extern bool
_mesa_transform_feedback_is_using_program(struct gl_context *ctx,
struct gl_shader_program *shProg);
#endif /* TRANSFORM_FEEDBACK_H */