1848b8605Smrg/**************************************************************************
2848b8605Smrg *
3848b8605Smrg * Copyright 2013 Marek Olšák <maraeo@gmail.com>
4848b8605Smrg * All Rights Reserved.
5848b8605Smrg *
6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg * copy of this software and associated documentation files (the
8848b8605Smrg * "Software"), to deal in the Software without restriction, including
9848b8605Smrg * without limitation the rights to use, copy, modify, merge, publish,
10848b8605Smrg * distribute, sub license, and/or sell copies of the Software, and to
11848b8605Smrg * permit persons to whom the Software is furnished to do so, subject to
12848b8605Smrg * the following conditions:
13848b8605Smrg *
14848b8605Smrg * The above copyright notice and this permission notice (including the
15848b8605Smrg * next paragraph) shall be included in all copies or substantial portions
16848b8605Smrg * of the Software.
17848b8605Smrg *
18848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20848b8605Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21848b8605Smrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
22848b8605Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23848b8605Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24848b8605Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25848b8605Smrg *
26848b8605Smrg **************************************************************************/
27848b8605Smrg
28848b8605Smrg/* This head-up display module can draw transparent graphs on top of what
29848b8605Smrg * the app is rendering, visualizing various data like framerate, cpu load,
30848b8605Smrg * performance counters, etc. It can be hook up into any state tracker.
31848b8605Smrg *
32848b8605Smrg * The HUD is controlled with the GALLIUM_HUD environment variable.
33848b8605Smrg * Set GALLIUM_HUD=help for more info.
34848b8605Smrg */
35848b8605Smrg
36b8e80941Smrg#include <inttypes.h>
37b8e80941Smrg#include <signal.h>
38848b8605Smrg#include <stdio.h>
39848b8605Smrg
40848b8605Smrg#include "hud/hud_context.h"
41848b8605Smrg#include "hud/hud_private.h"
42848b8605Smrg
43848b8605Smrg#include "cso_cache/cso_context.h"
44848b8605Smrg#include "util/u_draw_quad.h"
45b8e80941Smrg#include "util/u_format.h"
46848b8605Smrg#include "util/u_inlines.h"
47848b8605Smrg#include "util/u_memory.h"
48848b8605Smrg#include "util/u_math.h"
49b8e80941Smrg#include "util/u_sampler.h"
50848b8605Smrg#include "util/u_simple_shaders.h"
51848b8605Smrg#include "util/u_string.h"
52848b8605Smrg#include "util/u_upload_mgr.h"
53848b8605Smrg#include "tgsi/tgsi_text.h"
54848b8605Smrg#include "tgsi/tgsi_dump.h"
55848b8605Smrg
56b8e80941Smrg/* Control the visibility of all HUD contexts */
57b8e80941Smrgstatic boolean huds_visible = TRUE;
58848b8605Smrg
59848b8605Smrg
60b8e80941Smrg#ifdef PIPE_OS_UNIX
61b8e80941Smrgstatic void
62b8e80941Smrgsignal_visible_handler(int sig, siginfo_t *siginfo, void *context)
63b8e80941Smrg{
64b8e80941Smrg   huds_visible = !huds_visible;
65b8e80941Smrg}
66b8e80941Smrg#endif
67848b8605Smrg
68848b8605Smrgstatic void
69848b8605Smrghud_draw_colored_prims(struct hud_context *hud, unsigned prim,
70848b8605Smrg                       float *buffer, unsigned num_vertices,
71848b8605Smrg                       float r, float g, float b, float a,
72848b8605Smrg                       int xoffset, int yoffset, float yscale)
73848b8605Smrg{
74848b8605Smrg   struct cso_context *cso = hud->cso;
75b8e80941Smrg   unsigned size = num_vertices * hud->color_prims.vbuf.stride;
76b8e80941Smrg
77b8e80941Smrg   /* If a recording context is inactive, don't draw anything. */
78b8e80941Smrg   if (size > hud->color_prims.buffer_size)
79b8e80941Smrg      return;
80b8e80941Smrg
81b8e80941Smrg   memcpy(hud->color_prims.vertices, buffer, size);
82848b8605Smrg
83848b8605Smrg   hud->constants.color[0] = r;
84848b8605Smrg   hud->constants.color[1] = g;
85848b8605Smrg   hud->constants.color[2] = b;
86848b8605Smrg   hud->constants.color[3] = a;
87848b8605Smrg   hud->constants.translate[0] = (float) xoffset;
88848b8605Smrg   hud->constants.translate[1] = (float) yoffset;
89848b8605Smrg   hud->constants.scale[0] = 1;
90848b8605Smrg   hud->constants.scale[1] = yscale;
91848b8605Smrg   cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf);
92848b8605Smrg
93b8e80941Smrg   cso_set_vertex_buffers(cso, 0, 1, &hud->color_prims.vbuf);
94848b8605Smrg   cso_set_fragment_shader_handle(hud->cso, hud->fs_color);
95848b8605Smrg   cso_draw_arrays(cso, prim, 0, num_vertices);
96b8e80941Smrg
97b8e80941Smrg   hud->color_prims.vertices += size / sizeof(float);
98b8e80941Smrg   hud->color_prims.vbuf.buffer_offset += size;
99b8e80941Smrg   hud->color_prims.buffer_size -= size;
100848b8605Smrg}
101848b8605Smrg
102848b8605Smrgstatic void
103848b8605Smrghud_draw_colored_quad(struct hud_context *hud, unsigned prim,
104848b8605Smrg                      unsigned x1, unsigned y1, unsigned x2, unsigned y2,
105848b8605Smrg                      float r, float g, float b, float a)
106848b8605Smrg{
107848b8605Smrg   float buffer[] = {
108848b8605Smrg      (float) x1, (float) y1,
109848b8605Smrg      (float) x1, (float) y2,
110848b8605Smrg      (float) x2, (float) y2,
111848b8605Smrg      (float) x2, (float) y1,
112848b8605Smrg   };
113848b8605Smrg
114848b8605Smrg   hud_draw_colored_prims(hud, prim, buffer, 4, r, g, b, a, 0, 0, 1);
115848b8605Smrg}
116848b8605Smrg
117848b8605Smrgstatic void
118848b8605Smrghud_draw_background_quad(struct hud_context *hud,
119848b8605Smrg                         unsigned x1, unsigned y1, unsigned x2, unsigned y2)
120848b8605Smrg{
121848b8605Smrg   float *vertices = hud->bg.vertices + hud->bg.num_vertices*2;
122848b8605Smrg   unsigned num = 0;
123848b8605Smrg
124848b8605Smrg   assert(hud->bg.num_vertices + 4 <= hud->bg.max_num_vertices);
125848b8605Smrg
126848b8605Smrg   vertices[num++] = (float) x1;
127848b8605Smrg   vertices[num++] = (float) y1;
128848b8605Smrg
129848b8605Smrg   vertices[num++] = (float) x1;
130848b8605Smrg   vertices[num++] = (float) y2;
131848b8605Smrg
132848b8605Smrg   vertices[num++] = (float) x2;
133848b8605Smrg   vertices[num++] = (float) y2;
134848b8605Smrg
135848b8605Smrg   vertices[num++] = (float) x2;
136848b8605Smrg   vertices[num++] = (float) y1;
137848b8605Smrg
138848b8605Smrg   hud->bg.num_vertices += num/2;
139848b8605Smrg}
140848b8605Smrg
141848b8605Smrgstatic void
142848b8605Smrghud_draw_string(struct hud_context *hud, unsigned x, unsigned y,
143848b8605Smrg                const char *str, ...)
144848b8605Smrg{
145848b8605Smrg   char buf[256];
146848b8605Smrg   char *s = buf;
147848b8605Smrg   float *vertices = hud->text.vertices + hud->text.num_vertices*4;
148848b8605Smrg   unsigned num = 0;
149848b8605Smrg
150848b8605Smrg   va_list ap;
151848b8605Smrg   va_start(ap, str);
152848b8605Smrg   util_vsnprintf(buf, sizeof(buf), str, ap);
153848b8605Smrg   va_end(ap);
154848b8605Smrg
155848b8605Smrg   if (!*s)
156848b8605Smrg      return;
157848b8605Smrg
158848b8605Smrg   hud_draw_background_quad(hud,
159848b8605Smrg                            x, y,
160848b8605Smrg                            x + strlen(buf)*hud->font.glyph_width,
161848b8605Smrg                            y + hud->font.glyph_height);
162848b8605Smrg
163848b8605Smrg   while (*s) {
164848b8605Smrg      unsigned x1 = x;
165848b8605Smrg      unsigned y1 = y;
166848b8605Smrg      unsigned x2 = x + hud->font.glyph_width;
167848b8605Smrg      unsigned y2 = y + hud->font.glyph_height;
168848b8605Smrg      unsigned tx1 = (*s % 16) * hud->font.glyph_width;
169848b8605Smrg      unsigned ty1 = (*s / 16) * hud->font.glyph_height;
170848b8605Smrg      unsigned tx2 = tx1 + hud->font.glyph_width;
171848b8605Smrg      unsigned ty2 = ty1 + hud->font.glyph_height;
172848b8605Smrg
173848b8605Smrg      if (*s == ' ') {
174848b8605Smrg         x += hud->font.glyph_width;
175848b8605Smrg         s++;
176848b8605Smrg         continue;
177848b8605Smrg      }
178848b8605Smrg
179848b8605Smrg      assert(hud->text.num_vertices + num/4 + 4 <= hud->text.max_num_vertices);
180848b8605Smrg
181848b8605Smrg      vertices[num++] = (float) x1;
182848b8605Smrg      vertices[num++] = (float) y1;
183848b8605Smrg      vertices[num++] = (float) tx1;
184848b8605Smrg      vertices[num++] = (float) ty1;
185848b8605Smrg
186848b8605Smrg      vertices[num++] = (float) x1;
187848b8605Smrg      vertices[num++] = (float) y2;
188848b8605Smrg      vertices[num++] = (float) tx1;
189848b8605Smrg      vertices[num++] = (float) ty2;
190848b8605Smrg
191848b8605Smrg      vertices[num++] = (float) x2;
192848b8605Smrg      vertices[num++] = (float) y2;
193848b8605Smrg      vertices[num++] = (float) tx2;
194848b8605Smrg      vertices[num++] = (float) ty2;
195848b8605Smrg
196848b8605Smrg      vertices[num++] = (float) x2;
197848b8605Smrg      vertices[num++] = (float) y1;
198848b8605Smrg      vertices[num++] = (float) tx2;
199848b8605Smrg      vertices[num++] = (float) ty1;
200848b8605Smrg
201848b8605Smrg      x += hud->font.glyph_width;
202848b8605Smrg      s++;
203848b8605Smrg   }
204848b8605Smrg
205848b8605Smrg   hud->text.num_vertices += num/4;
206848b8605Smrg}
207848b8605Smrg
208848b8605Smrgstatic void
209b8e80941Smrgnumber_to_human_readable(double num, enum pipe_driver_query_type type,
210b8e80941Smrg                         char *out)
211848b8605Smrg{
212848b8605Smrg   static const char *byte_units[] =
213b8e80941Smrg      {" B", " KB", " MB", " GB", " TB", " PB", " EB"};
214848b8605Smrg   static const char *metric_units[] =
215848b8605Smrg      {"", " k", " M", " G", " T", " P", " E"};
216b8e80941Smrg   static const char *time_units[] =
217b8e80941Smrg      {" us", " ms", " s"};  /* based on microseconds */
218b8e80941Smrg   static const char *hz_units[] =
219b8e80941Smrg      {" Hz", " KHz", " MHz", " GHz"};
220b8e80941Smrg   static const char *percent_units[] = {"%"};
221b8e80941Smrg   static const char *dbm_units[] = {" (-dBm)"};
222b8e80941Smrg   static const char *temperature_units[] = {" C"};
223b8e80941Smrg   static const char *volt_units[] = {" mV", " V"};
224b8e80941Smrg   static const char *amp_units[] = {" mA", " A"};
225b8e80941Smrg   static const char *watt_units[] = {" mW", " W"};
226b8e80941Smrg   static const char *float_units[] = {""};
227b8e80941Smrg
228b8e80941Smrg   const char **units;
229b8e80941Smrg   unsigned max_unit;
230b8e80941Smrg   double divisor = (type == PIPE_DRIVER_QUERY_TYPE_BYTES) ? 1024 : 1000;
231b8e80941Smrg   unsigned unit = 0;
232848b8605Smrg   double d = num;
233848b8605Smrg
234b8e80941Smrg   switch (type) {
235b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_MICROSECONDS:
236b8e80941Smrg      max_unit = ARRAY_SIZE(time_units)-1;
237b8e80941Smrg      units = time_units;
238b8e80941Smrg      break;
239b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_VOLTS:
240b8e80941Smrg      max_unit = ARRAY_SIZE(volt_units)-1;
241b8e80941Smrg      units = volt_units;
242b8e80941Smrg      break;
243b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_AMPS:
244b8e80941Smrg      max_unit = ARRAY_SIZE(amp_units)-1;
245b8e80941Smrg      units = amp_units;
246b8e80941Smrg      break;
247b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_DBM:
248b8e80941Smrg      max_unit = ARRAY_SIZE(dbm_units)-1;
249b8e80941Smrg      units = dbm_units;
250b8e80941Smrg      break;
251b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_TEMPERATURE:
252b8e80941Smrg      max_unit = ARRAY_SIZE(temperature_units)-1;
253b8e80941Smrg      units = temperature_units;
254b8e80941Smrg      break;
255b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_FLOAT:
256b8e80941Smrg      max_unit = ARRAY_SIZE(float_units)-1;
257b8e80941Smrg      units = float_units;
258b8e80941Smrg      break;
259b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE:
260b8e80941Smrg      max_unit = ARRAY_SIZE(percent_units)-1;
261b8e80941Smrg      units = percent_units;
262b8e80941Smrg      break;
263b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_BYTES:
264b8e80941Smrg      max_unit = ARRAY_SIZE(byte_units)-1;
265b8e80941Smrg      units = byte_units;
266b8e80941Smrg      break;
267b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_HZ:
268b8e80941Smrg      max_unit = ARRAY_SIZE(hz_units)-1;
269b8e80941Smrg      units = hz_units;
270b8e80941Smrg      break;
271b8e80941Smrg   case PIPE_DRIVER_QUERY_TYPE_WATTS:
272b8e80941Smrg      max_unit = ARRAY_SIZE(watt_units)-1;
273b8e80941Smrg      units = watt_units;
274b8e80941Smrg      break;
275b8e80941Smrg   default:
276b8e80941Smrg      max_unit = ARRAY_SIZE(metric_units)-1;
277b8e80941Smrg      units = metric_units;
278b8e80941Smrg   }
279b8e80941Smrg
280b8e80941Smrg   while (d > divisor && unit < max_unit) {
281848b8605Smrg      d /= divisor;
282848b8605Smrg      unit++;
283848b8605Smrg   }
284848b8605Smrg
285b8e80941Smrg   /* Round to 3 decimal places so as not to print trailing zeros. */
286b8e80941Smrg   if (d*1000 != (int)(d*1000))
287b8e80941Smrg      d = round(d * 1000) / 1000;
288b8e80941Smrg
289b8e80941Smrg   /* Show at least 4 digits with at most 3 decimal places, but not zeros. */
290b8e80941Smrg   if (d >= 1000 || d == (int)d)
291848b8605Smrg      sprintf(out, "%.0f%s", d, units[unit]);
292b8e80941Smrg   else if (d >= 100 || d*10 == (int)(d*10))
293848b8605Smrg      sprintf(out, "%.1f%s", d, units[unit]);
294b8e80941Smrg   else if (d >= 10 || d*100 == (int)(d*100))
295848b8605Smrg      sprintf(out, "%.2f%s", d, units[unit]);
296b8e80941Smrg   else
297b8e80941Smrg      sprintf(out, "%.3f%s", d, units[unit]);
298848b8605Smrg}
299848b8605Smrg
300848b8605Smrgstatic void
301848b8605Smrghud_draw_graph_line_strip(struct hud_context *hud, const struct hud_graph *gr,
302848b8605Smrg                          unsigned xoffset, unsigned yoffset, float yscale)
303848b8605Smrg{
304848b8605Smrg   if (gr->num_vertices <= 1)
305848b8605Smrg      return;
306848b8605Smrg
307848b8605Smrg   assert(gr->index <= gr->num_vertices);
308848b8605Smrg
309848b8605Smrg   hud_draw_colored_prims(hud, PIPE_PRIM_LINE_STRIP,
310848b8605Smrg                          gr->vertices, gr->index,
311848b8605Smrg                          gr->color[0], gr->color[1], gr->color[2], 1,
312848b8605Smrg                          xoffset + (gr->pane->max_num_vertices - gr->index - 1) * 2 - 1,
313848b8605Smrg                          yoffset, yscale);
314848b8605Smrg
315848b8605Smrg   if (gr->num_vertices <= gr->index)
316848b8605Smrg      return;
317848b8605Smrg
318848b8605Smrg   hud_draw_colored_prims(hud, PIPE_PRIM_LINE_STRIP,
319848b8605Smrg                          gr->vertices + gr->index*2,
320848b8605Smrg                          gr->num_vertices - gr->index,
321848b8605Smrg                          gr->color[0], gr->color[1], gr->color[2], 1,
322848b8605Smrg                          xoffset - gr->index*2 - 1, yoffset, yscale);
323848b8605Smrg}
324848b8605Smrg
325848b8605Smrgstatic void
326848b8605Smrghud_pane_accumulate_vertices(struct hud_context *hud,
327848b8605Smrg                             const struct hud_pane *pane)
328848b8605Smrg{
329848b8605Smrg   struct hud_graph *gr;
330848b8605Smrg   float *line_verts = hud->whitelines.vertices + hud->whitelines.num_vertices*2;
331848b8605Smrg   unsigned i, num = 0;
332848b8605Smrg   char str[32];
333b8e80941Smrg   const unsigned last_line = pane->last_line;
334848b8605Smrg
335848b8605Smrg   /* draw background */
336848b8605Smrg   hud_draw_background_quad(hud,
337848b8605Smrg                            pane->x1, pane->y1,
338848b8605Smrg                            pane->x2, pane->y2);
339848b8605Smrg
340848b8605Smrg   /* draw numbers on the right-hand side */
341b8e80941Smrg   for (i = 0; i <= last_line; i++) {
342848b8605Smrg      unsigned x = pane->x2 + 2;
343b8e80941Smrg      unsigned y = pane->inner_y1 +
344b8e80941Smrg                   pane->inner_height * (last_line - i) / last_line -
345848b8605Smrg                   hud->font.glyph_height / 2;
346848b8605Smrg
347b8e80941Smrg      number_to_human_readable(pane->max_value * i / last_line,
348b8e80941Smrg                               pane->type, str);
349b8e80941Smrg      hud_draw_string(hud, x, y, "%s", str);
350848b8605Smrg   }
351848b8605Smrg
352848b8605Smrg   /* draw info below the pane */
353848b8605Smrg   i = 0;
354848b8605Smrg   LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
355848b8605Smrg      unsigned x = pane->x1 + 2;
356848b8605Smrg      unsigned y = pane->y2 + 2 + i*hud->font.glyph_height;
357848b8605Smrg
358b8e80941Smrg      number_to_human_readable(gr->current_value, pane->type, str);
359848b8605Smrg      hud_draw_string(hud, x, y, "  %s: %s", gr->name, str);
360848b8605Smrg      i++;
361848b8605Smrg   }
362848b8605Smrg
363848b8605Smrg   /* draw border */
364848b8605Smrg   assert(hud->whitelines.num_vertices + num/2 + 8 <= hud->whitelines.max_num_vertices);
365848b8605Smrg   line_verts[num++] = (float) pane->x1;
366848b8605Smrg   line_verts[num++] = (float) pane->y1;
367848b8605Smrg   line_verts[num++] = (float) pane->x2;
368848b8605Smrg   line_verts[num++] = (float) pane->y1;
369848b8605Smrg
370848b8605Smrg   line_verts[num++] = (float) pane->x2;
371848b8605Smrg   line_verts[num++] = (float) pane->y1;
372848b8605Smrg   line_verts[num++] = (float) pane->x2;
373848b8605Smrg   line_verts[num++] = (float) pane->y2;
374848b8605Smrg
375848b8605Smrg   line_verts[num++] = (float) pane->x1;
376848b8605Smrg   line_verts[num++] = (float) pane->y2;
377848b8605Smrg   line_verts[num++] = (float) pane->x2;
378848b8605Smrg   line_verts[num++] = (float) pane->y2;
379848b8605Smrg
380848b8605Smrg   line_verts[num++] = (float) pane->x1;
381848b8605Smrg   line_verts[num++] = (float) pane->y1;
382848b8605Smrg   line_verts[num++] = (float) pane->x1;
383848b8605Smrg   line_verts[num++] = (float) pane->y2;
384848b8605Smrg
385848b8605Smrg   /* draw horizontal lines inside the graph */
386b8e80941Smrg   for (i = 0; i <= last_line; i++) {
387b8e80941Smrg      float y = round((pane->max_value * i / (double)last_line) *
388b8e80941Smrg                      pane->yscale + pane->inner_y2);
389848b8605Smrg
390848b8605Smrg      assert(hud->whitelines.num_vertices + num/2 + 2 <= hud->whitelines.max_num_vertices);
391848b8605Smrg      line_verts[num++] = pane->x1;
392848b8605Smrg      line_verts[num++] = y;
393848b8605Smrg      line_verts[num++] = pane->x2;
394848b8605Smrg      line_verts[num++] = y;
395848b8605Smrg   }
396848b8605Smrg
397848b8605Smrg   hud->whitelines.num_vertices += num/2;
398848b8605Smrg}
399848b8605Smrg
400b8e80941Smrgstatic void
401b8e80941Smrghud_pane_accumulate_vertices_simple(struct hud_context *hud,
402b8e80941Smrg                                    const struct hud_pane *pane)
403b8e80941Smrg{
404b8e80941Smrg   struct hud_graph *gr;
405b8e80941Smrg   unsigned i;
406b8e80941Smrg   char str[32];
407b8e80941Smrg
408b8e80941Smrg   /* draw info below the pane */
409b8e80941Smrg   i = 0;
410b8e80941Smrg   LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
411b8e80941Smrg      unsigned x = pane->x1;
412b8e80941Smrg      unsigned y = pane->y_simple + i*hud->font.glyph_height;
413b8e80941Smrg
414b8e80941Smrg      number_to_human_readable(gr->current_value, pane->type, str);
415b8e80941Smrg      hud_draw_string(hud, x, y, "%s: %s", gr->name, str);
416b8e80941Smrg      i++;
417b8e80941Smrg   }
418b8e80941Smrg}
419b8e80941Smrg
420848b8605Smrgstatic void
421848b8605Smrghud_pane_draw_colored_objects(struct hud_context *hud,
422848b8605Smrg                              const struct hud_pane *pane)
423848b8605Smrg{
424848b8605Smrg   struct hud_graph *gr;
425848b8605Smrg   unsigned i;
426848b8605Smrg
427848b8605Smrg   /* draw colored quads below the pane */
428848b8605Smrg   i = 0;
429848b8605Smrg   LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
430848b8605Smrg      unsigned x = pane->x1 + 2;
431848b8605Smrg      unsigned y = pane->y2 + 2 + i*hud->font.glyph_height;
432848b8605Smrg
433848b8605Smrg      hud_draw_colored_quad(hud, PIPE_PRIM_QUADS, x + 1, y + 1, x + 12, y + 13,
434848b8605Smrg                            gr->color[0], gr->color[1], gr->color[2], 1);
435848b8605Smrg      i++;
436848b8605Smrg   }
437848b8605Smrg
438848b8605Smrg   /* draw the line strips */
439848b8605Smrg   LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
440848b8605Smrg      hud_draw_graph_line_strip(hud, gr, pane->inner_x1, pane->inner_y2, pane->yscale);
441848b8605Smrg   }
442848b8605Smrg}
443848b8605Smrg
444848b8605Smrgstatic void
445b8e80941Smrghud_prepare_vertices(struct hud_context *hud, struct vertex_queue *v,
446b8e80941Smrg                     unsigned num_vertices, unsigned stride)
447848b8605Smrg{
448848b8605Smrg   v->num_vertices = 0;
449848b8605Smrg   v->max_num_vertices = num_vertices;
450848b8605Smrg   v->vbuf.stride = stride;
451b8e80941Smrg   v->buffer_size = stride * num_vertices;
452848b8605Smrg}
453848b8605Smrg
454848b8605Smrg/**
455848b8605Smrg * Draw the HUD to the texture \p tex.
456848b8605Smrg * The texture is usually the back buffer being displayed.
457848b8605Smrg */
458b8e80941Smrgstatic void
459b8e80941Smrghud_draw_results(struct hud_context *hud, struct pipe_resource *tex)
460848b8605Smrg{
461848b8605Smrg   struct cso_context *cso = hud->cso;
462848b8605Smrg   struct pipe_context *pipe = hud->pipe;
463848b8605Smrg   struct pipe_framebuffer_state fb;
464848b8605Smrg   struct pipe_surface surf_templ, *surf;
465848b8605Smrg   struct pipe_viewport_state viewport;
466848b8605Smrg   const struct pipe_sampler_state *sampler_states[] =
467848b8605Smrg         { &hud->font_sampler_state };
468848b8605Smrg   struct hud_pane *pane;
469b8e80941Smrg
470b8e80941Smrg   if (!huds_visible)
471b8e80941Smrg      return;
472848b8605Smrg
473848b8605Smrg   hud->fb_width = tex->width0;
474848b8605Smrg   hud->fb_height = tex->height0;
475848b8605Smrg   hud->constants.two_div_fb_width = 2.0f / hud->fb_width;
476848b8605Smrg   hud->constants.two_div_fb_height = 2.0f / hud->fb_height;
477848b8605Smrg
478b8e80941Smrg   cso_save_state(cso, (CSO_BIT_FRAMEBUFFER |
479b8e80941Smrg                        CSO_BIT_SAMPLE_MASK |
480b8e80941Smrg                        CSO_BIT_MIN_SAMPLES |
481b8e80941Smrg                        CSO_BIT_BLEND |
482b8e80941Smrg                        CSO_BIT_DEPTH_STENCIL_ALPHA |
483b8e80941Smrg                        CSO_BIT_FRAGMENT_SHADER |
484b8e80941Smrg                        CSO_BIT_FRAGMENT_SAMPLER_VIEWS |
485b8e80941Smrg                        CSO_BIT_FRAGMENT_SAMPLERS |
486b8e80941Smrg                        CSO_BIT_RASTERIZER |
487b8e80941Smrg                        CSO_BIT_VIEWPORT |
488b8e80941Smrg                        CSO_BIT_STREAM_OUTPUTS |
489b8e80941Smrg                        CSO_BIT_GEOMETRY_SHADER |
490b8e80941Smrg                        CSO_BIT_TESSCTRL_SHADER |
491b8e80941Smrg                        CSO_BIT_TESSEVAL_SHADER |
492b8e80941Smrg                        CSO_BIT_VERTEX_SHADER |
493b8e80941Smrg                        CSO_BIT_VERTEX_ELEMENTS |
494b8e80941Smrg                        CSO_BIT_AUX_VERTEX_BUFFER_SLOT |
495b8e80941Smrg                        CSO_BIT_PAUSE_QUERIES |
496b8e80941Smrg                        CSO_BIT_RENDER_CONDITION));
497848b8605Smrg   cso_save_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX);
498848b8605Smrg
499848b8605Smrg   /* set states */
500848b8605Smrg   memset(&surf_templ, 0, sizeof(surf_templ));
501848b8605Smrg   surf_templ.format = tex->format;
502b8e80941Smrg
503b8e80941Smrg   /* Without this, AA lines look thinner if they are between 2 pixels
504b8e80941Smrg    * because the alpha is 0.5 on both pixels. (it's ugly)
505b8e80941Smrg    *
506b8e80941Smrg    * sRGB makes the width of all AA lines look the same.
507b8e80941Smrg    */
508b8e80941Smrg   if (hud->has_srgb) {
509b8e80941Smrg      enum pipe_format srgb_format = util_format_srgb(tex->format);
510b8e80941Smrg
511b8e80941Smrg      if (srgb_format != PIPE_FORMAT_NONE)
512b8e80941Smrg         surf_templ.format = srgb_format;
513b8e80941Smrg   }
514848b8605Smrg   surf = pipe->create_surface(pipe, tex, &surf_templ);
515848b8605Smrg
516848b8605Smrg   memset(&fb, 0, sizeof(fb));
517848b8605Smrg   fb.nr_cbufs = 1;
518848b8605Smrg   fb.cbufs[0] = surf;
519848b8605Smrg   fb.zsbuf = NULL;
520848b8605Smrg   fb.width = hud->fb_width;
521848b8605Smrg   fb.height = hud->fb_height;
522848b8605Smrg
523848b8605Smrg   viewport.scale[0] = 0.5f * hud->fb_width;
524848b8605Smrg   viewport.scale[1] = 0.5f * hud->fb_height;
525848b8605Smrg   viewport.scale[2] = 1.0f;
526848b8605Smrg   viewport.translate[0] = 0.5f * hud->fb_width;
527848b8605Smrg   viewport.translate[1] = 0.5f * hud->fb_height;
528848b8605Smrg   viewport.translate[2] = 0.0f;
529848b8605Smrg
530848b8605Smrg   cso_set_framebuffer(cso, &fb);
531848b8605Smrg   cso_set_sample_mask(cso, ~0);
532848b8605Smrg   cso_set_min_samples(cso, 1);
533848b8605Smrg   cso_set_depth_stencil_alpha(cso, &hud->dsa);
534848b8605Smrg   cso_set_rasterizer(cso, &hud->rasterizer);
535848b8605Smrg   cso_set_viewport(cso, &viewport);
536848b8605Smrg   cso_set_stream_outputs(cso, 0, NULL, NULL);
537b8e80941Smrg   cso_set_tessctrl_shader_handle(cso, NULL);
538b8e80941Smrg   cso_set_tesseval_shader_handle(cso, NULL);
539848b8605Smrg   cso_set_geometry_shader_handle(cso, NULL);
540848b8605Smrg   cso_set_vertex_shader_handle(cso, hud->vs);
541848b8605Smrg   cso_set_vertex_elements(cso, 2, hud->velems);
542848b8605Smrg   cso_set_render_condition(cso, NULL, FALSE, 0);
543848b8605Smrg   cso_set_sampler_views(cso, PIPE_SHADER_FRAGMENT, 1,
544848b8605Smrg                         &hud->font_sampler_view);
545848b8605Smrg   cso_set_samplers(cso, PIPE_SHADER_FRAGMENT, 1, sampler_states);
546848b8605Smrg   cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf);
547848b8605Smrg
548848b8605Smrg   /* draw accumulated vertices for background quads */
549b8e80941Smrg   cso_set_blend(cso, &hud->alpha_blend);
550848b8605Smrg   cso_set_fragment_shader_handle(hud->cso, hud->fs_color);
551848b8605Smrg
552848b8605Smrg   if (hud->bg.num_vertices) {
553848b8605Smrg      hud->constants.color[0] = 0;
554848b8605Smrg      hud->constants.color[1] = 0;
555848b8605Smrg      hud->constants.color[2] = 0;
556848b8605Smrg      hud->constants.color[3] = 0.666f;
557848b8605Smrg      hud->constants.translate[0] = 0;
558848b8605Smrg      hud->constants.translate[1] = 0;
559848b8605Smrg      hud->constants.scale[0] = 1;
560848b8605Smrg      hud->constants.scale[1] = 1;
561848b8605Smrg
562848b8605Smrg      cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf);
563b8e80941Smrg      cso_set_vertex_buffers(cso, 0, 1, &hud->bg.vbuf);
564848b8605Smrg      cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->bg.num_vertices);
565848b8605Smrg   }
566b8e80941Smrg   pipe_resource_reference(&hud->bg.vbuf.buffer.resource, NULL);
567b8e80941Smrg
568b8e80941Smrg   /* draw accumulated vertices for text */
569b8e80941Smrg   if (hud->text.num_vertices) {
570b8e80941Smrg      cso_set_vertex_buffers(cso, 0, 1, &hud->text.vbuf);
571b8e80941Smrg      cso_set_fragment_shader_handle(hud->cso, hud->fs_text);
572b8e80941Smrg      cso_draw_arrays(cso, PIPE_PRIM_QUADS, 0, hud->text.num_vertices);
573b8e80941Smrg   }
574b8e80941Smrg   pipe_resource_reference(&hud->text.vbuf.buffer.resource, NULL);
575b8e80941Smrg
576b8e80941Smrg   if (hud->simple) {
577b8e80941Smrg      cso_restore_state(cso);
578b8e80941Smrg      cso_restore_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX);
579b8e80941Smrg
580b8e80941Smrg      pipe_surface_reference(&surf, NULL);
581b8e80941Smrg      return;
582b8e80941Smrg   }
583848b8605Smrg
584848b8605Smrg   /* draw accumulated vertices for white lines */
585b8e80941Smrg   cso_set_blend(cso, &hud->no_blend);
586b8e80941Smrg
587848b8605Smrg   hud->constants.color[0] = 1;
588848b8605Smrg   hud->constants.color[1] = 1;
589848b8605Smrg   hud->constants.color[2] = 1;
590848b8605Smrg   hud->constants.color[3] = 1;
591848b8605Smrg   hud->constants.translate[0] = 0;
592848b8605Smrg   hud->constants.translate[1] = 0;
593848b8605Smrg   hud->constants.scale[0] = 1;
594848b8605Smrg   hud->constants.scale[1] = 1;
595848b8605Smrg   cso_set_constant_buffer(cso, PIPE_SHADER_VERTEX, 0, &hud->constbuf);
596848b8605Smrg
597848b8605Smrg   if (hud->whitelines.num_vertices) {
598b8e80941Smrg      cso_set_vertex_buffers(cso, 0, 1, &hud->whitelines.vbuf);
599848b8605Smrg      cso_set_fragment_shader_handle(hud->cso, hud->fs_color);
600848b8605Smrg      cso_draw_arrays(cso, PIPE_PRIM_LINES, 0, hud->whitelines.num_vertices);
601848b8605Smrg   }
602b8e80941Smrg   pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, NULL);
603848b8605Smrg
604848b8605Smrg   /* draw the rest */
605b8e80941Smrg   cso_set_blend(cso, &hud->alpha_blend);
606b8e80941Smrg   cso_set_rasterizer(cso, &hud->rasterizer_aa_lines);
607848b8605Smrg   LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) {
608848b8605Smrg      if (pane)
609848b8605Smrg         hud_pane_draw_colored_objects(hud, pane);
610848b8605Smrg   }
611848b8605Smrg
612b8e80941Smrg   cso_restore_state(cso);
613848b8605Smrg   cso_restore_constant_buffer_slot0(cso, PIPE_SHADER_VERTEX);
614848b8605Smrg
615848b8605Smrg   pipe_surface_reference(&surf, NULL);
616848b8605Smrg}
617848b8605Smrg
618b8e80941Smrgstatic void
619b8e80941Smrghud_start_queries(struct hud_context *hud, struct pipe_context *pipe)
620b8e80941Smrg{
621b8e80941Smrg   struct hud_pane *pane;
622b8e80941Smrg   struct hud_graph *gr;
623b8e80941Smrg
624b8e80941Smrg   /* Start queries. */
625b8e80941Smrg   hud_batch_query_begin(hud->batch_query, pipe);
626b8e80941Smrg
627b8e80941Smrg   LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) {
628b8e80941Smrg      LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
629b8e80941Smrg         if (gr->begin_query)
630b8e80941Smrg            gr->begin_query(gr, pipe);
631b8e80941Smrg      }
632b8e80941Smrg   }
633b8e80941Smrg}
634b8e80941Smrg
635b8e80941Smrg/* Stop queries, query results, and record vertices for charts. */
636b8e80941Smrgstatic void
637b8e80941Smrghud_stop_queries(struct hud_context *hud, struct pipe_context *pipe)
638b8e80941Smrg{
639b8e80941Smrg   struct hud_pane *pane;
640b8e80941Smrg   struct hud_graph *gr, *next;
641b8e80941Smrg
642b8e80941Smrg   /* prepare vertex buffers */
643b8e80941Smrg   hud_prepare_vertices(hud, &hud->bg, 16 * 256, 2 * sizeof(float));
644b8e80941Smrg   hud_prepare_vertices(hud, &hud->whitelines, 4 * 256, 2 * sizeof(float));
645b8e80941Smrg   hud_prepare_vertices(hud, &hud->text, 16 * 1024, 4 * sizeof(float));
646b8e80941Smrg   hud_prepare_vertices(hud, &hud->color_prims, 32 * 1024, 2 * sizeof(float));
647b8e80941Smrg
648b8e80941Smrg   /* Allocate everything once and divide the storage into 3 portions
649b8e80941Smrg    * manually, because u_upload_alloc can unmap memory from previous calls.
650b8e80941Smrg    */
651b8e80941Smrg   u_upload_alloc(pipe->stream_uploader, 0,
652b8e80941Smrg                  hud->bg.buffer_size +
653b8e80941Smrg                  hud->whitelines.buffer_size +
654b8e80941Smrg                  hud->text.buffer_size +
655b8e80941Smrg                  hud->color_prims.buffer_size,
656b8e80941Smrg                  16, &hud->bg.vbuf.buffer_offset, &hud->bg.vbuf.buffer.resource,
657b8e80941Smrg                  (void**)&hud->bg.vertices);
658b8e80941Smrg   if (!hud->bg.vertices)
659b8e80941Smrg      return;
660b8e80941Smrg
661b8e80941Smrg   pipe_resource_reference(&hud->whitelines.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource);
662b8e80941Smrg   pipe_resource_reference(&hud->text.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource);
663b8e80941Smrg   pipe_resource_reference(&hud->color_prims.vbuf.buffer.resource, hud->bg.vbuf.buffer.resource);
664b8e80941Smrg
665b8e80941Smrg   hud->whitelines.vbuf.buffer_offset = hud->bg.vbuf.buffer_offset +
666b8e80941Smrg                                        hud->bg.buffer_size;
667b8e80941Smrg   hud->whitelines.vertices = hud->bg.vertices +
668b8e80941Smrg                              hud->bg.buffer_size / sizeof(float);
669b8e80941Smrg
670b8e80941Smrg   hud->text.vbuf.buffer_offset = hud->whitelines.vbuf.buffer_offset +
671b8e80941Smrg                                  hud->whitelines.buffer_size;
672b8e80941Smrg   hud->text.vertices = hud->whitelines.vertices +
673b8e80941Smrg                        hud->whitelines.buffer_size / sizeof(float);
674b8e80941Smrg
675b8e80941Smrg   hud->color_prims.vbuf.buffer_offset = hud->text.vbuf.buffer_offset +
676b8e80941Smrg                                         hud->text.buffer_size;
677b8e80941Smrg   hud->color_prims.vertices = hud->text.vertices +
678b8e80941Smrg                               hud->text.buffer_size / sizeof(float);
679b8e80941Smrg
680b8e80941Smrg   /* prepare all graphs */
681b8e80941Smrg   hud_batch_query_update(hud->batch_query, pipe);
682b8e80941Smrg
683b8e80941Smrg   LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) {
684b8e80941Smrg      LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
685b8e80941Smrg         gr->query_new_value(gr, pipe);
686b8e80941Smrg      }
687b8e80941Smrg
688b8e80941Smrg      if (pane->sort_items) {
689b8e80941Smrg         LIST_FOR_EACH_ENTRY_SAFE(gr, next, &pane->graph_list, head) {
690b8e80941Smrg            /* ignore the last one */
691b8e80941Smrg            if (&gr->head == pane->graph_list.prev)
692b8e80941Smrg               continue;
693b8e80941Smrg
694b8e80941Smrg            /* This is an incremental bubble sort, because we only do one pass
695b8e80941Smrg             * per frame. It will eventually reach an equilibrium.
696b8e80941Smrg             */
697b8e80941Smrg            if (gr->current_value <
698b8e80941Smrg                LIST_ENTRY(struct hud_graph, next, head)->current_value) {
699b8e80941Smrg               LIST_DEL(&gr->head);
700b8e80941Smrg               LIST_ADD(&gr->head, &next->head);
701b8e80941Smrg            }
702b8e80941Smrg         }
703b8e80941Smrg      }
704b8e80941Smrg
705b8e80941Smrg      if (hud->simple)
706b8e80941Smrg         hud_pane_accumulate_vertices_simple(hud, pane);
707b8e80941Smrg      else
708b8e80941Smrg         hud_pane_accumulate_vertices(hud, pane);
709b8e80941Smrg   }
710b8e80941Smrg
711b8e80941Smrg   /* unmap the uploader's vertex buffer before drawing */
712b8e80941Smrg   u_upload_unmap(pipe->stream_uploader);
713b8e80941Smrg}
714b8e80941Smrg
715b8e80941Smrg/**
716b8e80941Smrg * Record queries and draw the HUD. The "cso" parameter acts as a filter.
717b8e80941Smrg * If "cso" is not the recording context, recording is skipped.
718b8e80941Smrg * If "cso" is not the drawing context, drawing is skipped.
719b8e80941Smrg * cso == NULL ignores the filter.
720b8e80941Smrg */
721b8e80941Smrgvoid
722b8e80941Smrghud_run(struct hud_context *hud, struct cso_context *cso,
723b8e80941Smrg        struct pipe_resource *tex)
724b8e80941Smrg{
725b8e80941Smrg   struct pipe_context *pipe = cso ? cso_get_pipe_context(cso) : NULL;
726b8e80941Smrg
727b8e80941Smrg   /* If "cso" is the recording or drawing context or NULL, execute
728b8e80941Smrg    * the operation. Otherwise, don't do anything.
729b8e80941Smrg    */
730b8e80941Smrg   if (hud->record_pipe && (!pipe || pipe == hud->record_pipe))
731b8e80941Smrg      hud_stop_queries(hud, hud->record_pipe);
732b8e80941Smrg
733b8e80941Smrg   if (hud->cso && (!cso || cso == hud->cso))
734b8e80941Smrg      hud_draw_results(hud, tex);
735b8e80941Smrg
736b8e80941Smrg   if (hud->record_pipe && (!pipe || pipe == hud->record_pipe))
737b8e80941Smrg      hud_start_queries(hud, hud->record_pipe);
738b8e80941Smrg}
739b8e80941Smrg
740b8e80941Smrg/**
741b8e80941Smrg * Record query results and assemble vertices if "pipe" is a recording but
742b8e80941Smrg * not drawing context.
743b8e80941Smrg */
744b8e80941Smrgvoid
745b8e80941Smrghud_record_only(struct hud_context *hud, struct pipe_context *pipe)
746b8e80941Smrg{
747b8e80941Smrg   assert(pipe);
748b8e80941Smrg
749b8e80941Smrg   /* If it's a drawing context, only hud_run() records query results. */
750b8e80941Smrg   if (pipe == hud->pipe || pipe != hud->record_pipe)
751b8e80941Smrg      return;
752b8e80941Smrg
753b8e80941Smrg   hud_stop_queries(hud, hud->record_pipe);
754b8e80941Smrg   hud_start_queries(hud, hud->record_pipe);
755b8e80941Smrg}
756b8e80941Smrg
757b8e80941Smrgstatic void
758b8e80941Smrgfixup_bytes(enum pipe_driver_query_type type, int position, uint64_t *exp10)
759b8e80941Smrg{
760b8e80941Smrg   if (type == PIPE_DRIVER_QUERY_TYPE_BYTES && position % 3 == 0)
761b8e80941Smrg      *exp10 = (*exp10 / 1000) * 1024;
762b8e80941Smrg}
763b8e80941Smrg
764848b8605Smrg/**
765848b8605Smrg * Set the maximum value for the Y axis of the graph.
766848b8605Smrg * This scales the graph accordingly.
767848b8605Smrg */
768848b8605Smrgvoid
769848b8605Smrghud_pane_set_max_value(struct hud_pane *pane, uint64_t value)
770848b8605Smrg{
771b8e80941Smrg   double leftmost_digit;
772b8e80941Smrg   uint64_t exp10;
773b8e80941Smrg   int i;
774b8e80941Smrg
775b8e80941Smrg   /* The following code determines the max_value in the graph as well as
776b8e80941Smrg    * how many describing lines are drawn. The max_value is rounded up,
777b8e80941Smrg    * so that all drawn numbers are rounded for readability.
778b8e80941Smrg    * We want to print multiples of a simple number instead of multiples of
779b8e80941Smrg    * hard-to-read numbers like 1.753.
780b8e80941Smrg    */
781b8e80941Smrg
782b8e80941Smrg   /* Find the left-most digit. Make sure exp10 * 10 and fixup_bytes doesn't
783b8e80941Smrg    * overflow. (11 is safe) */
784b8e80941Smrg   exp10 = 1;
785b8e80941Smrg   for (i = 0; exp10 <= UINT64_MAX / 11 && exp10 * 9 < value; i++) {
786b8e80941Smrg      exp10 *= 10;
787b8e80941Smrg      fixup_bytes(pane->type, i + 1, &exp10);
788b8e80941Smrg   }
789b8e80941Smrg
790b8e80941Smrg   leftmost_digit = DIV_ROUND_UP(value, exp10);
791b8e80941Smrg
792b8e80941Smrg   /* Round 9 to 10. */
793b8e80941Smrg   if (leftmost_digit == 9) {
794b8e80941Smrg      leftmost_digit = 1;
795b8e80941Smrg      exp10 *= 10;
796b8e80941Smrg      fixup_bytes(pane->type, i + 1, &exp10);
797b8e80941Smrg   }
798b8e80941Smrg
799b8e80941Smrg   switch ((unsigned)leftmost_digit) {
800b8e80941Smrg   case 1:
801b8e80941Smrg      pane->last_line = 5; /* lines in +1/5 increments */
802b8e80941Smrg      break;
803b8e80941Smrg   case 2:
804b8e80941Smrg      pane->last_line = 8; /* lines in +1/4 increments. */
805b8e80941Smrg      break;
806b8e80941Smrg   case 3:
807b8e80941Smrg   case 4:
808b8e80941Smrg      pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments */
809b8e80941Smrg      break;
810b8e80941Smrg   case 5:
811b8e80941Smrg   case 6:
812b8e80941Smrg   case 7:
813b8e80941Smrg   case 8:
814b8e80941Smrg      pane->last_line = leftmost_digit; /* lines in +1 increments */
815b8e80941Smrg      break;
816b8e80941Smrg   default:
817b8e80941Smrg      assert(0);
818b8e80941Smrg   }
819b8e80941Smrg
820b8e80941Smrg   /* Truncate {3,4} to {2.5, 3.5} if possible. */
821b8e80941Smrg   for (i = 3; i <= 4; i++) {
822b8e80941Smrg      if (leftmost_digit == i && value <= (i - 0.5) * exp10) {
823b8e80941Smrg         leftmost_digit = i - 0.5;
824b8e80941Smrg         pane->last_line = leftmost_digit * 2; /* lines in +1/2 increments. */
825b8e80941Smrg      }
826b8e80941Smrg   }
827b8e80941Smrg
828b8e80941Smrg   /* Truncate 2 to a multiple of 0.2 in (1, 1.6] if possible. */
829b8e80941Smrg   if (leftmost_digit == 2) {
830b8e80941Smrg      for (i = 1; i <= 3; i++) {
831b8e80941Smrg         if (value <= (1 + i*0.2) * exp10) {
832b8e80941Smrg            leftmost_digit = 1 + i*0.2;
833b8e80941Smrg            pane->last_line = 5 + i; /* lines in +1/5 increments. */
834b8e80941Smrg            break;
835b8e80941Smrg         }
836b8e80941Smrg      }
837b8e80941Smrg   }
838b8e80941Smrg
839b8e80941Smrg   pane->max_value = leftmost_digit * exp10;
840848b8605Smrg   pane->yscale = -(int)pane->inner_height / (float)pane->max_value;
841848b8605Smrg}
842848b8605Smrg
843b8e80941Smrgstatic void
844b8e80941Smrghud_pane_update_dyn_ceiling(struct hud_graph *gr, struct hud_pane *pane)
845b8e80941Smrg{
846b8e80941Smrg   unsigned i;
847b8e80941Smrg   float tmp = 0.0f;
848b8e80941Smrg
849b8e80941Smrg   if (pane->dyn_ceil_last_ran != gr->index) {
850b8e80941Smrg      LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
851b8e80941Smrg         for (i = 0; i <  gr->num_vertices; ++i) {
852b8e80941Smrg            tmp = gr->vertices[i * 2 + 1] > tmp ?
853b8e80941Smrg                  gr->vertices[i * 2 + 1] : tmp;
854b8e80941Smrg         }
855b8e80941Smrg      }
856b8e80941Smrg
857b8e80941Smrg      /* Avoid setting it lower than the initial starting height. */
858b8e80941Smrg      tmp = tmp > pane->initial_max_value ? tmp : pane->initial_max_value;
859b8e80941Smrg      hud_pane_set_max_value(pane, tmp);
860b8e80941Smrg   }
861b8e80941Smrg
862b8e80941Smrg   /*
863b8e80941Smrg    * Mark this adjustment run so we could avoid repeating a full update
864b8e80941Smrg    * again needlessly in case the pane has more than one graph.
865b8e80941Smrg    */
866b8e80941Smrg   pane->dyn_ceil_last_ran = gr->index;
867b8e80941Smrg}
868b8e80941Smrg
869848b8605Smrgstatic struct hud_pane *
870b8e80941Smrghud_pane_create(struct hud_context *hud,
871b8e80941Smrg                unsigned x1, unsigned y1, unsigned x2, unsigned y2,
872b8e80941Smrg                unsigned y_simple,
873b8e80941Smrg                unsigned period, uint64_t max_value, uint64_t ceiling,
874b8e80941Smrg                boolean dyn_ceiling, boolean sort_items)
875848b8605Smrg{
876848b8605Smrg   struct hud_pane *pane = CALLOC_STRUCT(hud_pane);
877848b8605Smrg
878848b8605Smrg   if (!pane)
879848b8605Smrg      return NULL;
880848b8605Smrg
881b8e80941Smrg   pane->hud = hud;
882848b8605Smrg   pane->x1 = x1;
883848b8605Smrg   pane->y1 = y1;
884848b8605Smrg   pane->x2 = x2;
885848b8605Smrg   pane->y2 = y2;
886b8e80941Smrg   pane->y_simple = y_simple;
887848b8605Smrg   pane->inner_x1 = x1 + 1;
888848b8605Smrg   pane->inner_x2 = x2 - 1;
889848b8605Smrg   pane->inner_y1 = y1 + 1;
890848b8605Smrg   pane->inner_y2 = y2 - 1;
891848b8605Smrg   pane->inner_width = pane->inner_x2 - pane->inner_x1;
892848b8605Smrg   pane->inner_height = pane->inner_y2 - pane->inner_y1;
893848b8605Smrg   pane->period = period;
894848b8605Smrg   pane->max_num_vertices = (x2 - x1 + 2) / 2;
895b8e80941Smrg   pane->ceiling = ceiling;
896b8e80941Smrg   pane->dyn_ceiling = dyn_ceiling;
897b8e80941Smrg   pane->dyn_ceil_last_ran = 0;
898b8e80941Smrg   pane->sort_items = sort_items;
899b8e80941Smrg   pane->initial_max_value = max_value;
900848b8605Smrg   hud_pane_set_max_value(pane, max_value);
901848b8605Smrg   LIST_INITHEAD(&pane->graph_list);
902848b8605Smrg   return pane;
903848b8605Smrg}
904848b8605Smrg
905b8e80941Smrg/* replace '-' with a space */
906b8e80941Smrgstatic void
907b8e80941Smrgstrip_hyphens(char *s)
908b8e80941Smrg{
909b8e80941Smrg   while (*s) {
910b8e80941Smrg      if (*s == '-')
911b8e80941Smrg         *s = ' ';
912b8e80941Smrg      s++;
913b8e80941Smrg   }
914b8e80941Smrg}
915b8e80941Smrg
916848b8605Smrg/**
917848b8605Smrg * Add a graph to an existing pane.
918848b8605Smrg * One pane can contain multiple graphs over each other.
919848b8605Smrg */
920848b8605Smrgvoid
921848b8605Smrghud_pane_add_graph(struct hud_pane *pane, struct hud_graph *gr)
922848b8605Smrg{
923848b8605Smrg   static const float colors[][3] = {
924848b8605Smrg      {0, 1, 0},
925848b8605Smrg      {1, 0, 0},
926848b8605Smrg      {0, 1, 1},
927848b8605Smrg      {1, 0, 1},
928848b8605Smrg      {1, 1, 0},
929b8e80941Smrg      {0.5, 1, 0.5},
930b8e80941Smrg      {1, 0.5, 0.5},
931b8e80941Smrg      {0.5, 1, 1},
932b8e80941Smrg      {1, 0.5, 1},
933b8e80941Smrg      {1, 1, 0.5},
934b8e80941Smrg      {0, 0.5, 0},
935b8e80941Smrg      {0.5, 0, 0},
936b8e80941Smrg      {0, 0.5, 0.5},
937b8e80941Smrg      {0.5, 0, 0.5},
938b8e80941Smrg      {0.5, 0.5, 0},
939848b8605Smrg   };
940b8e80941Smrg   unsigned color = pane->next_color % ARRAY_SIZE(colors);
941848b8605Smrg
942b8e80941Smrg   strip_hyphens(gr->name);
943848b8605Smrg
944848b8605Smrg   gr->vertices = MALLOC(pane->max_num_vertices * sizeof(float) * 2);
945b8e80941Smrg   gr->color[0] = colors[color][0];
946b8e80941Smrg   gr->color[1] = colors[color][1];
947b8e80941Smrg   gr->color[2] = colors[color][2];
948848b8605Smrg   gr->pane = pane;
949848b8605Smrg   LIST_ADDTAIL(&gr->head, &pane->graph_list);
950848b8605Smrg   pane->num_graphs++;
951b8e80941Smrg   pane->next_color++;
952848b8605Smrg}
953848b8605Smrg
954848b8605Smrgvoid
955b8e80941Smrghud_graph_add_value(struct hud_graph *gr, double value)
956848b8605Smrg{
957b8e80941Smrg   gr->current_value = value;
958b8e80941Smrg   value = value > gr->pane->ceiling ? gr->pane->ceiling : value;
959b8e80941Smrg
960b8e80941Smrg   if (gr->fd) {
961b8e80941Smrg      if (fabs(value - lround(value)) > FLT_EPSILON) {
962b8e80941Smrg         fprintf(gr->fd, "%f\n", value);
963b8e80941Smrg      }
964b8e80941Smrg      else {
965b8e80941Smrg         fprintf(gr->fd, "%" PRIu64 "\n", (uint64_t) lround(value));
966b8e80941Smrg      }
967b8e80941Smrg   }
968b8e80941Smrg
969848b8605Smrg   if (gr->index == gr->pane->max_num_vertices) {
970848b8605Smrg      gr->vertices[0] = 0;
971848b8605Smrg      gr->vertices[1] = gr->vertices[(gr->index-1)*2+1];
972848b8605Smrg      gr->index = 1;
973848b8605Smrg   }
974848b8605Smrg   gr->vertices[(gr->index)*2+0] = (float) (gr->index * 2);
975848b8605Smrg   gr->vertices[(gr->index)*2+1] = (float) value;
976848b8605Smrg   gr->index++;
977848b8605Smrg
978848b8605Smrg   if (gr->num_vertices < gr->pane->max_num_vertices) {
979848b8605Smrg      gr->num_vertices++;
980848b8605Smrg   }
981848b8605Smrg
982b8e80941Smrg   if (gr->pane->dyn_ceiling == true) {
983b8e80941Smrg      hud_pane_update_dyn_ceiling(gr, gr->pane);
984b8e80941Smrg   }
985848b8605Smrg   if (value > gr->pane->max_value) {
986848b8605Smrg      hud_pane_set_max_value(gr->pane, value);
987848b8605Smrg   }
988848b8605Smrg}
989848b8605Smrg
990848b8605Smrgstatic void
991b8e80941Smrghud_graph_destroy(struct hud_graph *graph, struct pipe_context *pipe)
992848b8605Smrg{
993848b8605Smrg   FREE(graph->vertices);
994848b8605Smrg   if (graph->free_query_data)
995b8e80941Smrg      graph->free_query_data(graph->query_data, pipe);
996b8e80941Smrg   if (graph->fd)
997b8e80941Smrg      fclose(graph->fd);
998848b8605Smrg   FREE(graph);
999848b8605Smrg}
1000848b8605Smrg
1001b8e80941Smrgstatic void strcat_without_spaces(char *dst, const char *src)
1002b8e80941Smrg{
1003b8e80941Smrg   dst += strlen(dst);
1004b8e80941Smrg   while (*src) {
1005b8e80941Smrg      if (*src == ' ')
1006b8e80941Smrg         *dst++ = '_';
1007b8e80941Smrg      else
1008b8e80941Smrg         *dst++ = *src;
1009b8e80941Smrg      src++;
1010b8e80941Smrg   }
1011b8e80941Smrg   *dst = 0;
1012b8e80941Smrg}
1013b8e80941Smrg
1014b8e80941Smrg
1015b8e80941Smrg#ifdef PIPE_OS_WINDOWS
1016b8e80941Smrg#define W_OK 0
1017b8e80941Smrgstatic int
1018b8e80941Smrgaccess(const char *pathname, int mode)
1019b8e80941Smrg{
1020b8e80941Smrg   /* no-op */
1021b8e80941Smrg   return 0;
1022b8e80941Smrg}
1023b8e80941Smrg
1024b8e80941Smrg#define PATH_SEP "\\"
1025b8e80941Smrg
1026b8e80941Smrg#else
1027b8e80941Smrg
1028b8e80941Smrg#define PATH_SEP "/"
1029b8e80941Smrg
1030b8e80941Smrg#endif
1031b8e80941Smrg
1032b8e80941Smrg
1033b8e80941Smrg/**
1034b8e80941Smrg * If the GALLIUM_HUD_DUMP_DIR env var is set, we'll write the raw
1035b8e80941Smrg * HUD values to files at ${GALLIUM_HUD_DUMP_DIR}/<stat> where <stat>
1036b8e80941Smrg * is a HUD variable such as "fps", or "cpu"
1037b8e80941Smrg */
1038b8e80941Smrgstatic void
1039b8e80941Smrghud_graph_set_dump_file(struct hud_graph *gr)
1040b8e80941Smrg{
1041b8e80941Smrg   const char *hud_dump_dir = getenv("GALLIUM_HUD_DUMP_DIR");
1042b8e80941Smrg
1043b8e80941Smrg   if (hud_dump_dir && access(hud_dump_dir, W_OK) == 0) {
1044b8e80941Smrg      char *dump_file = malloc(strlen(hud_dump_dir) + sizeof(PATH_SEP)
1045b8e80941Smrg                               + sizeof(gr->name));
1046b8e80941Smrg      if (dump_file) {
1047b8e80941Smrg         strcpy(dump_file, hud_dump_dir);
1048b8e80941Smrg         strcat(dump_file, PATH_SEP);
1049b8e80941Smrg         strcat_without_spaces(dump_file, gr->name);
1050b8e80941Smrg         gr->fd = fopen(dump_file, "w+");
1051b8e80941Smrg         if (gr->fd) {
1052b8e80941Smrg            /* flush output after each line is written */
1053b8e80941Smrg            setvbuf(gr->fd, NULL, _IOLBF, 0);
1054b8e80941Smrg         }
1055b8e80941Smrg         free(dump_file);
1056b8e80941Smrg      }
1057b8e80941Smrg   }
1058b8e80941Smrg}
1059b8e80941Smrg
1060848b8605Smrg/**
1061848b8605Smrg * Read a string from the environment variable.
1062848b8605Smrg * The separators "+", ",", ":", and ";" terminate the string.
1063848b8605Smrg * Return the number of read characters.
1064848b8605Smrg */
1065848b8605Smrgstatic int
1066848b8605Smrgparse_string(const char *s, char *out)
1067848b8605Smrg{
1068848b8605Smrg   int i;
1069848b8605Smrg
1070b8e80941Smrg   for (i = 0; *s && *s != '+' && *s != ',' && *s != ':' && *s != ';' && *s != '=';
1071848b8605Smrg        s++, out++, i++)
1072848b8605Smrg      *out = *s;
1073848b8605Smrg
1074848b8605Smrg   *out = 0;
1075848b8605Smrg
1076b8e80941Smrg   if (*s && !i) {
1077848b8605Smrg      fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) while "
1078848b8605Smrg              "parsing a string\n", *s, *s);
1079b8e80941Smrg      fflush(stderr);
1080b8e80941Smrg   }
1081b8e80941Smrg
1082848b8605Smrg   return i;
1083848b8605Smrg}
1084848b8605Smrg
1085b8e80941Smrgstatic char *
1086b8e80941Smrgread_pane_settings(char *str, unsigned * const x, unsigned * const y,
1087b8e80941Smrg               unsigned * const width, unsigned * const height,
1088b8e80941Smrg               uint64_t * const ceiling, boolean * const dyn_ceiling,
1089b8e80941Smrg               boolean *reset_colors, boolean *sort_items)
1090b8e80941Smrg{
1091b8e80941Smrg   char *ret = str;
1092b8e80941Smrg   unsigned tmp;
1093b8e80941Smrg
1094b8e80941Smrg   while (*str == '.') {
1095b8e80941Smrg      ++str;
1096b8e80941Smrg      switch (*str) {
1097b8e80941Smrg      case 'x':
1098b8e80941Smrg         ++str;
1099b8e80941Smrg         *x = strtoul(str, &ret, 10);
1100b8e80941Smrg         str = ret;
1101b8e80941Smrg         break;
1102b8e80941Smrg
1103b8e80941Smrg      case 'y':
1104b8e80941Smrg         ++str;
1105b8e80941Smrg         *y = strtoul(str, &ret, 10);
1106b8e80941Smrg         str = ret;
1107b8e80941Smrg         break;
1108b8e80941Smrg
1109b8e80941Smrg      case 'w':
1110b8e80941Smrg         ++str;
1111b8e80941Smrg         tmp = strtoul(str, &ret, 10);
1112b8e80941Smrg         *width = tmp > 80 ? tmp : 80; /* 80 is chosen arbitrarily */
1113b8e80941Smrg         str = ret;
1114b8e80941Smrg         break;
1115b8e80941Smrg
1116b8e80941Smrg      /*
1117b8e80941Smrg       * Prevent setting height to less than 50. If the height is set to less,
1118b8e80941Smrg       * the text of the Y axis labels on the graph will start overlapping.
1119b8e80941Smrg       */
1120b8e80941Smrg      case 'h':
1121b8e80941Smrg         ++str;
1122b8e80941Smrg         tmp = strtoul(str, &ret, 10);
1123b8e80941Smrg         *height = tmp > 50 ? tmp : 50;
1124b8e80941Smrg         str = ret;
1125b8e80941Smrg         break;
1126b8e80941Smrg
1127b8e80941Smrg      case 'c':
1128b8e80941Smrg         ++str;
1129b8e80941Smrg         tmp = strtoul(str, &ret, 10);
1130b8e80941Smrg         *ceiling = tmp > 10 ? tmp : 10;
1131b8e80941Smrg         str = ret;
1132b8e80941Smrg         break;
1133b8e80941Smrg
1134b8e80941Smrg      case 'd':
1135b8e80941Smrg         ++str;
1136b8e80941Smrg         ret = str;
1137b8e80941Smrg         *dyn_ceiling = true;
1138b8e80941Smrg         break;
1139b8e80941Smrg
1140b8e80941Smrg      case 'r':
1141b8e80941Smrg         ++str;
1142b8e80941Smrg         ret = str;
1143b8e80941Smrg         *reset_colors = true;
1144b8e80941Smrg         break;
1145b8e80941Smrg
1146b8e80941Smrg      case 's':
1147b8e80941Smrg         ++str;
1148b8e80941Smrg         ret = str;
1149b8e80941Smrg         *sort_items = true;
1150b8e80941Smrg         break;
1151b8e80941Smrg
1152b8e80941Smrg      default:
1153b8e80941Smrg         fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *str);
1154b8e80941Smrg         fflush(stderr);
1155b8e80941Smrg      }
1156b8e80941Smrg
1157b8e80941Smrg   }
1158b8e80941Smrg
1159b8e80941Smrg   return ret;
1160b8e80941Smrg}
1161b8e80941Smrg
1162848b8605Smrgstatic boolean
1163848b8605Smrghas_occlusion_query(struct pipe_screen *screen)
1164848b8605Smrg{
1165848b8605Smrg   return screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY) != 0;
1166848b8605Smrg}
1167848b8605Smrg
1168848b8605Smrgstatic boolean
1169848b8605Smrghas_streamout(struct pipe_screen *screen)
1170848b8605Smrg{
1171848b8605Smrg   return screen->get_param(screen, PIPE_CAP_MAX_STREAM_OUTPUT_BUFFERS) != 0;
1172848b8605Smrg}
1173848b8605Smrg
1174848b8605Smrgstatic boolean
1175848b8605Smrghas_pipeline_stats_query(struct pipe_screen *screen)
1176848b8605Smrg{
1177848b8605Smrg   return screen->get_param(screen, PIPE_CAP_QUERY_PIPELINE_STATISTICS) != 0;
1178848b8605Smrg}
1179848b8605Smrg
1180848b8605Smrgstatic void
1181b8e80941Smrghud_parse_env_var(struct hud_context *hud, struct pipe_screen *screen,
1182b8e80941Smrg                  const char *env)
1183848b8605Smrg{
1184848b8605Smrg   unsigned num, i;
1185b8e80941Smrg   char name_a[256], s[256];
1186b8e80941Smrg   char *name;
1187848b8605Smrg   struct hud_pane *pane = NULL;
1188b8e80941Smrg   unsigned x = 10, y = 10, y_simple = 10;
1189848b8605Smrg   unsigned width = 251, height = 100;
1190848b8605Smrg   unsigned period = 500 * 1000;  /* default period (1/2 second) */
1191b8e80941Smrg   uint64_t ceiling = UINT64_MAX;
1192b8e80941Smrg   unsigned column_width = 251;
1193b8e80941Smrg   boolean dyn_ceiling = false;
1194b8e80941Smrg   boolean reset_colors = false;
1195b8e80941Smrg   boolean sort_items = false;
1196848b8605Smrg   const char *period_env;
1197848b8605Smrg
1198b8e80941Smrg   if (util_strncmp(env, "simple,", 7) == 0) {
1199b8e80941Smrg      hud->simple = true;
1200b8e80941Smrg      env += 7;
1201b8e80941Smrg   }
1202b8e80941Smrg
1203848b8605Smrg   /*
1204848b8605Smrg    * The GALLIUM_HUD_PERIOD env var sets the graph update rate.
1205848b8605Smrg    * The env var is in seconds (a float).
1206848b8605Smrg    * Zero means update after every frame.
1207848b8605Smrg    */
1208848b8605Smrg   period_env = getenv("GALLIUM_HUD_PERIOD");
1209848b8605Smrg   if (period_env) {
1210848b8605Smrg      float p = (float) atof(period_env);
1211848b8605Smrg      if (p >= 0.0f) {
1212848b8605Smrg         period = (unsigned) (p * 1000 * 1000);
1213848b8605Smrg      }
1214848b8605Smrg   }
1215848b8605Smrg
1216b8e80941Smrg   while ((num = parse_string(env, name_a)) != 0) {
1217b8e80941Smrg      bool added = true;
1218b8e80941Smrg
1219848b8605Smrg      env += num;
1220848b8605Smrg
1221b8e80941Smrg      /* check for explicit location, size and etc. settings */
1222b8e80941Smrg      name = read_pane_settings(name_a, &x, &y, &width, &height, &ceiling,
1223b8e80941Smrg                                &dyn_ceiling, &reset_colors, &sort_items);
1224b8e80941Smrg
1225b8e80941Smrg     /*
1226b8e80941Smrg      * Keep track of overall column width to avoid pane overlapping in case
1227b8e80941Smrg      * later we create a new column while the bottom pane in the current
1228b8e80941Smrg      * column is less wide than the rest of the panes in it.
1229b8e80941Smrg      */
1230b8e80941Smrg     column_width = width > column_width ? width : column_width;
1231b8e80941Smrg
1232848b8605Smrg      if (!pane) {
1233b8e80941Smrg         pane = hud_pane_create(hud, x, y, x + width, y + height, y_simple,
1234b8e80941Smrg                                period, 10, ceiling, dyn_ceiling, sort_items);
1235848b8605Smrg         if (!pane)
1236848b8605Smrg            return;
1237848b8605Smrg      }
1238848b8605Smrg
1239b8e80941Smrg      if (reset_colors) {
1240b8e80941Smrg         pane->next_color = 0;
1241b8e80941Smrg         reset_colors = false;
1242b8e80941Smrg      }
1243b8e80941Smrg
1244848b8605Smrg      /* Add a graph. */
1245b8e80941Smrg#if defined(HAVE_GALLIUM_EXTRA_HUD) || defined(HAVE_LIBSENSORS)
1246b8e80941Smrg      char arg_name[64];
1247b8e80941Smrg#endif
1248848b8605Smrg      /* IF YOU CHANGE THIS, UPDATE print_help! */
1249848b8605Smrg      if (strcmp(name, "fps") == 0) {
1250848b8605Smrg         hud_fps_graph_install(pane);
1251848b8605Smrg      }
1252b8e80941Smrg      else if (strcmp(name, "frametime") == 0) {
1253b8e80941Smrg         hud_frametime_graph_install(pane);
1254b8e80941Smrg      }
1255848b8605Smrg      else if (strcmp(name, "cpu") == 0) {
1256848b8605Smrg         hud_cpu_graph_install(pane, ALL_CPUS);
1257848b8605Smrg      }
1258848b8605Smrg      else if (sscanf(name, "cpu%u%s", &i, s) == 1) {
1259848b8605Smrg         hud_cpu_graph_install(pane, i);
1260848b8605Smrg      }
1261b8e80941Smrg      else if (strcmp(name, "API-thread-busy") == 0) {
1262b8e80941Smrg         hud_thread_busy_install(pane, name, false);
1263b8e80941Smrg      }
1264b8e80941Smrg      else if (strcmp(name, "API-thread-offloaded-slots") == 0) {
1265b8e80941Smrg         hud_thread_counter_install(pane, name, HUD_COUNTER_OFFLOADED);
1266b8e80941Smrg      }
1267b8e80941Smrg      else if (strcmp(name, "API-thread-direct-slots") == 0) {
1268b8e80941Smrg         hud_thread_counter_install(pane, name, HUD_COUNTER_DIRECT);
1269b8e80941Smrg      }
1270b8e80941Smrg      else if (strcmp(name, "API-thread-num-syncs") == 0) {
1271b8e80941Smrg         hud_thread_counter_install(pane, name, HUD_COUNTER_SYNCS);
1272b8e80941Smrg      }
1273b8e80941Smrg      else if (strcmp(name, "main-thread-busy") == 0) {
1274b8e80941Smrg         hud_thread_busy_install(pane, name, true);
1275b8e80941Smrg      }
1276b8e80941Smrg#ifdef HAVE_GALLIUM_EXTRA_HUD
1277b8e80941Smrg      else if (sscanf(name, "nic-rx-%s", arg_name) == 1) {
1278b8e80941Smrg         hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_RX);
1279b8e80941Smrg      }
1280b8e80941Smrg      else if (sscanf(name, "nic-tx-%s", arg_name) == 1) {
1281b8e80941Smrg         hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_TX);
1282b8e80941Smrg      }
1283b8e80941Smrg      else if (sscanf(name, "nic-rssi-%s", arg_name) == 1) {
1284b8e80941Smrg         hud_nic_graph_install(pane, arg_name, NIC_RSSI_DBM);
1285b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_DBM;
1286b8e80941Smrg      }
1287b8e80941Smrg      else if (sscanf(name, "diskstat-rd-%s", arg_name) == 1) {
1288b8e80941Smrg         hud_diskstat_graph_install(pane, arg_name, DISKSTAT_RD);
1289b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES;
1290b8e80941Smrg      }
1291b8e80941Smrg      else if (sscanf(name, "diskstat-wr-%s", arg_name) == 1) {
1292b8e80941Smrg         hud_diskstat_graph_install(pane, arg_name, DISKSTAT_WR);
1293b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES;
1294b8e80941Smrg      }
1295b8e80941Smrg      else if (sscanf(name, "cpufreq-min-cpu%u", &i) == 1) {
1296b8e80941Smrg         hud_cpufreq_graph_install(pane, i, CPUFREQ_MINIMUM);
1297b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_HZ;
1298b8e80941Smrg      }
1299b8e80941Smrg      else if (sscanf(name, "cpufreq-cur-cpu%u", &i) == 1) {
1300b8e80941Smrg         hud_cpufreq_graph_install(pane, i, CPUFREQ_CURRENT);
1301b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_HZ;
1302b8e80941Smrg      }
1303b8e80941Smrg      else if (sscanf(name, "cpufreq-max-cpu%u", &i) == 1) {
1304b8e80941Smrg         hud_cpufreq_graph_install(pane, i, CPUFREQ_MAXIMUM);
1305b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_HZ;
1306b8e80941Smrg      }
1307b8e80941Smrg#endif
1308b8e80941Smrg#ifdef HAVE_LIBSENSORS
1309b8e80941Smrg      else if (sscanf(name, "sensors_temp_cu-%s", arg_name) == 1) {
1310b8e80941Smrg         hud_sensors_temp_graph_install(pane, arg_name,
1311b8e80941Smrg                                        SENSORS_TEMP_CURRENT);
1312b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE;
1313b8e80941Smrg      }
1314b8e80941Smrg      else if (sscanf(name, "sensors_temp_cr-%s", arg_name) == 1) {
1315b8e80941Smrg         hud_sensors_temp_graph_install(pane, arg_name,
1316b8e80941Smrg                                        SENSORS_TEMP_CRITICAL);
1317b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE;
1318b8e80941Smrg      }
1319b8e80941Smrg      else if (sscanf(name, "sensors_volt_cu-%s", arg_name) == 1) {
1320b8e80941Smrg         hud_sensors_temp_graph_install(pane, arg_name,
1321b8e80941Smrg                                        SENSORS_VOLTAGE_CURRENT);
1322b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_VOLTS;
1323b8e80941Smrg      }
1324b8e80941Smrg      else if (sscanf(name, "sensors_curr_cu-%s", arg_name) == 1) {
1325b8e80941Smrg         hud_sensors_temp_graph_install(pane, arg_name,
1326b8e80941Smrg                                        SENSORS_CURRENT_CURRENT);
1327b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_AMPS;
1328b8e80941Smrg      }
1329b8e80941Smrg      else if (sscanf(name, "sensors_pow_cu-%s", arg_name) == 1) {
1330b8e80941Smrg         hud_sensors_temp_graph_install(pane, arg_name,
1331b8e80941Smrg                                        SENSORS_POWER_CURRENT);
1332b8e80941Smrg         pane->type = PIPE_DRIVER_QUERY_TYPE_WATTS;
1333b8e80941Smrg      }
1334b8e80941Smrg#endif
1335848b8605Smrg      else if (strcmp(name, "samples-passed") == 0 &&
1336b8e80941Smrg               has_occlusion_query(screen)) {
1337b8e80941Smrg         hud_pipe_query_install(&hud->batch_query, pane,
1338b8e80941Smrg                                "samples-passed",
1339b8e80941Smrg                                PIPE_QUERY_OCCLUSION_COUNTER, 0, 0,
1340b8e80941Smrg                                PIPE_DRIVER_QUERY_TYPE_UINT64,
1341b8e80941Smrg                                PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE,
1342b8e80941Smrg                                0);
1343848b8605Smrg      }
1344848b8605Smrg      else if (strcmp(name, "primitives-generated") == 0 &&
1345b8e80941Smrg               has_streamout(screen)) {
1346b8e80941Smrg         hud_pipe_query_install(&hud->batch_query, pane,
1347b8e80941Smrg                                "primitives-generated",
1348b8e80941Smrg                                PIPE_QUERY_PRIMITIVES_GENERATED, 0, 0,
1349b8e80941Smrg                                PIPE_DRIVER_QUERY_TYPE_UINT64,
1350b8e80941Smrg                                PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE,
1351b8e80941Smrg                                0);
1352848b8605Smrg      }
1353848b8605Smrg      else {
1354848b8605Smrg         boolean processed = FALSE;
1355848b8605Smrg
1356848b8605Smrg         /* pipeline statistics queries */
1357b8e80941Smrg         if (has_pipeline_stats_query(screen)) {
1358848b8605Smrg            static const char *pipeline_statistics_names[] =
1359848b8605Smrg            {
1360848b8605Smrg               "ia-vertices",
1361848b8605Smrg               "ia-primitives",
1362848b8605Smrg               "vs-invocations",
1363848b8605Smrg               "gs-invocations",
1364848b8605Smrg               "gs-primitives",
1365848b8605Smrg               "clipper-invocations",
1366848b8605Smrg               "clipper-primitives-generated",
1367848b8605Smrg               "ps-invocations",
1368848b8605Smrg               "hs-invocations",
1369848b8605Smrg               "ds-invocations",
1370848b8605Smrg               "cs-invocations"
1371848b8605Smrg            };
1372b8e80941Smrg            for (i = 0; i < ARRAY_SIZE(pipeline_statistics_names); ++i)
1373848b8605Smrg               if (strcmp(name, pipeline_statistics_names[i]) == 0)
1374848b8605Smrg                  break;
1375b8e80941Smrg            if (i < ARRAY_SIZE(pipeline_statistics_names)) {
1376b8e80941Smrg               hud_pipe_query_install(&hud->batch_query, pane, name,
1377848b8605Smrg                                      PIPE_QUERY_PIPELINE_STATISTICS, i,
1378b8e80941Smrg                                      0, PIPE_DRIVER_QUERY_TYPE_UINT64,
1379b8e80941Smrg                                      PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE,
1380b8e80941Smrg                                      0);
1381848b8605Smrg               processed = TRUE;
1382848b8605Smrg            }
1383848b8605Smrg         }
1384848b8605Smrg
1385848b8605Smrg         /* driver queries */
1386848b8605Smrg         if (!processed) {
1387b8e80941Smrg            if (!hud_driver_query_install(&hud->batch_query, pane,
1388b8e80941Smrg                                          screen, name)) {
1389848b8605Smrg               fprintf(stderr, "gallium_hud: unknown driver query '%s'\n", name);
1390b8e80941Smrg               fflush(stderr);
1391b8e80941Smrg               added = false;
1392848b8605Smrg            }
1393848b8605Smrg         }
1394848b8605Smrg      }
1395848b8605Smrg
1396848b8605Smrg      if (*env == ':') {
1397848b8605Smrg         env++;
1398848b8605Smrg
1399848b8605Smrg         if (!pane) {
1400848b8605Smrg            fprintf(stderr, "gallium_hud: syntax error: unexpected ':', "
1401848b8605Smrg                    "expected a name\n");
1402b8e80941Smrg            fflush(stderr);
1403848b8605Smrg            break;
1404848b8605Smrg         }
1405848b8605Smrg
1406848b8605Smrg         num = parse_string(env, s);
1407848b8605Smrg         env += num;
1408848b8605Smrg
1409848b8605Smrg         if (num && sscanf(s, "%u", &i) == 1) {
1410848b8605Smrg            hud_pane_set_max_value(pane, i);
1411b8e80941Smrg            pane->initial_max_value = i;
1412848b8605Smrg         }
1413848b8605Smrg         else {
1414848b8605Smrg            fprintf(stderr, "gallium_hud: syntax error: unexpected '%c' (%i) "
1415b8e80941Smrg                            "after ':'\n", *env, *env);
1416b8e80941Smrg            fflush(stderr);
1417b8e80941Smrg         }
1418b8e80941Smrg      }
1419b8e80941Smrg
1420b8e80941Smrg      if (*env == '=') {
1421b8e80941Smrg         env++;
1422b8e80941Smrg
1423b8e80941Smrg         if (!pane) {
1424b8e80941Smrg            fprintf(stderr, "gallium_hud: syntax error: unexpected '=', "
1425b8e80941Smrg                    "expected a name\n");
1426b8e80941Smrg            fflush(stderr);
1427b8e80941Smrg            break;
1428b8e80941Smrg         }
1429b8e80941Smrg
1430b8e80941Smrg         num = parse_string(env, s);
1431b8e80941Smrg         env += num;
1432b8e80941Smrg
1433b8e80941Smrg         strip_hyphens(s);
1434b8e80941Smrg         if (added && !LIST_IS_EMPTY(&pane->graph_list)) {
1435b8e80941Smrg            struct hud_graph *graph;
1436b8e80941Smrg            graph = LIST_ENTRY(struct hud_graph, pane->graph_list.prev, head);
1437b8e80941Smrg            strncpy(graph->name, s, sizeof(graph->name)-1);
1438b8e80941Smrg            graph->name[sizeof(graph->name)-1] = 0;
1439848b8605Smrg         }
1440848b8605Smrg      }
1441848b8605Smrg
1442848b8605Smrg      if (*env == 0)
1443848b8605Smrg         break;
1444848b8605Smrg
1445848b8605Smrg      /* parse a separator */
1446848b8605Smrg      switch (*env) {
1447848b8605Smrg      case '+':
1448848b8605Smrg         env++;
1449848b8605Smrg         break;
1450848b8605Smrg
1451848b8605Smrg      case ',':
1452848b8605Smrg         env++;
1453b8e80941Smrg         if (!pane)
1454b8e80941Smrg            break;
1455b8e80941Smrg
1456848b8605Smrg         y += height + hud->font.glyph_height * (pane->num_graphs + 2);
1457b8e80941Smrg         y_simple += hud->font.glyph_height * (pane->num_graphs + 1);
1458b8e80941Smrg         height = 100;
1459848b8605Smrg
1460848b8605Smrg         if (pane && pane->num_graphs) {
1461848b8605Smrg            LIST_ADDTAIL(&pane->head, &hud->pane_list);
1462848b8605Smrg            pane = NULL;
1463848b8605Smrg         }
1464848b8605Smrg         break;
1465848b8605Smrg
1466848b8605Smrg      case ';':
1467848b8605Smrg         env++;
1468848b8605Smrg         y = 10;
1469b8e80941Smrg         y_simple = 10;
1470b8e80941Smrg         x += column_width + hud->font.glyph_width * 9;
1471b8e80941Smrg         height = 100;
1472848b8605Smrg
1473848b8605Smrg         if (pane && pane->num_graphs) {
1474848b8605Smrg            LIST_ADDTAIL(&pane->head, &hud->pane_list);
1475848b8605Smrg            pane = NULL;
1476848b8605Smrg         }
1477b8e80941Smrg
1478b8e80941Smrg         /* Starting a new column; reset column width. */
1479b8e80941Smrg         column_width = 251;
1480848b8605Smrg         break;
1481848b8605Smrg
1482848b8605Smrg      default:
1483848b8605Smrg         fprintf(stderr, "gallium_hud: syntax error: unexpected '%c'\n", *env);
1484b8e80941Smrg         fflush(stderr);
1485848b8605Smrg      }
1486b8e80941Smrg
1487b8e80941Smrg      /* Reset to defaults for the next pane in case these were modified. */
1488b8e80941Smrg      width = 251;
1489b8e80941Smrg      ceiling = UINT64_MAX;
1490b8e80941Smrg      dyn_ceiling = false;
1491b8e80941Smrg      sort_items = false;
1492b8e80941Smrg
1493848b8605Smrg   }
1494848b8605Smrg
1495848b8605Smrg   if (pane) {
1496848b8605Smrg      if (pane->num_graphs) {
1497848b8605Smrg         LIST_ADDTAIL(&pane->head, &hud->pane_list);
1498848b8605Smrg      }
1499848b8605Smrg      else {
1500848b8605Smrg         FREE(pane);
1501848b8605Smrg      }
1502848b8605Smrg   }
1503b8e80941Smrg
1504b8e80941Smrg   LIST_FOR_EACH_ENTRY(pane, &hud->pane_list, head) {
1505b8e80941Smrg      struct hud_graph *gr;
1506b8e80941Smrg
1507b8e80941Smrg      LIST_FOR_EACH_ENTRY(gr, &pane->graph_list, head) {
1508b8e80941Smrg         hud_graph_set_dump_file(gr);
1509b8e80941Smrg      }
1510b8e80941Smrg   }
1511848b8605Smrg}
1512848b8605Smrg
1513848b8605Smrgstatic void
1514848b8605Smrgprint_help(struct pipe_screen *screen)
1515848b8605Smrg{
1516848b8605Smrg   int i, num_queries, num_cpus = hud_get_num_cpus();
1517848b8605Smrg
1518848b8605Smrg   puts("Syntax: GALLIUM_HUD=name1[+name2][...][:value1][,nameI...][;nameJ...]");
1519848b8605Smrg   puts("");
1520848b8605Smrg   puts("  Names are identifiers of data sources which will be drawn as graphs");
1521848b8605Smrg   puts("  in panes. Multiple graphs can be drawn in the same pane.");
1522848b8605Smrg   puts("  There can be multiple panes placed in rows and columns.");
1523848b8605Smrg   puts("");
1524848b8605Smrg   puts("  '+' separates names which will share a pane.");
1525848b8605Smrg   puts("  ':[value]' specifies the initial maximum value of the Y axis");
1526848b8605Smrg   puts("             for the given pane.");
1527848b8605Smrg   puts("  ',' creates a new pane below the last one.");
1528848b8605Smrg   puts("  ';' creates a new pane at the top of the next column.");
1529b8e80941Smrg   puts("  '=' followed by a string, changes the name of the last data source");
1530b8e80941Smrg   puts("      to that string");
1531848b8605Smrg   puts("");
1532848b8605Smrg   puts("  Example: GALLIUM_HUD=\"cpu,fps;primitives-generated\"");
1533848b8605Smrg   puts("");
1534b8e80941Smrg   puts("  Additionally, by prepending '.[identifier][value]' modifiers to");
1535b8e80941Smrg   puts("  a name, it is possible to explicitly set the location and size");
1536b8e80941Smrg   puts("  of a pane, along with limiting overall maximum value of the");
1537b8e80941Smrg   puts("  Y axis and activating dynamic readjustment of the Y axis.");
1538b8e80941Smrg   puts("  Several modifiers may be applied to the same pane simultaneously.");
1539b8e80941Smrg   puts("");
1540b8e80941Smrg   puts("  'x[value]' sets the location of the pane on the x axis relative");
1541b8e80941Smrg   puts("             to the upper-left corner of the viewport, in pixels.");
1542b8e80941Smrg   puts("  'y[value]' sets the location of the pane on the y axis relative");
1543b8e80941Smrg   puts("             to the upper-left corner of the viewport, in pixels.");
1544b8e80941Smrg   puts("  'w[value]' sets width of the graph pixels.");
1545b8e80941Smrg   puts("  'h[value]' sets height of the graph in pixels.");
1546b8e80941Smrg   puts("  'c[value]' sets the ceiling of the value of the Y axis.");
1547b8e80941Smrg   puts("             If the graph needs to draw values higher than");
1548b8e80941Smrg   puts("             the ceiling allows, the value is clamped.");
1549b8e80941Smrg   puts("  'd' activates dynamic Y axis readjustment to set the value of");
1550b8e80941Smrg   puts("      the Y axis to match the highest value still visible in the graph.");
1551b8e80941Smrg   puts("  'r' resets the color counter (the next color will be green)");
1552b8e80941Smrg   puts("  's' sort items below graphs in descending order");
1553b8e80941Smrg   puts("");
1554b8e80941Smrg   puts("  If 'c' and 'd' modifiers are used simultaneously, both are in effect:");
1555b8e80941Smrg   puts("  the Y axis does not go above the restriction imposed by 'c' while");
1556b8e80941Smrg   puts("  still adjusting the value of the Y axis down when appropriate.");
1557b8e80941Smrg   puts("");
1558b8e80941Smrg   puts("  You can change behavior of the whole HUD by adding these options at");
1559b8e80941Smrg   puts("  the beginning of the environment variable:");
1560b8e80941Smrg   puts("  'simple,' disables all the fancy stuff and only draws text.");
1561b8e80941Smrg   puts("");
1562b8e80941Smrg   puts("  Example: GALLIUM_HUD=\".w256.h64.x1600.y520.d.c1000fps+cpu,.datom-count\"");
1563b8e80941Smrg   puts("");
1564848b8605Smrg   puts("  Available names:");
1565848b8605Smrg   puts("    fps");
1566b8e80941Smrg   puts("    frametime");
1567848b8605Smrg   puts("    cpu");
1568848b8605Smrg
1569848b8605Smrg   for (i = 0; i < num_cpus; i++)
1570848b8605Smrg      printf("    cpu%i\n", i);
1571848b8605Smrg
1572848b8605Smrg   if (has_occlusion_query(screen))
1573848b8605Smrg      puts("    samples-passed");
1574848b8605Smrg   if (has_streamout(screen))
1575848b8605Smrg      puts("    primitives-generated");
1576848b8605Smrg
1577848b8605Smrg   if (has_pipeline_stats_query(screen)) {
1578848b8605Smrg      puts("    ia-vertices");
1579848b8605Smrg      puts("    ia-primitives");
1580848b8605Smrg      puts("    vs-invocations");
1581848b8605Smrg      puts("    gs-invocations");
1582848b8605Smrg      puts("    gs-primitives");
1583848b8605Smrg      puts("    clipper-invocations");
1584848b8605Smrg      puts("    clipper-primitives-generated");
1585848b8605Smrg      puts("    ps-invocations");
1586848b8605Smrg      puts("    hs-invocations");
1587848b8605Smrg      puts("    ds-invocations");
1588848b8605Smrg      puts("    cs-invocations");
1589848b8605Smrg   }
1590848b8605Smrg
1591b8e80941Smrg#ifdef HAVE_GALLIUM_EXTRA_HUD
1592b8e80941Smrg   hud_get_num_disks(1);
1593b8e80941Smrg   hud_get_num_nics(1);
1594b8e80941Smrg   hud_get_num_cpufreq(1);
1595b8e80941Smrg#endif
1596b8e80941Smrg#ifdef HAVE_LIBSENSORS
1597b8e80941Smrg   hud_get_num_sensors(1);
1598b8e80941Smrg#endif
1599b8e80941Smrg
1600848b8605Smrg   if (screen->get_driver_query_info){
1601b8e80941Smrg      boolean skipping = false;
1602848b8605Smrg      struct pipe_driver_query_info info;
1603848b8605Smrg      num_queries = screen->get_driver_query_info(screen, 0, NULL);
1604848b8605Smrg
1605848b8605Smrg      for (i = 0; i < num_queries; i++){
1606848b8605Smrg         screen->get_driver_query_info(screen, i, &info);
1607b8e80941Smrg         if (info.flags & PIPE_DRIVER_QUERY_FLAG_DONT_LIST) {
1608b8e80941Smrg            if (!skipping)
1609b8e80941Smrg               puts("    ...");
1610b8e80941Smrg            skipping = true;
1611b8e80941Smrg         } else {
1612b8e80941Smrg            printf("    %s\n", info.name);
1613b8e80941Smrg            skipping = false;
1614b8e80941Smrg         }
1615848b8605Smrg      }
1616848b8605Smrg   }
1617848b8605Smrg
1618848b8605Smrg   puts("");
1619b8e80941Smrg   fflush(stdout);
1620848b8605Smrg}
1621848b8605Smrg
1622b8e80941Smrgstatic void
1623b8e80941Smrghud_unset_draw_context(struct hud_context *hud)
1624848b8605Smrg{
1625b8e80941Smrg   struct pipe_context *pipe = hud->pipe;
1626848b8605Smrg
1627b8e80941Smrg   if (!pipe)
1628b8e80941Smrg      return;
1629848b8605Smrg
1630b8e80941Smrg   pipe_sampler_view_reference(&hud->font_sampler_view, NULL);
1631b8e80941Smrg
1632b8e80941Smrg   if (hud->fs_color) {
1633b8e80941Smrg      pipe->delete_fs_state(pipe, hud->fs_color);
1634b8e80941Smrg      hud->fs_color = NULL;
1635b8e80941Smrg   }
1636b8e80941Smrg   if (hud->fs_text) {
1637b8e80941Smrg      pipe->delete_fs_state(pipe, hud->fs_text);
1638b8e80941Smrg      hud->fs_text = NULL;
1639b8e80941Smrg   }
1640b8e80941Smrg   if (hud->vs) {
1641b8e80941Smrg      pipe->delete_vs_state(pipe, hud->vs);
1642b8e80941Smrg      hud->vs = NULL;
1643848b8605Smrg   }
1644848b8605Smrg
1645b8e80941Smrg   hud->cso = NULL;
1646b8e80941Smrg   hud->pipe = NULL;
1647b8e80941Smrg}
1648848b8605Smrg
1649b8e80941Smrgstatic bool
1650b8e80941Smrghud_set_draw_context(struct hud_context *hud, struct cso_context *cso)
1651b8e80941Smrg{
1652b8e80941Smrg   struct pipe_context *pipe = cso_get_pipe_context(cso);
1653b8e80941Smrg
1654b8e80941Smrg   assert(!hud->pipe);
1655848b8605Smrg   hud->pipe = pipe;
1656848b8605Smrg   hud->cso = cso;
1657848b8605Smrg
1658b8e80941Smrg   struct pipe_sampler_view view_templ;
1659b8e80941Smrg   u_sampler_view_default_template(
1660b8e80941Smrg         &view_templ, hud->font.texture, hud->font.texture->format);
1661b8e80941Smrg   hud->font_sampler_view = pipe->create_sampler_view(pipe, hud->font.texture,
1662b8e80941Smrg                                                      &view_templ);
1663b8e80941Smrg   if (!hud->font_sampler_view)
1664b8e80941Smrg      goto fail;
1665848b8605Smrg
1666848b8605Smrg   /* fragment shader */
1667848b8605Smrg   hud->fs_color =
1668848b8605Smrg         util_make_fragment_passthrough_shader(pipe,
1669848b8605Smrg                                               TGSI_SEMANTIC_COLOR,
1670848b8605Smrg                                               TGSI_INTERPOLATE_CONSTANT,
1671848b8605Smrg                                               TRUE);
1672848b8605Smrg
1673848b8605Smrg   {
1674848b8605Smrg      /* Read a texture and do .xxxx swizzling. */
1675848b8605Smrg      static const char *fragment_shader_text = {
1676848b8605Smrg         "FRAG\n"
1677848b8605Smrg         "DCL IN[0], GENERIC[0], LINEAR\n"
1678848b8605Smrg         "DCL SAMP[0]\n"
1679b8e80941Smrg         "DCL SVIEW[0], RECT, FLOAT\n"
1680848b8605Smrg         "DCL OUT[0], COLOR[0]\n"
1681848b8605Smrg         "DCL TEMP[0]\n"
1682848b8605Smrg
1683848b8605Smrg         "TEX TEMP[0], IN[0], SAMP[0], RECT\n"
1684848b8605Smrg         "MOV OUT[0], TEMP[0].xxxx\n"
1685848b8605Smrg         "END\n"
1686848b8605Smrg      };
1687848b8605Smrg
1688848b8605Smrg      struct tgsi_token tokens[1000];
1689b8e80941Smrg      struct pipe_shader_state state;
1690848b8605Smrg
1691b8e80941Smrg      if (!tgsi_text_translate(fragment_shader_text, tokens, ARRAY_SIZE(tokens))) {
1692848b8605Smrg         assert(0);
1693b8e80941Smrg         goto fail;
1694848b8605Smrg      }
1695b8e80941Smrg      pipe_shader_state_from_tgsi(&state, tokens);
1696848b8605Smrg      hud->fs_text = pipe->create_fs_state(pipe, &state);
1697848b8605Smrg   }
1698848b8605Smrg
1699848b8605Smrg   /* vertex shader */
1700848b8605Smrg   {
1701848b8605Smrg      static const char *vertex_shader_text = {
1702848b8605Smrg         "VERT\n"
1703848b8605Smrg         "DCL IN[0..1]\n"
1704848b8605Smrg         "DCL OUT[0], POSITION\n"
1705848b8605Smrg         "DCL OUT[1], COLOR[0]\n" /* color */
1706848b8605Smrg         "DCL OUT[2], GENERIC[0]\n" /* texcoord */
1707848b8605Smrg         /* [0] = color,
1708848b8605Smrg          * [1] = (2/fb_width, 2/fb_height, xoffset, yoffset)
1709848b8605Smrg          * [2] = (xscale, yscale, 0, 0) */
1710b8e80941Smrg         "DCL CONST[0][0..2]\n"
1711848b8605Smrg         "DCL TEMP[0]\n"
1712848b8605Smrg         "IMM[0] FLT32 { -1, 0, 0, 1 }\n"
1713848b8605Smrg
1714848b8605Smrg         /* v = in * (xscale, yscale) + (xoffset, yoffset) */
1715b8e80941Smrg         "MAD TEMP[0].xy, IN[0], CONST[0][2].xyyy, CONST[0][1].zwww\n"
1716848b8605Smrg         /* pos = v * (2 / fb_width, 2 / fb_height) - (1, 1) */
1717b8e80941Smrg         "MAD OUT[0].xy, TEMP[0], CONST[0][1].xyyy, IMM[0].xxxx\n"
1718848b8605Smrg         "MOV OUT[0].zw, IMM[0]\n"
1719848b8605Smrg
1720b8e80941Smrg         "MOV OUT[1], CONST[0][0]\n"
1721848b8605Smrg         "MOV OUT[2], IN[1]\n"
1722848b8605Smrg         "END\n"
1723848b8605Smrg      };
1724848b8605Smrg
1725848b8605Smrg      struct tgsi_token tokens[1000];
1726b8e80941Smrg      struct pipe_shader_state state;
1727b8e80941Smrg      if (!tgsi_text_translate(vertex_shader_text, tokens, ARRAY_SIZE(tokens))) {
1728848b8605Smrg         assert(0);
1729b8e80941Smrg         goto fail;
1730848b8605Smrg      }
1731b8e80941Smrg      pipe_shader_state_from_tgsi(&state, tokens);
1732848b8605Smrg      hud->vs = pipe->create_vs_state(pipe, &state);
1733848b8605Smrg   }
1734848b8605Smrg
1735b8e80941Smrg   return true;
1736b8e80941Smrg
1737b8e80941Smrgfail:
1738b8e80941Smrg   hud_unset_draw_context(hud);
1739b8e80941Smrg   fprintf(stderr, "hud: failed to set a draw context");
1740b8e80941Smrg   return false;
1741b8e80941Smrg}
1742b8e80941Smrg
1743b8e80941Smrgstatic void
1744b8e80941Smrghud_unset_record_context(struct hud_context *hud)
1745b8e80941Smrg{
1746b8e80941Smrg   struct pipe_context *pipe = hud->record_pipe;
1747b8e80941Smrg   struct hud_pane *pane, *pane_tmp;
1748b8e80941Smrg   struct hud_graph *graph, *graph_tmp;
1749b8e80941Smrg
1750b8e80941Smrg   if (!pipe)
1751b8e80941Smrg      return;
1752b8e80941Smrg
1753b8e80941Smrg   LIST_FOR_EACH_ENTRY_SAFE(pane, pane_tmp, &hud->pane_list, head) {
1754b8e80941Smrg      LIST_FOR_EACH_ENTRY_SAFE(graph, graph_tmp, &pane->graph_list, head) {
1755b8e80941Smrg         LIST_DEL(&graph->head);
1756b8e80941Smrg         hud_graph_destroy(graph, pipe);
1757b8e80941Smrg      }
1758b8e80941Smrg      LIST_DEL(&pane->head);
1759b8e80941Smrg      FREE(pane);
1760b8e80941Smrg   }
1761b8e80941Smrg
1762b8e80941Smrg   hud_batch_query_cleanup(&hud->batch_query, pipe);
1763b8e80941Smrg   hud->record_pipe = NULL;
1764b8e80941Smrg}
1765b8e80941Smrg
1766b8e80941Smrgstatic void
1767b8e80941Smrghud_set_record_context(struct hud_context *hud, struct pipe_context *pipe)
1768b8e80941Smrg{
1769b8e80941Smrg   hud->record_pipe = pipe;
1770b8e80941Smrg}
1771b8e80941Smrg
1772b8e80941Smrg/**
1773b8e80941Smrg * Create the HUD.
1774b8e80941Smrg *
1775b8e80941Smrg * If "share" is non-NULL and GALLIUM_HUD_SHARE=x,y is set, increment the
1776b8e80941Smrg * reference counter of "share", set "cso" as the recording or drawing context
1777b8e80941Smrg * according to the environment variable, and return "share".
1778b8e80941Smrg * This allows sharing the HUD instance within a multi-context share group,
1779b8e80941Smrg * record queries in one context and draw them in another.
1780b8e80941Smrg */
1781b8e80941Smrgstruct hud_context *
1782b8e80941Smrghud_create(struct cso_context *cso, struct hud_context *share)
1783b8e80941Smrg{
1784b8e80941Smrg   const char *share_env = debug_get_option("GALLIUM_HUD_SHARE", NULL);
1785b8e80941Smrg   unsigned record_ctx = 0, draw_ctx = 0;
1786b8e80941Smrg
1787b8e80941Smrg   if (share_env && sscanf(share_env, "%u,%u", &record_ctx, &draw_ctx) != 2)
1788b8e80941Smrg      share_env = NULL;
1789b8e80941Smrg
1790b8e80941Smrg   if (share && share_env) {
1791b8e80941Smrg      /* All contexts in a share group share the HUD instance.
1792b8e80941Smrg       * Only one context can record queries and only one context
1793b8e80941Smrg       * can draw the HUD.
1794b8e80941Smrg       *
1795b8e80941Smrg       * GALLIUM_HUD_SHARE=x,y determines the context indices.
1796b8e80941Smrg       */
1797b8e80941Smrg      int context_id = p_atomic_inc_return(&share->refcount) - 1;
1798b8e80941Smrg
1799b8e80941Smrg      if (context_id == record_ctx) {
1800b8e80941Smrg         assert(!share->record_pipe);
1801b8e80941Smrg         hud_set_record_context(share, cso_get_pipe_context(cso));
1802b8e80941Smrg      }
1803b8e80941Smrg
1804b8e80941Smrg      if (context_id == draw_ctx) {
1805b8e80941Smrg         assert(!share->pipe);
1806b8e80941Smrg         hud_set_draw_context(share, cso);
1807b8e80941Smrg      }
1808b8e80941Smrg
1809b8e80941Smrg      return share;
1810b8e80941Smrg   }
1811b8e80941Smrg
1812b8e80941Smrg   struct pipe_screen *screen = cso_get_pipe_context(cso)->screen;
1813b8e80941Smrg   struct hud_context *hud;
1814b8e80941Smrg   unsigned i;
1815b8e80941Smrg   const char *env = debug_get_option("GALLIUM_HUD", NULL);
1816b8e80941Smrg#ifdef PIPE_OS_UNIX
1817b8e80941Smrg   unsigned signo = debug_get_num_option("GALLIUM_HUD_TOGGLE_SIGNAL", 0);
1818b8e80941Smrg   static boolean sig_handled = FALSE;
1819b8e80941Smrg   struct sigaction action = {};
1820b8e80941Smrg#endif
1821b8e80941Smrg   huds_visible = debug_get_bool_option("GALLIUM_HUD_VISIBLE", TRUE);
1822b8e80941Smrg
1823b8e80941Smrg   if (!env || !*env)
1824b8e80941Smrg      return NULL;
1825b8e80941Smrg
1826b8e80941Smrg   if (strcmp(env, "help") == 0) {
1827b8e80941Smrg      print_help(screen);
1828b8e80941Smrg      return NULL;
1829b8e80941Smrg   }
1830b8e80941Smrg
1831b8e80941Smrg   hud = CALLOC_STRUCT(hud_context);
1832b8e80941Smrg   if (!hud)
1833b8e80941Smrg      return NULL;
1834b8e80941Smrg
1835b8e80941Smrg   /* font (the context is only used for the texture upload) */
1836b8e80941Smrg   if (!util_font_create(cso_get_pipe_context(cso),
1837b8e80941Smrg                         UTIL_FONT_FIXED_8X13, &hud->font)) {
1838b8e80941Smrg      FREE(hud);
1839b8e80941Smrg      return NULL;
1840b8e80941Smrg   }
1841b8e80941Smrg
1842b8e80941Smrg   hud->refcount = 1;
1843b8e80941Smrg   hud->has_srgb = screen->is_format_supported(screen,
1844b8e80941Smrg                                               PIPE_FORMAT_B8G8R8A8_SRGB,
1845b8e80941Smrg                                               PIPE_TEXTURE_2D, 0, 0,
1846b8e80941Smrg                                               PIPE_BIND_RENDER_TARGET) != 0;
1847b8e80941Smrg
1848b8e80941Smrg   /* blend state */
1849b8e80941Smrg   hud->no_blend.rt[0].colormask = PIPE_MASK_RGBA;
1850b8e80941Smrg
1851b8e80941Smrg   hud->alpha_blend.rt[0].colormask = PIPE_MASK_RGBA;
1852b8e80941Smrg   hud->alpha_blend.rt[0].blend_enable = 1;
1853b8e80941Smrg   hud->alpha_blend.rt[0].rgb_func = PIPE_BLEND_ADD;
1854b8e80941Smrg   hud->alpha_blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA;
1855b8e80941Smrg   hud->alpha_blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA;
1856b8e80941Smrg   hud->alpha_blend.rt[0].alpha_func = PIPE_BLEND_ADD;
1857b8e80941Smrg   hud->alpha_blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO;
1858b8e80941Smrg   hud->alpha_blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE;
1859b8e80941Smrg
1860b8e80941Smrg   /* rasterizer */
1861b8e80941Smrg   hud->rasterizer.half_pixel_center = 1;
1862b8e80941Smrg   hud->rasterizer.bottom_edge_rule = 1;
1863b8e80941Smrg   hud->rasterizer.depth_clip_near = 1;
1864b8e80941Smrg   hud->rasterizer.depth_clip_far = 1;
1865b8e80941Smrg   hud->rasterizer.line_width = 1;
1866b8e80941Smrg   hud->rasterizer.line_last_pixel = 1;
1867b8e80941Smrg
1868b8e80941Smrg   hud->rasterizer_aa_lines = hud->rasterizer;
1869b8e80941Smrg   hud->rasterizer_aa_lines.line_smooth = 1;
1870b8e80941Smrg
1871848b8605Smrg   /* vertex elements */
1872848b8605Smrg   for (i = 0; i < 2; i++) {
1873848b8605Smrg      hud->velems[i].src_offset = i * 2 * sizeof(float);
1874848b8605Smrg      hud->velems[i].src_format = PIPE_FORMAT_R32G32_FLOAT;
1875b8e80941Smrg      hud->velems[i].vertex_buffer_index = 0;
1876848b8605Smrg   }
1877848b8605Smrg
1878848b8605Smrg   /* sampler state (for font drawing) */
1879848b8605Smrg   hud->font_sampler_state.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
1880848b8605Smrg   hud->font_sampler_state.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
1881848b8605Smrg   hud->font_sampler_state.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
1882848b8605Smrg   hud->font_sampler_state.normalized_coords = 0;
1883848b8605Smrg
1884848b8605Smrg   /* constants */
1885848b8605Smrg   hud->constbuf.buffer_size = sizeof(hud->constants);
1886848b8605Smrg   hud->constbuf.user_buffer = &hud->constants;
1887848b8605Smrg
1888848b8605Smrg   LIST_INITHEAD(&hud->pane_list);
1889848b8605Smrg
1890b8e80941Smrg   /* setup sig handler once for all hud contexts */
1891b8e80941Smrg#ifdef PIPE_OS_UNIX
1892b8e80941Smrg   if (!sig_handled && signo != 0) {
1893b8e80941Smrg      action.sa_sigaction = &signal_visible_handler;
1894b8e80941Smrg      action.sa_flags = SA_SIGINFO;
1895b8e80941Smrg
1896b8e80941Smrg      if (signo >= NSIG)
1897b8e80941Smrg         fprintf(stderr, "gallium_hud: invalid signal %u\n", signo);
1898b8e80941Smrg      else if (sigaction(signo, &action, NULL) < 0)
1899b8e80941Smrg         fprintf(stderr, "gallium_hud: unable to set handler for signal %u\n", signo);
1900b8e80941Smrg      fflush(stderr);
1901b8e80941Smrg
1902b8e80941Smrg      sig_handled = TRUE;
1903b8e80941Smrg   }
1904b8e80941Smrg#endif
1905b8e80941Smrg
1906b8e80941Smrg   if (record_ctx == 0)
1907b8e80941Smrg      hud_set_record_context(hud, cso_get_pipe_context(cso));
1908b8e80941Smrg   if (draw_ctx == 0)
1909b8e80941Smrg      hud_set_draw_context(hud, cso);
1910b8e80941Smrg
1911b8e80941Smrg   hud_parse_env_var(hud, screen, env);
1912848b8605Smrg   return hud;
1913848b8605Smrg}
1914848b8605Smrg
1915b8e80941Smrg/**
1916b8e80941Smrg * Destroy a HUD. If the HUD has several users, decrease the reference counter
1917b8e80941Smrg * and detach the context from the HUD.
1918b8e80941Smrg */
1919848b8605Smrgvoid
1920b8e80941Smrghud_destroy(struct hud_context *hud, struct cso_context *cso)
1921848b8605Smrg{
1922b8e80941Smrg   if (!cso || hud->record_pipe == cso_get_pipe_context(cso))
1923b8e80941Smrg      hud_unset_record_context(hud);
1924848b8605Smrg
1925b8e80941Smrg   if (!cso || hud->cso == cso)
1926b8e80941Smrg      hud_unset_draw_context(hud);
1927b8e80941Smrg
1928b8e80941Smrg   if (p_atomic_dec_zero(&hud->refcount)) {
1929b8e80941Smrg      pipe_resource_reference(&hud->font.texture, NULL);
1930b8e80941Smrg      FREE(hud);
1931848b8605Smrg   }
1932b8e80941Smrg}
1933848b8605Smrg
1934b8e80941Smrgvoid
1935b8e80941Smrghud_add_queue_for_monitoring(struct hud_context *hud,
1936b8e80941Smrg                             struct util_queue_monitoring *queue_info)
1937b8e80941Smrg{
1938b8e80941Smrg   assert(!hud->monitored_queue);
1939b8e80941Smrg   hud->monitored_queue = queue_info;
1940848b8605Smrg}
1941