khr_debug: add basic tests for glDebugMessageControl

Submitted by Chia-I Wu on April 25, 2014, 10:36 a.m.

Details

Message ID 1398422181-28749-1-git-send-email-olvaffe@gmail.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Chia-I Wu April 25, 2014, 10:36 a.m.
Signed-off-by: Chia-I Wu <olv@lunarg.com>
Cc: Timothy Arceri <t_arceri@yahoo.com.au>
---
 tests/all.py                                 |   3 +
 tests/spec/khr_debug/CMakeLists.gl.txt       |   1 +
 tests/spec/khr_debug/CMakeLists.gles2.txt    |   1 +
 tests/spec/khr_debug/CMakeLists.gles3.txt    |   1 +
 tests/spec/khr_debug/debug-message-control.c | 392 +++++++++++++++++++++++++++
 5 files changed, 398 insertions(+)
 create mode 100644 tests/spec/khr_debug/debug-message-control.c

Patch hide | download patch | download mbox

diff --git a/tests/all.py b/tests/all.py
index d218498..f54efa6 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -1866,6 +1866,9 @@  khr_debug['object-label_gles3'] = concurrent_test('khr_debug-object-label_gles3'
 khr_debug['push-pop-group_gl'] = concurrent_test('khr_debug-push-pop-group_gl')
 khr_debug['push-pop-group_gles2'] = concurrent_test('khr_debug-push-pop-group_gles2')
 khr_debug['push-pop-group_gles3'] = concurrent_test('khr_debug-push-pop-group_gles3')
+khr_debug['message-control_gl'] = concurrent_test('khr_debug-message-control_gl')
+khr_debug['message-control_gles2'] = concurrent_test('khr_debug-message-control_gles2')
+khr_debug['message-control_gles3'] = concurrent_test('khr_debug-message-control_gles3')
 
 # Group ARB_occlusion_query2
 arb_occlusion_query2 = Group()
diff --git a/tests/spec/khr_debug/CMakeLists.gl.txt b/tests/spec/khr_debug/CMakeLists.gl.txt
index cfbe9ed..1822896 100644
--- a/tests/spec/khr_debug/CMakeLists.gl.txt
+++ b/tests/spec/khr_debug/CMakeLists.gl.txt
@@ -11,5 +11,6 @@  link_libraries (
 
 piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
 piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
+piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
 
 # vim: ft=cmake:
diff --git a/tests/spec/khr_debug/CMakeLists.gles2.txt b/tests/spec/khr_debug/CMakeLists.gles2.txt
index e132cad..328b880 100644
--- a/tests/spec/khr_debug/CMakeLists.gles2.txt
+++ b/tests/spec/khr_debug/CMakeLists.gles2.txt
@@ -11,5 +11,6 @@  link_libraries (
 
 piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
 piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
+piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
 
 # vim: ft=cmake:
diff --git a/tests/spec/khr_debug/CMakeLists.gles3.txt b/tests/spec/khr_debug/CMakeLists.gles3.txt
index e132cad..328b880 100644
--- a/tests/spec/khr_debug/CMakeLists.gles3.txt
+++ b/tests/spec/khr_debug/CMakeLists.gles3.txt
@@ -11,5 +11,6 @@  link_libraries (
 
 piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
 piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
+piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
 
 # vim: ft=cmake:
diff --git a/tests/spec/khr_debug/debug-message-control.c b/tests/spec/khr_debug/debug-message-control.c
new file mode 100644
index 0000000..4ef6d2c
--- /dev/null
+++ b/tests/spec/khr_debug/debug-message-control.c
@@ -0,0 +1,392 @@ 
+/*
+ * Copyright (c) 2014 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL AUTHORS AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+#ifdef PIGLIT_USE_OPENGL
+	config.supports_gl_compat_version = 11;
+	config.require_debug_context = true;
+#else /* using GLES */
+	config.supports_gl_es_version = 20;
+#endif
+
+	config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+#ifdef PIGLIT_USE_OPENGL
+#define GET_FUNC(x) x
+#else /* using GLES */
+#define GET_FUNC(x) x ## KHR
+#endif
+
+static PFNGLGETDEBUGMESSAGELOGPROC GetDebugMessageLog;
+static PFNGLDEBUGMESSAGEINSERTPROC DebugMessageInsert;
+static PFNGLDEBUGMESSAGECONTROLPROC DebugMessageControl;
+static PFNGLDEBUGMESSAGECALLBACKPROC DebugMessageCallback;
+
+#define MESSAGE_SOURCES 2
+#define MESSAGE_TYPES 2
+#define MESSAGE_SEVERITIES 2
+#define MESSAGE_LENGTH 16
+
+static const struct {
+	GLenum sources[MESSAGE_SOURCES];
+	GLenum types[MESSAGE_TYPES];
+	GLenum severities[MESSAGE_SEVERITIES];
+} message_templ = {
+	{ GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_SOURCE_THIRD_PARTY },
+	{ GL_DEBUG_TYPE_MARKER, GL_DEBUG_TYPE_OTHER },
+	{ GL_DEBUG_SEVERITY_HIGH, GL_DEBUG_SEVERITY_MEDIUM }
+};
+
+static struct message {
+	GLenum source;
+	GLenum type;
+	GLuint id;
+	GLenum severity;
+	char buf[MESSAGE_LENGTH];
+} messages[MESSAGE_SOURCES * MESSAGE_TYPES * MESSAGE_SEVERITIES];
+
+struct message_callback_data {
+	GLuint ids[MESSAGE_SOURCES * MESSAGE_TYPES * MESSAGE_SEVERITIES];
+	int count;
+};
+
+static void init_messages(void)
+{
+	int s, t, u;
+	int id = 0, idx = 0;
+
+	for (s = 0; s < ARRAY_SIZE(message_templ.sources); s++) {
+		for (t = 0; t < ARRAY_SIZE(message_templ.types); t++) {
+			for (u = 0; u < ARRAY_SIZE(message_templ.severities); u++) {
+				id = 7 * id + 3;
+				messages[idx].source = message_templ.sources[s];
+				messages[idx].type = message_templ.types[t];
+				messages[idx].id = id;
+				messages[idx].severity = message_templ.severities[u];
+				snprintf(messages[idx].buf, sizeof(messages[idx].buf),
+						"message %d", idx);
+
+				idx++;
+			}
+		}
+	}
+
+	assert(idx == ARRAY_SIZE(messages));
+}
+
+static void insert_messages(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(messages); i++) {
+		const struct message *msg = &messages[i];
+		DebugMessageInsert(msg->source, msg->type, msg->id,
+				msg->severity, -1, msg->buf);
+	}
+}
+
+static bool is_our_message(GLenum source,
+		           GLenum type,
+		           GLuint id,
+		           GLenum severity,
+		           GLsizei length,
+		           const char *buf)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(messages); i++) {
+		const struct message *msg = &messages[i];
+
+		/*
+		 * do not check severity as we will send the same message with
+		 * different severities
+		 */
+		if (msg->source == source && msg->type == type &&
+		    msg->id == id && !strcmp(msg->buf, buf))
+			break;
+	}
+
+	return (i < ARRAY_SIZE(messages));
+}
+
+static void message_callback(GLenum source,
+		             GLenum type,
+		             GLuint id,
+		             GLenum severity,
+		             GLsizei length,
+		             const char* message,
+		             void* user)
+{
+	struct message_callback_data *data =
+		(struct message_callback_data *) user;
+
+	if (!is_our_message(source, type, id, severity, length, message))
+		return;
+
+	data->ids[data->count++] = id;
+}
+
+static bool
+test_mixed_control(void)
+{
+	const struct message *msg0 = &messages[0];
+	const struct message *msg1 = &messages[1];
+	struct message_callback_data data;
+	GLuint ids[2];
+	bool pass = true;
+
+	assert(msg0->source == msg1->source && msg0->type == msg1->type &&
+			msg0->severity != msg1->severity);
+
+	memset(&data, 0, sizeof(data));
+
+	/* disable all messages */
+	DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
+			GL_DONT_CARE, 0, NULL, GL_FALSE);
+	/* enable msg0 and msg1 */
+	ids[0] = msg0->id;
+	ids[1] = msg1->id;
+	DebugMessageControl(msg0->source, msg0->type,
+			GL_DONT_CARE, 2, ids, GL_TRUE);
+	/* disable msg1 by disabling messages having the same severity */
+	DebugMessageControl(msg1->source, msg1->type,
+			msg1->severity, 0, NULL, GL_FALSE);
+
+	DebugMessageCallback((GLDEBUGPROC) message_callback,
+			(void *) &data);
+
+	{
+		data.count = 0;
+		insert_messages();
+		if (data.count != 1 || data.ids[0] != msg0->id) {
+			fprintf(stderr, "msg0 is expected\n");
+			pass = false;
+		}
+
+		/* send msg0 with the same severity as msg1 */
+		data.count = 0;
+		DebugMessageInsert(msg0->source, msg0->type, msg0->id,
+				msg1->severity, -1, msg0->buf);
+		if (data.count != 0) {
+			fprintf(stderr, "no message is expected\n");
+			pass = false;
+		}
+	}
+
+	DebugMessageCallback(NULL, NULL);
+
+	return pass;
+}
+
+static bool
+test_id_control(void)
+{
+	bool pass = true;
+	int i;
+
+	/*
+	 * When an ID is specified, source and type must be specified.
+	 * Severity must not be specified.
+	 */
+	DebugMessageControl(messages[0].source, messages[0].type,
+			messages[0].severity, 1, &messages[0].id, GL_FALSE);
+	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
+		pass = false;
+	DebugMessageControl(messages[0].source, GL_DONT_CARE,
+			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
+	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
+		pass = false;
+	DebugMessageControl(GL_DONT_CARE, messages[0].type,
+			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
+	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
+		pass = false;
+	DebugMessageControl(messages[0].source, messages[0].type,
+			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
+	if (!piglit_check_gl_error(GL_NO_ERROR))
+		pass = false;
+
+	/* test enabling messages through IDs */
+	for (i = 0; i < ARRAY_SIZE(messages); i++) {
+		struct message_callback_data data;
+		int n;
+
+		memset(&data, 0, sizeof(data));
+
+		/* disable all messages */
+		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
+				GL_DONT_CARE, 0, NULL, GL_FALSE);
+
+		/* enable the first (i + 1) messages */
+		for (n = 0; n <= i; n++) {
+			const struct message *msg = &messages[n];
+			DebugMessageControl(msg->source, msg->type,
+					GL_DONT_CARE, 1, &msg->id, GL_TRUE);
+		}
+
+		DebugMessageCallback((GLDEBUGPROC) message_callback,
+				(void *) &data);
+
+		data.count = 0;
+		insert_messages();
+		if (data.count != n) {
+			fprintf(stderr, "ID Test %d expects %d messages, got %d messages\n",
+					i, n, data.count);
+			pass = false;
+		}
+		else {
+			for (n = 0; n <= i; n++) {
+				if (data.ids[n] != messages[n].id) {
+					fprintf(stderr, "ID Test %d expects id %d, got id %d\n",
+							i, messages[n].id, data.ids[n]);
+					pass = false;
+				}
+			}
+		}
+
+		DebugMessageCallback(NULL, NULL);
+	}
+
+	/* test enalbing multiple IDs in one call */
+	{
+		const struct message *msg0 = &messages[0];
+		const struct message *msg1 = &messages[1];
+		struct message_callback_data data;
+		GLuint ids[2];
+
+		assert(msg0->source == msg1->source &&
+		       msg0->type == msg1->type &&
+		       msg0->severity != msg1->severity);
+
+		memset(&data, 0, sizeof(data));
+		ids[0] = msg0->id;
+		ids[1] = msg1->id;
+
+		/* disable all but the first two messages */
+		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
+				GL_DONT_CARE, 0, NULL, GL_FALSE);
+		DebugMessageControl(msg0->source, msg0->type,
+				GL_DONT_CARE, 2, ids, GL_TRUE);
+
+		DebugMessageCallback((GLDEBUGPROC) message_callback,
+				(void *) &data);
+		{
+			data.count = 0;
+			insert_messages();
+			if (data.count != 2 || data.ids[0] != ids[0] || data.ids[1] != ids[1]) {
+				fprintf(stderr, "failed to enable multiple messages once\n");
+				pass = false;
+			}
+		}
+	}
+
+	return pass;
+}
+
+static bool
+test_set_control(void)
+{
+	const struct {
+		GLenum source;
+		GLenum type;
+		GLenum severity;
+		int expected_count;
+	} sets[] = {
+		{ GL_DONT_CARE,             GL_DONT_CARE,           GL_DONT_CARE,                8 },
+		{ message_templ.sources[0], GL_DONT_CARE,           GL_DONT_CARE,                4 },
+		{ GL_DONT_CARE,             message_templ.types[0], GL_DONT_CARE,                4 },
+		{ GL_DONT_CARE,             GL_DONT_CARE,           message_templ.severities[0], 4 },
+		{ message_templ.sources[0], message_templ.types[0], GL_DONT_CARE,                2 },
+		{ message_templ.sources[0], GL_DONT_CARE,           message_templ.severities[0], 2 },
+		{ GL_DONT_CARE,             message_templ.types[0], message_templ.severities[0], 2 },
+		{ message_templ.sources[0], message_templ.types[0], message_templ.severities[0], 1 },
+		{ GL_DEBUG_SOURCE_API,      GL_DONT_CARE,           GL_DONT_CARE,                0 },
+	};
+	bool pass = true;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sets); i++) {
+		struct message_callback_data data;
+
+		memset(&data, 0, sizeof(data));
+
+		/* disable all messages */
+		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
+				GL_DONT_CARE, 0, NULL, GL_FALSE);
+
+		/* enable the set to test */
+		DebugMessageControl(sets[i].source, sets[i].type,
+				sets[i].severity, 0, NULL, GL_TRUE);
+
+		DebugMessageCallback((GLDEBUGPROC) message_callback,
+				(void *) &data);
+
+		data.count = 0;
+		insert_messages();
+		if (data.count != sets[i].expected_count) {
+			fprintf(stderr, "Set Test %d expects %d messages, got %d messages\n",
+					i, sets[i].expected_count, data.count);
+			pass = false;
+		}
+
+		DebugMessageCallback(NULL, NULL);
+	}
+
+	return pass;
+}
+
+void piglit_init(int argc, char **argv)
+{
+	bool pass = true;
+
+	GetDebugMessageLog = GET_FUNC(glGetDebugMessageLog);
+	DebugMessageInsert = GET_FUNC(glDebugMessageInsert);
+	DebugMessageControl = GET_FUNC(glDebugMessageControl);
+	DebugMessageCallback = GET_FUNC(glDebugMessageCallback);
+
+	piglit_require_extension("GL_KHR_debug");
+
+	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+	glEnable(GL_DEBUG_OUTPUT);
+
+	if (!piglit_check_gl_error(GL_NO_ERROR))
+		piglit_report_result(PIGLIT_FAIL);
+
+	init_messages();
+	pass = test_set_control();
+	pass = test_id_control() && pass;
+	pass = test_mixed_control() && pass;
+
+	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
+}
+
+enum piglit_result
+piglit_display(void)
+{
+	return PIGLIT_PASS;
+}

