[Piglit] [PATCH] Add tests for the GL_AMD_performance_monitor extension.

Kenneth Graunke kenneth at whitecape.org
Tue Mar 26 22:21:23 PDT 2013


One of the challenging aspects of testing this extension is that it
defines an implementation-specific set of groups and counters.  Many of
the tests here arbitrarily operate on the counters in the first group,
while a few sanity check all counters in all groups.

All tests pass on AMD's Catalyst 12.06 driver on a Radeon 3650, except
for: test_number_of_groups_partial_array, test_delete_monitor_invalid,
and test_get_counter_data_byte_size.  I believe the first two are
legitimate bugs in AMD's implementation, and the latter is a
disagreement about the associativity of text within a paragraph.  Still,
these are minor details and I believe the tests are valid.

Signed-off-by: Kenneth Graunke <kenneth at whitecape.org>
---
 tests/all.tests                                    |    5 +
 tests/spec/CMakeLists.txt                          |    1 +
 .../spec/amd_performance_monitor/CMakeLists.gl.txt |   13 +
 tests/spec/amd_performance_monitor/CMakeLists.txt  |    1 +
 tests/spec/amd_performance_monitor/api.c           | 1080 ++++++++++++++++++++
 tests/spec/amd_performance_monitor/measure.c       |  387 +++++++
 6 files changed, 1487 insertions(+)
 create mode 100644 tests/spec/amd_performance_monitor/CMakeLists.gl.txt
 create mode 100644 tests/spec/amd_performance_monitor/CMakeLists.txt
 create mode 100644 tests/spec/amd_performance_monitor/api.c
 create mode 100644 tests/spec/amd_performance_monitor/measure.c

diff --git a/tests/all.tests b/tests/all.tests
index 911cd61..f8a40e2 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -863,6 +863,11 @@ add_shader_test_dir(spec['glsl-es-3.00'],
 		    recursive=True)
 add_concurrent_test(spec['glsl-es-3.00']['execution'], 'varying-struct-centroid_gles3')
 
+# AMD_performance_monitor
+profile.test_list['AMD_performance_monitor/api'] =
+	PlainExecTest('amd_performance_monitor_api -auto')
+profile.test_list['AMD_performance_monitor/measure'] =
+	PlainExecTest('amd_performance_monitor_measure -auto')
 
 # Group AMD_conservative_depth
 spec['AMD_conservative_depth'] = Group()
diff --git a/tests/spec/CMakeLists.txt b/tests/spec/CMakeLists.txt
index 18b1d37..a1492cc 100644
--- a/tests/spec/CMakeLists.txt
+++ b/tests/spec/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory (amd_performance_monitor)
 add_subdirectory (arb_color_buffer_float)
 add_subdirectory (arb_debug_output)
 add_subdirectory (arb_draw_instanced)
