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