1 /************************************************************************** 2 * 3 * Copyright 2013 Marek Olk <maraeo (at) gmail.com> 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 /* This file contains code for reading values from pipe queries 29 * for displaying on the HUD. To prevent stalls when reading queries, we 30 * keep a list of busy queries in a ring. We read only those queries which 31 * are idle. 32 */ 33 34 #include "hud/hud_private.h" 35 #include "pipe/p_screen.h" 36 #include "util/os_time.h" 37 #include "util/u_math.h" 38 #include "util/u_memory.h" 39 #include <stdio.h> 40 41 // Must be a power of two 42 #define NUM_QUERIES 8 43 44 struct hud_batch_query_context { 45 unsigned num_query_types; 46 unsigned allocated_query_types; 47 unsigned *query_types; 48 49 boolean failed; 50 struct pipe_query *query[NUM_QUERIES]; 51 union pipe_query_result *result[NUM_QUERIES]; 52 unsigned head, pending, results; 53 }; 54 55 void 56 hud_batch_query_update(struct hud_batch_query_context *bq, 57 struct pipe_context *pipe) 58 { 59 if (!bq || bq->failed) 60 return; 61 62 if (bq->query[bq->head]) 63 pipe->end_query(pipe, bq->query[bq->head]); 64 65 bq->results = 0; 66 67 while (bq->pending) { 68 unsigned idx = (bq->head - bq->pending + 1) % NUM_QUERIES; 69 struct pipe_query *query = bq->query[idx]; 70 71 if (!bq->result[idx]) 72 bq->result[idx] = MALLOC(sizeof(bq->result[idx]->batch[0]) * 73 bq->num_query_types); 74 if (!bq->result[idx]) { 75 fprintf(stderr, "gallium_hud: out of memory.\n"); 76 bq->failed = TRUE; 77 return; 78 } 79 80 if (!pipe->get_query_result(pipe, query, FALSE, bq->result[idx])) 81 break; 82 83 ++bq->results; 84 --bq->pending; 85 } 86 87 bq->head = (bq->head + 1) % NUM_QUERIES; 88 89 if (bq->pending == NUM_QUERIES) { 90 fprintf(stderr, 91 "gallium_hud: all queries busy after %i frames, dropping data.\n", 92 NUM_QUERIES); 93 94 assert(bq->query[bq->head]); 95 96 pipe->destroy_query(pipe, bq->query[bq->head]); 97 bq->query[bq->head] = NULL; 98 } 99 100 ++bq->pending; 101 102 if (!bq->query[bq->head]) { 103 bq->query[bq->head] = pipe->create_batch_query(pipe, 104 bq->num_query_types, 105 bq->query_types); 106 107 if (!bq->query[bq->head]) { 108 fprintf(stderr, 109 "gallium_hud: create_batch_query failed. You may have " 110 "selected too many or incompatible queries.\n"); 111 bq->failed = TRUE; 112 return; 113 } 114 } 115 } 116 117 void 118 hud_batch_query_begin(struct hud_batch_query_context *bq, 119 struct pipe_context *pipe) 120 { 121 if (!bq || bq->failed || !bq->query[bq->head]) 122 return; 123 124 if (!pipe->begin_query(pipe, bq->query[bq->head])) { 125 fprintf(stderr, 126 "gallium_hud: could not begin batch query. You may have " 127 "selected too many or incompatible queries.\n"); 128 bq->failed = TRUE; 129 } 130 } 131 132 static boolean 133 batch_query_add(struct hud_batch_query_context **pbq, 134 unsigned query_type, unsigned *result_index) 135 { 136 struct hud_batch_query_context *bq = *pbq; 137 unsigned i; 138 139 if (!bq) { 140 bq = CALLOC_STRUCT(hud_batch_query_context); 141 if (!bq) 142 return false; 143 *pbq = bq; 144 } 145 146 for (i = 0; i < bq->num_query_types; ++i) { 147 if (bq->query_types[i] == query_type) { 148 *result_index = i; 149 return true; 150 } 151 } 152 153 if (bq->num_query_types == bq->allocated_query_types) { 154 unsigned new_alloc = MAX2(16, bq->allocated_query_types * 2); 155 unsigned *new_query_types 156 = REALLOC(bq->query_types, 157 bq->allocated_query_types * sizeof(unsigned), 158 new_alloc * sizeof(unsigned)); 159 if (!new_query_types) 160 return false; 161 bq->query_types = new_query_types; 162 bq->allocated_query_types = new_alloc; 163 } 164 165 bq->query_types[bq->num_query_types] = query_type; 166 *result_index = bq->num_query_types++; 167 return true; 168 } 169 170 void 171 hud_batch_query_cleanup(struct hud_batch_query_context **pbq, 172 struct pipe_context *pipe) 173 { 174 struct hud_batch_query_context *bq = *pbq; 175 unsigned idx; 176 177 if (!bq) 178 return; 179 180 *pbq = NULL; 181 182 if (bq->query[bq->head] && !bq->failed) 183 pipe->end_query(pipe, bq->query[bq->head]); 184 185 for (idx = 0; idx < NUM_QUERIES; ++idx) { 186 if (bq->query[idx]) 187 pipe->destroy_query(pipe, bq->query[idx]); 188 FREE(bq->result[idx]); 189 } 190 191 FREE(bq->query_types); 192 FREE(bq); 193 } 194 195 struct query_info { 196 struct hud_batch_query_context *batch; 197 enum pipe_query_type query_type; 198 199 /** index to choose fields in pipe_query_data_pipeline_statistics, 200 * for example. 201 */ 202 unsigned result_index; 203 enum pipe_driver_query_result_type result_type; 204 enum pipe_driver_query_type type; 205 206 /* Ring of queries. If a query is busy, we use another slot. */ 207 struct pipe_query *query[NUM_QUERIES]; 208 unsigned head, tail; 209 210 uint64_t last_time; 211 uint64_t results_cumulative; 212 unsigned num_results; 213 }; 214 215 static void 216 query_new_value_batch(struct query_info *info) 217 { 218 struct hud_batch_query_context *bq = info->batch; 219 unsigned result_index = info->result_index; 220 unsigned idx = (bq->head - bq->pending) % NUM_QUERIES; 221 unsigned results = bq->results; 222 223 while (results) { 224 info->results_cumulative += bq->result[idx]->batch[result_index].u64; 225 ++info->num_results; 226 227 --results; 228 idx = (idx - 1) % NUM_QUERIES; 229 } 230 } 231 232 static void 233 query_new_value_normal(struct query_info *info, struct pipe_context *pipe) 234 { 235 if (info->last_time) { 236 if (info->query[info->head]) 237 pipe->end_query(pipe, info->query[info->head]); 238 239 /* read query results */ 240 while (1) { 241 struct pipe_query *query = info->query[info->tail]; 242 union pipe_query_result result; 243 uint64_t *res64 = (uint64_t *)&result; 244 245 if (query && pipe->get_query_result(pipe, query, FALSE, &result)) { 246 if (info->type == PIPE_DRIVER_QUERY_TYPE_FLOAT) { 247 assert(info->result_index == 0); 248 info->results_cumulative += (uint64_t) (result.f * 1000.0f); 249 } 250 else { 251 info->results_cumulative += res64[info->result_index]; 252 } 253 info->num_results++; 254 255 if (info->tail == info->head) 256 break; 257 258 info->tail = (info->tail+1) % NUM_QUERIES; 259 } 260 else { 261 /* the oldest query is busy */ 262 if ((info->head+1) % NUM_QUERIES == info->tail) { 263 /* all queries are busy, throw away the last query and create 264 * a new one */ 265 fprintf(stderr, 266 "gallium_hud: all queries are busy after %i frames, " 267 "can't add another query\n", 268 NUM_QUERIES); 269 if (info->query[info->head]) 270 pipe->destroy_query(pipe, info->query[info->head]); 271 info->query[info->head] = 272 pipe->create_query(pipe, info->query_type, 0); 273 } 274 else { 275 /* the last query is busy, we need to add a new one we can use 276 * for this frame */ 277 info->head = (info->head+1) % NUM_QUERIES; 278 if (!info->query[info->head]) { 279 info->query[info->head] = 280 pipe->create_query(pipe, info->query_type, 0); 281 } 282 } 283 break; 284 } 285 } 286 } 287 else { 288 /* initialize */ 289 info->query[info->head] = pipe->create_query(pipe, info->query_type, 0); 290 } 291 } 292 293 static void 294 begin_query(struct hud_graph *gr, struct pipe_context *pipe) 295 { 296 struct query_info *info = gr->query_data; 297 298 assert(!info->batch); 299 if (info->query[info->head]) 300 pipe->begin_query(pipe, info->query[info->head]); 301 } 302 303 static void 304 query_new_value(struct hud_graph *gr, struct pipe_context *pipe) 305 { 306 struct query_info *info = gr->query_data; 307 uint64_t now = os_time_get(); 308 309 if (info->batch) { 310 query_new_value_batch(info); 311 } else { 312 query_new_value_normal(info, pipe); 313 } 314 315 if (!info->last_time) { 316 info->last_time = now; 317 return; 318 } 319 320 if (info->num_results && info->last_time + gr->pane->period <= now) { 321 double value; 322 323 switch (info->result_type) { 324 default: 325 case PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE: 326 value = info->results_cumulative / info->num_results; 327 break; 328 case PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE: 329 value = info->results_cumulative; 330 break; 331 } 332 333 if (info->type == PIPE_DRIVER_QUERY_TYPE_FLOAT) { 334 value /= 1000.0; 335 } 336 337 hud_graph_add_value(gr, value); 338 339 info->last_time = now; 340 info->results_cumulative = 0; 341 info->num_results = 0; 342 } 343 } 344 345 static void 346 free_query_info(void *ptr, struct pipe_context *pipe) 347 { 348 struct query_info *info = ptr; 349 350 if (!info->batch && info->last_time) { 351 int i; 352 353 pipe->end_query(pipe, info->query[info->head]); 354 355 for (i = 0; i < ARRAY_SIZE(info->query); i++) { 356 if (info->query[i]) { 357 pipe->destroy_query(pipe, info->query[i]); 358 } 359 } 360 } 361 FREE(info); 362 } 363 364 365 /** 366 * \param result_index to select fields of pipe_query_data_pipeline_statistics, 367 * for example. 368 */ 369 void 370 hud_pipe_query_install(struct hud_batch_query_context **pbq, 371 struct hud_pane *pane, 372 const char *name, 373 enum pipe_query_type query_type, 374 unsigned result_index, 375 uint64_t max_value, enum pipe_driver_query_type type, 376 enum pipe_driver_query_result_type result_type, 377 unsigned flags) 378 { 379 struct hud_graph *gr; 380 struct query_info *info; 381 382 gr = CALLOC_STRUCT(hud_graph); 383 if (!gr) 384 return; 385 386 strncpy(gr->name, name, sizeof(gr->name)); 387 gr->name[sizeof(gr->name) - 1] = '\0'; 388 gr->query_data = CALLOC_STRUCT(query_info); 389 if (!gr->query_data) 390 goto fail_gr; 391 392 gr->query_new_value = query_new_value; 393 gr->free_query_data = free_query_info; 394 395 info = gr->query_data; 396 info->result_type = result_type; 397 info->type = type; 398 399 if (flags & PIPE_DRIVER_QUERY_FLAG_BATCH) { 400 if (!batch_query_add(pbq, query_type, &info->result_index)) 401 goto fail_info; 402 info->batch = *pbq; 403 } else { 404 gr->begin_query = begin_query; 405 info->query_type = query_type; 406 info->result_index = result_index; 407 } 408 409 hud_pane_add_graph(pane, gr); 410 pane->type = type; /* must be set before updating the max_value */ 411 412 if (pane->max_value < max_value) 413 hud_pane_set_max_value(pane, max_value); 414 return; 415 416 fail_info: 417 FREE(info); 418 fail_gr: 419 FREE(gr); 420 } 421 422 boolean 423 hud_driver_query_install(struct hud_batch_query_context **pbq, 424 struct hud_pane *pane, struct pipe_screen *screen, 425 const char *name) 426 { 427 struct pipe_driver_query_info query; 428 unsigned num_queries, i; 429 boolean found = FALSE; 430 431 if (!screen->get_driver_query_info) 432 return FALSE; 433 434 num_queries = screen->get_driver_query_info(screen, 0, NULL); 435 436 for (i = 0; i < num_queries; i++) { 437 if (screen->get_driver_query_info(screen, i, &query) && 438 strcmp(query.name, name) == 0) { 439 found = TRUE; 440 break; 441 } 442 } 443 444 if (!found) 445 return FALSE; 446 447 hud_pipe_query_install(pbq, pane, query.name, query.query_type, 0, 448 query.max_value.u64, query.type, query.result_type, 449 query.flags); 450 451 return TRUE; 452 } 453