diff options
author | Chris Craik <ccraik@google.com> | 2012-01-23 17:29:12 -0800 |
---|---|---|
committer | Chris Craik <ccraik@google.com> | 2012-01-24 13:25:49 -0800 |
commit | 702c6fdc4799593c16eb0051703e3bd62086ff42 (patch) | |
tree | a78f71f31b5c8a6281e2c913d66c79fc49d6ca3c /tests/TileBenchmark | |
parent | d4e34d61d01222ff90684b9a1dc4f9c8be560e7c (diff) | |
download | frameworks_base-702c6fdc4799593c16eb0051703e3bd62086ff42.zip frameworks_base-702c6fdc4799593c16eb0051703e3bd62086ff42.tar.gz frameworks_base-702c6fdc4799593c16eb0051703e3bd62086ff42.tar.bz2 |
Allow profiling of animation performance
Also fixes manual testing mode
Depends on external/webkit change: https://android-git.corp.google.com/g/#/c/159794/
Change-Id: I169e80f9b5328b1b5a7b0aeaf09652de67febd8d
Diffstat (limited to 'tests/TileBenchmark')
5 files changed, 400 insertions, 100 deletions
diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml index 5af52dc..6c7055b 100644 --- a/tests/TileBenchmark/res/values/strings.xml +++ b/tests/TileBenchmark/res/values/strings.xml @@ -49,8 +49,9 @@ <!-- Drop down menu entry - automatically scroll to the end of the page with scrollBy() [CHAR LIMIT=15] --> <string name="movement_auto_scroll">Auto-scroll</string> - <!-- Drop down menu entry - [CHAR LIMIT=15] --> - <string name="movement_auto_fling">Auto-fling</string> + <!-- Drop down menu entry - automatically record for a set time before + stopping [CHAR LIMIT=15] --> + <string name="movement_timed">Timed</string> <!-- Drop down menu entry - manually navigate the page(s), hit 'capture' button [CHAR LIMIT=15] --> <string name="movement_manual">Manual</string> @@ -67,14 +68,21 @@ <!-- 75th percentile - 75% of frames fall below this value [CHAR LIMIT=12] --> <string name="percentile_75">75%ile</string> + <!-- standard deviation [CHAR LIMIT=12] --> + <string name="std_dev">StdDev</string> + <!-- mean [CHAR LIMIT=12] --> + <string name="mean">mean</string> + + + <!-- Frame rate [CHAR LIMIT=15] --> <string name="frames_per_second">Frames/sec</string> <!-- Portion of viewport covered by good tiles [CHAR LIMIT=15] --> <string name="viewport_coverage">Coverage</string> <!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] --> <string name="render_millis">RenderMillis</string> - <!-- Number of rendering stalls while running the test [CHAR LIMIT=15] --> - <string name="render_stalls">Stalls</string> + <!-- Animation Framerate [CHAR LIMIT=15] --> + <string name="animation_framerate">AnimFramerate</string> <!-- Format string for stat value overlay [CHAR LIMIT=15] --> <string name="format_stat">%4.4f</string> diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java index cc39b75..6356cc1 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java @@ -27,17 +27,57 @@ import android.os.Bundle; import android.os.Environment; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; +import android.webkit.WebSettings; +import android.widget.Spinner; public class PerformanceTest extends ActivityInstrumentationTestCase2<ProfileActivity> { + public static class AnimStat { + double aggVal = 0; + double aggSqrVal = 0; + double count = 0; + } + private class StatAggregator extends PlaybackGraphs { private HashMap<String, Double> mDataMap = new HashMap<String, Double>(); + private HashMap<String, AnimStat> mAnimDataMap = new HashMap<String, AnimStat>(); private int mCount = 0; + public void aggregate() { + boolean inAnimTests = mAnimTests != null; + Resources resources = mWeb.getResources(); + String animFramerateString = resources.getString(R.string.animation_framerate); + for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { + String name = e.getKey(); + if (inAnimTests) { + if (name.equals(animFramerateString)) { + // in animation testing phase, record animation framerate and aggregate + // stats, differentiating on values of mAnimTestNr and mDoubleBuffering + String fullName = ANIM_TEST_NAMES[mAnimTestNr] + " " + name; + fullName += mDoubleBuffering ? " tiled" : " webkit"; + + if (!mAnimDataMap.containsKey(fullName)) { + mAnimDataMap.put(fullName, new AnimStat()); + } + AnimStat statVals = mAnimDataMap.get(fullName); + statVals.aggVal += e.getValue(); + statVals.aggSqrVal += e.getValue() * e.getValue(); + statVals.count += 1; + } + } else { + double aggVal = mDataMap.containsKey(name) + ? mDataMap.get(name) : 0; + mDataMap.put(name, aggVal + e.getValue()); + } + } + + if (inAnimTests) { + return; + } + mCount++; - Resources resources = mView.getResources(); for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { for (int statIndex = 0; statIndex < Stats.length; statIndex++) { String metricLabel = resources.getString( @@ -53,34 +93,47 @@ public class PerformanceTest extends mDataMap.put(label, aggVal); } } - for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { - double aggVal = mDataMap.containsKey(e.getKey()) - ? mDataMap.get(e.getKey()) : 0; - mDataMap.put(e.getKey(), aggVal + e.getValue()); - } + } + // build the final bundle of results public Bundle getBundle() { Bundle b = new Bundle(); - int count = 0 == mCount ? Integer.MAX_VALUE : mCount; + int count = (0 == mCount) ? Integer.MAX_VALUE : mCount; for (Map.Entry<String, Double> e : mDataMap.entrySet()) { b.putDouble(e.getKey(), e.getValue() / count); } + + for (Map.Entry<String, AnimStat> e : mAnimDataMap.entrySet()) { + String statName = e.getKey(); + AnimStat statVals = e.getValue(); + + double avg = statVals.aggVal/statVals.count; + double stdDev = Math.sqrt((statVals.aggSqrVal / statVals.count) - avg * avg); + + b.putDouble(statName, avg); + b.putDouble(statName + " STD DEV", stdDev); + } + return b; } } ProfileActivity mActivity; - ProfiledWebView mView; - StatAggregator mStats = new StatAggregator(); + ProfiledWebView mWeb; + Spinner mMovementSpinner; + StatAggregator mStats; private static final String LOGTAG = "PerformanceTest"; private static final String TEST_LOCATION = "webkit/page_cycler"; private static final String URL_PREFIX = "file://"; private static final String URL_POSTFIX = "/index.html?skip=true"; private static final int MAX_ITERATIONS = 4; - private static final String TEST_DIRS[] = { - "alexa25_2011"//, "alexa_us", "android", "dom", "intl2", "moz", "moz2" + private static final String SCROLL_TEST_DIRS[] = { + "alexa25_2011" + }; + private static final String ANIM_TEST_DIRS[] = { + "dhtml" }; public PerformanceTest() { @@ -91,7 +144,22 @@ public class PerformanceTest extends protected void setUp() throws Exception { super.setUp(); mActivity = getActivity(); - mView = (ProfiledWebView) mActivity.findViewById(R.id.web); + mWeb = (ProfiledWebView) mActivity.findViewById(R.id.web); + mMovementSpinner = (Spinner) mActivity.findViewById(R.id.movement); + mStats = new StatAggregator(); + + // use mStats as a condition variable between the UI thread and + // this(the testing) thread + mActivity.setCallback(new ProfileCallback() { + @Override + public void profileCallback(RunData data) { + mStats.setData(data); + synchronized (mStats) { + mStats.notify(); + } + } + }); + } private boolean loadUrl(final String url) { @@ -100,12 +168,13 @@ public class PerformanceTest extends mActivity.runOnUiThread(new Runnable() { @Override public void run() { - mView.loadUrl(url); + mWeb.loadUrl(url); } }); synchronized (mStats) { mStats.wait(); } + mStats.aggregate(); } catch (InterruptedException e) { e.printStackTrace(); @@ -114,15 +183,30 @@ public class PerformanceTest extends return true; } - private boolean runIteration() { + private boolean validTest(String nextTest) { + // if testing animations, test must be in mAnimTests + if (mAnimTests == null) + return true; + + for (String test : mAnimTests) { + if (test.equals(nextTest)) { + return true; + } + } + return false; + } + + private boolean runIteration(String[] testDirs) { File sdFile = Environment.getExternalStorageDirectory(); - for (String testDirName : TEST_DIRS) { + for (String testDirName : testDirs) { File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName); Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath() + "', exists=" + testDir.exists()); + for (File siteDir : testDir.listFiles()) { - if (!siteDir.isDirectory()) + if (!siteDir.isDirectory() || !validTest(siteDir.getName())) { continue; + } if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath() + URL_POSTFIX)) { @@ -133,7 +217,44 @@ public class PerformanceTest extends return true; } - public void testMetrics() { + private boolean runTestDirs(String[] testDirs) { + for (int i = 0; i < MAX_ITERATIONS; i++) + if (!runIteration(testDirs)) { + return false; + } + return true; + } + + private void pushDoubleBuffering() { + getInstrumentation().runOnMainSync(new Runnable() { + public void run() { + mWeb.setDoubleBuffering(mDoubleBuffering); + } + }); + } + + private void setScrollingTestingMode(final boolean scrolled) { + getInstrumentation().runOnMainSync(new Runnable() { + public void run() { + mMovementSpinner.setSelection(scrolled ? 0 : 2); + } + }); + } + + + private String[] mAnimTests = null; + private int mAnimTestNr = -1; + private boolean mDoubleBuffering = true; + private static final String[] ANIM_TEST_NAMES = { + "slow", "fast" + }; + private static final String[][] ANIM_TESTS = { + {"scrolling", "replaceimages", "layers5", "layers1"}, + {"slidingballs", "meter", "slidein", "fadespacing", "colorfade", + "mozilla", "movingtext", "diagball", "zoom", "imageslide"}, + }; + + private boolean checkMedia() { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state) @@ -141,27 +262,43 @@ public class PerformanceTest extends Log.d(LOGTAG, "ARG Can't access sd card!"); // Can't read the SD card, fail and die! getInstrumentation().sendStatus(1, null); - return; + return false; } + return true; + } - // use mGraphs as a condition variable between the UI thread and - // this(the testing) thread - mActivity.setCallback(new ProfileCallback() { - @Override - public void profileCallback(RunData data) { - Log.d(LOGTAG, "test completion callback"); - mStats.setData(data); - synchronized (mStats) { - mStats.notify(); + public void testMetrics() { + setScrollingTestingMode(true); + if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) { + getInstrumentation().sendStatus(0, mStats.getBundle()); + } else { + getInstrumentation().sendStatus(1, null); + } + } + + private boolean runAnimationTests() { + for (int doubleBuffer = 0; doubleBuffer <= 1; doubleBuffer++) { + mDoubleBuffering = doubleBuffer == 1; + pushDoubleBuffering(); + for (mAnimTestNr = 0; mAnimTestNr < ANIM_TESTS.length; mAnimTestNr++) { + mAnimTests = ANIM_TESTS[mAnimTestNr]; + if (!runTestDirs(ANIM_TEST_DIRS)) { + return false; } } - }); + } + return true; + } - for (int i = 0; i < MAX_ITERATIONS; i++) - if (!runIteration()) { - getInstrumentation().sendStatus(1, null); - return; - } - getInstrumentation().sendStatus(0, mStats.getBundle()); + public void testAnimations() { + // instead of autoscrolling, load each page until either an timer fires, + // or the animation signals complete via javascript + setScrollingTestingMode(false); + + if (checkMedia() && runAnimationTests()) { + getInstrumentation().sendStatus(0, mStats.getBundle()); + } else { + getInstrumentation().sendStatus(1, null); + } } } diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java index 9ea90f8..a3ae9be 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java @@ -80,7 +80,7 @@ public class PlaybackGraphs { for (int tileID = 1; tileID < frame.length; tileID++) { TileData data = frame[tileID]; double coverage = viewportCoverage(frame[0], data); - total += coverage * (data.isReady ? 1 : 0); + total += coverage * (data.isReady ? 100 : 0); totalCount += coverage; } if (totalCount == 0) { @@ -91,7 +91,7 @@ public class PlaybackGraphs { @Override public double getMax() { - return 1; + return 100; } @Override @@ -108,6 +108,9 @@ public class PlaybackGraphs { } public static double getPercentile(double sortedValues[], double ratioAbove) { + if (sortedValues.length == 0) + return -1; + double index = ratioAbove * (sortedValues.length - 1); int intIndex = (int) Math.floor(index); if (index == intIndex) { @@ -118,6 +121,31 @@ public class PlaybackGraphs { + sortedValues[intIndex + 1] * (alpha); } + public static double getMean(double sortedValues[]) { + if (sortedValues.length == 0) + return -1; + + double agg = 0; + for (double val : sortedValues) { + agg += val; + } + return agg / sortedValues.length; + } + + public static double getStdDev(double sortedValues[]) { + if (sortedValues.length == 0) + return -1; + + double agg = 0; + double sqrAgg = 0; + for (double val : sortedValues) { + agg += val; + sqrAgg += val*val; + } + double mean = agg / sortedValues.length; + return Math.sqrt((sqrAgg / sortedValues.length) - (mean * mean)); + } + protected static StatGen[] Stats = new StatGen[] { new StatGen() { @Override @@ -149,6 +177,26 @@ public class PlaybackGraphs { public int getLabelId() { return R.string.percentile_75; } + }, new StatGen() { + @Override + public double getValue(double[] sortedValues) { + return getStdDev(sortedValues); + } + + @Override + public int getLabelId() { + return R.string.std_dev; + } + }, new StatGen() { + @Override + public double getValue(double[] sortedValues) { + return getMean(sortedValues); + } + + @Override + public int getLabelId() { + return R.string.mean; + } }, }; @@ -159,40 +207,47 @@ public class PlaybackGraphs { } private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>(); - protected double[][] mStats = new double[Metrics.length][Stats.length]; + protected final double[][] mStats = new double[Metrics.length][Stats.length]; protected HashMap<String, Double> mSingleStats; + private void gatherFrameMetric(int metricIndex, double metricValues[], RunData data) { + // create graph out of rectangles, one per frame + int lastBar = 0; + for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) { + TileData frame[] = data.frames[frameIndex]; + int newBar = (frame[0].top + frame[0].bottom) / 2; + + MetricGen s = Metrics[metricIndex]; + double absoluteValue = s.getValue(frame); + double relativeValue = absoluteValue / s.getMax(); + relativeValue = Math.min(1,relativeValue); + relativeValue = Math.max(0,relativeValue); + int rightPos = (int) (-BAR_WIDTH * metricIndex); + int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue)); + + ShapeDrawable graphBar = new ShapeDrawable(); + graphBar.getPaint().setColor(Color.BLUE); + graphBar.setBounds(leftPos, lastBar, rightPos, newBar); + + mShapes.add(graphBar); + metricValues[frameIndex] = absoluteValue; + lastBar = newBar; + } + } + public void setData(RunData data) { mShapes.clear(); double metricValues[] = new double[data.frames.length]; + mSingleStats = data.singleStats; + if (data.frames.length == 0) { return; } for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { - // create graph out of rectangles, one per frame - int lastBar = 0; - for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) { - TileData frame[] = data.frames[frameIndex]; - int newBar = (frame[0].top + frame[0].bottom) / 2; - - MetricGen s = Metrics[metricIndex]; - double absoluteValue = s.getValue(frame); - double relativeValue = absoluteValue / s.getMax(); - relativeValue = Math.min(1,relativeValue); - relativeValue = Math.max(0,relativeValue); - int rightPos = (int) (-BAR_WIDTH * metricIndex); - int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue)); - - ShapeDrawable graphBar = new ShapeDrawable(); - graphBar.getPaint().setColor(Color.BLUE); - graphBar.setBounds(leftPos, lastBar, rightPos, newBar); - - mShapes.add(graphBar); - metricValues[frameIndex] = absoluteValue; - lastBar = newBar; - } + // calculate metric based on list of frames + gatherFrameMetric(metricIndex, metricValues, data); // store aggregate statistics per metric (median, and similar) Arrays.sort(metricValues); @@ -200,8 +255,6 @@ public class PlaybackGraphs { mStats[metricIndex][statIndex] = Stats[statIndex].getValue(metricValues); } - - mSingleStats = data.singleStats; } } diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java index d38d006..2e77157 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java @@ -22,11 +22,12 @@ import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; +import android.os.CountDownTimer; +import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; -import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AdapterView; @@ -49,6 +50,8 @@ import java.io.ObjectOutputStream; */ public class ProfileActivity extends Activity { + private static final int TIMED_RECORD_MILLIS = 2000; + public interface ProfileCallback { public void profileCallback(RunData data); } @@ -65,6 +68,7 @@ public class ProfileActivity extends Activity { LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient(); AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient(); + TimedLoggingWebViewClient mTimedLoggingWebViewClient = new TimedLoggingWebViewClient(); private enum TestingState { NOT_TESTING, @@ -93,18 +97,18 @@ public class ProfileActivity extends Activity { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String movementStr = parent.getItemAtPosition(position).toString(); - if (movementStr == getResources().getString( - R.string.movement_auto_scroll) - || movementStr == getResources().getString( - R.string.movement_auto_fling)) { + if (movementStr == getResources().getString(R.string.movement_auto_scroll)) { mWeb.setWebViewClient(mAutoLoggingWebViewClient); mCaptureButton.setEnabled(false); mVelocitySpinner.setEnabled(true); - } else if (movementStr == getResources().getString( - R.string.movement_manual)) { + } else if (movementStr == getResources().getString(R.string.movement_manual)) { mWeb.setWebViewClient(mLoggingWebViewClient); mCaptureButton.setEnabled(true); mVelocitySpinner.setEnabled(false); + } else if (movementStr == getResources().getString(R.string.movement_timed)) { + mWeb.setWebViewClient(mTimedLoggingWebViewClient); + mCaptureButton.setEnabled(false); + mVelocitySpinner.setEnabled(false); } } @@ -124,16 +128,46 @@ public class ProfileActivity extends Activity { super.onPageStarted(view, url, favicon); mUrl.setText(url); } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + view.requestFocus(); + ((ProfiledWebView)view).onPageFinished(); + } } private class AutoLoggingWebViewClient extends LoggingWebViewClient { + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + startViewProfiling(true); + } @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + setTestingState(TestingState.PRE_TESTING); + } + } + + private class TimedLoggingWebViewClient extends LoggingWebViewClient { + @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); - view.requestFocus(); + startViewProfiling(false); - startViewProfiling(true); + // after a fixed time after page finished, stop testing + new CountDownTimer(TIMED_RECORD_MILLIS, TIMED_RECORD_MILLIS) { + @Override + public void onTick(long millisUntilFinished) { + } + + @Override + public void onFinish() { + mWeb.stopScrollTest(); + } + }.start(); } @Override @@ -178,11 +212,13 @@ public class ProfileActivity extends Activity { mMovementSpinner.setEnabled(false); break; case START_TESTING: + mCaptureButton.setChecked(true); mUrl.setBackgroundResource(R.color.background_start_testing); mInspectButton.setEnabled(false); mMovementSpinner.setEnabled(false); break; case STOP_TESTING: + mCaptureButton.setChecked(false); mUrl.setBackgroundResource(R.color.background_stop_testing); break; case SAVED_TESTING: @@ -195,7 +231,6 @@ public class ProfileActivity extends Activity { /** auto - automatically scroll. */ private void startViewProfiling(boolean auto) { // toggle capture button to indicate capture state to user - mCaptureButton.setChecked(true); mWeb.startScrollTest(mCallback, auto); setTestingState(TestingState.START_TESTING); } @@ -217,7 +252,7 @@ public class ProfileActivity extends Activity { public void profileCallback(RunData data) { new StoreFileTask().execute(new Pair<String, RunData>( TEMP_FILENAME, data)); - mCaptureButton.setChecked(false); + Log.d("ProfileActivity", "stored " + data.frames.length + " frames in file"); setTestingState(TestingState.STOP_TESTING); } }); @@ -245,8 +280,8 @@ public class ProfileActivity extends Activity { // Movement spinner String content[] = { getResources().getString(R.string.movement_auto_scroll), - getResources().getString(R.string.movement_auto_fling), - getResources().getString(R.string.movement_manual) + getResources().getString(R.string.movement_manual), + getResources().getString(R.string.movement_timed) }; adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, content); @@ -270,13 +305,7 @@ public class ProfileActivity extends Activity { }); // Custom profiling WebView - WebSettings settings = mWeb.getSettings(); - settings.setJavaScriptEnabled(true); - settings.setSupportZoom(true); - settings.setEnableSmoothTransition(true); - settings.setBuiltInZoomControls(true); - settings.setLoadWithOverviewMode(true); - settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does + mWeb.init(this); mWeb.setWebViewClient(new LoggingWebViewClient()); // URL text entry diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java index 83f1668..a706f78 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java @@ -20,21 +20,28 @@ import android.content.Context; import android.os.CountDownTimer; import android.util.AttributeSet; import android.util.Log; +import android.webkit.WebSettings; import android.webkit.WebView; +import android.widget.Toast; + +import java.util.ArrayList; import com.test.tilebenchmark.ProfileActivity.ProfileCallback; import com.test.tilebenchmark.RunData.TileData; public class ProfiledWebView extends WebView { + private static final String LOGTAG = "ProfiledWebView"; + private int mSpeed; private boolean mIsTesting = false; private boolean mIsScrolling = false; private ProfileCallback mCallback; private long mContentInvalMillis; - private boolean mHadToBeForced = false; private static final int LOAD_STALL_MILLIS = 2000; // nr of millis after load, // before test is forced + private double mLoadTime; + private double mAnimationTime; public ProfiledWebView(Context context) { super(context); @@ -53,6 +60,39 @@ public class ProfiledWebView extends WebView { super(context, attrs, defStyle, privateBrowsing); } + private class JavaScriptInterface { + Context mContext; + + /** Instantiate the interface and set the context */ + JavaScriptInterface(Context c) { + mContext = c; + } + + /** Show a toast from the web page */ + public void animationComplete() { + Toast.makeText(mContext, "Animation complete!", Toast.LENGTH_SHORT).show(); + //Log.d(LOGTAG, "anim complete"); + mAnimationTime = System.currentTimeMillis(); + } + } + + public void init(Context c) { + WebSettings settings = getSettings(); + settings.setJavaScriptEnabled(true); + settings.setSupportZoom(true); + settings.setEnableSmoothTransition(true); + settings.setBuiltInZoomControls(true); + settings.setLoadWithOverviewMode(true); + settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does + addJavascriptInterface(new JavaScriptInterface(c), "Android"); + mAnimationTime = 0; + mLoadTime = 0; + } + + public void onPageFinished() { + mLoadTime = System.currentTimeMillis(); + } + @Override protected void onDraw(android.graphics.Canvas canvas) { if (mIsTesting && mIsScrolling) { @@ -72,9 +112,12 @@ public class ProfiledWebView extends WebView { * scrolling, invalidate all content and redraw it, measuring time taken. */ public void startScrollTest(ProfileCallback callback, boolean autoScrolling) { - mIsScrolling = autoScrolling; mCallback = callback; mIsTesting = false; + mIsScrolling = false; + WebSettings settings = getSettings(); + settings.setProperty("tree_updates", "0"); + if (autoScrolling) { // after a while, force it to start even if the pages haven't swapped @@ -86,13 +129,18 @@ public class ProfiledWebView extends WebView { @Override public void onFinish() { // invalidate all content, and kick off redraw + Log.d("ProfiledWebView", + "kicking off test with callback registration, and tile discard..."); registerPageSwapCallback(); discardAllTextures(); invalidate(); - + mIsScrolling = true; mContentInvalMillis = System.currentTimeMillis(); } }.start(); + } else { + mIsTesting = true; + tileProfilingStart(); } } @@ -102,13 +150,35 @@ public class ProfiledWebView extends WebView { */ @Override protected void pageSwapCallback(boolean startAnim) { - mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis; - super.pageSwapCallback(startAnim); - Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis - + "millis"); - mIsTesting = true; - invalidate(); // ensure a redraw so that auto-scrolling can occur - tileProfilingStart(); + if (!mIsTesting && mIsScrolling) { + // kick off testing + mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis; + super.pageSwapCallback(startAnim); + Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis"); + mIsTesting = true; + invalidate(); // ensure a redraw so that auto-scrolling can occur + tileProfilingStart(); + } + } + + private double animFramerate() { + WebSettings settings = getSettings(); + String updatesString = settings.getProperty("tree_updates"); + int updates = (updatesString == null) ? -1 : Integer.parseInt(updatesString); + + double animationTime; + if (mAnimationTime == 0) { + animationTime = System.currentTimeMillis() - mLoadTime; + } else { + animationTime = mAnimationTime - mLoadTime; + } + + return updates * 1000 / animationTime; + } + + public void setDoubleBuffering(boolean useDoubleBuffering) { + WebSettings settings = getSettings(); + settings.setProperty("use_double_buffering", useDoubleBuffering ? "true" : "false"); } /* @@ -127,11 +197,12 @@ public class ProfiledWebView extends WebView { // record the time spent (before scrolling) rendering the page data.singleStats.put(getResources().getString(R.string.render_millis), (double)mContentInvalMillis); - // record if the page render timed out - Log.d("ProfiledWebView", "hadtobeforced = " + mHadToBeForced); - data.singleStats.put(getResources().getString(R.string.render_stalls), - mHadToBeForced ? 1.0 : 0.0); - mHadToBeForced = false; + + // record framerate + double framerate = animFramerate(); + Log.d(LOGTAG, "anim framerate was "+framerate); + data.singleStats.put(getResources().getString(R.string.animation_framerate), + framerate); for (int frame = 0; frame < data.frames.length; frame++) { data.frames[frame] = new TileData[ @@ -159,6 +230,8 @@ public class ProfiledWebView extends WebView { @Override public void loadUrl(String url) { + mAnimationTime = 0; + mLoadTime = 0; if (!url.startsWith("http://") && !url.startsWith("file://")) { url = "http://" + url; } |