Skip to content

Commit 35589a6

Browse files
goderbauertbosch
authored andcommitted
feat(benchpress): more smoothness metrics
Benchpress now prints out the best and worst frame time in addition to the percentage of frames that hit the target of 60fps. It also renames 'meanFrameTime' to 'frameTime.mean'. That way, all frameTime metrics start with a common suffix and will be grouped together in the console reporter. part of #821
1 parent 598a75e commit 35589a6

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

modules/benchpress/src/metric/perflog_metric.ts

+32-6
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,19 @@ export class PerflogMetric extends Metric {
6262
}
6363
}
6464
if (this._captureFrames) {
65-
res['meanFrameTime'] = this._perfLogFeatures.frameCapture ?
66-
'mean frame time in ms (target: 16.6ms for 60fps)' :
67-
'WARNING: Metric requested, but not supported by driver';
65+
if (!this._perfLogFeatures.frameCapture) {
66+
var warningMsg = 'WARNING: Metric requested, but not supported by driver';
67+
// using dot syntax for metric name to keep them grouped together in console reporter
68+
res['frameTime.mean'] = warningMsg;
69+
res['frameTime.worst'] = warningMsg;
70+
res['frameTime.best'] = warningMsg;
71+
res['frameTime.smooth'] = warningMsg;
72+
} else {
73+
res['frameTime.mean'] = 'mean frame time in ms (target: 16.6ms for 60fps)';
74+
res['frameTime.worst'] = 'worst frame time in ms';
75+
res['frameTime.best'] = 'best frame time in ms';
76+
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
77+
}
6878
}
6979
StringMapWrapper.forEach(this._microMetrics,
7080
(desc, name) => { StringMapWrapper.set(res, name, desc); });
@@ -172,7 +182,10 @@ export class PerflogMetric extends Metric {
172182
result['renderTime'] = 0;
173183
}
174184
if (this._captureFrames) {
175-
result['meanFrameTime'] = 0;
185+
result['frameTime.mean'] = 0;
186+
result['frameTime.best'] = 0;
187+
result['frameTime.worst'] = 0;
188+
result['frameTime.smooth'] = 0;
176189
}
177190
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
178191

@@ -288,22 +301,35 @@ export class PerflogMetric extends Metric {
288301
'frame capture requested in benchpress, but no start event was found');
289302
}
290303
if (frameTimes.length > 0) {
291-
result['meanFrameTime'] =
292-
ListWrapper.reduce(frameTimes, (a, b) => a + b, 0) / frameTimes.length;
304+
this._addFrameMetrics(result, frameTimes);
293305
}
294306
result['pureScriptTime'] = result['scriptTime'] - gcTimeInScript - renderTimeInScript;
295307
return result;
296308
}
297309

310+
_addFrameMetrics(result: StringMap<string, any>, frameTimes: any[]) {
311+
result['frameTime.mean'] =
312+
ListWrapper.reduce(frameTimes, (a, b) => a + b, 0) / frameTimes.length;
313+
var firstFrame = ListWrapper.get(frameTimes, 0);
314+
result['frameTime.worst'] = ListWrapper.reduce(frameTimes, (a, b) => a > b ? a : b, firstFrame);
315+
result['frameTime.best'] = ListWrapper.reduce(frameTimes, (a, b) => a < b ? a : b, firstFrame);
316+
result['frameTime.smooth'] =
317+
ListWrapper.filter(frameTimes, (a) => a < _FRAME_TIME_SMOOTH_THRESHOLD).length /
318+
frameTimes.length;
319+
}
320+
298321
_markName(index) { return `${_MARK_NAME_PREFIX}${index}`; }
299322
}
323+
300324
var _MICRO_ITERATIONS_REGEX = RegExpWrapper.create('(.+)\\*(\\d+)$');
301325

302326
var _MAX_RETRY_COUNT = 20;
303327
var _MARK_NAME_PREFIX = 'benchpress';
304328
var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
305329

