1 #include <math.h>
2 #include <assert.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "prof.h"
7 #include "prof_internal.h"
8
9 // whether zone-self-data is kept to allow the history graph
10 #define Prof_ZONE_HISTORY
11
12 // whether full detailed (and large)
13 #define Prof_CALL_HISTORY
14
15 // number of frames of history to keep
16 #define NUM_FRAME_SLOTS 128
17
18
19 // number of unique zones allowed in the entire application
20 // @TODO: remove MAX_PROFILING_ZONES and make it dynamic
21 #define MAX_PROFILING_ZONES 512
22
23 ////////////////////////////////////////////////////////////////////////
24
25 // the number of moving averages
26 #define NUM_PROFILE_TRACKER_HISTORY_SLOTS 3
27
28 // the number of frames to ignore before starting the moving averages
29 #define NUM_THROWAWAY_UPDATES 3
30
31 // threshhold for a moving average of an integer to be at zero
32 #define INT_ZERO_THRESHHOLD 0.25
33
34 Prof_Zone *Prof_zones[MAX_PROFILING_ZONES];
35
36 #ifdef Prof_ZONE_HISTORY
37 static float zone_history[MAX_PROFILING_ZONES][NUM_FRAME_SLOTS]; // 256K
38 #endif
39
40 // these structures are used solely to track data over time
41 typedef struct
42 {
43 double values[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
44 double variances[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
45 #ifdef Prof_CALL_HISTORY
46 float history[NUM_FRAME_SLOTS];
47 #endif
48 } History_Scalar;
49
50 typedef struct
51 {
52 History_Scalar self_time;
53 History_Scalar hierarchical_time;
54 History_Scalar entry_count;
55 int max_recursion;
56 } Profile_Tracker_Data_Record;
57
58 static History_Scalar frame_time;
59
60 static double times_to_reach_90_percent[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
61 static double precomputed_factors [NUM_PROFILE_TRACKER_HISTORY_SLOTS];
62
63 static int num_active_zones;
64 static int update_index; // 2^31 at 100fps = 280 days
65 static double last_update_time;
66 static Prof_Report_Mode displayed_quantity;
67
68 #define FRAME_TIME_INITIAL 0.001
69
70 static int history_index;
71 static int display_frame;
72 static int slot = 1;
73
clear(History_Scalar * s)74 static void clear(History_Scalar *s)
75 {
76 int i;
77 for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
78 s->values[i] = 0;
79 s->variances[i] = 0;
80 }
81 }
82
update(History_Scalar * s,double new_value,double * k_array)83 static void update(History_Scalar *s, double new_value, double *k_array)
84 {
85 int i;
86
87 double new_variance = new_value * new_value;
88
89 for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
90 double k = k_array[i];
91 s->values[i] = s->values[i] * k + new_value * (1 - k);
92 s->variances[i] = s->variances[i] * k + new_variance * (1 - k);
93 }
94 #ifdef Prof_CALL_HISTORY
95 s->history[history_index] = (float) new_value;
96 #endif
97 }
98
eternity_set(History_Scalar * s,double new_value)99 static void eternity_set(History_Scalar *s, double new_value)
100 {
101 double new_variance = new_value * new_value;
102
103 int i;
104 for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
105 s->values[i] = new_value;
106 s->variances[i] = new_variance;
107 }
108 #ifdef Prof_CALL_HISTORY
109 s->history[history_index] = (float) new_value;
110 #endif
111 }
112
get_value(History_Scalar * s)113 static double get_value(History_Scalar *s)
114 {
115 #ifdef Prof_CALL_HISTORY
116 if (display_frame) {
117 return s->history[(history_index - display_frame + NUM_FRAME_SLOTS) % NUM_FRAME_SLOTS];
118 }
119 #endif
120 return s->values[slot];
121 }
122
Prof_init_highlevel()123 void Prof_init_highlevel()
124 {
125 int j;
126
127 update_index = 0;
128 last_update_time = 0;
129
130 times_to_reach_90_percent[0] = 0.1f;
131 times_to_reach_90_percent[1] = 0.8f;
132 times_to_reach_90_percent[2] = 2.5f;
133
134 displayed_quantity = Prof_SELF_TIME;
135
136 clear(&frame_time);
137
138 for (j = 0; j < NUM_PROFILE_TRACKER_HISTORY_SLOTS; j++) {
139 frame_time.values[j] = FRAME_TIME_INITIAL;
140 }
141 }
142
143
144 #ifdef Prof_ENABLED
145 static Prof_Zone *expand = &Prof_region__global;
146 #else
147 static Prof_Zone *expand = NULL;
148 #endif
149
Prof_set_report_mode(Prof_Report_Mode desired)150 Prof_extern_C void Prof_set_report_mode(Prof_Report_Mode desired)
151 {
152 displayed_quantity = desired;
153 }
154
155 // visit all Prof_Zone_Stacks
156 extern void Prof_traverse(void (*func)(Prof_Zone_Stack *c));
157
propogate_stack(Prof_Zone_Stack * c)158 static void propogate_stack(Prof_Zone_Stack *c)
159 {
160 Prof_Zone_Stack *p = c;
161
162 // propogate times up the stack for hierarchical
163 // times, but watch out for recursion
164
165 while (p->zone) {
166 if (!p->zone->visited) {
167 p->total_hier_ticks += c->total_self_ticks;
168 p->zone->visited = 1;
169 }
170 p = p->parent;
171 }
172 p = c;
173 while (p->zone) {
174 p->zone->visited = 0;
175 p = p->parent;
176 }
177 }
178
clear_stack(Prof_Zone_Stack * c)179 static void clear_stack(Prof_Zone_Stack *c)
180 {
181 c->total_hier_ticks = 0;
182 c->total_self_ticks = 0;
183 c->total_entry_count = 0;
184 }
185
186 static double sum;
sum_times(Prof_Zone_Stack * c)187 static void sum_times(Prof_Zone_Stack *c)
188 {
189 sum += c->total_self_ticks;
190 }
191
192 static double timestamps_to_seconds;
update_history(Prof_Zone_Stack * c)193 static void update_history(Prof_Zone_Stack *c)
194 {
195 double self_time, hier_time, entry_count;
196
197 Profile_Tracker_Data_Record *record = (Profile_Tracker_Data_Record *) c->highlevel;
198 Prof_Zone *z = c->zone;
199
200 if (record == NULL) {
201 record = (Profile_Tracker_Data_Record *) malloc(sizeof(*record));
202 c->highlevel = (void *) record;
203 clear(&record->entry_count);
204 clear(&record->self_time);
205 clear(&record->hierarchical_time);
206 record->max_recursion = 0;
207 }
208
209 if (c->recursion_depth > record->max_recursion)
210 record->max_recursion = c->recursion_depth;
211
212 self_time = c->total_self_ticks * timestamps_to_seconds;
213 hier_time = c->total_hier_ticks * timestamps_to_seconds;
214 entry_count = c->total_entry_count;
215
216 if (update_index < NUM_THROWAWAY_UPDATES) {
217 eternity_set(&record->entry_count, entry_count);
218 eternity_set(&record->self_time, self_time);
219 eternity_set(&record->hierarchical_time, hier_time);
220 } else {
221 update(&record->self_time, self_time, precomputed_factors);
222 update(&record->hierarchical_time, hier_time, precomputed_factors);
223 update(&record->entry_count, entry_count, precomputed_factors);
224 }
225
226 #ifdef Prof_ZONE_HISTORY
227 * ((float *) z->highlevel) += (float) self_time;
228 #endif
229 }
230
231 const double SPEEDSTEP_DETECTION_RATIO = 0.08;
232 static int speedstep_warning;
233
Prof_update(int record_data)234 Prof_extern_C void Prof_update(int record_data)
235 {
236 #ifdef Prof_ENABLED
237 Prof_Begin(iprof_update)
238
239 static History_Scalar integer_timestamps_per_second;
240 static Prof_Int64 last_integer_timestamp;
241 static Prof_Int64 current_integer_timestamp;
242
243 int i;
244 double now, dt;
245 Prof_Int64 timestamp_delta;
246 double timestamps_per_second;
247
248 assert(Prof_num_zones <= MAX_PROFILING_ZONES);
249
250 Prof_traverse(propogate_stack);
251
252 // Precompute the time factors
253
254 now = Prof_get_time();
255
256 if (update_index == 0) {
257 dt = FRAME_TIME_INITIAL;
258 } else {
259 dt = now - last_update_time;
260 if (dt == 0) dt = FRAME_TIME_INITIAL;
261 }
262
263 last_update_time = now;
264
265 for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
266 precomputed_factors[i] = pow(0.1f, dt / times_to_reach_90_percent[i]);
267 }
268
269 precomputed_factors[0] = 0; // instantaneous.
270
271 Prof_get_timestamp(¤t_integer_timestamp);
272 if (update_index == 0) {
273 sum = 0;
274 Prof_traverse(sum_times);
275 if (sum == 0) sum = 1;
276 timestamp_delta = (Prof_Int64) sum;
277 } else {
278 timestamp_delta = current_integer_timestamp - last_integer_timestamp;
279 if (timestamp_delta == 0) timestamp_delta = 1;
280 }
281
282 last_integer_timestamp = current_integer_timestamp;
283 timestamps_per_second = (double) timestamp_delta / dt;
284
285 if (update_index < NUM_THROWAWAY_UPDATES) {
286 eternity_set(&integer_timestamps_per_second, timestamps_per_second);
287 } else {
288 update(&integer_timestamps_per_second, timestamps_per_second, precomputed_factors);
289 }
290
291 {
292 const int ss_slot = 1;
293 double ss_val, ss_variance, ss_stdev, ss_ratio;
294
295 ss_val = integer_timestamps_per_second.values[ss_slot];
296 ss_variance = integer_timestamps_per_second.variances[ss_slot] - ss_val*ss_val;
297 ss_stdev = sqrt(fabs(ss_variance));
298 ss_ratio;
299 if (ss_val) {
300 ss_ratio = ss_stdev / fabs(ss_val);
301 } else {
302 ss_ratio = 0;
303 }
304
305 speedstep_warning = (ss_ratio > SPEEDSTEP_DETECTION_RATIO);
306 }
307
308 if (!record_data) {
309 Prof_traverse(clear_stack);
310 Prof_End
311 return;
312 }
313
314 if (timestamps_per_second) {
315 timestamps_to_seconds = 1.0 / timestamps_per_second;
316 } else {
317 timestamps_to_seconds = 0;
318 }
319
320 #ifdef Prof_ZONE_HISTORY
321 for (i=0; i < Prof_num_zones; ++i) {
322 Prof_zones[i]->highlevel = (void *) &zone_history[i][history_index];
323 zone_history[i][history_index] = 0;
324 }
325 #endif
326
327 Prof_traverse(update_history);
328
329 update(&frame_time, dt, precomputed_factors);
330
331 ++update_index;
332 history_index = (history_index + 1) % NUM_FRAME_SLOTS;
333
334 Prof_traverse(clear_stack);
335
336 Prof_End
337 #endif // Prof_ENABLED
338 }
339
allocate_buffer(int n)340 static Prof_Report *allocate_buffer(int n)
341 {
342 int i;
343 Prof_Report *pob = (Prof_Report *) malloc(sizeof(*pob));
344 pob->num_record = n;
345 pob->record = (Prof_Report_Record *) malloc(sizeof(*pob->record) * pob->num_record);
346 pob->title[0] = pob->title[1] = NULL;
347 for (i=0; i < NUM_TITLE; ++i)
348 pob->title[i] = NULL;
349 for (i=0; i < NUM_HEADER; ++i)
350 pob->header[i] = NULL;
351 for (i=0; i < n; ++i) {
352 pob->record[i].values[0] = 0;
353 pob->record[i].values[1] = 0;
354 pob->record[i].values[2] = 0;
355 pob->record[i].values[3] = 0;
356 pob->record[i].value_flag = 0;
357 pob->record[i].heat = 0;
358 pob->record[i].indent = 0;
359 pob->record[i].number = 0;
360 }
361 return pob;
362 }
363
364 static int uncounted;
365 static Prof_Recursion_Mode recurse = Prof_FLATTEN_RECURSION;
366
propogate_to_zone(Prof_Zone_Stack * c)367 static void propogate_to_zone(Prof_Zone_Stack *c)
368 {
369 Prof_Zone *z = c->zone;
370 Profile_Tracker_Data_Record *d = (Profile_Tracker_Data_Record *) c->highlevel;
371 Prof_Report_Record *r;
372
373 #if 1
374 r = (Prof_Report_Record *) z->highlevel;
375 #else
376 if (recurse == Prof_FLATTEN_RECURSION)
377 r = (Prof_Report_Record *) z->highlevel;
378 else
379 r = ((Prof_Report_Record **) z->highlevel)[c->recursion_depth];
380 #endif
381
382 if (d) {
383 double t;
384
385 r->values[0] += 1000 * get_value(&d->self_time);
386 r->values[1] += 1000 * get_value(&d->hierarchical_time);
387 r->values[2] += get_value(&d->entry_count);
388
389 // arbitrary determination for how low a moving average
390 // has to go to reach 0
391 if (get_value(&d->entry_count) > INT_ZERO_THRESHHOLD) {
392 if (d->max_recursion > r->number)
393 r->number = d->max_recursion;
394 if (c->parent->zone)
395 ((Prof_Report_Record *) c->parent->zone->highlevel)->prefix = '+';
396 }
397
398 #ifdef Prof_CALL_HISTORY
399 if (display_frame) return; // no variances when examining history
400 #endif
401 if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
402 t = d->hierarchical_time.variances[slot];
403 } else {
404 t = d->self_time.variances[slot];
405 }
406
407 t = 1000 * 1000 * t;
408
409 if (r->heat == 0)
410 r->heat = t;
411 else
412 r->heat = r->heat + t + 2 * sqrt(r->heat * t);
413 } else {
414 ++uncounted;
415 }
416 }
417
propogate_expanded(Prof_Zone_Stack * c)418 static void propogate_expanded(Prof_Zone_Stack *c)
419 {
420 Profile_Tracker_Data_Record *d = (Profile_Tracker_Data_Record *) c->highlevel;
421 if (d == NULL) {
422 ++uncounted;
423 return;
424 }
425 if (c->parent->zone && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD) {
426 ((Prof_Report_Record *) c->parent->zone->highlevel)[0].prefix = '+';
427 ((Prof_Report_Record *) c->parent->zone->highlevel)[1].prefix = '+';
428 ((Prof_Report_Record *) c->parent->zone->highlevel)[2].prefix = '+';
429 }
430
431 if (c->zone == expand) {
432 Prof_Report_Record *r = (Prof_Report_Record *) expand->highlevel;
433 // accumulate this time to ourselves
434 r[2].values[0] += 1000 * get_value(&d->self_time);
435 r[2].values[1] += 1000 * get_value(&d->hierarchical_time);
436 r[2].values[2] += get_value(&d->entry_count);
437 if (d->max_recursion > r[2].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
438 r[2].number = d->max_recursion;
439 // propogate it to the parents
440 if (c->parent->zone) {
441 r = (Prof_Report_Record *) c->parent->zone->highlevel;
442 r[1].values[0] += 1000 * get_value(&d->self_time);
443 r[1].values[1] += 1000 * get_value(&d->hierarchical_time);
444 r[1].values[2] += get_value(&d->entry_count);
445 d = (Profile_Tracker_Data_Record *) c->parent->highlevel;
446 if (d->max_recursion > r[1].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
447 r[1].number = d->max_recursion;
448 }
449 }
450
451 if (c->parent->zone == expand) {
452 Prof_Report_Record *r = (Prof_Report_Record *) c->zone->highlevel;
453 r[0].values[0] += 1000 * get_value(&d->self_time);
454 r[0].values[1] += 1000 * get_value(&d->hierarchical_time);
455 r[0].values[2] += get_value(&d->entry_count);
456 if (d->max_recursion > r[0].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
457 r[0].number = d->max_recursion;
458 }
459 }
460
compute_heat(double variance,double value)461 static double compute_heat(double variance, double value)
462 {
463 double factor, stdev;
464 double fabs_value = fabs(value);
465 const float VARIANCE_TOLERANCE_FACTOR = 0.5f;
466
467 variance = variance - value*value;
468 if (variance < 0) variance = 0;
469 stdev = sqrt(variance);
470
471 if (fabs_value < 0.000001) {
472 return 0;
473 } else {
474 factor = (stdev / fabs_value) * (1.0f / VARIANCE_TOLERANCE_FACTOR);
475 }
476
477 if (factor < 0) return 0;
478 if (factor > 1) return 1;
479 return factor;
480 }
481
pob_compare(const void * p,const void * q)482 static int pob_compare(const void *p, const void *q)
483 {
484 double a = ((Prof_Report_Record *) p)->values[0];
485 double b = ((Prof_Report_Record *) q)->values[0];
486
487 return (b < a) ? -1 : (b > a);
488 }
489
pob_expand_compare(const void * p,const void * q)490 static int pob_expand_compare(const void *p, const void *q)
491 {
492 Prof_Report_Record * a = (Prof_Report_Record *) p;
493 Prof_Report_Record * b = (Prof_Report_Record *) q;
494
495 if (a->indent != b->indent) {
496 if (a->indent == 5) return -1;
497 if (b->indent == 5) return 1;
498 if (a->indent == 3) return 1;
499 if (b->indent == 3) return -1;
500 return 0;
501 }
502 if (a->values[1] == b->values[1])
503 return 0;
504
505 if (a->values[1] < b->values[1]) {
506 if (a->indent == 5) return -1;
507 return 1;
508 }
509
510 if (a->indent == 5) return 1;
511 return -1;
512 }
513
514 static int cursor;
515 static int update_cursor;
516
Prof_create_report(void)517 Prof_Report *Prof_create_report(void)
518 {
519 double avg_frame_time,fps;
520 char *displayed_quantity_name;
521 int i,s;
522 Prof_Report *pob;
523
524 if (displayed_quantity == Prof_CALL_GRAPH)
525 s = 3;
526 else
527 s = 1;
528
529 pob = allocate_buffer(Prof_num_zones * s);
530 for (i=0; i < Prof_num_zones; ++i) {
531 Prof_Zone *z = Prof_zones[i];
532 Prof_Report_Record *r = &pob->record[i*s];
533 z->highlevel = (void *) r;
534 if (displayed_quantity == Prof_CALL_GRAPH) {
535 r[0].name = r[1].name = r[2].name = z->name;
536 r[0].value_flag = 1 | 2 | 4;
537 r[1].value_flag = 1 | 2 | 4;
538 r[2].value_flag = 1 | 2 | 4;
539 r[0].indent = 3;
540 r[1].indent = 5;
541 r[2].indent = 0;
542 r[0].zone = r[1].zone = r[2].zone = (void *) z;
543 r[0].prefix = r[1].prefix = r[2].prefix = 0;
544 } else {
545 r->value_flag = 1 | 2 | 4;
546 r->name = z->name;
547 r->zone = (void *) z;
548 r->indent = 0;
549 r->prefix = 0;
550 }
551 }
552
553 avg_frame_time = frame_time.values[slot];
554 if (avg_frame_time == 0) avg_frame_time = 0.01f;
555 fps = 1.0f / avg_frame_time;
556
557 displayed_quantity_name = "*error*";
558 switch (displayed_quantity) {
559 case Prof_SELF_TIME:
560 displayed_quantity_name = "sort self";
561 break;
562 case Prof_HIERARCHICAL_TIME:
563 displayed_quantity_name = "sort hier";
564 break;
565 case Prof_CALL_GRAPH:
566 displayed_quantity_name = "sort hier";
567 break;
568 }
569
570 pob->title[0] = (char *) malloc(BUFSIZ);
571 sprintf(pob->title[0],
572 "%3.3lf ms/frame (fps: %3.2lf) %s",
573 avg_frame_time * 1000, fps, displayed_quantity_name);
574
575 #ifdef Prof_CALL_HISTORY
576 if (display_frame) {
577 sprintf(pob->title[0] + strlen(pob->title[0]), " - %d frame%s ago",
578 display_frame, display_frame == 1 ? "" : "s");
579 } else {
580 strcat(pob->title[0], " - current frame");
581 }
582 #endif
583
584 if (speedstep_warning)
585 pob->title[1] = _strdup("WARNING: SpeedStep-like timer inconsistencies detected. Results are unreliable!");
586
587 if (displayed_quantity == Prof_CALL_GRAPH) {
588 Prof_Report_Record *r = (Prof_Report_Record *) expand->highlevel;
589 int j=0;
590
591 Prof_traverse(propogate_expanded);
592
593 r[2].prefix = '-';
594
595 for (i=0; i < pob->num_record; ++i) {
596 if (pob->record[i].values[0] || pob->record[i].values[1] || pob->record[i].values[2]) {
597 pob->record[j] = pob->record[i];
598 ++j;
599 }
600 }
601 pob->num_record = j;
602
603 qsort(pob->record, pob->num_record, sizeof(pob->record[0]), pob_expand_compare);
604
605 for (i=0; i < pob->num_record; ++i)
606 if (pob->record[i].indent == 5)
607 pob->record[i].indent = 3;
608 } else {
609
610 uncounted = 0;
611 Prof_traverse(propogate_to_zone);
612
613 for (i=0; i < Prof_num_zones; ++i) {
614 if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
615 double t = pob->record[i].values[0];
616 pob->record[i].values[0] = pob->record[i].values[1];
617 pob->record[i].values[1] = t;
618 }
619
620 pob->record[i].heat = compute_heat(pob->record[i].heat, pob->record[i].values[0]);
621 }
622
623 qsort(pob->record, pob->num_record, sizeof(pob->record[0]), pob_compare);
624
625 }
626
627 if (update_cursor) {
628 for (i=0; i < pob->num_record; ++i) {
629 if (pob->record[i].zone == expand) {
630 cursor = i;
631 break;
632 }
633 }
634 update_cursor = 0;
635 }
636
637 pob->header[0] = _strdup("zone");
638 if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
639 pob->header[1] = _strdup("hier");
640 pob->header[2] = _strdup("self");
641 } else {
642 pob->header[1] = _strdup("self");
643 pob->header[2] = _strdup("hier");
644 }
645 pob->header[3] = _strdup("count");
646
647 if (cursor < 0) cursor = 0;
648 if (cursor >= pob->num_record) cursor = pob->num_record-1;
649 pob->hilight = cursor;
650
651 return pob;
652 }
653
Prof_free_report(Prof_Report * z)654 void Prof_free_report(Prof_Report *z)
655 {
656 int i;
657 for (i=0; i < NUM_TITLE; ++i)
658 if (z->title[i])
659 free(z->title[i]);
660 for (i=0; i < NUM_HEADER; ++i)
661 if (z->header[i])
662 free(z->header[i]);
663 free(z->record);
664 free(z);
665 }
666
Prof_move_cursor(int num)667 Prof_extern_C void Prof_move_cursor(int num)
668 {
669 cursor += num;
670 }
671
Prof_set_cursor(int num)672 Prof_extern_C void Prof_set_cursor(int num)
673 {
674 cursor = num;
675 }
676
Prof_select(void)677 Prof_extern_C void Prof_select(void)
678 {
679 Prof_Report *b = Prof_create_report();
680 if (b->hilight >= 0) {
681 void *z = b->record[b->hilight].zone;
682 if (z != NULL) {
683 expand = (Prof_Zone *) z;
684 displayed_quantity = Prof_CALL_GRAPH;
685 }
686 }
687 Prof_free_report(b);
688 update_cursor = 1;
689 }
690
Prof_select_parent(void)691 Prof_extern_C void Prof_select_parent(void)
692 {
693 int i;
694 void *old = (void *) expand;
695 Prof_Report *b = Prof_create_report();
696 for (i=0; i < b->num_record; ++i) {
697 if (b->record[i].indent == 0) break;
698 if (b->record[i].zone == old) continue;
699 expand = (Prof_Zone *) b->record[i].zone;
700 }
701 Prof_free_report(b);
702 update_cursor = 1;
703 }
704
Prof_set_frame(int num)705 Prof_extern_C void Prof_set_frame(int num)
706 {
707 if (num < 0) num = 0;
708 if (num >= NUM_FRAME_SLOTS) num = NUM_FRAME_SLOTS-1;
709
710 display_frame = num;
711 }
712
Prof_move_frame(int delta)713 Prof_extern_C void Prof_move_frame(int delta)
714 {
715 // convert so negative delta = "into the past"
716 Prof_set_frame(display_frame - delta);
717 }
718
Prof_set_smoothing(int x)719 Prof_extern_C void Prof_set_smoothing(int x)
720 {
721 if (x <= 0) x = 0;
722 if (x >= NUM_PROFILE_TRACKER_HISTORY_SLOTS)
723 x = NUM_PROFILE_TRACKER_HISTORY_SLOTS-1;
724
725 slot = x;
726 }
727
728 // currently does nothing
Prof_set_recursion(Prof_Recursion_Mode e)729 Prof_extern_C void Prof_set_recursion(Prof_Recursion_Mode e)
730 {
731 recurse = e;
732 }
733
id(Prof_Zone * z)734 static int id(Prof_Zone *z)
735 {
736 // hash the string so that the id is consistent from
737 // run to run (rather than using the pointer itself which isn't)
738 // @TODO: only compute this at zone init time?
739
740 unsigned int h = 0x55555555;
741 char *n = z->name;
742
743 while (*n)
744 h = (h << 5) + (h >> 27) + *n++;
745
746 return h;
747 }
748
Prof_graph(int num_frames,void (* callback)(int id,int x0,int x1,float * values,void * data),void * data)749 void Prof_graph(int num_frames, void (*callback)(int id, int x0, int x1, float *values, void *data), void *data)
750 {
751 #ifdef Prof_ZONE_HISTORY
752 int i,h = history_index;
753 if (num_frames > NUM_FRAME_SLOTS)
754 num_frames = NUM_FRAME_SLOTS;
755
756 for (i=0; i < Prof_num_zones; ++i) {
757 if (h >= num_frames) {
758 callback(id(Prof_zones[i]), 0, num_frames, &zone_history[i][h-num_frames], data);
759 } else {
760 callback(id(Prof_zones[i]), num_frames - h, num_frames, &zone_history[i][0], data);
761 callback(id(Prof_zones[i]), 0, num_frames-h, &zone_history[i][NUM_FRAME_SLOTS-(num_frames-h)], data);
762 }
763 }
764
765 // display frame "cursor"
766 if (display_frame != 0) {
767 float value[2] = { 2.0, 0 };
768 callback(0, NUM_FRAME_SLOTS-1-display_frame, NUM_FRAME_SLOTS-1-display_frame, value, data);
769 }
770 #endif
771 }
772
773
774
775