[2/4] panfrost: Expose perfcounters through the pipe_query interface

Submitted by Boris Brezillon on April 4, 2019, 3:37 p.m.

Details

Message ID 20190404153741.18750-3-boris.brezillon@collabora.com
State New
Headers show
Series "panfrost: Expose HW counters through the pipe_query iface" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Boris Brezillon April 4, 2019, 3:37 p.m.
There's existing support for dumping perf counters in a files but
nothing to expose them in a standard way to the gallium pipe infra.

Define new driver hooks and add a common infra to expose perfcounters
in a standard way.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 src/gallium/drivers/panfrost/meson.build   |   3 +-
 src/gallium/drivers/panfrost/pan_context.c |  80 ++++++++-
 src/gallium/drivers/panfrost/pan_context.h |   9 +-
 src/gallium/drivers/panfrost/pan_perfcnt.c | 192 +++++++++++++++++++++
 src/gallium/drivers/panfrost/pan_perfcnt.h |  36 ++++
 src/gallium/drivers/panfrost/pan_screen.c  |   9 +-
 src/gallium/drivers/panfrost/pan_screen.h  |  41 +++++
 7 files changed, 362 insertions(+), 8 deletions(-)
 create mode 100644 src/gallium/drivers/panfrost/pan_perfcnt.c
 create mode 100644 src/gallium/drivers/panfrost/pan_perfcnt.h

Patch hide | download patch | download mbox

