1/************************************************************************** 2 * 3 * Copyright 2013 Marek Olšák <maraeo@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 head-up display module can draw transparent graphs on top of what 29 * the app is rendering, visualizing various data like framerate, cpu load, 30 * performance counters, etc. It can be hook up into any state tracker. 31 * 32 * The HUD is controlled with the GALLIUM_HUD environment variable. 33 * Set GALLIUM_HUD=help for more info. 34 */ 35 36#include <inttypes.h> 37#include <signal.h> 38#include <stdio.h> 39 40#include "hud/hud_context.h" 41#include "hud/hud_private.h" 42 43#include "cso_cache/cso_context.h" 44#include "util/u_draw_quad.h" 45#include "util/u_format.h" 46#include "util/u_inlines.h" 47#include "util/u_memory.h" 48#include "util/u_math.h" 49#include "util/u_sampler.h" 50#include "util/u_simple_shaders.h" 51#include "util/u_string.h" 52#include "util/u_upload_mgr.h" 53#include "tgsi/tgsi_text.h" 54#include "tgsi/tgsi_dump.h" 55 56/* Control the visibility of all HUD contexts */ 57static boolean huds_visible = TRUE; 58 59 60#ifdef PIPE_OS_UNIX 61static void 62signal_visible_handler(int sig, siginfo_t *siginfo, void *context) 63{ 64 huds_visible = !huds_visible; 65} 66#endif 67 68static void 69hud_draw_colored_prims(struct hud_context *hud, unsigned prim, 70 float *buffer, unsigned num_vertices, 71 float r, float g, float b, float a, 72 int xoffset, int yoffset, float yscale) 73{ 74 struct cso_context *cso = hud->cso; 75 unsigned size = num_vertices * hud->color_prims.vbuf.stride; 76 77 /* If a recording context is inactive, don't draw anything. */ 78 if (size > hud->color_prims.buffer_size) 79 return; 80 81 memcpy(hud->color_prims.vertices, buffer, size); 82 83 hud->constants.color[0] = r; 84 hud->constants.color[1] = g; 85 hud->constants.color[2] = b; 86 hud->constants.color[3] = a; 87 hud->constants.translate[0] = (float) xoffset; 88 hud->constants.translate[1] = (float) yoffset; 89 hud->constants.scale[0] = 1; 90 hud->constants.scale[1] = yscale; 91 cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf); 92 93 cso_set_vertex_buffers(cso, 0, 1, &hud->color_prims.vbuf); 94 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 95 cso_draw_arrays(cso, prim, 0, num_vertices); 96 97 hud->color_prims.vertices += size / sizeof(float); 98 hud->color_prims.vbuf.buffer_offset += size; 99 hud->color_prims.buffer_size -= size; 100} 101 102static void 103hud_draw_colored_quad(struct hud_context *hud, unsigned prim, 104 unsigned x1, unsigned y1, unsigned x2, unsigned y2, 105 float r, float g, float b, float a) 106{ 107 float buffer[] = { 108 (float) x1, (float) y1, 109 (float) x1, (float) y2, 110 (float) x2, (float) y2, 111 (float) x2, (float) y1, 112 }; 113 114 hud_draw_colored_prims(hud, prim, buffer, 4, r, g, b, a, 0, 0, 1); 115} 116 117static void 118hud_draw_background_quad(struct hud_context *hud, 119 unsigned x1, unsigned y1, unsigned x2, unsigned y2) 120{ 121 float *vertices = hud->bg.vertices + hud->bg.num_vertices*2; 122 unsigned num = 0; 123 124 assert(hud->bg.num_vertices + 4 <= hud->bg.max_num_vertices); 125 126 vertices[num++] = (float) x1; 127 vertices[num++] = (float) y1; 128 129 vertices[num++] = (float) x1; 130 vertices[num++] = (float) y2; 131 132 vertices[num++] = (float) x2; 133 vertices[num++] = (float) y2; 134 135 vertices[num++] = (float) x2; 136 vertices[num++] = (float) y1; 137 138 hud->bg.num_vertices += num/2; 139} 140 141static void 142hud_draw_string(struct hud_context *hud, unsigned x, unsigned y, 143 const char *str, ...) 144{ 145 char buf[256]; 146 char *s = buf; 147 float *vertices = hud->text.vertices + hud->text.num_vertices*4; 148 unsigned num = 0; 149 150 va_list ap; 151 va_start(ap, str); 152 util_vsnprintf(buf, sizeof(buf), str, ap); 153 va_end(ap); 154 155 if (!*s) 156 return; 157 158 hud_draw_background_quad(hud, 159 x, y, 160 x + strlen(buf)*hud->font.glyph_width, 161 y + hud->font.glyph_height); 162 163 while (*s) { 164 unsigned x1 = x; 165 unsigned y1 = y; 166 unsigned x2 = x + hud->font.glyph_width; 167 unsigned y2 = y + hud->font.glyph_height; 168 unsigned tx1 = (*s % 16) * hud->font.glyph_width; 169 unsigned ty1 = (*s / 16) * hud->font.glyph_height; 170 unsigned tx2 = tx1 + hud->font.glyph_width; 171 unsigned ty2 = ty1 + hud->font.glyph_height; 172 173 if (*s == ' ') { 174 x += hud->font.glyph_width; 175 s++; 176 continue; 177 } 178 179 assert(hud->text.num_vertices + num/4 + 4 <= hud->text.max_num_vertices); 180 181 vertices[num++] = (float) x1; 182 vertices[num++] = (float) y1; 183 vertices[num++] = (float) tx1; 184 vertices[num++] = (float) ty1; 185 186 vertices[num++] = (float) x1; 187 vertices[num++] = (float) y2; 188 vertices[num++] = (float) tx1; 189 vertices[num++] = (float) ty2; 190 191 vertices[num++] = (float) x2; 192 vertices[num++] = (float) y2; 193 vertices[num++] = (float) tx2; 194 vertices[num++] = (float) ty2; 195 196 vertices[num++] = (float) x2; 197 vertices[num++] = (float) y1; 198 vertices[num++] = (float) tx2; 199 vertices[num++] = (float) ty1; 200 201 x += hud->font.glyph_width; 202 s++; 203 } 204 205 hud->text.num_vertices += num/4; 206} 207 208static void 209number_to_human_readable(double num, enum pipe_driver_query_type type, 210 char *out) 211{ 212 static const char *byte_units[] = 213 {" B", " KB", " MB", " GB", " TB", " PB", " EB"}; 214 static const char *metric_units[] = 215 {"", " k", " M", " G", " T", " P", " E"}; 216 static const char *time_units[] = 217 {" us", " ms", " s"}; /* based on microseconds */ 218 static const char *hz_units[] = 219 {" Hz", " KHz", " MHz", " GHz"}; 220 static const char *percent_units[] = {"%"}; 221 static const char *dbm_units[] = {" (-dBm)"}; 222 static const char *temperature_units[] = {" C"}; 223 static const char *volt_units[] = {" mV", " V"}; 224 static const char *amp_units[] = {" mA", " A"}; 225 static const char *watt_units[] = {" mW", " W"}; 226 static const char *float_units[] = {""}; 227 228 const char **units; 229 unsigned max_unit; 230 double divisor = (type == PIPE_DRIVER_QUERY_TYPE_BYTES) ? 1024 : 1000; 231 unsigned unit = 0; 232 double d = num; 233 234 switch (type) { 235 case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS: 236 max_unit = ARRAY_SIZE(time_units)-1; 237 units = time_units; 238 break; 239 case PIPE_DRIVER_QUERY_TYPE_VOLTS: 240 max_unit = ARRAY_SIZE(volt_units)-1; 241 units = volt_units; 242 break; 243 case PIPE_DRIVER_QUERY_TYPE_AMPS: 244 max_unit = ARRAY_SIZE(amp_units)-1; 245 units = amp_units; 246 break; 247 case PIPE_DRIVER_QUERY_TYPE_DBM: 248 max_unit = ARRAY_SIZE(dbm_units)-1; 249 units = dbm_units; 250 break; 251 case PIPE_DRIVER_QUERY_TYPE_TEMPERATURE: 252 max_unit = ARRAY_SIZE(temperature_units)-1; 253 units = temperature_units; 254 break; 255 case PIPE_DRIVER_QUERY_TYPE_FLOAT: 256 max_unit = ARRAY_SIZE(float_units)-1; 257 units = float_units; 258 break; 259 case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE: 260 max_unit = ARRAY_SIZE(percent_units)-1; 261 units = percent_units; 262 break; 263 case PIPE_DRIVER_QUERY_TYPE_BYTES: 264 max_unit = ARRAY_SIZE(byte_units)-1; 265 units = byte_units; 266 break; 267 case PIPE_DRIVER_QUERY_TYPE_HZ: 268 max_unit = ARRAY_SIZE(hz_units)-1; 269 units = hz_units; 270 break; 271 case PIPE_DRIVER_QUERY_TYPE_WATTS: 272 max_unit = ARRAY_SIZE(watt_units)-1; 273 units = watt_units; 274 break; 275 default: 276 max_unit = ARRAY_SIZE(metric_units)-1; 277 units = metric_units; 278 } 279 280 while (d > divisor && unit < max_unit) { 281 d /= divisor; 282 unit++; 283 } 284 285 /* Round to 3 decimal places so as not to print trailing zeros. */ 286 if (d*1000 != (int)(d*1000)) 287 d = round(d * 1000) / 1000; 288 289 /* Show at least 4 digits with at most 3 decimal places, but not zeros. */ 290 if (d >= 1000 || d == (int)d) 291 sprintf(out, "%.0f%s", d, units[unit]); 292 else if (d >= 100 || d*10 == (int)(d*10)) 293 sprintf(out, "%.1f%s", d, units[unit]); 294 else if (d >= 10 || d*100 == (int)(d*100)) 295 sprintf(out, "%.2f%s", d, units[unit]); 296 else 297 sprintf(out, "%.3f%s", d, units[unit]); 298} 299 300static void 301hud_draw_graph_line_strip(struct hud_context *hud, const struct hud_graph *gr, 302 unsigned xoffset, unsigned yoffset, float yscale) 303{ 304 if (gr->num_vertices <= 1) 305 return; 306 307 assert(gr->index <= gr->num_vertices); 308 309 hud_draw_colored_prims(hud, PIPE_PRIM_LINE_STRIP, 310 gr->vertices, gr->index, 311 gr->color[0], gr->color[1], gr->color[2], 1, 312 xoffset + (gr->pane->max_num_vertices - gr->index - 1) * 2 - 1, 313 yoffset, yscale); 314 315 if (gr->num_vertices <= gr->index) 316 return; 317 318 hud_draw_colored_prims(hud, PIPE_PRIM_LINE_STRIP, 319 gr->vertices + gr->index*2, 320 gr->num_vertices - gr->index, 321 gr->color[0], gr->color[1], gr->color[2], 1, 322 xoffset - gr->index*2 - 1, yoffset, yscale); 323} 324 325static void 326hud_pane_accumulate_vertices(struct hud_context *hud, 327 const struct hud_pane *pane) 328{ 329 struct hud_graph *gr; 330 float *line_verts = hud->whitelines.vertices + hud->whitelines.num_vertices*2; 331 unsigned i, num = 0; 332 char str[32]; 333 const unsigned last_line = pane->last_line; 334 335 /* draw background */ 336 hud_draw_background_quad(hud, 337 pane->x1, pane->y1, 338 pane->x2, pane->y2); 339 340 /* draw numbers on the right-hand side */ 341 for (i = 0; i <= last_line; i++) { 342 unsigned x = pane->x2 + 2; 343 unsigned y = pane->inner_y1 + 344 pane->inner_height * (last_line - i) / last_line - 345 hud->font.glyph_height / 2; 346 347 number_to_human_readable(pane->max_value * i / last_line, 348 pane->type, str); 349 hud_draw_string(hud, x, y, "%s", str); 350 } 351 352 /* draw info below the pane */ 353 i = 0; 354 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 355 unsigned x = pane->x1 + 2; 356 unsigned y = pane->y2 + 2 + i*hud->font.glyph_height; 357 358 number_to_human_readable(gr->current_value, pane->type, str); 359 hud_draw_string(hud, x, y, " %s: %s", gr->name, str); 360 i++; 361 } 362 363 /* draw border */ 364 assert(hud->whitelines.num_vertices + num/2 + 8 <= hud->whitelines.max_num_vertices); 365 line_verts[num++] = (float) pane->x1; 366 line_verts[num++] = (float) pane->y1; 367 line_verts[num++] = (float) pane->x2; 368 line_verts[num++] = (float) pane->y1; 369 370 line_verts[num++] = (float) pane->x2; 371 line_verts[num++] = (float) pane->y1; 372 line_verts[num++] = (float) pane->x2; 373 line_verts[num++] = (float) pane->y2; 374 375 line_verts[num++] = (float) pane->x1; 376 line_verts[num++] = (float) pane->y2; 377 line_verts[num++] = (float) pane->x2; 378 line_verts[num++] = (float) pane->y2; 379 380 line_verts[num++] = (float) pane->x1; 381 line_verts[num++] = (float) pane->y1; 382 line_verts[num++] = (float) pane->x1; 383 line_verts[num++] = (float) pane->y2; 384 385 /* draw horizontal lines inside the graph */ 386 for (i = 0; i <= last_line; i++) { 387 float y = round((pane->max_value * i / (double)last_line) * 388 pane->yscale + pane->inner_y2); 389 390 assert(hud->whitelines.num_vertices + num/2 + 2 <= hud->whitelines.max_num_vertices); 391 line_verts[num++] = pane->x1; 392 line_verts[num++] = y; 393 line_verts[num++] = pane->x2; 394 line_verts[num++] = y; 395 } 396 397 hud->whitelines.num_vertices += num/2; 398} 399 400static void 401hud_pane_accumulate_vertices_simple(struct hud_context *hud, 402 const struct hud_pane *pane) 403{ 404 struct hud_graph *gr; 405 unsigned i; 406 char str[32]; 407 408 /* draw info below the pane */ 409 i = 0; 410 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 411 unsigned x = pane->x1; 412 unsigned y = pane->y_simple + i*hud->font.glyph_height; 413 414 number_to_human_readable(gr->current_value, pane->type, str); 415 hud_draw_string(hud, x, y, "%s: %s", gr->name, str); 416 i++; 417 } 418} 419 420static void 421hud_pane_draw_colored_objects(struct hud_context *hud, 422 const struct hud_pane *pane) 423{ 424 struct hud_graph *gr; 425 unsigned i; 426 427 /* draw colored quads below the pane */ 428 i = 0; 429 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 430 unsigned x = pane->x1 + 2; 431 unsigned y = pane->y2 + 2 + i*hud->font.glyph_height; 432 433 hud_draw_colored_quad(hud, PIPE_PRIM_QUADS, x + 1, y + 1, x + 12, y + 13, 434 gr->color[0], gr->color[1], gr->color[2], 1); 435 i++; 436 } 437 438 /* draw the line strips */ 439 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 440 hud_draw_graph_line_strip(hud, gr, pane->inner_x1, pane->inner_y2, pane->yscale); 441 } 442} 443 444static void 445hud_prepare_vertices(struct hud_context *hud, struct vertex_queue *v, 446 unsigned num_vertices, unsigned stride) 447{ 448 v->num_vertices = 0; 449 v->max_num_vertices = num_vertices; 450 v->vbuf.stride = stride; 451 v->buffer_size = stride * num_vertices; 452} 453 454/** 455 * Draw the HUD to the texture \p tex. 456 * The texture is usually the back buffer being displayed. 457 */ 458static void 459hud_draw_results(struct hud_context *hud, struct pipe_resource *tex) 460{ 461 struct cso_context *cso = hud->cso; 462 struct pipe_context *pipe = hud->pipe; 463 struct pipe_framebuffer_state fb; 464 struct pipe_surface surf_templ, *surf; 465 struct pipe_viewport_state viewport; 466 const struct pipe_sampler_state *sampler_states[] = 467 { &hud->font_sampler_state }; 468 struct hud_pane *pane; 469 470 if (!huds_visible) 471 return; 472 473 hud->fb_width = tex->width0; 474 hud->fb_height = tex->height0; 475 hud->constants.two_div_fb_width = 2.0f / hud->fb_width; 476 hud->constants.two_div_fb_height = 2.0f / hud->fb_height; 477 478 cso_save_state(cso, (CSO_BIT_FRAMEBUFFER | 479 CSO_BIT_SAMPLE_MASK | 480 CSO_BIT_MIN_SAMPLES | 481 CSO_BIT_BLEND | 482 CSO_BIT_DEPTH_STENCIL_ALPHA | 483 CSO_BIT_FRAGMENT_SHADER | 484 CSO_BIT_FRAGMENT_SAMPLER_VIEWS | 485 CSO_BIT_FRAGMENT_SAMPLERS | 486 CSO_BIT_RASTERIZER | 487 CSO_BIT_VIEWPORT | 488 CSO_BIT_STREAM_OUTPUTS | 489 CSO_BIT_GEOMETRY_SHADER | 490 CSO_BIT_TESSCTRL_SHADER | 491 CSO_BIT_TESSEVAL_SHADER | 492 CSO_BIT_VERTEX_SHADER | 493 CSO_BIT_VERTEX_ELEMENTS | 494 CSO_BIT_AUX_VERTEX_BUFFER_SLOT | 495 CSO_BIT_PAUSE_QUERIES | 496 CSO_BIT_RENDER_CONDITION)); 497 cso_save_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX); 498 499 /* set states */ 500 memset(&surf_templ, 0, sizeof(surf_templ)); 501 surf_templ.format = tex->format; 502 503 /* Without this, AA lines look thinner if they are between 2 pixels 504 * because the alpha is 0.5 on both pixels. (it's ugly) 505 * 506 * sRGB makes the width of all AA lines look the same. 507 */ 508 if (hud->has_srgb) { 509 enum pipe_format srgb_format = util_format_srgb(tex->format); 510 511 if (srgb_format != PIPE_FORMAT_NONE) 512 surf_templ.format = srgb_format; 513 } 514 surf = pipe->create_surface(pipe, tex, &surf_templ); 515 516 memset(&fb, 0, sizeof(fb)); 517 fb.nr_cbufs = 1; 518 fb.cbufs[0] = surf; 519 fb.zsbuf = NULL; 520 fb.width = hud->fb_width; 521 fb.height = hud->fb_height; 522 523 viewport.scale[0] = 0.5f * hud->fb_width; 524 viewport.scale[1] = 0.5f * hud->fb_height; 525 viewport.scale[2] = 1.0f; 526 viewport.translate[0] = 0.5f * hud->fb_width; 527 viewport.translate[1] = 0.5f * hud->fb_height; 528 viewport.translate[2] = 0.0f; 529 530 cso_set_framebuffer(cso, &fb); 531 cso_set_sample_mask(cso, ~0); 532 cso_set_min_samples(cso, 1); 533 cso_set_depth_stencil_alpha(cso, &hud->dsa); 534 cso_set_rasterizer(cso, &hud->rasterizer); 535 cso_set_viewport(cso, &viewport); 536 cso_set_stream_outputs(cso, 0, NULL, NULL); 537 cso_set_tessctrl_shader_handle(cso, NULL); 538 cso_set_tesseval_shader_handle(cso, NULL); 539 cso_set_geometry_shader_handle(cso, NULL); 540 cso_set_vertex_shader_handle(cso, hud->vs); 541 cso_set_vertex_elements(cso, 2, hud->velems); 542 cso_set_render_condition(cso, NULL, FALSE, 0); 543 cso_set_sampler_views(cso, PIPE_SHADER_FRAGMENT, 1, 544 &hud->font_sampler_view); 545 cso_set_samplers(cso, PIPE_SHADER_FRAGMENT, 1, sampler_states); 546 cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf); 547 548 /* draw accumulated vertices for background quads */ 549 cso_set_blend(cso, &hud->alpha_blend); 550 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 551 552 if (hud->bg.num_vertices) { 553 hud->constants.color[0] = 0; 554 hud->constants.color[1] = 0; 555 hud->constants.color[2] = 0; 556 hud->constants.color[3] = 0.666f; 557 hud->constants.translate[0] = 0; 558 hud->constants.translate[1] = 0; 559 hud->constants.scale[0] = 1; 560 hud->constants.scale[1] = 1; 561 562 cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf); 563 cso_set_vertex_buffers(cso, 0, 1, &hud->bg.vbuf); 564 cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->bg.num_vertices); 565 } 566 pipe_resource_reference(&hud->bg.vbuf.buffer.resource, NULL); 567 568 /* draw accumulated vertices for text */ 569 if (hud->text.num_vertices) { 570 cso_set_vertex_buffers(cso, 0, 1, &hud->text.vbuf); 571 cso_set_fragment_shader_handle(hud->cso, hud->fs_text); 572 cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->text.num_vertices); 573 } 574 pipe_resource_reference(&hud->text.vbuf.buffer.resource, NULL); 575 576 if (hud->simple) { 577 cso_restore_state(cso); 578 cso_restore_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX); 579 580 pipe_surface_reference(&surf, NULL); 581 return; 582 } 583 584 /* draw accumulated vertices for white lines */ 585 cso_set_blend(cso, &hud->no_blend); 586 587 hud->constants.color[0] = 1; 588 hud->constants.color[1] = 1; 589 hud->constants.color[2] = 1; 590 hud->constants.color[3] = 1; 591 hud->constants.translate[0] = 0; 592 hud->constants.translate[1] = 0; 593 hud->constants.scale[0] = 1; 594 hud->constants.scale[1] = 1; 595 cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf); 596 597 if (hud->whitelines.num_vertices) { 598 cso_set_vertex_buffers(cso, 0, 1, &hud->whitelines.vbuf); 599 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 600 cso_draw_arrays(cso, PIPE_PRIM_LINES, 0, hud->whitelines.num_vertices); 601 } 602 pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, NULL); 603 604 /* draw the rest */ 605 cso_set_blend(cso, &hud->alpha_blend); 606 cso_set_rasterizer(cso, &hud->rasterizer_aa_lines); 607 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 608 if (pane) 609 hud_pane_draw_colored_objects(hud, pane); 610 } 611 612 cso_restore_state(cso); 613 cso_restore_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX); 614 615 pipe_surface_reference(&surf, NULL); 616} 617 618static void 619hud_start_queries(struct hud_context *hud, struct pipe_context *pipe) 620{ 621 struct hud_pane *pane; 622 struct hud_graph *gr; 623 624 /* Start queries. */ 625 hud_batch_query_begin(hud->batch_query, pipe); 626 627 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 628 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 629 if (gr->begin_query) 630 gr->begin_query(gr, pipe); 631 } 632 } 633} 634 635/* Stop queries, query results, and record vertices for charts. */ 636static void 637hud_stop_queries(struct hud_context *hud, struct pipe_context *pipe) 638{ 639 struct hud_pane *pane; 640 struct hud_graph *gr, *next; 641 642 /* prepare vertex buffers */ 643 hud_prepare_vertices(hud, &hud->bg, 16 * 256, 2 * sizeof(float)); 644 hud_prepare_vertices(hud, &hud->whitelines, 4 * 256, 2 * sizeof(float)); 645 hud_prepare_vertices(hud, &hud->text, 16 * 1024, 4 * sizeof(float)); 646 hud_prepare_vertices(hud, &hud->color_prims, 32 * 1024, 2 * sizeof(float)); 647 648 /* Allocate everything once and divide the storage into 3 portions 649 * manually, because u_upload_alloc can unmap memory from previous calls. 650 */ 651 u_upload_alloc(pipe->stream_uploader, 0, 652 hud->bg.buffer_size + 653 hud->whitelines.buffer_size + 654 hud->text.buffer_size + 655 hud->color_prims.buffer_size, 656 16, &hud->bg.vbuf.buffer_offset, &hud->bg.vbuf.buffer.resource, 657 (void**)&hud->bg.vertices); 658 if (!hud->bg.vertices) 659 return; 660 661 pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource); 662 pipe_resource_reference(&hud->text.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource); 663 pipe_resource_reference(&hud->color_prims.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource); 664 665 hud->whitelines.vbuf.buffer_offset = hud->bg.vbuf.buffer_offset + 666 hud->bg.buffer_size; 667 hud->whitelines.vertices = hud->bg.vertices + 668 hud->bg.buffer_size / sizeof(float); 669 670 hud->text.vbuf.buffer_offset = hud->whitelines.vbuf.buffer_offset + 671 hud->whitelines.buffer_size; 672 hud->text.vertices = hud->whitelines.vertices + 673 hud->whitelines.buffer_size / sizeof(float); 674 675 hud->color_prims.vbuf.buffer_offset = hud->text.vbuf.buffer_offset + 676 hud->text.buffer_size; 677 hud->color_prims.vertices = hud->text.vertices + 678 hud->text.buffer_size / sizeof(float); 679 680 /* prepare all graphs */ 681 hud_batch_query_update(hud->batch_query, pipe); 682 683 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 684 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 685 gr->query_new_value(gr, pipe); 686 } 687 688 if (pane->sort_items) { 689 LIST_FOR_EACH_ENTRY_SAFE(gr, next, &pane->graph_list, head) { 690 /* ignore the last one */ 691 if (&gr->head == pane->graph_list.prev) 692 continue; 693 694 /* This is an incremental bubble sort, because we only do one pass 695 * per frame. It will eventually reach an equilibrium. 696 */ 697 if (gr->current_value < 698 LIST_ENTRY(struct hud_graph, next, head)->current_value) { 699 LIST_DEL(&gr->head); 700 LIST_ADD(&gr->head, &next->head); 701 } 702 } 703 } 704 705 if (hud->simple) 706 hud_pane_accumulate_vertices_simple(hud, pane); 707 else 708 hud_pane_accumulate_vertices(hud, pane); 709 } 710 711 /* unmap the uploader's vertex buffer before drawing */ 712 u_upload_unmap(pipe->stream_uploader); 713} 714 715/** 716 * Record queries and draw the HUD. The "cso" parameter acts as a filter. 717 * If "cso" is not the recording context, recording is skipped. 718 * If "cso" is not the drawing context, drawing is skipped. 719 * cso == NULL ignores the filter. 720 */ 721void 722hud_run(struct hud_context *hud, struct cso_context *cso, 723 struct pipe_resource *tex) 724{ 725 struct pipe_context *pipe = cso ? cso_get_pipe_context(cso) : NULL; 726 727 /* If "cso" is the recording or drawing context or NULL, execute 728 * the operation. Otherwise, don't do anything. 729 */ 730 if (hud->record_pipe && (!pipe || pipe == hud->record_pipe)) 731 hud_stop_queries(hud, hud->record_pipe); 732 733 if (hud->cso && (!cso || cso == hud->cso)) 734 hud_draw_results(hud, tex); 735 736 if (hud->record_pipe && (!pipe || pipe == hud->record_pipe)) 737 hud_start_queries(hud, hud->record_pipe); 738} 739 740/** 741 * Record query results and assemble vertices if "pipe" is a recording but 742 * not drawing context. 743 */ 744void 745hud_record_only(struct hud_context *hud, struct pipe_context *pipe) 746{ 747 assert(pipe); 748 749 /* If it's a drawing context, only hud_run() records query results. */ 750 if (pipe == hud->pipe || pipe != hud->record_pipe) 751 return; 752 753 hud_stop_queries(hud, hud->record_pipe); 754 hud_start_queries(hud, hud->record_pipe); 755} 756 757static void 758fixup_bytes(enum pipe_driver_query_type type, int position, uint64_t *exp10) 759{ 760 if (type == PIPE_DRIVER_QUERY_TYPE_BYTES && position % 3 == 0) 761 *exp10 = (*exp10 / 1000) * 1024; 762} 763 764/** 765 * Set the maximum value for the Y axis of the graph. 766 * This scales the graph accordingly. 767 */ 768void 769hud_pane_set_max_value(struct hud_pane *pane, uint64_t value) 770{ 771 double leftmost_digit; 772 uint64_t exp10; 773 int i; 774 775 /* The following code determines the max_value in the graph as well as 776 * how many describing lines are drawn. The max_value is rounded up, 777 * so that all drawn numbers are rounded for readability. 778 * We want to print multiples of a simple number instead of multiples of 779 * hard-to-read numbers like 1.753. 780 */ 781 782 /* Find the left-most digit. Make sure exp10 * 10 and fixup_bytes doesn't 783 * overflow. (11 is safe) */ 784 exp10 = 1; 785 for (i = 0; exp10 <= UINT64_MAX / 11 && exp10 * 9 < value; i++) { 786 exp10 *= 10; 787 fixup_bytes(pane->type, i + 1, &exp10); 788 } 789 790 leftmost_digit = DIV_ROUND_UP(value, exp10); 791 792 /* Round 9 to 10. */ 793 if (leftmost_digit == 9) { 794 leftmost_digit = 1; 795 exp10 *= 10; 796 fixup_bytes(pane->type, i + 1, &exp10); 797 } 798 799 switch ((unsigned)leftmost_digit) { 800 case 1: 801 pane->last_line = 5; /* lines in +1/5 increments */ 802 break; 803 case 2: 804 pane->last_line = 8; /* lines in +1/4 increments. */ 805 break; 806 case 3: 807 case 4: 808 pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments */ 809 break; 810 case 5: 811 case 6: 812 case 7: 813 case 8: 814 pane->last_line = leftmost_digit; /* lines in +1 increments */ 815 break; 816 default: 817 assert(0); 818 } 819 820 /* Truncate {3,4} to {2.5, 3.5} if possible. */ 821 for (i = 3; i <= 4; i++) { 822 if (leftmost_digit == i && value <= (i - 0.5) * exp10) { 823 leftmost_digit = i - 0.5; 824 pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments. */ 825 } 826 } 827 828 /* Truncate 2 to a multiple of 0.2 in (1, 1.6] if possible. */ 829 if (leftmost_digit == 2) { 830 for (i = 1; i <= 3; i++) { 831 if (value <= (1 + i*0.2) * exp10) { 832 leftmost_digit = 1 + i*0.2; 833 pane->last_line = 5 + i; /* lines in +1/5 increments. */ 834 break; 835 } 836 } 837 } 838 839 pane->max_value = leftmost_digit * exp10; 840 pane->yscale = -(int)pane->inner_height / (float)pane->max_value; 841} 842 843static void 844hud_pane_update_dyn_ceiling(struct hud_graph *gr, struct hud_pane *pane) 845{ 846 unsigned i; 847 float tmp = 0.0f; 848 849 if (pane->dyn_ceil_last_ran != gr->index) { 850 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 851 for (i = 0; i < gr->num_vertices; ++i) { 852 tmp = gr->vertices[i * 2 + 1] > tmp ? 853 gr->vertices[i * 2 + 1] : tmp; 854 } 855 } 856 857 /* Avoid setting it lower than the initial starting height. */ 858 tmp = tmp > pane->initial_max_value ? tmp : pane->initial_max_value; 859 hud_pane_set_max_value(pane, tmp); 860 } 861 862 /* 863 * Mark this adjustment run so we could avoid repeating a full update 864 * again needlessly in case the pane has more than one graph. 865 */ 866 pane->dyn_ceil_last_ran = gr->index; 867} 868 869static struct hud_pane * 870hud_pane_create(struct hud_context *hud, 871 unsigned x1, unsigned y1, unsigned x2, unsigned y2, 872 unsigned y_simple, 873 unsigned period, uint64_t max_value, uint64_t ceiling, 874 boolean dyn_ceiling, boolean sort_items) 875{ 876 struct hud_pane *pane = CALLOC_STRUCT(hud_pane); 877 878 if (!pane) 879 return NULL; 880 881 pane->hud = hud; 882 pane->x1 = x1; 883 pane->y1 = y1; 884 pane->x2 = x2; 885 pane->y2 = y2; 886 pane->y_simple = y_simple; 887 pane->inner_x1 = x1 + 1; 888 pane->inner_x2 = x2 - 1; 889 pane->inner_y1 = y1 + 1; 890 pane->inner_y2 = y2 - 1; 891 pane->inner_width = pane->inner_x2 - pane->inner_x1; 892 pane->inner_height = pane->inner_y2 - pane->inner_y1; 893 pane->period = period; 894 pane->max_num_vertices = (x2 - x1 + 2) / 2; 895 pane->ceiling = ceiling; 896 pane->dyn_ceiling = dyn_ceiling; 897 pane->dyn_ceil_last_ran = 0; 898 pane->sort_items = sort_items; 899 pane->initial_max_value = max_value; 900 hud_pane_set_max_value(pane, max_value); 901 LIST_INITHEAD(&pane->graph_list); 902 return pane; 903} 904 905/* replace '-' with a space */ 906static void 907strip_hyphens(char *s) 908{ 909 while (*s) { 910 if (*s == '-') 911 *s = ' '; 912 s++; 913 } 914} 915 916/** 917 * Add a graph to an existing pane. 918 * One pane can contain multiple graphs over each other. 919 */ 920void 921hud_pane_add_graph(struct hud_pane *pane, struct hud_graph *gr) 922{ 923 static const float colors[][3] = { 924 {0, 1, 0}, 925 {1, 0, 0}, 926 {0, 1, 1}, 927 {1, 0, 1}, 928 {1, 1, 0}, 929 {0.5, 1, 0.5}, 930 {1, 0.5, 0.5}, 931 {0.5, 1, 1}, 932 {1, 0.5, 1}, 933 {1, 1, 0.5}, 934 {0, 0.5, 0}, 935 {0.5, 0, 0}, 936 {0, 0.5, 0.5}, 937 {0.5, 0, 0.5}, 938 {0.5, 0.5, 0}, 939 }; 940 unsigned color = pane->next_color % ARRAY_SIZE(colors); 941 942 strip_hyphens(gr->name); 943 944 gr->vertices = MALLOC(pane->max_num_vertices * sizeof(float) * 2); 945 gr->color[0] = colors[color][0]; 946 gr->color[1] = colors[color][1]; 947 gr->color[2] = colors[color][2]; 948 gr->pane = pane; 949 LIST_ADDTAIL(&gr->head, &pane->graph_list); 950 pane->num_graphs++; 951 pane->next_color++; 952} 953 954void 955hud_graph_add_value(struct hud_graph *gr, double value) 956{ 957 gr->current_value = value; 958 value = value > gr->pane->ceiling ? gr->pane->ceiling : value; 959 960 if (gr->fd) { 961 if (fabs(value - lround(value)) > FLT_EPSILON) { 962 fprintf(gr->fd, "%f\n", value); 963 } 964 else { 965 fprintf(gr->fd, "%" PRIu64 "\n", (uint64_t) lround(value)); 966 } 967 } 968 969 if (gr->index == gr->pane->max_num_vertices) { 970 gr->vertices[0] = 0; 971 gr->vertices[1] = gr->vertices[(gr->index-1)*2+1]; 972 gr->index = 1; 973 } 974 gr->vertices[(gr->index)*2+0] = (float) (gr->index * 2); 975 gr->vertices[(gr->index)*2+1] = (float) value; 976 gr->index++; 977 978 if (gr->num_vertices < gr->pane->max_num_vertices) { 979 gr->num_vertices++; 980 } 981 982 if (gr->pane->dyn_ceiling == true) { 983 hud_pane_update_dyn_ceiling(gr, gr->pane); 984 } 985 if (value > gr->pane->max_value) { 986 hud_pane_set_max_value(gr->pane, value); 987 } 988} 989 990static void 991hud_graph_destroy(struct hud_graph *graph, struct pipe_context *pipe) 992{ 993 FREE(graph->vertices); 994 if (graph->free_query_data) 995 graph->free_query_data(graph->query_data, pipe); 996 if (graph->fd) 997 fclose(graph->fd); 998 FREE(graph); 999} 1000 1001static void strcat_without_spaces(char *dst, const char *src) 1002{ 1003 dst += strlen(dst); 1004 while (*src) { 1005 if (*src == ' ') 1006 *dst++ = '_'; 1007 else 1008 *dst++ = *src; 1009 src++; 1010 } 1011 *dst = 0; 1012} 1013 1014 1015#ifdef PIPE_OS_WINDOWS 1016#define W_OK 0 1017static int 1018access(const char *pathname, int mode) 1019{ 1020 /* no-op */ 1021 return 0; 1022} 1023 1024#define PATH_SEP "\\" 1025 1026#else 1027 1028#define PATH_SEP "/" 1029 1030#endif 1031 1032 1033/** 1034 * If the GALLIUM_HUD_DUMP_DIR env var is set, we'll write the raw 1035 * HUD values to files at ${GALLIUM_HUD_DUMP_DIR}/<stat> where <stat> 1036 * is a HUD variable such as "fps", or "cpu" 1037 */ 1038static void 1039hud_graph_set_dump_file(struct hud_graph *gr) 1040{ 1041 const char *hud_dump_dir = getenv("GALLIUM_HUD_DUMP_DIR"); 1042 1043 if (hud_dump_dir && access(hud_dump_dir, W_OK) == 0) { 1044 char *dump_file = malloc(strlen(hud_dump_dir) + sizeof(PATH_SEP) 1045 + sizeof(gr->name)); 1046 if (dump_file) { 1047 strcpy(dump_file, hud_dump_dir); 1048 strcat(dump_file, PATH_SEP); 1049 strcat_without_spaces(dump_file, gr->name); 1050 gr->fd = fopen(dump_file, "w+"); 1051 if (gr->fd) { 1052 /* flush output after each line is written */ 1053 setvbuf(gr->fd, NULL, _IOLBF, 0); 1054 } 1055 free(dump_file); 1056 } 1057 } 1058} 1059 1060/** 1061 * Read a string from the environment variable. 1062 * The separators "+", ",", ":", and ";" terminate the string. 1063 * Return the number of read characters. 1064 */ 1065static int 1066parse_string(const char *s, char *out) 1067{ 1068 int i; 1069 1070 for (i = 0; *s && *s != '+' && *s != ',' && *s != ':' && *s != ';' && *s != '='; 1071 s++, out++, i++) 1072 *out = *s; 1073 1074 *out = 0; 1075 1076 if (*s && !i) { 1077 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) while " 1078 "parsing a string\n", *s, *s); 1079 fflush(stderr); 1080 } 1081 1082 return i; 1083} 1084 1085static char * 1086read_pane_settings(char *str, unsigned * const x, unsigned * const y, 1087 unsigned * const width, unsigned * const height, 1088 uint64_t * const ceiling, boolean * const dyn_ceiling, 1089 boolean *reset_colors, boolean *sort_items) 1090{ 1091 char *ret = str; 1092 unsigned tmp; 1093 1094 while (*str == '.') { 1095 ++str; 1096 switch (*str) { 1097 case 'x': 1098 ++str; 1099 *x = strtoul(str, &ret, 10); 1100 str = ret; 1101 break; 1102 1103 case 'y': 1104 ++str; 1105 *y = strtoul(str, &ret, 10); 1106 str = ret; 1107 break; 1108 1109 case 'w': 1110 ++str; 1111 tmp = strtoul(str, &ret, 10); 1112 *width = tmp > 80 ? tmp : 80; /* 80 is chosen arbitrarily */ 1113 str = ret; 1114 break; 1115 1116 /* 1117 * Prevent setting height to less than 50. If the height is set to less, 1118 * the text of the Y axis labels on the graph will start overlapping. 1119 */ 1120 case 'h': 1121 ++str; 1122 tmp = strtoul(str, &ret, 10); 1123 *height = tmp > 50 ? tmp : 50; 1124 str = ret; 1125 break; 1126 1127 case 'c': 1128 ++str; 1129 tmp = strtoul(str, &ret, 10); 1130 *ceiling = tmp > 10 ? tmp : 10; 1131 str = ret; 1132 break; 1133 1134 case 'd': 1135 ++str; 1136 ret = str; 1137 *dyn_ceiling = true; 1138 break; 1139 1140 case 'r': 1141 ++str; 1142 ret = str; 1143 *reset_colors = true; 1144 break; 1145 1146 case 's': 1147 ++str; 1148 ret = str; 1149 *sort_items = true; 1150 break; 1151 1152 default: 1153 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *str); 1154 fflush(stderr); 1155 } 1156 1157 } 1158 1159 return ret; 1160} 1161 1162static boolean 1163has_occlusion_query(struct pipe_screen *screen) 1164{ 1165 return screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY) != 0; 1166} 1167 1168static boolean 1169has_streamout(struct pipe_screen *screen) 1170{ 1171 return screen->get_param(screen, PIPE_CAP_MAX_STREAM_OUTPUT_BUFFERS) != 0; 1172} 1173 1174static boolean 1175has_pipeline_stats_query(struct pipe_screen *screen) 1176{ 1177 return screen->get_param(screen, PIPE_CAP_QUERY_PIPELINE_STATISTICS) != 0; 1178} 1179 1180static void 1181hud_parse_env_var(struct hud_context *hud, struct pipe_screen *screen, 1182 const char *env) 1183{ 1184 unsigned num, i; 1185 char name_a[256], s[256]; 1186 char *name; 1187 struct hud_pane *pane = NULL; 1188 unsigned x = 10, y = 10, y_simple = 10; 1189 unsigned width = 251, height = 100; 1190 unsigned period = 500 * 1000; /* default period (1/2 second) */ 1191 uint64_t ceiling = UINT64_MAX; 1192 unsigned column_width = 251; 1193 boolean dyn_ceiling = false; 1194 boolean reset_colors = false; 1195 boolean sort_items = false; 1196 const char *period_env; 1197 1198 if (util_strncmp(env, "simple,", 7) == 0) { 1199 hud->simple = true; 1200 env += 7; 1201 } 1202 1203 /* 1204 * The GALLIUM_HUD_PERIOD env var sets the graph update rate. 1205 * The env var is in seconds (a float). 1206 * Zero means update after every frame. 1207 */ 1208 period_env = getenv("GALLIUM_HUD_PERIOD"); 1209 if (period_env) { 1210 float p = (float) atof(period_env); 1211 if (p >= 0.0f) { 1212 period = (unsigned) (p * 1000 * 1000); 1213 } 1214 } 1215 1216 while ((num = parse_string(env, name_a)) != 0) { 1217 bool added = true; 1218 1219 env += num; 1220 1221 /* check for explicit location, size and etc. settings */ 1222 name = read_pane_settings(name_a, &x, &y, &width, &height, &ceiling, 1223 &dyn_ceiling, &reset_colors, &sort_items); 1224 1225 /* 1226 * Keep track of overall column width to avoid pane overlapping in case 1227 * later we create a new column while the bottom pane in the current 1228 * column is less wide than the rest of the panes in it. 1229 */ 1230 column_width = width > column_width ? width : column_width; 1231 1232 if (!pane) { 1233 pane = hud_pane_create(hud, x, y, x + width, y + height, y_simple, 1234 period, 10, ceiling, dyn_ceiling, sort_items); 1235 if (!pane) 1236 return; 1237 } 1238 1239 if (reset_colors) { 1240 pane->next_color = 0; 1241 reset_colors = false; 1242 } 1243 1244 /* Add a graph. */ 1245#if defined(HAVE_GALLIUM_EXTRA_HUD) || defined(HAVE_LIBSENSORS) 1246 char arg_name[64]; 1247#endif 1248 /* IF YOU CHANGE THIS, UPDATE print_help! */ 1249 if (strcmp(name, "fps") == 0) { 1250 hud_fps_graph_install(pane); 1251 } 1252 else if (strcmp(name, "frametime") == 0) { 1253 hud_frametime_graph_install(pane); 1254 } 1255 else if (strcmp(name, "cpu") == 0) { 1256 hud_cpu_graph_install(pane, ALL_CPUS); 1257 } 1258 else if (sscanf(name, "cpu%u%s", &i, s) == 1) { 1259 hud_cpu_graph_install(pane, i); 1260 } 1261 else if (strcmp(name, "API-thread-busy") == 0) { 1262 hud_thread_busy_install(pane, name, false); 1263 } 1264 else if (strcmp(name, "API-thread-offloaded-slots") == 0) { 1265 hud_thread_counter_install(pane, name, HUD_COUNTER_OFFLOADED); 1266 } 1267 else if (strcmp(name, "API-thread-direct-slots") == 0) { 1268 hud_thread_counter_install(pane, name, HUD_COUNTER_DIRECT); 1269 } 1270 else if (strcmp(name, "API-thread-num-syncs") == 0) { 1271 hud_thread_counter_install(pane, name, HUD_COUNTER_SYNCS); 1272 } 1273 else if (strcmp(name, "main-thread-busy") == 0) { 1274 hud_thread_busy_install(pane, name, true); 1275 } 1276#ifdef HAVE_GALLIUM_EXTRA_HUD 1277 else if (sscanf(name, "nic-rx-%s", arg_name) == 1) { 1278 hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_RX); 1279 } 1280 else if (sscanf(name, "nic-tx-%s", arg_name) == 1) { 1281 hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_TX); 1282 } 1283 else if (sscanf(name, "nic-rssi-%s", arg_name) == 1) { 1284 hud_nic_graph_install(pane, arg_name, NIC_RSSI_DBM); 1285 pane->type = PIPE_DRIVER_QUERY_TYPE_DBM; 1286 } 1287 else if (sscanf(name, "diskstat-rd-%s", arg_name) == 1) { 1288 hud_diskstat_graph_install(pane, arg_name, DISKSTAT_RD); 1289 pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES; 1290 } 1291 else if (sscanf(name, "diskstat-wr-%s", arg_name) == 1) { 1292 hud_diskstat_graph_install(pane, arg_name, DISKSTAT_WR); 1293 pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES; 1294 } 1295 else if (sscanf(name, "cpufreq-min-cpu%u", &i) == 1) { 1296 hud_cpufreq_graph_install(pane, i, CPUFREQ_MINIMUM); 1297 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1298 } 1299 else if (sscanf(name, "cpufreq-cur-cpu%u", &i) == 1) { 1300 hud_cpufreq_graph_install(pane, i, CPUFREQ_CURRENT); 1301 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1302 } 1303 else if (sscanf(name, "cpufreq-max-cpu%u", &i) == 1) { 1304 hud_cpufreq_graph_install(pane, i, CPUFREQ_MAXIMUM); 1305 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1306 } 1307#endif 1308#ifdef HAVE_LIBSENSORS 1309 else if (sscanf(name, "sensors_temp_cu-%s", arg_name) == 1) { 1310 hud_sensors_temp_graph_install(pane, arg_name, 1311 SENSORS_TEMP_CURRENT); 1312 pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE; 1313 } 1314 else if (sscanf(name, "sensors_temp_cr-%s", arg_name) == 1) { 1315 hud_sensors_temp_graph_install(pane, arg_name, 1316 SENSORS_TEMP_CRITICAL); 1317 pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE; 1318 } 1319 else if (sscanf(name, "sensors_volt_cu-%s", arg_name) == 1) { 1320 hud_sensors_temp_graph_install(pane, arg_name, 1321 SENSORS_VOLTAGE_CURRENT); 1322 pane->type = PIPE_DRIVER_QUERY_TYPE_VOLTS; 1323 } 1324 else if (sscanf(name, "sensors_curr_cu-%s", arg_name) == 1) { 1325 hud_sensors_temp_graph_install(pane, arg_name, 1326 SENSORS_CURRENT_CURRENT); 1327 pane->type = PIPE_DRIVER_QUERY_TYPE_AMPS; 1328 } 1329 else if (sscanf(name, "sensors_pow_cu-%s", arg_name) == 1) { 1330 hud_sensors_temp_graph_install(pane, arg_name, 1331 SENSORS_POWER_CURRENT); 1332 pane->type = PIPE_DRIVER_QUERY_TYPE_WATTS; 1333 } 1334#endif 1335 else if (strcmp(name, "samples-passed") == 0 && 1336 has_occlusion_query(screen)) { 1337 hud_pipe_query_install(&hud->batch_query, pane, 1338 "samples-passed", 1339 PIPE_QUERY_OCCLUSION_COUNTER, 0, 0, 1340 PIPE_DRIVER_QUERY_TYPE_UINT64, 1341 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1342 0); 1343 } 1344 else if (strcmp(name, "primitives-generated") == 0 && 1345 has_streamout(screen)) { 1346 hud_pipe_query_install(&hud->batch_query, pane, 1347 "primitives-generated", 1348 PIPE_QUERY_PRIMITIVES_GENERATED, 0, 0, 1349 PIPE_DRIVER_QUERY_TYPE_UINT64, 1350 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1351 0); 1352 } 1353 else { 1354 boolean processed = FALSE; 1355 1356 /* pipeline statistics queries */ 1357 if (has_pipeline_stats_query(screen)) { 1358 static const char *pipeline_statistics_names[] = 1359 { 1360 "ia-vertices", 1361 "ia-primitives", 1362 "vs-invocations", 1363 "gs-invocations", 1364 "gs-primitives", 1365 "clipper-invocations", 1366 "clipper-primitives-generated", 1367 "ps-invocations", 1368 "hs-invocations", 1369 "ds-invocations", 1370 "cs-invocations" 1371 }; 1372 for (i = 0; i < ARRAY_SIZE(pipeline_statistics_names); ++i) 1373 if (strcmp(name, pipeline_statistics_names[i]) == 0) 1374 break; 1375 if (i < ARRAY_SIZE(pipeline_statistics_names)) { 1376 hud_pipe_query_install(&hud->batch_query, pane, name, 1377 PIPE_QUERY_PIPELINE_STATISTICS, i, 1378 0, PIPE_DRIVER_QUERY_TYPE_UINT64, 1379 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1380 0); 1381 processed = TRUE; 1382 } 1383 } 1384 1385 /* driver queries */ 1386 if (!processed) { 1387 if (!hud_driver_query_install(&hud->batch_query, pane, 1388 screen, name)) { 1389 fprintf(stderr, "gallium_hud: unknown driver query '%s'\n", name); 1390 fflush(stderr); 1391 added = false; 1392 } 1393 } 1394 } 1395 1396 if (*env == ':') { 1397 env++; 1398 1399 if (!pane) { 1400 fprintf(stderr, "gallium_hud: syntax error: unexpected ':', " 1401 "expected a name\n"); 1402 fflush(stderr); 1403 break; 1404 } 1405 1406 num = parse_string(env, s); 1407 env += num; 1408 1409 if (num && sscanf(s, "%u", &i) == 1) { 1410 hud_pane_set_max_value(pane, i); 1411 pane->initial_max_value = i; 1412 } 1413 else { 1414 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) " 1415 "after ':'\n", *env, *env); 1416 fflush(stderr); 1417 } 1418 } 1419 1420 if (*env == '=') { 1421 env++; 1422 1423 if (!pane) { 1424 fprintf(stderr, "gallium_hud: syntax error: unexpected '=', " 1425 "expected a name\n"); 1426 fflush(stderr); 1427 break; 1428 } 1429 1430 num = parse_string(env, s); 1431 env += num; 1432 1433 strip_hyphens(s); 1434 if (added && !LIST_IS_EMPTY(&pane->graph_list)) { 1435 struct hud_graph *graph; 1436 graph = LIST_ENTRY(struct hud_graph, pane->graph_list.prev, head); 1437 strncpy(graph->name, s, sizeof(graph->name)-1); 1438 graph->name[sizeof(graph->name)-1] = 0; 1439 } 1440 } 1441 1442 if (*env == 0) 1443 break; 1444 1445 /* parse a separator */ 1446 switch (*env) { 1447 case '+': 1448 env++; 1449 break; 1450 1451 case ',': 1452 env++; 1453 if (!pane) 1454 break; 1455 1456 y += height + hud->font.glyph_height * (pane->num_graphs + 2); 1457 y_simple += hud->font.glyph_height * (pane->num_graphs + 1); 1458 height = 100; 1459 1460 if (pane && pane->num_graphs) { 1461 LIST_ADDTAIL(&pane->head, &hud->pane_list); 1462 pane = NULL; 1463 } 1464 break; 1465 1466 case ';': 1467 env++; 1468 y = 10; 1469 y_simple = 10; 1470 x += column_width + hud->font.glyph_width * 9; 1471 height = 100; 1472 1473 if (pane && pane->num_graphs) { 1474 LIST_ADDTAIL(&pane->head, &hud->pane_list); 1475 pane = NULL; 1476 } 1477 1478 /* Starting a new column; reset column width. */ 1479 column_width = 251; 1480 break; 1481 1482 default: 1483 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *env); 1484 fflush(stderr); 1485 } 1486 1487 /* Reset to defaults for the next pane in case these were modified. */ 1488 width = 251; 1489 ceiling = UINT64_MAX; 1490 dyn_ceiling = false; 1491 sort_items = false; 1492 1493 } 1494 1495 if (pane) { 1496 if (pane->num_graphs) { 1497 LIST_ADDTAIL(&pane->head, &hud->pane_list); 1498 } 1499 else { 1500 FREE(pane); 1501 } 1502 } 1503 1504 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 1505 struct hud_graph *gr; 1506 1507 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 1508 hud_graph_set_dump_file(gr); 1509 } 1510 } 1511} 1512 1513static void 1514print_help(struct pipe_screen *screen) 1515{ 1516 int i, num_queries, num_cpus = hud_get_num_cpus(); 1517 1518 puts("Syntax: GALLIUM_HUD=name1[+name2][...][:value1][,nameI...][;nameJ...]"); 1519 puts(""); 1520 puts(" Names are identifiers of data sources which will be drawn as graphs"); 1521 puts(" in panes. Multiple graphs can be drawn in the same pane."); 1522 puts(" There can be multiple panes placed in rows and columns."); 1523 puts(""); 1524 puts(" '+' separates names which will share a pane."); 1525 puts(" ':[value]' specifies the initial maximum value of the Y axis"); 1526 puts(" for the given pane."); 1527 puts(" ',' creates a new pane below the last one."); 1528 puts(" ';' creates a new pane at the top of the next column."); 1529 puts(" '=' followed by a string, changes the name of the last data source"); 1530 puts(" to that string"); 1531 puts(""); 1532 puts(" Example: GALLIUM_HUD=\"cpu,fps;primitives-generated\""); 1533 puts(""); 1534 puts(" Additionally, by prepending '.[identifier][value]' modifiers to"); 1535 puts(" a name, it is possible to explicitly set the location and size"); 1536 puts(" of a pane, along with limiting overall maximum value of the"); 1537 puts(" Y axis and activating dynamic readjustment of the Y axis."); 1538 puts(" Several modifiers may be applied to the same pane simultaneously."); 1539 puts(""); 1540 puts(" 'x[value]' sets the location of the pane on the x axis relative"); 1541 puts(" to the upper-left corner of the viewport, in pixels."); 1542 puts(" 'y[value]' sets the location of the pane on the y axis relative"); 1543 puts(" to the upper-left corner of the viewport, in pixels."); 1544 puts(" 'w[value]' sets width of the graph pixels."); 1545 puts(" 'h[value]' sets height of the graph in pixels."); 1546 puts(" 'c[value]' sets the ceiling of the value of the Y axis."); 1547 puts(" If the graph needs to draw values higher than"); 1548 puts(" the ceiling allows, the value is clamped."); 1549 puts(" 'd' activates dynamic Y axis readjustment to set the value of"); 1550 puts(" the Y axis to match the highest value still visible in the graph."); 1551 puts(" 'r' resets the color counter (the next color will be green)"); 1552 puts(" 's' sort items below graphs in descending order"); 1553 puts(""); 1554 puts(" If 'c' and 'd' modifiers are used simultaneously, both are in effect:"); 1555 puts(" the Y axis does not go above the restriction imposed by 'c' while"); 1556 puts(" still adjusting the value of the Y axis down when appropriate."); 1557 puts(""); 1558 puts(" You can change behavior of the whole HUD by adding these options at"); 1559 puts(" the beginning of the environment variable:"); 1560 puts(" 'simple,' disables all the fancy stuff and only draws text."); 1561 puts(""); 1562 puts(" Example: GALLIUM_HUD=\".w256.h64.x1600.y520.d.c1000fps+cpu,.datom-count\""); 1563 puts(""); 1564 puts(" Available names:"); 1565 puts(" fps"); 1566 puts(" frametime"); 1567 puts(" cpu"); 1568 1569 for (i = 0; i < num_cpus; i++) 1570 printf(" cpu%i\n", i); 1571 1572 if (has_occlusion_query(screen)) 1573 puts(" samples-passed"); 1574 if (has_streamout(screen)) 1575 puts(" primitives-generated"); 1576 1577 if (has_pipeline_stats_query(screen)) { 1578 puts(" ia-vertices"); 1579 puts(" ia-primitives"); 1580 puts(" vs-invocations"); 1581 puts(" gs-invocations"); 1582 puts(" gs-primitives"); 1583 puts(" clipper-invocations"); 1584 puts(" clipper-primitives-generated"); 1585 puts(" ps-invocations"); 1586 puts(" hs-invocations"); 1587 puts(" ds-invocations"); 1588 puts(" cs-invocations"); 1589 } 1590 1591#ifdef HAVE_GALLIUM_EXTRA_HUD 1592 hud_get_num_disks(1); 1593 hud_get_num_nics(1); 1594 hud_get_num_cpufreq(1); 1595#endif 1596#ifdef HAVE_LIBSENSORS 1597 hud_get_num_sensors(1); 1598#endif 1599 1600 if (screen->get_driver_query_info){ 1601 boolean skipping = false; 1602 struct pipe_driver_query_info info; 1603 num_queries = screen->get_driver_query_info(screen, 0, NULL); 1604 1605 for (i = 0; i < num_queries; i++){ 1606 screen->get_driver_query_info(screen, i, &info); 1607 if (info.flags & PIPE_DRIVER_QUERY_FLAG_DONT_LIST) { 1608 if (!skipping) 1609 puts(" ..."); 1610 skipping = true; 1611 } else { 1612 printf(" %s\n", info.name); 1613 skipping = false; 1614 } 1615 } 1616 } 1617 1618 puts(""); 1619 fflush(stdout); 1620} 1621 1622static void 1623hud_unset_draw_context(struct hud_context *hud) 1624{ 1625 struct pipe_context *pipe = hud->pipe; 1626 1627 if (!pipe) 1628 return; 1629 1630 pipe_sampler_view_reference(&hud->font_sampler_view, NULL); 1631 1632 if (hud->fs_color) { 1633 pipe->delete_fs_state(pipe, hud->fs_color); 1634 hud->fs_color = NULL; 1635 } 1636 if (hud->fs_text) { 1637 pipe->delete_fs_state(pipe, hud->fs_text); 1638 hud->fs_text = NULL; 1639 } 1640 if (hud->vs) { 1641 pipe->delete_vs_state(pipe, hud->vs); 1642 hud->vs = NULL; 1643 } 1644 1645 hud->cso = NULL; 1646 hud->pipe = NULL; 1647} 1648 1649static bool 1650hud_set_draw_context(struct hud_context *hud, struct cso_context *cso) 1651{ 1652 struct pipe_context *pipe = cso_get_pipe_context(cso); 1653 1654 assert(!hud->pipe); 1655 hud->pipe = pipe; 1656 hud->cso = cso; 1657 1658 struct pipe_sampler_view view_templ; 1659 u_sampler_view_default_template( 1660 &view_templ, hud->font.texture, hud->font.texture->format); 1661 hud->font_sampler_view = pipe->create_sampler_view(pipe, hud->font.texture, 1662 &view_templ); 1663 if (!hud->font_sampler_view) 1664 goto fail; 1665 1666 /* fragment shader */ 1667 hud->fs_color = 1668 util_make_fragment_passthrough_shader(pipe, 1669 TGSI_SEMANTIC_COLOR, 1670 TGSI_INTERPOLATE_CONSTANT, 1671 TRUE); 1672 1673 { 1674 /* Read a texture and do .xxxx swizzling. */ 1675 static const char *fragment_shader_text = { 1676 "FRAG\n" 1677 "DCL IN[0], GENERIC[0], LINEAR\n" 1678 "DCL SAMP[0]\n" 1679 "DCL SVIEW[0], RECT, FLOAT\n" 1680 "DCL OUT[0], COLOR[0]\n" 1681 "DCL TEMP[0]\n" 1682 1683 "TEX TEMP[0], IN[0], SAMP[0], RECT\n" 1684 "MOV OUT[0], TEMP[0].xxxx\n" 1685 "END\n" 1686 }; 1687 1688 struct tgsi_token tokens[1000]; 1689 struct pipe_shader_state state; 1690 1691 if (!tgsi_text_translate(fragment_shader_text, tokens, ARRAY_SIZE(tokens))) { 1692 assert(0); 1693 goto fail; 1694 } 1695 pipe_shader_state_from_tgsi(&state, tokens); 1696 hud->fs_text = pipe->create_fs_state(pipe, &state); 1697 } 1698 1699 /* vertex shader */ 1700 { 1701 static const char *vertex_shader_text = { 1702 "VERT\n" 1703 "DCL IN[0..1]\n" 1704 "DCL OUT[0], POSITION\n" 1705 "DCL OUT[1], COLOR[0]\n" /* color */ 1706 "DCL OUT[2], GENERIC[0]\n" /* texcoord */ 1707 /* [0] = color, 1708 * [1] = (2/fb_width, 2/fb_height, xoffset, yoffset) 1709 * [2] = (xscale, yscale, 0, 0) */ 1710 "DCL CONST[0][0..2]\n" 1711 "DCL TEMP[0]\n" 1712 "IMM[0] FLT32 { -1, 0, 0, 1 }\n" 1713 1714 /* v = in * (xscale, yscale) + (xoffset, yoffset) */ 1715 "MAD TEMP[0].xy, IN[0], CONST[0][2].xyyy, CONST[0][1].zwww\n" 1716 /* pos = v * (2 / fb_width, 2 / fb_height) - (1, 1) */ 1717 "MAD OUT[0].xy, TEMP[0], CONST[0][1].xyyy, IMM[0].xxxx\n" 1718 "MOV OUT[0].zw, IMM[0]\n" 1719 1720 "MOV OUT[1], CONST[0][0]\n" 1721 "MOV OUT[2], IN[1]\n" 1722 "END\n" 1723 }; 1724 1725 struct tgsi_token tokens[1000]; 1726 struct pipe_shader_state state; 1727 if (!tgsi_text_translate(vertex_shader_text, tokens, ARRAY_SIZE(tokens))) { 1728 assert(0); 1729 goto fail; 1730 } 1731 pipe_shader_state_from_tgsi(&state, tokens); 1732 hud->vs = pipe->create_vs_state(pipe, &state); 1733 } 1734 1735 return true; 1736 1737fail: 1738 hud_unset_draw_context(hud); 1739 fprintf(stderr, "hud: failed to set a draw context"); 1740 return false; 1741} 1742 1743static void 1744hud_unset_record_context(struct hud_context *hud) 1745{ 1746 struct pipe_context *pipe = hud->record_pipe; 1747 struct hud_pane *pane, *pane_tmp; 1748 struct hud_graph *graph, *graph_tmp; 1749 1750 if (!pipe) 1751 return; 1752 1753 LIST_FOR_EACH_ENTRY_SAFE(pane, pane_tmp, &hud->pane_list, head) { 1754 LIST_FOR_EACH_ENTRY_SAFE(graph, graph_tmp, &pane->graph_list, head) { 1755 LIST_DEL(&graph->head); 1756 hud_graph_destroy(graph, pipe); 1757 } 1758 LIST_DEL(&pane->head); 1759 FREE(pane); 1760 } 1761 1762 hud_batch_query_cleanup(&hud->batch_query, pipe); 1763 hud->record_pipe = NULL; 1764} 1765 1766static void 1767hud_set_record_context(struct hud_context *hud, struct pipe_context *pipe) 1768{ 1769 hud->record_pipe = pipe; 1770} 1771 1772/** 1773 * Create the HUD. 1774 * 1775 * If "share" is non-NULL and GALLIUM_HUD_SHARE=x,y is set, increment the 1776 * reference counter of "share", set "cso" as the recording or drawing context 1777 * according to the environment variable, and return "share". 1778 * This allows sharing the HUD instance within a multi-context share group, 1779 * record queries in one context and draw them in another. 1780 */ 1781struct hud_context * 1782hud_create(struct cso_context *cso, struct hud_context *share) 1783{ 1784 const char *share_env = debug_get_option("GALLIUM_HUD_SHARE", NULL); 1785 unsigned record_ctx = 0, draw_ctx = 0; 1786 1787 if (share_env && sscanf(share_env, "%u,%u", &record_ctx, &draw_ctx) != 2) 1788 share_env = NULL; 1789 1790 if (share && share_env) { 1791 /* All contexts in a share group share the HUD instance. 1792 * Only one context can record queries and only one context 1793 * can draw the HUD. 1794 * 1795 * GALLIUM_HUD_SHARE=x,y determines the context indices. 1796 */ 1797 int context_id = p_atomic_inc_return(&share->refcount) - 1; 1798 1799 if (context_id == record_ctx) { 1800 assert(!share->record_pipe); 1801 hud_set_record_context(share, cso_get_pipe_context(cso)); 1802 } 1803 1804 if (context_id == draw_ctx) { 1805 assert(!share->pipe); 1806 hud_set_draw_context(share, cso); 1807 } 1808 1809 return share; 1810 } 1811 1812 struct pipe_screen *screen = cso_get_pipe_context(cso)->screen; 1813 struct hud_context *hud; 1814 unsigned i; 1815 const char *env = debug_get_option("GALLIUM_HUD", NULL); 1816#ifdef PIPE_OS_UNIX 1817 unsigned signo = debug_get_num_option("GALLIUM_HUD_TOGGLE_SIGNAL", 0); 1818 static boolean sig_handled = FALSE; 1819 struct sigaction action = {}; 1820#endif 1821 huds_visible = debug_get_bool_option("GALLIUM_HUD_VISIBLE", TRUE); 1822 1823 if (!env || !*env) 1824 return NULL; 1825 1826 if (strcmp(env, "help") == 0) { 1827 print_help(screen); 1828 return NULL; 1829 } 1830 1831 hud = CALLOC_STRUCT(hud_context); 1832 if (!hud) 1833 return NULL; 1834 1835 /* font (the context is only used for the texture upload) */ 1836 if (!util_font_create(cso_get_pipe_context(cso), 1837 UTIL_FONT_FIXED_8X13, &hud->font)) { 1838 FREE(hud); 1839 return NULL; 1840 } 1841 1842 hud->refcount = 1; 1843 hud->has_srgb = screen->is_format_supported(screen, 1844 PIPE_FORMAT_B8G8R8A8_SRGB, 1845 PIPE_TEXTURE_2D, 0, 0, 1846 PIPE_BIND_RENDER_TARGET) != 0; 1847 1848 /* blend state */ 1849 hud->no_blend.rt[0].colormask = PIPE_MASK_RGBA; 1850 1851 hud->alpha_blend.rt[0].colormask = PIPE_MASK_RGBA; 1852 hud->alpha_blend.rt[0].blend_enable = 1; 1853 hud->alpha_blend.rt[0].rgb_func = PIPE_BLEND_ADD; 1854 hud->alpha_blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA; 1855 hud->alpha_blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; 1856 hud->alpha_blend.rt[0].alpha_func = PIPE_BLEND_ADD; 1857 hud->alpha_blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO; 1858 hud->alpha_blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE; 1859 1860 /* rasterizer */ 1861 hud->rasterizer.half_pixel_center = 1; 1862 hud->rasterizer.bottom_edge_rule = 1; 1863 hud->rasterizer.depth_clip_near = 1; 1864 hud->rasterizer.depth_clip_far = 1; 1865 hud->rasterizer.line_width = 1; 1866 hud->rasterizer.line_last_pixel = 1; 1867 1868 hud->rasterizer_aa_lines = hud->rasterizer; 1869 hud->rasterizer_aa_lines.line_smooth = 1; 1870 1871 /* vertex elements */ 1872 for (i = 0; i < 2; i++) { 1873 hud->velems[i].src_offset = i * 2 * sizeof(float); 1874 hud->velems[i].src_format = PIPE_FORMAT_R32G32_FLOAT; 1875 hud->velems[i].vertex_buffer_index = 0; 1876 } 1877 1878 /* sampler state (for font drawing) */ 1879 hud->font_sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1880 hud->font_sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1881 hud->font_sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1882 hud->font_sampler_state.normalized_coords = 0; 1883 1884 /* constants */ 1885 hud->constbuf.buffer_size = sizeof(hud->constants); 1886 hud->constbuf.user_buffer = &hud->constants; 1887 1888 LIST_INITHEAD(&hud->pane_list); 1889 1890 /* setup sig handler once for all hud contexts */ 1891#ifdef PIPE_OS_UNIX 1892 if (!sig_handled && signo != 0) { 1893 action.sa_sigaction = &signal_visible_handler; 1894 action.sa_flags = SA_SIGINFO; 1895 1896 if (signo >= NSIG) 1897 fprintf(stderr, "gallium_hud: invalid signal %u\n", signo); 1898 else if (sigaction(signo, &action, NULL) < 0) 1899 fprintf(stderr, "gallium_hud: unable to set handler for signal %u\n", signo); 1900 fflush(stderr); 1901 1902 sig_handled = TRUE; 1903 } 1904#endif 1905 1906 if (record_ctx == 0) 1907 hud_set_record_context(hud, cso_get_pipe_context(cso)); 1908 if (draw_ctx == 0) 1909 hud_set_draw_context(hud, cso); 1910 1911 hud_parse_env_var(hud, screen, env); 1912 return hud; 1913} 1914 1915/** 1916 * Destroy a HUD. If the HUD has several users, decrease the reference counter 1917 * and detach the context from the HUD. 1918 */ 1919void 1920hud_destroy(struct hud_context *hud, struct cso_context *cso) 1921{ 1922 if (!cso || hud->record_pipe == cso_get_pipe_context(cso)) 1923 hud_unset_record_context(hud); 1924 1925 if (!cso || hud->cso == cso) 1926 hud_unset_draw_context(hud); 1927 1928 if (p_atomic_dec_zero(&hud->refcount)) { 1929 pipe_resource_reference(&hud->font.texture, NULL); 1930 FREE(hud); 1931 } 1932} 1933 1934void 1935hud_add_queue_for_monitoring(struct hud_context *hud, 1936 struct util_queue_monitoring *queue_info) 1937{ 1938 assert(!hud->monitored_queue); 1939 hud->monitored_queue = queue_info; 1940} 1941