17ec681f3Smrg#include "zink_query.h" 27ec681f3Smrg 37ec681f3Smrg#include "zink_context.h" 47ec681f3Smrg#include "zink_fence.h" 57ec681f3Smrg#include "zink_resource.h" 67ec681f3Smrg#include "zink_screen.h" 77ec681f3Smrg 87ec681f3Smrg#include "util/hash_table.h" 97ec681f3Smrg#include "util/set.h" 107ec681f3Smrg#include "util/u_dump.h" 117ec681f3Smrg#include "util/u_inlines.h" 127ec681f3Smrg#include "util/u_memory.h" 137ec681f3Smrg 147ec681f3Smrg#if defined(PIPE_ARCH_X86_64) || defined(PIPE_ARCH_PPC_64) || defined(PIPE_ARCH_AARCH64) || defined(PIPE_ARCH_MIPS64) 157ec681f3Smrg#define NUM_QUERIES 5000 167ec681f3Smrg#else 177ec681f3Smrg#define NUM_QUERIES 500 187ec681f3Smrg#endif 197ec681f3Smrg 207ec681f3Smrgstruct zink_query_buffer { 217ec681f3Smrg struct list_head list; 227ec681f3Smrg unsigned num_results; 237ec681f3Smrg struct pipe_resource *buffer; 247ec681f3Smrg struct pipe_resource *xfb_buffers[PIPE_MAX_VERTEX_STREAMS - 1]; 257ec681f3Smrg}; 267ec681f3Smrg 277ec681f3Smrgstruct zink_query { 287ec681f3Smrg struct threaded_query base; 297ec681f3Smrg enum pipe_query_type type; 307ec681f3Smrg 317ec681f3Smrg VkQueryPool query_pool; 327ec681f3Smrg VkQueryPool xfb_query_pool[PIPE_MAX_VERTEX_STREAMS - 1]; //stream 0 is in the base pool 337ec681f3Smrg unsigned curr_query, last_start; 347ec681f3Smrg 357ec681f3Smrg VkQueryType vkqtype; 367ec681f3Smrg unsigned index; 377ec681f3Smrg bool precise; 387ec681f3Smrg bool xfb_running; 397ec681f3Smrg bool xfb_overflow; 407ec681f3Smrg 417ec681f3Smrg bool active; /* query is considered active by vk */ 427ec681f3Smrg bool needs_reset; /* query is considered active by vk and cannot be destroyed */ 437ec681f3Smrg bool dead; /* query should be destroyed when its fence finishes */ 447ec681f3Smrg bool needs_update; /* query needs to update its qbos */ 457ec681f3Smrg 467ec681f3Smrg struct list_head active_list; 477ec681f3Smrg 487ec681f3Smrg struct list_head stats_list; /* when active, statistics queries are added to ctx->primitives_generated_queries */ 497ec681f3Smrg bool have_gs[NUM_QUERIES]; /* geometry shaders use GEOMETRY_SHADER_PRIMITIVES_BIT */ 507ec681f3Smrg bool have_xfb[NUM_QUERIES]; /* xfb was active during this query */ 517ec681f3Smrg 527ec681f3Smrg struct zink_batch_usage *batch_id; //batch that the query was started in 537ec681f3Smrg 547ec681f3Smrg struct list_head buffers; 557ec681f3Smrg union { 567ec681f3Smrg struct zink_query_buffer *curr_qbo; 577ec681f3Smrg struct pipe_fence_handle *fence; //PIPE_QUERY_GPU_FINISHED 587ec681f3Smrg }; 597ec681f3Smrg 607ec681f3Smrg struct zink_resource *predicate; 617ec681f3Smrg bool predicate_dirty; 627ec681f3Smrg}; 637ec681f3Smrg 647ec681f3Smrgstatic void 657ec681f3Smrgupdate_qbo(struct zink_context *ctx, struct zink_query *q); 667ec681f3Smrgstatic void 677ec681f3Smrgreset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q); 687ec681f3Smrg 697ec681f3Smrgstatic inline unsigned 707ec681f3Smrgget_num_results(enum pipe_query_type query_type) 717ec681f3Smrg{ 727ec681f3Smrg switch (query_type) { 737ec681f3Smrg case PIPE_QUERY_OCCLUSION_COUNTER: 747ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE: 757ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: 767ec681f3Smrg case PIPE_QUERY_TIME_ELAPSED: 777ec681f3Smrg case PIPE_QUERY_TIMESTAMP: 787ec681f3Smrg case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE: 797ec681f3Smrg return 1; 807ec681f3Smrg case PIPE_QUERY_PRIMITIVES_GENERATED: 817ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: 827ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_PREDICATE: 837ec681f3Smrg case PIPE_QUERY_PRIMITIVES_EMITTED: 847ec681f3Smrg return 2; 857ec681f3Smrg default: 867ec681f3Smrg debug_printf("unknown query: %s\n", 877ec681f3Smrg util_str_query_type(query_type, true)); 887ec681f3Smrg unreachable("zink: unknown query type"); 897ec681f3Smrg } 907ec681f3Smrg} 917ec681f3Smrg 927ec681f3Smrgstatic VkQueryPipelineStatisticFlags 937ec681f3Smrgpipeline_statistic_convert(enum pipe_statistics_query_index idx) 947ec681f3Smrg{ 957ec681f3Smrg unsigned map[] = { 967ec681f3Smrg [PIPE_STAT_QUERY_IA_VERTICES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT, 977ec681f3Smrg [PIPE_STAT_QUERY_IA_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT, 987ec681f3Smrg [PIPE_STAT_QUERY_VS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT, 997ec681f3Smrg [PIPE_STAT_QUERY_GS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT, 1007ec681f3Smrg [PIPE_STAT_QUERY_GS_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT, 1017ec681f3Smrg [PIPE_STAT_QUERY_C_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT, 1027ec681f3Smrg [PIPE_STAT_QUERY_C_PRIMITIVES] = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT, 1037ec681f3Smrg [PIPE_STAT_QUERY_PS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT, 1047ec681f3Smrg [PIPE_STAT_QUERY_HS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT, 1057ec681f3Smrg [PIPE_STAT_QUERY_DS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT, 1067ec681f3Smrg [PIPE_STAT_QUERY_CS_INVOCATIONS] = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT 1077ec681f3Smrg }; 1087ec681f3Smrg assert(idx < ARRAY_SIZE(map)); 1097ec681f3Smrg return map[idx]; 1107ec681f3Smrg} 1117ec681f3Smrg 1127ec681f3Smrgstatic void 1137ec681f3Smrgtimestamp_to_nanoseconds(struct zink_screen *screen, uint64_t *timestamp) 1147ec681f3Smrg{ 1157ec681f3Smrg /* The number of valid bits in a timestamp value is determined by 1167ec681f3Smrg * the VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is written. 1177ec681f3Smrg * - 17.5. Timestamp Queries 1187ec681f3Smrg */ 1197ec681f3Smrg if (screen->timestamp_valid_bits < 64) 1207ec681f3Smrg *timestamp &= (1ull << screen->timestamp_valid_bits) - 1; 1217ec681f3Smrg 1227ec681f3Smrg /* The number of nanoseconds it takes for a timestamp value to be incremented by 1 1237ec681f3Smrg * can be obtained from VkPhysicalDeviceLimits::timestampPeriod 1247ec681f3Smrg * - 17.5. Timestamp Queries 1257ec681f3Smrg */ 1267ec681f3Smrg *timestamp *= screen->info.props.limits.timestampPeriod; 1277ec681f3Smrg} 1287ec681f3Smrg 1297ec681f3Smrgstatic VkQueryType 1307ec681f3Smrgconvert_query_type(unsigned query_type, bool *precise) 1317ec681f3Smrg{ 1327ec681f3Smrg *precise = false; 1337ec681f3Smrg switch (query_type) { 1347ec681f3Smrg case PIPE_QUERY_OCCLUSION_COUNTER: 1357ec681f3Smrg *precise = true; 1367ec681f3Smrg FALLTHROUGH; 1377ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE: 1387ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: 1397ec681f3Smrg return VK_QUERY_TYPE_OCCLUSION; 1407ec681f3Smrg case PIPE_QUERY_TIME_ELAPSED: 1417ec681f3Smrg case PIPE_QUERY_TIMESTAMP: 1427ec681f3Smrg return VK_QUERY_TYPE_TIMESTAMP; 1437ec681f3Smrg case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE: 1447ec681f3Smrg case PIPE_QUERY_PRIMITIVES_GENERATED: 1457ec681f3Smrg return VK_QUERY_TYPE_PIPELINE_STATISTICS; 1467ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: 1477ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_PREDICATE: 1487ec681f3Smrg case PIPE_QUERY_PRIMITIVES_EMITTED: 1497ec681f3Smrg return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT; 1507ec681f3Smrg default: 1517ec681f3Smrg debug_printf("unknown query: %s\n", 1527ec681f3Smrg util_str_query_type(query_type, true)); 1537ec681f3Smrg unreachable("zink: unknown query type"); 1547ec681f3Smrg } 1557ec681f3Smrg} 1567ec681f3Smrg 1577ec681f3Smrgstatic bool 1587ec681f3Smrgneeds_stats_list(struct zink_query *query) 1597ec681f3Smrg{ 1607ec681f3Smrg return query->type == PIPE_QUERY_PRIMITIVES_GENERATED || 1617ec681f3Smrg query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE || 1627ec681f3Smrg query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE; 1637ec681f3Smrg} 1647ec681f3Smrg 1657ec681f3Smrgstatic bool 1667ec681f3Smrgis_time_query(struct zink_query *query) 1677ec681f3Smrg{ 1687ec681f3Smrg return query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIME_ELAPSED; 1697ec681f3Smrg} 1707ec681f3Smrg 1717ec681f3Smrgstatic bool 1727ec681f3Smrgis_so_overflow_query(struct zink_query *query) 1737ec681f3Smrg{ 1747ec681f3Smrg return query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE || query->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE; 1757ec681f3Smrg} 1767ec681f3Smrg 1777ec681f3Smrgstatic bool 1787ec681f3Smrgis_bool_query(struct zink_query *query) 1797ec681f3Smrg{ 1807ec681f3Smrg return is_so_overflow_query(query) || 1817ec681f3Smrg query->type == PIPE_QUERY_OCCLUSION_PREDICATE || 1827ec681f3Smrg query->type == PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE || 1837ec681f3Smrg query->type == PIPE_QUERY_GPU_FINISHED; 1847ec681f3Smrg} 1857ec681f3Smrg 1867ec681f3Smrgstatic bool 1877ec681f3Smrgqbo_append(struct pipe_screen *screen, struct zink_query *query) 1887ec681f3Smrg{ 1897ec681f3Smrg if (query->curr_qbo && query->curr_qbo->list.next) 1907ec681f3Smrg return true; 1917ec681f3Smrg struct zink_query_buffer *qbo = CALLOC_STRUCT(zink_query_buffer); 1927ec681f3Smrg if (!qbo) 1937ec681f3Smrg return false; 1947ec681f3Smrg qbo->buffer = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER, 1957ec681f3Smrg PIPE_USAGE_STAGING, 1967ec681f3Smrg /* this is the maximum possible size of the results in a given buffer */ 1977ec681f3Smrg NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t)); 1987ec681f3Smrg if (!qbo->buffer) 1997ec681f3Smrg goto fail; 2007ec681f3Smrg if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) { 2017ec681f3Smrg /* need separate xfb buffer */ 2027ec681f3Smrg qbo->xfb_buffers[0] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER, 2037ec681f3Smrg PIPE_USAGE_STAGING, 2047ec681f3Smrg /* this is the maximum possible size of the results in a given buffer */ 2057ec681f3Smrg NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t)); 2067ec681f3Smrg if (!qbo->xfb_buffers[0]) 2077ec681f3Smrg goto fail; 2087ec681f3Smrg } else if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 2097ec681f3Smrg /* need to monitor all xfb streams */ 2107ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++) { 2117ec681f3Smrg /* need separate xfb buffer */ 2127ec681f3Smrg qbo->xfb_buffers[i] = pipe_buffer_create(screen, PIPE_BIND_QUERY_BUFFER, 2137ec681f3Smrg PIPE_USAGE_STAGING, 2147ec681f3Smrg /* this is the maximum possible size of the results in a given buffer */ 2157ec681f3Smrg NUM_QUERIES * get_num_results(query->type) * sizeof(uint64_t)); 2167ec681f3Smrg if (!qbo->xfb_buffers[i]) 2177ec681f3Smrg goto fail; 2187ec681f3Smrg } 2197ec681f3Smrg } 2207ec681f3Smrg list_addtail(&qbo->list, &query->buffers); 2217ec681f3Smrg 2227ec681f3Smrg return true; 2237ec681f3Smrgfail: 2247ec681f3Smrg pipe_resource_reference(&qbo->buffer, NULL); 2257ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++) 2267ec681f3Smrg pipe_resource_reference(&qbo->xfb_buffers[i], NULL); 2277ec681f3Smrg FREE(qbo); 2287ec681f3Smrg return false; 2297ec681f3Smrg} 2307ec681f3Smrg 2317ec681f3Smrgstatic void 2327ec681f3Smrgdestroy_query(struct zink_screen *screen, struct zink_query *query) 2337ec681f3Smrg{ 2347ec681f3Smrg assert(zink_screen_usage_check_completion(screen, query->batch_id)); 2357ec681f3Smrg if (query->query_pool) 2367ec681f3Smrg VKSCR(DestroyQueryPool)(screen->dev, query->query_pool, NULL); 2377ec681f3Smrg struct zink_query_buffer *qbo, *next; 2387ec681f3Smrg LIST_FOR_EACH_ENTRY_SAFE(qbo, next, &query->buffers, list) { 2397ec681f3Smrg pipe_resource_reference(&qbo->buffer, NULL); 2407ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers); i++) 2417ec681f3Smrg pipe_resource_reference(&qbo->xfb_buffers[i], NULL); 2427ec681f3Smrg FREE(qbo); 2437ec681f3Smrg } 2447ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) { 2457ec681f3Smrg if (query->xfb_query_pool[i]) 2467ec681f3Smrg VKSCR(DestroyQueryPool)(screen->dev, query->xfb_query_pool[i], NULL); 2477ec681f3Smrg } 2487ec681f3Smrg pipe_resource_reference((struct pipe_resource**)&query->predicate, NULL); 2497ec681f3Smrg FREE(query); 2507ec681f3Smrg} 2517ec681f3Smrg 2527ec681f3Smrgstatic void 2537ec681f3Smrgreset_qbo(struct zink_query *q) 2547ec681f3Smrg{ 2557ec681f3Smrg q->curr_qbo = list_first_entry(&q->buffers, struct zink_query_buffer, list); 2567ec681f3Smrg q->curr_qbo->num_results = 0; 2577ec681f3Smrg} 2587ec681f3Smrg 2597ec681f3Smrgstatic struct pipe_query * 2607ec681f3Smrgzink_create_query(struct pipe_context *pctx, 2617ec681f3Smrg unsigned query_type, unsigned index) 2627ec681f3Smrg{ 2637ec681f3Smrg struct zink_screen *screen = zink_screen(pctx->screen); 2647ec681f3Smrg struct zink_query *query = CALLOC_STRUCT(zink_query); 2657ec681f3Smrg VkQueryPoolCreateInfo pool_create = {0}; 2667ec681f3Smrg 2677ec681f3Smrg if (!query) 2687ec681f3Smrg return NULL; 2697ec681f3Smrg list_inithead(&query->buffers); 2707ec681f3Smrg 2717ec681f3Smrg query->index = index; 2727ec681f3Smrg query->type = query_type; 2737ec681f3Smrg if (query->type == PIPE_QUERY_GPU_FINISHED) 2747ec681f3Smrg return (struct pipe_query *)query; 2757ec681f3Smrg query->vkqtype = convert_query_type(query_type, &query->precise); 2767ec681f3Smrg if (query->vkqtype == -1) 2777ec681f3Smrg return NULL; 2787ec681f3Smrg 2797ec681f3Smrg assert(!query->precise || query->vkqtype == VK_QUERY_TYPE_OCCLUSION); 2807ec681f3Smrg 2817ec681f3Smrg query->curr_query = 0; 2827ec681f3Smrg 2837ec681f3Smrg pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; 2847ec681f3Smrg pool_create.queryType = query->vkqtype; 2857ec681f3Smrg pool_create.queryCount = NUM_QUERIES; 2867ec681f3Smrg if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) 2877ec681f3Smrg pool_create.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT | 2887ec681f3Smrg VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT; 2897ec681f3Smrg else if (query_type == PIPE_QUERY_PIPELINE_STATISTICS_SINGLE) 2907ec681f3Smrg pool_create.pipelineStatistics = pipeline_statistic_convert(index); 2917ec681f3Smrg 2927ec681f3Smrg VkResult status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->query_pool); 2937ec681f3Smrg if (status != VK_SUCCESS) 2947ec681f3Smrg goto fail; 2957ec681f3Smrg if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED) { 2967ec681f3Smrg /* if xfb is active, we need to use an xfb query, otherwise we need pipeline statistics */ 2977ec681f3Smrg pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; 2987ec681f3Smrg pool_create.queryType = VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT; 2997ec681f3Smrg pool_create.queryCount = NUM_QUERIES; 3007ec681f3Smrg 3017ec681f3Smrg status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->xfb_query_pool[0]); 3027ec681f3Smrg if (status != VK_SUCCESS) 3037ec681f3Smrg goto fail; 3047ec681f3Smrg } else if (query_type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 3057ec681f3Smrg /* need to monitor all xfb streams */ 3067ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(query->xfb_query_pool); i++) { 3077ec681f3Smrg status = VKSCR(CreateQueryPool)(screen->dev, &pool_create, NULL, &query->xfb_query_pool[i]); 3087ec681f3Smrg if (status != VK_SUCCESS) 3097ec681f3Smrg goto fail; 3107ec681f3Smrg } 3117ec681f3Smrg } 3127ec681f3Smrg if (!qbo_append(pctx->screen, query)) 3137ec681f3Smrg goto fail; 3147ec681f3Smrg struct zink_batch *batch = &zink_context(pctx)->batch; 3157ec681f3Smrg batch->has_work = true; 3167ec681f3Smrg query->needs_reset = true; 3177ec681f3Smrg if (query->type == PIPE_QUERY_TIMESTAMP) { 3187ec681f3Smrg query->active = true; 3197ec681f3Smrg /* defer pool reset until end_query since we're guaranteed to be threadsafe then */ 3207ec681f3Smrg reset_qbo(query); 3217ec681f3Smrg } 3227ec681f3Smrg return (struct pipe_query *)query; 3237ec681f3Smrgfail: 3247ec681f3Smrg destroy_query(screen, query); 3257ec681f3Smrg return NULL; 3267ec681f3Smrg} 3277ec681f3Smrg 3287ec681f3Smrgstatic void 3297ec681f3Smrgzink_destroy_query(struct pipe_context *pctx, 3307ec681f3Smrg struct pipe_query *q) 3317ec681f3Smrg{ 3327ec681f3Smrg struct zink_screen *screen = zink_screen(pctx->screen); 3337ec681f3Smrg struct zink_query *query = (struct zink_query *)q; 3347ec681f3Smrg 3357ec681f3Smrg /* only destroy if this query isn't active on any batches, 3367ec681f3Smrg * otherwise just mark dead and wait 3377ec681f3Smrg */ 3387ec681f3Smrg if (query->batch_id) { 3397ec681f3Smrg p_atomic_set(&query->dead, true); 3407ec681f3Smrg return; 3417ec681f3Smrg } 3427ec681f3Smrg 3437ec681f3Smrg destroy_query(screen, query); 3447ec681f3Smrg} 3457ec681f3Smrg 3467ec681f3Smrgvoid 3477ec681f3Smrgzink_prune_query(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_query *query) 3487ec681f3Smrg{ 3497ec681f3Smrg if (!zink_batch_usage_matches(query->batch_id, bs)) 3507ec681f3Smrg return; 3517ec681f3Smrg query->batch_id = NULL; 3527ec681f3Smrg if (p_atomic_read(&query->dead)) 3537ec681f3Smrg destroy_query(screen, query); 3547ec681f3Smrg} 3557ec681f3Smrg 3567ec681f3Smrgstatic void 3577ec681f3Smrgcheck_query_results(struct zink_query *query, union pipe_query_result *result, 3587ec681f3Smrg int num_results, uint64_t *results, uint64_t *xfb_results) 3597ec681f3Smrg{ 3607ec681f3Smrg uint64_t last_val = 0; 3617ec681f3Smrg int result_size = get_num_results(query->type); 3627ec681f3Smrg for (int i = 0; i < num_results * result_size; i += result_size) { 3637ec681f3Smrg switch (query->type) { 3647ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE: 3657ec681f3Smrg case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: 3667ec681f3Smrg case PIPE_QUERY_GPU_FINISHED: 3677ec681f3Smrg result->b |= results[i] != 0; 3687ec681f3Smrg break; 3697ec681f3Smrg 3707ec681f3Smrg case PIPE_QUERY_TIME_ELAPSED: 3717ec681f3Smrg case PIPE_QUERY_TIMESTAMP: 3727ec681f3Smrg /* the application can sum the differences between all N queries to determine the total execution time. 3737ec681f3Smrg * - 17.5. Timestamp Queries 3747ec681f3Smrg */ 3757ec681f3Smrg if (query->type != PIPE_QUERY_TIME_ELAPSED || i) 3767ec681f3Smrg result->u64 += results[i] - last_val; 3777ec681f3Smrg last_val = results[i]; 3787ec681f3Smrg break; 3797ec681f3Smrg case PIPE_QUERY_OCCLUSION_COUNTER: 3807ec681f3Smrg result->u64 += results[i]; 3817ec681f3Smrg break; 3827ec681f3Smrg case PIPE_QUERY_PRIMITIVES_GENERATED: 3837ec681f3Smrg if (query->have_xfb[query->last_start + i / 2] || query->index) 3847ec681f3Smrg result->u64 += xfb_results[i + 1]; 3857ec681f3Smrg else 3867ec681f3Smrg /* if a given draw had a geometry shader, we need to use the second result */ 3877ec681f3Smrg result->u64 += results[i + query->have_gs[query->last_start + i / 2]]; 3887ec681f3Smrg break; 3897ec681f3Smrg case PIPE_QUERY_PRIMITIVES_EMITTED: 3907ec681f3Smrg /* A query pool created with this type will capture 2 integers - 3917ec681f3Smrg * numPrimitivesWritten and numPrimitivesNeeded - 3927ec681f3Smrg * for the specified vertex stream output from the last vertex processing stage. 3937ec681f3Smrg * - from VK_EXT_transform_feedback spec 3947ec681f3Smrg */ 3957ec681f3Smrg result->u64 += results[i]; 3967ec681f3Smrg break; 3977ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: 3987ec681f3Smrg case PIPE_QUERY_SO_OVERFLOW_PREDICATE: 3997ec681f3Smrg /* A query pool created with this type will capture 2 integers - 4007ec681f3Smrg * numPrimitivesWritten and numPrimitivesNeeded - 4017ec681f3Smrg * for the specified vertex stream output from the last vertex processing stage. 4027ec681f3Smrg * - from VK_EXT_transform_feedback spec 4037ec681f3Smrg */ 4047ec681f3Smrg if (query->have_xfb[query->last_start + i / 2]) 4057ec681f3Smrg result->b |= results[i] != results[i + 1]; 4067ec681f3Smrg break; 4077ec681f3Smrg case PIPE_QUERY_PIPELINE_STATISTICS_SINGLE: 4087ec681f3Smrg result->u64 += results[i]; 4097ec681f3Smrg break; 4107ec681f3Smrg 4117ec681f3Smrg default: 4127ec681f3Smrg debug_printf("unhandled query type: %s\n", 4137ec681f3Smrg util_str_query_type(query->type, true)); 4147ec681f3Smrg unreachable("unexpected query type"); 4157ec681f3Smrg } 4167ec681f3Smrg } 4177ec681f3Smrg} 4187ec681f3Smrg 4197ec681f3Smrgstatic bool 4207ec681f3Smrgget_query_result(struct pipe_context *pctx, 4217ec681f3Smrg struct pipe_query *q, 4227ec681f3Smrg bool wait, 4237ec681f3Smrg union pipe_query_result *result) 4247ec681f3Smrg{ 4257ec681f3Smrg struct zink_screen *screen = zink_screen(pctx->screen); 4267ec681f3Smrg struct zink_query *query = (struct zink_query *)q; 4277ec681f3Smrg unsigned flags = PIPE_MAP_READ; 4287ec681f3Smrg 4297ec681f3Smrg if (!wait) 4307ec681f3Smrg flags |= PIPE_MAP_DONTBLOCK; 4317ec681f3Smrg if (query->base.flushed) 4327ec681f3Smrg /* this is not a context-safe operation; ensure map doesn't use slab alloc */ 4337ec681f3Smrg flags |= PIPE_MAP_THREAD_SAFE; 4347ec681f3Smrg 4357ec681f3Smrg util_query_clear_result(result, query->type); 4367ec681f3Smrg 4377ec681f3Smrg int num_results = query->curr_query - query->last_start; 4387ec681f3Smrg int result_size = get_num_results(query->type) * sizeof(uint64_t); 4397ec681f3Smrg 4407ec681f3Smrg struct zink_query_buffer *qbo; 4417ec681f3Smrg struct pipe_transfer *xfer; 4427ec681f3Smrg LIST_FOR_EACH_ENTRY(qbo, &query->buffers, list) { 4437ec681f3Smrg uint64_t *xfb_results = NULL; 4447ec681f3Smrg uint64_t *results; 4457ec681f3Smrg bool is_timestamp = query->type == PIPE_QUERY_TIMESTAMP || query->type == PIPE_QUERY_TIMESTAMP_DISJOINT; 4467ec681f3Smrg if (!qbo->num_results) 4477ec681f3Smrg continue; 4487ec681f3Smrg results = pipe_buffer_map_range(pctx, qbo->buffer, 0, 4497ec681f3Smrg (is_timestamp ? 1 : qbo->num_results) * result_size, flags, &xfer); 4507ec681f3Smrg if (!results) { 4517ec681f3Smrg if (wait) 4527ec681f3Smrg debug_printf("zink: qbo read failed!"); 4537ec681f3Smrg return false; 4547ec681f3Smrg } 4557ec681f3Smrg struct pipe_transfer *xfb_xfer = NULL; 4567ec681f3Smrg if (query->type == PIPE_QUERY_PRIMITIVES_GENERATED) { 4577ec681f3Smrg xfb_results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[0], 0, 4587ec681f3Smrg qbo->num_results * result_size, flags, &xfb_xfer); 4597ec681f3Smrg if (!xfb_results) { 4607ec681f3Smrg if (wait) 4617ec681f3Smrg debug_printf("zink: xfb qbo read failed!"); 4627ec681f3Smrg pipe_buffer_unmap(pctx, xfer); 4637ec681f3Smrg return false; 4647ec681f3Smrg } 4657ec681f3Smrg } 4667ec681f3Smrg check_query_results(query, result, is_timestamp ? 1 : qbo->num_results, results, xfb_results); 4677ec681f3Smrg pipe_buffer_unmap(pctx, xfer); 4687ec681f3Smrg if (xfb_xfer) 4697ec681f3Smrg pipe_buffer_unmap(pctx, xfb_xfer); 4707ec681f3Smrg if (query->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 4717ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(qbo->xfb_buffers) && !result->b; i++) { 4727ec681f3Smrg uint64_t *results = pipe_buffer_map_range(pctx, qbo->xfb_buffers[i], 4737ec681f3Smrg 0, 4747ec681f3Smrg qbo->num_results * result_size, flags, &xfer); 4757ec681f3Smrg if (!results) { 4767ec681f3Smrg if (wait) 4777ec681f3Smrg debug_printf("zink: qbo read failed!"); 4787ec681f3Smrg return false; 4797ec681f3Smrg } 4807ec681f3Smrg check_query_results(query, result, num_results, results, xfb_results); 4817ec681f3Smrg pipe_buffer_unmap(pctx, xfer); 4827ec681f3Smrg } 4837ec681f3Smrg /* if overflow is detected we can stop */ 4847ec681f3Smrg if (result->b) 4857ec681f3Smrg break; 4867ec681f3Smrg } 4877ec681f3Smrg } 4887ec681f3Smrg 4897ec681f3Smrg if (is_time_query(query)) 4907ec681f3Smrg timestamp_to_nanoseconds(screen, &result->u64); 4917ec681f3Smrg 4927ec681f3Smrg return true; 4937ec681f3Smrg} 4947ec681f3Smrg 4957ec681f3Smrgstatic void 4967ec681f3Smrgforce_cpu_read(struct zink_context *ctx, struct pipe_query *pquery, enum pipe_query_value_type result_type, struct pipe_resource *pres, unsigned offset) 4977ec681f3Smrg{ 4987ec681f3Smrg struct pipe_context *pctx = &ctx->base; 4997ec681f3Smrg unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t); 5007ec681f3Smrg struct zink_query *query = (struct zink_query*)pquery; 5017ec681f3Smrg union pipe_query_result result; 5027ec681f3Smrg 5037ec681f3Smrg if (query->needs_update) 5047ec681f3Smrg update_qbo(ctx, query); 5057ec681f3Smrg 5067ec681f3Smrg bool success = get_query_result(pctx, pquery, true, &result); 5077ec681f3Smrg if (!success) { 5087ec681f3Smrg debug_printf("zink: getting query result failed\n"); 5097ec681f3Smrg return; 5107ec681f3Smrg } 5117ec681f3Smrg 5127ec681f3Smrg if (result_type <= PIPE_QUERY_TYPE_U32) { 5137ec681f3Smrg uint32_t u32; 5147ec681f3Smrg uint32_t limit; 5157ec681f3Smrg if (result_type == PIPE_QUERY_TYPE_I32) 5167ec681f3Smrg limit = INT_MAX; 5177ec681f3Smrg else 5187ec681f3Smrg limit = UINT_MAX; 5197ec681f3Smrg if (is_bool_query(query)) 5207ec681f3Smrg u32 = result.b; 5217ec681f3Smrg else 5227ec681f3Smrg u32 = MIN2(limit, result.u64); 5237ec681f3Smrg pipe_buffer_write(pctx, pres, offset, result_size, &u32); 5247ec681f3Smrg } else { 5257ec681f3Smrg uint64_t u64; 5267ec681f3Smrg if (is_bool_query(query)) 5277ec681f3Smrg u64 = result.b; 5287ec681f3Smrg else 5297ec681f3Smrg u64 = result.u64; 5307ec681f3Smrg pipe_buffer_write(pctx, pres, offset, result_size, &u64); 5317ec681f3Smrg } 5327ec681f3Smrg} 5337ec681f3Smrg 5347ec681f3Smrgstatic void 5357ec681f3Smrgcopy_pool_results_to_buffer(struct zink_context *ctx, struct zink_query *query, VkQueryPool pool, 5367ec681f3Smrg unsigned query_id, struct zink_resource *res, unsigned offset, 5377ec681f3Smrg int num_results, VkQueryResultFlags flags) 5387ec681f3Smrg{ 5397ec681f3Smrg struct zink_batch *batch = &ctx->batch; 5407ec681f3Smrg unsigned type_size = (flags & VK_QUERY_RESULT_64_BIT) ? sizeof(uint64_t) : sizeof(uint32_t); 5417ec681f3Smrg unsigned base_result_size = get_num_results(query->type) * type_size; 5427ec681f3Smrg unsigned result_size = base_result_size * num_results; 5437ec681f3Smrg if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) 5447ec681f3Smrg result_size += type_size; 5457ec681f3Smrg zink_batch_no_rp(ctx); 5467ec681f3Smrg /* if it's a single query that doesn't need special handling, we can copy it and be done */ 5477ec681f3Smrg zink_batch_reference_resource_rw(batch, res, true); 5487ec681f3Smrg zink_resource_buffer_barrier(ctx, res, VK_ACCESS_TRANSFER_WRITE_BIT, 0); 5497ec681f3Smrg util_range_add(&res->base.b, &res->valid_buffer_range, offset, offset + result_size); 5507ec681f3Smrg assert(query_id < NUM_QUERIES); 5517ec681f3Smrg VKCTX(CmdCopyQueryPoolResults)(batch->state->cmdbuf, pool, query_id, num_results, res->obj->buffer, 5527ec681f3Smrg offset, base_result_size, flags); 5537ec681f3Smrg} 5547ec681f3Smrg 5557ec681f3Smrgstatic void 5567ec681f3Smrgcopy_results_to_buffer(struct zink_context *ctx, struct zink_query *query, struct zink_resource *res, unsigned offset, int num_results, VkQueryResultFlags flags) 5577ec681f3Smrg{ 5587ec681f3Smrg copy_pool_results_to_buffer(ctx, query, query->query_pool, query->last_start, res, offset, num_results, flags); 5597ec681f3Smrg} 5607ec681f3Smrg 5617ec681f3Smrgstatic void 5627ec681f3Smrgreset_pool(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q) 5637ec681f3Smrg{ 5647ec681f3Smrg /* This command must only be called outside of a render pass instance 5657ec681f3Smrg * 5667ec681f3Smrg * - vkCmdResetQueryPool spec 5677ec681f3Smrg */ 5687ec681f3Smrg zink_batch_no_rp(ctx); 5697ec681f3Smrg if (q->needs_update) 5707ec681f3Smrg update_qbo(ctx, q); 5717ec681f3Smrg 5727ec681f3Smrg VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->query_pool, 0, NUM_QUERIES); 5737ec681f3Smrg if (q->type == PIPE_QUERY_PRIMITIVES_GENERATED) 5747ec681f3Smrg VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->xfb_query_pool[0], 0, NUM_QUERIES); 5757ec681f3Smrg else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 5767ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) 5777ec681f3Smrg VKCTX(CmdResetQueryPool)(batch->state->cmdbuf, q->xfb_query_pool[i], 0, NUM_QUERIES); 5787ec681f3Smrg } 5797ec681f3Smrg memset(q->have_gs, 0, sizeof(q->have_gs)); 5807ec681f3Smrg memset(q->have_xfb, 0, sizeof(q->have_xfb)); 5817ec681f3Smrg q->last_start = q->curr_query = 0; 5827ec681f3Smrg q->needs_reset = false; 5837ec681f3Smrg /* create new qbo for non-timestamp queries: 5847ec681f3Smrg * timestamp queries should never need more than 2 entries in the qbo 5857ec681f3Smrg */ 5867ec681f3Smrg if (q->type == PIPE_QUERY_TIMESTAMP) 5877ec681f3Smrg return; 5887ec681f3Smrg if (qbo_append(ctx->base.screen, q)) 5897ec681f3Smrg reset_qbo(q); 5907ec681f3Smrg else 5917ec681f3Smrg debug_printf("zink: qbo alloc failed on reset!"); 5927ec681f3Smrg} 5937ec681f3Smrg 5947ec681f3Smrgstatic inline unsigned 5957ec681f3Smrgget_buffer_offset(struct zink_query *q, struct pipe_resource *pres, unsigned query_id) 5967ec681f3Smrg{ 5977ec681f3Smrg return (query_id - q->last_start) * get_num_results(q->type) * sizeof(uint64_t); 5987ec681f3Smrg} 5997ec681f3Smrg 6007ec681f3Smrgstatic void 6017ec681f3Smrgupdate_qbo(struct zink_context *ctx, struct zink_query *q) 6027ec681f3Smrg{ 6037ec681f3Smrg struct zink_query_buffer *qbo = q->curr_qbo; 6047ec681f3Smrg unsigned offset = 0; 6057ec681f3Smrg uint32_t query_id = q->curr_query - 1; 6067ec681f3Smrg bool is_timestamp = q->type == PIPE_QUERY_TIMESTAMP || q->type == PIPE_QUERY_TIMESTAMP_DISJOINT; 6077ec681f3Smrg /* timestamp queries just write to offset 0 always */ 6087ec681f3Smrg if (!is_timestamp) 6097ec681f3Smrg offset = get_buffer_offset(q, qbo->buffer, query_id); 6107ec681f3Smrg copy_pool_results_to_buffer(ctx, q, q->query_pool, query_id, zink_resource(qbo->buffer), 6117ec681f3Smrg offset, 6127ec681f3Smrg 1, VK_QUERY_RESULT_64_BIT); 6137ec681f3Smrg 6147ec681f3Smrg if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED || 6157ec681f3Smrg q->type == PIPE_QUERY_PRIMITIVES_GENERATED || 6167ec681f3Smrg q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) { 6177ec681f3Smrg copy_pool_results_to_buffer(ctx, q, 6187ec681f3Smrg q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool, 6197ec681f3Smrg query_id, 6207ec681f3Smrg zink_resource(qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer), 6217ec681f3Smrg get_buffer_offset(q, qbo->xfb_buffers[0] ? qbo->xfb_buffers[0] : qbo->buffer, query_id), 6227ec681f3Smrg 1, VK_QUERY_RESULT_64_BIT); 6237ec681f3Smrg } 6247ec681f3Smrg 6257ec681f3Smrg else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 6267ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) { 6277ec681f3Smrg copy_pool_results_to_buffer(ctx, q, q->xfb_query_pool[i], query_id, zink_resource(qbo->xfb_buffers[i]), 6287ec681f3Smrg get_buffer_offset(q, qbo->xfb_buffers[i], query_id), 6297ec681f3Smrg 1, VK_QUERY_RESULT_64_BIT); 6307ec681f3Smrg } 6317ec681f3Smrg } 6327ec681f3Smrg 6337ec681f3Smrg if (!is_timestamp) 6347ec681f3Smrg q->curr_qbo->num_results++; 6357ec681f3Smrg else 6367ec681f3Smrg q->curr_qbo->num_results = 1; 6377ec681f3Smrg q->needs_update = false; 6387ec681f3Smrg} 6397ec681f3Smrg 6407ec681f3Smrgstatic void 6417ec681f3Smrgbegin_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q) 6427ec681f3Smrg{ 6437ec681f3Smrg VkQueryControlFlags flags = 0; 6447ec681f3Smrg 6457ec681f3Smrg q->predicate_dirty = true; 6467ec681f3Smrg if (q->needs_reset) 6477ec681f3Smrg reset_pool(ctx, batch, q); 6487ec681f3Smrg assert(q->curr_query < NUM_QUERIES); 6497ec681f3Smrg q->active = true; 6507ec681f3Smrg batch->has_work = true; 6517ec681f3Smrg if (q->type == PIPE_QUERY_TIME_ELAPSED) { 6527ec681f3Smrg VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, q->query_pool, q->curr_query); 6537ec681f3Smrg q->curr_query++; 6547ec681f3Smrg update_qbo(ctx, q); 6557ec681f3Smrg zink_batch_usage_set(&q->batch_id, batch->state); 6567ec681f3Smrg _mesa_set_add(batch->state->active_queries, q); 6577ec681f3Smrg } 6587ec681f3Smrg /* ignore the rest of begin_query for timestamps */ 6597ec681f3Smrg if (is_time_query(q)) 6607ec681f3Smrg return; 6617ec681f3Smrg if (q->precise) 6627ec681f3Smrg flags |= VK_QUERY_CONTROL_PRECISE_BIT; 6637ec681f3Smrg if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED || 6647ec681f3Smrg q->type == PIPE_QUERY_PRIMITIVES_GENERATED || 6657ec681f3Smrg q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) { 6667ec681f3Smrg VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf, 6677ec681f3Smrg q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool, 6687ec681f3Smrg q->curr_query, 6697ec681f3Smrg flags, 6707ec681f3Smrg q->index); 6717ec681f3Smrg q->xfb_running = true; 6727ec681f3Smrg } else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 6737ec681f3Smrg VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf, 6747ec681f3Smrg q->query_pool, 6757ec681f3Smrg q->curr_query, 6767ec681f3Smrg flags, 6777ec681f3Smrg 0); 6787ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) 6797ec681f3Smrg VKCTX(CmdBeginQueryIndexedEXT)(batch->state->cmdbuf, 6807ec681f3Smrg q->xfb_query_pool[i], 6817ec681f3Smrg q->curr_query, 6827ec681f3Smrg flags, 6837ec681f3Smrg i + 1); 6847ec681f3Smrg q->xfb_running = true; 6857ec681f3Smrg } 6867ec681f3Smrg if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) 6877ec681f3Smrg VKCTX(CmdBeginQuery)(batch->state->cmdbuf, q->query_pool, q->curr_query, flags); 6887ec681f3Smrg if (needs_stats_list(q)) 6897ec681f3Smrg list_addtail(&q->stats_list, &ctx->primitives_generated_queries); 6907ec681f3Smrg zink_batch_usage_set(&q->batch_id, batch->state); 6917ec681f3Smrg _mesa_set_add(batch->state->active_queries, q); 6927ec681f3Smrg} 6937ec681f3Smrg 6947ec681f3Smrgstatic bool 6957ec681f3Smrgzink_begin_query(struct pipe_context *pctx, 6967ec681f3Smrg struct pipe_query *q) 6977ec681f3Smrg{ 6987ec681f3Smrg struct zink_query *query = (struct zink_query *)q; 6997ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 7007ec681f3Smrg struct zink_batch *batch = &ctx->batch; 7017ec681f3Smrg 7027ec681f3Smrg query->last_start = query->curr_query; 7037ec681f3Smrg /* drop all past results */ 7047ec681f3Smrg reset_qbo(query); 7057ec681f3Smrg 7067ec681f3Smrg begin_query(ctx, batch, query); 7077ec681f3Smrg 7087ec681f3Smrg return true; 7097ec681f3Smrg} 7107ec681f3Smrg 7117ec681f3Smrgstatic void 7127ec681f3Smrgupdate_query_id(struct zink_context *ctx, struct zink_query *q) 7137ec681f3Smrg{ 7147ec681f3Smrg if (++q->curr_query == NUM_QUERIES) { 7157ec681f3Smrg /* always reset on start; this ensures we can actually submit the batch that the current query is on */ 7167ec681f3Smrg q->needs_reset = true; 7177ec681f3Smrg } 7187ec681f3Smrg ctx->batch.has_work = true; 7197ec681f3Smrg 7207ec681f3Smrg if (ctx->batch.in_rp) 7217ec681f3Smrg q->needs_update = true; 7227ec681f3Smrg else 7237ec681f3Smrg update_qbo(ctx, q); 7247ec681f3Smrg} 7257ec681f3Smrg 7267ec681f3Smrgstatic void 7277ec681f3Smrgend_query(struct zink_context *ctx, struct zink_batch *batch, struct zink_query *q) 7287ec681f3Smrg{ 7297ec681f3Smrg ASSERTED struct zink_query_buffer *qbo = q->curr_qbo; 7307ec681f3Smrg assert(qbo); 7317ec681f3Smrg assert(!is_time_query(q)); 7327ec681f3Smrg q->active = false; 7337ec681f3Smrg if (q->type == PIPE_QUERY_PRIMITIVES_EMITTED || 7347ec681f3Smrg q->type == PIPE_QUERY_PRIMITIVES_GENERATED || 7357ec681f3Smrg q->type == PIPE_QUERY_SO_OVERFLOW_PREDICATE) { 7367ec681f3Smrg VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf, 7377ec681f3Smrg q->xfb_query_pool[0] ? q->xfb_query_pool[0] : q->query_pool, 7387ec681f3Smrg q->curr_query, q->index); 7397ec681f3Smrg } 7407ec681f3Smrg 7417ec681f3Smrg else if (q->type == PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE) { 7427ec681f3Smrg VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf, q->query_pool, q->curr_query, 0); 7437ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(q->xfb_query_pool); i++) { 7447ec681f3Smrg VKCTX(CmdEndQueryIndexedEXT)(batch->state->cmdbuf, q->xfb_query_pool[i], q->curr_query, i + 1); 7457ec681f3Smrg } 7467ec681f3Smrg } 7477ec681f3Smrg if (q->vkqtype != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT && !is_time_query(q)) 7487ec681f3Smrg VKCTX(CmdEndQuery)(batch->state->cmdbuf, q->query_pool, q->curr_query); 7497ec681f3Smrg 7507ec681f3Smrg if (needs_stats_list(q)) 7517ec681f3Smrg list_delinit(&q->stats_list); 7527ec681f3Smrg 7537ec681f3Smrg update_query_id(ctx, q); 7547ec681f3Smrg} 7557ec681f3Smrg 7567ec681f3Smrgstatic bool 7577ec681f3Smrgzink_end_query(struct pipe_context *pctx, 7587ec681f3Smrg struct pipe_query *q) 7597ec681f3Smrg{ 7607ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 7617ec681f3Smrg struct zink_query *query = (struct zink_query *)q; 7627ec681f3Smrg struct zink_batch *batch = &ctx->batch; 7637ec681f3Smrg 7647ec681f3Smrg if (query->type == PIPE_QUERY_GPU_FINISHED) { 7657ec681f3Smrg pctx->flush(pctx, &query->fence, PIPE_FLUSH_DEFERRED); 7667ec681f3Smrg return true; 7677ec681f3Smrg } 7687ec681f3Smrg 7697ec681f3Smrg /* FIXME: this can be called from a thread, but it needs to write to the cmdbuf */ 7707ec681f3Smrg threaded_context_unwrap_sync(pctx); 7717ec681f3Smrg 7727ec681f3Smrg if (needs_stats_list(query)) 7737ec681f3Smrg list_delinit(&query->stats_list); 7747ec681f3Smrg if (is_time_query(query)) { 7757ec681f3Smrg if (query->needs_reset) 7767ec681f3Smrg reset_pool(ctx, batch, query); 7777ec681f3Smrg VKCTX(CmdWriteTimestamp)(batch->state->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 7787ec681f3Smrg query->query_pool, query->curr_query); 7797ec681f3Smrg zink_batch_usage_set(&query->batch_id, batch->state); 7807ec681f3Smrg _mesa_set_add(batch->state->active_queries, query); 7817ec681f3Smrg update_query_id(ctx, query); 7827ec681f3Smrg } else if (query->active) 7837ec681f3Smrg end_query(ctx, batch, query); 7847ec681f3Smrg 7857ec681f3Smrg return true; 7867ec681f3Smrg} 7877ec681f3Smrg 7887ec681f3Smrgstatic bool 7897ec681f3Smrgzink_get_query_result(struct pipe_context *pctx, 7907ec681f3Smrg struct pipe_query *q, 7917ec681f3Smrg bool wait, 7927ec681f3Smrg union pipe_query_result *result) 7937ec681f3Smrg{ 7947ec681f3Smrg struct zink_query *query = (void*)q; 7957ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 7967ec681f3Smrg 7977ec681f3Smrg if (query->type == PIPE_QUERY_GPU_FINISHED) { 7987ec681f3Smrg struct pipe_screen *screen = pctx->screen; 7997ec681f3Smrg 8007ec681f3Smrg result->b = screen->fence_finish(screen, query->base.flushed ? NULL : pctx, 8017ec681f3Smrg query->fence, wait ? PIPE_TIMEOUT_INFINITE : 0); 8027ec681f3Smrg return result->b; 8037ec681f3Smrg } 8047ec681f3Smrg 8057ec681f3Smrg if (query->needs_update) 8067ec681f3Smrg update_qbo(ctx, query); 8077ec681f3Smrg 8087ec681f3Smrg if (zink_batch_usage_is_unflushed(query->batch_id)) { 8097ec681f3Smrg if (!threaded_query(q)->flushed) 8107ec681f3Smrg pctx->flush(pctx, NULL, 0); 8117ec681f3Smrg if (!wait) 8127ec681f3Smrg return false; 8137ec681f3Smrg } else if (!threaded_query(q)->flushed && 8147ec681f3Smrg /* timeline drivers can wait during buffer map */ 8157ec681f3Smrg !zink_screen(pctx->screen)->info.have_KHR_timeline_semaphore) 8167ec681f3Smrg zink_batch_usage_check_completion(ctx, query->batch_id); 8177ec681f3Smrg 8187ec681f3Smrg return get_query_result(pctx, q, wait, result); 8197ec681f3Smrg} 8207ec681f3Smrg 8217ec681f3Smrgvoid 8227ec681f3Smrgzink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch) 8237ec681f3Smrg{ 8247ec681f3Smrg set_foreach(batch->state->active_queries, entry) { 8257ec681f3Smrg struct zink_query *query = (void*)entry->key; 8267ec681f3Smrg /* if a query isn't active here then we don't need to reactivate it on the next batch */ 8277ec681f3Smrg if (query->active && !is_time_query(query)) { 8287ec681f3Smrg end_query(ctx, batch, query); 8297ec681f3Smrg /* the fence is going to steal the set off the batch, so we have to copy 8307ec681f3Smrg * the active queries onto a list 8317ec681f3Smrg */ 8327ec681f3Smrg list_addtail(&query->active_list, &ctx->suspended_queries); 8337ec681f3Smrg } 8347ec681f3Smrg if (query->needs_update) 8357ec681f3Smrg update_qbo(ctx, query); 8367ec681f3Smrg if (query->last_start && query->curr_query > NUM_QUERIES / 2) 8377ec681f3Smrg reset_pool(ctx, batch, query); 8387ec681f3Smrg } 8397ec681f3Smrg} 8407ec681f3Smrg 8417ec681f3Smrgvoid 8427ec681f3Smrgzink_resume_queries(struct zink_context *ctx, struct zink_batch *batch) 8437ec681f3Smrg{ 8447ec681f3Smrg struct zink_query *query, *next; 8457ec681f3Smrg LIST_FOR_EACH_ENTRY_SAFE(query, next, &ctx->suspended_queries, active_list) { 8467ec681f3Smrg begin_query(ctx, batch, query); 8477ec681f3Smrg list_delinit(&query->active_list); 8487ec681f3Smrg } 8497ec681f3Smrg} 8507ec681f3Smrg 8517ec681f3Smrgvoid 8527ec681f3Smrgzink_query_update_gs_states(struct zink_context *ctx) 8537ec681f3Smrg{ 8547ec681f3Smrg struct zink_query *query; 8557ec681f3Smrg LIST_FOR_EACH_ENTRY(query, &ctx->primitives_generated_queries, stats_list) { 8567ec681f3Smrg assert(query->curr_query < ARRAY_SIZE(query->have_gs)); 8577ec681f3Smrg assert(query->active); 8587ec681f3Smrg query->have_gs[query->curr_query] = !!ctx->gfx_stages[PIPE_SHADER_GEOMETRY]; 8597ec681f3Smrg query->have_xfb[query->curr_query] = !!ctx->num_so_targets; 8607ec681f3Smrg } 8617ec681f3Smrg} 8627ec681f3Smrg 8637ec681f3Smrgstatic void 8647ec681f3Smrgzink_set_active_query_state(struct pipe_context *pctx, bool enable) 8657ec681f3Smrg{ 8667ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 8677ec681f3Smrg ctx->queries_disabled = !enable; 8687ec681f3Smrg 8697ec681f3Smrg struct zink_batch *batch = &ctx->batch; 8707ec681f3Smrg if (ctx->queries_disabled) 8717ec681f3Smrg zink_suspend_queries(ctx, batch); 8727ec681f3Smrg else 8737ec681f3Smrg zink_resume_queries(ctx, batch); 8747ec681f3Smrg} 8757ec681f3Smrg 8767ec681f3Smrgvoid 8777ec681f3Smrgzink_start_conditional_render(struct zink_context *ctx) 8787ec681f3Smrg{ 8797ec681f3Smrg if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering)) 8807ec681f3Smrg return; 8817ec681f3Smrg struct zink_batch *batch = &ctx->batch; 8827ec681f3Smrg VkConditionalRenderingFlagsEXT begin_flags = 0; 8837ec681f3Smrg if (ctx->render_condition.inverted) 8847ec681f3Smrg begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; 8857ec681f3Smrg VkConditionalRenderingBeginInfoEXT begin_info = {0}; 8867ec681f3Smrg begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT; 8877ec681f3Smrg begin_info.buffer = ctx->render_condition.query->predicate->obj->buffer; 8887ec681f3Smrg begin_info.flags = begin_flags; 8897ec681f3Smrg VKCTX(CmdBeginConditionalRenderingEXT)(batch->state->cmdbuf, &begin_info); 8907ec681f3Smrg zink_batch_reference_resource_rw(batch, ctx->render_condition.query->predicate, false); 8917ec681f3Smrg} 8927ec681f3Smrg 8937ec681f3Smrgvoid 8947ec681f3Smrgzink_stop_conditional_render(struct zink_context *ctx) 8957ec681f3Smrg{ 8967ec681f3Smrg struct zink_batch *batch = &ctx->batch; 8977ec681f3Smrg zink_clear_apply_conditionals(ctx); 8987ec681f3Smrg if (unlikely(!zink_screen(ctx->base.screen)->info.have_EXT_conditional_rendering)) 8997ec681f3Smrg return; 9007ec681f3Smrg VKCTX(CmdEndConditionalRenderingEXT)(batch->state->cmdbuf); 9017ec681f3Smrg} 9027ec681f3Smrg 9037ec681f3Smrgbool 9047ec681f3Smrgzink_check_conditional_render(struct zink_context *ctx) 9057ec681f3Smrg{ 9067ec681f3Smrg if (!ctx->render_condition_active) 9077ec681f3Smrg return true; 9087ec681f3Smrg assert(ctx->render_condition.query); 9097ec681f3Smrg 9107ec681f3Smrg union pipe_query_result result; 9117ec681f3Smrg zink_get_query_result(&ctx->base, (struct pipe_query*)ctx->render_condition.query, true, &result); 9127ec681f3Smrg return is_bool_query(ctx->render_condition.query) ? 9137ec681f3Smrg ctx->render_condition.inverted != result.b : 9147ec681f3Smrg ctx->render_condition.inverted != !!result.u64; 9157ec681f3Smrg} 9167ec681f3Smrg 9177ec681f3Smrgstatic void 9187ec681f3Smrgzink_render_condition(struct pipe_context *pctx, 9197ec681f3Smrg struct pipe_query *pquery, 9207ec681f3Smrg bool condition, 9217ec681f3Smrg enum pipe_render_cond_flag mode) 9227ec681f3Smrg{ 9237ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 9247ec681f3Smrg struct zink_query *query = (struct zink_query *)pquery; 9257ec681f3Smrg zink_batch_no_rp(ctx); 9267ec681f3Smrg VkQueryResultFlagBits flags = 0; 9277ec681f3Smrg 9287ec681f3Smrg if (query == NULL) { 9297ec681f3Smrg /* force conditional clears if they exist */ 9307ec681f3Smrg if (ctx->clears_enabled && !ctx->batch.in_rp) 9317ec681f3Smrg zink_batch_rp(ctx); 9327ec681f3Smrg if (ctx->batch.in_rp) 9337ec681f3Smrg zink_stop_conditional_render(ctx); 9347ec681f3Smrg ctx->render_condition_active = false; 9357ec681f3Smrg ctx->render_condition.query = NULL; 9367ec681f3Smrg return; 9377ec681f3Smrg } 9387ec681f3Smrg 9397ec681f3Smrg if (!query->predicate) { 9407ec681f3Smrg struct pipe_resource *pres; 9417ec681f3Smrg 9427ec681f3Smrg /* need to create a vulkan buffer to copy the data into */ 9437ec681f3Smrg pres = pipe_buffer_create(pctx->screen, PIPE_BIND_QUERY_BUFFER, PIPE_USAGE_DEFAULT, sizeof(uint64_t)); 9447ec681f3Smrg if (!pres) 9457ec681f3Smrg return; 9467ec681f3Smrg 9477ec681f3Smrg query->predicate = zink_resource(pres); 9487ec681f3Smrg } 9497ec681f3Smrg if (query->predicate_dirty) { 9507ec681f3Smrg struct zink_resource *res = query->predicate; 9517ec681f3Smrg 9527ec681f3Smrg if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT) 9537ec681f3Smrg flags |= VK_QUERY_RESULT_WAIT_BIT; 9547ec681f3Smrg 9557ec681f3Smrg flags |= VK_QUERY_RESULT_64_BIT; 9567ec681f3Smrg int num_results = query->curr_query - query->last_start; 9577ec681f3Smrg if (query->type != PIPE_QUERY_PRIMITIVES_GENERATED && 9587ec681f3Smrg !is_so_overflow_query(query)) { 9597ec681f3Smrg copy_results_to_buffer(ctx, query, res, 0, num_results, flags); 9607ec681f3Smrg } else { 9617ec681f3Smrg /* these need special handling */ 9627ec681f3Smrg force_cpu_read(ctx, pquery, PIPE_QUERY_TYPE_U32, &res->base.b, 0); 9637ec681f3Smrg } 9647ec681f3Smrg query->predicate_dirty = false; 9657ec681f3Smrg } 9667ec681f3Smrg ctx->render_condition.inverted = condition; 9677ec681f3Smrg ctx->render_condition_active = true; 9687ec681f3Smrg ctx->render_condition.query = query; 9697ec681f3Smrg if (ctx->batch.in_rp) 9707ec681f3Smrg zink_start_conditional_render(ctx); 9717ec681f3Smrg} 9727ec681f3Smrg 9737ec681f3Smrgstatic void 9747ec681f3Smrgzink_get_query_result_resource(struct pipe_context *pctx, 9757ec681f3Smrg struct pipe_query *pquery, 9767ec681f3Smrg bool wait, 9777ec681f3Smrg enum pipe_query_value_type result_type, 9787ec681f3Smrg int index, 9797ec681f3Smrg struct pipe_resource *pres, 9807ec681f3Smrg unsigned offset) 9817ec681f3Smrg{ 9827ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 9837ec681f3Smrg struct zink_screen *screen = zink_screen(pctx->screen); 9847ec681f3Smrg struct zink_query *query = (struct zink_query*)pquery; 9857ec681f3Smrg struct zink_resource *res = zink_resource(pres); 9867ec681f3Smrg unsigned result_size = result_type <= PIPE_QUERY_TYPE_U32 ? sizeof(uint32_t) : sizeof(uint64_t); 9877ec681f3Smrg VkQueryResultFlagBits size_flags = result_type <= PIPE_QUERY_TYPE_U32 ? 0 : VK_QUERY_RESULT_64_BIT; 9887ec681f3Smrg unsigned num_queries = query->curr_query - query->last_start; 9897ec681f3Smrg unsigned query_id = query->last_start; 9907ec681f3Smrg 9917ec681f3Smrg if (index == -1) { 9927ec681f3Smrg /* VK_QUERY_RESULT_WITH_AVAILABILITY_BIT will ALWAYS write some kind of result data 9937ec681f3Smrg * in addition to the availability result, which is a problem if we're just trying to get availability data 9947ec681f3Smrg * 9957ec681f3Smrg * if we know that there's no valid buffer data in the preceding buffer range, then we can just 9967ec681f3Smrg * stomp on it with a glorious queued buffer copy instead of forcing a stall to manually write to the 9977ec681f3Smrg * buffer 9987ec681f3Smrg */ 9997ec681f3Smrg 10007ec681f3Smrg VkQueryResultFlags flag = is_time_query(query) ? 0 : VK_QUERY_RESULT_PARTIAL_BIT; 10017ec681f3Smrg unsigned src_offset = result_size * get_num_results(query->type); 10027ec681f3Smrg if (zink_batch_usage_check_completion(ctx, query->batch_id)) { 10037ec681f3Smrg uint64_t u64[4] = {0}; 10047ec681f3Smrg if (VKCTX(GetQueryPoolResults)(screen->dev, query->query_pool, query_id, 1, sizeof(u64), u64, 10057ec681f3Smrg 0, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag) == VK_SUCCESS) { 10067ec681f3Smrg pipe_buffer_write(pctx, pres, offset, result_size, (unsigned char*)u64 + src_offset); 10077ec681f3Smrg return; 10087ec681f3Smrg } 10097ec681f3Smrg } 10107ec681f3Smrg struct pipe_resource *staging = pipe_buffer_create(pctx->screen, 0, PIPE_USAGE_STAGING, src_offset + result_size); 10117ec681f3Smrg copy_results_to_buffer(ctx, query, zink_resource(staging), 0, 1, size_flags | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT | flag); 10127ec681f3Smrg zink_copy_buffer(ctx, res, zink_resource(staging), offset, result_size * get_num_results(query->type), result_size); 10137ec681f3Smrg pipe_resource_reference(&staging, NULL); 10147ec681f3Smrg return; 10157ec681f3Smrg } 10167ec681f3Smrg 10177ec681f3Smrg if (!is_time_query(query) && !is_bool_query(query)) { 10187ec681f3Smrg if (num_queries == 1 && query->type != PIPE_QUERY_PRIMITIVES_GENERATED && 10197ec681f3Smrg query->type != PIPE_QUERY_PRIMITIVES_EMITTED && 10207ec681f3Smrg !is_bool_query(query)) { 10217ec681f3Smrg if (size_flags == VK_QUERY_RESULT_64_BIT) { 10227ec681f3Smrg if (query->needs_update) 10237ec681f3Smrg update_qbo(ctx, query); 10247ec681f3Smrg /* internal qbo always writes 64bit value so we can just direct copy */ 10257ec681f3Smrg zink_copy_buffer(ctx, res, zink_resource(query->curr_qbo->buffer), offset, 10267ec681f3Smrg get_buffer_offset(query, query->curr_qbo->buffer, query->last_start), 10277ec681f3Smrg result_size); 10287ec681f3Smrg } else 10297ec681f3Smrg /* have to do a new copy for 32bit */ 10307ec681f3Smrg copy_results_to_buffer(ctx, query, res, offset, 1, size_flags); 10317ec681f3Smrg return; 10327ec681f3Smrg } 10337ec681f3Smrg } 10347ec681f3Smrg 10357ec681f3Smrg /* TODO: use CS to aggregate results */ 10367ec681f3Smrg 10377ec681f3Smrg /* unfortunately, there's no way to accumulate results from multiple queries on the gpu without either 10387ec681f3Smrg * clobbering all but the last result or writing the results sequentially, so we have to manually write the result 10397ec681f3Smrg */ 10407ec681f3Smrg force_cpu_read(ctx, pquery, result_type, pres, offset); 10417ec681f3Smrg} 10427ec681f3Smrg 10437ec681f3Smrgstatic uint64_t 10447ec681f3Smrgzink_get_timestamp(struct pipe_context *pctx) 10457ec681f3Smrg{ 10467ec681f3Smrg struct zink_screen *screen = zink_screen(pctx->screen); 10477ec681f3Smrg uint64_t timestamp, deviation; 10487ec681f3Smrg assert(screen->info.have_EXT_calibrated_timestamps); 10497ec681f3Smrg VkCalibratedTimestampInfoEXT cti = {0}; 10507ec681f3Smrg cti.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT; 10517ec681f3Smrg cti.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT; 10527ec681f3Smrg VKSCR(GetCalibratedTimestampsEXT)(screen->dev, 1, &cti, ×tamp, &deviation); 10537ec681f3Smrg timestamp_to_nanoseconds(screen, ×tamp); 10547ec681f3Smrg return timestamp; 10557ec681f3Smrg} 10567ec681f3Smrg 10577ec681f3Smrgvoid 10587ec681f3Smrgzink_context_query_init(struct pipe_context *pctx) 10597ec681f3Smrg{ 10607ec681f3Smrg struct zink_context *ctx = zink_context(pctx); 10617ec681f3Smrg list_inithead(&ctx->suspended_queries); 10627ec681f3Smrg list_inithead(&ctx->primitives_generated_queries); 10637ec681f3Smrg 10647ec681f3Smrg pctx->create_query = zink_create_query; 10657ec681f3Smrg pctx->destroy_query = zink_destroy_query; 10667ec681f3Smrg pctx->begin_query = zink_begin_query; 10677ec681f3Smrg pctx->end_query = zink_end_query; 10687ec681f3Smrg pctx->get_query_result = zink_get_query_result; 10697ec681f3Smrg pctx->get_query_result_resource = zink_get_query_result_resource; 10707ec681f3Smrg pctx->set_active_query_state = zink_set_active_query_state; 10717ec681f3Smrg pctx->render_condition = zink_render_condition; 10727ec681f3Smrg pctx->get_timestamp = zink_get_timestamp; 10737ec681f3Smrg} 1074