mesa: allocate gl_debug_state on demand

We don't need to allocate all the state related to GL_ARB_debug_output
until some aspect of that extension is actually needed.

The sizeof(gl_debug_state) is huge (~285KB on 64-bit systems), not even
counting the 54(!) hash tables and lists that it contains.  This change
reduces the size of gl_context alone from 431KB bytes to 145KB bytes on
64-bit systems and from 277KB bytes to 78KB bytes on 32-bit systems.

Reviewed-by: Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
Brian Paul
2014-02-06 18:21:58 -07:00
parent 31b2625cb5
commit 6e8d04ac3e
9 changed files with 279 additions and 158 deletions

View File

@@ -118,6 +118,7 @@ gl_enum_to_debug_severity(GLenum e)
return i;
}
/**
* Handles generating a GL_ARB_debug_output message ID generated by the GL or
* GLSL compiler.
@@ -185,6 +186,49 @@ enum {
};
/**
* Return debug state for the context. The debug state will be allocated
* and initialized upon the first call.
*/
struct gl_debug_state *
_mesa_get_debug_state(struct gl_context *ctx)
{
if (!ctx->Debug) {
ctx->Debug = CALLOC_STRUCT(gl_debug_state);
if (!ctx->Debug) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "allocating debug state");
}
else {
struct gl_debug_state *debug = ctx->Debug;
int s, t, sev;
/* Enable all the messages with severity HIGH or MEDIUM by default. */
memset(debug->Defaults[0][MESA_DEBUG_SEVERITY_HIGH], GL_TRUE,
sizeof debug->Defaults[0][MESA_DEBUG_SEVERITY_HIGH]);
memset(debug->Defaults[0][MESA_DEBUG_SEVERITY_MEDIUM], GL_TRUE,
sizeof debug->Defaults[0][MESA_DEBUG_SEVERITY_MEDIUM]);
memset(debug->Defaults[0][MESA_DEBUG_SEVERITY_LOW], GL_FALSE,
sizeof debug->Defaults[0][MESA_DEBUG_SEVERITY_LOW]);
/* Initialize state for filtering known debug messages. */
for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) {
for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) {
debug->Namespaces[0][s][t].IDs = _mesa_NewHashTable();
assert(debug->Namespaces[0][s][t].IDs);
for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) {
make_empty_list(&debug->Namespaces[0][s][t].Severity[sev]);
}
}
}
}
}
return ctx->Debug;
}
/**
* Returns the state of the given message source/type/ID tuple.
*/
@@ -195,50 +239,59 @@ should_log(struct gl_context *ctx,
GLuint id,
enum mesa_debug_severity severity)
{
GLint gstack = ctx->Debug.GroupStackDepth;
struct gl_debug_namespace *nspace =
&ctx->Debug.Namespaces[gstack][source][type];
uintptr_t state;
struct gl_debug_state *debug;
uintptr_t state = 0;
if (!ctx->Debug.DebugOutput)
if (!ctx->Debug) {
/* no debug state set so far */
return GL_FALSE;
/* 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_debug_severity *entry;
if (state == NOT_FOUND) {
if (ctx->Debug.Defaults[gstack][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);
}
debug = _mesa_get_debug_state(ctx);
if (debug) {
const GLint gstack = debug->GroupStackDepth;
struct gl_debug_namespace *nspace =
&debug->Namespaces[gstack][source][type];
if (!debug->DebugOutput)
return GL_FALSE;
/* 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_debug_severity *entry;
if (state == NOT_FOUND) {
if (debug->Defaults[gstack][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);
}
@@ -253,32 +306,36 @@ set_message_state(struct gl_context *ctx,
enum mesa_debug_type type,
GLuint id, GLboolean enabled)
{
GLint gstack = ctx->Debug.GroupStackDepth;
struct gl_debug_namespace *nspace =
&ctx->Debug.Namespaces[gstack][source][type];
uintptr_t state;
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
/* 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 (debug) {
GLint gstack = debug->GroupStackDepth;
struct gl_debug_namespace *nspace =
&debug->Namespaces[gstack][source][type];
uintptr_t state;
if (state == NOT_FOUND)
state = enabled ? ENABLED : DISABLED;
else {
if (enabled)
state |= ENABLED_BIT;
/* 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 &= ~ENABLED_BIT;
}
state = nspace->ZeroID;
if (id)
_mesa_HashInsert(nspace->IDs, id, (void*)state);
else
nspace->ZeroID = state;
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;
}
}
@@ -361,40 +418,44 @@ log_msg(struct gl_context *ctx, enum mesa_debug_source source,
enum mesa_debug_type type, GLuint id,
enum mesa_debug_severity severity, GLint len, const char *buf)
{
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
GLint nextEmpty;
struct gl_debug_msg *emptySlot;
if (!debug)
return;
assert(len >= 0 && len < MAX_DEBUG_MESSAGE_LENGTH);
if (!should_log(ctx, source, type, id, severity))
return;
if (ctx->Debug.Callback) {
if (debug->Callback) {
GLenum gl_type = debug_type_enums[type];
GLenum gl_severity = debug_severity_enums[severity];
if (ctx->Debug.ARBCallback) {
if (debug->ARBCallback) {
gl_severity = remap_severity(gl_severity);
gl_type = remap_type(gl_type);
}
ctx->Debug.Callback(debug_source_enums[source], gl_type, id, gl_severity,
len, buf, ctx->Debug.CallbackData);
debug->Callback(debug_source_enums[source], gl_type, id, gl_severity,
len, buf, debug->CallbackData);
return;
}
if (ctx->Debug.NumMessages == MAX_DEBUG_LOGGED_MESSAGES)
if (debug->NumMessages == MAX_DEBUG_LOGGED_MESSAGES)
return;
nextEmpty = (ctx->Debug.NextMsg + ctx->Debug.NumMessages)
nextEmpty = (debug->NextMsg + debug->NumMessages)
% MAX_DEBUG_LOGGED_MESSAGES;
emptySlot = &ctx->Debug.Log[nextEmpty];
emptySlot = &debug->Log[nextEmpty];
store_message_details(emptySlot, source, type, id, severity, len, buf);
if (ctx->Debug.NumMessages == 0)
ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length;
if (debug->NumMessages == 0)
debug->NextMsgLength = debug->Log[debug->NextMsg].length;
ctx->Debug.NumMessages++;
debug->NumMessages++;
}
@@ -413,16 +474,17 @@ get_msg(struct gl_context *ctx, GLenum *source, GLenum *type,
GLuint *id, GLenum *severity, GLsizei bufSize, char *buf,
unsigned caller)
{
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
struct gl_debug_msg *msg;
GLsizei length;
if (ctx->Debug.NumMessages == 0)
if (!debug || debug->NumMessages == 0)
return 0;
msg = &ctx->Debug.Log[ctx->Debug.NextMsg];
msg = &debug->Log[debug->NextMsg];
length = msg->length;
assert(length > 0 && length == ctx->Debug.NextMsgLength);
assert(length > 0 && length == debug->NextMsgLength);
if (bufSize < length && buf != NULL)
return 0;
@@ -457,10 +519,10 @@ get_msg(struct gl_context *ctx, GLenum *source, GLenum *type,
msg->message = NULL;
msg->length = 0;
ctx->Debug.NumMessages--;
ctx->Debug.NextMsg++;
ctx->Debug.NextMsg %= MAX_DEBUG_LOGGED_MESSAGES;
ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length;
debug->NumMessages--;
debug->NextMsg++;
debug->NextMsg %= MAX_DEBUG_LOGGED_MESSAGES;
debug->NextMsgLength = debug->Log[debug->NextMsg].length;
return length;
}
@@ -564,8 +626,12 @@ control_messages(struct gl_context *ctx,
enum mesa_debug_severity severity,
GLboolean enabled)
{
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
int s, t, sev, smax, tmax, sevmax;
GLint gstack = ctx->Debug.GroupStackDepth;
const GLint gstack = debug ? debug->GroupStackDepth : 0;
if (!debug)
return;
if (source == MESA_DEBUG_SOURCE_COUNT) {
source = 0;
@@ -595,10 +661,10 @@ control_messages(struct gl_context *ctx,
struct gl_debug_severity *entry;
/* change the default for IDs we've never seen before. */
ctx->Debug.Defaults[gstack][sev][s][t] = enabled;
debug->Defaults[gstack][sev][s][t] = enabled;
/* Now change the state of IDs we *have* seen... */
foreach(node, &ctx->Debug.Namespaces[gstack][s][t].Severity[sev]) {
foreach(node, &debug->Namespaces[gstack][s][t].Severity[sev]) {
entry = (struct gl_debug_severity *)node;
set_message_state(ctx, s, t, entry->ID, enabled);
}
@@ -766,25 +832,32 @@ do_nothing(GLuint key, void *data, void *userData)
}
/**
* Free context state pertaining to error/debug state for the given stack
* depth.
*/
static void
free_errors_data(struct gl_context *ctx, GLint gstack)
{
struct gl_debug_state *debug = ctx->Debug;
enum mesa_debug_type t;
enum mesa_debug_source s;
enum mesa_debug_severity sev;
assert(debug);
/* Tear down state for filtering debug messages. */
for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) {
for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) {
_mesa_HashDeleteAll(ctx->Debug.Namespaces[gstack][s][t].IDs,
_mesa_HashDeleteAll(debug->Namespaces[gstack][s][t].IDs,
do_nothing, NULL);
_mesa_DeleteHashTable(ctx->Debug.Namespaces[gstack][s][t].IDs);
_mesa_DeleteHashTable(debug->Namespaces[gstack][s][t].IDs);
for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) {
struct simple_node *node, *tmp;
struct gl_debug_severity *entry;
foreach_s(node, tmp,
&ctx->Debug.Namespaces[gstack][s][t].Severity[sev]) {
&debug->Namespaces[gstack][s][t].Severity[sev]) {
entry = (struct gl_debug_severity *)node;
free(entry);
}
@@ -838,9 +911,12 @@ void GLAPIENTRY
_mesa_DebugMessageCallback(GLDEBUGPROC callback, const void *userParam)
{
GET_CURRENT_CONTEXT(ctx);
ctx->Debug.Callback = callback;
ctx->Debug.CallbackData = userParam;
ctx->Debug.ARBCallback = GL_FALSE;
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
if (debug) {
debug->Callback = callback;
debug->CallbackData = userParam;
debug->ARBCallback = GL_FALSE;
}
}
@@ -848,15 +924,18 @@ void GLAPIENTRY
_mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length,
const GLchar *message)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
const char *callerstr = "glPushDebugGroup";
int s, t, sev;
GLint prevStackDepth;
GLint currStackDepth;
struct gl_debug_msg *emptySlot;
GET_CURRENT_CONTEXT(ctx);
if (!debug)
return;
if (ctx->Debug.GroupStackDepth >= MAX_DEBUG_GROUP_STACK_DEPTH-1) {
if (debug->GroupStackDepth >= MAX_DEBUG_GROUP_STACK_DEPTH-1) {
_mesa_error(ctx, GL_STACK_OVERFLOW, "%s", callerstr);
return;
}
@@ -875,14 +954,14 @@ _mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length,
GL_DEBUG_SEVERITY_NOTIFICATION, length,
message, callerstr);
prevStackDepth = ctx->Debug.GroupStackDepth;
ctx->Debug.GroupStackDepth++;
currStackDepth = ctx->Debug.GroupStackDepth;
prevStackDepth = debug->GroupStackDepth;
debug->GroupStackDepth++;
currStackDepth = debug->GroupStackDepth;
/* pop reuses the message details from push so we store this */
if (length < 0)
length = strlen(message);
emptySlot = &ctx->Debug.DebugGroupMsgs[ctx->Debug.GroupStackDepth];
emptySlot = &debug->DebugGroupMsgs[debug->GroupStackDepth];
store_message_details(emptySlot, gl_enum_to_debug_source(source),
gl_enum_to_debug_type(GL_DEBUG_TYPE_PUSH_GROUP),
id,
@@ -895,27 +974,27 @@ _mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length,
for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) {
for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) {
/* copy id settings */
ctx->Debug.Namespaces[currStackDepth][s][t].IDs =
_mesa_HashClone(ctx->Debug.Namespaces[prevStackDepth][s][t].IDs);
debug->Namespaces[currStackDepth][s][t].IDs =
_mesa_HashClone(debug->Namespaces[prevStackDepth][s][t].IDs);
for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) {
struct gl_debug_severity *entry, *prevEntry;
struct simple_node *node;
/* copy default settings for unknown ids */
ctx->Debug.Defaults[currStackDepth][sev][s][t] =
ctx->Debug.Defaults[prevStackDepth][sev][s][t];
debug->Defaults[currStackDepth][sev][s][t] =
debug->Defaults[prevStackDepth][sev][s][t];
/* copy known id severity settings */
make_empty_list(&ctx->Debug.Namespaces[currStackDepth][s][t].Severity[sev]);
foreach(node, &ctx->Debug.Namespaces[prevStackDepth][s][t].Severity[sev]) {
make_empty_list(&debug->Namespaces[currStackDepth][s][t].Severity[sev]);
foreach(node, &debug->Namespaces[prevStackDepth][s][t].Severity[sev]) {
prevEntry = (struct gl_debug_severity *)node;
entry = malloc(sizeof *entry);
if (!entry)
return;
entry->ID = prevEntry->ID;
insert_at_tail(&ctx->Debug.Namespaces[currStackDepth][s][t].Severity[sev], &entry->link);
insert_at_tail(&debug->Namespaces[currStackDepth][s][t].Severity[sev], &entry->link);
}
}
}
@@ -926,21 +1005,24 @@ _mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length,
void GLAPIENTRY
_mesa_PopDebugGroup(void)
{
GET_CURRENT_CONTEXT(ctx);
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
const char *callerstr = "glPopDebugGroup";
struct gl_debug_msg *gdmessage;
GLint prevStackDepth;
GET_CURRENT_CONTEXT(ctx);
if (!debug)
return;
if (ctx->Debug.GroupStackDepth <= 0) {
if (debug->GroupStackDepth <= 0) {
_mesa_error(ctx, GL_STACK_UNDERFLOW, "%s", callerstr);
return;
}
prevStackDepth = ctx->Debug.GroupStackDepth;
ctx->Debug.GroupStackDepth--;
prevStackDepth = debug->GroupStackDepth;
debug->GroupStackDepth--;
gdmessage = &ctx->Debug.DebugGroupMsgs[prevStackDepth];
gdmessage = &debug->DebugGroupMsgs[prevStackDepth];
/* using log_msg() directly here as verification of parameters
* already done in push
*/
@@ -1005,44 +1087,19 @@ void GLAPIENTRY
_mesa_DebugMessageCallbackARB(GLDEBUGPROCARB callback, const void *userParam)
{
GET_CURRENT_CONTEXT(ctx);
ctx->Debug.Callback = callback;
ctx->Debug.CallbackData = userParam;
ctx->Debug.ARBCallback = GL_TRUE;
struct gl_debug_state *debug = _mesa_get_debug_state(ctx);
if (debug) {
debug->Callback = callback;
debug->CallbackData = userParam;
debug->ARBCallback = GL_TRUE;
}
}
void
_mesa_init_errors(struct gl_context *ctx)
{
int s, t, sev;
ctx->Debug.Callback = NULL;
ctx->Debug.SyncOutput = GL_FALSE;
ctx->Debug.Log[0].length = 0;
ctx->Debug.NumMessages = 0;
ctx->Debug.NextMsg = 0;
ctx->Debug.NextMsgLength = 0;
ctx->Debug.GroupStackDepth = 0;
/* Enable all the messages with severity HIGH or MEDIUM by default. */
memset(ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_HIGH], GL_TRUE,
sizeof ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_HIGH]);
memset(ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_MEDIUM], GL_TRUE,
sizeof ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_MEDIUM]);
memset(ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_LOW], GL_FALSE,
sizeof ctx->Debug.Defaults[0][MESA_DEBUG_SEVERITY_LOW]);
/* Initialize state for filtering known debug messages. */
for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) {
for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) {
ctx->Debug.Namespaces[0][s][t].IDs = _mesa_NewHashTable();
assert(ctx->Debug.Namespaces[0][s][t].IDs);
for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) {
make_empty_list(&ctx->Debug.Namespaces[0][s][t].Severity[sev]);
}
}
}
/* no-op */
}
@@ -1053,10 +1110,12 @@ _mesa_init_errors(struct gl_context *ctx)
void
_mesa_free_errors_data(struct gl_context *ctx)
{
GLint i;
if (ctx->Debug) {
GLint i;
for (i = 0; i <= ctx->Debug.GroupStackDepth; i++) {
free_errors_data(ctx, i);
for (i = 0; i <= ctx->Debug->GroupStackDepth; i++) {
free_errors_data(ctx, i);
}
}
}