diff --git a/tests/spec/amd_performance_monitor/CMakeLists.gl.txt b/tests/spec/amd_performance_monitor/CMakeLists.gl.txt
new file mode 100644
index 0000000..5ddc12d
--- /dev/null
+++ b/tests/spec/amd_performance_monitor/CMakeLists.gl.txt
@@ -0,0 +1,13 @@
+include_directories(
+	${GLEXT_INCLUDE_DIR}
+	${OPENGL_INCLUDE_PATH}
+)
+
+link_libraries (
+	piglitutil_${piglit_target_api}
+	${OPENGL_gl_LIBRARY}
+	${OPENGL_glu_LIBRARY}
+)
+
+piglit_add_executable (amd_performance_monitor_api api.c)
+piglit_add_executable (amd_performance_monitor_measure measure.c)
diff --git a/tests/spec/amd_performance_monitor/CMakeLists.txt b/tests/spec/amd_performance_monitor/CMakeLists.txt
new file mode 100644
index 0000000..144a306
--- /dev/null
+++ b/tests/spec/amd_performance_monitor/CMakeLists.txt
@@ -0,0 +1 @@
+piglit_include_target_api()
diff --git a/tests/spec/amd_performance_monitor/api.c b/tests/spec/amd_performance_monitor/api.c
new file mode 100644
index 0000000..81a2641
--- /dev/null
+++ b/tests/spec/amd_performance_monitor/api.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/**
+ * \file api.c
+ *
+ * Basic AMD_performance_monitor infrastructure tests.  These test the
+ * mechanism to retrieve counter and group information, string processing,
+ * and various error conditions.  They do not actually activate monitoring.
+ */
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+	config.supports_gl_compat_version = 10;
+	config.window_visual = PIGLIT_GL_VISUAL_RGB;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+/******************************************************************************/
+
+/**
+ * Get a list of group IDs.
+ */
+static void
+get_groups(unsigned **groups, int *num_groups)
+{
+	glGetPerfMonitorGroupsAMD(num_groups, 0, NULL);
+	*groups = calloc(*num_groups, sizeof(unsigned));
+	glGetPerfMonitorGroupsAMD(NULL, *num_groups, *groups);
+}
+
+/**
+ * Get a list of counter IDs in a given group.
+ */
+static void
+get_counters(unsigned group, unsigned **counters, int *num_counters)
+{
+	glGetPerfMonitorCountersAMD(group, num_counters, NULL, 0, NULL);
+	*counters = calloc(*num_counters, sizeof(unsigned));
+	glGetPerfMonitorCountersAMD(group, NULL, NULL, *num_counters, *counters);
+}
+
+/**
+ * Return true if x is in xs.
+ */
+static bool
+in_list(int x, unsigned *xs, int elts)
+{
+	int i;
+	for (i = 0; i < elts; i++) {
+		if (x == xs[i])
+			return true;
+	}
+	return false;
+}
+
+/**
+ * Find an invalid group ID.
+ */
+static unsigned
+find_invalid_group(unsigned *groups, int num_groups)
+{
+	unsigned invalid_group = ~0;
+
+	/* Most implementations probably use small consecutive integers, so
+	 * start at ~0 and work backwards.  Hopefully we shouldn't loop.
+	 */
+	while (in_list(invalid_group, groups, num_groups))
+		--invalid_group;
+
+	return invalid_group;
+}
+
+/**
+ * Find an invalid counter ID.
+ */
+static unsigned
+find_invalid_counter(unsigned *counters, int num_counters)
+{
+	unsigned invalid_counter = ~0;
+
+	/* Most implementations probably use small consecutive integers, so
+	 * start at ~0 and work backwards.  Hopefully we shouldn't loop.
+	 */
+	while (in_list(invalid_counter, counters, num_counters))
+		--invalid_counter;
+
+	return invalid_counter;
+}
+
+#define report(pass) \
+    piglit_report_subtest_result((pass) ? PIGLIT_PASS : PIGLIT_FAIL, __func__); \
+    return
+
+/******************************************************************************/
+
+/**
+ * Call glGetPerfMonitorGroupsAMD() with a NULL numGroups pointer.
+ *
+ * Verify that it doesn't attempt to write the number of groups and crash.
+ */
+static void
+test_number_of_groups_null_num_groups_pointer(void)
+{
+	glGetPerfMonitorGroupsAMD(NULL, 0, NULL);
+	report(piglit_check_gl_error(GL_NO_ERROR));
+}
+
+
+/**
+ * Call glGetPerfMonitorGroupsAMD() with NULL for groups but non-zero groupSize.
+ *
+ * Verify that it returns the number of groups but doesn't try to write any
+ * group IDs and crash.
+ */
+static void
+test_number_of_groups_null_groups_pointer(void)
+{
+	bool pass = true;
+	int num_groups = -1;
+
+	glGetPerfMonitorGroupsAMD(&num_groups, 777, NULL);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = num_groups >= 0 && pass;
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorGroupsAMD() with zero for groupSize.
+ *
+ * Verify that it doesn't write any group IDs.
+ */
+static void
+test_number_of_groups_zero_size_array(void)
+{
+	bool pass = true;
+	unsigned groups[2] = {0xd0d0d0d0, 0xd1d1d1d1};
+	int num_groups = -1;
+
+	glGetPerfMonitorGroupsAMD(&num_groups, 0, groups);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* num_groups must have changed */
+	pass = num_groups >= 0 && pass;
+
+	/* The groups array should not have changed. */
+	pass = groups[0] == 0xd0d0d0d0 && pass;
+	pass = groups[1] == 0xd1d1d1d1 && pass;
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorGroupsAMD() with a groups array bigger than groupSize.
+ *
+ * Verify that it fills the correct number of array slots with group IDs.
+ */
+static void
+test_number_of_groups_partial_array(void)
+{
+	bool pass = true;
+	unsigned groups[] = {0xdddddddd, 0xdddddddd, 0xdddddddd, 0xdddddddd};
+
+	/* Artificially low array size */
+	const int groups_array_size = 2;
+	int num_groups = -1;
+	int i;
+
+	/* This should return the number of groups.  It should not attempt to
+	 * write any groups since the pointer is NULL.
+	 */
+	glGetPerfMonitorGroupsAMD(&num_groups, groups_array_size, groups);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* num_groups must have changed */
+	pass = num_groups >= 0 && pass;
+
+	/* The first few elements should have changed. */
+	for (i = 0; i < MIN2(num_groups, groups_array_size); i++) {
+		pass = groups[i] != 0xdddddddd && pass;
+	}
+
+	/* Catalyst 12.6 on a Radeon 3650 appears to have a bug where this
+	 * returns 3 elements instead of 2.  According to the spec,
+	 * "The number of entries that will be returned in <groups> is
+	 *  determined by <groupSize>."
+	 *
+	 * Technically, it does not say that N elements will be returned if
+	 * groupSize is N, but that's the only reasonable assumption.
+	 */
+
+	/* The rest should remain untouched. */
+	for (; i < ARRAY_SIZE(groups); i++) {
+		pass = groups[i] == 0xdddddddd && pass;
+	}
+
+	report(pass);
+}
+
+/******************************************************************************/
+
+/**
+ * Call glGetPerfMonitorCountersAMD() with an invalid group ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_get_counters_invalid_group(unsigned invalid_group)
+{
+	glGetPerfMonitorCountersAMD(invalid_group, NULL, NULL, 0, NULL);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorCountersAMD() with a bunch of NULL pointers.
+ *
+ * Verify that it doesn't crash attempting to write numCounters,
+ * maxActiveCounters, or the counters list.
+ */
+static void
+test_get_counters_null_pointers(unsigned valid_group)
+{
+	glGetPerfMonitorCountersAMD(valid_group, NULL, NULL, 0, NULL);
+	report(piglit_check_gl_error(GL_NO_ERROR));
+}
+
+/**
+ * Call glGetPerfMonitorCountersAMD() with NULL for the array but non-zero size.
+ *
+ * Verify that it returns the number of groups but doesn't try to write any
+ * group IDs and crash.
+ */
+static void
+test_get_counters_null_pointer_non_zero_size(unsigned valid_group)
+{
+	glGetPerfMonitorCountersAMD(valid_group, NULL, NULL, 777, NULL);
+	report(piglit_check_gl_error(GL_NO_ERROR));
+}
+
+/**
+ * Call glGetPerfMonitorCountersAMD() with zero for countersSize.
+ *
+ * Verify that it doesn't write any IDs, but does return other data.
+ */
+static void
+test_get_counters_zero_size_array(unsigned valid_group)
+{
+	bool pass = true;
+	unsigned counters[2] = {0xd0d0d0d0, 0xd1d1d1d1};
+	int num_counters = -1;
+	int max_active_counters = -1;
+
+	glGetPerfMonitorCountersAMD(valid_group, &num_counters,
+				    &max_active_counters,
+				    0, counters);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* Expect a positive number of counters. */
+	pass = num_counters >= 0 && pass;
+
+	/* Expect a positive maximum active counters. */
+	pass = max_active_counters >= 0 && pass;
+
+	/* The counters array should not have changed. */
+	pass = counters[0] == 0xd0d0d0d0 && pass;
+	pass = counters[1] == 0xd1d1d1d1 && pass;
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorGroupsAMD() with a groups array bigger than groupSize.
+ *
+ * Verify that it fills the correct number of array slots with group IDs.
+ */
+static void
+test_get_counters_partial_array(unsigned valid_group)
+{
+	bool pass = true;
+	unsigned counters[] = {0xdddddddd, 0xdddddddd, 0xdddddddd, 0xdddddddd};
+
+	/* Artificially low array size */
+	const int counters_array_size = 2;
+	int num_counters = -1;
+	int i;
+
+	/* This should return the number of groups.  It should not attempt to
+	 * write any groups since the pointer is NULL.
+	 */
+	glGetPerfMonitorCountersAMD(valid_group, &num_counters, NULL,
+				    counters_array_size, counters);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* num_counters must have changed */
+	pass = num_counters >= 0 && pass;
+
+	/* The first few elements should have changed. */
+	for (i = 0; i < MIN2(num_counters, counters_array_size); i++) {
+		pass = counters[i] != 0xdddddddd && pass;
+	}
+
+	/* The rest should remain untouched. */
+	for (; i < ARRAY_SIZE(counters); i++) {
+		pass = counters[i] == 0xdddddddd && pass;
+	}
+
+	report(pass);
+}
+
+/******************************************************************************/
+
+/**
+ * Call glGetPerfMonitorGroupStringAMD() with an invalid group ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_group_string_invalid_group(unsigned invalid_group)
+{
+	glGetPerfMonitorGroupStringAMD(invalid_group, 0, NULL, NULL);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorGroupStringAMD() with a NULL length pointer.
+ *
+ * Verify that it doesn't crash.
+ */
+static void
+test_group_string_null_length(unsigned valid_group)
+{
+	glGetPerfMonitorGroupStringAMD(valid_group, 0, NULL, NULL);
+	report(piglit_check_gl_error(GL_NO_ERROR));
+}
+
+/**
+ * Call glGetPerfMonitorGroupStringAMD() with a single character buffer.
+ *
+ * Verify that length is correct and no buffer overflows occur.
+ */
+static void
+test_group_string_single_character_buffer(unsigned valid_group)
+{
+	bool pass = true;
+	char name[3] = "```";
+	GLsizei length = 0xd0d0d0d0;
+
+	glGetPerfMonitorGroupStringAMD(valid_group, 1, &length, name);
+	pass = piglit_check_gl_error(GL_NO_ERROR);
+
+	/* Verify buffer contents: only the first character should change. */
+	pass = name[0] != '`' && pass;
+	pass = name[1] == '`' && pass;
+	pass = name[2] == '`' && pass;
+
+	/* length is the number of characters written excluding the null
+	 * terminator.
+	 */
+	if (name[0] == '\0') {
+		pass = length == 0 && pass;
+	} else {
+		/* AMD Catalyst 12.06 (Radeon 3650) does not write a null
+		 * terminator.  Instead, it writes the first part of the name.
+		 */
+		pass = length == 1 && pass;
+	}
+
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorGroupStringAMD() with a small buffer.
+ *
+ * Verify that a name is returned, length is valid, and no overflows occur.
+ */
+static void
+test_group_string_small_buffer(unsigned valid_group)
+{
+	bool pass = true;
+	char name[3] = "```";
+	GLsizei length = 0xd0d0d0d0;
+	int i;
+
+	glGetPerfMonitorGroupStringAMD(valid_group, 3, &length, name);
+
+	pass = length <= 3 && pass;
+
+	/* Verify buffer contents: accept no null terminator. */
+	for (i = 0; i < length; i++)
+		pass = name[i] != '`' && pass;
+
+	if (length < 3) {
+		pass = name[length] == '\0';
+		for (i = length + 1; i < 3; i++)
+			pass = name[i] == '`' && pass;
+	}
+
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorGroupStringAMD() with an appropriately sized buffer.
+ *
+ * Verify that a name is returned, length is valid, and no overflows occur.
+ */
+static void
+test_group_string_normal_buffer(unsigned valid_group)
+{
+	bool pass = true;
+	char *name;
+	GLsizei length = 0xd0d0d0d0;
+	int i;
+
+	/* Get the length; bail if unwritten to avoid huge allocations. */
+	glGetPerfMonitorGroupStringAMD(valid_group, 0, &length, NULL);
+	pass = pass && piglit_check_gl_error(GL_NO_ERROR);
+	if (length == 0xd0d0d0d0)
+		report(false);
+
+	name = malloc(length + 1);
+	assert(name != NULL);
+
+	/* Fill the buffer with a known character (` marks) */
+	memset(name, '`', length + 1);
+
+	/* Get the name; everything will fit. */
+	glGetPerfMonitorGroupStringAMD(valid_group, length + 1, NULL, name);
+	pass = pass && piglit_check_gl_error(GL_NO_ERROR);
+
+	/* Indexes in the interval [0, length) must have been written, or
+	 * else length is wrong.
+	 */
+	for (i = 0; i < length; i++)
+		pass = name[i] != '`' && pass;
+
+	/* The last character must be the null terminator. */
+	pass = name[length] == '\0' && pass;
+
+	report(pass);
+}
+
+/******************************************************************************/
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with an invalid group ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_counter_string_invalid_group(unsigned invalid_group)
+{
+	glGetPerfMonitorCounterStringAMD(invalid_group, 0, 0, NULL, NULL);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with an invalid counter ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_counter_string_invalid_counter(unsigned group, unsigned invalid_counter)
+{
+	glGetPerfMonitorCounterStringAMD(group, invalid_counter, 0, NULL, NULL);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with a NULL length pointer.
+ *
+ * Verify that it doesn't crash.
+ */
+static void
+test_counter_string_null_length(unsigned group, unsigned counter)
+{
+	glGetPerfMonitorCounterStringAMD(group, counter, 0, NULL, NULL);
+	report(piglit_check_gl_error(GL_NO_ERROR));
+}
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with a single character buffer.
+ *
+ * Verify that length is correct and no buffer overflows occur.
+ */
+static void
+test_counter_string_single_character_buffer(unsigned group, unsigned counter)
+{
+	bool pass = true;
+	char name[3] = "```";
+	GLsizei length = 0xd0d0d0d0;
+
+	glGetPerfMonitorCounterStringAMD(group, counter, 1, &length, name);
+	pass = piglit_check_gl_error(GL_NO_ERROR);
+
+	/* Verify buffer contents */
+	pass = name[0] != '`' && pass;
+	pass = name[1] == '`' && pass;
+	pass = name[2] == '`' && pass;
+
+	/* length is the number of characters written excluding the null
+	 * terminator.
+	 */
+	if (name[0] == '\0') {
+		pass = length == 0 && pass;
+	} else {
+		/* AMD Catalyst 12.06 (Radeon 3650) does not write a null
+		 * terminator.  Instead, it writes the first part of the name.
+		 */
+		pass = length == 1 && pass;
+	}
+
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with a small buffer.
+ *
+ * Verify that a name is returned, length is valid, and no overflows occur.
+ */
+static void
+test_counter_string_small_buffer(unsigned group, unsigned counter)
+{
+	bool pass = true;
+	char name[3] = "```";
+	GLsizei length = 0xd0d0d0d0;
+	int i;
+
+	glGetPerfMonitorCounterStringAMD(group, counter, 3, &length, name);
+
+	pass = length <= 3 && pass;
+
+	/* Verify buffer contents: accept no null terminator. */
+	for (i = 0; i < length; i++)
+		pass = name[i] != '`' && pass;
+
+	if (length < 3) {
+		pass = name[length] == '\0';
+		for (i = length + 1; i < 3; i++)
+			pass = name[i] == '`' && pass;
+	}
+
+	report(pass);
+}
+
+/**
+ * Call glGetPerfMonitorCounterStringAMD() with an appropriately sized buffer.
+ *
+ * Verify that a name is returned, length is valid, and no overflows occur.
+ */
+static void
+test_counter_string_normal_buffer(unsigned group, unsigned counter)
+{
+	bool pass = true;
+	char *name;
+	GLsizei length = 0xd0d0d0d0;
+	int i;
+
+	/* Get the length; bail if unwritten to avoid huge allocations. */
+	glGetPerfMonitorCounterStringAMD(group, counter, 0, &length, NULL);
+	pass = pass && piglit_check_gl_error(GL_NO_ERROR);
+	if (length == 0xd0d0d0d0)
+		report(false);
+
+	name = malloc(length + 1);
+	assert(name != NULL);
+
+	/* Fill the buffer with a known character (` marks) */
+	memset(name, '`', length + 1);
+
+	/* Get the name; everything will fit. */
+	glGetPerfMonitorCounterStringAMD(group, counter, length + 1, NULL, name);
+	pass = pass && piglit_check_gl_error(GL_NO_ERROR);
+
+	/* Indexes in the interval [0, length) must have been written, or
+	 * else length is wrong.
+	 */
+	for (i = 0; i < length; i++)
+		pass = name[i] != '`' && pass;
+
+	/* The last character must be the null terminator. */
+	pass = name[length] == '\0' && pass;
+
+	report(pass);
+}
+
+/******************************************************************************/
+
+/**
+ * Call glGetPerfMonitorCounterInfoAMD() with an invalid group ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_counter_info_invalid_group(unsigned invalid_group)
+{
+	GLenum type;
+	glGetPerfMonitorCounterInfoAMD(invalid_group, 0, GL_COUNTER_TYPE_AMD,
+				       &type);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorCounterInfoAMD() with an invalid counter ID.
+ *
+ * Verify that it produces INVALID_VALUE.
+ */
+static void
+test_counter_info_invalid_counter(unsigned group, unsigned invalid_counter)
+{
+	GLenum type;
+	glGetPerfMonitorCounterInfoAMD(group, invalid_counter,
+				       GL_COUNTER_TYPE_AMD, &type);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glGetPerfMonitorCounterInfoAMD() on every group/counter and verify that:
+ * - All counters must have a valid type.
+ * - Percentage counters must have a range of [0.0f, 100.0f]
+ * - Counter ranges should return a minimum strictly less than the maximum.
+ * - The counter range query doesn't return too much data.
+ */
+static void
+test_counter_info(unsigned *groups, int num_groups)
+{
+	int i;
+	int j;
+
+	for (i = 0; i < num_groups; i++) {
+		unsigned *counters;
+		int num_counters;
+		get_counters(groups[i], &counters, &num_counters);
+
+		for (j = 0; j < num_counters; j++) {
+			GLenum type = GL_NONE;
+			uint64_t data[3];
+			uint64_t min_u, max_u;
+			float min_f, max_f;
+			uint32_t unchanged;
+			bool is_unsigned = false;
+
+			glGetPerfMonitorCounterInfoAMD(groups[i], counters[j],
+						       GL_COUNTER_TYPE_AMD,
+						       &type);
+
+			/* Get the range */
+			memset(data, 0xff, sizeof(uint64_t) * 3);
+			glGetPerfMonitorCounterInfoAMD(groups[i], counters[j],
+						       GL_COUNTER_RANGE_AMD,
+						       data);
+
+			/* Validate the type and use it to interpret the
+			 * minimum/maximum information.
+			 */
+			switch (type) {
+			case GL_UNSIGNED_INT:
+				min_u = ((uint32_t *) data)[0];
+				max_u = ((uint32_t *) data)[1];
+				unchanged = ((uint32_t *) data)[2];
+				is_unsigned = true;
+				break;
+			case GL_UNSIGNED_INT64_AMD:
+				min_u = data[0];
+				max_u = data[1];
+				unchanged = ((uint32_t *) data)[4];
+				is_unsigned = true;
+				break;
+			case GL_PERCENTAGE_AMD:
+			case GL_FLOAT:
+				min_f = ((float *) data)[0];
+				max_f = ((float *) data)[1];
+				unchanged = ((uint32_t *) data)[2];
+				break;
+			default:
+				printf("Group %u/Counter %u has an invalid type: %x\n", groups[i], counters[j], type);
+				report(false);
+			}
+
+			/* Make sure it didn't write too much data. */
+			if (unchanged != 0xffffffff) {
+				printf("COUNTER_RANGE_AMD query for group %u/Counter %u wrote too much data to the buffer.\n", groups[i], counters[j]);
+				report(false);
+			}
+
+			/* "If type value returned is PERCENTAGE_AMD, then this
+			 *  describes a float value that is in the range [0.0 ..
+			 *  100.0]."  So we can check this.
+			 */
+			if (type == GL_PERCENTAGE_AMD) {
+				if (min_f != 0.0f || max_f != 100.0f) {
+					printf("Group %u/Counter %u's minimum (%f) and maximum (%f) must be 0.0f and 100.0f, respectively.\n", groups[i], counters[j], min_f, max_f);
+					report(false);
+				}
+			} else if (is_unsigned) {
+				/* The spec doesn't explicitly state it, but it
+				 * makes sense for the minimum to be strictly
+				 * less than the maximum.  Do a service to
+				 * driver authors and validate that.
+				 */
+				if (min_u >= max_u) {
+					printf("Group %u/Counter %u's minimum (%" PRIu64 ") is >= the maximum (%" PRIu64 ").\n", groups[i], counters[j], min_u, max_u);
+					report(false);
+				}
+			} else if (type == GL_FLOAT) {
+				if (min_f >= max_f) {
+					printf("Group %u/Counter %u's minimum (%f) is >= the maximum (%f).\n", groups[i], counters[j], min_f, max_f);
+					report(false);
+				}
+			}
+		}
+
+		free(counters);
+	}
+	report(true);
+}
+
+/******************************************************************************/
+
+
+/**
+ * Call glBeginPerfMonitorAMD() on an invalid monitor ID.
+ * (Should be run before any Gen tests to ensure this ID is invalid.)
+ *
+ * XXX: This isn't actually specified, but it seems like it ought to be.
+ */
+void
+test_begin_invalid_monitor(void)
+{
+	glBeginPerfMonitorAMD(777);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glEndPerfMonitorAMD() on an invalid monitor ID.
+ * (Should be run before any Gen tests to ensure this ID is invalid.)
+ *
+ * XXX: This isn't actually specified, but it seems like it ought to be.
+ *
+ * AMD Catalyst 12.6 (Radeon 3650) instead produces INVALID_OPERATION,
+ * presumably because the (invalid) monitor hasn't been started.  (See
+ * test_end_without_begin.)  So we allow either here.
+ */
+void
+test_end_invalid_monitor(void)
+{
+	GLenum error;
+	glEndPerfMonitorAMD(777);
+	error = glGetError();
+	report(error == GL_INVALID_VALUE || error == GL_INVALID_OPERATION);
+}
+
+/**
+ * Call glGetPerfMonitorCounterDataAMD() with an invalid monitor ID.
+ *
+ * XXX: This isn't actually specified, but it seems like it ought to be.
+ */
+static void
+test_get_counter_data_invalid_monitor(void)
+{
+	unsigned value;
+	glGetPerfMonitorCounterDataAMD(777, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       0, &value, NULL);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glSelectPerfMonitorCountersAMD() with an invalid monitor ID.
+ *
+ * "If <monitor> is not a valid monitor created by GenPerfMonitorsAMD, then
+ *  INVALID_VALUE will be generated."
+ */
+static void
+test_select_counters_invalid_monitor(void)
+{
+	unsigned junk;
+	glSelectPerfMonitorCountersAMD(777, false, 0, 0, &junk);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Call glDeletePerfMonitorsAMD() on an invalid monitor ID.
+ * (Should be run before any Gen tests to ensure this ID is invalid.)
+ *
+ * "If a monitor ID in the list <monitors> does not reference a previously
+ *  generated performance monitor, an INVALID_VALUE error is generated."
+ *
+ * AMD Catalyst 12.6 (Radeon 3650) fails this test, producing NO_ERROR.
+ */
+static void
+test_delete_monitor_invalid(void)
+{
+	unsigned monitor = 777;
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(piglit_check_gl_error(GL_INVALID_VALUE));
+}
+
+/**
+ * Mean tests for glGetPerfMonitorCounterDataAMD()'s data return mechanism.
+ *
+ * AMD Catalyst 12.6 (Radeon 3650) fails this test.  It does not set
+ * bytes_written, yet writes 0 for each of these queries.  It apparently
+ * interprets these fields as only relevant to the PERFMON_RESULT_AMD query.
+ */
+static void
+test_get_counter_data_byte_size(void)
+{
+	bool pass = true;
+	unsigned monitor;
+	unsigned value;
+	GLsizei bytes_written;
+
+	glGenPerfMonitorsAMD(1, &monitor);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* "It is an INVALID_OPERATION error far <data> to be NULL." */
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       0, NULL, NULL);
+	pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
+
+	/* "The argument <dataSize> specifies the number of bytes available in
+	 *  the <data> buffer for writing."
+	 *
+	 * It would be easy to accidentally treat this as 4-byte units, so
+	 * be mean and try < sizeof(int) sizes.
+	 */
+
+	/* dataSize = 0: Nothing should be written. */
+	value = bytes_written = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       0, &value, &bytes_written);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = value == 0xd0d0d0d0 && pass;
+	pass = bytes_written == 0 && pass;
+
+	/* dataSize = 1: Unclear.  Accept nothing or 1 byte written. */
+	value = bytes_written = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       1, &value, &bytes_written);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = value == 0xd0d0d0d0 && pass;
+	if (bytes_written == 1) {
+		pass = value == 0xd0d0d000 && pass;
+	} else if (bytes_written == 0) {
+		pass = value == 0xd0d0d0d0 && pass;
+	} else {
+		pass = false;
+	}
+
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(pass);
+}
+
+static void
+test_gen_initial_state(void)
+{
+	bool pass = true;
+	unsigned monitor;
+	unsigned value;
+
+	glGenPerfMonitorsAMD(1, &monitor);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	/* "The value of the PERFMON_RESULT_AVAILABLE_AMD, PERMON_RESULT_AMD,
+	 *  and PERFMON_RESULT_SIZE queries will all initially be 0."
+	 */
+	value = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       4, &value, NULL);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = value == 0 && pass;
+
+	/* AMD Catalyst 12.6 (Radeon 3650) actually does write 0 for the
+	 * PERFMON_RESULT query even though it isn't available.  This
+	 * matches the spec, but is strange.
+	 */
+	value = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AMD,
+				       4, &value, NULL);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = value == 0 && pass;
+
+	value = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
+				       4, &value, NULL);
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+	pass = value == 0 && pass;
+
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(pass);
+}
+
+/**
+ * "INVALID_OPERATION error will be generated if EndPerfMonitorAMD is
+ *  called when a performance monitor is not currently started."
+ */
+void
+test_end_without_begin(void)
+{
+	unsigned monitor;
+	glGenPerfMonitorsAMD(1, &monitor);
+	glEndPerfMonitorAMD(monitor);
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(piglit_check_gl_error(GL_INVALID_OPERATION));
+}
+
+/**
+ * "INVALID_OPERATION error will be generated if BeginPerfMonitorAMD is
+ *  called when a performance monitor is already active."
+ */
+void
+test_double_begin(void)
+{
+	GLenum error;
+	bool pass;
+	unsigned monitor;
+	glGenPerfMonitorsAMD(1, &monitor);
+	glBeginPerfMonitorAMD(monitor);
+
+	error = glGetError();
+	if (error != GL_NO_ERROR) {
+		glDeletePerfMonitorsAMD(1, &monitor);
+		/* Monitoring couldn't start for some reason; bail. */
+		if (error == GL_INVALID_OPERATION)
+			return;
+		/* We weren't expecting this other error. */
+		report(false);
+	}
+
+	/* Double begin */
+	glBeginPerfMonitorAMD(monitor);
+	pass = piglit_check_gl_error(GL_INVALID_OPERATION);
+
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(pass);
+}
+
+/******************************************************************************/
+
+/**
+ * Call glSelectPerfMonitorCountersAMD() with an invalid group ID.
+ *
+ * "If <group> is not a valid group, the INVALID_VALUE error will be generated."
+ */
+static void
+test_select_counters_invalid_group(unsigned invalid_group)
+{
+	unsigned monitor;
+	unsigned junk;
+	bool pass;
+	glGenPerfMonitorsAMD(1, &monitor);
+	glSelectPerfMonitorCountersAMD(monitor, false, invalid_group, 0, &junk);
+	pass = piglit_check_gl_error(GL_INVALID_VALUE);
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(pass);
+}
+
+
+/**
+ * Call glSelectPerfMonitorCountersAMD() with numCounters < 0.
+ *
+ * "If <numCounters> is less than 0, an INVALID_VALUE error will be generated."
+ */
+static void
+test_select_counters_invalid_num_counters(unsigned group)
+{
+	unsigned monitor;
+	unsigned junk;
+	bool pass;
+	glGenPerfMonitorsAMD(1, &monitor);
+	glSelectPerfMonitorCountersAMD(monitor, false, group, -1, &junk);
+	pass = piglit_check_gl_error(GL_INVALID_VALUE);
+	glDeletePerfMonitorsAMD(1, &monitor);
+	report(pass);
+}
+
+/******************************************************************************/
+
+enum piglit_result
+piglit_display(void)
+{
+	return PIGLIT_FAIL;
+}
+
+/**
+ * The main test program.
+ */
+void
+piglit_init(int argc, char **argv)
+{
+	unsigned *groups;
+	int num_groups;
+	unsigned *g0_counters;
+	int num_g0_counters;
+	unsigned invalid_group;
+	unsigned invalid_counter;
+
+	piglit_require_extension("GL_AMD_performance_monitor");
+
+	/* Basic glGetPerfMonitorGroupsAMD() tests */
+	test_number_of_groups_null_num_groups_pointer();
+	test_number_of_groups_null_groups_pointer();
+	test_number_of_groups_zero_size_array();
+	test_number_of_groups_partial_array();
+
+	get_groups(&groups, &num_groups);
+	invalid_group = find_invalid_group(groups, num_groups);
+
+	test_get_counters_invalid_group(invalid_group);
+	test_group_string_invalid_group(invalid_group);
+	test_counter_string_invalid_group(invalid_group);
+	test_counter_info_invalid_group(invalid_group);
+
+	test_begin_invalid_monitor();
+	test_end_invalid_monitor();
+	test_delete_monitor_invalid();
+	test_get_counter_data_invalid_monitor();
+	test_select_counters_invalid_monitor();
+	test_get_counter_data_byte_size();
+	test_gen_initial_state();
+	test_end_without_begin();
+	test_double_begin();
+
+	test_select_counters_invalid_group(invalid_group);
+
+	/* If there are no groups, the rest of the tests can't run.  Bail. */
+	if (num_groups == 0)
+		exit(0);
+
+	test_get_counters_null_pointers(groups[0]);
+	test_get_counters_null_pointer_non_zero_size(groups[0]);
+	test_get_counters_zero_size_array(groups[0]);
+	test_get_counters_partial_array(groups[0]);
+	test_group_string_null_length(groups[0]);
+	test_group_string_single_character_buffer(groups[0]);
+	test_group_string_small_buffer(groups[0]);
+	test_group_string_normal_buffer(groups[0]);
+
+	test_counter_info(groups, num_groups);
+
+	test_select_counters_invalid_num_counters(groups[0]);
+
+	get_counters(groups[0], &g0_counters, &num_g0_counters);
+	invalid_counter = find_invalid_counter(g0_counters, num_g0_counters);
+
+	test_counter_string_invalid_counter(groups[0], invalid_counter);
+	test_counter_info_invalid_counter(groups[0], invalid_counter);
+
+	/* If there are no counters, the rest of the tests can't run.  Bail. */
+	if (num_g0_counters == 0)
+		exit(0);
+
+	test_counter_string_null_length(groups[0], g0_counters[0]);
+	test_counter_string_single_character_buffer(groups[0], g0_counters[0]);
+	test_counter_string_small_buffer(groups[0], g0_counters[0]);
+	test_counter_string_normal_buffer(groups[0], g0_counters[0]);
+
+	exit(0);
+}
diff --git a/tests/spec/amd_performance_monitor/measure.c b/tests/spec/amd_performance_monitor/measure.c
new file mode 100644
index 0000000..e89ff00
--- /dev/null
+++ b/tests/spec/amd_performance_monitor/measure.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/**
+ * \file measure.c
+ *
+ * Some AMD_performance_monitor tests that actually measure things.
+ */
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+	config.supports_gl_compat_version = 10;
+	config.window_visual = PIGLIT_GL_VISUAL_RGB;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+/******************************************************************************/
+
+/**
+ * Get a list of group IDs.
+ */
+static void
+get_groups(unsigned **groups, int *num_groups)
+{
+	glGetPerfMonitorGroupsAMD(num_groups, 0, NULL);
+	*groups = calloc(*num_groups, sizeof(unsigned));
+	glGetPerfMonitorGroupsAMD(NULL, *num_groups, *groups);
+}
+
+/**
+ * Get a list of counter IDs in a given group.
+ */
+static void
+get_counters(unsigned group, unsigned **counters, int *num_counters,
+	     int *max_active_counters)
+{
+	glGetPerfMonitorCountersAMD(group, num_counters, NULL, 0, NULL);
+	*counters = calloc(*num_counters, sizeof(unsigned));
+	glGetPerfMonitorCountersAMD(group, NULL, max_active_counters,
+				    *num_counters, *counters);
+}
+
+#define verify(x)                                                     \
+	if (!(x)) {                                                   \
+		piglit_report_subtest_result(PIGLIT_FAIL, test_name); \
+		return;                                               \
+	}
+
+/******************************************************************************/
+
+/**
+ * Poll until PERFMON_RESULT_AVAILABLE returns 1; glFinish() on each iteration.
+ *
+ * Only loop for 5 times to guard against implementations that never finish.
+ */
+static bool
+wait_until_available(unsigned monitor)
+{
+	int i;
+	unsigned available = 0;
+	for (i = 0; !available && i < 5; i++) {
+		glFinish();
+		glGetPerfMonitorCounterDataAMD(monitor,
+					       GL_PERFMON_RESULT_AVAILABLE_AMD,
+					       sizeof(unsigned), &available,
+					       NULL);
+	}
+	return available;
+}
+
+/**
+ * Basic functional test: enable all the counters in the first group
+ * (up to the maximum that can be active at a time), begin monitoring,
+ * end monitoring, make sure results are available, sanity check the
+ * result size, and get the results.
+ */
+static void
+test_basic_measurement(unsigned group)
+{
+	unsigned monitor;
+	unsigned *counters;
+	int num_counters;
+	int max_active_counters;
+	unsigned usable_counters;
+	unsigned result_size = 0;
+	GLsizei bytes_written = 0;
+	unsigned *data;
+
+	uint32_t *p;
+	unsigned value;
+
+	const char *test_name;
+
+	/**
+	 * Test #1: Basic Measurement.
+	 *
+	 * Enable all the counters in the first group (up to the maximum that
+	 * can be active at a time), begin monitoring, end monitoring, make
+	 * sure results are available, sanity check the result size, and get
+	 * the results.
+	 */
+	test_name = "basic measurement";
+
+	get_counters(group, &counters, &num_counters,
+		     &max_active_counters);
+	verify(max_active_counters >= 0);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	usable_counters = MIN2(num_counters, max_active_counters);
+
+	glGenPerfMonitorsAMD(1, &monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Enable counters 0 .. usable_counters from the list. */
+	glSelectPerfMonitorCountersAMD(monitor, true, group, usable_counters,
+				       counters);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Start monitoring */
+	glBeginPerfMonitorAMD(monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Drawing...meh */
+	glFinish();
+
+	/* End monitoring */
+	glEndPerfMonitorAMD(monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Wait for the result to be available. */
+	verify(wait_until_available(monitor));
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Get the result size. */
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
+				       sizeof(unsigned), &result_size, NULL);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Make sure the size is in bytes. */
+	verify(result_size % sizeof(unsigned) == 0);
+
+	/* The format is <Group ID, Group, Value>.  The first two are
+	 * uint32_ts.  Value is either a float, uint32_t, or uint64_t.
+	 * As a sanity check, make sure the result size is within
+	 * reasonable limits.  Don't bother checking the actual types
+	 * since that's a bunch of work.
+	 */
+	verify(result_size >= 3 * sizeof(uint32_t) * usable_counters)
+	verify(result_size <=
+	       (2 * sizeof(uint32_t) + sizeof(uint64_t)) * usable_counters);
+
+	/* Get the results. */
+	data = calloc(1, result_size);
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AMD,
+				       result_size, data, &bytes_written);
+
+	verify(bytes_written == result_size);
+
+	piglit_report_subtest_result(PIGLIT_PASS, test_name);
+
+	/**
+	 * Test #2: Verify counter results against specified range.
+	 */
+	p = data;
+	while ((char *) p < ((char *) data) + bytes_written) {
+		uint32_t group_id = p[0];
+		uint32_t counter_id = p[1];
+
+		/* Counter values */
+		uint32_t u32 = p[2];
+		float f = ((float *) p)[2];
+		uint64_t u64 = ((uint64_t *) p)[1];
+
+		/* Query results */
+		GLenum counter_type = GL_NONE;
+		uint64_t range[2];
+
+		/* There's only one group, so it better match */
+		verify(group_id == group);
+
+		/* Getting the counter data also validates the counter ID
+		 * without having to walk through the whole list of counters.
+		 */
+		glGetPerfMonitorCounterInfoAMD(group_id, counter_id,
+					       GL_COUNTER_TYPE_AMD,
+					       &counter_type);
+		verify(piglit_check_gl_error(GL_NO_ERROR));
+
+		glGetPerfMonitorCounterInfoAMD(group_id, counter_id,
+					       GL_COUNTER_RANGE_AMD,
+					       range);
+		verify(piglit_check_gl_error(GL_NO_ERROR));
+
+		/* Make sure it falls within the proper range */
+		switch (counter_type) {
+		case GL_UNSIGNED_INT: {
+			uint32_t min = ((uint32_t *) range)[0];
+			uint32_t max = ((uint32_t *) range)[1];
+			verify(u32 >= min);
+			verify(u32 <= max);
+			break;
+		}
+		case GL_UNSIGNED_INT64_AMD: {
+			verify(u64 >= range[0]);
+			verify(u64 <= range[1]);
+			break;
+		}
+		case GL_PERCENTAGE_AMD:
+		case GL_FLOAT: {
+			float min = ((float *) range)[0];
+			float max = ((float *) range)[1];
+			verify(f >= min);
+			verify(f <= max);
+			break;
+		}
+		}
+
+		p += (counter_type == GL_UNSIGNED_INT64_AMD) ? 4 : 3;
+	}
+	verify(result_size == ((char *) p - (char *) data));
+
+	free(data);
+
+	/**
+	 * Test #3: Changing the set of active counters resets queries.
+	 *
+	 * "When SelectPerfMonitorCountersAMD is called on a monitor, any
+	 *  outstanding results for that monitor become invalidated and the
+	 *  result queries PERFMON_RESULT_SIZE_AMD and
+	 *  PERFMON_RESULT_AVAILABLE_AMD are reset to 0."
+	 */
+	test_name = "selecting counters resets queries";
+
+	/* Turn off the first counter. */
+	glSelectPerfMonitorCountersAMD(monitor, false, group, 1, counters);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Results should no longer be available.  All queries should
+	 * return 0.
+	 */
+	value = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       sizeof(unsigned), &value, NULL);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+	verify(value == 0);
+
+	value = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
+				       sizeof(unsigned), &value, NULL);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+	verify(value == 0);
+
+	piglit_report_subtest_result(PIGLIT_PASS, test_name);
+
+	glDeletePerfMonitorsAMD(1, &monitor);
+}
+
+
+/**
+ * Make sure that calling SelectPerfMonitorCountersAMD on an active monitor
+ * is possible, resets active queries, and restarts monitoring (so it remains
+ * active).
+ *
+ * This is not actually specified, but matches the behavior of AMD's driver.
+ * Being an AMD extension, other implementations should probably match theirs.
+ */
+static void
+test_change_counters_while_active(unsigned group)
+{
+	unsigned monitor;
+	unsigned *counters;
+	int num_counters;
+	int max_active_counters;
+	unsigned usable_counters;
+	unsigned data;
+	const char *test_name = "change counters while active";
+
+	get_counters(group, &counters, &num_counters,
+		     &max_active_counters);
+	verify(max_active_counters >= 0);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	usable_counters = MIN2(num_counters, max_active_counters);
+
+	if (usable_counters == 0)
+		return; /* skip */
+
+	glGenPerfMonitorsAMD(1, &monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Enable counters 0 .. usable_counters from the list. */
+	glSelectPerfMonitorCountersAMD(monitor, true, group, usable_counters,
+				       counters);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Start monitoring */
+	glBeginPerfMonitorAMD(monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Turn off the first counter.  The specification is unclear whether
+	 * or not this should be allowed while monitoring is active, but it
+	 * apparently is (Catalyst 12.06 on a Radeon 3650).
+	 */
+	glSelectPerfMonitorCountersAMD(monitor, false, group, 1, counters);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	/* Verify that all queries have been reset to 0 */
+	data = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
+				       sizeof(unsigned), &data, NULL);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+	verify(data == 0);
+
+	data = 0xd0d0d0d0;
+	glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
+				       sizeof(unsigned), &data, NULL);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+	verify(data == 0);
+
+	/* The spec doesn't explicitly mention whether or not monitoring
+	 * is still active, but apparently it is.
+	 */
+	glEndPerfMonitorAMD(monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	glDeletePerfMonitorsAMD(1, &monitor);
+	verify(piglit_check_gl_error(GL_NO_ERROR));
+
+	piglit_report_subtest_result(PIGLIT_PASS, test_name);
+}
+
+
+/******************************************************************************/
+
+enum piglit_result
+piglit_display(void)
+{
+	return PIGLIT_FAIL;
+}
+
+/**
+ * The main test program.
+ */
+void
+piglit_init(int argc, char **argv)
+{
+	unsigned *groups;
+	int num_groups;
+
+	piglit_require_extension("GL_AMD_performance_monitor");
+
+	/* Basic glGetPerfMonitorGroupsAMD() tests */
+	get_groups(&groups, &num_groups);
+
+	/* If there are no groups, the rest of the tests can't run.  Bail. */
+	if (num_groups == 0)
+		exit(0);
+
+	test_basic_measurement(groups[0]);
+	test_change_counters_while_active(groups[0]);
+
+	exit(0);
+}
-- 
1.8.2



More information about the Piglit mailing list