/* Watch usage of Vivante GPU live. * Needs profiling support built-in (build kernel and etnaviv with VIVANTE_PROFILER=1). */ /* Uncomment if the platform has the clock_gettime call, to use a monotonic * clock */ /* #define HAVE_CLOCK */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CLOCK #include #else #include #endif #include #include #include #include static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; static const char clear_screen[] = {0x1b, '[', 'H', 0x1b, '[', 'J', 0x0}; static const char color_num_max[] = "\x1b[1;37m"; static const char color_num_zero[] = "\x1b[1;30m"; static const char color_num[] = "\x1b[1;33m"; static const char color_reset[] = "\x1b[0m"; static const char color_percentage_1[] = "\x1b[38;5;154;48;5;236m"; static const char color_percentage_2[] = "\x1b[38;5;112;48;5;236m"; static const char color_title[] = "\x1b[38;5;249m"; static void print_percentage_bar(float percent, int bar_width) { int bar_avail_len = bar_width * 8; int bar_len = bar_avail_len * (percent + .5) / 100.0; int cur_line_len = 0; int i; if(bar_len > bar_avail_len) bar_len = bar_avail_len; for (i = bar_len; i >= 8; i -= 8) { printf("%s", bars[8]); cur_line_len++; } if (i) { printf("%s", bars[i]); cur_line_len++; } /* NB: We can't use a field width with utf8 so we manually * guarantee a field with of 45 chars for any bar. */ printf("%*s", bar_width - cur_line_len, ""); } /* Get time in microseconds */ static unsigned long gettime(void) { #ifdef HAVE_CLOCK struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return (t.tv_nsec/1000 + (t.tv_sec * 1000000)); #else struct timeval t; gettimeofday(&t, NULL); return (t.tv_usec + (t.tv_sec * 1000000)); #endif } /* Return number of lines and columns in the terminal */ static void get_screen_size(int *lines, int *cols) { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) != -1) { *lines = ws.ws_row; *cols = ws.ws_col; } else { *lines = 25; /* default */ *cols = 80; } } /* Format unsigned 64 bit number with thousands separators. * Result is always nul-terminated within outsz characters. */ static void format_number(char *out, int outsz, uint64_t num) { char temp[100]; int len; int groups, group_size; int in_ptr, out_ptr; if(outsz == 0) return; snprintf(temp, sizeof(temp), "%llu", num); len = strlen(temp); /* group digits per three */ groups = (len+2) / 3; group_size = len - (groups - 1) * 3; /* First group */ in_ptr = out_ptr = 0; outsz -= 1; for(int i=0; i 99.0) printf("%s", color_num_max); else if(percent < 1.0) printf("%s", color_num_zero); else printf("%s", color_num); } printf("%5.1f%% ", percent); if(color) printf("%s%s", color_reset, (l%2) ? color_percentage_1 : color_percentage_2); print_percentage_bar(percent, bar_width); if(color) printf("%s", color_reset); } int main(int argc, char **argv) { int samples_per_second = 100; bool interactive = true; int mode = MODE_PERF; bool color = true; int opt; bool error = false; while ((opt = getopt(argc, argv, "m:s:n")) != -1) { switch(opt) { case 'm': switch(optarg[0]) { case 'm': mode = MODE_MAX; break; case 'p': mode = MODE_PERF; break; case 'o': mode = MODE_OCCUPANCY; break; case 'd': mode = MODE_DMA; break; default: printf("Unknown mode %s\n", optarg); } break; case 'n': color = false; break; case 's': samples_per_second = atoi(optarg); break; case 'h': default: error = true; } } if(error) { printf("Usage:\n"); printf(" %s [-m ] [-n] [-s ] \n", argv[0]); printf("\n"); printf(" -m Set mode:\n"); printf(" p Show performance counters (default)\n"); printf(" m Show performance counter maximum\n"); printf(" o Show occupancy (non-idle) states of modules\n"); printf(" d Show DMA engine states\n"); printf(" -n Disable color\n"); printf(" -h Show this help message\n"); printf(" -s Number of samples per second (default 100)\n"); exit(1); } struct viv_conn *conn = 0; int rv; rv = viv_open(VIV_HW_3D, &conn); if(rv!=0) { fprintf(stderr, "Error opening device\n"); exit(1); } uint32_t orig_num_profile_counters = viv_get_num_profile_counters(); derived_counters_base = orig_num_profile_counters; uint32_t num_profile_counters = derived_counters_base + NUM_DERIVED_COUNTERS; bool *reset_after_read = calloc(num_profile_counters, sizeof(bool)); uint32_t *counter_data = calloc(num_profile_counters, 4); uint32_t *counter_data_last = calloc(num_profile_counters, 4); uint64_t *events_per_s = calloc(num_profile_counters, 8); uint64_t *events_per_s_max = calloc(num_profile_counters, 8); /* reset counters and initial values */ if(viv_read_profile_counters_3d(conn, counter_data_last) != 0) { fprintf(stderr, "Error querying counters (probably unsupported with this kernel, or not built into libetnaviv)\n"); exit(1); } viv_get_counters_reset_after_read(conn, reset_after_read); uint32_t begin_time = gettime(); useconds_t interval = 1000000 / samples_per_second; while(true) { /* Scale counters by real elapsed time */ for(int c=0; c= (NUM_CMD_STATE_NAMES-1)) /* Mark unknowns as UNKNOWN */ cmd_state_idx = NUM_CMD_STATE_NAMES-1; cmd_state[cmd_state_idx]++; cmd_dma_state[(data>>8) & 3]++; cmd_fetch_state[(data>>10) & 3]++; req_dma_state[(data>>12) & 3]++; cal_state[(data>>14) & 3]++; ve_req_state[(data>>16) & 3]++; } else { if(viv_read_profile_counters_3d(conn, counter_data) != 0) { fprintf(stderr, "Error querying counters (probably unsupported with this kernel, or not built into libetnaviv)\n"); exit(1); } for(int c=0; c counter_data[c]) { events_per_s[c] += counter_data[c]; } else { events_per_s[c] += (uint32_t)(counter_data[c] - counter_data_last[c]); } } else events_per_s[c] += counter_data[c]; } for(int c=0; c events_per_s_max[c]) events_per_s_max[c] = events_per_s[c]; } if(interactive) { int max_lines, max_cols; printf("%s", clear_screen); get_screen_size(&max_lines, &max_cols); max_lines -= 1; if(mode == MODE_PERF) { /* XXX check that width doesn't exceed screen width */ for(int l=0; ldescription); printf(" "); c += max_lines; } printf("\n"); } } else if(mode == MODE_MAX) { /* XXX check that width doesn't exceed screen width */ for(int l=0; ldescription); printf(" "); c += max_lines; } printf("\n"); } } else if(mode == MODE_OCCUPANCY) { int lines = NUM_IDLE_MODULES; if(lines > max_lines) lines = max_lines; if(color) printf("%s", color_title); printf("Module occupancy\n"); if(color) printf("%s", color_reset); for(int l=0; lcolumn == column) { if(l == table->base_y-1) { if(color) printf("%s", color_title); printf("%-*s", fill_width, table->title); if(color) printf("%s", color_reset); match = true; break; } else if(l >= table->base_y && l < (table->base_y + table->data_size)) { int y = l - table->base_y; double percent = 100.0 * (double)table->data[y] / (double)samples_per_second; print_percentage_row(y, percent, table->data_names[y], 10, color, bar_width); match = true; break; } } } if(!match) /* empty slot */ { printf("%-*s", fill_width, ""); } printf(" "); } printf("\n"); } } } begin_time = end_time; } /* * XXX define new mode MODE_OCCUPANCY and some derived percentage bars: * - [PA] Number of primitives per vertex (max 1) * - [PA] % of primitives culled * - VS -> PA -> SE -> RA primitives/vertices in each stage * - RA -> PS -> PE pixels/quads in each stage * - Pixels per PS inst * - Vertices per VS inst * - % of texture requests trilinear/bilinear * - overdraw (killed by depth) */ return 0; }