Comments

I've skimmed over this and it looks ok. Haven't had time to review fully
but I have tested on Mesa and Catalyst so:

Tested-by: Timothy Arceri <t_arceri@yahoo.com.au>

On Fri, 2014-04-25 at 18:36 +0800, Chia-I Wu wrote:
> Signed-off-by: Chia-I Wu <olv@lunarg.com>
> Cc: Timothy Arceri <t_arceri@yahoo.com.au>
> ---
>  tests/all.py                                 |   3 +
>  tests/spec/khr_debug/CMakeLists.gl.txt       |   1 +
>  tests/spec/khr_debug/CMakeLists.gles2.txt    |   1 +
>  tests/spec/khr_debug/CMakeLists.gles3.txt    |   1 +
>  tests/spec/khr_debug/debug-message-control.c | 392 +++++++++++++++++++++++++++
>  5 files changed, 398 insertions(+)
>  create mode 100644 tests/spec/khr_debug/debug-message-control.c
> 
> diff --git a/tests/all.py b/tests/all.py
> index d218498..f54efa6 100644
> --- a/tests/all.py
> +++ b/tests/all.py
> @@ -1866,6 +1866,9 @@ khr_debug['object-label_gles3'] = concurrent_test('khr_debug-object-label_gles3'
>  khr_debug['push-pop-group_gl'] = concurrent_test('khr_debug-push-pop-group_gl')
>  khr_debug['push-pop-group_gles2'] = concurrent_test('khr_debug-push-pop-group_gles2')
>  khr_debug['push-pop-group_gles3'] = concurrent_test('khr_debug-push-pop-group_gles3')
> +khr_debug['message-control_gl'] = concurrent_test('khr_debug-message-control_gl')
> +khr_debug['message-control_gles2'] = concurrent_test('khr_debug-message-control_gles2')
> +khr_debug['message-control_gles3'] = concurrent_test('khr_debug-message-control_gles3')
>  
>  # Group ARB_occlusion_query2
>  arb_occlusion_query2 = Group()
> diff --git a/tests/spec/khr_debug/CMakeLists.gl.txt b/tests/spec/khr_debug/CMakeLists.gl.txt
> index cfbe9ed..1822896 100644
> --- a/tests/spec/khr_debug/CMakeLists.gl.txt
> +++ b/tests/spec/khr_debug/CMakeLists.gl.txt
> @@ -11,5 +11,6 @@ link_libraries (
>  
>  piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
>  piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
> +piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
>  
>  # vim: ft=cmake:
> diff --git a/tests/spec/khr_debug/CMakeLists.gles2.txt b/tests/spec/khr_debug/CMakeLists.gles2.txt
> index e132cad..328b880 100644
> --- a/tests/spec/khr_debug/CMakeLists.gles2.txt
> +++ b/tests/spec/khr_debug/CMakeLists.gles2.txt
> @@ -11,5 +11,6 @@ link_libraries (
>  
>  piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
>  piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
> +piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
>  
>  # vim: ft=cmake:
> diff --git a/tests/spec/khr_debug/CMakeLists.gles3.txt b/tests/spec/khr_debug/CMakeLists.gles3.txt
> index e132cad..328b880 100644
> --- a/tests/spec/khr_debug/CMakeLists.gles3.txt
> +++ b/tests/spec/khr_debug/CMakeLists.gles3.txt
> @@ -11,5 +11,6 @@ link_libraries (
>  
>  piglit_add_executable (khr_debug-object-label_${piglit_target_api} debug-object-label.c)
>  piglit_add_executable (khr_debug-push-pop-group_${piglit_target_api} debug-push-pop-group.c)
> +piglit_add_executable (khr_debug-message-control_${piglit_target_api} debug-message-control.c)
>  
>  # vim: ft=cmake:
> diff --git a/tests/spec/khr_debug/debug-message-control.c b/tests/spec/khr_debug/debug-message-control.c
> new file mode 100644
> index 0000000..4ef6d2c
> --- /dev/null
> +++ b/tests/spec/khr_debug/debug-message-control.c
> @@ -0,0 +1,392 @@
> +/*
> + * Copyright (c) 2014 LunarG, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NON-INFRINGEMENT.  IN NO EVENT SHALL AUTHORS AND/OR THEIR SUPPLIERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +
> +#include "piglit-util-gl-common.h"
> +
> +PIGLIT_GL_TEST_CONFIG_BEGIN
> +
> +#ifdef PIGLIT_USE_OPENGL
> +	config.supports_gl_compat_version = 11;
> +	config.require_debug_context = true;
> +#else /* using GLES */
> +	config.supports_gl_es_version = 20;
> +#endif
> +
> +	config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE;
> +
> +PIGLIT_GL_TEST_CONFIG_END
> +
> +#ifdef PIGLIT_USE_OPENGL
> +#define GET_FUNC(x) x
> +#else /* using GLES */
> +#define GET_FUNC(x) x ## KHR
> +#endif
> +
> +static PFNGLGETDEBUGMESSAGELOGPROC GetDebugMessageLog;
> +static PFNGLDEBUGMESSAGEINSERTPROC DebugMessageInsert;
> +static PFNGLDEBUGMESSAGECONTROLPROC DebugMessageControl;
> +static PFNGLDEBUGMESSAGECALLBACKPROC DebugMessageCallback;
> +
> +#define MESSAGE_SOURCES 2
> +#define MESSAGE_TYPES 2
> +#define MESSAGE_SEVERITIES 2
> +#define MESSAGE_LENGTH 16
> +
> +static const struct {
> +	GLenum sources[MESSAGE_SOURCES];
> +	GLenum types[MESSAGE_TYPES];
> +	GLenum severities[MESSAGE_SEVERITIES];
> +} message_templ = {
> +	{ GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_SOURCE_THIRD_PARTY },
> +	{ GL_DEBUG_TYPE_MARKER, GL_DEBUG_TYPE_OTHER },
> +	{ GL_DEBUG_SEVERITY_HIGH, GL_DEBUG_SEVERITY_MEDIUM }
> +};
> +
> +static struct message {
> +	GLenum source;
> +	GLenum type;
> +	GLuint id;
> +	GLenum severity;
> +	char buf[MESSAGE_LENGTH];
> +} messages[MESSAGE_SOURCES * MESSAGE_TYPES * MESSAGE_SEVERITIES];
> +
> +struct message_callback_data {
> +	GLuint ids[MESSAGE_SOURCES * MESSAGE_TYPES * MESSAGE_SEVERITIES];
> +	int count;
> +};
> +
> +static void init_messages(void)
> +{
> +	int s, t, u;
> +	int id = 0, idx = 0;
> +
> +	for (s = 0; s < ARRAY_SIZE(message_templ.sources); s++) {
> +		for (t = 0; t < ARRAY_SIZE(message_templ.types); t++) {
> +			for (u = 0; u < ARRAY_SIZE(message_templ.severities); u++) {
> +				id = 7 * id + 3;
> +				messages[idx].source = message_templ.sources[s];
> +				messages[idx].type = message_templ.types[t];
> +				messages[idx].id = id;
> +				messages[idx].severity = message_templ.severities[u];
> +				snprintf(messages[idx].buf, sizeof(messages[idx].buf),
> +						"message %d", idx);
> +
> +				idx++;
> +			}
> +		}
> +	}
> +
> +	assert(idx == ARRAY_SIZE(messages));
> +}
> +
> +static void insert_messages(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(messages); i++) {
> +		const struct message *msg = &messages[i];
> +		DebugMessageInsert(msg->source, msg->type, msg->id,
> +				msg->severity, -1, msg->buf);
> +	}
> +}
> +
> +static bool is_our_message(GLenum source,
> +		           GLenum type,
> +		           GLuint id,
> +		           GLenum severity,
> +		           GLsizei length,
> +		           const char *buf)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(messages); i++) {
> +		const struct message *msg = &messages[i];
> +
> +		/*
> +		 * do not check severity as we will send the same message with
> +		 * different severities
> +		 */
> +		if (msg->source == source && msg->type == type &&
> +		    msg->id == id && !strcmp(msg->buf, buf))
> +			break;
> +	}
> +
> +	return (i < ARRAY_SIZE(messages));
> +}
> +
> +static void message_callback(GLenum source,
> +		             GLenum type,
> +		             GLuint id,
> +		             GLenum severity,
> +		             GLsizei length,
> +		             const char* message,
> +		             void* user)
> +{
> +	struct message_callback_data *data =
> +		(struct message_callback_data *) user;
> +
> +	if (!is_our_message(source, type, id, severity, length, message))
> +		return;
> +
> +	data->ids[data->count++] = id;
> +}
> +
> +static bool
> +test_mixed_control(void)
> +{
> +	const struct message *msg0 = &messages[0];
> +	const struct message *msg1 = &messages[1];
> +	struct message_callback_data data;
> +	GLuint ids[2];
> +	bool pass = true;
> +
> +	assert(msg0->source == msg1->source && msg0->type == msg1->type &&
> +			msg0->severity != msg1->severity);
> +
> +	memset(&data, 0, sizeof(data));
> +
> +	/* disable all messages */
> +	DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
> +			GL_DONT_CARE, 0, NULL, GL_FALSE);
> +	/* enable msg0 and msg1 */
> +	ids[0] = msg0->id;
> +	ids[1] = msg1->id;
> +	DebugMessageControl(msg0->source, msg0->type,
> +			GL_DONT_CARE, 2, ids, GL_TRUE);
> +	/* disable msg1 by disabling messages having the same severity */
> +	DebugMessageControl(msg1->source, msg1->type,
> +			msg1->severity, 0, NULL, GL_FALSE);
> +
> +	DebugMessageCallback((GLDEBUGPROC) message_callback,
> +			(void *) &data);
> +
> +	{
> +		data.count = 0;
> +		insert_messages();
> +		if (data.count != 1 || data.ids[0] != msg0->id) {
> +			fprintf(stderr, "msg0 is expected\n");
> +			pass = false;
> +		}
> +
> +		/* send msg0 with the same severity as msg1 */
> +		data.count = 0;
> +		DebugMessageInsert(msg0->source, msg0->type, msg0->id,
> +				msg1->severity, -1, msg0->buf);
> +		if (data.count != 0) {
> +			fprintf(stderr, "no message is expected\n");
> +			pass = false;
> +		}
> +	}
> +
> +	DebugMessageCallback(NULL, NULL);
> +
> +	return pass;
> +}
> +
> +static bool
> +test_id_control(void)
> +{
> +	bool pass = true;
> +	int i;
> +
> +	/*
> +	 * When an ID is specified, source and type must be specified.
> +	 * Severity must not be specified.
> +	 */
> +	DebugMessageControl(messages[0].source, messages[0].type,
> +			messages[0].severity, 1, &messages[0].id, GL_FALSE);
> +	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
> +		pass = false;
> +	DebugMessageControl(messages[0].source, GL_DONT_CARE,
> +			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
> +	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
> +		pass = false;
> +	DebugMessageControl(GL_DONT_CARE, messages[0].type,
> +			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
> +	if (!piglit_check_gl_error(GL_INVALID_OPERATION))
> +		pass = false;
> +	DebugMessageControl(messages[0].source, messages[0].type,
> +			GL_DONT_CARE, 1, &messages[0].id, GL_FALSE);
> +	if (!piglit_check_gl_error(GL_NO_ERROR))
> +		pass = false;
> +
> +	/* test enabling messages through IDs */
> +	for (i = 0; i < ARRAY_SIZE(messages); i++) {
> +		struct message_callback_data data;
> +		int n;
> +
> +		memset(&data, 0, sizeof(data));
> +
> +		/* disable all messages */
> +		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
> +				GL_DONT_CARE, 0, NULL, GL_FALSE);
> +
> +		/* enable the first (i + 1) messages */
> +		for (n = 0; n <= i; n++) {
> +			const struct message *msg = &messages[n];
> +			DebugMessageControl(msg->source, msg->type,
> +					GL_DONT_CARE, 1, &msg->id, GL_TRUE);
> +		}
> +
> +		DebugMessageCallback((GLDEBUGPROC) message_callback,
> +				(void *) &data);
> +
> +		data.count = 0;
> +		insert_messages();
> +		if (data.count != n) {
> +			fprintf(stderr, "ID Test %d expects %d messages, got %d messages\n",
> +					i, n, data.count);
> +			pass = false;
> +		}
> +		else {
> +			for (n = 0; n <= i; n++) {
> +				if (data.ids[n] != messages[n].id) {
> +					fprintf(stderr, "ID Test %d expects id %d, got id %d\n",
> +							i, messages[n].id, data.ids[n]);
> +					pass = false;
> +				}
> +			}
> +		}
> +
> +		DebugMessageCallback(NULL, NULL);
> +	}
> +
> +	/* test enalbing multiple IDs in one call */
> +	{
> +		const struct message *msg0 = &messages[0];
> +		const struct message *msg1 = &messages[1];
> +		struct message_callback_data data;
> +		GLuint ids[2];
> +
> +		assert(msg0->source == msg1->source &&
> +		       msg0->type == msg1->type &&
> +		       msg0->severity != msg1->severity);
> +
> +		memset(&data, 0, sizeof(data));
> +		ids[0] = msg0->id;
> +		ids[1] = msg1->id;
> +
> +		/* disable all but the first two messages */
> +		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
> +				GL_DONT_CARE, 0, NULL, GL_FALSE);
> +		DebugMessageControl(msg0->source, msg0->type,
> +				GL_DONT_CARE, 2, ids, GL_TRUE);
> +
> +		DebugMessageCallback((GLDEBUGPROC) message_callback,
> +				(void *) &data);
> +		{
> +			data.count = 0;
> +			insert_messages();
> +			if (data.count != 2 || data.ids[0] != ids[0] || data.ids[1] != ids[1]) {
> +				fprintf(stderr, "failed to enable multiple messages once\n");
> +				pass = false;
> +			}
> +		}
> +	}
> +
> +	return pass;
> +}
> +
> +static bool
> +test_set_control(void)
> +{
> +	const struct {
> +		GLenum source;
> +		GLenum type;
> +		GLenum severity;
> +		int expected_count;
> +	} sets[] = {
> +		{ GL_DONT_CARE,             GL_DONT_CARE,           GL_DONT_CARE,                8 },
> +		{ message_templ.sources[0], GL_DONT_CARE,           GL_DONT_CARE,                4 },
> +		{ GL_DONT_CARE,             message_templ.types[0], GL_DONT_CARE,                4 },
> +		{ GL_DONT_CARE,             GL_DONT_CARE,           message_templ.severities[0], 4 },
> +		{ message_templ.sources[0], message_templ.types[0], GL_DONT_CARE,                2 },
> +		{ message_templ.sources[0], GL_DONT_CARE,           message_templ.severities[0], 2 },
> +		{ GL_DONT_CARE,             message_templ.types[0], message_templ.severities[0], 2 },
> +		{ message_templ.sources[0], message_templ.types[0], message_templ.severities[0], 1 },
> +		{ GL_DEBUG_SOURCE_API,      GL_DONT_CARE,           GL_DONT_CARE,                0 },
> +	};
> +	bool pass = true;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(sets); i++) {
> +		struct message_callback_data data;
> +
> +		memset(&data, 0, sizeof(data));
> +
> +		/* disable all messages */
> +		DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
> +				GL_DONT_CARE, 0, NULL, GL_FALSE);
> +
> +		/* enable the set to test */
> +		DebugMessageControl(sets[i].source, sets[i].type,
> +				sets[i].severity, 0, NULL, GL_TRUE);
> +
> +		DebugMessageCallback((GLDEBUGPROC) message_callback,
> +				(void *) &data);
> +
> +		data.count = 0;
> +		insert_messages();
> +		if (data.count != sets[i].expected_count) {
> +			fprintf(stderr, "Set Test %d expects %d messages, got %d messages\n",
> +					i, sets[i].expected_count, data.count);
> +			pass = false;
> +		}
> +
> +		DebugMessageCallback(NULL, NULL);
> +	}
> +
> +	return pass;
> +}
> +
> +void piglit_init(int argc, char **argv)
> +{
> +	bool pass = true;
> +
> +	GetDebugMessageLog = GET_FUNC(glGetDebugMessageLog);
> +	DebugMessageInsert = GET_FUNC(glDebugMessageInsert);
> +	DebugMessageControl = GET_FUNC(glDebugMessageControl);
> +	DebugMessageCallback = GET_FUNC(glDebugMessageCallback);
> +
> +	piglit_require_extension("GL_KHR_debug");
> +
> +	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
> +	glEnable(GL_DEBUG_OUTPUT);
> +
> +	if (!piglit_check_gl_error(GL_NO_ERROR))
> +		piglit_report_result(PIGLIT_FAIL);
> +
> +	init_messages();
> +	pass = test_set_control();
> +	pass = test_id_control() && pass;
> +	pass = test_mixed_control() && pass;
> +
> +	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
> +}
> +
> +enum piglit_result
> +piglit_display(void)
> +{
> +	return PIGLIT_PASS;
> +}