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#include "st_util.h" 33 34#include "util/bitset.h" 35 36#include "pipe/p_context.h" 37#include "pipe/p_screen.h" 38#include "util/u_memory.h" 39 40static bool 41init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 42{ 43 struct st_context *st = st_context(ctx); 44 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 45 struct pipe_context *pipe = st->pipe; 46 unsigned *batch = NULL; 47 unsigned num_active_counters = 0; 48 unsigned max_batch_counters = 0; 49 unsigned num_batch_counters = 0; 50 int gid, cid; 51 52 st_flush_bitmap_cache(st); 53 54 /* Determine the number of active counters. */ 55 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { 56 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; 57 const struct st_perf_monitor_group *stg = &st->perfmon[gid]; 58 59 if (m->ActiveGroups[gid] > g->MaxActiveCounters) { 60 /* Maximum number of counters reached. Cannot start the session. */ 61 if (ST_DEBUG & DEBUG_MESA) { 62 debug_printf("Maximum number of counters reached. " 63 "Cannot start the session!\n"); 64 } 65 return false; 66 } 67 68 num_active_counters += m->ActiveGroups[gid]; 69 if (stg->has_batch) 70 max_batch_counters += m->ActiveGroups[gid]; 71 } 72 73 if (!num_active_counters) 74 return true; 75 76 stm->active_counters = CALLOC(num_active_counters, 77 sizeof(*stm->active_counters)); 78 if (!stm->active_counters) 79 return false; 80 81 if (max_batch_counters) { 82 batch = CALLOC(max_batch_counters, sizeof(*batch)); 83 if (!batch) 84 return false; 85 } 86 87 /* Create a query for each active counter. */ 88 for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) { 89 const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid]; 90 const struct st_perf_monitor_group *stg = &st->perfmon[gid]; 91 BITSET_WORD tmp; 92 93 BITSET_FOREACH_SET(cid, tmp, m->ActiveCounters[gid], g->NumCounters) { 94 const struct st_perf_monitor_counter *stc = &stg->counters[cid]; 95 struct st_perf_counter_object *cntr = 96 &stm->active_counters[stm->num_active_counters]; 97 98 cntr->id = cid; 99 cntr->group_id = gid; 100 if (stc->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) { 101 cntr->batch_index = num_batch_counters; 102 batch[num_batch_counters++] = stc->query_type; 103 } else { 104 cntr->query = pipe->create_query(pipe, stc->query_type, 0); 105 if (!cntr->query) 106 goto fail; 107 } 108 ++stm->num_active_counters; 109 } 110 } 111 112 /* Create the batch query. */ 113 if (num_batch_counters) { 114 stm->batch_query = pipe->create_batch_query(pipe, num_batch_counters, 115 batch); 116 stm->batch_result = CALLOC(num_batch_counters, sizeof(stm->batch_result->batch[0])); 117 if (!stm->batch_query || !stm->batch_result) 118 goto fail; 119 } 120 121 FREE(batch); 122 return true; 123 124fail: 125 FREE(batch); 126 return false; 127} 128 129static void 130reset_perf_monitor(struct st_perf_monitor_object *stm, 131 struct pipe_context *pipe) 132{ 133 unsigned i; 134 135 for (i = 0; i < stm->num_active_counters; ++i) { 136 struct pipe_query *query = stm->active_counters[i].query; 137 if (query) 138 pipe->destroy_query(pipe, query); 139 } 140 FREE(stm->active_counters); 141 stm->active_counters = NULL; 142 stm->num_active_counters = 0; 143 144 if (stm->batch_query) { 145 pipe->destroy_query(pipe, stm->batch_query); 146 stm->batch_query = NULL; 147 } 148 FREE(stm->batch_result); 149 stm->batch_result = NULL; 150} 151 152static struct gl_perf_monitor_object * 153st_NewPerfMonitor(struct gl_context *ctx) 154{ 155 struct st_perf_monitor_object *stq = ST_CALLOC_STRUCT(st_perf_monitor_object); 156 if (stq) 157 return &stq->base; 158 return NULL; 159} 160 161static void 162st_DeletePerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 163{ 164 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 165 struct pipe_context *pipe = st_context(ctx)->pipe; 166 167 reset_perf_monitor(stm, pipe); 168 FREE(stm); 169} 170 171static GLboolean 172st_BeginPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 173{ 174 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 175 struct pipe_context *pipe = st_context(ctx)->pipe; 176 unsigned i; 177 178 if (!stm->num_active_counters) { 179 /* Create a query for each active counter before starting 180 * a new monitoring session. */ 181 if (!init_perf_monitor(ctx, m)) 182 goto fail; 183 } 184 185 /* Start the query for each active counter. */ 186 for (i = 0; i < stm->num_active_counters; ++i) { 187 struct pipe_query *query = stm->active_counters[i].query; 188 if (query && !pipe->begin_query(pipe, query)) 189 goto fail; 190 } 191 192 if (stm->batch_query && !pipe->begin_query(pipe, stm->batch_query)) 193 goto fail; 194 195 return true; 196 197fail: 198 /* Failed to start the monitoring session. */ 199 reset_perf_monitor(stm, pipe); 200 return false; 201} 202 203static void 204st_EndPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 205{ 206 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 207 struct pipe_context *pipe = st_context(ctx)->pipe; 208 unsigned i; 209 210 /* Stop the query for each active counter. */ 211 for (i = 0; i < stm->num_active_counters; ++i) { 212 struct pipe_query *query = stm->active_counters[i].query; 213 if (query) 214 pipe->end_query(pipe, query); 215 } 216 217 if (stm->batch_query) 218 pipe->end_query(pipe, stm->batch_query); 219} 220 221static void 222st_ResetPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m) 223{ 224 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 225 struct pipe_context *pipe = st_context(ctx)->pipe; 226 227 if (!m->Ended) 228 st_EndPerfMonitor(ctx, m); 229 230 reset_perf_monitor(stm, pipe); 231 232 if (m->Active) 233 st_BeginPerfMonitor(ctx, m); 234} 235 236static GLboolean 237st_IsPerfMonitorResultAvailable(struct gl_context *ctx, 238 struct gl_perf_monitor_object *m) 239{ 240 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 241 struct pipe_context *pipe = st_context(ctx)->pipe; 242 unsigned i; 243 244 if (!stm->num_active_counters) 245 return false; 246 247 /* The result of a monitoring session is only available if the query of 248 * each active counter is idle. */ 249 for (i = 0; i < stm->num_active_counters; ++i) { 250 struct pipe_query *query = stm->active_counters[i].query; 251 union pipe_query_result result; 252 if (query && !pipe->get_query_result(pipe, query, FALSE, &result)) { 253 /* The query is busy. */ 254 return false; 255 } 256 } 257 258 if (stm->batch_query && 259 !pipe->get_query_result(pipe, stm->batch_query, FALSE, stm->batch_result)) 260 return false; 261 262 return true; 263} 264 265static void 266st_GetPerfMonitorResult(struct gl_context *ctx, 267 struct gl_perf_monitor_object *m, 268 GLsizei dataSize, 269 GLuint *data, 270 GLint *bytesWritten) 271{ 272 struct st_perf_monitor_object *stm = st_perf_monitor_object(m); 273 struct pipe_context *pipe = st_context(ctx)->pipe; 274 unsigned i; 275 276 /* Copy data to the supplied array (data). 277 * 278 * The output data format is: <group ID, counter ID, value> for each 279 * active counter. The API allows counters to appear in any order. 280 */ 281 GLsizei offset = 0; 282 bool have_batch_query = false; 283 284 if (stm->batch_query) 285 have_batch_query = pipe->get_query_result(pipe, stm->batch_query, TRUE, 286 stm->batch_result); 287 288 /* Read query results for each active counter. */ 289 for (i = 0; i < stm->num_active_counters; ++i) { 290 struct st_perf_counter_object *cntr = &stm->active_counters[i]; 291 union pipe_query_result result = { 0 }; 292 int gid, cid; 293 GLenum type; 294 295 cid = cntr->id; 296 gid = cntr->group_id; 297 type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type; 298 299 if (cntr->query) { 300 if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result)) 301 continue; 302 } else { 303 if (!have_batch_query) 304 continue; 305 result.batch[0] = stm->batch_result->batch[cntr->batch_index]; 306 } 307 308 data[offset++] = gid; 309 data[offset++] = cid; 310 switch (type) { 311 case GL_UNSIGNED_INT64_AMD: 312 memcpy(&data[offset], &result.u64, sizeof(uint64_t)); 313 offset += sizeof(uint64_t) / sizeof(GLuint); 314 break; 315 case GL_UNSIGNED_INT: 316 memcpy(&data[offset], &result.u32, sizeof(uint32_t)); 317 offset += sizeof(uint32_t) / sizeof(GLuint); 318 break; 319 case GL_FLOAT: 320 case GL_PERCENTAGE_AMD: 321 memcpy(&data[offset], &result.f, sizeof(GLfloat)); 322 offset += sizeof(GLfloat) / sizeof(GLuint); 323 break; 324 } 325 } 326 327 if (bytesWritten) 328 *bytesWritten = offset * sizeof(GLuint); 329} 330 331 332bool 333st_have_perfmon(struct st_context *st) 334{ 335 struct pipe_screen *screen = st->pipe->screen; 336 337 if (!screen->get_driver_query_info || !screen->get_driver_query_group_info) 338 return false; 339 340 return screen->get_driver_query_group_info(screen, 0, NULL) != 0; 341} 342 343static void 344st_InitPerfMonitorGroups(struct gl_context *ctx) 345{ 346 struct st_context *st = st_context(ctx); 347 struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor; 348 struct pipe_screen *screen = st->pipe->screen; 349 struct gl_perf_monitor_group *groups = NULL; 350 struct st_perf_monitor_group *stgroups = NULL; 351 int num_counters, num_groups; 352 int gid, cid; 353 354 /* Get the number of available queries. */ 355 num_counters = screen->get_driver_query_info(screen, 0, NULL); 356 357 /* Get the number of available groups. */ 358 num_groups = screen->get_driver_query_group_info(screen, 0, NULL); 359 groups = CALLOC(num_groups, sizeof(*groups)); 360 if (!groups) 361 return; 362 363 stgroups = CALLOC(num_groups, sizeof(*stgroups)); 364 if (!stgroups) 365 goto fail_only_groups; 366 367 for (gid = 0; gid < num_groups; gid++) { 368 struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups]; 369 struct st_perf_monitor_group *stg = &stgroups[perfmon->NumGroups]; 370 struct pipe_driver_query_group_info group_info; 371 struct gl_perf_monitor_counter *counters = NULL; 372 struct st_perf_monitor_counter *stcounters = NULL; 373 374 if (!screen->get_driver_query_group_info(screen, gid, &group_info)) 375 continue; 376 377 g->Name = group_info.name; 378 g->MaxActiveCounters = group_info.max_active_queries; 379 380 if (group_info.num_queries) 381 counters = CALLOC(group_info.num_queries, sizeof(*counters)); 382 if (!counters) 383 goto fail; 384 g->Counters = counters; 385 386 stcounters = CALLOC(group_info.num_queries, sizeof(*stcounters)); 387 if (!stcounters) 388 goto fail; 389 stg->counters = stcounters; 390 391 for (cid = 0; cid < num_counters; cid++) { 392 struct gl_perf_monitor_counter *c = &counters[g->NumCounters]; 393 struct st_perf_monitor_counter *stc = &stcounters[g->NumCounters]; 394 struct pipe_driver_query_info info; 395 396 if (!screen->get_driver_query_info(screen, cid, &info)) 397 continue; 398 if (info.group_id != gid) 399 continue; 400 401 c->Name = info.name; 402 switch (info.type) { 403 case PIPE_DRIVER_QUERY_TYPE_UINT64: 404 case PIPE_DRIVER_QUERY_TYPE_BYTES: 405 case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS: 406 case PIPE_DRIVER_QUERY_TYPE_HZ: 407 c->Minimum.u64 = 0; 408 c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : -1; 409 c->Type = GL_UNSIGNED_INT64_AMD; 410 break; 411 case PIPE_DRIVER_QUERY_TYPE_UINT: 412 c->Minimum.u32 = 0; 413 c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : -1; 414 c->Type = GL_UNSIGNED_INT; 415 break; 416 case PIPE_DRIVER_QUERY_TYPE_FLOAT: 417 c->Minimum.f = 0.0; 418 c->Maximum.f = info.max_value.f ? info.max_value.f : -1; 419 c->Type = GL_FLOAT; 420 break; 421 case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE: 422 c->Minimum.f = 0.0f; 423 c->Maximum.f = 100.0f; 424 c->Type = GL_PERCENTAGE_AMD; 425 break; 426 default: 427 unreachable("Invalid driver query type!"); 428 } 429 430 stc->query_type = info.query_type; 431 stc->flags = info.flags; 432 if (stc->flags & PIPE_DRIVER_QUERY_FLAG_BATCH) 433 stg->has_batch = true; 434 435 g->NumCounters++; 436 } 437 perfmon->NumGroups++; 438 } 439 perfmon->Groups = groups; 440 st->perfmon = stgroups; 441 442 return; 443 444fail: 445 for (gid = 0; gid < num_groups; gid++) { 446 FREE(stgroups[gid].counters); 447 FREE((void *)groups[gid].Counters); 448 } 449 FREE(stgroups); 450fail_only_groups: 451 FREE(groups); 452} 453 454void 455st_destroy_perfmon(struct st_context *st) 456{ 457 struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor; 458 int gid; 459 460 for (gid = 0; gid < perfmon->NumGroups; gid++) { 461 FREE(st->perfmon[gid].counters); 462 FREE((void *)perfmon->Groups[gid].Counters); 463 } 464 FREE(st->perfmon); 465 FREE((void *)perfmon->Groups); 466} 467 468void st_init_perfmon_functions(struct dd_function_table *functions) 469{ 470 functions->InitPerfMonitorGroups = st_InitPerfMonitorGroups; 471 functions->NewPerfMonitor = st_NewPerfMonitor; 472 functions->DeletePerfMonitor = st_DeletePerfMonitor; 473 functions->BeginPerfMonitor = st_BeginPerfMonitor; 474 functions->EndPerfMonitor = st_EndPerfMonitor; 475 functions->ResetPerfMonitor = st_ResetPerfMonitor; 476 functions->IsPerfMonitorResultAvailable = st_IsPerfMonitorResultAvailable; 477 functions->GetPerfMonitorResult = st_GetPerfMonitorResult; 478} 479