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");
120 if (!pipe
->begin_query(pipe
, bq
->query
[bq
->head
])) {
122 "gallium_hud: could not begin batch query. You may have "
123 "selected too many or incompatible queries.\n");
129 batch_query_add(struct hud_batch_query_context
**pbq
,
130 struct pipe_context
*pipe
, unsigned query_type
,
131 unsigned *result_index
)
133 struct hud_batch_query_context
*bq
= *pbq
;
137 bq
= CALLOC_STRUCT(hud_batch_query_context
);
144 for (i
= 0; i
< bq
->num_query_types
; ++i
) {
145 if (bq
->query_types
[i
] == query_type
) {
151 if (bq
->num_query_types
== bq
->allocated_query_types
) {
152 unsigned new_alloc
= MAX2(16, bq
->allocated_query_types
* 2);
153 unsigned *new_query_types
154 = REALLOC(bq
->query_types
,
155 bq
->allocated_query_types
* sizeof(unsigned),
156 new_alloc
* sizeof(unsigned));
157 if (!new_query_types
)
159 bq
->query_types
= new_query_types
;
160 bq
->allocated_query_types
= new_alloc
;
163 bq
->query_types
[bq
->num_query_types
] = query_type
;
164 *result_index
= bq
->num_query_types
++;
169 hud_batch_query_cleanup(struct hud_batch_query_context
**pbq
)
171 struct hud_batch_query_context
*bq
= *pbq
;
179 if (bq
->query
[bq
->head
] && !bq
->failed
)
180 bq
->pipe
->end_query(bq
->pipe
, bq
->query
[bq
->head
]);
182 for (idx
= 0; idx
< NUM_QUERIES
; ++idx
) {
184 bq
->pipe
->destroy_query(bq
->pipe
, bq
->query
[idx
]);
185 FREE(bq
->result
[idx
]);
188 FREE(bq
->query_types
);
193 struct pipe_context
*pipe
;
194 struct hud_batch_query_context
*batch
;
196 unsigned result_index
; /* unit depends on query_type */
197 enum pipe_driver_query_result_type result_type
;
199 /* Ring of queries. If a query is busy, we use another slot. */
200 struct pipe_query
*query
[NUM_QUERIES
];
204 uint64_t results_cumulative
;
205 unsigned num_results
;
209 query_new_value_batch(struct query_info
*info
)
211 struct hud_batch_query_context
*bq
= info
->batch
;
212 unsigned result_index
= info
->result_index
;
213 unsigned idx
= (bq
->head
- bq
->pending
) % NUM_QUERIES
;
214 unsigned results
= bq
->results
;
217 info
->results_cumulative
+= bq
->result
[idx
]->batch
[result_index
].u64
;
221 idx
= (idx
- 1) % NUM_QUERIES
;
226 query_new_value_normal(struct query_info
*info
)
228 struct pipe_context
*pipe
= info
->pipe
;
230 if (info
->last_time
) {
231 if (info
->query
[info
->head
])
232 pipe
->end_query(pipe
, info
->query
[info
->head
]);
234 /* read query results */
236 struct pipe_query
*query
= info
->query
[info
->tail
];
237 union pipe_query_result result
;
238 uint64_t *res64
= (uint64_t *)&result
;
240 if (query
&& pipe
->get_query_result(pipe
, query
, FALSE
, &result
)) {
241 info
->results_cumulative
+= res64
[info
->result_index
];
244 if (info
->tail
== info
->head
)
247 info
->tail
= (info
->tail
+1) % NUM_QUERIES
;
250 /* the oldest query is busy */
251 if ((info
->head
+1) % NUM_QUERIES
== info
->tail
) {
252 /* all queries are busy, throw away the last query and create
255 "gallium_hud: all queries are busy after %i frames, "
256 "can't add another query\n",
258 if (info
->query
[info
->head
])
259 pipe
->destroy_query(pipe
, info
->query
[info
->head
]);
260 info
->query
[info
->head
] =
261 pipe
->create_query(pipe
, info
->query_type
, 0);
264 /* the last query is busy, we need to add a new one we can use
266 info
->head
= (info
->head
+1) % NUM_QUERIES
;
267 if (!info
->query
[info
->head
]) {
268 info
->query
[info
->head
] =
269 pipe
->create_query(pipe
, info
->query_type
, 0);
278 info
->query
[info
->head
] = pipe
->create_query(pipe
, info
->query_type
, 0);
281 if (info
->query
[info
->head
])
282 pipe
->begin_query(pipe
, info
->query
[info
->head
]);
286 query_new_value(struct hud_graph
*gr
)
288 struct query_info
*info
= gr
->query_data
;
289 uint64_t now
= os_time_get();
292 query_new_value_batch(info
);
294 query_new_value_normal(info
);
297 if (!info
->last_time
) {
298 info
->last_time
= now
;
302 if (info
->num_results
&& info
->last_time
+ gr
->pane
->period
<= now
) {
305 switch (info
->result_type
) {
307 case PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE
:
308 value
= info
->results_cumulative
/ info
->num_results
;
310 case PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE
:
311 value
= info
->results_cumulative
;
315 hud_graph_add_value(gr
, value
);
317 info
->last_time
= now
;
318 info
->results_cumulative
= 0;
319 info
->num_results
= 0;
324 free_query_info(void *ptr
)
326 struct query_info
*info
= ptr
;
328 if (!info
->batch
&& info
->last_time
) {
329 struct pipe_context
*pipe
= info
->pipe
;
332 pipe
->end_query(pipe
, info
->query
[info
->head
]);
334 for (i
= 0; i
< ARRAY_SIZE(info
->query
); i
++) {
335 if (info
->query
[i
]) {
336 pipe
->destroy_query(pipe
, info
->query
[i
]);
344 hud_pipe_query_install(struct hud_batch_query_context
**pbq
,
345 struct hud_pane
*pane
, struct pipe_context
*pipe
,
346 const char *name
, unsigned query_type
,
347 unsigned result_index
,
348 uint64_t max_value
, enum pipe_driver_query_type type
,
349 enum pipe_driver_query_result_type result_type
,
352 struct hud_graph
*gr
;
353 struct query_info
*info
;
355 gr
= CALLOC_STRUCT(hud_graph
);
359 strncpy(gr
->name
, name
, sizeof(gr
->name
));
360 gr
->name
[sizeof(gr
->name
) - 1] = '\0';
361 gr
->query_data
= CALLOC_STRUCT(query_info
);
365 gr
->query_new_value
= query_new_value
;
366 gr
->free_query_data
= free_query_info
;
368 info
= gr
->query_data
;
370 info
->result_type
= result_type
;
372 if (flags
& PIPE_DRIVER_QUERY_FLAG_BATCH
) {
373 if (!batch_query_add(pbq
, pipe
, query_type
, &info
->result_index
))
377 info
->query_type
= query_type
;
378 info
->result_index
= result_index
;
381 hud_graph_set_dump_file(gr
);
383 hud_pane_add_graph(pane
, gr
);
384 pane
->type
= type
; /* must be set before updating the max_value */
386 if (pane
->max_value
< max_value
)
387 hud_pane_set_max_value(pane
, max_value
);
397 hud_driver_query_install(struct hud_batch_query_context
**pbq
,
398 struct hud_pane
*pane
, struct pipe_context
*pipe
,
401 struct pipe_screen
*screen
= pipe
->screen
;
402 struct pipe_driver_query_info query
;
403 unsigned num_queries
, i
;
404 boolean found
= FALSE
;
406 if (!screen
->get_driver_query_info
)
409 num_queries
= screen
->get_driver_query_info(screen
, 0, NULL
);
411 for (i
= 0; i
< num_queries
; i
++) {
412 if (screen
->get_driver_query_info(screen
, i
, &query
) &&
413 strcmp(query
.name
, name
) == 0) {
422 hud_pipe_query_install(pbq
, pane
, pipe
, query
.name
, query
.query_type
, 0,
423 query
.max_value
.u64
, query
.type
, query
.result_type
,