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 "os/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 struct pipe_context *pipe; 46 unsigned num_query_types; 47 unsigned allocated_query_types; 48 unsigned *query_types; 49 50 boolean failed; 51 struct pipe_query *query[NUM_QUERIES]; 52 union pipe_query_result *result[NUM_QUERIES]; 53 unsigned head, pending, results; 54 }; 55 56 void 57 hud_batch_query_update(struct hud_batch_query_context *bq) 58 { 59 struct pipe_context *pipe; 60 61 if (!bq || bq->failed) 62 return; 63 64 pipe = bq->pipe; 65 66 if (bq->query[bq->head]) 67 pipe->end_query(pipe, bq->query[bq->head]); 68 69 bq->results = 0; 70 71 while (bq->pending) { 72 unsigned idx = (bq->head - bq->pending + 1) % NUM_QUERIES; 73 struct pipe_query *query = bq->query[idx]; 74 75 if (!bq->result[idx]) 76 bq->result[idx] = MALLOC(sizeof(bq->result[idx]->batch[0]) * 77 bq->num_query_types); 78 if (!bq->result[idx]) { 79 fprintf(stderr, "gallium_hud: out of memory.\n"); 80 bq->failed = TRUE; 81 return; 82 } 83 84 if (!pipe->get_query_result(pipe, query, FALSE, bq->result[idx])) 85 break; 86 87 ++bq->results; 88 --bq->pending; 89 } 90 91 bq->head = (bq->head + 1) % NUM_QUERIES; 92 93 if (bq->pending == NUM_QUERIES) { 94 fprintf(stderr, 95 "gallium_hud: all queries busy after %i frames, dropping data.\n", 96 NUM_QUERIES); 97 98 assert(bq->query[bq->head]); 99 100 pipe->destroy_query(bq->pipe, bq->query[bq->head]); 101 bq->query[bq->head] = NULL; 102 } 103 104 ++bq->pending; 105 106 if (!bq->query[bq->head]) { 107 bq->query[bq->head] = pipe->create_batch_query(pipe, 108 bq->num_query_types, 109 bq->query_types); 110 111 if (!bq->query[bq->head]) { 112 fprintf(stderr, 113 "gallium_hud: create_batch_query failed. You may have " 114 "selected too many or incompatible queries.\n"); 115 bq->failed = TRUE; 116 return; 117 } 118 } 119 } 120 121 void 122 hud_batch_query_begin(struct hud_batch_query_context *bq) 123 { 124 if (!bq || bq->failed || !bq->query[bq->head]) 125 return; 126 127 if (!bq->pipe->begin_query(bq->pipe, bq->query[bq->head])) { 128 fprintf(stderr, 129 "gallium_hud: could not begin batch query. You may have " 130 "selected too many or incompatible queries.\n"); 131 bq->failed = TRUE; 132 } 133 } 134 135 static boolean 136 batch_query_add(struct hud_batch_query_context **pbq, 137 struct pipe_context *pipe, unsigned query_type, 138 unsigned *result_index) 139 { 140 struct hud_batch_query_context *bq = *pbq; 141 unsigned i; 142 143 if (!bq) { 144 bq = CALLOC_STRUCT(hud_batch_query_context); 145 if (!bq) 146 return false; 147 bq->pipe = pipe; 148 *pbq = bq; 149 } 150 151 for (i = 0; i < bq->num_query_types; ++i) { 152 if (bq->query_types[i] == query_type) { 153 *result_index = i; 154 return true; 155 } 156 } 157 158 if (bq->num_query_types == bq->allocated_query_types) { 159 unsigned new_alloc = MAX2(16, bq->allocated_query_types * 2); 160 unsigned *new_query_types 161 = REALLOC(bq->query_types, 162 bq->allocated_query_types * sizeof(unsigned), 163 new_alloc * sizeof(unsigned)); 164 if (!new_query_types) 165 return false; 166 bq->query_types = new_query_types; 167 bq->allocated_query_types = new_alloc; 168 } 169 170 bq->query_types[bq->num_query_types] = query_type; 171 *result_index = bq->num_query_types++; 172 return true; 173 } 174 175 void 176 hud_batch_query_cleanup(struct hud_batch_query_context **pbq) 177 { 178 struct hud_batch_query_context *bq = *pbq; 179 unsigned idx; 180 181 if (!bq) 182 return; 183 184 *pbq = NULL; 185 186 if (bq->query[bq->head] && !bq->failed) 187 bq->pipe->end_query(bq->pipe, bq->query[bq->head]); 188 189 for (idx = 0; idx < NUM_QUERIES; ++idx) { 190 if (bq->query[idx]) 191 bq->pipe->destroy_query(bq->pipe, bq->query[idx]); 192 FREE(bq->result[idx]); 193 } 194 195 FREE(bq->query_types); 196 FREE(bq); 197 } 198 199 struct query_info { 200 struct pipe_context *pipe; 201 struct hud_batch_query_context *batch; 202 unsigned query_type; 203 unsigned result_index; /* unit depends on query_type */ 204 enum pipe_driver_query_result_type result_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) 234 { 235 struct pipe_context *pipe = info->pipe; 236 237 if (info->last_time) { 238 if (info->query[info->head]) 239 pipe->end_query(pipe, info->query[info->head]); 240 241 /* read query results */ 242 while (1) { 243 struct pipe_query *query = info->query[info->tail]; 244 union pipe_query_result result; 245 uint64_t *res64 = (uint64_t *)&result; 246 247 if (query && pipe->get_query_result(pipe, query, FALSE, &result)) { 248 info->results_cumulative += res64[info->result_index]; 249 info->num_results++; 250 251 if (info->tail == info->head) 252 break; 253 254 info->tail = (info->tail+1) % NUM_QUERIES; 255 } 256 else { 257 /* the oldest query is busy */ 258 if ((info->head+1) % NUM_QUERIES == info->tail) { 259 /* all queries are busy, throw away the last query and create 260 * a new one */ 261 fprintf(stderr, 262 "gallium_hud: all queries are busy after %i frames, " 263 "can't add another query\n", 264 NUM_QUERIES); 265 if (info->query[info->head]) 266 pipe->destroy_query(pipe, info->query[info->head]); 267 info->query[info->head] = 268 pipe->create_query(pipe, info->query_type, 0); 269 } 270 else { 271 /* the last query is busy, we need to add a new one we can use 272 * for this frame */ 273 info->head = (info->head+1) % NUM_QUERIES; 274 if (!info->query[info->head]) { 275 info->query[info->head] = 276 pipe->create_query(pipe, info->query_type, 0); 277 } 278 } 279 break; 280 } 281 } 282 } 283 else { 284 /* initialize */ 285 info->query[info->head] = pipe->create_query(pipe, info->query_type, 0); 286 } 287 } 288 289 static void 290 begin_query(struct hud_graph *gr) 291 { 292 struct query_info *info = gr->query_data; 293 struct pipe_context *pipe = info->pipe; 294 295 assert(!info->batch); 296 if (info->query[info->head]) 297 pipe->begin_query(pipe, info->query[info->head]); 298 } 299 300 static void 301 query_new_value(struct hud_graph *gr) 302 { 303 struct query_info *info = gr->query_data; 304 uint64_t now = os_time_get(); 305 306 if (info->batch) { 307 query_new_value_batch(info); 308 } else { 309 query_new_value_normal(info); 310 } 311 312 if (!info->last_time) { 313 info->last_time = now; 314 return; 315 } 316 317 if (info->num_results && info->last_time + gr->pane->period <= now) { 318 uint64_t value; 319 320 switch (info->result_type) { 321 default: 322 case PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE: 323 value = info->results_cumulative / info->num_results; 324 break; 325 case PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE: 326 value = info->results_cumulative; 327 break; 328 } 329 330 hud_graph_add_value(gr, value); 331 332 info->last_time = now; 333 info->results_cumulative = 0; 334 info->num_results = 0; 335 } 336 } 337 338 static void 339 free_query_info(void *ptr) 340 { 341 struct query_info *info = ptr; 342 343 if (!info->batch && info->last_time) { 344 struct pipe_context *pipe = info->pipe; 345 int i; 346 347 pipe->end_query(pipe, info->query[info->head]); 348 349 for (i = 0; i < ARRAY_SIZE(info->query); i++) { 350 if (info->query[i]) { 351 pipe->destroy_query(pipe, info->query[i]); 352 } 353 } 354 } 355 FREE(info); 356 } 357 358 void 359 hud_pipe_query_install(struct hud_batch_query_context **pbq, 360 struct hud_pane *pane, struct pipe_context *pipe, 361 const char *name, unsigned query_type, 362 unsigned result_index, 363 uint64_t max_value, enum pipe_driver_query_type type, 364 enum pipe_driver_query_result_type result_type, 365 unsigned flags) 366 { 367 struct hud_graph *gr; 368 struct query_info *info; 369 370 gr = CALLOC_STRUCT(hud_graph); 371 if (!gr) 372 return; 373 374 strncpy(gr->name, name, sizeof(gr->name)); 375 gr->name[sizeof(gr->name) - 1] = '\0'; 376 gr->query_data = CALLOC_STRUCT(query_info); 377 if (!gr->query_data) 378 goto fail_gr; 379 380 gr->query_new_value = query_new_value; 381 gr->free_query_data = free_query_info; 382 383 info = gr->query_data; 384 info->pipe = pipe; 385 info->result_type = result_type; 386 387 if (flags & PIPE_DRIVER_QUERY_FLAG_BATCH) { 388 if (!batch_query_add(pbq, pipe, query_type, &info->result_index)) 389 goto fail_info; 390 info->batch = *pbq; 391 } else { 392 gr->begin_query = begin_query; 393 info->query_type = query_type; 394 info->result_index = result_index; 395 } 396 397 hud_graph_set_dump_file(gr); 398 399 hud_pane_add_graph(pane, gr); 400 pane->type = type; /* must be set before updating the max_value */ 401 402 if (pane->max_value < max_value) 403 hud_pane_set_max_value(pane, max_value); 404 return; 405 406 fail_info: 407 FREE(info); 408 fail_gr: 409 FREE(gr); 410 } 411 412 boolean 413 hud_driver_query_install(struct hud_batch_query_context **pbq, 414 struct hud_pane *pane, struct pipe_context *pipe, 415 const char *name) 416 { 417 struct pipe_screen *screen = pipe->screen; 418 struct pipe_driver_query_info query; 419 unsigned num_queries, i; 420 boolean found = FALSE; 421 422 if (!screen->get_driver_query_info) 423 return FALSE; 424 425 num_queries = screen->get_driver_query_info(screen, 0, NULL); 426 427 for (i = 0; i < num_queries; i++) { 428 if (screen->get_driver_query_info(screen, i, &query) && 429 strcmp(query.name, name) == 0) { 430 found = TRUE; 431 break; 432 } 433 } 434 435 if (!found) 436 return FALSE; 437 438 hud_pipe_query_install(pbq, pane, pipe, query.name, query.query_type, 0, 439 query.max_value.u64, query.type, query.result_type, 440 query.flags); 441 442 return TRUE; 443 } 444