1/* 2 * Copyright © 2012-2017 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * 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 OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24/** 25 * \file performance_query.c 26 * Core Mesa support for the INTEL_performance_query extension. 27 */ 28 29#include <stdbool.h> 30#include "glheader.h" 31#include "context.h" 32#include "enums.h" 33#include "hash.h" 34#include "macros.h" 35#include "mtypes.h" 36#include "performance_query.h" 37#include "util/ralloc.h" 38 39void 40_mesa_init_performance_queries(struct gl_context *ctx) 41{ 42 ctx->PerfQuery.Objects = _mesa_NewHashTable(); 43} 44 45static void 46free_performance_query(GLuint key, void *data, void *user) 47{ 48 struct gl_perf_query_object *m = data; 49 struct gl_context *ctx = user; 50 51 ctx->Driver.DeletePerfQuery(ctx, m); 52} 53 54void 55_mesa_free_performance_queries(struct gl_context *ctx) 56{ 57 _mesa_HashDeleteAll(ctx->PerfQuery.Objects, 58 free_performance_query, ctx); 59 _mesa_DeleteHashTable(ctx->PerfQuery.Objects); 60} 61 62static inline struct gl_perf_query_object * 63lookup_object(struct gl_context *ctx, GLuint id) 64{ 65 return _mesa_HashLookup(ctx->PerfQuery.Objects, id); 66} 67 68static GLuint 69init_performance_query_info(struct gl_context *ctx) 70{ 71 if (ctx->Driver.InitPerfQueryInfo) 72 return ctx->Driver.InitPerfQueryInfo(ctx); 73 else 74 return 0; 75} 76 77/* For INTEL_performance_query, query id 0 is reserved to be invalid. */ 78static inline unsigned 79queryid_to_index(GLuint queryid) 80{ 81 return queryid - 1; 82} 83 84static inline GLuint 85index_to_queryid(unsigned index) 86{ 87 return index + 1; 88} 89 90static inline bool 91queryid_valid(const struct gl_context *ctx, unsigned numQueries, GLuint queryid) 92{ 93 /* The GL_INTEL_performance_query spec says: 94 * 95 * "Performance counter ids values start with 1. Performance counter id 0 96 * is reserved as an invalid counter." 97 */ 98 return queryid != 0 && queryid_to_index(queryid) < numQueries; 99} 100 101static inline GLuint 102counterid_to_index(GLuint counterid) 103{ 104 return counterid - 1; 105} 106 107static void 108output_clipped_string(GLchar *stringRet, 109 GLuint stringMaxLen, 110 const char *string) 111{ 112 if (!stringRet) 113 return; 114 115 strncpy(stringRet, string ? string : "", stringMaxLen); 116 117 /* No specification given about whether returned strings needs 118 * to be zero-terminated. Zero-terminate the string always as we 119 * don't otherwise communicate the length of the returned 120 * string. 121 */ 122 if (stringMaxLen > 0) 123 stringRet[stringMaxLen - 1] = '\0'; 124} 125 126/*****************************************************************************/ 127 128extern void GLAPIENTRY 129_mesa_GetFirstPerfQueryIdINTEL(GLuint *queryId) 130{ 131 GET_CURRENT_CONTEXT(ctx); 132 133 unsigned numQueries; 134 135 /* The GL_INTEL_performance_query spec says: 136 * 137 * "If queryId pointer is equal to 0, INVALID_VALUE error is generated." 138 */ 139 if (!queryId) { 140 _mesa_error(ctx, GL_INVALID_VALUE, 141 "glGetFirstPerfQueryIdINTEL(queryId == NULL)"); 142 return; 143 } 144 145 numQueries = init_performance_query_info(ctx); 146 147 /* The GL_INTEL_performance_query spec says: 148 * 149 * "If the given hardware platform doesn't support any performance 150 * queries, then the value of 0 is returned and INVALID_OPERATION error 151 * is raised." 152 */ 153 if (numQueries == 0) { 154 *queryId = 0; 155 _mesa_error(ctx, GL_INVALID_OPERATION, 156 "glGetFirstPerfQueryIdINTEL(no queries supported)"); 157 return; 158 } 159 160 *queryId = index_to_queryid(0); 161} 162 163extern void GLAPIENTRY 164_mesa_GetNextPerfQueryIdINTEL(GLuint queryId, GLuint *nextQueryId) 165{ 166 GET_CURRENT_CONTEXT(ctx); 167 168 unsigned numQueries; 169 170 /* The GL_INTEL_performance_query spec says: 171 * 172 * "The result is passed in location pointed by nextQueryId. If query 173 * identified by queryId is the last query available the value of 0 is 174 * returned. If the specified performance query identifier is invalid 175 * then INVALID_VALUE error is generated. If nextQueryId pointer is 176 * equal to 0, an INVALID_VALUE error is generated. Whenever error is 177 * generated, the value of 0 is returned." 178 */ 179 180 if (!nextQueryId) { 181 _mesa_error(ctx, GL_INVALID_VALUE, 182 "glGetNextPerfQueryIdINTEL(nextQueryId == NULL)"); 183 return; 184 } 185 186 numQueries = init_performance_query_info(ctx); 187 188 if (!queryid_valid(ctx, numQueries, queryId)) { 189 _mesa_error(ctx, GL_INVALID_VALUE, 190 "glGetNextPerfQueryIdINTEL(invalid query)"); 191 return; 192 } 193 194 if (queryid_valid(ctx, numQueries, ++queryId)) 195 *nextQueryId = queryId; 196 else 197 *nextQueryId = 0; 198} 199 200extern void GLAPIENTRY 201_mesa_GetPerfQueryIdByNameINTEL(char *queryName, GLuint *queryId) 202{ 203 GET_CURRENT_CONTEXT(ctx); 204 205 unsigned numQueries; 206 unsigned i; 207 208 /* The GL_INTEL_performance_query spec says: 209 * 210 * "If queryName does not reference a valid query name, an INVALID_VALUE 211 * error is generated." 212 */ 213 if (!queryName) { 214 _mesa_error(ctx, GL_INVALID_VALUE, 215 "glGetPerfQueryIdByNameINTEL(queryName == NULL)"); 216 return; 217 } 218 219 /* The specification does not state that this produces an error but 220 * to be consistent with glGetFirstPerfQueryIdINTEL we generate an 221 * INVALID_VALUE error 222 */ 223 if (!queryId) { 224 _mesa_error(ctx, GL_INVALID_VALUE, 225 "glGetPerfQueryIdByNameINTEL(queryId == NULL)"); 226 return; 227 } 228 229 numQueries = init_performance_query_info(ctx); 230 231 for (i = 0; i < numQueries; ++i) { 232 const GLchar *name; 233 GLuint ignore; 234 235 ctx->Driver.GetPerfQueryInfo(ctx, i, &name, &ignore, &ignore, &ignore); 236 237 if (strcmp(name, queryName) == 0) { 238 *queryId = index_to_queryid(i); 239 return; 240 } 241 } 242 243 _mesa_error(ctx, GL_INVALID_VALUE, 244 "glGetPerfQueryIdByNameINTEL(invalid query name)"); 245} 246 247extern void GLAPIENTRY 248_mesa_GetPerfQueryInfoINTEL(GLuint queryId, 249 GLuint nameLength, GLchar *name, 250 GLuint *dataSize, 251 GLuint *numCounters, 252 GLuint *numActive, 253 GLuint *capsMask) 254{ 255 GET_CURRENT_CONTEXT(ctx); 256 257 unsigned numQueries = init_performance_query_info(ctx); 258 unsigned queryIndex = queryid_to_index(queryId); 259 const char *queryName; 260 GLuint queryDataSize; 261 GLuint queryNumCounters; 262 GLuint queryNumActive; 263 264 if (!queryid_valid(ctx, numQueries, queryId)) { 265 /* The GL_INTEL_performance_query spec says: 266 * 267 * "If queryId does not reference a valid query type, an 268 * INVALID_VALUE error is generated." 269 */ 270 _mesa_error(ctx, GL_INVALID_VALUE, 271 "glGetPerfQueryInfoINTEL(invalid query)"); 272 return; 273 } 274 275 ctx->Driver.GetPerfQueryInfo(ctx, queryIndex, 276 &queryName, 277 &queryDataSize, 278 &queryNumCounters, 279 &queryNumActive); 280 281 output_clipped_string(name, nameLength, queryName); 282 283 if (dataSize) 284 *dataSize = queryDataSize; 285 286 if (numCounters) 287 *numCounters = queryNumCounters; 288 289 /* The GL_INTEL_performance_query spec says: 290 * 291 * "-- the actual number of already created query instances in 292 * maxInstances location" 293 * 294 * 1) Typo in the specification, should be noActiveInstances. 295 * 2) Another typo in the specification, maxInstances parameter is not listed 296 * in the declaration of this function in the list of new functions. 297 */ 298 if (numActive) 299 *numActive = queryNumActive; 300 301 /* Assume for now that all queries are per-context */ 302 if (capsMask) 303 *capsMask = GL_PERFQUERY_SINGLE_CONTEXT_INTEL; 304} 305 306extern void GLAPIENTRY 307_mesa_GetPerfCounterInfoINTEL(GLuint queryId, GLuint counterId, 308 GLuint nameLength, GLchar *name, 309 GLuint descLength, GLchar *desc, 310 GLuint *offset, 311 GLuint *dataSize, 312 GLuint *typeEnum, 313 GLuint *dataTypeEnum, 314 GLuint64 *rawCounterMaxValue) 315{ 316 GET_CURRENT_CONTEXT(ctx); 317 318 unsigned numQueries = init_performance_query_info(ctx); 319 unsigned queryIndex = queryid_to_index(queryId); 320 const char *queryName; 321 GLuint queryDataSize; 322 GLuint queryNumCounters; 323 GLuint queryNumActive; 324 unsigned counterIndex; 325 const char *counterName; 326 const char *counterDesc; 327 GLuint counterOffset; 328 GLuint counterDataSize; 329 GLuint counterTypeEnum; 330 GLuint counterDataTypeEnum; 331 GLuint64 counterRawMax; 332 333 if (!queryid_valid(ctx, numQueries, queryId)) { 334 /* The GL_INTEL_performance_query spec says: 335 * 336 * "If the pair of queryId and counterId does not reference a valid 337 * counter, an INVALID_VALUE error is generated." 338 */ 339 _mesa_error(ctx, GL_INVALID_VALUE, 340 "glGetPerfCounterInfoINTEL(invalid queryId)"); 341 return; 342 } 343 344 ctx->Driver.GetPerfQueryInfo(ctx, queryIndex, 345 &queryName, 346 &queryDataSize, 347 &queryNumCounters, 348 &queryNumActive); 349 350 counterIndex = counterid_to_index(counterId); 351 352 if (counterIndex >= queryNumCounters) { 353 _mesa_error(ctx, GL_INVALID_VALUE, 354 "glGetPerfCounterInfoINTEL(invalid counterId)"); 355 return; 356 } 357 358 ctx->Driver.GetPerfCounterInfo(ctx, queryIndex, counterIndex, 359 &counterName, 360 &counterDesc, 361 &counterOffset, 362 &counterDataSize, 363 &counterTypeEnum, 364 &counterDataTypeEnum, 365 &counterRawMax); 366 367 output_clipped_string(name, nameLength, counterName); 368 output_clipped_string(desc, descLength, counterDesc); 369 370 if (offset) 371 *offset = counterOffset; 372 373 if (dataSize) 374 *dataSize = counterDataSize; 375 376 if (typeEnum) 377 *typeEnum = counterTypeEnum; 378 379 if (dataTypeEnum) 380 *dataTypeEnum = counterDataTypeEnum; 381 382 if (rawCounterMaxValue) 383 *rawCounterMaxValue = counterRawMax; 384 385 if (rawCounterMaxValue) { 386 /* The GL_INTEL_performance_query spec says: 387 * 388 * "for some raw counters for which the maximal value is 389 * deterministic, the maximal value of the counter in 1 second is 390 * returned in the location pointed by rawCounterMaxValue, otherwise, 391 * the location is written with the value of 0." 392 * 393 * Since it's very useful to be able to report a maximum value for 394 * more that just counters using the _COUNTER_RAW_INTEL or 395 * _COUNTER_DURATION_RAW_INTEL enums (e.g. for a _THROUGHPUT tools 396 * want to be able to visualize the absolute throughput with respect 397 * to the theoretical maximum that's possible) and there doesn't seem 398 * to be any reason not to allow _THROUGHPUT counters to also be 399 * considerer "raw" here, we always leave it up to the backend to 400 * decide when it's appropriate to report a maximum counter value or 0 401 * if not. 402 */ 403 *rawCounterMaxValue = counterRawMax; 404 } 405} 406 407extern void GLAPIENTRY 408_mesa_CreatePerfQueryINTEL(GLuint queryId, GLuint *queryHandle) 409{ 410 GET_CURRENT_CONTEXT(ctx); 411 412 unsigned numQueries = init_performance_query_info(ctx); 413 GLuint id; 414 struct gl_perf_query_object *obj; 415 416 /* The GL_INTEL_performance_query spec says: 417 * 418 * "If queryId does not reference a valid query type, an INVALID_VALUE 419 * error is generated." 420 */ 421 if (!queryid_valid(ctx, numQueries, queryId)) { 422 _mesa_error(ctx, GL_INVALID_VALUE, 423 "glCreatePerfQueryINTEL(invalid queryId)"); 424 return; 425 } 426 427 /* This is not specified in the extension, but is the only sane thing to 428 * do. 429 */ 430 if (queryHandle == NULL) { 431 _mesa_error(ctx, GL_INVALID_VALUE, 432 "glCreatePerfQueryINTEL(queryHandle == NULL)"); 433 return; 434 } 435 436 id = _mesa_HashFindFreeKeyBlock(ctx->PerfQuery.Objects, 1); 437 if (!id) { 438 /* The GL_INTEL_performance_query spec says: 439 * 440 * "If the query instance cannot be created due to exceeding the 441 * number of allowed instances or driver fails query creation due to 442 * an insufficient memory reason, an OUT_OF_MEMORY error is 443 * generated, and the location pointed by queryHandle returns NULL." 444 */ 445 _mesa_error_no_memory(__func__); 446 return; 447 } 448 449 obj = ctx->Driver.NewPerfQueryObject(ctx, queryid_to_index(queryId)); 450 if (obj == NULL) { 451 _mesa_error_no_memory(__func__); 452 return; 453 } 454 455 obj->Id = id; 456 obj->Active = false; 457 obj->Ready = false; 458 459 _mesa_HashInsert(ctx->PerfQuery.Objects, id, obj); 460 *queryHandle = id; 461} 462 463extern void GLAPIENTRY 464_mesa_DeletePerfQueryINTEL(GLuint queryHandle) 465{ 466 GET_CURRENT_CONTEXT(ctx); 467 468 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle); 469 470 /* The GL_INTEL_performance_query spec says: 471 * 472 * "If a query handle doesn't reference a previously created performance 473 * query instance, an INVALID_VALUE error is generated." 474 */ 475 if (obj == NULL) { 476 _mesa_error(ctx, GL_INVALID_VALUE, 477 "glDeletePerfQueryINTEL(invalid queryHandle)"); 478 return; 479 } 480 481 /* To avoid complications in the backend we never ask the backend to 482 * delete an active query or a query object while we are still 483 * waiting for data. 484 */ 485 486 if (obj->Active) 487 _mesa_EndPerfQueryINTEL(queryHandle); 488 489 if (obj->Used && !obj->Ready) { 490 ctx->Driver.WaitPerfQuery(ctx, obj); 491 obj->Ready = true; 492 } 493 494 _mesa_HashRemove(ctx->PerfQuery.Objects, queryHandle); 495 ctx->Driver.DeletePerfQuery(ctx, obj); 496} 497 498extern void GLAPIENTRY 499_mesa_BeginPerfQueryINTEL(GLuint queryHandle) 500{ 501 GET_CURRENT_CONTEXT(ctx); 502 503 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle); 504 505 /* The GL_INTEL_performance_query spec says: 506 * 507 * "If a query handle doesn't reference a previously created performance 508 * query instance, an INVALID_VALUE error is generated." 509 */ 510 if (obj == NULL) { 511 _mesa_error(ctx, GL_INVALID_VALUE, 512 "glBeginPerfQueryINTEL(invalid queryHandle)"); 513 return; 514 } 515 516 /* The GL_INTEL_performance_query spec says: 517 * 518 * "Note that some query types, they cannot be collected in the same 519 * time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if 520 * they refer to queries of such different types. In such case 521 * INVALID_OPERATION error is generated." 522 * 523 * We also generate an INVALID_OPERATION error if the driver can't begin 524 * a query for its own reasons, and for nesting the same query. 525 */ 526 if (obj->Active) { 527 _mesa_error(ctx, GL_INVALID_OPERATION, 528 "glBeginPerfQueryINTEL(already active)"); 529 return; 530 } 531 532 /* To avoid complications in the backend we never ask the backend to 533 * reuse a query object and begin a new query while we are still 534 * waiting for data on that object. 535 */ 536 if (obj->Used && !obj->Ready) { 537 ctx->Driver.WaitPerfQuery(ctx, obj); 538 obj->Ready = true; 539 } 540 541 if (ctx->Driver.BeginPerfQuery(ctx, obj)) { 542 obj->Used = true; 543 obj->Active = true; 544 obj->Ready = false; 545 } else { 546 _mesa_error(ctx, GL_INVALID_OPERATION, 547 "glBeginPerfQueryINTEL(driver unable to begin query)"); 548 } 549} 550 551extern void GLAPIENTRY 552_mesa_EndPerfQueryINTEL(GLuint queryHandle) 553{ 554 GET_CURRENT_CONTEXT(ctx); 555 556 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle); 557 558 /* Not explicitly covered in the spec, but for consistency... */ 559 if (obj == NULL) { 560 _mesa_error(ctx, GL_INVALID_VALUE, 561 "glEndPerfQueryINTEL(invalid queryHandle)"); 562 return; 563 } 564 565 /* The GL_INTEL_performance_query spec says: 566 * 567 * "If a performance query is not currently started, an 568 * INVALID_OPERATION error will be generated." 569 */ 570 571 if (!obj->Active) { 572 _mesa_error(ctx, GL_INVALID_OPERATION, 573 "glEndPerfQueryINTEL(not active)"); 574 return; 575 } 576 577 ctx->Driver.EndPerfQuery(ctx, obj); 578 579 obj->Active = false; 580 obj->Ready = false; 581} 582 583extern void GLAPIENTRY 584_mesa_GetPerfQueryDataINTEL(GLuint queryHandle, GLuint flags, 585 GLsizei dataSize, void *data, GLuint *bytesWritten) 586{ 587 GET_CURRENT_CONTEXT(ctx); 588 589 struct gl_perf_query_object *obj = lookup_object(ctx, queryHandle); 590 591 /* Not explicitly covered in the spec, but for consistency... */ 592 if (obj == NULL) { 593 _mesa_error(ctx, GL_INVALID_VALUE, 594 "glEndPerfQueryINTEL(invalid queryHandle)"); 595 return; 596 } 597 598 /* The GL_INTEL_performance_query spec says: 599 * 600 * "If bytesWritten or data pointers are NULL then an INVALID_VALUE 601 * error is generated." 602 */ 603 if (!bytesWritten || !data) { 604 _mesa_error(ctx, GL_INVALID_VALUE, 605 "glGetPerfQueryDataINTEL(bytesWritten or data is NULL)"); 606 return; 607 } 608 609 /* Just for good measure in case a lazy application is only 610 * checking this and not checking for errors... 611 */ 612 *bytesWritten = 0; 613 614 /* Not explicitly covered in the spec but to be consistent with 615 * EndPerfQuery which validates that an application only ends an 616 * active query we also validate that an application doesn't try 617 * and get the data for a still active query... 618 */ 619 if (obj->Active) { 620 _mesa_error(ctx, GL_INVALID_OPERATION, 621 "glGetPerfQueryDataINTEL(query still active)"); 622 return; 623 } 624 625 obj->Ready = ctx->Driver.IsPerfQueryReady(ctx, obj); 626 627 if (!obj->Ready) { 628 if (flags == GL_PERFQUERY_FLUSH_INTEL) { 629 ctx->Driver.Flush(ctx); 630 } else if (flags == GL_PERFQUERY_WAIT_INTEL) { 631 ctx->Driver.WaitPerfQuery(ctx, obj); 632 obj->Ready = true; 633 } 634 } 635 636 if (obj->Ready) 637 ctx->Driver.GetPerfQueryData(ctx, obj, dataSize, data, bytesWritten); 638} 639