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