Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/drivers/zink/zink_query.c
4570 views
1
#include "zink_query.h"
2
3
#include "zink_context.h"
4
#include "zink_fence.h"
5
#include "zink_resource.h"
6
#include "zink_screen.h"
7
8
#include "util/hash_table.h"
9
#include "util/set.h"
10
#include "util/u_dump.h"
11
#include "util/u_inlines.h"
12
#include "util/u_memory.h"
13
14
#define NUM_QUERIES 5000
15
16
struct zink_query_buffer {
17
struct list_head list;
18
unsigned num_results;
19
struct pipe_resource *buffer;
20
struct pipe_resource *xfb_buffers[PIPE_MAX_VERTEX_STREAMS - 1];
21
};
22
23
struct zink_query {
24
struct threaded_query base;
25
enum pipe_query_type type;
26
27
VkQueryPool query_pool;
28
VkQueryPool xfb_query_pool[PIPE_MAX_VERTEX_STREAMS - 1]; //stream 0 is in the base pool
29
unsigned curr_query, last_start;
30
31
VkQueryType vkqtype;
32
unsigned index;
33
bool precise;
34
bool xfb_running;
35
bool xfb_overflow;
36
37
bool active; /* query is considered active by vk */
38
bool needs_reset; /* query is considered active by vk and cannot be destroyed */
39
bool dead; /* query should be destroyed when its fence finishes */
40
bool needs_update; /* query needs to update its qbos */
41
42
unsigned fences;
43
struct list_head active_list;
44
45
struct list_head stats_list; /* when active, statistics queries are added to ctx->primitives_generated_queries */
46
bool have_gs[NUM_QUERIES]; /* geometry shaders use GEOMETRY_SHADER_PRIMITIVES_BIT */
47
bool have_xfb[NUM_QUERIES]; /* xfb was active during this query */
48
49
struct zink_batch_usage *batch_id; //batch that the query was started in
50
51
struct list_head buffers;
52
struct zink_query_buffer *curr_qbo;
53
54
struct zink_resource *predicate;
55
bool predicate_dirty;
56
};
57
58
static void
59
update_qbo(struct zink_context *ctx, struct zink_query *q);
60
static void
61
reset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q);
62
63
static inline unsigned
64
get_num_results(enum pipe_query_type query_type)
65
{
66
switch (query_type) {
67
case PIPE_QUERY_OCCLUSION_COUNTER:
68
case PIPE_QUERY_OCCLUSION_PREDICATE:
69
case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
70
case PIPE_QUERY_TIME_ELAPSED:
71
case PIPE_QUERY_TIMESTAMP:
72
case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
73
return 1;
74
case PIPE_QUERY_PRIMITIVES_GENERATED:
75
case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
76
case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
77
case PIPE_QUERY_PRIMITIVES_EMITTED:
78
return 2;
79
default:
80
debug_printf("unknown query: %s\n",
81
util_str_query_type(query_type, true));
82
unreachable("zink: unknown query type");
83
}
84
}
85
86
static VkQueryPipelineStatisticFlags
87
pipeline_statistic_convert(enum pipe_statistics_query_index idx)
88
{
89
unsigned map[] = {
90
[PIPE_STAT_QUERY_IA_VERTICES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT,
91
[PIPE_STAT_QUERY_IA_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT,
92
[PIPE_STAT_QUERY_VS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT,
93
[PIPE_STAT_QUERY_GS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT,
94
[PIPE_STAT_QUERY_GS_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT,
95
[PIPE_STAT_QUERY_C_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT,
96
[PIPE_STAT_QUERY_C_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT,
97
[PIPE_STAT_QUERY_PS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT,
98
[PIPE_STAT_QUERY_HS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT,
99
[PIPE_STAT_QUERY_DS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT,
100
[PIPE_STAT_QUERY_CS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT
101
};
102
assert(idx < ARRAY_SIZE(map));
103
return map[idx];
104
}
105
106
static void
107
timestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp)
108
{
109
/* The number of valid bits in a timestamp value is determined by
110
* the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written.
111
* - 17.5. Timestamp Queries
112
*/
113
if (screen->timestamp_valid_bits < 64)
114
*timestamp &= (1ull << screen->timestamp_valid_bits) - 1;
115
116
/* The number of nanoseconds it takes for a timestamp value to be incremented by 1
117
* can be obtained from VkPhysicalDeviceLimits::timestampPeriod
118
* - 17.5. Timestamp Queries
119
*/
120
*timestamp *= screen->info.props.limits.timestampPeriod;
121
}
122
123
static VkQueryType
124
convert_query_type(unsigned query_type, bool *precise)
125
{
126
*precise = false;
127
switch (query_type) {
128
case PIPE_QUERY_OCCLUSION_COUNTER:
129
*precise = true;
130
FALLTHROUGH;
131
case PIPE_QUERY_OCCLUSION_PREDICATE:
132
case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
133
return VK_QUERY_TYPE_OCCLUSION;
134
case PIPE_QUERY_TIME_ELAPSED:
135
case PIPE_QUERY_TIMESTAMP:
136
return VK_QUERY_TYPE_TIMESTAMP;
137
case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
138
case PIPE_QUERY_PRIMITIVES_GENERATED:
139
return VK_QUERY_TYPE_PIPELINE_STATISTICS;
140
case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
141
case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
142
case PIPE_QUERY_PRIMITIVES_EMITTED:
143
return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
144
default:
145
debug_printf("unknown query: %s\n",
146
util_str_query_type(query_type, true));
147
unreachable("zink: unknown query type");
148
}
149
}
150
151
static bool
152
needs_stats_list(struct zink_query *query)
153
{
154
return query->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
155
query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE ||
156
query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
157
}
158
159
static bool
160
is_time_query(struct zink_query *query)
161
{
162
return query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIME_ELAPSED;
163
}
164
165
static bool
166
is_so_overflow_query(struct zink_query *query)
167
{
168
return query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE || query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE;
169
}
170
171
static bool
172
is_bool_query(struct zink_query *query)
173
{
174
return is_so_overflow_query(query) ||
175
query->type == PIPE_QUERY_OCCLUSION_PREDICATE ||
176
query->type == PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE ||
177
query->type == PIPE_QUERY_GPU_FINISHED;
178
}
179
180
static void
181
qbo_sync_from_prev(struct zink_context *ctx, struct zink_query *query, unsigned id_offset, unsigned last_start)
182
{
183
assert(id_offset);
184
185
struct zink_query_buffer *prev = list_last_entry(&query->buffers, struct zink_query_buffer, list);
186
unsigned result_size = get_num_results(query->type) * sizeof(uint64_t);
187
/* this is get_buffer_offset() but without the zink_query object */
188
unsigned qbo_offset = last_start * get_num_results(query->type) * sizeof(uint64_t);
189
query->curr_query = id_offset;
190
query->curr_qbo->num_results = id_offset;
191
zink_copy_buffer(ctx, NULL, zink_resource(query->curr_qbo->buffer), zink_resource(prev->buffer), 0,
192
qbo_offset,
193
id_offset * result_size);
194
}
195
196
static bool
197
qbo_append(struct pipe_screen *screen, struct zink_query *query)
198
{
199
if (query->curr_qbo && query->curr_qbo->list.next)
200
return true;
201
struct zink_query_buffer *qbo = CALLOC_STRUCT(zink_query_buffer);
202
if (!qbo)
203
return false;
204
qbo->buffer = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
205
PIPE_USAGE_STREAM,
206
/* this is the maximum possible size of the results in a given buffer */
207
NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
208
if (!qbo->buffer)
209
goto fail;
210
if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
211
/* need separate xfb buffer */
212
qbo->xfb_buffers[0] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
213
PIPE_USAGE_STREAM,
214
/* this is the maximum possible size of the results in a given buffer */
215
NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
216
if (!qbo->xfb_buffers[0])
217
goto fail;
218
} else if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
219
/* need to monitor all xfb streams */
220
for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++) {
221
/* need separate xfb buffer */
222
qbo->xfb_buffers[i] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER,
223
PIPE_USAGE_STREAM,
224
/* this is the maximum possible size of the results in a given buffer */
225
NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t));
226
if (!qbo->xfb_buffers[i])
227
goto fail;
228
}
229
}
230
list_addtail(&qbo->list, &query->buffers);
231
232
return true;
233
fail:
234
pipe_resource_reference(&qbo->buffer, NULL);
235
for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++)
236
pipe_resource_reference(&qbo->xfb_buffers[i], NULL);
237
FREE(qbo);
238
return false;
239
}
240
241
static void
242
destroy_query(struct zink_screen *screen, struct zink_query *query)
243
{
244
assert(!p_atomic_read(&query->fences));
245
if (query->query_pool)
246
vkDestroyQueryPool(screen->dev, query->query_pool, NULL);
247
struct zink_query_buffer *qbo, *next;
248
LIST_FOR_EACH_ENTRY_SAFE(qbo, next, &query->buffers, list) {
249
pipe_resource_reference(&qbo->buffer, NULL);
250
for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++)
251
pipe_resource_reference(&qbo->xfb_buffers[i], NULL);
252
FREE(qbo);
253
}
254
for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) {
255
if (query->xfb_query_pool[i])
256
vkDestroyQueryPool(screen->dev, query->xfb_query_pool[i], NULL);
257
}
258
pipe_resource_reference((struct pipe_resource**)&query->predicate, NULL);
259
FREE(query);
260
}
261
262
static void
263
reset_qbo(struct zink_query *q)
264
{
265
q->curr_qbo = list_first_entry(&q->buffers, struct zink_query_buffer, list);
266
q->curr_qbo->num_results = 0;
267
}
268
269
static struct pipe_query *
270
zink_create_query(struct pipe_context *pctx,
271
unsigned query_type, unsigned index)
272
{
273
struct zink_screen *screen = zink_screen(pctx->screen);
274
struct zink_query *query = CALLOC_STRUCT(zink_query);
275
VkQueryPoolCreateInfo pool_create = {0};
276
277
if (!query)
278
return NULL;
279
list_inithead(&query->buffers);
280
281
query->index = index;
282
query->type = query_type;
283
query->vkqtype = convert_query_type(query_type, &query->precise);
284
if (query->vkqtype == -1)
285
return NULL;
286
287
query->curr_query = 0;
288
289
pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
290
pool_create.queryType = query->vkqtype;
291
pool_create.queryCount = NUM_QUERIES;
292
if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED)
293
pool_create.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT |
294
VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT;
295
else if (query_type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE)
296
pool_create.pipelineStatistics = pipeline_statistic_convert(index);
297
298
VkResult status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->query_pool);
299
if (status != VK_SUCCESS)
300
goto fail;
301
if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) {
302
/* if xfb is active, we need to use an xfb query, otherwise we need pipeline statistics */
303
pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
304
pool_create.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
305
pool_create.queryCount = NUM_QUERIES;
306
307
status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->xfb_query_pool[0]);
308
if (status != VK_SUCCESS)
309
goto fail;
310
} else if (query_type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
311
/* need to monitor all xfb streams */
312
for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) {
313
status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->xfb_query_pool[i]);
314
if (status != VK_SUCCESS)
315
goto fail;
316
}
317
}
318
if (!qbo_append(pctx->screen, query))
319
goto fail;
320
struct zink_batch *batch = &zink_context(pctx)->batch;
321
batch->has_work = true;
322
query->needs_reset = true;
323
if (query->type == PIPE_QUERY_TIMESTAMP) {
324
query->active = true;
325
/* defer pool reset until end_query since we're guaranteed to be threadsafe then */
326
reset_qbo(query);
327
}
328
return (struct pipe_query *)query;
329
fail:
330
destroy_query(screen, query);
331
return NULL;
332
}
333
334
static void
335
zink_destroy_query(struct pipe_context *pctx,
336
struct pipe_query *q)
337
{
338
struct zink_screen *screen = zink_screen(pctx->screen);
339
struct zink_query *query = (struct zink_query *)q;
340
341
p_atomic_set(&query->dead, true);
342
if (p_atomic_read(&query->fences)) {
343
if (query->xfb_running)
344
zink_fence_wait(pctx);
345
return;
346
}
347
348
destroy_query(screen, query);
349
}
350
351
void
352
zink_prune_query(struct zink_screen *screen, struct zink_query *query)
353
{
354
if (!p_atomic_dec_return(&query->fences)) {
355
if (p_atomic_read(&query->dead))
356
destroy_query(screen, query);
357
}
358
}
359
360
static void
361
check_query_results(struct zink_query *query, union pipe_query_result *result,
362
int num_results, uint64_t *results, uint64_t *xfb_results)
363
{
364
uint64_t last_val = 0;
365
int result_size = get_num_results(query->type);
366
for (int i = 0; i < num_results * result_size; i += result_size) {
367
switch (query->type) {
368
case PIPE_QUERY_OCCLUSION_PREDICATE:
369
case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
370
case PIPE_QUERY_GPU_FINISHED:
371
result->b |= results[i] != 0;
372
break;
373
374
case PIPE_QUERY_TIME_ELAPSED:
375
case PIPE_QUERY_TIMESTAMP:
376
/* the application can sum the differences between all N queries to determine the total execution time.
377
* - 17.5. Timestamp Queries
378
*/
379
if (query->type != PIPE_QUERY_TIME_ELAPSED || i)
380
result->u64 += results[i] - last_val;
381
last_val = results[i];
382
break;
383
case PIPE_QUERY_OCCLUSION_COUNTER:
384
result->u64 += results[i];
385
break;
386
case PIPE_QUERY_PRIMITIVES_GENERATED:
387
if (query->have_xfb[query->last_start + i / 2] || query->index)
388
result->u64 += xfb_results[i + 1];
389
else
390
/* if a given draw had a geometry shader, we need to use the second result */
391
result->u64 += results[i + query->have_gs[query->last_start + i / 2]];
392
break;
393
case PIPE_QUERY_PRIMITIVES_EMITTED:
394
/* A query pool created with this type will capture 2 integers -
395
* numPrimitivesWritten and numPrimitivesNeeded -
396
* for the specified vertex stream output from the last vertex processing stage.
397
* - from VK_EXT_transform_feedback spec
398
*/
399
result->u64 += results[i];
400
break;
401
case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
402
case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
403
/* A query pool created with this type will capture 2 integers -
404
* numPrimitivesWritten and numPrimitivesNeeded -
405
* for the specified vertex stream output from the last vertex processing stage.
406
* - from VK_EXT_transform_feedback spec
407
*/
408
if (query->have_xfb[query->last_start + i / 2])
409
result->b |= results[i] != results[i + 1];
410
break;
411
case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE:
412
result->u64 += results[i];
413
break;
414
415
default:
416
debug_printf("unhandled query type: %s\n",
417
util_str_query_type(query->type, true));
418
unreachable("unexpected query type");
419
}
420
}
421
}
422
423
static bool
424
get_query_result(struct pipe_context *pctx,
425
struct pipe_query *q,
426
bool wait,
427
union pipe_query_result *result)
428
{
429
struct zink_screen *screen = zink_screen(pctx->screen);
430
struct zink_query *query = (struct zink_query *)q;
431
unsigned flags = PIPE_MAP_READ;
432
433
if (!wait)
434
flags |= PIPE_MAP_DONTBLOCK;
435
436
util_query_clear_result(result, query->type);
437
438
int num_results = query->curr_query - query->last_start;
439
int result_size = get_num_results(query->type) * sizeof(uint64_t);
440
441
struct zink_query_buffer *qbo;
442
struct pipe_transfer *xfer;
443
LIST_FOR_EACH_ENTRY(qbo, &query->buffers, list) {
444
uint64_t *xfb_results = NULL;
445
uint64_t *results;
446
bool is_timestamp = query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
447
results = pipe_buffer_map_range(pctx, qbo->buffer, 0,
448
(is_timestamp ? 1 : qbo->num_results) * result_size, flags, &xfer);
449
if (!results) {
450
if (wait)
451
debug_printf("zink: qbo read failed!");
452
return false;
453
}
454
struct pipe_transfer *xfb_xfer = NULL;
455
if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) {
456
xfb_results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[0], 0,
457
qbo->num_results * result_size, flags, &xfb_xfer);
458
if (!xfb_results) {
459
if (wait)
460
debug_printf("zink: xfb qbo read failed!");
461
}
462
}
463
check_query_results(query, result, is_timestamp ? 1 : qbo->num_results, results, xfb_results);
464
pipe_buffer_unmap(pctx, xfer);
465
if (xfb_xfer)
466
pipe_buffer_unmap(pctx, xfb_xfer);
467
if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
468
for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers) && !result->b; i++) {
469
uint64_t *results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[i],
470
0,
471
qbo->num_results * result_size, flags, &xfer);
472
if (!results) {
473
if (wait)
474
debug_printf("zink: qbo read failed!");
475
return false;
476
}
477
check_query_results(query, result, num_results, results, xfb_results);
478
pipe_buffer_unmap(pctx, xfer);
479
}
480
/* if overflow is detected we can stop */
481
if (result->b)
482
break;
483
}
484
}
485
486
if (is_time_query(query))
487
timestamp_to_nanoseconds(screen, &result->u64);
488
489
return true;
490
}
491
492
static void
493
force_cpu_read(struct zink_context *ctx, struct pipe_query *pquery, enum pipe_query_value_type result_type, struct pipe_resource *pres, unsigned offset)
494
{
495
struct pipe_context *pctx = &ctx->base;
496
unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
497
struct zink_query *query = (struct zink_query*)pquery;
498
union pipe_query_result result;
499
500
if (query->needs_update)
501
update_qbo(ctx, query);
502
503
bool success = get_query_result(pctx, pquery, true, &result);
504
if (!success) {
505
debug_printf("zink: getting query result failed\n");
506
return;
507
}
508
509
if (result_type <= PIPE_QUERY_TYPE_U32) {
510
uint32_t u32;
511
uint32_t limit;
512
if (result_type == PIPE_QUERY_TYPE_I32)
513
limit = INT_MAX;
514
else
515
limit = UINT_MAX;
516
if (is_bool_query(query))
517
u32 = result.b;
518
else
519
u32 = MIN2(limit, result.u64);
520
pipe_buffer_write(pctx, pres, offset, result_size, &u32);
521
} else {
522
uint64_t u64;
523
if (is_bool_query(query))
524
u64 = result.b;
525
else
526
u64 = result.u64;
527
pipe_buffer_write(pctx, pres, offset, result_size, &u64);
528
}
529
}
530
531
static void
532
copy_pool_results_to_buffer(struct zink_context *ctx, struct zink_query *query, VkQueryPool pool,
533
unsigned query_id, struct zink_resource *res, unsigned offset,
534
int num_results, VkQueryResultFlags flags)
535
{
536
struct zink_batch *batch = &ctx->batch;
537
unsigned type_size = (flags & VK_QUERY_RESULT_64_BIT) ? sizeof(uint64_t) : sizeof(uint32_t);
538
unsigned base_result_size = get_num_results(query->type) * type_size;
539
unsigned result_size = base_result_size * num_results;
540
if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)
541
result_size += type_size;
542
zink_batch_no_rp(ctx);
543
/* if it's a single query that doesn't need special handling, we can copy it and be done */
544
zink_batch_reference_resource_rw(batch, res, true);
545
zink_resource_buffer_barrier(ctx, batch, res, VK_ACCESS_TRANSFER_WRITE_BIT, 0);
546
util_range_add(&res->base.b, &res->valid_buffer_range, offset, offset + result_size);
547
assert(query_id < NUM_QUERIES);
548
vkCmdCopyQueryPoolResults(batch->state->cmdbuf, pool, query_id, num_results, res->obj->buffer,
549
offset, 0, flags);
550
}
551
552
static void
553
copy_results_to_buffer(struct zink_context *ctx, struct zink_query *query, struct zink_resource *res, unsigned offset, int num_results, VkQueryResultFlags flags)
554
{
555
copy_pool_results_to_buffer(ctx, query, query->query_pool, query->last_start, res, offset, num_results, flags);
556
}
557
558
static void
559
reset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
560
{
561
unsigned last_start = q->last_start;
562
unsigned id_offset = q->curr_query - q->last_start;
563
/* This command must only be called outside of a render pass instance
564
*
565
* - vkCmdResetQueryPool spec
566
*/
567
zink_batch_no_rp(ctx);
568
if (q->needs_update)
569
update_qbo(ctx, q);
570
571
vkCmdResetQueryPool(batch->state->cmdbuf, q->query_pool, 0, NUM_QUERIES);
572
if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED)
573
vkCmdResetQueryPool(batch->state->cmdbuf, q->xfb_query_pool[0], 0, NUM_QUERIES);
574
else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
575
for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++)
576
vkCmdResetQueryPool(batch->state->cmdbuf, q->xfb_query_pool[i], 0, NUM_QUERIES);
577
}
578
memset(q->have_gs, 0, sizeof(q->have_gs));
579
memset(q->have_xfb, 0, sizeof(q->have_xfb));
580
q->last_start = q->curr_query = 0;
581
q->needs_reset = false;
582
/* create new qbo for non-timestamp queries */
583
if (q->type != PIPE_QUERY_TIMESTAMP) {
584
if (qbo_append(ctx->base.screen, q))
585
reset_qbo(q);
586
else
587
debug_printf("zink: qbo alloc failed on reset!");
588
}
589
if (id_offset)
590
qbo_sync_from_prev(ctx, q, id_offset, last_start);
591
}
592
593
static inline unsigned
594
get_buffer_offset(struct zink_query *q, struct pipe_resource *pres, unsigned query_id)
595
{
596
return (query_id - q->last_start) * get_num_results(q->type) * sizeof(uint64_t);
597
}
598
599
static void
600
update_qbo(struct zink_context *ctx, struct zink_query *q)
601
{
602
struct zink_query_buffer *qbo = q->curr_qbo;
603
unsigned offset = 0;
604
uint32_t query_id = q->curr_query - 1;
605
bool is_timestamp = q->type == PIPE_QUERY_TIMESTAMP || q->type == PIPE_QUERY_TIMESTAMP_DISJOINT;
606
/* timestamp queries just write to offset 0 always */
607
if (!is_timestamp)
608
offset = get_buffer_offset(q, qbo->buffer, query_id);
609
copy_pool_results_to_buffer(ctx, q, q->query_pool, query_id, zink_resource(qbo->buffer),
610
offset,
611
1, VK_QUERY_RESULT_64_BIT);
612
613
if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
614
q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
615
q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
616
copy_pool_results_to_buffer(ctx, q,
617
q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool,
618
query_id,
619
zink_resource(qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer),
620
get_buffer_offset(q, qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer, query_id),
621
1, VK_QUERY_RESULT_64_BIT);
622
}
623
624
else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
625
for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) {
626
copy_pool_results_to_buffer(ctx, q, q->xfb_query_pool[i], query_id, zink_resource(qbo->xfb_buffers[i]),
627
get_buffer_offset(q, qbo->xfb_buffers[i], query_id),
628
1, VK_QUERY_RESULT_64_BIT);
629
}
630
}
631
632
if (!is_timestamp)
633
q->curr_qbo->num_results++;
634
q->needs_update = false;
635
}
636
637
static void
638
begin_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
639
{
640
VkQueryControlFlags flags = 0;
641
642
q->predicate_dirty = true;
643
if (q->needs_reset)
644
reset_pool(ctx, batch, q);
645
assert(q->curr_query < NUM_QUERIES);
646
q->active = true;
647
batch->has_work = true;
648
if (q->type == PIPE_QUERY_TIME_ELAPSED) {
649
vkCmdWriteTimestamp(batch->state->cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, q->query_pool, q->curr_query);
650
q->curr_query++;
651
update_qbo(ctx, q);
652
}
653
/* ignore the rest of begin_query for timestamps */
654
if (is_time_query(q))
655
return;
656
if (q->precise)
657
flags |= VK_QUERY_CONTROL_PRECISE_BIT;
658
if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
659
q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
660
q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
661
zink_screen(ctx->base.screen)->vk.CmdBeginQueryIndexedEXT(batch->state->cmdbuf,
662
q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool,
663
q->curr_query,
664
flags,
665
q->index);
666
q->xfb_running = true;
667
} else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
668
zink_screen(ctx->base.screen)->vk.CmdBeginQueryIndexedEXT(batch->state->cmdbuf,
669
q->query_pool,
670
q->curr_query,
671
flags,
672
0);
673
for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++)
674
zink_screen(ctx->base.screen)->vk.CmdBeginQueryIndexedEXT(batch->state->cmdbuf,
675
q->xfb_query_pool[i],
676
q->curr_query,
677
flags,
678
i + 1);
679
q->xfb_running = true;
680
}
681
if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
682
vkCmdBeginQuery(batch->state->cmdbuf, q->query_pool, q->curr_query, flags);
683
if (needs_stats_list(q))
684
list_addtail(&q->stats_list, &ctx->primitives_generated_queries);
685
p_atomic_inc(&q->fences);
686
zink_batch_usage_set(&q->batch_id, batch->state);
687
_mesa_set_add(batch->state->active_queries, q);
688
}
689
690
static bool
691
zink_begin_query(struct pipe_context *pctx,
692
struct pipe_query *q)
693
{
694
struct zink_query *query = (struct zink_query *)q;
695
struct zink_context *ctx = zink_context(pctx);
696
struct zink_batch *batch = &ctx->batch;
697
698
query->last_start = query->curr_query;
699
/* drop all past results */
700
reset_qbo(query);
701
702
begin_query(ctx, batch, query);
703
704
return true;
705
}
706
707
static void
708
end_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q)
709
{
710
struct zink_screen *screen = zink_screen(ctx->base.screen);
711
ASSERTED struct zink_query_buffer *qbo = q->curr_qbo;
712
assert(qbo);
713
batch->has_work = true;
714
q->active = q->type == PIPE_QUERY_TIMESTAMP;
715
if (is_time_query(q)) {
716
if (q->needs_reset)
717
reset_pool(ctx, batch, q);
718
vkCmdWriteTimestamp(batch->state->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
719
q->query_pool, q->curr_query);
720
zink_batch_usage_set(&q->batch_id, batch->state);
721
} else if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED ||
722
q->type == PIPE_QUERY_PRIMITIVES_GENERATED ||
723
q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) {
724
screen->vk.CmdEndQueryIndexedEXT(batch->state->cmdbuf, q->xfb_query_pool[0] ? q->xfb_query_pool[0] :
725
q->query_pool,
726
q->curr_query, q->index);
727
}
728
729
else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) {
730
screen->vk.CmdEndQueryIndexedEXT(batch->state->cmdbuf, q->query_pool, q->curr_query, 0);
731
for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) {
732
screen->vk.CmdEndQueryIndexedEXT(batch->state->cmdbuf, q->xfb_query_pool[i], q->curr_query, i + 1);
733
}
734
}
735
if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT && !is_time_query(q))
736
vkCmdEndQuery(batch->state->cmdbuf, q->query_pool, q->curr_query);
737
738
if (needs_stats_list(q))
739
list_delinit(&q->stats_list);
740
if (++q->curr_query == NUM_QUERIES) {
741
/* always reset on start; this ensures we can actually submit the batch that the current query is on */
742
q->needs_reset = true;
743
}
744
745
if (batch->in_rp)
746
q->needs_update = true;
747
else
748
update_qbo(ctx, q);
749
}
750
751
static bool
752
zink_end_query(struct pipe_context *pctx,
753
struct pipe_query *q)
754
{
755
struct zink_context *ctx = zink_context(pctx);
756
struct zink_query *query = (struct zink_query *)q;
757
struct zink_batch *batch = &ctx->batch;
758
759
/* FIXME: this can be called from a thread, but it needs to write to the cmdbuf */
760
threaded_context_unwrap_sync(pctx);
761
762
if (needs_stats_list(query))
763
list_delinit(&query->stats_list);
764
if (query->active)
765
end_query(ctx, batch, query);
766
767
return true;
768
}
769
770
static bool
771
zink_get_query_result(struct pipe_context *pctx,
772
struct pipe_query *q,
773
bool wait,
774
union pipe_query_result *result)
775
{
776
struct zink_query *query = (void*)q;
777
struct zink_context *ctx = zink_context(pctx);
778
779
if (query->needs_update)
780
update_qbo(ctx, query);
781
782
if (zink_batch_usage_is_unflushed(query->batch_id)) {
783
if (!threaded_query(q)->flushed)
784
pctx->flush(pctx, NULL, 0);
785
if (!wait)
786
return false;
787
}
788
789
return get_query_result(pctx, q, wait, result);
790
}
791
792
void
793
zink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
794
{
795
set_foreach(batch->state->active_queries, entry) {
796
struct zink_query *query = (void*)entry->key;
797
/* if a query isn't active here then we don't need to reactivate it on the next batch */
798
if (query->active) {
799
end_query(ctx, batch, query);
800
/* the fence is going to steal the set off the batch, so we have to copy
801
* the active queries onto a list
802
*/
803
list_addtail(&query->active_list, &ctx->suspended_queries);
804
}
805
if (query->needs_update)
806
update_qbo(ctx, query);
807
if (query->last_start && query->curr_query > NUM_QUERIES / 2)
808
reset_pool(ctx, batch, query);
809
}
810
}
811
812
void
813
zink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
814
{
815
struct zink_query *query, *next;
816
LIST_FOR_EACH_ENTRY_SAFE(query, next, &ctx->suspended_queries, active_list) {
817
begin_query(ctx, batch, query);
818
list_delinit(&query->active_list);
819
}
820
}
821
822
void
823
zink_query_update_gs_states(struct zink_context *ctx)
824
{
825
struct zink_query *query;
826
LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) {
827
assert(query->curr_query < ARRAY_SIZE(query->have_gs));
828
assert(query->active);
829
query->have_gs[query->curr_query] = !!ctx->gfx_stages[PIPE_SHADER_GEOMETRY];
830
query->have_xfb[query->curr_query] = !!ctx->num_so_targets;
831
}
832
}
833
834
static void
835
zink_set_active_query_state(struct pipe_context *pctx, bool enable)
836
{
837
struct zink_context *ctx = zink_context(pctx);
838
ctx->queries_disabled = !enable;
839
840
struct zink_batch *batch = &ctx->batch;
841
if (ctx->queries_disabled)
842
zink_suspend_queries(ctx, batch);
843
else
844
zink_resume_queries(ctx, batch);
845
}
846
847
void
848
zink_start_conditional_render(struct zink_context *ctx)
849
{
850
struct zink_batch *batch = &ctx->batch;
851
struct zink_screen *screen = zink_screen(ctx->base.screen);
852
VkConditionalRenderingFlagsEXT begin_flags = 0;
853
if (ctx->render_condition.inverted)
854
begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
855
VkConditionalRenderingBeginInfoEXT begin_info = {0};
856
begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
857
begin_info.buffer = ctx->render_condition.query->predicate->obj->buffer;
858
begin_info.flags = begin_flags;
859
screen->vk.CmdBeginConditionalRenderingEXT(batch->state->cmdbuf, &begin_info);
860
zink_batch_reference_resource_rw(batch, ctx->render_condition.query->predicate, false);
861
}
862
863
void
864
zink_stop_conditional_render(struct zink_context *ctx)
865
{
866
struct zink_batch *batch = &ctx->batch;
867
struct zink_screen *screen = zink_screen(ctx->base.screen);
868
zink_clear_apply_conditionals(ctx);
869
screen->vk.CmdEndConditionalRenderingEXT(batch->state->cmdbuf);
870
}
871
872
static void
873
zink_render_condition(struct pipe_context *pctx,
874
struct pipe_query *pquery,
875
bool condition,
876
enum pipe_render_cond_flag mode)
877
{
878
struct zink_context *ctx = zink_context(pctx);
879
struct zink_query *query = (struct zink_query *)pquery;
880
zink_batch_no_rp(ctx);
881
VkQueryResultFlagBits flags = 0;
882
883
if (query == NULL) {
884
/* force conditional clears if they exist */
885
if (ctx->clears_enabled && !ctx->batch.in_rp)
886
zink_batch_rp(ctx);
887
if (ctx->batch.in_rp)
888
zink_stop_conditional_render(ctx);
889
ctx->render_condition_active = false;
890
ctx->render_condition.query = NULL;
891
return;
892
}
893
894
if (!query->predicate) {
895
struct pipe_resource *pres;
896
897
/* need to create a vulkan buffer to copy the data into */
898
pres = pipe_buffer_create(pctx->screen, PIPE_BIND_QUERY_BUFFER, PIPE_USAGE_DEFAULT, sizeof(uint64_t));
899
if (!pres)
900
return;
901
902
query->predicate = zink_resource(pres);
903
}
904
if (query->predicate_dirty) {
905
struct zink_resource *res = query->predicate;
906
907
if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
908
flags |= VK_QUERY_RESULT_WAIT_BIT;
909
910
flags |= VK_QUERY_RESULT_64_BIT;
911
int num_results = query->curr_query - query->last_start;
912
if (query->type != PIPE_QUERY_PRIMITIVES_GENERATED &&
913
!is_so_overflow_query(query)) {
914
copy_results_to_buffer(ctx, query, res, 0, num_results, flags);
915
} else {
916
/* these need special handling */
917
force_cpu_read(ctx, pquery, PIPE_QUERY_TYPE_U32, &res->base.b, 0);
918
}
919
query->predicate_dirty = false;
920
}
921
ctx->render_condition.inverted = condition;
922
ctx->render_condition_active = true;
923
ctx->render_condition.query = query;
924
if (ctx->batch.in_rp)
925
zink_start_conditional_render(ctx);
926
}
927
928
static void
929
zink_get_query_result_resource(struct pipe_context *pctx,
930
struct pipe_query *pquery,
931
bool wait,
932
enum pipe_query_value_type result_type,
933
int index,
934
struct pipe_resource *pres,
935
unsigned offset)
936
{
937
struct zink_context *ctx = zink_context(pctx);
938
struct zink_screen *screen = zink_screen(pctx->screen);
939
struct zink_query *query = (struct zink_query*)pquery;
940
struct zink_resource *res = zink_resource(pres);
941
unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t);
942
VkQueryResultFlagBits size_flags = result_type <= PIPE_QUERY_TYPE_U32 ? 0 : VK_QUERY_RESULT_64_BIT;
943
unsigned num_queries = query->curr_query - query->last_start;
944
unsigned query_id = query->last_start;
945
unsigned fences = p_atomic_read(&query->fences);
946
947
if (index == -1) {
948
/* VK_QUERY_RESULT_WITH_AVAILABILITY_BIT will ALWAYS write some kind of result data
949
* in addition to the availability result, which is a problem if we're just trying to get availability data
950
*
951
* if we know that there's no valid buffer data in the preceding buffer range, then we can just
952
* stomp on it with a glorious queued buffer copy instead of forcing a stall to manually write to the
953
* buffer
954
*/
955
956
VkQueryResultFlags flag = is_time_query(query) ? 0 : VK_QUERY_RESULT_PARTIAL_BIT;
957
if (!fences) {
958
uint64_t u64[2] = {0};
959
if (vkGetQueryPoolResults(screen->dev, query->query_pool, query_id, 1, 2 * result_size, u64,
960
0, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag) == VK_SUCCESS) {
961
pipe_buffer_write(pctx, pres, offset, result_size, (unsigned char*)u64 + result_size);
962
return;
963
}
964
}
965
struct pipe_resource *staging = pipe_buffer_create(pctx->screen, 0, PIPE_USAGE_STAGING, result_size * 2);
966
copy_results_to_buffer(ctx, query, zink_resource(staging), 0, 1, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag);
967
zink_copy_buffer(ctx, &ctx->batch, res, zink_resource(staging), offset, result_size, result_size);
968
pipe_resource_reference(&staging, NULL);
969
return;
970
}
971
972
if (!is_time_query(query) && !is_bool_query(query)) {
973
if (num_queries == 1 && query->type != PIPE_QUERY_PRIMITIVES_GENERATED &&
974
query->type != PIPE_QUERY_PRIMITIVES_EMITTED &&
975
!is_bool_query(query)) {
976
if (size_flags == VK_QUERY_RESULT_64_BIT) {
977
if (query->needs_update)
978
update_qbo(ctx, query);
979
/* internal qbo always writes 64bit value so we can just direct copy */
980
zink_copy_buffer(ctx, NULL, res, zink_resource(query->curr_qbo->buffer), offset,
981
get_buffer_offset(query, query->curr_qbo->buffer, query->last_start),
982
result_size);
983
} else
984
/* have to do a new copy for 32bit */
985
copy_results_to_buffer(ctx, query, res, offset, 1, size_flags);
986
return;
987
}
988
}
989
990
/* TODO: use CS to aggregate results */
991
992
/* unfortunately, there's no way to accumulate results from multiple queries on the gpu without either
993
* clobbering all but the last result or writing the results sequentially, so we have to manually write the result
994
*/
995
force_cpu_read(ctx, pquery, result_type, pres, offset);
996
}
997
998
static uint64_t
999
zink_get_timestamp(struct pipe_context *pctx)
1000
{
1001
struct zink_screen *screen = zink_screen(pctx->screen);
1002
uint64_t timestamp, deviation;
1003
assert(screen->info.have_EXT_calibrated_timestamps);
1004
VkCalibratedTimestampInfoEXT cti = {0};
1005
cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;
1006
cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;
1007
screen->vk.GetCalibratedTimestampsEXT(screen->dev, 1, &cti, &timestamp, &deviation);
1008
timestamp_to_nanoseconds(screen, &timestamp);
1009
return timestamp;
1010
}
1011
1012
void
1013
zink_context_query_init(struct pipe_context *pctx)
1014
{
1015
struct zink_context *ctx = zink_context(pctx);
1016
list_inithead(&ctx->suspended_queries);
1017
list_inithead(&ctx->primitives_generated_queries);
1018
1019
pctx->create_query = zink_create_query;
1020
pctx->destroy_query = zink_destroy_query;
1021
pctx->begin_query = zink_begin_query;
1022
pctx->end_query = zink_end_query;
1023
pctx->get_query_result = zink_get_query_result;
1024
pctx->get_query_result_resource = zink_get_query_result_resource;
1025
pctx->set_active_query_state = zink_set_active_query_state;
1026
pctx->render_condition = zink_render_condition;
1027
pctx->get_timestamp = zink_get_timestamp;
1028
}
1029
1030