306330
var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
331+
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
332+
var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
307333

308334
var _BINDINGS = [
309335
bind(PerflogMetric)

modules/benchpress/test/metric/perflog_metric_spec.ts

+61-3
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,20 @@ export function main() {
106106
var description =
107107
createMetric([[]], null, new PerfLogFeatures({frameCapture: true}), null, true)
108108
.describe();
109-
expect(description['meanFrameTime']).not.toContain('WARNING');
109+
expect(description['frameTime.mean']).not.toContain('WARNING');
110+
expect(description['frameTime.best']).not.toContain('WARNING');
111+
expect(description['frameTime.worst']).not.toContain('WARNING');
112+
expect(description['frameTime.smooth']).not.toContain('WARNING');
110113
});
111114

112115
it('should describe itself if frame capture is requested and not available', () => {
113116
var description =
114117
createMetric([[]], null, new PerfLogFeatures({frameCapture: false}), null, true)
115118
.describe();
116-
expect(description['meanFrameTime']).toContain('WARNING');
119+
expect(description['frameTime.mean']).toContain('WARNING');
120+
expect(description['frameTime.best']).toContain('WARNING');
121+
expect(description['frameTime.worst']).toContain('WARNING');
122+
expect(description['frameTime.smooth']).toContain('WARNING');
117123
});
118124

119125
describe('beginMeasure', () => {
@@ -336,7 +342,7 @@ export function main() {
336342
],
337343
null, true)
338344
.then((data) => {
339-
expect(data['meanFrameTime']).toBe(((3 - 1) + (4 - 3)) / 2);
345+
expect(data['frameTime.mean']).toBe(((3 - 1) + (4 - 3)) / 2);
340346
async.done();
341347
});
342348
}));
@@ -398,6 +404,58 @@ export function main() {
398404
});
399405
}));
400406

407+
it('should calculate best and worst frame time', inject([AsyncTestCompleter], (async) => {
408+
aggregate([
409+
eventFactory.markStart('frameCapture', 0),
410+
eventFactory.instant('frame', 1),
411+
eventFactory.instant('frame', 9),
412+
eventFactory.instant('frame', 15),
413+
eventFactory.instant('frame', 18),
414+
eventFactory.instant('frame', 28),
415+
eventFactory.instant('frame', 32),
416+
eventFactory.markEnd('frameCapture', 10)
417+
],
418+
null, true)
419+
.then((data) => {
420+
expect(data['frameTime.worst']).toBe(10);
421+
expect(data['frameTime.best']).toBe(3);
422+
async.done();
423+
});
424+
}));
425+
426+
it('should calculate percentage of smoothness to be good',
427+
inject([AsyncTestCompleter], (async) => {
428+
aggregate([
429+
eventFactory.markStart('frameCapture', 0),
430+
eventFactory.instant('frame', 1),
431+
eventFactory.instant('frame', 2),
432+
eventFactory.instant('frame', 3),
433+
eventFactory.markEnd('frameCapture', 4)
434+
],
435+
null, true)
436+
.then((data) => {
437+
expect(data['frameTime.smooth']).toBe(1.0);
438+
async.done();
439+
});
440+
}));
441+
442+
it('should calculate percentage of smoothness to be bad',
443+
inject([AsyncTestCompleter], (async) => {
444+
aggregate([
445+
eventFactory.markStart('frameCapture', 0),
446+
eventFactory.instant('frame', 1),
447+
eventFactory.instant('frame', 2),
448+
eventFactory.instant('frame', 22),
449+
eventFactory.instant('frame', 23),
450+
eventFactory.instant('frame', 24),
451+
eventFactory.markEnd('frameCapture', 4)
452+
],
453+
null, true)
454+
.then((data) => {
455+
expect(data['frameTime.smooth']).toBe(0.75);
456+
async.done();
457+
});
458+
}));
401459

402460
});
403461

0 commit comments

Comments
 (0)