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 gallium frontend. 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 "frontend/api.h" 44#include "cso_cache/cso_context.h" 45#include "util/u_draw_quad.h" 46#include "util/format/u_format.h" 47#include "util/u_inlines.h" 48#include "util/u_memory.h" 49#include "util/u_math.h" 50#include "util/u_sampler.h" 51#include "util/u_simple_shaders.h" 52#include "util/u_string.h" 53#include "util/u_upload_mgr.h" 54#include "tgsi/tgsi_text.h" 55#include "tgsi/tgsi_dump.h" 56 57/* Control the visibility of all HUD contexts */ 58static boolean huds_visible = TRUE; 59static int hud_scale = 1; 60 61 62#ifdef PIPE_OS_UNIX 63static void 64signal_visible_handler(int sig, siginfo_t *siginfo, void *context) 65{ 66 huds_visible = !huds_visible; 67} 68#endif 69 70static void 71hud_draw_colored_prims(struct hud_context *hud, unsigned prim, 72 float *buffer, unsigned num_vertices, 73 float r, float g, float b, float a, 74 int xoffset, int yoffset, float yscale) 75{ 76 struct cso_context *cso = hud->cso; 77 struct pipe_context *pipe = hud->pipe; 78 struct pipe_vertex_buffer vbuffer = {0}; 79 80 hud->constants.color[0] = r; 81 hud->constants.color[1] = g; 82 hud->constants.color[2] = b; 83 hud->constants.color[3] = a; 84 hud->constants.translate[0] = (float) (xoffset * hud_scale); 85 hud->constants.translate[1] = (float) (yoffset * hud_scale); 86 hud->constants.scale[0] = hud_scale; 87 hud->constants.scale[1] = yscale * hud_scale; 88 pipe->set_constant_buffer(pipe, PIPE_SHADER_VERTEX, 0, false, &hud->constbuf); 89 90 u_upload_data(hud->pipe->stream_uploader, 0, 91 num_vertices * 2 * sizeof(float), 16, buffer, 92 &vbuffer.buffer_offset, &vbuffer.buffer.resource); 93 u_upload_unmap(hud->pipe->stream_uploader); 94 vbuffer.stride = 2 * sizeof(float); 95 96 cso_set_vertex_buffers(cso, 0, 1, 0, false, &vbuffer); 97 pipe_resource_reference(&vbuffer.buffer.resource, NULL); 98 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 99 cso_draw_arrays(cso, prim, 0, num_vertices); 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 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_SAMPLERS | 485 CSO_BIT_RASTERIZER | 486 CSO_BIT_VIEWPORT | 487 CSO_BIT_STREAM_OUTPUTS | 488 CSO_BIT_GEOMETRY_SHADER | 489 CSO_BIT_TESSCTRL_SHADER | 490 CSO_BIT_TESSEVAL_SHADER | 491 CSO_BIT_VERTEX_SHADER | 492 CSO_BIT_VERTEX_ELEMENTS | 493 CSO_BIT_PAUSE_QUERIES | 494 CSO_BIT_RENDER_CONDITION)); 495 496 /* set states */ 497 memset(&surf_templ, 0, sizeof(surf_templ)); 498 surf_templ.format = tex->format; 499 500 /* Without this, AA lines look thinner if they are between 2 pixels 501 * because the alpha is 0.5 on both pixels. (it's ugly) 502 * 503 * sRGB makes the width of all AA lines look the same. 504 */ 505 if (hud->has_srgb) { 506 enum pipe_format srgb_format = util_format_srgb(tex->format); 507 508 if (srgb_format != PIPE_FORMAT_NONE) 509 surf_templ.format = srgb_format; 510 } 511 surf = pipe->create_surface(pipe, tex, &surf_templ); 512 513 memset(&fb, 0, sizeof(fb)); 514 fb.nr_cbufs = 1; 515 fb.cbufs[0] = surf; 516 fb.zsbuf = NULL; 517 fb.width = hud->fb_width; 518 fb.height = hud->fb_height; 519 520 viewport.scale[0] = 0.5f * hud->fb_width; 521 viewport.scale[1] = 0.5f * hud->fb_height; 522 viewport.scale[2] = 0.0f; 523 viewport.translate[0] = 0.5f * hud->fb_width; 524 viewport.translate[1] = 0.5f * hud->fb_height; 525 viewport.translate[2] = 0.0f; 526 viewport.swizzle_x = PIPE_VIEWPORT_SWIZZLE_POSITIVE_X; 527 viewport.swizzle_y = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Y; 528 viewport.swizzle_z = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Z; 529 viewport.swizzle_w = PIPE_VIEWPORT_SWIZZLE_POSITIVE_W; 530 531 cso_set_framebuffer(cso, &fb); 532 cso_set_sample_mask(cso, ~0); 533 cso_set_min_samples(cso, 1); 534 cso_set_depth_stencil_alpha(cso, &hud->dsa); 535 cso_set_rasterizer(cso, &hud->rasterizer); 536 cso_set_viewport(cso, &viewport); 537 cso_set_stream_outputs(cso, 0, NULL, NULL); 538 cso_set_tessctrl_shader_handle(cso, NULL); 539 cso_set_tesseval_shader_handle(cso, NULL); 540 cso_set_geometry_shader_handle(cso, NULL); 541 cso_set_vertex_shader_handle(cso, hud->vs_color); 542 cso_set_vertex_elements(cso, &hud->velems); 543 cso_set_render_condition(cso, NULL, FALSE, 0); 544 pipe->set_sampler_views(pipe, PIPE_SHADER_FRAGMENT, 0, 1, 0, false, 545 &hud->font_sampler_view); 546 cso_set_samplers(cso, PIPE_SHADER_FRAGMENT, 1, sampler_states); 547 pipe->set_constant_buffer(pipe, PIPE_SHADER_VERTEX, 0, false, &hud->constbuf); 548 549 /* draw accumulated vertices for background quads */ 550 cso_set_blend(cso, &hud->alpha_blend); 551 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 552 553 if (hud->bg.num_vertices) { 554 hud->constants.color[0] = 0; 555 hud->constants.color[1] = 0; 556 hud->constants.color[2] = 0; 557 hud->constants.color[3] = 0.666f; 558 hud->constants.translate[0] = 0; 559 hud->constants.translate[1] = 0; 560 hud->constants.scale[0] = hud_scale; 561 hud->constants.scale[1] = hud_scale; 562 563 pipe->set_constant_buffer(pipe, PIPE_SHADER_VERTEX, 0, false, &hud->constbuf); 564 565 cso_set_vertex_buffers(cso, 0, 1, 0, false, &hud->bg.vbuf); 566 cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->bg.num_vertices); 567 } 568 pipe_resource_reference(&hud->bg.vbuf.buffer.resource, NULL); 569 570 /* draw accumulated vertices for text */ 571 if (hud->text.num_vertices) { 572 cso_set_vertex_shader_handle(cso, hud->vs_text); 573 cso_set_vertex_buffers(cso, 0, 1, 0, false, &hud->text.vbuf); 574 cso_set_fragment_shader_handle(hud->cso, hud->fs_text); 575 cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->text.num_vertices); 576 } 577 pipe_resource_reference(&hud->text.vbuf.buffer.resource, NULL); 578 579 if (hud->simple) 580 goto done; 581 582 /* draw accumulated vertices for white lines */ 583 cso_set_blend(cso, &hud->no_blend); 584 585 hud->constants.color[0] = 1; 586 hud->constants.color[1] = 1; 587 hud->constants.color[2] = 1; 588 hud->constants.color[3] = 1; 589 hud->constants.translate[0] = 0; 590 hud->constants.translate[1] = 0; 591 hud->constants.scale[0] = hud_scale; 592 hud->constants.scale[1] = hud_scale; 593 pipe->set_constant_buffer(pipe, PIPE_SHADER_VERTEX, 0, false, &hud->constbuf); 594 595 if (hud->whitelines.num_vertices) { 596 cso_set_vertex_shader_handle(cso, hud->vs_color); 597 cso_set_vertex_buffers(cso, 0, 1, 0, false, &hud->whitelines.vbuf); 598 cso_set_fragment_shader_handle(hud->cso, hud->fs_color); 599 cso_draw_arrays(cso, PIPE_PRIM_LINES, 0, hud->whitelines.num_vertices); 600 } 601 pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, NULL); 602 603 /* draw the rest */ 604 cso_set_blend(cso, &hud->alpha_blend); 605 cso_set_rasterizer(cso, &hud->rasterizer_aa_lines); 606 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 607 if (pane) 608 hud_pane_draw_colored_objects(hud, pane); 609 } 610 611done: 612 cso_restore_state(cso, CSO_UNBIND_FS_SAMPLERVIEW0 | CSO_UNBIND_VS_CONSTANTS | CSO_UNBIND_VERTEX_BUFFER0); 613 614 /* restore states not restored by cso */ 615 if (hud->st) { 616 hud->st->invalidate_state(hud->st, 617 ST_INVALIDATE_FS_SAMPLER_VIEWS | 618 ST_INVALIDATE_VS_CONSTBUF0 | 619 ST_INVALIDATE_VERTEX_BUFFERS); 620 } 621 622 pipe_surface_reference(&surf, NULL); 623} 624 625static void 626hud_start_queries(struct hud_context *hud, struct pipe_context *pipe) 627{ 628 struct hud_pane *pane; 629 struct hud_graph *gr; 630 631 /* Start queries. */ 632 hud_batch_query_begin(hud->batch_query, pipe); 633 634 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 635 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 636 if (gr->begin_query) 637 gr->begin_query(gr, pipe); 638 } 639 } 640} 641 642/* Stop queries, query results, and record vertices for charts. */ 643static void 644hud_stop_queries(struct hud_context *hud, struct pipe_context *pipe) 645{ 646 struct hud_pane *pane; 647 struct hud_graph *gr, *next; 648 649 /* prepare vertex buffers */ 650 hud_prepare_vertices(hud, &hud->bg, 16 * 256, 2 * sizeof(float)); 651 hud_prepare_vertices(hud, &hud->whitelines, 4 * 256, 2 * sizeof(float)); 652 hud_prepare_vertices(hud, &hud->text, 16 * 1024, 4 * sizeof(float)); 653 654 /* Allocate everything once and divide the storage into 3 portions 655 * manually, because u_upload_alloc can unmap memory from previous calls. 656 */ 657 u_upload_alloc(pipe->stream_uploader, 0, 658 hud->bg.buffer_size + 659 hud->whitelines.buffer_size + 660 hud->text.buffer_size, 661 16, &hud->bg.vbuf.buffer_offset, &hud->bg.vbuf.buffer.resource, 662 (void**)&hud->bg.vertices); 663 if (!hud->bg.vertices) 664 return; 665 666 pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource); 667 pipe_resource_reference(&hud->text.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource); 668 669 hud->whitelines.vbuf.buffer_offset = hud->bg.vbuf.buffer_offset + 670 hud->bg.buffer_size; 671 hud->whitelines.vertices = hud->bg.vertices + 672 hud->bg.buffer_size / sizeof(float); 673 674 hud->text.vbuf.buffer_offset = hud->whitelines.vbuf.buffer_offset + 675 hud->whitelines.buffer_size; 676 hud->text.vertices = hud->whitelines.vertices + 677 hud->whitelines.buffer_size / sizeof(float); 678 679 /* prepare all graphs */ 680 hud_batch_query_update(hud->batch_query, pipe); 681 682 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 683 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 684 gr->query_new_value(gr, pipe); 685 } 686 687 if (pane->sort_items) { 688 LIST_FOR_EACH_ENTRY_SAFE(gr, next, &pane->graph_list, head) { 689 /* ignore the last one */ 690 if (&gr->head == pane->graph_list.prev) 691 continue; 692 693 /* This is an incremental bubble sort, because we only do one pass 694 * per frame. It will eventually reach an equilibrium. 695 */ 696 if (gr->current_value < 697 list_entry(next, struct hud_graph, head)->current_value) { 698 list_del(&gr->head); 699 list_add(&gr->head, &next->head); 700 } 701 } 702 } 703 704 if (hud->simple) 705 hud_pane_accumulate_vertices_simple(hud, pane); 706 else 707 hud_pane_accumulate_vertices(hud, pane); 708 } 709 710 /* unmap the uploader's vertex buffer before drawing */ 711 u_upload_unmap(pipe->stream_uploader); 712} 713 714/** 715 * Record queries and draw the HUD. The "cso" parameter acts as a filter. 716 * If "cso" is not the recording context, recording is skipped. 717 * If "cso" is not the drawing context, drawing is skipped. 718 * cso == NULL ignores the filter. 719 */ 720void 721hud_run(struct hud_context *hud, struct cso_context *cso, 722 struct pipe_resource *tex) 723{ 724 struct pipe_context *pipe = cso ? cso_get_pipe_context(cso) : NULL; 725 726 /* If "cso" is the recording or drawing context or NULL, execute 727 * the operation. Otherwise, don't do anything. 728 */ 729 if (hud->record_pipe && (!pipe || pipe == hud->record_pipe)) 730 hud_stop_queries(hud, hud->record_pipe); 731 732 if (hud->cso && (!cso || cso == hud->cso)) 733 hud_draw_results(hud, tex); 734 735 if (hud->record_pipe && (!pipe || pipe == hud->record_pipe)) 736 hud_start_queries(hud, hud->record_pipe); 737} 738 739/** 740 * Record query results and assemble vertices if "pipe" is a recording but 741 * not drawing context. 742 */ 743void 744hud_record_only(struct hud_context *hud, struct pipe_context *pipe) 745{ 746 assert(pipe); 747 748 /* If it's a drawing context, only hud_run() records query results. */ 749 if (pipe == hud->pipe || pipe != hud->record_pipe) 750 return; 751 752 hud_stop_queries(hud, hud->record_pipe); 753 hud_start_queries(hud, hud->record_pipe); 754} 755 756static void 757fixup_bytes(enum pipe_driver_query_type type, int position, uint64_t *exp10) 758{ 759 if (type == PIPE_DRIVER_QUERY_TYPE_BYTES && position % 3 == 0) 760 *exp10 = (*exp10 / 1000) * 1024; 761} 762 763/** 764 * Set the maximum value for the Y axis of the graph. 765 * This scales the graph accordingly. 766 */ 767void 768hud_pane_set_max_value(struct hud_pane *pane, uint64_t value) 769{ 770 double leftmost_digit; 771 uint64_t exp10; 772 int i; 773 774 /* The following code determines the max_value in the graph as well as 775 * how many describing lines are drawn. The max_value is rounded up, 776 * so that all drawn numbers are rounded for readability. 777 * We want to print multiples of a simple number instead of multiples of 778 * hard-to-read numbers like 1.753. 779 */ 780 781 /* Find the left-most digit. Make sure exp10 * 10 and fixup_bytes doesn't 782 * overflow. (11 is safe) */ 783 exp10 = 1; 784 for (i = 0; exp10 <= UINT64_MAX / 11 && exp10 * 9 < value; i++) { 785 exp10 *= 10; 786 fixup_bytes(pane->type, i + 1, &exp10); 787 } 788 789 leftmost_digit = DIV_ROUND_UP(value, exp10); 790 791 /* Round 9 to 10. */ 792 if (leftmost_digit == 9) { 793 leftmost_digit = 1; 794 exp10 *= 10; 795 fixup_bytes(pane->type, i + 1, &exp10); 796 } 797 798 switch ((unsigned)leftmost_digit) { 799 case 1: 800 pane->last_line = 5; /* lines in +1/5 increments */ 801 break; 802 case 2: 803 pane->last_line = 8; /* lines in +1/4 increments. */ 804 break; 805 case 3: 806 case 4: 807 pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments */ 808 break; 809 case 5: 810 case 6: 811 case 7: 812 case 8: 813 pane->last_line = leftmost_digit; /* lines in +1 increments */ 814 break; 815 default: 816 assert(0); 817 } 818 819 /* Truncate {3,4} to {2.5, 3.5} if possible. */ 820 for (i = 3; i <= 4; i++) { 821 if (leftmost_digit == i && value <= (i - 0.5) * exp10) { 822 leftmost_digit = i - 0.5; 823 pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments. */ 824 } 825 } 826 827 /* Truncate 2 to a multiple of 0.2 in (1, 1.6] if possible. */ 828 if (leftmost_digit == 2) { 829 for (i = 1; i <= 3; i++) { 830 if (value <= (1 + i*0.2) * exp10) { 831 leftmost_digit = 1 + i*0.2; 832 pane->last_line = 5 + i; /* lines in +1/5 increments. */ 833 break; 834 } 835 } 836 } 837 838 pane->max_value = leftmost_digit * exp10; 839 pane->yscale = -(int)pane->inner_height / (float)pane->max_value; 840} 841 842static void 843hud_pane_update_dyn_ceiling(struct hud_graph *gr, struct hud_pane *pane) 844{ 845 unsigned i; 846 float tmp = 0.0f; 847 848 if (pane->dyn_ceil_last_ran != gr->index) { 849 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 850 for (i = 0; i < gr->num_vertices; ++i) { 851 tmp = gr->vertices[i * 2 + 1] > tmp ? 852 gr->vertices[i * 2 + 1] : tmp; 853 } 854 } 855 856 /* Avoid setting it lower than the initial starting height. */ 857 tmp = tmp > pane->initial_max_value ? tmp : pane->initial_max_value; 858 hud_pane_set_max_value(pane, tmp); 859 } 860 861 /* 862 * Mark this adjustment run so we could avoid repeating a full update 863 * again needlessly in case the pane has more than one graph. 864 */ 865 pane->dyn_ceil_last_ran = gr->index; 866} 867 868static struct hud_pane * 869hud_pane_create(struct hud_context *hud, 870 unsigned x1, unsigned y1, unsigned x2, unsigned y2, 871 unsigned y_simple, 872 unsigned period, uint64_t max_value, uint64_t ceiling, 873 boolean dyn_ceiling, boolean sort_items) 874{ 875 struct hud_pane *pane = CALLOC_STRUCT(hud_pane); 876 877 if (!pane) 878 return NULL; 879 880 pane->hud = hud; 881 pane->x1 = x1; 882 pane->y1 = y1; 883 pane->x2 = x2; 884 pane->y2 = y2; 885 pane->y_simple = y_simple; 886 pane->inner_x1 = x1 + 1; 887 pane->inner_x2 = x2 - 1; 888 pane->inner_y1 = y1 + 1; 889 pane->inner_y2 = y2 - 1; 890 pane->inner_width = pane->inner_x2 - pane->inner_x1; 891 pane->inner_height = pane->inner_y2 - pane->inner_y1; 892 pane->period = period; 893 pane->max_num_vertices = (x2 - x1 + 2) / 2; 894 pane->ceiling = ceiling; 895 pane->dyn_ceiling = dyn_ceiling; 896 pane->dyn_ceil_last_ran = 0; 897 pane->sort_items = sort_items; 898 pane->initial_max_value = max_value; 899 hud_pane_set_max_value(pane, max_value); 900 list_inithead(&pane->graph_list); 901 return pane; 902} 903 904/* replace '-' with a space */ 905static void 906strip_hyphens(char *s) 907{ 908 while (*s) { 909 if (*s == '-') 910 *s = ' '; 911 s++; 912 } 913} 914 915/** 916 * Add a graph to an existing pane. 917 * One pane can contain multiple graphs over each other. 918 */ 919void 920hud_pane_add_graph(struct hud_pane *pane, struct hud_graph *gr) 921{ 922 static const float colors[][3] = { 923 {0, 1, 0}, 924 {1, 0, 0}, 925 {0, 1, 1}, 926 {1, 0, 1}, 927 {1, 1, 0}, 928 {0.5, 1, 0.5}, 929 {1, 0.5, 0.5}, 930 {0.5, 1, 1}, 931 {1, 0.5, 1}, 932 {1, 1, 0.5}, 933 {0, 0.5, 0}, 934 {0.5, 0, 0}, 935 {0, 0.5, 0.5}, 936 {0.5, 0, 0.5}, 937 {0.5, 0.5, 0}, 938 }; 939 unsigned color = pane->next_color % ARRAY_SIZE(colors); 940 941 strip_hyphens(gr->name); 942 943 gr->vertices = MALLOC(pane->max_num_vertices * sizeof(float) * 2); 944 gr->color[0] = colors[color][0]; 945 gr->color[1] = colors[color][1]; 946 gr->color[2] = colors[color][2]; 947 gr->pane = pane; 948 list_addtail(&gr->head, &pane->graph_list); 949 pane->num_graphs++; 950 pane->next_color++; 951} 952 953void 954hud_graph_add_value(struct hud_graph *gr, double value) 955{ 956 gr->current_value = value; 957 value = value > gr->pane->ceiling ? gr->pane->ceiling : value; 958 959 if (gr->fd) { 960 if (fabs(value - lround(value)) > FLT_EPSILON) { 961 fprintf(gr->fd, "%f\n", value); 962 } 963 else { 964 fprintf(gr->fd, "%" PRIu64 "\n", (uint64_t) lround(value)); 965 } 966 } 967 968 if (gr->index == gr->pane->max_num_vertices) { 969 gr->vertices[0] = 0; 970 gr->vertices[1] = gr->vertices[(gr->index-1)*2+1]; 971 gr->index = 1; 972 } 973 gr->vertices[(gr->index)*2+0] = (float) (gr->index * 2); 974 gr->vertices[(gr->index)*2+1] = (float) value; 975 gr->index++; 976 977 if (gr->num_vertices < gr->pane->max_num_vertices) { 978 gr->num_vertices++; 979 } 980 981 if (gr->pane->dyn_ceiling == true) { 982 hud_pane_update_dyn_ceiling(gr, gr->pane); 983 } 984 if (value > gr->pane->max_value) { 985 hud_pane_set_max_value(gr->pane, value); 986 } 987} 988 989static void 990hud_graph_destroy(struct hud_graph *graph, struct pipe_context *pipe) 991{ 992 FREE(graph->vertices); 993 if (graph->free_query_data) 994 graph->free_query_data(graph->query_data, pipe); 995 if (graph->fd) 996 fclose(graph->fd); 997 FREE(graph); 998} 999 1000static void strcat_without_spaces(char *dst, const char *src) 1001{ 1002 dst += strlen(dst); 1003 while (*src) { 1004 if (*src == ' ') 1005 *dst++ = '_'; 1006 else 1007 *dst++ = *src; 1008 src++; 1009 } 1010 *dst = 0; 1011} 1012 1013 1014#ifdef PIPE_OS_WINDOWS 1015#define W_OK 0 1016static int 1017access(const char *pathname, int mode) 1018{ 1019 /* no-op */ 1020 return 0; 1021} 1022 1023#define PATH_SEP "\\" 1024 1025#else 1026 1027#define PATH_SEP "/" 1028 1029#endif 1030 1031 1032/** 1033 * If the GALLIUM_HUD_DUMP_DIR env var is set, we'll write the raw 1034 * HUD values to files at ${GALLIUM_HUD_DUMP_DIR}/<stat> where <stat> 1035 * is a HUD variable such as "fps", or "cpu" 1036 */ 1037static void 1038hud_graph_set_dump_file(struct hud_graph *gr) 1039{ 1040 const char *hud_dump_dir = getenv("GALLIUM_HUD_DUMP_DIR"); 1041 1042 if (hud_dump_dir && access(hud_dump_dir, W_OK) == 0) { 1043 char *dump_file = malloc(strlen(hud_dump_dir) + sizeof(PATH_SEP) 1044 + sizeof(gr->name)); 1045 if (dump_file) { 1046 strcpy(dump_file, hud_dump_dir); 1047 strcat(dump_file, PATH_SEP); 1048 strcat_without_spaces(dump_file, gr->name); 1049 gr->fd = fopen(dump_file, "w+"); 1050 if (gr->fd) { 1051 /* flush output after each line is written */ 1052 setvbuf(gr->fd, NULL, _IOLBF, 0); 1053 } 1054 free(dump_file); 1055 } 1056 } 1057} 1058 1059/** 1060 * Read a string from the environment variable. 1061 * The separators "+", ",", ":", and ";" terminate the string. 1062 * Return the number of read characters. 1063 */ 1064static int 1065parse_string(const char *s, char *out) 1066{ 1067 int i; 1068 1069 for (i = 0; *s && *s != '+' && *s != ',' && *s != ':' && *s != ';' && *s != '='; 1070 s++, out++, i++) 1071 *out = *s; 1072 1073 *out = 0; 1074 1075 if (*s && !i) { 1076 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) while " 1077 "parsing a string\n", *s, *s); 1078 fflush(stderr); 1079 } 1080 1081 return i; 1082} 1083 1084static char * 1085read_pane_settings(char *str, unsigned * const x, unsigned * const y, 1086 unsigned * const width, unsigned * const height, 1087 uint64_t * const ceiling, boolean * const dyn_ceiling, 1088 boolean *reset_colors, boolean *sort_items) 1089{ 1090 char *ret = str; 1091 unsigned tmp; 1092 1093 while (*str == '.') { 1094 ++str; 1095 switch (*str) { 1096 case 'x': 1097 ++str; 1098 *x = strtoul(str, &ret, 10); 1099 str = ret; 1100 break; 1101 1102 case 'y': 1103 ++str; 1104 *y = strtoul(str, &ret, 10); 1105 str = ret; 1106 break; 1107 1108 case 'w': 1109 ++str; 1110 tmp = strtoul(str, &ret, 10); 1111 *width = tmp > 80 ? tmp : 80; /* 80 is chosen arbitrarily */ 1112 str = ret; 1113 break; 1114 1115 /* 1116 * Prevent setting height to less than 50. If the height is set to less, 1117 * the text of the Y axis labels on the graph will start overlapping. 1118 */ 1119 case 'h': 1120 ++str; 1121 tmp = strtoul(str, &ret, 10); 1122 *height = tmp > 50 ? tmp : 50; 1123 str = ret; 1124 break; 1125 1126 case 'c': 1127 ++str; 1128 tmp = strtoul(str, &ret, 10); 1129 *ceiling = tmp > 10 ? tmp : 10; 1130 str = ret; 1131 break; 1132 1133 case 'd': 1134 ++str; 1135 ret = str; 1136 *dyn_ceiling = true; 1137 break; 1138 1139 case 'r': 1140 ++str; 1141 ret = str; 1142 *reset_colors = true; 1143 break; 1144 1145 case 's': 1146 ++str; 1147 ret = str; 1148 *sort_items = true; 1149 break; 1150 1151 default: 1152 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *str); 1153 fflush(stderr); 1154 } 1155 1156 } 1157 1158 return ret; 1159} 1160 1161static boolean 1162has_occlusion_query(struct pipe_screen *screen) 1163{ 1164 return screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY) != 0; 1165} 1166 1167static boolean 1168has_streamout(struct pipe_screen *screen) 1169{ 1170 return screen->get_param(screen, PIPE_CAP_MAX_STREAM_OUTPUT_BUFFERS) != 0; 1171} 1172 1173static boolean 1174has_pipeline_stats_query(struct pipe_screen *screen) 1175{ 1176 return screen->get_param(screen, PIPE_CAP_QUERY_PIPELINE_STATISTICS) != 0; 1177} 1178 1179static void 1180hud_parse_env_var(struct hud_context *hud, struct pipe_screen *screen, 1181 const char *env) 1182{ 1183 unsigned num, i; 1184 char name_a[256], s[256]; 1185 char *name; 1186 struct hud_pane *pane = NULL; 1187 unsigned x = 10, y = 10, y_simple = 10; 1188 unsigned width = 251, height = 100; 1189 unsigned period = 500 * 1000; /* default period (1/2 second) */ 1190 uint64_t ceiling = UINT64_MAX; 1191 unsigned column_width = 251; 1192 boolean dyn_ceiling = false; 1193 boolean reset_colors = false; 1194 boolean sort_items = false; 1195 const char *period_env; 1196 1197 if (strncmp(env, "simple,", 7) == 0) { 1198 hud->simple = true; 1199 env += 7; 1200 } 1201 1202 /* 1203 * The GALLIUM_HUD_PERIOD env var sets the graph update rate. 1204 * The env var is in seconds (a float). 1205 * Zero means update after every frame. 1206 */ 1207 period_env = getenv("GALLIUM_HUD_PERIOD"); 1208 if (period_env) { 1209 float p = (float) atof(period_env); 1210 if (p >= 0.0f) { 1211 period = (unsigned) (p * 1000 * 1000); 1212 } 1213 } 1214 1215 while ((num = parse_string(env, name_a)) != 0) { 1216 bool added = true; 1217 1218 env += num; 1219 1220 /* check for explicit location, size and etc. settings */ 1221 name = read_pane_settings(name_a, &x, &y, &width, &height, &ceiling, 1222 &dyn_ceiling, &reset_colors, &sort_items); 1223 1224 /* 1225 * Keep track of overall column width to avoid pane overlapping in case 1226 * later we create a new column while the bottom pane in the current 1227 * column is less wide than the rest of the panes in it. 1228 */ 1229 column_width = width > column_width ? width : column_width; 1230 1231 if (!pane) { 1232 pane = hud_pane_create(hud, x, y, x + width, y + height, y_simple, 1233 period, 10, ceiling, dyn_ceiling, sort_items); 1234 if (!pane) 1235 return; 1236 } 1237 1238 if (reset_colors) { 1239 pane->next_color = 0; 1240 reset_colors = false; 1241 } 1242 1243 /* Add a graph. */ 1244#if defined(HAVE_GALLIUM_EXTRA_HUD) || defined(HAVE_LIBSENSORS) 1245 char arg_name[64]; 1246#endif 1247 /* IF YOU CHANGE THIS, UPDATE print_help! */ 1248 if (strcmp(name, "fps") == 0) { 1249 hud_fps_graph_install(pane); 1250 } 1251 else if (strcmp(name, "frametime") == 0) { 1252 hud_frametime_graph_install(pane); 1253 } 1254 else if (strcmp(name, "cpu") == 0) { 1255 hud_cpu_graph_install(pane, ALL_CPUS); 1256 } 1257 else if (sscanf(name, "cpu%u%s", &i, s) == 1) { 1258 hud_cpu_graph_install(pane, i); 1259 } 1260 else if (strcmp(name, "API-thread-busy") == 0) { 1261 hud_thread_busy_install(pane, name, false); 1262 } 1263 else if (strcmp(name, "API-thread-offloaded-slots") == 0) { 1264 hud_thread_counter_install(pane, name, HUD_COUNTER_OFFLOADED); 1265 } 1266 else if (strcmp(name, "API-thread-direct-slots") == 0) { 1267 hud_thread_counter_install(pane, name, HUD_COUNTER_DIRECT); 1268 } 1269 else if (strcmp(name, "API-thread-num-syncs") == 0) { 1270 hud_thread_counter_install(pane, name, HUD_COUNTER_SYNCS); 1271 } 1272 else if (strcmp(name, "main-thread-busy") == 0) { 1273 hud_thread_busy_install(pane, name, true); 1274 } 1275#ifdef HAVE_GALLIUM_EXTRA_HUD 1276 else if (sscanf(name, "nic-rx-%s", arg_name) == 1) { 1277 hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_RX); 1278 } 1279 else if (sscanf(name, "nic-tx-%s", arg_name) == 1) { 1280 hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_TX); 1281 } 1282 else if (sscanf(name, "nic-rssi-%s", arg_name) == 1) { 1283 hud_nic_graph_install(pane, arg_name, NIC_RSSI_DBM); 1284 pane->type = PIPE_DRIVER_QUERY_TYPE_DBM; 1285 } 1286 else if (sscanf(name, "diskstat-rd-%s", arg_name) == 1) { 1287 hud_diskstat_graph_install(pane, arg_name, DISKSTAT_RD); 1288 pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES; 1289 } 1290 else if (sscanf(name, "diskstat-wr-%s", arg_name) == 1) { 1291 hud_diskstat_graph_install(pane, arg_name, DISKSTAT_WR); 1292 pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES; 1293 } 1294 else if (sscanf(name, "cpufreq-min-cpu%u", &i) == 1) { 1295 hud_cpufreq_graph_install(pane, i, CPUFREQ_MINIMUM); 1296 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1297 } 1298 else if (sscanf(name, "cpufreq-cur-cpu%u", &i) == 1) { 1299 hud_cpufreq_graph_install(pane, i, CPUFREQ_CURRENT); 1300 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1301 } 1302 else if (sscanf(name, "cpufreq-max-cpu%u", &i) == 1) { 1303 hud_cpufreq_graph_install(pane, i, CPUFREQ_MAXIMUM); 1304 pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; 1305 } 1306#endif 1307#ifdef HAVE_LIBSENSORS 1308 else if (sscanf(name, "sensors_temp_cu-%s", arg_name) == 1) { 1309 hud_sensors_temp_graph_install(pane, arg_name, 1310 SENSORS_TEMP_CURRENT); 1311 pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE; 1312 } 1313 else if (sscanf(name, "sensors_temp_cr-%s", arg_name) == 1) { 1314 hud_sensors_temp_graph_install(pane, arg_name, 1315 SENSORS_TEMP_CRITICAL); 1316 pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE; 1317 } 1318 else if (sscanf(name, "sensors_volt_cu-%s", arg_name) == 1) { 1319 hud_sensors_temp_graph_install(pane, arg_name, 1320 SENSORS_VOLTAGE_CURRENT); 1321 pane->type = PIPE_DRIVER_QUERY_TYPE_VOLTS; 1322 } 1323 else if (sscanf(name, "sensors_curr_cu-%s", arg_name) == 1) { 1324 hud_sensors_temp_graph_install(pane, arg_name, 1325 SENSORS_CURRENT_CURRENT); 1326 pane->type = PIPE_DRIVER_QUERY_TYPE_AMPS; 1327 } 1328 else if (sscanf(name, "sensors_pow_cu-%s", arg_name) == 1) { 1329 hud_sensors_temp_graph_install(pane, arg_name, 1330 SENSORS_POWER_CURRENT); 1331 pane->type = PIPE_DRIVER_QUERY_TYPE_WATTS; 1332 } 1333#endif 1334 else if (strcmp(name, "samples-passed") == 0 && 1335 has_occlusion_query(screen)) { 1336 hud_pipe_query_install(&hud->batch_query, pane, 1337 "samples-passed", 1338 PIPE_QUERY_OCCLUSION_COUNTER, 0, 0, 1339 PIPE_DRIVER_QUERY_TYPE_UINT64, 1340 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1341 0); 1342 } 1343 else if (strcmp(name, "primitives-generated") == 0 && 1344 has_streamout(screen)) { 1345 hud_pipe_query_install(&hud->batch_query, pane, 1346 "primitives-generated", 1347 PIPE_QUERY_PRIMITIVES_GENERATED, 0, 0, 1348 PIPE_DRIVER_QUERY_TYPE_UINT64, 1349 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1350 0); 1351 } 1352 else { 1353 boolean processed = FALSE; 1354 1355 /* pipeline statistics queries */ 1356 if (has_pipeline_stats_query(screen)) { 1357 static const char *pipeline_statistics_names[] = 1358 { 1359 "ia-vertices", 1360 "ia-primitives", 1361 "vs-invocations", 1362 "gs-invocations", 1363 "gs-primitives", 1364 "clipper-invocations", 1365 "clipper-primitives-generated", 1366 "ps-invocations", 1367 "hs-invocations", 1368 "ds-invocations", 1369 "cs-invocations" 1370 }; 1371 for (i = 0; i < ARRAY_SIZE(pipeline_statistics_names); ++i) 1372 if (strcmp(name, pipeline_statistics_names[i]) == 0) 1373 break; 1374 if (i < ARRAY_SIZE(pipeline_statistics_names)) { 1375 hud_pipe_query_install(&hud->batch_query, pane, name, 1376 PIPE_QUERY_PIPELINE_STATISTICS, i, 1377 0, PIPE_DRIVER_QUERY_TYPE_UINT64, 1378 PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE, 1379 0); 1380 processed = TRUE; 1381 } 1382 } 1383 1384 /* driver queries */ 1385 if (!processed) { 1386 if (!hud_driver_query_install(&hud->batch_query, pane, 1387 screen, name)) { 1388 fprintf(stderr, "gallium_hud: unknown driver query '%s'\n", name); 1389 fflush(stderr); 1390 added = false; 1391 } 1392 } 1393 } 1394 1395 if (*env == ':') { 1396 env++; 1397 1398 if (!pane) { 1399 fprintf(stderr, "gallium_hud: syntax error: unexpected ':', " 1400 "expected a name\n"); 1401 fflush(stderr); 1402 break; 1403 } 1404 1405 num = parse_string(env, s); 1406 env += num; 1407 1408 if (num && sscanf(s, "%u", &i) == 1) { 1409 hud_pane_set_max_value(pane, i); 1410 pane->initial_max_value = i; 1411 } 1412 else { 1413 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) " 1414 "after ':'\n", *env, *env); 1415 fflush(stderr); 1416 } 1417 } 1418 1419 if (*env == '=') { 1420 env++; 1421 1422 if (!pane) { 1423 fprintf(stderr, "gallium_hud: syntax error: unexpected '=', " 1424 "expected a name\n"); 1425 fflush(stderr); 1426 break; 1427 } 1428 1429 num = parse_string(env, s); 1430 env += num; 1431 1432 strip_hyphens(s); 1433 if (added && !list_is_empty(&pane->graph_list)) { 1434 struct hud_graph *graph; 1435 graph = list_entry(pane->graph_list.prev, struct hud_graph, head); 1436 strncpy(graph->name, s, sizeof(graph->name)-1); 1437 graph->name[sizeof(graph->name)-1] = 0; 1438 } 1439 } 1440 1441 if (*env == 0) 1442 break; 1443 1444 /* parse a separator */ 1445 switch (*env) { 1446 case '+': 1447 env++; 1448 break; 1449 1450 case ',': 1451 env++; 1452 if (!pane) 1453 break; 1454 1455 y += height + hud->font.glyph_height * (pane->num_graphs + 2); 1456 y_simple += hud->font.glyph_height * (pane->num_graphs + 1); 1457 height = 100; 1458 1459 if (pane && pane->num_graphs) { 1460 list_addtail(&pane->head, &hud->pane_list); 1461 pane = NULL; 1462 } 1463 break; 1464 1465 case ';': 1466 env++; 1467 y = 10; 1468 y_simple = 10; 1469 x += column_width + hud->font.glyph_width * 9; 1470 height = 100; 1471 1472 if (pane && pane->num_graphs) { 1473 list_addtail(&pane->head, &hud->pane_list); 1474 pane = NULL; 1475 } 1476 1477 /* Starting a new column; reset column width. */ 1478 column_width = 251; 1479 break; 1480 1481 default: 1482 fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *env); 1483 fflush(stderr); 1484 } 1485 1486 /* Reset to defaults for the next pane in case these were modified. */ 1487 width = 251; 1488 ceiling = UINT64_MAX; 1489 dyn_ceiling = false; 1490 sort_items = false; 1491 1492 } 1493 1494 if (pane) { 1495 if (pane->num_graphs) { 1496 list_addtail(&pane->head, &hud->pane_list); 1497 } 1498 else { 1499 FREE(pane); 1500 } 1501 } 1502 1503 LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) { 1504 struct hud_graph *gr; 1505 1506 LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) { 1507 hud_graph_set_dump_file(gr); 1508 } 1509 } 1510} 1511 1512static void 1513print_help(struct pipe_screen *screen) 1514{ 1515 int i, num_queries, num_cpus = hud_get_num_cpus(); 1516 1517 puts("Syntax: GALLIUM_HUD=name1[+name2][...][:value1][,nameI...][;nameJ...]"); 1518 puts(""); 1519 puts(" Names are identifiers of data sources which will be drawn as graphs"); 1520 puts(" in panes. Multiple graphs can be drawn in the same pane."); 1521 puts(" There can be multiple panes placed in rows and columns."); 1522 puts(""); 1523 puts(" '+' separates names which will share a pane."); 1524 puts(" ':[value]' specifies the initial maximum value of the Y axis"); 1525 puts(" for the given pane."); 1526 puts(" ',' creates a new pane below the last one."); 1527 puts(" ';' creates a new pane at the top of the next column."); 1528 puts(" '=' followed by a string, changes the name of the last data source"); 1529 puts(" to that string"); 1530 puts(""); 1531 puts(" Example: GALLIUM_HUD=\"cpu,fps;primitives-generated\""); 1532 puts(""); 1533 puts(" Additionally, by prepending '.[identifier][value]' modifiers to"); 1534 puts(" a name, it is possible to explicitly set the location and size"); 1535 puts(" of a pane, along with limiting overall maximum value of the"); 1536 puts(" Y axis and activating dynamic readjustment of the Y axis."); 1537 puts(" Several modifiers may be applied to the same pane simultaneously."); 1538 puts(""); 1539 puts(" 'x[value]' sets the location of the pane on the x axis relative"); 1540 puts(" to the upper-left corner of the viewport, in pixels."); 1541 puts(" 'y[value]' sets the location of the pane on the y axis relative"); 1542 puts(" to the upper-left corner of the viewport, in pixels."); 1543 puts(" 'w[value]' sets width of the graph pixels."); 1544 puts(" 'h[value]' sets height of the graph in pixels."); 1545 puts(" 'c[value]' sets the ceiling of the value of the Y axis."); 1546 puts(" If the graph needs to draw values higher than"); 1547 puts(" the ceiling allows, the value is clamped."); 1548 puts(" 'd' activates dynamic Y axis readjustment to set the value of"); 1549 puts(" the Y axis to match the highest value still visible in the graph."); 1550 puts(" 'r' resets the color counter (the next color will be green)"); 1551 puts(" 's' sort items below graphs in descending order"); 1552 puts(""); 1553 puts(" If 'c' and 'd' modifiers are used simultaneously, both are in effect:"); 1554 puts(" the Y axis does not go above the restriction imposed by 'c' while"); 1555 puts(" still adjusting the value of the Y axis down when appropriate."); 1556 puts(""); 1557 puts(" You can change behavior of the whole HUD by adding these options at"); 1558 puts(" the beginning of the environment variable:"); 1559 puts(" 'simple,' disables all the fancy stuff and only draws text."); 1560 puts(""); 1561 puts(" Example: GALLIUM_HUD=\".w256.h64.x1600.y520.d.c1000fps+cpu,.datom-count\""); 1562 puts(""); 1563 puts(" Available names:"); 1564 puts(" fps"); 1565 puts(" frametime"); 1566 puts(" cpu"); 1567 1568 for (i = 0; i < num_cpus; i++) 1569 printf(" cpu%i\n", i); 1570 1571 if (has_occlusion_query(screen)) 1572 puts(" samples-passed"); 1573 if (has_streamout(screen)) 1574 puts(" primitives-generated"); 1575 1576 if (has_pipeline_stats_query(screen)) { 1577 puts(" ia-vertices"); 1578 puts(" ia-primitives"); 1579 puts(" vs-invocations"); 1580 puts(" gs-invocations"); 1581 puts(" gs-primitives"); 1582 puts(" clipper-invocations"); 1583 puts(" clipper-primitives-generated"); 1584 puts(" ps-invocations"); 1585 puts(" hs-invocations"); 1586 puts(" ds-invocations"); 1587 puts(" cs-invocations"); 1588 } 1589 1590#ifdef HAVE_GALLIUM_EXTRA_HUD 1591 hud_get_num_disks(1); 1592 hud_get_num_nics(1); 1593 hud_get_num_cpufreq(1); 1594#endif 1595#ifdef HAVE_LIBSENSORS 1596 hud_get_num_sensors(1); 1597#endif 1598 1599 if (screen->get_driver_query_info){ 1600 boolean skipping = false; 1601 struct pipe_driver_query_info info; 1602 num_queries = screen->get_driver_query_info(screen, 0, NULL); 1603 1604 for (i = 0; i < num_queries; i++){ 1605 screen->get_driver_query_info(screen, i, &info); 1606 if (info.flags & PIPE_DRIVER_QUERY_FLAG_DONT_LIST) { 1607 if (!skipping) 1608 puts(" ..."); 1609 skipping = true; 1610 } else { 1611 printf(" %s\n", info.name); 1612 skipping = false; 1613 } 1614 } 1615 } 1616 1617 puts(""); 1618 fflush(stdout); 1619} 1620 1621static void 1622hud_unset_draw_context(struct hud_context *hud) 1623{ 1624 struct pipe_context *pipe = hud->pipe; 1625 1626 if (!pipe) 1627 return; 1628 1629 pipe_sampler_view_reference(&hud->font_sampler_view, NULL); 1630 1631 if (hud->fs_color) { 1632 pipe->delete_fs_state(pipe, hud->fs_color); 1633 hud->fs_color = NULL; 1634 } 1635 if (hud->fs_text) { 1636 pipe->delete_fs_state(pipe, hud->fs_text); 1637 hud->fs_text = NULL; 1638 } 1639 if (hud->vs_color) { 1640 pipe->delete_vs_state(pipe, hud->vs_color); 1641 hud->vs_color = NULL; 1642 } 1643 if (hud->vs_text) { 1644 pipe->delete_vs_state(pipe, hud->vs_text); 1645 hud->vs_text = NULL; 1646 } 1647 1648 hud->cso = NULL; 1649 hud->pipe = NULL; 1650} 1651 1652static bool 1653hud_set_draw_context(struct hud_context *hud, struct cso_context *cso, 1654 struct st_context_iface *st) 1655{ 1656 struct pipe_context *pipe = cso_get_pipe_context(cso); 1657 1658 assert(!hud->pipe); 1659 hud->pipe = pipe; 1660 hud->cso = cso; 1661 hud->st = st; 1662 1663 struct pipe_sampler_view view_templ; 1664 u_sampler_view_default_template( 1665 &view_templ, hud->font.texture, hud->font.texture->format); 1666 hud->font_sampler_view = pipe->create_sampler_view(pipe, hud->font.texture, 1667 &view_templ); 1668 if (!hud->font_sampler_view) 1669 goto fail; 1670 1671 /* color fragment shader */ 1672 hud->fs_color = 1673 util_make_fragment_passthrough_shader(pipe, 1674 TGSI_SEMANTIC_COLOR, 1675 TGSI_INTERPOLATE_CONSTANT, 1676 TRUE); 1677 1678 /* text fragment shader */ 1679 { 1680 /* Read a texture and do .xxxx swizzling. */ 1681 static const char *fragment_shader_text = { 1682 "FRAG\n" 1683 "DCL IN[0], GENERIC[0], LINEAR\n" 1684 "DCL SAMP[0]\n" 1685 "DCL SVIEW[0], 2D, FLOAT\n" 1686 "DCL OUT[0], COLOR[0]\n" 1687 "DCL TEMP[0]\n" 1688 1689 "TEX TEMP[0], IN[0], SAMP[0], 2D\n" 1690 "MOV OUT[0], TEMP[0].xxxx\n" 1691 "END\n" 1692 }; 1693 1694 struct tgsi_token tokens[1000]; 1695 struct pipe_shader_state state = {0}; 1696 1697 if (!tgsi_text_translate(fragment_shader_text, tokens, ARRAY_SIZE(tokens))) { 1698 assert(0); 1699 goto fail; 1700 } 1701 pipe_shader_state_from_tgsi(&state, tokens); 1702 hud->fs_text = pipe->create_fs_state(pipe, &state); 1703 } 1704 1705 /* color vertex shader */ 1706 { 1707 static const char *vertex_shader_text = { 1708 "VERT\n" 1709 "DCL IN[0..1]\n" 1710 "DCL OUT[0], POSITION\n" 1711 "DCL OUT[1], COLOR[0]\n" /* color */ 1712 "DCL OUT[2], GENERIC[0]\n" /* texcoord */ 1713 /* [0] = color, 1714 * [1] = (2/fb_width, 2/fb_height, xoffset, yoffset) 1715 * [2] = (xscale, yscale, 0, 0) */ 1716 "DCL CONST[0][0..2]\n" 1717 "DCL TEMP[0]\n" 1718 "IMM[0] FLT32 { -1, 0, 0, 1 }\n" 1719 1720 /* v = in * (xscale, yscale) + (xoffset, yoffset) */ 1721 "MAD TEMP[0].xy, IN[0], CONST[0][2].xyyy, CONST[0][1].zwww\n" 1722 /* pos = v * (2 / fb_width, 2 / fb_height) - (1, 1) */ 1723 "MAD OUT[0].xy, TEMP[0], CONST[0][1].xyyy, IMM[0].xxxx\n" 1724 "MOV OUT[0].zw, IMM[0]\n" 1725 1726 "MOV OUT[1], CONST[0][0]\n" 1727 "MOV OUT[2], IN[1]\n" 1728 "END\n" 1729 }; 1730 1731 struct tgsi_token tokens[1000]; 1732 struct pipe_shader_state state = {0}; 1733 if (!tgsi_text_translate(vertex_shader_text, tokens, ARRAY_SIZE(tokens))) { 1734 assert(0); 1735 goto fail; 1736 } 1737 pipe_shader_state_from_tgsi(&state, tokens); 1738 hud->vs_color = pipe->create_vs_state(pipe, &state); 1739 } 1740 1741 /* text vertex shader */ 1742 { 1743 /* similar to the above, without the color component 1744 * to match the varyings in fs_text */ 1745 static const char *vertex_shader_text = { 1746 "VERT\n" 1747 "DCL IN[0..1]\n" 1748 "DCL OUT[0], POSITION\n" 1749 "DCL OUT[1], GENERIC[0]\n" /* texcoord */ 1750 /* [0] = color, 1751 * [1] = (2/fb_width, 2/fb_height, xoffset, yoffset) 1752 * [2] = (xscale, yscale, 0, 0) */ 1753 "DCL CONST[0][0..2]\n" 1754 "DCL TEMP[0]\n" 1755 "IMM[0] FLT32 { -1, 0, 0, 1 }\n" 1756 "IMM[1] FLT32 { 0.0078125, 0.00390625, 1, 1 }\n" // 1.0 / 128, 1.0 / 256, 1, 1 1757 1758 /* v = in * (xscale, yscale) + (xoffset, yoffset) */ 1759 "MAD TEMP[0].xy, IN[0], CONST[0][2].xyyy, CONST[0][1].zwww\n" 1760 /* pos = v * (2 / fb_width, 2 / fb_height) - (1, 1) */ 1761 "MAD OUT[0].xy, TEMP[0], CONST[0][1].xyyy, IMM[0].xxxx\n" 1762 "MOV OUT[0].zw, IMM[0]\n" 1763 1764 "MUL OUT[1], IN[1], IMM[1]\n" 1765 "END\n" 1766 }; 1767 1768 struct tgsi_token tokens[1000]; 1769 struct pipe_shader_state state = {0}; 1770 if (!tgsi_text_translate(vertex_shader_text, tokens, ARRAY_SIZE(tokens))) { 1771 assert(0); 1772 goto fail; 1773 } 1774 pipe_shader_state_from_tgsi(&state, tokens); 1775 hud->vs_text = pipe->create_vs_state(pipe, &state); 1776 } 1777 1778 return true; 1779 1780fail: 1781 hud_unset_draw_context(hud); 1782 fprintf(stderr, "hud: failed to set a draw context"); 1783 return false; 1784} 1785 1786static void 1787hud_unset_record_context(struct hud_context *hud) 1788{ 1789 struct pipe_context *pipe = hud->record_pipe; 1790 struct hud_pane *pane, *pane_tmp; 1791 struct hud_graph *graph, *graph_tmp; 1792 1793 if (!pipe) 1794 return; 1795 1796 LIST_FOR_EACH_ENTRY_SAFE(pane, pane_tmp, &hud->pane_list, head) { 1797 LIST_FOR_EACH_ENTRY_SAFE(graph, graph_tmp, &pane->graph_list, head) { 1798 list_del(&graph->head); 1799 hud_graph_destroy(graph, pipe); 1800 } 1801 list_del(&pane->head); 1802 FREE(pane); 1803 } 1804 1805 hud_batch_query_cleanup(&hud->batch_query, pipe); 1806 hud->record_pipe = NULL; 1807} 1808 1809static void 1810hud_set_record_context(struct hud_context *hud, struct pipe_context *pipe) 1811{ 1812 hud->record_pipe = pipe; 1813} 1814 1815/** 1816 * Create the HUD. 1817 * 1818 * If "share" is non-NULL and GALLIUM_HUD_SHARE=x,y is set, increment the 1819 * reference counter of "share", set "cso" as the recording or drawing context 1820 * according to the environment variable, and return "share". 1821 * This allows sharing the HUD instance within a multi-context share group, 1822 * record queries in one context and draw them in another. 1823 */ 1824struct hud_context * 1825hud_create(struct cso_context *cso, struct st_context_iface *st, 1826 struct hud_context *share) 1827{ 1828 const char *share_env = debug_get_option("GALLIUM_HUD_SHARE", NULL); 1829 unsigned record_ctx = 0, draw_ctx = 0; 1830 1831 if (share_env && sscanf(share_env, "%u,%u", &record_ctx, &draw_ctx) != 2) 1832 share_env = NULL; 1833 1834 if (share && share_env) { 1835 /* All contexts in a share group share the HUD instance. 1836 * Only one context can record queries and only one context 1837 * can draw the HUD. 1838 * 1839 * GALLIUM_HUD_SHARE=x,y determines the context indices. 1840 */ 1841 int context_id = p_atomic_inc_return(&share->refcount) - 1; 1842 1843 if (context_id == record_ctx) { 1844 assert(!share->record_pipe); 1845 hud_set_record_context(share, cso_get_pipe_context(cso)); 1846 } 1847 1848 if (context_id == draw_ctx) { 1849 assert(!share->pipe); 1850 hud_set_draw_context(share, cso, st); 1851 } 1852 1853 return share; 1854 } 1855 1856 struct pipe_screen *screen = cso_get_pipe_context(cso)->screen; 1857 struct hud_context *hud; 1858 unsigned i; 1859 const char *env = debug_get_option("GALLIUM_HUD", NULL); 1860#ifdef PIPE_OS_UNIX 1861 unsigned signo = debug_get_num_option("GALLIUM_HUD_TOGGLE_SIGNAL", 0); 1862 static boolean sig_handled = FALSE; 1863 struct sigaction action; 1864 1865 memset(&action, 0, sizeof(action)); 1866#endif 1867 huds_visible = debug_get_bool_option("GALLIUM_HUD_VISIBLE", TRUE); 1868 hud_scale = debug_get_num_option("GALLIUM_HUD_SCALE", 1); 1869 1870 if (!env || !*env) 1871 return NULL; 1872 1873 if (strcmp(env, "help") == 0) { 1874 print_help(screen); 1875 return NULL; 1876 } 1877 1878 hud = CALLOC_STRUCT(hud_context); 1879 if (!hud) 1880 return NULL; 1881 1882 /* font (the context is only used for the texture upload) */ 1883 if (!util_font_create(cso_get_pipe_context(cso), 1884 UTIL_FONT_FIXED_8X13, &hud->font)) { 1885 FREE(hud); 1886 return NULL; 1887 } 1888 1889 hud->refcount = 1; 1890 1891 static const enum pipe_format srgb_formats[] = { 1892 PIPE_FORMAT_B8G8R8A8_SRGB, 1893 PIPE_FORMAT_B8G8R8X8_SRGB 1894 }; 1895 for (i = 0; i < ARRAY_SIZE(srgb_formats); i++) { 1896 if (!screen->is_format_supported(screen, srgb_formats[i], 1897 PIPE_TEXTURE_2D, 0, 0, 1898 PIPE_BIND_RENDER_TARGET)) 1899 break; 1900 } 1901 1902 hud->has_srgb = (i == ARRAY_SIZE(srgb_formats)); 1903 1904 /* blend state */ 1905 hud->no_blend.rt[0].colormask = PIPE_MASK_RGBA; 1906 1907 hud->alpha_blend.rt[0].colormask = PIPE_MASK_RGBA; 1908 hud->alpha_blend.rt[0].blend_enable = 1; 1909 hud->alpha_blend.rt[0].rgb_func = PIPE_BLEND_ADD; 1910 hud->alpha_blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA; 1911 hud->alpha_blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA; 1912 hud->alpha_blend.rt[0].alpha_func = PIPE_BLEND_ADD; 1913 hud->alpha_blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO; 1914 hud->alpha_blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE; 1915 1916 /* rasterizer */ 1917 hud->rasterizer.half_pixel_center = 1; 1918 hud->rasterizer.bottom_edge_rule = 1; 1919 hud->rasterizer.depth_clip_near = 1; 1920 hud->rasterizer.depth_clip_far = 1; 1921 hud->rasterizer.line_width = 1; 1922 hud->rasterizer.line_last_pixel = 1; 1923 1924 hud->rasterizer_aa_lines = hud->rasterizer; 1925 hud->rasterizer_aa_lines.line_smooth = 1; 1926 1927 /* vertex elements */ 1928 hud->velems.count = 2; 1929 for (i = 0; i < 2; i++) { 1930 hud->velems.velems[i].src_offset = i * 2 * sizeof(float); 1931 hud->velems.velems[i].src_format = PIPE_FORMAT_R32G32_FLOAT; 1932 hud->velems.velems[i].vertex_buffer_index = 0; 1933 } 1934 1935 /* sampler state (for font drawing) */ 1936 hud->font_sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1937 hud->font_sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1938 hud->font_sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE; 1939 hud->font_sampler_state.normalized_coords = 1; 1940 1941 /* constants */ 1942 hud->constbuf.buffer_size = sizeof(hud->constants); 1943 hud->constbuf.user_buffer = &hud->constants; 1944 1945 list_inithead(&hud->pane_list); 1946 1947 /* setup sig handler once for all hud contexts */ 1948#ifdef PIPE_OS_UNIX 1949 if (!sig_handled && signo != 0) { 1950 action.sa_sigaction = &signal_visible_handler; 1951 action.sa_flags = SA_SIGINFO; 1952 1953 if (signo >= NSIG) 1954 fprintf(stderr, "gallium_hud: invalid signal %u\n", signo); 1955 else if (sigaction(signo, &action, NULL) < 0) 1956 fprintf(stderr, "gallium_hud: unable to set handler for signal %u\n", signo); 1957 fflush(stderr); 1958 1959 sig_handled = TRUE; 1960 } 1961#endif 1962 1963 if (record_ctx == 0) 1964 hud_set_record_context(hud, cso_get_pipe_context(cso)); 1965 if (draw_ctx == 0) 1966 hud_set_draw_context(hud, cso, st); 1967 1968 hud_parse_env_var(hud, screen, env); 1969 return hud; 1970} 1971 1972/** 1973 * Destroy a HUD. If the HUD has several users, decrease the reference counter 1974 * and detach the context from the HUD. 1975 */ 1976void 1977hud_destroy(struct hud_context *hud, struct cso_context *cso) 1978{ 1979 if (!cso || hud->record_pipe == cso_get_pipe_context(cso)) 1980 hud_unset_record_context(hud); 1981 1982 if (!cso || hud->cso == cso) 1983 hud_unset_draw_context(hud); 1984 1985 if (p_atomic_dec_zero(&hud->refcount)) { 1986 pipe_resource_reference(&hud->font.texture, NULL); 1987 FREE(hud); 1988 } 1989} 1990 1991void 1992hud_add_queue_for_monitoring(struct hud_context *hud, 1993 struct util_queue_monitoring *queue_info) 1994{ 1995 assert(!hud->monitored_queue); 1996 hud->monitored_queue = queue_info; 1997} 1998