Path: blob/21.2-virgl/src/gallium/drivers/radeonsi/si_perfcounter.c
4570 views
/*1* Copyright 2015 Advanced Micro Devices, Inc.2* All Rights Reserved.3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* the rights to use, copy, modify, merge, publish, distribute, sublicense,8* and/or sell copies of the Software, and to permit persons to whom the9* Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,20* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include "si_build_pm4.h"25#include "si_query.h"26#include "util/u_memory.h"2728#include "ac_perfcounter.h"2930struct si_query_group {31struct si_query_group *next;32struct ac_pc_block *block;33unsigned sub_gid; /* only used during init */34unsigned result_base; /* only used during init */35int se;36int instance;37unsigned num_counters;38unsigned selectors[AC_QUERY_MAX_COUNTERS];39};4041struct si_query_counter {42unsigned base;43unsigned qwords;44unsigned stride; /* in uint64s */45};4647struct si_query_pc {48struct si_query b;49struct si_query_buffer buffer;5051/* Size of the results in memory, in bytes. */52unsigned result_size;5354unsigned shaders;55unsigned num_counters;56struct si_query_counter *counters;57struct si_query_group *groups;58};5960static void si_pc_emit_instance(struct si_context *sctx, int se, int instance)61{62struct radeon_cmdbuf *cs = &sctx->gfx_cs;63unsigned value = S_030800_SH_BROADCAST_WRITES(1);6465if (se >= 0) {66value |= S_030800_SE_INDEX(se);67} else {68value |= S_030800_SE_BROADCAST_WRITES(1);69}7071if (sctx->chip_class >= GFX10) {72/* TODO: Expose counters from each shader array separately if needed. */73value |= S_030800_SA_BROADCAST_WRITES(1);74}7576if (instance >= 0) {77value |= S_030800_INSTANCE_INDEX(instance);78} else {79value |= S_030800_INSTANCE_BROADCAST_WRITES(1);80}8182radeon_begin(cs);83radeon_set_uconfig_reg(cs, R_030800_GRBM_GFX_INDEX, value);84radeon_end();85}8687static void si_pc_emit_shaders(struct si_context *sctx, unsigned shaders)88{89struct radeon_cmdbuf *cs = &sctx->gfx_cs;9091radeon_begin(cs);92radeon_set_uconfig_reg_seq(cs, R_036780_SQ_PERFCOUNTER_CTRL, 2, false);93radeon_emit(cs, shaders & 0x7f);94radeon_emit(cs, 0xffffffff);95radeon_end();96}9798static void si_pc_emit_select(struct si_context *sctx, struct ac_pc_block *block, unsigned count,99unsigned *selectors)100{101struct ac_pc_block_base *regs = block->b->b;102struct radeon_cmdbuf *cs = &sctx->gfx_cs;103unsigned idx;104105assert(count <= regs->num_counters);106107/* Fake counters. */108if (!regs->select0)109return;110111radeon_begin(cs);112113for (idx = 0; idx < count; ++idx) {114radeon_set_uconfig_reg_seq(cs, regs->select0[idx], 1, false);115radeon_emit(cs, selectors[idx] | regs->select_or);116}117118for (idx = 0; idx < regs->num_spm_counters; idx++) {119radeon_set_uconfig_reg_seq(cs, regs->select1[idx], 1, false);120radeon_emit(cs, 0);121}122123radeon_end();124}125126static void si_pc_emit_start(struct si_context *sctx, struct si_resource *buffer, uint64_t va)127{128struct radeon_cmdbuf *cs = &sctx->gfx_cs;129130si_cp_copy_data(sctx, &sctx->gfx_cs, COPY_DATA_DST_MEM, buffer, va - buffer->gpu_address,131COPY_DATA_IMM, NULL, 1);132133radeon_begin(cs);134radeon_set_uconfig_reg(cs, R_036020_CP_PERFMON_CNTL,135S_036020_PERFMON_STATE(V_036020_CP_PERFMON_STATE_DISABLE_AND_RESET));136radeon_emit(cs, PKT3(PKT3_EVENT_WRITE, 0, 0));137radeon_emit(cs, EVENT_TYPE(V_028A90_PERFCOUNTER_START) | EVENT_INDEX(0));138radeon_set_uconfig_reg(cs, R_036020_CP_PERFMON_CNTL,139S_036020_PERFMON_STATE(V_036020_CP_PERFMON_STATE_START_COUNTING));140radeon_end();141}142143/* Note: The buffer was already added in si_pc_emit_start, so we don't have to144* do it again in here. */145static void si_pc_emit_stop(struct si_context *sctx, struct si_resource *buffer, uint64_t va)146{147struct radeon_cmdbuf *cs = &sctx->gfx_cs;148149si_cp_release_mem(sctx, cs, V_028A90_BOTTOM_OF_PIPE_TS, 0, EOP_DST_SEL_MEM, EOP_INT_SEL_NONE,150EOP_DATA_SEL_VALUE_32BIT, buffer, va, 0, SI_NOT_QUERY);151si_cp_wait_mem(sctx, cs, va, 0, 0xffffffff, WAIT_REG_MEM_EQUAL);152153radeon_begin(cs);154radeon_emit(cs, PKT3(PKT3_EVENT_WRITE, 0, 0));155radeon_emit(cs, EVENT_TYPE(V_028A90_PERFCOUNTER_SAMPLE) | EVENT_INDEX(0));156radeon_emit(cs, PKT3(PKT3_EVENT_WRITE, 0, 0));157radeon_emit(cs, EVENT_TYPE(V_028A90_PERFCOUNTER_STOP) | EVENT_INDEX(0));158radeon_set_uconfig_reg(159cs, R_036020_CP_PERFMON_CNTL,160S_036020_PERFMON_STATE(V_036020_CP_PERFMON_STATE_STOP_COUNTING) | S_036020_PERFMON_SAMPLE_ENABLE(1));161radeon_end();162}163164static void si_pc_emit_read(struct si_context *sctx, struct ac_pc_block *block, unsigned count,165uint64_t va)166{167struct ac_pc_block_base *regs = block->b->b;168struct radeon_cmdbuf *cs = &sctx->gfx_cs;169unsigned idx;170unsigned reg = regs->counter0_lo;171unsigned reg_delta = 8;172173radeon_begin(cs);174175if (regs->select0) {176for (idx = 0; idx < count; ++idx) {177if (regs->counters)178reg = regs->counters[idx];179180radeon_emit(cs, PKT3(PKT3_COPY_DATA, 4, 0));181radeon_emit(cs, COPY_DATA_SRC_SEL(COPY_DATA_PERF) | COPY_DATA_DST_SEL(COPY_DATA_DST_MEM) |182COPY_DATA_COUNT_SEL); /* 64 bits */183radeon_emit(cs, reg >> 2);184radeon_emit(cs, 0); /* unused */185radeon_emit(cs, va);186radeon_emit(cs, va >> 32);187va += sizeof(uint64_t);188reg += reg_delta;189}190} else {191/* Fake counters. */192for (idx = 0; idx < count; ++idx) {193radeon_emit(cs, PKT3(PKT3_COPY_DATA, 4, 0));194radeon_emit(cs, COPY_DATA_SRC_SEL(COPY_DATA_IMM) | COPY_DATA_DST_SEL(COPY_DATA_DST_MEM) |195COPY_DATA_COUNT_SEL);196radeon_emit(cs, 0); /* immediate */197radeon_emit(cs, 0);198radeon_emit(cs, va);199radeon_emit(cs, va >> 32);200va += sizeof(uint64_t);201}202}203radeon_end();204}205206static void si_pc_query_destroy(struct si_context *sctx, struct si_query *squery)207{208struct si_query_pc *query = (struct si_query_pc *)squery;209210while (query->groups) {211struct si_query_group *group = query->groups;212query->groups = group->next;213FREE(group);214}215216FREE(query->counters);217218si_query_buffer_destroy(sctx->screen, &query->buffer);219FREE(query);220}221222void si_inhibit_clockgating(struct si_context *sctx, struct radeon_cmdbuf *cs, bool inhibit)223{224radeon_begin(&sctx->gfx_cs);225226if (sctx->chip_class >= GFX10) {227radeon_set_uconfig_reg(cs, R_037390_RLC_PERFMON_CLK_CNTL,228S_037390_PERFMON_CLOCK_STATE(inhibit));229} else if (sctx->chip_class >= GFX8) {230radeon_set_uconfig_reg(cs, R_0372FC_RLC_PERFMON_CLK_CNTL,231S_0372FC_PERFMON_CLOCK_STATE(inhibit));232}233radeon_end();234}235236static void si_pc_query_resume(struct si_context *sctx, struct si_query *squery)237/*238struct si_query_hw *hwquery,239struct si_resource *buffer, uint64_t va)*/240{241struct si_query_pc *query = (struct si_query_pc *)squery;242int current_se = -1;243int current_instance = -1;244245if (!si_query_buffer_alloc(sctx, &query->buffer, NULL, query->result_size))246return;247si_need_gfx_cs_space(sctx, 0);248249if (query->shaders)250si_pc_emit_shaders(sctx, query->shaders);251252si_inhibit_clockgating(sctx, &sctx->gfx_cs, true);253254for (struct si_query_group *group = query->groups; group; group = group->next) {255struct ac_pc_block *block = group->block;256257if (group->se != current_se || group->instance != current_instance) {258current_se = group->se;259current_instance = group->instance;260si_pc_emit_instance(sctx, group->se, group->instance);261}262263si_pc_emit_select(sctx, block, group->num_counters, group->selectors);264}265266if (current_se != -1 || current_instance != -1)267si_pc_emit_instance(sctx, -1, -1);268269uint64_t va = query->buffer.buf->gpu_address + query->buffer.results_end;270si_pc_emit_start(sctx, query->buffer.buf, va);271}272273static void si_pc_query_suspend(struct si_context *sctx, struct si_query *squery)274{275struct si_query_pc *query = (struct si_query_pc *)squery;276277if (!query->buffer.buf)278return;279280uint64_t va = query->buffer.buf->gpu_address + query->buffer.results_end;281query->buffer.results_end += query->result_size;282283si_pc_emit_stop(sctx, query->buffer.buf, va);284285for (struct si_query_group *group = query->groups; group; group = group->next) {286struct ac_pc_block *block = group->block;287unsigned se = group->se >= 0 ? group->se : 0;288unsigned se_end = se + 1;289290if ((block->b->b->flags & AC_PC_BLOCK_SE) && (group->se < 0))291se_end = sctx->screen->info.max_se;292293do {294unsigned instance = group->instance >= 0 ? group->instance : 0;295296do {297si_pc_emit_instance(sctx, se, instance);298si_pc_emit_read(sctx, block, group->num_counters, va);299va += sizeof(uint64_t) * group->num_counters;300} while (group->instance < 0 && ++instance < block->num_instances);301} while (++se < se_end);302}303304si_pc_emit_instance(sctx, -1, -1);305306si_inhibit_clockgating(sctx, &sctx->gfx_cs, false);307}308309static bool si_pc_query_begin(struct si_context *ctx, struct si_query *squery)310{311struct si_query_pc *query = (struct si_query_pc *)squery;312313si_query_buffer_reset(ctx, &query->buffer);314315list_addtail(&query->b.active_list, &ctx->active_queries);316ctx->num_cs_dw_queries_suspend += query->b.num_cs_dw_suspend;317318si_pc_query_resume(ctx, squery);319320return true;321}322323static bool si_pc_query_end(struct si_context *ctx, struct si_query *squery)324{325struct si_query_pc *query = (struct si_query_pc *)squery;326327si_pc_query_suspend(ctx, squery);328329list_del(&squery->active_list);330ctx->num_cs_dw_queries_suspend -= squery->num_cs_dw_suspend;331332return query->buffer.buf != NULL;333}334335static void si_pc_query_add_result(struct si_query_pc *query, void *buffer,336union pipe_query_result *result)337{338uint64_t *results = buffer;339unsigned i, j;340341for (i = 0; i < query->num_counters; ++i) {342struct si_query_counter *counter = &query->counters[i];343344for (j = 0; j < counter->qwords; ++j) {345uint32_t value = results[counter->base + j * counter->stride];346result->batch[i].u64 += value;347}348}349}350351static bool si_pc_query_get_result(struct si_context *sctx, struct si_query *squery, bool wait,352union pipe_query_result *result)353{354struct si_query_pc *query = (struct si_query_pc *)squery;355356memset(result, 0, sizeof(result->batch[0]) * query->num_counters);357358for (struct si_query_buffer *qbuf = &query->buffer; qbuf; qbuf = qbuf->previous) {359unsigned usage = PIPE_MAP_READ | (wait ? 0 : PIPE_MAP_DONTBLOCK);360unsigned results_base = 0;361void *map;362363if (squery->b.flushed)364map = sctx->ws->buffer_map(sctx->ws, qbuf->buf->buf, NULL, usage);365else366map = si_buffer_map(sctx, qbuf->buf, usage);367368if (!map)369return false;370371while (results_base != qbuf->results_end) {372si_pc_query_add_result(query, map + results_base, result);373results_base += query->result_size;374}375}376377return true;378}379380static const struct si_query_ops batch_query_ops = {381.destroy = si_pc_query_destroy,382.begin = si_pc_query_begin,383.end = si_pc_query_end,384.get_result = si_pc_query_get_result,385386.suspend = si_pc_query_suspend,387.resume = si_pc_query_resume,388};389390static struct si_query_group *get_group_state(struct si_screen *screen, struct si_query_pc *query,391struct ac_pc_block *block, unsigned sub_gid)392{393struct si_perfcounters *pc = screen->perfcounters;394struct si_query_group *group = query->groups;395396while (group) {397if (group->block == block && group->sub_gid == sub_gid)398return group;399group = group->next;400}401402group = CALLOC_STRUCT(si_query_group);403if (!group)404return NULL;405406group->block = block;407group->sub_gid = sub_gid;408409if (block->b->b->flags & AC_PC_BLOCK_SHADER) {410unsigned sub_gids = block->num_instances;411unsigned shader_id;412unsigned shaders;413unsigned query_shaders;414415if (ac_pc_block_has_per_se_groups(&pc->base, block))416sub_gids = sub_gids * screen->info.max_se;417shader_id = sub_gid / sub_gids;418sub_gid = sub_gid % sub_gids;419420shaders = ac_pc_shader_type_bits[shader_id];421422query_shaders = query->shaders & ~AC_PC_SHADERS_WINDOWING;423if (query_shaders && query_shaders != shaders) {424fprintf(stderr, "si_perfcounter: incompatible shader groups\n");425FREE(group);426return NULL;427}428query->shaders = shaders;429}430431if (block->b->b->flags & AC_PC_BLOCK_SHADER_WINDOWED && !query->shaders) {432// A non-zero value in query->shaders ensures that the shader433// masking is reset unless the user explicitly requests one.434query->shaders = AC_PC_SHADERS_WINDOWING;435}436437if (ac_pc_block_has_per_se_groups(&pc->base, block)) {438group->se = sub_gid / block->num_instances;439sub_gid = sub_gid % block->num_instances;440} else {441group->se = -1;442}443444if (ac_pc_block_has_per_instance_groups(&pc->base, block)) {445group->instance = sub_gid;446} else {447group->instance = -1;448}449450group->next = query->groups;451query->groups = group;452453return group;454}455456struct pipe_query *si_create_batch_query(struct pipe_context *ctx, unsigned num_queries,457unsigned *query_types)458{459struct si_screen *screen = (struct si_screen *)ctx->screen;460struct si_perfcounters *pc = screen->perfcounters;461struct ac_pc_block *block;462struct si_query_group *group;463struct si_query_pc *query;464unsigned base_gid, sub_gid, sub_index;465unsigned i, j;466467if (!pc)468return NULL;469470query = CALLOC_STRUCT(si_query_pc);471if (!query)472return NULL;473474query->b.ops = &batch_query_ops;475476query->num_counters = num_queries;477478/* Collect selectors per group */479for (i = 0; i < num_queries; ++i) {480unsigned sub_gid;481482if (query_types[i] < SI_QUERY_FIRST_PERFCOUNTER)483goto error;484485block =486ac_lookup_counter(&pc->base, query_types[i] - SI_QUERY_FIRST_PERFCOUNTER, &base_gid, &sub_index);487if (!block)488goto error;489490sub_gid = sub_index / block->b->selectors;491sub_index = sub_index % block->b->selectors;492493group = get_group_state(screen, query, block, sub_gid);494if (!group)495goto error;496497if (group->num_counters >= block->b->b->num_counters) {498fprintf(stderr, "perfcounter group %s: too many selected\n", block->b->b->name);499goto error;500}501group->selectors[group->num_counters] = sub_index;502++group->num_counters;503}504505/* Compute result bases and CS size per group */506query->b.num_cs_dw_suspend = pc->num_stop_cs_dwords;507query->b.num_cs_dw_suspend += pc->num_instance_cs_dwords;508509i = 0;510for (group = query->groups; group; group = group->next) {511struct ac_pc_block *block = group->block;512unsigned read_dw;513unsigned instances = 1;514515if ((block->b->b->flags & AC_PC_BLOCK_SE) && group->se < 0)516instances = screen->info.max_se;517if (group->instance < 0)518instances *= block->num_instances;519520group->result_base = i;521query->result_size += sizeof(uint64_t) * instances * group->num_counters;522i += instances * group->num_counters;523524read_dw = 6 * group->num_counters;525query->b.num_cs_dw_suspend += instances * read_dw;526query->b.num_cs_dw_suspend += instances * pc->num_instance_cs_dwords;527}528529if (query->shaders) {530if (query->shaders == AC_PC_SHADERS_WINDOWING)531query->shaders = 0xffffffff;532}533534/* Map user-supplied query array to result indices */535query->counters = CALLOC(num_queries, sizeof(*query->counters));536for (i = 0; i < num_queries; ++i) {537struct si_query_counter *counter = &query->counters[i];538struct ac_pc_block *block;539540block =541ac_lookup_counter(&pc->base, query_types[i] - SI_QUERY_FIRST_PERFCOUNTER, &base_gid, &sub_index);542543sub_gid = sub_index / block->b->selectors;544sub_index = sub_index % block->b->selectors;545546group = get_group_state(screen, query, block, sub_gid);547assert(group != NULL);548549for (j = 0; j < group->num_counters; ++j) {550if (group->selectors[j] == sub_index)551break;552}553554counter->base = group->result_base + j;555counter->stride = group->num_counters;556557counter->qwords = 1;558if ((block->b->b->flags & AC_PC_BLOCK_SE) && group->se < 0)559counter->qwords = screen->info.max_se;560if (group->instance < 0)561counter->qwords *= block->num_instances;562}563564return (struct pipe_query *)query;565566error:567si_pc_query_destroy((struct si_context *)ctx, &query->b);568return NULL;569}570571int si_get_perfcounter_info(struct si_screen *screen, unsigned index,572struct pipe_driver_query_info *info)573{574struct si_perfcounters *pc = screen->perfcounters;575struct ac_pc_block *block;576unsigned base_gid, sub;577578if (!pc)579return 0;580581if (!info) {582unsigned bid, num_queries = 0;583584for (bid = 0; bid < pc->base.num_blocks; ++bid) {585num_queries += pc->base.blocks[bid].b->selectors * pc->base.blocks[bid].num_groups;586}587588return num_queries;589}590591block = ac_lookup_counter(&pc->base, index, &base_gid, &sub);592if (!block)593return 0;594595if (!block->selector_names) {596if (!ac_init_block_names(&screen->info, &pc->base, block))597return 0;598}599info->name = block->selector_names + sub * block->selector_name_stride;600info->query_type = SI_QUERY_FIRST_PERFCOUNTER + index;601info->max_value.u64 = 0;602info->type = PIPE_DRIVER_QUERY_TYPE_UINT64;603info->result_type = PIPE_DRIVER_QUERY_RESULT_TYPE_AVERAGE;604info->group_id = base_gid + sub / block->b->selectors;605info->flags = PIPE_DRIVER_QUERY_FLAG_BATCH;606if (sub > 0 && sub + 1 < block->b->selectors * block->num_groups)607info->flags |= PIPE_DRIVER_QUERY_FLAG_DONT_LIST;608return 1;609}610611int si_get_perfcounter_group_info(struct si_screen *screen, unsigned index,612struct pipe_driver_query_group_info *info)613{614struct si_perfcounters *pc = screen->perfcounters;615struct ac_pc_block *block;616617if (!pc)618return 0;619620if (!info)621return pc->base.num_groups;622623block = ac_lookup_group(&pc->base, &index);624if (!block)625return 0;626627if (!block->group_names) {628if (!ac_init_block_names(&screen->info, &pc->base, block))629return 0;630}631info->name = block->group_names + index * block->group_name_stride;632info->num_queries = block->b->selectors;633info->max_active_queries = block->b->b->num_counters;634return 1;635}636637void si_destroy_perfcounters(struct si_screen *screen)638{639struct si_perfcounters *pc = screen->perfcounters;640641if (!pc)642return;643644ac_destroy_perfcounters(&pc->base);645screen->perfcounters = NULL;646}647648void si_init_perfcounters(struct si_screen *screen)649{650bool separate_se, separate_instance;651652separate_se = debug_get_bool_option("RADEON_PC_SEPARATE_SE", false);653separate_instance = debug_get_bool_option("RADEON_PC_SEPARATE_INSTANCE", false);654655screen->perfcounters = CALLOC_STRUCT(si_perfcounters);656if (!screen->perfcounters)657return;658659screen->perfcounters->num_stop_cs_dwords = 14 + si_cp_write_fence_dwords(screen);660screen->perfcounters->num_instance_cs_dwords = 3;661662if (!ac_init_perfcounters(&screen->info, separate_se, separate_instance,663&screen->perfcounters->base)) {664si_destroy_perfcounters(screen);665}666}667668669