Home | History | Annotate | Download | only in surfacevalidator
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.view.cts.surfacevalidator;
     17 
     18 import android.content.Context;
     19 import android.graphics.Bitmap;
     20 import android.graphics.Point;
     21 import android.os.Handler;
     22 import android.os.HandlerThread;
     23 import android.os.Trace;
     24 import android.renderscript.Allocation;
     25 import android.renderscript.Element;
     26 import android.renderscript.RenderScript;
     27 import android.renderscript.Type;
     28 import android.util.Log;
     29 import android.util.SparseArray;
     30 import android.view.Surface;
     31 
     32 public class SurfacePixelValidator {
     33     private static final String TAG = "SurfacePixelValidator";
     34 
     35     /**
     36      * Observed that first few frames have errors with SurfaceView placement, so we skip for now.
     37      * b/29603849 tracking that issue.
     38      */
     39     private static final int NUM_FIRST_FRAMES_SKIPPED = 8;
     40 
     41     // If no channel is greater than this value, pixel will be considered 'blackish'.
     42     private static final short PIXEL_CHANNEL_THRESHOLD = 4;
     43 
     44     private static final int MAX_CAPTURED_FAILURES = 5;
     45 
     46     private final int mWidth;
     47     private final int mHeight;
     48 
     49     private final HandlerThread mWorkerThread;
     50     private final Handler mWorkerHandler;
     51 
     52     private final PixelChecker mPixelChecker;
     53 
     54     private final RenderScript mRS;
     55 
     56     private final Allocation mInPixelsAllocation;
     57     private final ScriptC_PixelCounter mScript;
     58 
     59 
     60     private final Object mResultLock = new Object();
     61     private int mResultSuccessFrames;
     62     private int mResultFailureFrames;
     63     private SparseArray<Bitmap> mFirstFailures = new SparseArray<>(MAX_CAPTURED_FAILURES);
     64 
     65     private Runnable mConsumeRunnable = new Runnable() {
     66         int numSkipped = 0;
     67         @Override
     68         public void run() {
     69             Trace.beginSection("consume buffer");
     70             mInPixelsAllocation.ioReceive();
     71             Trace.endSection();
     72 
     73             Trace.beginSection("compare and sum");
     74             int blackishPixelCount = mScript.reduce_countBlackishPixels(mInPixelsAllocation).get();
     75             Trace.endSection();
     76 
     77             boolean success = mPixelChecker.checkPixels(blackishPixelCount, mWidth, mHeight);
     78             synchronized (mResultLock) {
     79                 if (numSkipped < NUM_FIRST_FRAMES_SKIPPED) {
     80                     numSkipped++;
     81                     Log.d(TAG, "skipped frame nr " + numSkipped + ", success = " + success);
     82                 } else {
     83                     if (success) {
     84                         mResultSuccessFrames++;
     85                     } else {
     86                         mResultFailureFrames++;
     87                         int totalFramesSeen = mResultSuccessFrames + mResultFailureFrames;
     88                         Log.d(TAG, "Failure (pixel count = " + blackishPixelCount
     89                                 + ") occurred on frame " + totalFramesSeen);
     90 
     91                         if (mFirstFailures.size() < MAX_CAPTURED_FAILURES) {
     92                             Log.d(TAG, "Capturing bitmap #" + mFirstFailures.size());
     93                             // error, worth looking at...
     94                             Bitmap capture = Bitmap.createBitmap(mWidth, mHeight,
     95                                     Bitmap.Config.ARGB_8888);
     96                             mInPixelsAllocation.copyTo(capture);
     97                             mFirstFailures.put(totalFramesSeen, capture);
     98                         }
     99                     }
    100                 }
    101             }
    102         }
    103     };
    104 
    105     public SurfacePixelValidator(Context context, Point size, PixelChecker pixelChecker) {
    106         mWidth = size.x;
    107         mHeight = size.y;
    108 
    109         mWorkerThread = new HandlerThread("SurfacePixelValidator");
    110         mWorkerThread.start();
    111         mWorkerHandler = new Handler(mWorkerThread.getLooper());
    112 
    113         mPixelChecker = pixelChecker;
    114 
    115         mRS = RenderScript.create(context);
    116         mScript = new ScriptC_PixelCounter(mRS);
    117 
    118         mInPixelsAllocation = createBufferQueueAllocation();
    119         mScript.set_THRESHOLD(PIXEL_CHANNEL_THRESHOLD);
    120 
    121         mInPixelsAllocation.setOnBufferAvailableListener(
    122                 allocation -> mWorkerHandler.post(mConsumeRunnable));
    123     }
    124 
    125     public Surface getSurface() {
    126         return mInPixelsAllocation.getSurface();
    127     }
    128 
    129     private Allocation createBufferQueueAllocation() {
    130         return Allocation.createAllocations(mRS, Type.createXY(mRS,
    131                 Element.RGBA_8888(mRS)
    132                 /*Element.U32(mRS)*/, mWidth, mHeight),
    133                 Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT,
    134                 1)[0];
    135     }
    136 
    137     /**
    138      * Shuts down processing pipeline, and returns current pass/fail counts.
    139      *
    140      * Wait for pipeline to flush before calling this method. If not, frames that are still in
    141      * flight may be lost.
    142      */
    143     public void finish(CapturedActivity.TestResult testResult) {
    144         synchronized (mResultLock) {
    145             // could in theory miss results still processing, but only if latency is extremely high.
    146             // Caller should only call this
    147             testResult.failFrames = mResultFailureFrames;
    148             testResult.passFrames = mResultSuccessFrames;
    149 
    150             for (int i = 0; i < mFirstFailures.size(); i++) {
    151                 testResult.failures.put(mFirstFailures.keyAt(i), mFirstFailures.valueAt(i));
    152             }
    153         }
    154         mWorkerThread.quitSafely();
    155     }
    156 }
    157