| /* |
| * Copyright © 2019 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include "iris_monitor.h" |
| |
| #include <xf86drm.h> |
| |
| #include "iris_screen.h" |
| #include "iris_context.h" |
| #include "iris_perf.h" |
| |
| struct iris_monitor_object { |
| int num_active_counters; |
| int *active_counters; |
| |
| size_t result_size; |
| unsigned char *result_buffer; |
| |
| struct gen_perf_query_object *query; |
| }; |
| |
| int |
| iris_get_monitor_info(struct pipe_screen *pscreen, unsigned index, |
| struct pipe_driver_query_info *info) |
| { |
| const struct iris_screen *screen = (struct iris_screen *)pscreen; |
| const struct gen_perf_config *perf_cfg = screen->perf_cfg; |
| assert(perf_cfg); |
| if (!perf_cfg) |
| return 0; |
| |
| if (!info) { |
| /* return the number of metrics */ |
| return perf_cfg->n_counters; |
| } |
| |
| struct gen_perf_query_counter_info *counter_info = &perf_cfg->counter_infos[index]; |
| struct gen_perf_query_counter *counter = counter_info->counter; |
| |
| info->group_id = counter_info->location.group_idx; |
| info->name = counter->name; |
| info->query_type = PIPE_QUERY_DRIVER_SPECIFIC + index; |
| |
| if (counter->type == GEN_PERF_COUNTER_TYPE_THROUGHPUT) |
| info->result_type = PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE; |
| else |
| info->result_type = PIPE_DRIVER_QUERY_RESULT_TYPE_CUMULATIVE; |
| switch (counter->data_type) { |
| case GEN_PERF_COUNTER_DATA_TYPE_BOOL32: |
| case GEN_PERF_COUNTER_DATA_TYPE_UINT32: |
| info->type = PIPE_DRIVER_QUERY_TYPE_UINT; |
| assert(counter->raw_max <= UINT32_MAX); |
| info->max_value.u32 = (uint32_t)counter->raw_max; |
| break; |
| case GEN_PERF_COUNTER_DATA_TYPE_UINT64: |
| info->type = PIPE_DRIVER_QUERY_TYPE_UINT64; |
| info->max_value.u64 = counter->raw_max; |
| break; |
| case GEN_PERF_COUNTER_DATA_TYPE_FLOAT: |
| case GEN_PERF_COUNTER_DATA_TYPE_DOUBLE: |
| info->type = PIPE_DRIVER_QUERY_TYPE_FLOAT; |
| info->max_value.f = counter->raw_max; |
| break; |
| default: |
| assert(false); |
| break; |
| } |
| |
| /* indicates that this is an OA query, not a pipeline statistics query */ |
| info->flags = PIPE_DRIVER_QUERY_FLAG_BATCH; |
| return 1; |
| } |
| |
| static bool |
| iris_monitor_init_metrics(struct iris_screen *screen) |
| { |
| struct gen_perf_config *perf_cfg = gen_perf_new(screen); |
| if (unlikely(!perf_cfg)) |
| return false; |
| |
| screen->perf_cfg = perf_cfg; |
| |
| iris_perf_init_vtbl(perf_cfg); |
| |
| gen_perf_init_metrics(perf_cfg, &screen->devinfo, screen->fd, |
| true /* pipeline stats*/); |
| |
| return perf_cfg->n_counters > 0; |
| } |
| |
| int |
| iris_get_monitor_group_info(struct pipe_screen *pscreen, |
| unsigned group_index, |
| struct pipe_driver_query_group_info *info) |
| { |
| struct iris_screen *screen = (struct iris_screen *)pscreen; |
| if (!screen->perf_cfg) { |
| if (!iris_monitor_init_metrics(screen)) |
| return 0; |
| } |
| |
| const struct gen_perf_config *perf_cfg = screen->perf_cfg; |
| |
| if (!info) { |
| /* return the count that can be queried */ |
| return perf_cfg->n_queries; |
| } |
| |
| if (group_index >= perf_cfg->n_queries) { |
| /* out of range */ |
| return 0; |
| } |
| |
| struct gen_perf_query_info *query = &perf_cfg->queries[group_index]; |
| |
| info->name = query->name; |
| info->max_active_queries = query->n_counters; |
| info->num_queries = query->n_counters; |
| |
| return 1; |
| } |
| |
| static void |
| iris_init_monitor_ctx(struct iris_context *ice) |
| { |
| struct iris_screen *screen = (struct iris_screen *) ice->ctx.screen; |
| |
| ice->perf_ctx = gen_perf_new_context(ice); |
| if (unlikely(!ice->perf_ctx)) |
| return; |
| |
| struct gen_perf_context *perf_ctx = ice->perf_ctx; |
| struct gen_perf_config *perf_cfg = screen->perf_cfg; |
| gen_perf_init_context(perf_ctx, |
| perf_cfg, |
| ice, |
| screen->bufmgr, |
| &screen->devinfo, |
| ice->batches[IRIS_BATCH_RENDER].hw_ctx_id, |
| screen->fd); |
| } |
| |
| /* entry point for GenPerfMonitorsAMD */ |
| struct iris_monitor_object * |
| iris_create_monitor_object(struct iris_context *ice, |
| unsigned num_queries, |
| unsigned *query_types) |
| { |
| struct iris_screen *screen = (struct iris_screen *) ice->ctx.screen; |
| struct gen_perf_config *perf_cfg = screen->perf_cfg; |
| struct gen_perf_query_object *query_obj = NULL; |
| |
| /* initialize perf context if this has not already been done. This |
| * function is the first entry point that carries the gl context. |
| */ |
| if (ice->perf_ctx == NULL) { |
| iris_init_monitor_ctx(ice); |
| } |
| struct gen_perf_context *perf_ctx = ice->perf_ctx; |
| |
| assert(num_queries > 0); |
| int query_index = query_types[0] - PIPE_QUERY_DRIVER_SPECIFIC; |
| assert(query_index <= perf_cfg->n_counters); |
| const int group = perf_cfg->counter_infos[query_index].location.group_idx; |
| |
| struct iris_monitor_object *monitor = |
| calloc(1, sizeof(struct iris_monitor_object)); |
| if (unlikely(!monitor)) |
| goto allocation_failure; |
| |
| monitor->num_active_counters = num_queries; |
| monitor->active_counters = calloc(num_queries, sizeof(int)); |
| if (unlikely(!monitor->active_counters)) |
| goto allocation_failure; |
| |
| for (int i = 0; i < num_queries; ++i) { |
| unsigned current_query = query_types[i]; |
| unsigned current_query_index = current_query - PIPE_QUERY_DRIVER_SPECIFIC; |
| |
| /* all queries must be in the same group */ |
| assert(current_query_index <= perf_cfg->n_counters); |
| assert(perf_cfg->counter_infos[current_query_index].location.group_idx == group); |
| monitor->active_counters[i] = |
| perf_cfg->counter_infos[current_query_index].location.counter_idx; |
| } |
| |
| /* create the gen_perf_query */ |
| query_obj = gen_perf_new_query(perf_ctx, group); |
| if (unlikely(!query_obj)) |
| goto allocation_failure; |
| |
| monitor->query = query_obj; |
| monitor->result_size = perf_cfg->queries[group].data_size; |
| monitor->result_buffer = calloc(1, monitor->result_size); |
| if (unlikely(!monitor->result_buffer)) |
| goto allocation_failure; |
| |
| return monitor; |
| |
| allocation_failure: |
| if (monitor) { |
| free(monitor->active_counters); |
| free(monitor->result_buffer); |
| } |
| free(query_obj); |
| free(monitor); |
| return NULL; |
| } |
| |
| void |
| iris_destroy_monitor_object(struct pipe_context *ctx, |
| struct iris_monitor_object *monitor) |
| { |
| struct iris_context *ice = (struct iris_context *)ctx; |
| |
| gen_perf_delete_query(ice->perf_ctx, monitor->query); |
| free(monitor->result_buffer); |
| monitor->result_buffer = NULL; |
| free(monitor->active_counters); |
| monitor->active_counters = NULL; |
| free(monitor); |
| } |
| |
| bool |
| iris_begin_monitor(struct pipe_context *ctx, |
| struct iris_monitor_object *monitor) |
| { |
| struct iris_context *ice = (void *) ctx; |
| struct gen_perf_context *perf_ctx = ice->perf_ctx; |
| |
| return gen_perf_begin_query(perf_ctx, monitor->query); |
| } |
| |
| bool |
| iris_end_monitor(struct pipe_context *ctx, |
| struct iris_monitor_object *monitor) |
| { |
| struct iris_context *ice = (void *) ctx; |
| struct gen_perf_context *perf_ctx = ice->perf_ctx; |
| |
| gen_perf_end_query(perf_ctx, monitor->query); |
| return true; |
| } |
| |
| bool |
| iris_get_monitor_result(struct pipe_context *ctx, |
| struct iris_monitor_object *monitor, |
| bool wait, |
| union pipe_numeric_type_union *result) |
| { |
| struct iris_context *ice = (void *) ctx; |
| struct gen_perf_context *perf_ctx = ice->perf_ctx; |
| struct iris_batch *batch = &ice->batches[IRIS_BATCH_RENDER]; |
| |
| bool monitor_ready = |
| gen_perf_is_query_ready(perf_ctx, monitor->query, batch); |
| |
| if (!monitor_ready) { |
| if (!wait) |
| return false; |
| gen_perf_wait_query(perf_ctx, monitor->query, batch); |
| } |
| |
| assert(gen_perf_is_query_ready(perf_ctx, monitor->query, batch)); |
| |
| unsigned bytes_written; |
| gen_perf_get_query_data(perf_ctx, monitor->query, batch, |
| monitor->result_size, |
| (unsigned*) monitor->result_buffer, |
| &bytes_written); |
| if (bytes_written != monitor->result_size) |
| return false; |
| |
| /* copy metrics into the batch result */ |
| for (int i = 0; i < monitor->num_active_counters; ++i) { |
| int current_counter = monitor->active_counters[i]; |
| const struct gen_perf_query_info *info = |
| gen_perf_query_info(monitor->query); |
| const struct gen_perf_query_counter *counter = |
| &info->counters[current_counter]; |
| assert(gen_perf_query_counter_get_size(counter)); |
| switch (counter->data_type) { |
| case GEN_PERF_COUNTER_DATA_TYPE_UINT64: |
| result[i].u64 = *(uint64_t*)(monitor->result_buffer + counter->offset); |
| break; |
| case GEN_PERF_COUNTER_DATA_TYPE_FLOAT: |
| result[i].f = *(float*)(monitor->result_buffer + counter->offset); |
| break; |
| case GEN_PERF_COUNTER_DATA_TYPE_UINT32: |
| case GEN_PERF_COUNTER_DATA_TYPE_BOOL32: |
| result[i].u64 = *(uint32_t*)(monitor->result_buffer + counter->offset); |
| break; |
| case GEN_PERF_COUNTER_DATA_TYPE_DOUBLE: { |
| double v = *(double*)(monitor->result_buffer + counter->offset); |
| result[i].f = v; |
| break; |
| } |
| default: |
| unreachable("unexpected counter data type"); |
| } |
| } |
| return true; |
| } |