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 "os/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 struct pipe_context
*pipe
;
46 unsigned num_query_types
;
47 unsigned allocated_query_types
;
48 unsigned *query_types
;
51 struct pipe_query
*query
[NUM_QUERIES
];
52 union pipe_query_result
*result
[NUM_QUERIES
];
53 unsigned head
, pending
, results
;
57 hud_batch_query_update(struct hud_batch_query_context
*bq
)
59 struct pipe_context
*pipe
;
61 if (!bq
|| bq
->failed
)
66 if (bq
->query
[bq
->head
])
67 pipe
->end_query(pipe
, bq
->query
[bq
->head
]);
72 unsigned idx
= (bq
->head
- bq
->pending
+ 1) % NUM_QUERIES
;
73 struct pipe_query
*query
= bq
->query
[idx
];
76 bq
->result
[idx
] = MALLOC(sizeof(bq
->result
[idx
]->batch
[0]) *
78 if (!bq
->result
[idx
]) {
79 fprintf(stderr
, "gallium_hud: out of memory.\n");
84 if (!pipe
->get_query_result(pipe
, query
, FALSE
, bq
->result
[idx
]))
91 bq
->head
= (bq
->head
+ 1) % NUM_QUERIES
;
93 if (bq
->pending
== NUM_QUERIES
) {
95 "gallium_hud: all queries busy after %i frames, dropping data.\n",
98 assert(bq
->query
[bq
->head
]);
100 pipe
->destroy_query(bq
->pipe
, bq
->query
[bq
->head
]);
101 bq
->query
[bq
->head
] = NULL
;
106 if (!bq
->query
[bq
->head
]) {
107 bq
->query
[bq
->head
] = pipe
->create_batch_query(pipe
,
111 if (!bq
->query
[bq
->head
]) {
113 "gallium_hud: create_batch_query failed. You may have "
114 "selected too many or incompatible queries.\n");
122 hud_batch_query_begin(struct hud_batch_query_context
*bq
)
124 if (!bq
|| bq
->failed
|| !bq
->query
[bq
->head
])
127 if (!bq
->pipe
->begin_query(bq
->pipe
, bq
->query
[bq
->head
])) {
129 "gallium_hud: could not begin batch query. You may have "
130 "selected too many or incompatible queries.\n");
136 batch_query_add(struct hud_batch_query_context
**pbq
,
137 struct pipe_context
*pipe
, unsigned query_type
,
138 unsigned *result_index
)
140 struct hud_batch_query_context
*bq
= *pbq
;
144 bq
= CALLOC_STRUCT(hud_batch_query_context
);
151 for (i
= 0; i
< bq
->num_query_types
; ++i
) {
152 if (bq
->query_types
[i
] == query_type
) {
158 if (bq
->num_query_types
== bq
->allocated_query_types
) {
159 unsigned new_alloc
= MAX2(16, bq
->allocated_query_types
* 2);
160 unsigned *new_query_types
161 = REALLOC(bq
->query_types
,
162 bq
->allocated_query_types
* sizeof(unsigned),
163 new_alloc
* sizeof(unsigned));
164 if (!new_query_types
)
166 bq
->query_types
= new_query_types
;
167 bq
->allocated_query_types
= new_alloc
;
170 bq
->query_types
[bq
->num_query_types
] = query_type
;
171 *result_index
= bq
->num_query_types
++;
176 hud_batch_query_cleanup(struct hud_batch_query_context
**pbq
)
178 struct hud_batch_query_context
*bq
= *pbq
;
186 if (bq
->query
[bq
->head
] && !bq
->failed
)
187 bq
->pipe
->end_query(bq
->pipe
, bq
->query
[bq
->head
]);
189 for (idx
= 0; idx
< NUM_QUERIES
; ++idx
) {
191 bq
->pipe
->destroy_query(bq
->pipe
, bq
->query
[idx
]);
192 FREE(bq
->result
[idx
]);
195 FREE(bq
->query_types
);
200 struct pipe_context
*pipe
;
201 struct hud_batch_query_context
*batch
;
203 unsigned result_index
; /* unit depends on query_type */
204 enum pipe_driver_query_result_type result_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
)
235 struct pipe_context
*pipe
= info
->pipe
;
237 if (info
->last_time
) {
238 if (info
->query
[info
->head
])
239 pipe
->end_query(pipe
, info
->query
[info
->head
]);
241 /* read query results */
243 struct pipe_query
*query
= info
->query
[info
->tail
];
244 union pipe_query_result result
;
245 uint64_t *res64
= (uint64_t *)&result
;
247 if (query
&& pipe
->get_query_result(pipe
, query
, FALSE
, &result
)) {
248 info
->results_cumulative
+= res64
[info
->result_index
];
251 if (info
->tail
== info
->head
)
254 info
->tail
= (info
->tail
+1) % NUM_QUERIES
;
257 /* the oldest query is busy */
258 if ((info
->head
+1) % NUM_QUERIES
== info
->tail
) {
259 /* all queries are busy, throw away the last query and create
262 "gallium_hud: all queries are busy after %i frames, "
263 "can't add another query\n",
265 if (info
->query
[info
->head
])
266 pipe
->destroy_query(pipe
, info
->query
[info
->head
]);
267 info
->query
[info
->head
] =
268 pipe
->create_query(pipe
, info
->query_type
, 0);
271 /* the last query is busy, we need to add a new one we can use
273 info
->head
= (info
->head
+1) % NUM_QUERIES
;
274 if (!info
->query
[info
->head
]) {
275 info
->query
[info
->head
] =
276 pipe
->create_query(pipe
, info
->query_type
, 0);
285 info
->query
[info
->head
] = pipe
->create_query(pipe
, info
->query_type
, 0);
290 begin_query(struct hud_graph
*gr
)
292 struct query_info
*info
= gr
->query_data
;
293 struct pipe_context
*pipe
= info
->pipe
;
295 assert(!info
->batch
);
296 if (info
->query
[info
->head
])
297 pipe
->begin_query(pipe
, info
->query
[info
->head
]);
301 query_new_value(struct hud_graph
*gr
)
303 struct query_info
*info
= gr
->query_data
;
304 uint64_t now
= os_time_get();
307 query_new_value_batch(info
);
309 query_new_value_normal(info
);
312 if (!info
->last_time
) {
313 info
->last_time
= now
;
317 if (info
->num_results
&& info
->last_time
+ gr
->pane
->period
<= now
) {
320 switch (info
->result_type
) {
322 case PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE
:
323 value
= info
->results_cumulative
/ info
->num_results
;
325 case PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE
:
326 value
= info
->results_cumulative
;
330 hud_graph_add_value(gr
, value
);
332 info
->last_time
= now
;
333 info
->results_cumulative
= 0;
334 info
->num_results
= 0;
339 free_query_info(void *ptr
)
341 struct query_info
*info
= ptr
;
343 if (!info
->batch
&& info
->last_time
) {
344 struct pipe_context
*pipe
= info
->pipe
;
347 pipe
->end_query(pipe
, info
->query
[info
->head
]);
349 for (i
= 0; i
< ARRAY_SIZE(info
->query
); i
++) {
350 if (info
->query
[i
]) {
351 pipe
->destroy_query(pipe
, info
->query
[i
]);
359 hud_pipe_query_install(struct hud_batch_query_context
**pbq
,
360 struct hud_pane
*pane
, struct pipe_context
*pipe
,
361 const char *name
, unsigned query_type
,
362 unsigned result_index
,
363 uint64_t max_value
, enum pipe_driver_query_type type
,
364 enum pipe_driver_query_result_type result_type
,
367 struct hud_graph
*gr
;
368 struct query_info
*info
;
370 gr
= CALLOC_STRUCT(hud_graph
);
374 strncpy(gr
->name
, name
, sizeof(gr
->name
));
375 gr
->name
[sizeof(gr
->name
) - 1] = '\0';
376 gr
->query_data
= CALLOC_STRUCT(query_info
);
380 gr
->query_new_value
= query_new_value
;
381 gr
->free_query_data
= free_query_info
;
383 info
= gr
->query_data
;
385 info
->result_type
= result_type
;
387 if (flags
& PIPE_DRIVER_QUERY_FLAG_BATCH
) {
388 if (!batch_query_add(pbq
, pipe
, query_type
, &info
->result_index
))
392 gr
->begin_query
= begin_query
;
393 info
->query_type
= query_type
;
394 info
->result_index
= result_index
;
397 hud_graph_set_dump_file(gr
);
399 hud_pane_add_graph(pane
, gr
);
400 pane
->type
= type
; /* must be set before updating the max_value */
402 if (pane
->max_value
< max_value
)
403 hud_pane_set_max_value(pane
, max_value
);
413 hud_driver_query_install(struct hud_batch_query_context
**pbq
,
414 struct hud_pane
*pane
, struct pipe_context
*pipe
,
417 struct pipe_screen
*screen
= pipe
->screen
;
418 struct pipe_driver_query_info query
;
419 unsigned num_queries
, i
;
420 boolean found
= FALSE
;
422 if (!screen
->get_driver_query_info
)
425 num_queries
= screen
->get_driver_query_info(screen
, 0, NULL
);
427 for (i
= 0; i
< num_queries
; i
++) {
428 if (screen
->get_driver_query_info(screen
, i
, &query
) &&
429 strcmp(query
.name
, name
) == 0) {
438 hud_pipe_query_install(pbq
, pane
, pipe
, query
.name
, query
.query_type
, 0,
439 query
.max_value
.u64
, query
.type
, query
.result_type
,