diff --git a/src/gallium/drivers/panfrost/meson.build b/src/gallium/drivers/panfrost/meson.build
index b7e7d0c58082..2dfac261aaa4 100644
--- a/src/gallium/drivers/panfrost/meson.build
+++ b/src/gallium/drivers/panfrost/meson.build
@@ -44,7 +44,8 @@  files_panfrost = files(
   'pan_pretty_print.c',
   'pan_fragment.c',
   'pan_sfbd.c',
-  'pan_mfbd.c'
+  'pan_mfbd.c',
+  'pan_perfcnt.c'
 )
 
 inc_panfrost = [
diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index 9f401b1a7a12..7c80b498e9da 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -2319,17 +2319,76 @@  panfrost_create_query(struct pipe_context *pipe,
         return (struct pipe_query *) q;
 }
 
+static struct pipe_query *
+panfrost_create_batch_query(struct pipe_context *pipe, unsigned num_queries,
+			    unsigned *query_types)
+{
+	struct panfrost_screen *screen = pan_screen(pipe->screen);
+	struct panfrost_context *ctx = pan_context(pipe);
+	struct panfrost_query *query;
+	unsigned i, nhwqueries = 0;
+
+	if (!screen->driver->create_perfcnt_query)
+		return NULL;
+
+	query = CALLOC_STRUCT(panfrost_query);
+	if (!query)
+		return NULL;
+
+	query->type = PIPE_QUERY_DRIVER_SPECIFIC;
+
+	for (i = 0; i < num_queries; i++) {
+		if (query_types[i] >= PIPE_QUERY_DRIVER_SPECIFIC)
+			nhwqueries++;
+	}
+
+	if (!nhwqueries || nhwqueries != num_queries)
+		goto err_free_query;
+
+	query->perfcnt.ncounters = num_queries;
+	query->perfcnt.counters = CALLOC(num_queries, sizeof(*query->perfcnt.counters));
+	if (!query->perfcnt.counters)
+		goto err_free_query;
+
+	for (i = 0; i < num_queries; i++)
+		query->perfcnt.counters[i] = query_types[i] - PIPE_QUERY_DRIVER_SPECIFIC;
+
+	if (!screen->driver->create_perfcnt_query(ctx, &query->perfcnt))
+		goto err_free_query;
+
+	return (struct pipe_query *)query;
+
+err_free_query:
+	if (query->perfcnt.counters)
+		free(query->perfcnt.counters);
+
+	free(query);
+	return NULL;
+}
+
 static void
 panfrost_destroy_query(struct pipe_context *pipe, struct pipe_query *q)
 {
+	struct panfrost_screen *screen = pan_screen(pipe->screen);
+	struct panfrost_context *ctx = pan_context(pipe);
+	struct panfrost_query *query = (struct panfrost_query *)q;
+
+	if (query->type == PIPE_QUERY_DRIVER_SPECIFIC &&
+	    screen->driver->destroy_perfcnt_query)
+		screen->driver->destroy_perfcnt_query(ctx, &query->perfcnt);
+
         FREE(q);
 }
 
 static boolean
 panfrost_begin_query(struct pipe_context *pipe, struct pipe_query *q)
 {
-        struct panfrost_context *ctx = pan_context(pipe);
-        struct panfrost_query *query = (struct panfrost_query *) q;
+	struct panfrost_screen *screen = pan_screen(pipe->screen);
+	struct panfrost_context *ctx = pan_context(pipe);
+	struct panfrost_query *query = (struct panfrost_query *)q;
+
+	if (query->type == PIPE_QUERY_DRIVER_SPECIFIC)
+		return screen->driver->begin_perfcnt_query(ctx, &query->perfcnt);
 
         switch (query->type) {
                 case PIPE_QUERY_OCCLUSION_COUNTER:
@@ -2355,8 +2414,14 @@  panfrost_begin_query(struct pipe_context *pipe, struct pipe_query *q)
 static bool
 panfrost_end_query(struct pipe_context *pipe, struct pipe_query *q)
 {
+	struct panfrost_screen *screen = pan_screen(pipe->screen);
+        struct panfrost_query *query = (struct panfrost_query *)q;
         struct panfrost_context *ctx = pan_context(pipe);
-        ctx->occlusion_query = NULL;
+
+	if (query->type == PIPE_QUERY_DRIVER_SPECIFIC)
+		return screen->driver->end_perfcnt_query(ctx, &query->perfcnt);
+
+	ctx->occlusion_query = NULL;
         return true;
 }
 
@@ -2367,13 +2432,19 @@  panfrost_get_query_result(struct pipe_context *pipe,
                           union pipe_query_result *vresult)
 {
         /* STUB */
-        struct panfrost_query *query = (struct panfrost_query *) q;
+        struct panfrost_query *query = (struct panfrost_query *)q;
+	struct panfrost_screen *screen = pan_screen(pipe->screen);
+        struct panfrost_context *ctx = pan_context(pipe);
 
         /* We need to flush out the jobs to actually run the counter, TODO
          * check wait, TODO wallpaper after if needed */
 
         panfrost_flush(pipe, NULL, PIPE_FLUSH_END_OF_FRAME);
 
+	if (query->type == PIPE_QUERY_DRIVER_SPECIFIC)
+		return screen->driver->get_perfcnt_results(ctx, &query->perfcnt,
+							   wait, vresult);
+
         switch (query->type) {
                 case PIPE_QUERY_OCCLUSION_COUNTER:
                 case PIPE_QUERY_OCCLUSION_PREDICATE:
@@ -2537,6 +2608,7 @@  panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
         gallium->set_active_query_state = panfrost_set_active_query_state;
 
         gallium->create_query = panfrost_create_query;
+	gallium->create_batch_query = panfrost_create_batch_query;
         gallium->destroy_query = panfrost_destroy_query;
         gallium->begin_query = panfrost_begin_query;
         gallium->end_query = panfrost_end_query;
diff --git a/src/gallium/drivers/panfrost/pan_context.h b/src/gallium/drivers/panfrost/pan_context.h
index d071da1c62fa..fa3748dd26d9 100644
--- a/src/gallium/drivers/panfrost/pan_context.h
+++ b/src/gallium/drivers/panfrost/pan_context.h
@@ -73,13 +73,20 @@  struct panfrost_constant_buffer {
         void *buffer;
 };
 
+struct panfrost_perfcnt_query {
+	unsigned int ncounters;
+	unsigned int *counters;
+	void *driver_data;
+};
+
 struct panfrost_query {
         /* Passthrough from Gallium */
         unsigned type;
         unsigned index;
 
         /* Memory for the GPU to writeback the value of the query */
-        struct panfrost_transfer transfer;
+	struct panfrost_transfer transfer;
+	struct panfrost_perfcnt_query perfcnt;
 };
 
 struct panfrost_fence {
diff --git a/src/gallium/drivers/panfrost/pan_perfcnt.c b/src/gallium/drivers/panfrost/pan_perfcnt.c
new file mode 100644
index 000000000000..3bac92ad359d
--- /dev/null
+++ b/src/gallium/drivers/panfrost/pan_perfcnt.c
@@ -0,0 +1,192 @@ 
+/**************************************************************************
+ *
+ * Copyright 2019 Collabora Ltd
+ * All Rights Reserved.
+ *
+ * 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, 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 VMWARE AND/OR ITS 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 "drm-uapi/panfrost_drm.h"
+
+#include "util/u_memory.h"
+
+#include "pan_context.h"
+#include "pan_perfcnt.h"
+#include "pan_screen.h"
+
+
+#define PANFROST_COUNTER(_id, _name)	\
+	{				\
+		.id = _id,		\
+		.name = _name,		\
+	}
+
+#define PANFROST_BLK_COUNTERS(_blk, ...)						\
+	.block[PANFROST_##_blk##_BLOCK] = {						\
+		.counters = (struct panfrost_counter[]) { __VA_ARGS__ },		\
+		.ncounters = sizeof((struct panfrost_counter[]) { __VA_ARGS__ }) /	\
+			     sizeof(struct panfrost_counter),				\
+	}
+
+struct panfrost_gpu_counters {
+	unsigned int gpu_id;
+	const struct panfrost_counters *counters;
+};
+
+static const struct panfrost_gpu_counters gpus[0];
+
+static const char *block_names[] = {
+	[PANFROST_SHADER_BLOCK] = "SHADER",
+	[PANFROST_TILER_BLOCK] = "TILER",
+	[PANFROST_MMU_L2_BLOCK] = "MMU_L2",
+	[PANFROST_JM_BLOCK] = "JM",
+};
+
+static void panfrost_perfcnt_init_queries(struct panfrost_screen *pscreen)
+{
+	struct panfrost_perfcnt_query_info *qinfo;
+	const struct panfrost_counters *counters;
+	unsigned int i, j, k;
+
+	counters = pscreen->perfcnt_info.counters;
+	for (i = 0; i < PANFROST_NUM_BLOCKS; i++) {
+		for (j = 0; j < 64; j++) {
+			if (!((1ULL << j) & pscreen->perfcnt_info.instances[i]))
+				continue;
+
+			pscreen->perfcnt_info.nqueries += counters->block[i].ncounters;
+		}
+	}
+
+	if (!pscreen->perfcnt_info.nqueries)
+		return;
+
+	pscreen->perfcnt_info.queries = CALLOC(pscreen->perfcnt_info.nqueries,
+					       sizeof(*pscreen->perfcnt_info.queries));
+	assert(pscreen->perfcnt_info.queries);
+
+	qinfo = pscreen->perfcnt_info.queries;
+
+	for (i = 0; i < PANFROST_NUM_BLOCKS; i++) {
+		for (j = 0; j < 64; j++) {
+
+			if (!((1ULL << j) & pscreen->perfcnt_info.instances[i]))
+				continue;
+
+			for (k = 0; k < counters->block[i].ncounters; k++) {
+				char *name;
+
+				asprintf(&name, "%s.%s-%d",
+					 counters->block[i].counters[k].name,
+					 block_names[i], j);
+				assert(name);
+				qinfo->name = name;
+				qinfo->block = i;
+				qinfo->instance = j;
+				qinfo->counter = k;
+				qinfo++;
+			}
+		}
+	}
+}
+
+static void panfrost_perfcnt_cleanup_queries(struct panfrost_screen *pscreen)
+{
+	unsigned int i;
+
+	if (!pscreen->perfcnt_info.nqueries)
+		return;
+
+	for (i = 0; i < pscreen->perfcnt_info.nqueries; i++)
+		FREE((void *)pscreen->perfcnt_info.queries[i].name);
+
+	FREE(pscreen->perfcnt_info.queries);
+}
+
+static int panfrost_get_query_group_info(struct pipe_screen *screen,
+					 unsigned index,
+					 struct pipe_driver_query_group_info *info)
+{
+	struct panfrost_screen *pscreen = pan_screen(screen);
+
+	if (!info)
+		return 1;
+
+	if (index)
+		return 0;
+
+	info->name = "Panfrost GPU counters";
+	info->num_queries = pscreen->perfcnt_info.nqueries;
+	info->max_active_queries = info->num_queries;
+	return 1;
+}
+
+static int panfrost_get_query_info(struct pipe_screen *screen, unsigned index,
+				   struct pipe_driver_query_info *info)
+{
+	struct panfrost_screen *pscreen = pan_screen(screen);
+	struct panfrost_perfcnt_query_info *qinfo;
+
+	if (!info)
+		return pscreen->perfcnt_info.nqueries;
+
+	if (index >= pscreen->perfcnt_info.nqueries)
+		return 0;
+
+	qinfo = &pscreen->perfcnt_info.queries[index];
+	info->group_id = 0;
+	info->flags = PIPE_DRIVER_QUERY_FLAG_BATCH;
+	info->type = PIPE_DRIVER_QUERY_TYPE_UINT64;
+	info->result_type = PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE;
+	info->query_type = PIPE_QUERY_DRIVER_SPECIFIC + index;
+	info->name = qinfo->name;
+	return 1;
+}
+
+void panfrost_perfcnt_init(struct panfrost_screen *pscreen)
+{
+	unsigned gpu_id, i;
+
+	gpu_id = pscreen->driver->query_gpu_version(pscreen);
+	for (i = 0; i < ARRAY_SIZE(gpus); i++) {
+		if (gpus[i].gpu_id == gpu_id)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(gpus))
+		return;
+
+	pscreen->perfcnt_info.counters = gpus[i].counters;
+
+	if (pscreen->driver->init_perfcnt)
+		pscreen->driver->init_perfcnt(pscreen);
+
+	panfrost_perfcnt_init_queries(pscreen);
+
+	pscreen->base.get_driver_query_group_info = panfrost_get_query_group_info;
+	pscreen->base.get_driver_query_info = panfrost_get_query_info;
+}
+
+void panfrost_perfcnt_cleanup(struct panfrost_screen *pscreen)
+{
+	panfrost_perfcnt_cleanup_queries(pscreen);
+}
diff --git a/src/gallium/drivers/panfrost/pan_perfcnt.h b/src/gallium/drivers/panfrost/pan_perfcnt.h
new file mode 100644
index 000000000000..50100eb451f0
--- /dev/null
+++ b/src/gallium/drivers/panfrost/pan_perfcnt.h
@@ -0,0 +1,36 @@ 
+/**************************************************************************
+ *
+ * Copyright 2019 Collabora Ltd
+ * All Rights Reserved.
+ *
+ * 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, 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 VMWARE AND/OR ITS 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.
+ *
+ **************************************************************************/
+
+#ifndef PAN_PERFCNT_H
+#define PAN_PERFCNT_H
+
+#include "pan_screen.h"
+
+void panfrost_perfcnt_init(struct panfrost_screen *screen);
+void panfrost_perfcnt_cleanup(struct panfrost_screen *screen);
+
+#endif /* PAN_PERFCNT_H */
diff --git a/src/gallium/drivers/panfrost/pan_screen.c b/src/gallium/drivers/panfrost/pan_screen.c
index 71c6175d0697..65faed726272 100644
--- a/src/gallium/drivers/panfrost/pan_screen.c
+++ b/src/gallium/drivers/panfrost/pan_screen.c
@@ -46,6 +46,7 @@ 
 
 #include "pan_screen.h"
 #include "pan_resource.h"
+#include "pan_perfcnt.h"
 #include "pan_public.h"
 #include "pan_util.h"
 
@@ -499,9 +500,12 @@  panfrost_is_format_supported( struct pipe_screen *screen,
 
 
 static void
-panfrost_destroy_screen( struct pipe_screen *screen )
+panfrost_destroy_screen(struct pipe_screen *pscreen)
 {
-        FREE(screen);
+	struct panfrost_screen *screen = pan_screen(pscreen);
+
+	panfrost_perfcnt_cleanup(screen);
+	FREE(screen);
 }
 
 static void
@@ -601,6 +605,7 @@  panfrost_create_screen(int fd, struct renderonly *ro)
 	screen->last_fragment_flushed = true;
 
         panfrost_resource_screen_init(screen);
+	panfrost_perfcnt_init(screen);
 
         return &screen->base;
 }
diff --git a/src/gallium/drivers/panfrost/pan_screen.h b/src/gallium/drivers/panfrost/pan_screen.h
index cbadf8136751..93d67ea71c0a 100644
--- a/src/gallium/drivers/panfrost/pan_screen.h
+++ b/src/gallium/drivers/panfrost/pan_screen.h
@@ -40,6 +40,7 @@ 
 struct panfrost_context;
 struct panfrost_resource;
 struct panfrost_screen;
+struct panfrost_perfcnt_query;
 
 /* Flags for allocated memory */
 #define PAN_ALLOCATE_EXECUTE (1 << 0)
@@ -76,6 +77,45 @@  struct panfrost_driver {
                       struct pipe_context *ctx,
                       struct pipe_fence_handle *fence,
                       uint64_t timeout);
+	void (*init_perfcnt) (struct panfrost_screen *screen);
+	boolean (*create_perfcnt_query) (struct panfrost_context *ctx,
+					 struct panfrost_perfcnt_query *q);
+	void (*destroy_perfcnt_query) (struct panfrost_context *ctx,
+				       struct panfrost_perfcnt_query *q);
+	boolean (*begin_perfcnt_query) (struct panfrost_context *ctx,
+					struct panfrost_perfcnt_query *q);
+	boolean (*end_perfcnt_query) (struct panfrost_context *ctx,
+				      struct panfrost_perfcnt_query *q);
+	boolean (*get_perfcnt_results) (struct panfrost_context *ctx,
+					struct panfrost_perfcnt_query *q,
+					boolean wait,
+					union pipe_query_result *vresults);
+};
+
+struct panfrost_counter {
+	unsigned int id;
+	const char *name;
+};
+
+struct panfrost_counters {
+	struct {
+		unsigned int ncounters;
+		struct panfrost_counter *counters;
+	} block[4];
+};
+
+struct panfrost_perfcnt_query_info {
+	const char *name;
+	uint8_t block;
+	uint8_t instance;
+	uint8_t counter;
+};
+
+struct panfrost_perfcnt_info {
+	const struct panfrost_counters *counters;
+	uint64_t instances[4];
+	unsigned int nqueries;
+	struct panfrost_perfcnt_query_info *queries;
 };
 
 struct panfrost_screen {
@@ -83,6 +123,7 @@  struct panfrost_screen {
 
         struct renderonly *ro;
         struct panfrost_driver *driver;
+	struct panfrost_perfcnt_info perfcnt_info;
 
         struct panfrost_memory perf_counters;
 

Comments

> +	if (!screen->driver->create_perfcnt_query)
> +		return NULL;

Redundant -- it'll always be set since there's only DRM driver now. We
should probably drop the indirection wholesale, too, but that's for a
different patch. I guess if the DRM implementation isn't until next
patch, it's still probably cleanest to just squash the two and hope for
the best.

> +	    screen->driver->destroy_perfcnt_query)

"""
>          /* We need to flush out the jobs to actually run the counter, TODO
>           * check wait, TODO wallpaper after if needed */
>  
>          panfrost_flush(pipe, NULL, PIPE_FLUSH_END_OF_FRAME);

Does this flush apply to performance counters too?

> +static int panfrost_get_query_group_info(struct pipe_screen *screen,
> +					 unsigned index,
> +					 struct pipe_driver_query_group_info *info)
> +{
> +	struct panfrost_screen *pscreen = pan_screen(screen);
> +
> +	if (!info)
> +		return 1;

Erm, semantically, what does this mean?
On Thu, 4 Apr 2019 08:49:28 -0700
Alyssa Rosenzweig <alyssa@rosenzweig.io> wrote:

> > +	if (!screen->driver->create_perfcnt_query)
> > +		return NULL;  
> 
> Redundant -- it'll always be set since there's only DRM driver now. We
> should probably drop the indirection wholesale, too, but that's for a
> different patch. I guess if the DRM implementation isn't until next
> patch, it's still probably cleanest to just squash the two and hope for
> the best.

Will drop the indirect calls, and move the code to perfcnt.c (unless
you want to keep it in panfrost_drm.c).

> 
> > +	    screen->driver->destroy_perfcnt_query)  
> 
> """
> >          /* We need to flush out the jobs to actually run the counter, TODO
> >           * check wait, TODO wallpaper after if needed */
> >  
> >          panfrost_flush(pipe, NULL, PIPE_FLUSH_END_OF_FRAME);  
> 
> Does this flush apply to performance counters too?

We need to at least flush all jobs that have the perfmon pointed by
this query attached to them.

> 
> > +static int panfrost_get_query_group_info(struct pipe_screen *screen,
> > +					 unsigned index,
> > +					 struct pipe_driver_query_group_info *info)
> > +{
> > +	struct panfrost_screen *pscreen = pan_screen(screen);
> > +
> > +	if (!info)
> > +		return 1;  
> 
> Erm, semantically, what does this mean?

It returns the number of perf counter groups. I could declare 4 groups
(one per block), but I'm not convinced it's really useful here. The
main interest of counter groups is when you have a large amount
of counters inside a counter block but only a limited number of counters
can be enabled simultaneously. Declaring groups in that case helps the
user take a decision about which one he wants to enable, or force him
to replay several times the same operation each time with different
counters enabled (that's what apitrace does IIRC).
> Will drop the indirect calls, and move the code to perfcnt.c (unless
> you want to keep it in panfrost_drm.c).

I'm not sure. Tomeu introduced the indirection/abstraction/etc to begin
with; you might want to know his thoughts. I don't know if he has plans
for continued refactors. Personally, it doesn't make much difference to
me, but it might be nicer to clean this up in a separate series (it's
logically unrelated to performance counters). Up to you two how best to
handle this, of course :)

> We need to at least flush all jobs that have the perfmon pointed by
> this query attached to them.

Cool, just making sure.

> It returns the number of perf counter groups. I could declare 4 groups
> (one per block), but I'm not convinced it's really useful here. The
> main interest of counter groups is when you have a large amount
> of counters inside a counter block but only a limited number of counters
> can be enabled simultaneously. Declaring groups in that case helps the
> user take a decision about which one he wants to enable, or force him
> to replay several times the same operation each time with different
> counters enabled (that's what apitrace does IIRC).

Sure, alright. Just thought I'd ask. :)