blob: 6931e956d5e1a5750aeeef2ad9c3e56482392b3d [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.pts.ui;
import android.content.Context;
import android.content.Intent;
import android.openglperf.cts.GlPlanetsActivity;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import com.android.pts.util.PtsActivityInstrumentationTestCase2;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
/**
* measure time taken to render n frames with OpenGL.
* This will measure the jankiness of the Gl rendering.
* If some frames are delayed, total time will take longer than n x refresh_rate
*/
public class FpsTest extends PtsActivityInstrumentationTestCase2<GlPlanetsActivity> {
private static final String TAG = "FpsTest";
private static final int NUM_FRAMES_TO_RENDER = 60 * 60;
private static final long RENDERING_TIMEOUT = NUM_FRAMES_TO_RENDER / 10;
// error of this much in ms in refresh interval is not considered as jankiness
// note that this margin is set to be little big to consider that there can be additional delay
// measurement on some devices showed many jankiness around 2 to 3 ms,
// which should not be counted
private static final double REFRESH_INTERVAL_THRESHHOLD_IN_MS = 4.0;
private GlPlanetsActivity mActivity;
public FpsTest() {
super(GlPlanetsActivity.class);
}
@Override
protected void tearDown() throws Exception {
mActivity = null;
super.tearDown();
}
public void testFrameIntervals() throws Exception {
Intent intent = new Intent();
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES,
NUM_FRAMES_TO_RENDER);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, 0);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_VERTICES, true);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_INDICES, true);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_INDEX_BUFFERS, 10);
setActivityIntent(intent);
mActivity = getActivity();
boolean waitResult = mActivity.waitForGlPlanetsCompletionWithTimeout(RENDERING_TIMEOUT);
assertTrue("timeout while waiting for rendering completion", waitResult);
int[] frameInterval = mActivity.getFrameInterval();
assertTrue(frameInterval.length == NUM_FRAMES_TO_RENDER);
double fpsMeasured = mActivity.getAverageFps();
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
Display dpy = wm.getDefaultDisplay();
double fpsNominal = dpy.getRefreshRate();
double frameIntervalNominalInMs = 1000.0 / fpsNominal;
int jankNumber = 0;
// only count positive ( = real delay)
double maxDelay = 0;
// first one not valid, and should be thrown away
double[] intervals = new double[NUM_FRAMES_TO_RENDER - 1];
double[] jankiness = new double[NUM_FRAMES_TO_RENDER - 1];
double deltaAccumulated = 0;
for (int i = 0; i < NUM_FRAMES_TO_RENDER - 1; i++) {
intervals[i] = frameInterval[i + 1];
double delay = (double)intervals[i] - frameIntervalNominalInMs;
if (Math.abs(delay) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
// Falling here does not necessarily mean jank as it may be catching up
// the previous delay. Basically count the first delay as jank, but subsequent
// variation should be checked with accumulated delay
double delayAdjusted = 0;
if (deltaAccumulated == 0) { // This is the first delay. always consider as total
jankNumber++;
delayAdjusted = delay;
} else { // delay already happened
double deltaFromLastRefresh = deltaAccumulated - Math.floor(deltaAccumulated /
frameIntervalNominalInMs) * frameIntervalNominalInMs;
if (deltaAccumulated < 0) {
// adjust as the above operation makes delay positive
deltaFromLastRefresh -= frameIntervalNominalInMs;
}
delayAdjusted = delay + deltaFromLastRefresh;
if (Math.abs(delayAdjusted) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
jankNumber++;
} else { // caught up
delayAdjusted = 0;
deltaAccumulated = 0;
}
}
deltaAccumulated += delay;
if (delayAdjusted > maxDelay) {
maxDelay = delayAdjusted;
}
jankiness[i] = delayAdjusted;
} else {
jankiness[i] = 0;
deltaAccumulated = 0;
}
}
Log.i(TAG, " fps nominal " + fpsNominal + " fps measured " + fpsMeasured);
getReportLog().printArray("intervals ms", intervals, false);
getReportLog().printArray("jankiness ms", jankiness, false);
getReportLog().printSummaryFull("Frame interval", " max delay ms", maxDelay, "number of jank", jankNumber);
}
}