1 /**************************************************************************
3 * Copyright 2013 Marek Olšák <maraeo@gmail.com>
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
28 /* This file contains code for reading values from pipe queries
29 * for displaying on the HUD. To prevent stalls when reading queries, we
30 * keep a list of busy queries in a ring. We read only those queries which
34 #include "hud/hud_private.h"
35 #include "pipe/p_screen.h"
36 #include "util/os_time.h"
37 #include "util/u_math.h"
38 #include "util/u_memory.h"
41 // Must be a power of two
44 struct hud_batch_query_context
{
45 unsigned num_query_types
;
46 unsigned allocated_query_types
;
47 unsigned *query_types
;
50 struct pipe_query
*query
[NUM_QUERIES
];
51 union pipe_query_result
*result
[NUM_QUERIES
];
52 unsigned head
, pending
, results
;
56 hud_batch_query_update(struct hud_batch_query_context
*bq
,
57 struct pipe_context
*pipe
)
59 if (!bq
|| bq
->failed
)
62 if (bq
->query
[bq
->head
])
63 pipe
->end_query(pipe
, bq
->query
[bq
->head
]);
68 unsigned idx
= (bq
->head
- bq
->pending
+ 1) % NUM_QUERIES
;
69 struct pipe_query
*query
= bq
->query
[idx
];
72 bq
->result
[idx
] = MALLOC(sizeof(bq
->result
[idx
]->batch
[0]) *
74 if (!bq
->result
[idx
]) {
75 fprintf(stderr
, "gallium_hud: out of memory.\n");
80 if (!pipe
->get_query_result(pipe
, query
, FALSE
, bq
->result
[idx
]))
87 bq
->head
= (bq
->head
+ 1) % NUM_QUERIES
;
89 if (bq
->pending
== NUM_QUERIES
) {
91 "gallium_hud: all queries busy after %i frames, dropping data.\n",
94 assert(bq
->query
[bq
->head
]);
96 pipe
->destroy_query(pipe
, bq
->query
[bq
->head
]);
97 bq
->query
[bq
->head
] = NULL
;
102 if (!bq
->query
[bq
->head
]) {
103 bq
->query
[bq
->head
] = pipe
->create_batch_query(pipe
,
107 if (!bq
->query
[bq
->head
]) {
109 "gallium_hud: create_batch_query failed. You may have "
110 "selected too many or incompatible queries.\n");
118 hud_batch_query_begin(struct hud_batch_query_context
*bq
,
119 struct pipe_context
*pipe
)
121 if (!bq
|| bq
->failed
|| !bq
->query
[bq
->head
])
124 if (!pipe
->begin_query(pipe
, bq
->query
[bq
->head
])) {
126 "gallium_hud: could not begin batch query. You may have "
127 "selected too many or incompatible queries.\n");
133 batch_query_add(struct hud_batch_query_context
**pbq
,
134 unsigned query_type
, unsigned *result_index
)
136 struct hud_batch_query_context
*bq
= *pbq
;
140 bq
= CALLOC_STRUCT(hud_batch_query_context
);
146 for (i
= 0; i
< bq
->num_query_types
; ++i
) {
147 if (bq
->query_types
[i
] == query_type
) {
153 if (bq
->num_query_types
== bq
->allocated_query_types
) {
154 unsigned new_alloc
= MAX2(16, bq
->allocated_query_types
* 2);
155 unsigned *new_query_types
156 = REALLOC(bq
->query_types
,
157 bq
->allocated_query_types
* sizeof(unsigned),
158 new_alloc
* sizeof(unsigned));
159 if (!new_query_types
)
161 bq
->query_types
= new_query_types
;
162 bq
->allocated_query_types
= new_alloc
;
165 bq
->query_types
[bq
->num_query_types
] = query_type
;
166 *result_index
= bq
->num_query_types
++;
171 hud_batch_query_cleanup(struct hud_batch_query_context
**pbq
,
172 struct pipe_context
*pipe
)
174 struct hud_batch_query_context
*bq
= *pbq
;
182 if (bq
->query
[bq
->head
] && !bq
->failed
)
183 pipe
->end_query(pipe
, bq
->query
[bq
->head
]);
185 for (idx
= 0; idx
< NUM_QUERIES
; ++idx
) {
187 pipe
->destroy_query(pipe
, bq
->query
[idx
]);
188 FREE(bq
->result
[idx
]);
191 FREE(bq
->query_types
);
196 struct hud_batch_query_context
*batch
;
197 enum pipe_query_type query_type
;
199 /** index to choose fields in pipe_query_data_pipeline_statistics,
202 unsigned result_index
;
203 enum pipe_driver_query_result_type result_type
;
204 enum pipe_driver_query_type type
;
206 /* Ring of queries. If a query is busy, we use another slot. */
207 struct pipe_query
*query
[NUM_QUERIES
];
211 uint64_t results_cumulative
;
212 unsigned num_results
;
216 query_new_value_batch(struct query_info
*info
)
218 struct hud_batch_query_context
*bq
= info
->batch
;
219 unsigned result_index
= info
->result_index
;
220 unsigned idx
= (bq
->head
- bq
->pending
) % NUM_QUERIES
;
221 unsigned results
= bq
->results
;
224 info
->results_cumulative
+= bq
->result
[idx
]->batch
[result_index
].u64
;
228 idx
= (idx
- 1) % NUM_QUERIES
;
233 query_new_value_normal(struct query_info
*info
, struct pipe_context
*pipe
)
235 if (info
->last_time
) {
236 if (info
->query
[info
->head
])
237 pipe
->end_query(pipe
, info
->query
[info
->head
]);
239 /* read query results */
241 struct pipe_query
*query
= info
->query
[info
->tail
];
242 union pipe_query_result result
;
243 uint64_t *res64
= (uint64_t *)&result
;
245 if (query
&& pipe
->get_query_result(pipe
, query
, FALSE
, &result
)) {
246 if (info
->type
== PIPE_DRIVER_QUERY_TYPE_FLOAT
) {
247 assert(info
->result_index
== 0);
248 info
->results_cumulative
+= (uint64_t) (result
.f
* 1000.0f
);
251 info
->results_cumulative
+= res64
[info
->result_index
];
255 if (info
->tail
== info
->head
)
258 info
->tail
= (info
->tail
+1) % NUM_QUERIES
;
261 /* the oldest query is busy */
262 if ((info
->head
+1) % NUM_QUERIES
== info
->tail
) {
263 /* all queries are busy, throw away the last query and create
266 "gallium_hud: all queries are busy after %i frames, "
267 "can't add another query\n",
269 if (info
->query
[info
->head
])
270 pipe
->destroy_query(pipe
, info
->query
[info
->head
]);
271 info
->query
[info
->head
] =
272 pipe
->create_query(pipe
, info
->query_type
, 0);
275 /* the last query is busy, we need to add a new one we can use
277 info
->head
= (info
->head
+1) % NUM_QUERIES
;
278 if (!info
->query
[info
->head
]) {
279 info
->query
[info
->head
] =
280 pipe
->create_query(pipe
, info
->query_type
, 0);
289 info
->query
[info
->head
] = pipe
->create_query(pipe
, info
->query_type
, 0);
294 begin_query(struct hud_graph
*gr
, struct pipe_context
*pipe
)
296 struct query_info
*info
= gr
->query_data
;
298 assert(!info
->batch
);
299 if (info
->query
[info
->head
])
300 pipe
->begin_query(pipe
, info
->query
[info
->head
]);
304 query_new_value(struct hud_graph
*gr
, struct pipe_context
*pipe
)
306 struct query_info
*info
= gr
->query_data
;
307 uint64_t now
= os_time_get();
310 query_new_value_batch(info
);
312 query_new_value_normal(info
, pipe
);
315 if (!info
->last_time
) {
316 info
->last_time
= now
;
320 if (info
->num_results
&& info
->last_time
+ gr
->pane
->period
<= now
) {
323 switch (info
->result_type
) {
325 case PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE
:
326 value
= info
->results_cumulative
/ info
->num_results
;
328 case PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE
:
329 value
= info
->results_cumulative
;
333 if (info
->type
== PIPE_DRIVER_QUERY_TYPE_FLOAT
) {
337 hud_graph_add_value(gr
, value
);
339 info
->last_time
= now
;
340 info
->results_cumulative
= 0;
341 info
->num_results
= 0;
346 free_query_info(void *ptr
, struct pipe_context
*pipe
)
348 struct query_info
*info
= ptr
;
350 if (!info
->batch
&& info
->last_time
) {
353 pipe
->end_query(pipe
, info
->query
[info
->head
]);
355 for (i
= 0; i
< ARRAY_SIZE(info
->query
); i
++) {
356 if (info
->query
[i
]) {
357 pipe
->destroy_query(pipe
, info
->query
[i
]);
366 * \param result_index to select fields of pipe_query_data_pipeline_statistics,
370 hud_pipe_query_install(struct hud_batch_query_context
**pbq
,
371 struct hud_pane
*pane
,
373 enum pipe_query_type query_type
,
374 unsigned result_index
,
375 uint64_t max_value
, enum pipe_driver_query_type type
,
376 enum pipe_driver_query_result_type result_type
,
379 struct hud_graph
*gr
;
380 struct query_info
*info
;
382 gr
= CALLOC_STRUCT(hud_graph
);
386 strncpy(gr
->name
, name
, sizeof(gr
->name
));
387 gr
->name
[sizeof(gr
->name
) - 1] = '\0';
388 gr
->query_data
= CALLOC_STRUCT(query_info
);
392 gr
->query_new_value
= query_new_value
;
393 gr
->free_query_data
= free_query_info
;
395 info
= gr
->query_data
;
396 info
->result_type
= result_type
;
399 if (flags
& PIPE_DRIVER_QUERY_FLAG_BATCH
) {
400 if (!batch_query_add(pbq
, query_type
, &info
->result_index
))
404 gr
->begin_query
= begin_query
;
405 info
->query_type
= query_type
;
406 info
->result_index
= result_index
;
409 hud_pane_add_graph(pane
, gr
);
410 pane
->type
= type
; /* must be set before updating the max_value */
412 if (pane
->max_value
< max_value
)
413 hud_pane_set_max_value(pane
, max_value
);
423 hud_driver_query_install(struct hud_batch_query_context
**pbq
,
424 struct hud_pane
*pane
, struct pipe_screen
*screen
,
427 struct pipe_driver_query_info query
;
428 unsigned num_queries
, i
;
429 boolean found
= FALSE
;
431 if (!screen
->get_driver_query_info
)
434 num_queries
= screen
->get_driver_query_info(screen
, 0, NULL
);
436 for (i
= 0; i
< num_queries
; i
++) {
437 if (screen
->get_driver_query_info(screen
, i
, &query
) &&
438 strcmp(query
.name
, name
) == 0) {
447 hud_pipe_query_install(pbq
, pane
, query
.name
, query
.query_type
, 0,
448 query
.max_value
.u64
, query
.type
, query
.result_type
,