mesa: implement the last of GL_ARB_debug_output

Store client-defined message IDs in a hash table,
and sort them by severity into three linked lists
so they can be selected by severity level later.
This commit is contained in:
nobled
2011-05-04 19:42:55 +00:00
committed by Marek Olšák
parent 41308d9699
commit ae4a8a59b7
3 changed files with 245 additions and 28 deletions

View File

@@ -1138,6 +1138,8 @@ _mesa_free_context_data( struct gl_context *ctx )
/* needs to be after freeing shared state */
_mesa_free_display_list_data(ctx);
_mesa_free_errors_data(ctx);
if (ctx->Extensions.String)
free((void *) ctx->Extensions.String);

View File

@@ -33,6 +33,7 @@
#include "imports.h"
#include "context.h"
#include "dispatch.h"
#include "hash.h"
#include "mtypes.h"
#include "version.h"
@@ -40,6 +41,12 @@
#define MAXSTRING MAX_DEBUG_MESSAGE_LENGTH
struct gl_client_severity
{
struct simple_node link;
GLuint ID;
};
static char out_of_memory[] = "Debugging error: out of memory";
#define enum_is(e, kind1, kind2) \
@@ -115,6 +122,135 @@ enum_to_index(GLenum e)
};
}
/*
* We store a bitfield in the hash table, with five possible values total.
*
* The ENABLED_BIT's purpose is self-explanatory.
*
* The FOUND_BIT is needed to differentiate the value of DISABLED from
* the value returned by HashTableLookup() when it can't find the given key.
*
* The KNOWN_SEVERITY bit is a bit complicated:
*
* A client may call Control() with an array of IDs, then call Control()
* on all message IDs of a certain severity, then Insert() one of the
* previously specified IDs, giving us a known severity level, then call
* Control() on all message IDs of a certain severity level again.
*
* After the first call, those IDs will have a FOUND_BIT, but will not
* exist in any severity-specific list, so the second call will not
* impact them. This is undesirable but unavoidable given the API:
* The only entrypoint that gives a severity for a client-defined ID
* is the Insert() call.
*
* For the sake of Control(), we want to maintain the invariant
* that an ID will either appear in none of the three severity lists,
* or appear once, to minimize pointless duplication and potential surprises.
*
* Because Insert() is the only place that will learn an ID's severity,
* it should insert an ID into the appropriate list, but only if the ID
* doesn't exist in it or any other list yet. Because searching all three
* lists at O(n) is needlessly expensive, we store KNOWN_SEVERITY.
*/
enum {
FOUND_BIT = 1 << 0,
ENABLED_BIT = 1 << 1,
KNOWN_SEVERITY = 1 << 2,
/* HashTable reserves zero as a return value meaning 'not found' */
NOT_FOUND = 0,
DISABLED = FOUND_BIT,
ENABLED = ENABLED_BIT | FOUND_BIT
};
/**
* Returns the state of the given message ID in a client-controlled
* namespace.
* 'source', 'type', and 'severity' are array indices like TYPE_ERROR,
* not GL enums.
*/
static GLboolean
get_message_state(struct gl_context *ctx, int source, int type,
GLuint id, int severity)
{
struct gl_client_namespace *nspace =
&ctx->Debug.ClientIDs.Namespaces[source][type];
uintptr_t state;
/* In addition to not being able to store zero as a value, HashTable also
can't use zero as a key. */
if (id)
state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id);
else
state = nspace->ZeroID;
/* Only do this once for each ID. This makes sure the ID exists in,
at most, one list, and does not pointlessly appear multiple times. */
if (!(state & KNOWN_SEVERITY)) {
struct gl_client_severity *entry;
if (state == NOT_FOUND) {
if (ctx->Debug.ClientIDs.Defaults[severity][source][type])
state = ENABLED;
else
state = DISABLED;
}
entry = malloc(sizeof *entry);
if (!entry)
goto out;
state |= KNOWN_SEVERITY;
if (id)
_mesa_HashInsert(nspace->IDs, id, (void*)state);
else
nspace->ZeroID = state;
entry->ID = id;
insert_at_tail(&nspace->Severity[severity], &entry->link);
}
out:
return !!(state & ENABLED_BIT);
}
/**
* Sets the state of the given message ID in a client-controlled
* namespace.
* 'source' and 'type' are array indices like TYPE_ERROR, not GL enums.
*/
static void
set_message_state(struct gl_context *ctx, int source, int type,
GLuint id, GLboolean enabled)
{
struct gl_client_namespace *nspace =
&ctx->Debug.ClientIDs.Namespaces[source][type];
uintptr_t state;
/* In addition to not being able to store zero as a value, HashTable also
can't use zero as a key. */
if (id)
state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id);
else
state = nspace->ZeroID;
if (state == NOT_FOUND)
state = enabled ? ENABLED : DISABLED;
else {
if (enabled)
state |= ENABLED_BIT;
else
state &= ~ENABLED_BIT;
}
if (id)
_mesa_HashInsert(nspace->IDs, id, (void*)state);
else
nspace->ZeroID = state;
}
/**
* Whether a debugging message should be logged or not.
* For implementation-controlled namespaces, we keep an array
@@ -133,8 +269,7 @@ should_log(struct gl_context *ctx, GLenum source, GLenum type,
t = enum_to_index(type);
sev = enum_to_index(severity);
return ctx->Debug.ClientIDs.Defaults[sev][s][t];
/* TODO: tracking individual client IDs. */
return get_message_state(ctx, s, t, sev, id);
}
if (type_is(type, ERROR)) {
@@ -442,28 +577,22 @@ control_messages(GLboolean *array, GLuint size,
}
/**
* Debugging-message namespaces with the source APPLICATION or THIRD_PARTY
* require special handling, since the IDs in them are controlled by clients,
* not the OpenGL implementation.
* Set the state of all message IDs found in the given intersection
* of 'source', 'type', and 'severity'. Note that all three of these
* parameters are array indices, not the corresponding GL enums.
*
* When controlling entire categories of messages without specifying IDs,
* the default state for IDs that haven't been seen yet have to be adjusted.
* This requires both setting the state of all previously seen message
* IDs in the hash table, and setting the default state for all
* applicable combinations of source/type/severity, so that all the
* yet-unknown message IDs that may be used in the future will be
* impacted as if they were already known.
*/
static void
control_app_messages(struct gl_context *ctx, GLenum esource, GLenum etype,
GLenum eseverity, GLsizei count, const GLuint *ids,
GLboolean enabled)
control_app_messages_by_group(struct gl_context *ctx, int source, int type,
int severity, GLboolean enabled)
{
int source, type, severity, s, t, sev, smax, tmax, sevmax;
assert(!count && "glDebugMessageControlARB():"
" controlling for client-provided IDs is not implemented");
if (count)
return;
source = enum_to_index(esource);
type = enum_to_index(etype);
severity = enum_to_index(eseverity);
struct gl_client_debug *ClientIDs = &ctx->Debug.ClientIDs;
int s, t, sev, smax, tmax, sevmax;
if (source == SOURCE_ANY) {
source = 0;
@@ -488,8 +617,56 @@ control_app_messages(struct gl_context *ctx, GLenum esource, GLenum etype,
for (sev = severity; sev < sevmax; sev++)
for (s = source; s < smax; s++)
for (t = type; t < tmax; t++)
ctx->Debug.ClientIDs.Defaults[sev][s][t] = enabled;
for (t = type; t < tmax; t++) {
struct simple_node *node;
struct gl_client_severity *entry;
/* change the default for IDs we've never seen before. */
ClientIDs->Defaults[sev][s][t] = enabled;
/* Now change the state of IDs we *have* seen... */
foreach(node, &ClientIDs->Namespaces[s][t].Severity[sev]) {
entry = (struct gl_client_severity *)node;
set_message_state(ctx, s, t, entry->ID, enabled);
}
}
}
/**
* Debugging-message namespaces with the source APPLICATION or THIRD_PARTY
* require special handling, since the IDs in them are controlled by clients,
* not the OpenGL implementation.
*
* 'count' is the length of the array 'ids'. If 'count' is nonzero, all
* the given IDs in the namespace defined by 'esource' and 'etype'
* will be affected.
*
* If 'count' is zero, this sets the state of all IDs that match
* the combination of 'esource', 'etype', and 'eseverity'.
*/
static void
control_app_messages(struct gl_context *ctx, GLenum esource, GLenum etype,
GLenum eseverity, GLsizei count, const GLuint *ids,
GLboolean enabled)
{
int source, type, severity;
GLsizei i;
source = enum_to_index(esource);
type = enum_to_index(etype);
severity = enum_to_index(eseverity);
if (count)
assert(severity == SEVERITY_ANY && type != TYPE_ANY
&& source != SOURCE_ANY);
for (i = 0; i < count; i++)
set_message_state(ctx, source, type, ids[i], enabled);
if (count)
return;
control_app_messages_by_group(ctx, source, type, severity, enabled);
}
static void GLAPIENTRY
@@ -557,6 +734,9 @@ _mesa_init_errors_dispatch(struct _glapi_table *disp)
void
_mesa_init_errors(struct gl_context *ctx)
{
int s, t, sev;
struct gl_client_debug *ClientIDs = &ctx->Debug.ClientIDs;
ctx->Debug.Callback = NULL;
ctx->Debug.SyncOutput = GL_FALSE;
ctx->Debug.Log[0].length = 0;
@@ -569,12 +749,44 @@ _mesa_init_errors(struct gl_context *ctx)
memset(ctx->Debug.WinsysErrors, GL_TRUE, sizeof ctx->Debug.WinsysErrors);
memset(ctx->Debug.ShaderErrors, GL_TRUE, sizeof ctx->Debug.ShaderErrors);
memset(ctx->Debug.OtherErrors, GL_TRUE, sizeof ctx->Debug.OtherErrors);
memset(ctx->Debug.ClientIDs.Defaults[SEVERITY_HIGH], GL_TRUE,
sizeof ctx->Debug.ClientIDs.Defaults[SEVERITY_HIGH]);
memset(ctx->Debug.ClientIDs.Defaults[SEVERITY_MEDIUM], GL_TRUE,
sizeof ctx->Debug.ClientIDs.Defaults[SEVERITY_MEDIUM]);
memset(ctx->Debug.ClientIDs.Defaults[SEVERITY_LOW], GL_FALSE,
sizeof ctx->Debug.ClientIDs.Defaults[SEVERITY_LOW]);
memset(ClientIDs->Defaults[SEVERITY_HIGH], GL_TRUE,
sizeof ClientIDs->Defaults[SEVERITY_HIGH]);
memset(ClientIDs->Defaults[SEVERITY_MEDIUM], GL_TRUE,
sizeof ClientIDs->Defaults[SEVERITY_MEDIUM]);
memset(ClientIDs->Defaults[SEVERITY_LOW], GL_FALSE,
sizeof ClientIDs->Defaults[SEVERITY_LOW]);
/* Initialize state for filtering client-provided debug messages. */
for (s = 0; s < SOURCE_COUNT; s++)
for (t = 0; t < TYPE_COUNT; t++) {
ClientIDs->Namespaces[s][t].IDs = _mesa_NewHashTable();
assert(ClientIDs->Namespaces[s][t].IDs);
for (sev = 0; sev < SEVERITY_COUNT; sev++)
make_empty_list(&ClientIDs->Namespaces[s][t].Severity[sev]);
}
}
void
_mesa_free_errors_data(struct gl_context *ctx)
{
int s, t, sev;
struct gl_client_debug *ClientIDs = &ctx->Debug.ClientIDs;
/* Tear down state for filtering client-provided debug messages. */
for (s = 0; s < SOURCE_COUNT; s++)
for (t = 0; t < TYPE_COUNT; t++) {
_mesa_DeleteHashTable(ClientIDs->Namespaces[s][t].IDs);
for (sev = 0; sev < SEVERITY_COUNT; sev++) {
struct simple_node *node, *tmp;
struct gl_client_severity *entry;
foreach_s(node, tmp, &ClientIDs->Namespaces[s][t].Severity[sev]) {
entry = (struct gl_client_severity *)node;
FREE(entry);
}
}
}
}
/**********************************************************************/

View File

@@ -53,6 +53,9 @@ _mesa_init_errors_dispatch(struct _glapi_table *disp);
extern void
_mesa_init_errors( struct gl_context *ctx );
extern void
_mesa_free_errors_data( struct gl_context *ctx );
extern void
_mesa_warning( struct gl_context *gc, const char *fmtString, ... ) PRINTFLIKE(2, 3);