/tests/SurfaceComposition/src/android/surfacecomposition/CustomSurfaceView.java
Java | 243 lines | 175 code | 21 blank | 47 comment | 27 complexity | a377d876fef15b83c7260536e4ba0345 MD5 | raw file
- /*
- * Copyright (C) 2015 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 android.surfacecomposition;
- import java.util.Random;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.view.Surface;
- import android.view.SurfaceHolder;
- import android.view.SurfaceView;
- /**
- * This provides functionality to measure Surface update frame rate. The idea is to
- * constantly invalidates Surface in a separate thread. Lowest possible way is to
- * use SurfaceView which works with Surface. This gives a very small overhead
- * and very close to Android internals. Note, that lockCanvas is blocking
- * methods and it returns once SurfaceFlinger consumes previous buffer. This
- * gives the change to measure real performance of Surface compositor.
- */
- public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
- private final static long DURATION_TO_WARMUP_MS = 50;
- private final static long DURATION_TO_MEASURE_ROUGH_MS = 500;
- private final static long DURATION_TO_MEASURE_PRECISE_MS = 3000;
- private final static Random mRandom = new Random();
- private final Object mSurfaceLock = new Object();
- private Surface mSurface;
- private boolean mDrawNameOnReady = true;
- private boolean mSurfaceWasChanged = false;
- private String mName;
- private Canvas mCanvas;
- class ValidateThread extends Thread {
- private double mFPS = 0.0f;
- // Used to support early exit and prevent long computation.
- private double mBadFPS;
- private double mPerfectFPS;
- ValidateThread(double badFPS, double perfectFPS) {
- mBadFPS = badFPS;
- mPerfectFPS = perfectFPS;
- }
- public void run() {
- long startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < DURATION_TO_WARMUP_MS) {
- invalidateSurface(false);
- }
- startTime = System.currentTimeMillis();
- long endTime;
- int frameCnt = 0;
- while (true) {
- invalidateSurface(false);
- endTime = System.currentTimeMillis();
- ++frameCnt;
- mFPS = (double)frameCnt * 1000.0 / (endTime - startTime);
- if ((endTime - startTime) >= DURATION_TO_MEASURE_ROUGH_MS) {
- // Test if result looks too bad or perfect and stop early.
- if (mFPS <= mBadFPS || mFPS >= mPerfectFPS) {
- break;
- }
- }
- if ((endTime - startTime) >= DURATION_TO_MEASURE_PRECISE_MS) {
- break;
- }
- }
- }
- public double getFPS() {
- return mFPS;
- }
- }
- public CustomSurfaceView(Context context, String name) {
- super(context);
- mName = name;
- getHolder().addCallback(this);
- }
- public void setMode(int pixelFormat, boolean drawNameOnReady) {
- mDrawNameOnReady = drawNameOnReady;
- getHolder().setFormat(pixelFormat);
- }
- public void acquireCanvas() {
- synchronized (mSurfaceLock) {
- if (mCanvas != null) {
- throw new RuntimeException("Surface canvas was already acquired.");
- }
- if (mSurface != null) {
- mCanvas = mSurface.lockCanvas(null);
- }
- }
- }
- public void releaseCanvas() {
- synchronized (mSurfaceLock) {
- if (mCanvas != null) {
- if (mSurface == null) {
- throw new RuntimeException(
- "Surface was destroyed but canvas was not released.");
- }
- mSurface.unlockCanvasAndPost(mCanvas);
- mCanvas = null;
- }
- }
- }
- /**
- * Invalidate surface.
- */
- private void invalidateSurface(boolean drawSurfaceId) {
- synchronized (mSurfaceLock) {
- if (mSurface != null) {
- Canvas canvas = mSurface.lockCanvas(null);
- // Draw surface name for debug purpose only. This does not affect the test
- // because it is drawn only during allocation.
- if (drawSurfaceId) {
- int textSize = canvas.getHeight() / 24;
- Paint paint = new Paint();
- paint.setTextSize(textSize);
- int textWidth = (int)(paint.measureText(mName) + 0.5f);
- int x = mRandom.nextInt(canvas.getWidth() - textWidth);
- int y = textSize + mRandom.nextInt(canvas.getHeight() - textSize);
- // Create effect of fog to visually control correctness of composition.
- paint.setColor(0xFFFF8040);
- canvas.drawARGB(32, 255, 255, 255);
- canvas.drawText(mName, x, y, paint);
- }
- mSurface.unlockCanvasAndPost(canvas);
- }
- }
- }
- /**
- * Wait until surface is created and ready to use or return immediately if surface
- * already exists.
- */
- public void waitForSurfaceReady() {
- synchronized (mSurfaceLock) {
- if (mSurface == null) {
- try {
- mSurfaceLock.wait(5000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- if (mSurface == null)
- throw new RuntimeException("Surface is not ready.");
- mSurfaceWasChanged = false;
- }
- }
- /**
- * Wait until surface is destroyed or return immediately if surface does not exist.
- */
- public void waitForSurfaceDestroyed() {
- synchronized (mSurfaceLock) {
- if (mSurface != null) {
- try {
- mSurfaceLock.wait(5000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- if (mSurface != null)
- throw new RuntimeException("Surface still exists.");
- mSurfaceWasChanged = false;
- }
- }
- /**
- * Validate that surface has not been changed since waitForSurfaceReady or
- * waitForSurfaceDestroyed.
- */
- public void validateSurfaceNotChanged() {
- synchronized (mSurfaceLock) {
- if (mSurfaceWasChanged) {
- throw new RuntimeException("Surface was changed during the test execution.");
- }
- }
- }
- public double measureFPS(double badFPS, double perfectFPS) {
- try {
- ValidateThread validateThread = new ValidateThread(badFPS, perfectFPS);
- validateThread.start();
- validateThread.join();
- return validateThread.getFPS();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- synchronized (mSurfaceLock) {
- mSurfaceWasChanged = true;
- }
- }
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- // This method is always called at least once, after surfaceCreated.
- synchronized (mSurfaceLock) {
- mSurface = holder.getSurface();
- // We only need to invalidate the surface for the compositor performance test so that
- // it gets included in the composition process. For allocation performance we
- // don't need to invalidate surface and this allows us to remove non-necessary
- // surface invalidation from the test.
- if (mDrawNameOnReady) {
- invalidateSurface(true);
- }
- mSurfaceWasChanged = true;
- mSurfaceLock.notify();
- }
- }
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- synchronized (mSurfaceLock) {
- mSurface = null;
- mSurfaceWasChanged = true;
- mSurfaceLock.notify();
- }
- }
- }