ec12eb222065c5a2a2f1ba4cd8fa123230d0178f
[mesa.git] / src / mesa / state_tracker / st_cb_perfmon.c
1 /*
2 * Copyright (C) 2013 Christoph Bumiller
3 * Copyright (C) 2015 Samuel Pitoiset
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * Performance monitoring counters interface to gallium.
26 */
27
28 #include "st_debug.h"
29 #include "st_context.h"
30 #include "st_cb_bitmap.h"
31 #include "st_cb_perfmon.h"
32
33 #include "util/bitset.h"
34
35 #include "pipe/p_context.h"
36 #include "pipe/p_screen.h"
37 #include "util/u_memory.h"
38
39 static bool
40 init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
41 {
42 struct st_context *st = st_context(ctx);
43 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
44 struct pipe_context *pipe = st->pipe;
45 int gid, cid;
46
47 st_flush_bitmap_cache(st);
48
49 /* Create a query for each active counter. */
50 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) {
51 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid];
52 const struct st_perf_monitor_group *stg = &st->perfmon[gid];
53 BITSET_WORD tmp;
54
55 if (m->ActiveGroups[gid] > g->MaxActiveCounters) {
56 /* Maximum number of counters reached. Cannot start the session. */
57 if (ST_DEBUG & DEBUG_MESA) {
58 debug_printf("Maximum number of counters reached. "
59 "Cannot start the session!\n");
60 }
61 return false;
62 }
63
64 BITSET_FOREACH_SET(cid, tmp, m->ActiveCounters[gid], g->NumCounters) {
65 const struct st_perf_monitor_counter *stc = &stg->counters[cid];
66 struct st_perf_counter_object *cntr;
67
68 cntr = CALLOC_STRUCT(st_perf_counter_object);
69 if (!cntr)
70 return false;
71
72 cntr->query = pipe->create_query(pipe, stc->query_type, 0);
73 cntr->id = cid;
74 cntr->group_id = gid;
75
76 list_addtail(&cntr->list, &stm->active_counters);
77 }
78 }
79 return true;
80 }
81
82 static void
83 reset_perf_monitor(struct st_perf_monitor_object *stm,
84 struct pipe_context *pipe)
85 {
86 struct st_perf_counter_object *cntr, *tmp;
87
88 LIST_FOR_EACH_ENTRY_SAFE(cntr, tmp, &stm->active_counters, list) {
89 if (cntr->query)
90 pipe->destroy_query(pipe, cntr->query);
91 list_del(&cntr->list);
92 free(cntr);
93 }
94 }
95
96 static struct gl_perf_monitor_object *
97 st_NewPerfMonitor(struct gl_context *ctx)
98 {
99 struct st_perf_monitor_object *stq = ST_CALLOC_STRUCT(st_perf_monitor_object);
100 if (stq) {
101 list_inithead(&stq->active_counters);
102 return &stq->base;
103 }
104 return NULL;
105 }
106
107 static void
108 st_DeletePerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
109 {
110 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
111 struct pipe_context *pipe = st_context(ctx)->pipe;
112
113 reset_perf_monitor(stm, pipe);
114 FREE(stm);
115 }
116
117 static GLboolean
118 st_BeginPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
119 {
120 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
121 struct pipe_context *pipe = st_context(ctx)->pipe;
122 struct st_perf_counter_object *cntr;
123
124 if (LIST_IS_EMPTY(&stm->active_counters)) {
125 /* Create a query for each active counter before starting
126 * a new monitoring session. */
127 if (!init_perf_monitor(ctx, m))
128 goto fail;
129 }
130
131 /* Start the query for each active counter. */
132 LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
133 if (!pipe->begin_query(pipe, cntr->query))
134 goto fail;
135 }
136 return true;
137
138 fail:
139 /* Failed to start the monitoring session. */
140 reset_perf_monitor(stm, pipe);
141 return false;
142 }
143
144 static void
145 st_EndPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
146 {
147 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
148 struct pipe_context *pipe = st_context(ctx)->pipe;
149 struct st_perf_counter_object *cntr;
150
151 /* Stop the query for each active counter. */
152 LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list)
153 pipe->end_query(pipe, cntr->query);
154 }
155
156 static void
157 st_ResetPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
158 {
159 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
160 struct pipe_context *pipe = st_context(ctx)->pipe;
161
162 if (!m->Ended)
163 st_EndPerfMonitor(ctx, m);
164
165 reset_perf_monitor(stm, pipe);
166
167 if (m->Active)
168 st_BeginPerfMonitor(ctx, m);
169 }
170
171 static GLboolean
172 st_IsPerfMonitorResultAvailable(struct gl_context *ctx,
173 struct gl_perf_monitor_object *m)
174 {
175 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
176 struct pipe_context *pipe = st_context(ctx)->pipe;
177 struct st_perf_counter_object *cntr;
178
179 if (LIST_IS_EMPTY(&stm->active_counters))
180 return false;
181
182 /* The result of a monitoring session is only available if the query of
183 * each active counter is idle. */
184 LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
185 union pipe_query_result result;
186 if (!pipe->get_query_result(pipe, cntr->query, FALSE, &result)) {
187 /* The query is busy. */
188 return false;
189 }
190 }
191 return true;
192 }
193
194 static void
195 st_GetPerfMonitorResult(struct gl_context *ctx,
196 struct gl_perf_monitor_object *m,
197 GLsizei dataSize,
198 GLuint *data,
199 GLint *bytesWritten)
200 {
201 struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
202 struct pipe_context *pipe = st_context(ctx)->pipe;
203 struct st_perf_counter_object *cntr;
204
205 /* Copy data to the supplied array (data).
206 *
207 * The output data format is: <group ID, counter ID, value> for each
208 * active counter. The API allows counters to appear in any order.
209 */
210 GLsizei offset = 0;
211
212 /* Read query results for each active counter. */
213 LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
214 union pipe_query_result result = { 0 };
215 int gid, cid;
216 GLenum type;
217
218 cid = cntr->id;
219 gid = cntr->group_id;
220 type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type;
221
222 if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result))
223 continue;
224
225 data[offset++] = gid;
226 data[offset++] = cid;
227 switch (type) {
228 case GL_UNSIGNED_INT64_AMD:
229 *(uint64_t *)&data[offset] = result.u64;
230 offset += sizeof(uint64_t) / sizeof(GLuint);
231 break;
232 case GL_UNSIGNED_INT:
233 *(uint32_t *)&data[offset] = result.u32;
234 offset += sizeof(uint32_t) / sizeof(GLuint);
235 break;
236 case GL_FLOAT:
237 case GL_PERCENTAGE_AMD:
238 *(GLfloat *)&data[offset] = result.f;
239 offset += sizeof(GLfloat) / sizeof(GLuint);
240 break;
241 }
242 }
243
244 if (bytesWritten)
245 *bytesWritten = offset * sizeof(GLuint);
246 }
247
248
249 bool
250 st_init_perfmon(struct st_context *st)
251 {
252 struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor;
253 struct pipe_screen *screen = st->pipe->screen;
254 struct gl_perf_monitor_group *groups = NULL;
255 struct st_perf_monitor_group *stgroups = NULL;
256 int num_counters, num_groups;
257 int gid, cid;
258
259 if (!screen->get_driver_query_info || !screen->get_driver_query_group_info)
260 return false;
261
262 /* Get the number of available queries. */
263 num_counters = screen->get_driver_query_info(screen, 0, NULL);
264 if (!num_counters)
265 return false;
266
267 /* Get the number of available groups. */
268 num_groups = screen->get_driver_query_group_info(screen, 0, NULL);
269 if (num_groups)
270 groups = CALLOC(num_groups, sizeof(*groups));
271 if (!groups)
272 return false;
273
274 stgroups = CALLOC(num_groups, sizeof(*stgroups));
275 if (!stgroups)
276 goto fail_only_groups;
277
278 for (gid = 0; gid < num_groups; gid++) {
279 struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups];
280 struct pipe_driver_query_group_info group_info;
281 struct gl_perf_monitor_counter *counters = NULL;
282 struct st_perf_monitor_counter *stcounters = NULL;
283
284 if (!screen->get_driver_query_group_info(screen, gid, &group_info))
285 continue;
286
287 g->Name = group_info.name;
288 g->MaxActiveCounters = group_info.max_active_queries;
289
290 if (group_info.num_queries)
291 counters = CALLOC(group_info.num_queries, sizeof(*counters));
292 if (!counters)
293 goto fail;
294 g->Counters = counters;
295
296 stcounters = CALLOC(group_info.num_queries, sizeof(*stcounters));
297 if (!stcounters)
298 goto fail;
299 stgroups[perfmon->NumGroups].counters = stcounters;
300
301 for (cid = 0; cid < num_counters; cid++) {
302 struct gl_perf_monitor_counter *c = &counters[g->NumCounters];
303 struct st_perf_monitor_counter *stc = &stcounters[g->NumCounters];
304 struct pipe_driver_query_info info;
305
306 if (!screen->get_driver_query_info(screen, cid, &info))
307 continue;
308 if (info.group_id != gid)
309 continue;
310
311 c->Name = info.name;
312 switch (info.type) {
313 case PIPE_DRIVER_QUERY_TYPE_UINT64:
314 case PIPE_DRIVER_QUERY_TYPE_BYTES:
315 case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS:
316 case PIPE_DRIVER_QUERY_TYPE_HZ:
317 c->Minimum.u64 = 0;
318 c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : -1;
319 c->Type = GL_UNSIGNED_INT64_AMD;
320 break;
321 case PIPE_DRIVER_QUERY_TYPE_UINT:
322 c->Minimum.u32 = 0;
323 c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : -1;
324 c->Type = GL_UNSIGNED_INT;
325 break;
326 case PIPE_DRIVER_QUERY_TYPE_FLOAT:
327 c->Minimum.f = 0.0;
328 c->Maximum.f = info.max_value.f ? info.max_value.f : -1;
329 c->Type = GL_FLOAT;
330 break;
331 case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE:
332 c->Minimum.f = 0.0f;
333 c->Maximum.f = 100.0f;
334 c->Type = GL_PERCENTAGE_AMD;
335 break;
336 default:
337 unreachable("Invalid driver query type!");
338 }
339
340 stc->query_type = info.query_type;
341
342 g->NumCounters++;
343 }
344 perfmon->NumGroups++;
345 }
346 perfmon->Groups = groups;
347 st->perfmon = stgroups;
348
349 return true;
350
351 fail:
352 for (gid = 0; gid < num_groups; gid++) {
353 FREE(stgroups[gid].counters);
354 FREE((void *)groups[gid].Counters);
355 }
356 FREE(stgroups);
357 fail_only_groups:
358 FREE(groups);
359 return false;
360 }
361
362 void
363 st_destroy_perfmon(struct st_context *st)
364 {
365 struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor;
366 int gid;
367
368 for (gid = 0; gid < perfmon->NumGroups; gid++) {
369 FREE(st->perfmon[gid].counters);
370 FREE((void *)perfmon->Groups[gid].Counters);
371 }
372 FREE(st->perfmon);
373 FREE((void *)perfmon->Groups);
374 }
375
376 void st_init_perfmon_functions(struct dd_function_table *functions)
377 {
378 functions->NewPerfMonitor = st_NewPerfMonitor;
379 functions->DeletePerfMonitor = st_DeletePerfMonitor;
380 functions->BeginPerfMonitor = st_BeginPerfMonitor;
381 functions->EndPerfMonitor = st_EndPerfMonitor;
382 functions->ResetPerfMonitor = st_ResetPerfMonitor;
383 functions->IsPerfMonitorResultAvailable = st_IsPerfMonitorResultAvailable;
384 functions->GetPerfMonitorResult = st_GetPerfMonitorResult;
385 }