+static void
+print_program_metrics (metrics_t *metrics)
+{
+ metrics_info_t *info = metrics->info;
+ unsigned num_shader_stages = info->num_shader_stages;
+ per_stage_metrics_t *sorted, *per_stage;
+ double total_time, op_cycles;
+ op_metrics_t *op;
+ unsigned group_index, counter_index;
+ unsigned i, j, num_sorted;
+
+ /* Make a sorted list of the per-stage operations by time
+ * used, and figure out the total so we can print percentages.
+ */
+ num_sorted = metrics->num_op_metrics * num_shader_stages;
+
+ sorted = xmalloc (sizeof (*sorted) * num_sorted);
+
+ total_time = 0.0;
+
+ for (i = 0; i < metrics->num_op_metrics; i++) {
+
+ op = &metrics->op_metrics[i];
+
+ /* Accumulate total time across all ops. */
+ total_time += op->time_ns;
+
+ /* Also, find total cycles in all stages of this op. */
+ op_cycles = 0.0;
+
+ for (j = 0; j < num_shader_stages; j++) {
+ /* Active cycles */
+ group_index = info->stages[j].active_group_index;
+ counter_index = info->stages[j].active_counter_index;
+ op_cycles += op->counters[group_index][counter_index];
+
+ /* Stall cycles */
+ group_index = info->stages[j].stall_group_index;
+ counter_index = info->stages[j].stall_counter_index;
+ op_cycles += op->counters[group_index][counter_index];
+ }
+
+ for (j = 0; j < num_shader_stages; j++) {
+ double active_cycles, stall_cycles, stage_cycles;
+
+ /* Active cycles */
+ group_index = info->stages[j].active_group_index;
+ counter_index = info->stages[j].active_counter_index;
+ active_cycles = op->counters[group_index][counter_index];
+
+ /* Stall cycles */
+ group_index = info->stages[j].stall_group_index;
+ counter_index = info->stages[j].stall_counter_index;
+ stall_cycles = op->counters[group_index][counter_index];
+
+ stage_cycles = active_cycles + stall_cycles;
+
+ per_stage = &sorted[i * num_shader_stages + j];
+ per_stage->metrics = op;
+
+ if (op_cycles) {
+ per_stage->stage = &info->stages[j];
+ per_stage->time_ns = op->time_ns * (stage_cycles / op_cycles);
+ } else {
+ /* If we don't have any per-stage cycle counts
+ * for this operation, then use the first
+ * stage as a placeholder for all the time,
+ * but NULL-ify the stage info so that the
+ * report doesn't lie about this time being
+ * from any particular stage. */
+ per_stage->stage = NULL;
+ if (j == 0) {
+ per_stage->time_ns = op->time_ns;
+ } else {
+ per_stage->time_ns = 0.0;
+ }
+ }
+
+ if (stage_cycles) {
+ per_stage->active = active_cycles / stage_cycles;
+ } else {
+ per_stage->active = 0.0;
+ }
+ }
+ }
+
+ qsort_r (sorted, num_sorted, sizeof (*sorted),
+ time_compare, metrics->op_metrics);
+
+ for (i = 0; i < num_sorted; i++)
+ print_per_stage_metrics (metrics, &sorted[i], total_time);
+
+ free (sorted);
+}
+
+void
+metrics_collect_available (metrics_t *metrics)
+{
+ /* Consume all timer queries that are ready. */
+ timer_query_t *timer = metrics->timer_head;
+
+ while (timer) {
+ GLuint available, elapsed;
+
+ glGetQueryObjectuiv (timer->id,
+ GL_QUERY_RESULT_AVAILABLE, &available);
+ if (! available)
+ break;