Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2013 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 
     17 package android.view;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.content.res.TypedArray;
     24 import android.graphics.Bitmap;
     25 import android.graphics.Rect;
     26 import android.graphics.drawable.Drawable;
     27 import android.os.IBinder;
     28 import android.os.RemoteException;
     29 import android.os.ServiceManager;
     30 import android.os.SystemProperties;
     31 import android.os.Trace;
     32 import android.util.Log;
     33 import android.util.LongSparseArray;
     34 import android.util.TimeUtils;
     35 import android.view.Surface.OutOfResourcesException;
     36 import android.view.View.AttachInfo;
     37 
     38 import java.io.FileDescriptor;
     39 import java.io.PrintWriter;
     40 import java.util.ArrayList;
     41 import java.util.HashSet;
     42 
     43 /**
     44  * Hardware renderer that proxies the rendering to a render thread. Most calls
     45  * are currently synchronous.
     46  *
     47  * The UI thread can block on the RenderThread, but RenderThread must never
     48  * block on the UI thread.
     49  *
     50  * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
     51  * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
     52  * by the lifecycle of the RenderProxy.
     53  *
     54  * Note that although currently the EGL context & surfaces are created & managed
     55  * by the render thread, the goal is to move that into a shared structure that can
     56  * be managed by both threads. EGLSurface creation & deletion should ideally be
     57  * done on the UI thread and not the RenderThread to avoid stalling the
     58  * RenderThread with surface buffer allocation.
     59  *
     60  * @hide
     61  */
     62 public class ThreadedRenderer extends HardwareRenderer {
     63     private static final String LOGTAG = "ThreadedRenderer";
     64 
     65     // Keep in sync with DrawFrameTask.h SYNC_* flags
     66     // Nothing interesting to report
     67     private static final int SYNC_OK = 0;
     68     // Needs a ViewRoot invalidate
     69     private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0;
     70     // Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
     71     private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
     72 
     73     private static final String[] VISUALIZERS = {
     74         PROFILE_PROPERTY_VISUALIZE_BARS,
     75     };
     76 
     77     // Size of the rendered content.
     78     private int mWidth, mHeight;
     79 
     80     // Actual size of the drawing surface.
     81     private int mSurfaceWidth, mSurfaceHeight;
     82 
     83     // Insets between the drawing surface and rendered content. These are
     84     // applied as translation when updating the root render node.
     85     private int mInsetTop, mInsetLeft;
     86 
     87     // Whether the surface has insets. Used to protect opacity.
     88     private boolean mHasInsets;
     89 
     90     // Light and shadow properties specified by the theme.
     91     private final float mLightY;
     92     private final float mLightZ;
     93     private final float mLightRadius;
     94     private final int mAmbientShadowAlpha;
     95     private final int mSpotShadowAlpha;
     96 
     97     private long mNativeProxy;
     98     private boolean mInitialized = false;
     99     private RenderNode mRootNode;
    100     private Choreographer mChoreographer;
    101     private boolean mProfilingEnabled;
    102     private boolean mRootNodeNeedsUpdate;
    103 
    104     ThreadedRenderer(Context context, boolean translucent) {
    105         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
    106         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
    107         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
    108         mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
    109         mAmbientShadowAlpha =
    110                 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
    111         mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
    112         a.recycle();
    113 
    114         long rootNodePtr = nCreateRootRenderNode();
    115         mRootNode = RenderNode.adopt(rootNodePtr);
    116         mRootNode.setClipToBounds(false);
    117         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
    118 
    119         AtlasInitializer.sInstance.init(context, mNativeProxy);
    120 
    121         // Setup timing
    122         mChoreographer = Choreographer.getInstance();
    123         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
    124 
    125         loadSystemProperties();
    126     }
    127 
    128     @Override
    129     void destroy() {
    130         mInitialized = false;
    131         updateEnabledState(null);
    132         nDestroy(mNativeProxy);
    133     }
    134 
    135     private void updateEnabledState(Surface surface) {
    136         if (surface == null || !surface.isValid()) {
    137             setEnabled(false);
    138         } else {
    139             setEnabled(mInitialized);
    140         }
    141     }
    142 
    143     @Override
    144     boolean initialize(Surface surface) throws OutOfResourcesException {
    145         mInitialized = true;
    146         updateEnabledState(surface);
    147         boolean status = nInitialize(mNativeProxy, surface);
    148         surface.allocateBuffers();
    149         return status;
    150     }
    151 
    152     @Override
    153     void updateSurface(Surface surface) throws OutOfResourcesException {
    154         updateEnabledState(surface);
    155         nUpdateSurface(mNativeProxy, surface);
    156     }
    157 
    158     @Override
    159     boolean pauseSurface(Surface surface) {
    160         return nPauseSurface(mNativeProxy, surface);
    161     }
    162 
    163     @Override
    164     void destroyHardwareResources(View view) {
    165         destroyResources(view);
    166         nDestroyHardwareResources(mNativeProxy);
    167     }
    168 
    169     private static void destroyResources(View view) {
    170         view.destroyHardwareResources();
    171 
    172         if (view instanceof ViewGroup) {
    173             ViewGroup group = (ViewGroup) view;
    174 
    175             int count = group.getChildCount();
    176             for (int i = 0; i < count; i++) {
    177                 destroyResources(group.getChildAt(i));
    178             }
    179         }
    180     }
    181 
    182     @Override
    183     void invalidate(Surface surface) {
    184         updateSurface(surface);
    185     }
    186 
    187     @Override
    188     void detachSurfaceTexture(long hardwareLayer) {
    189         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
    190     }
    191 
    192     @Override
    193     void setup(int width, int height, Rect surfaceInsets) {
    194         final float lightX = width / 2.0f;
    195         mWidth = width;
    196         mHeight = height;
    197         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
    198                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
    199             mHasInsets = true;
    200             mInsetLeft = surfaceInsets.left;
    201             mInsetTop = surfaceInsets.top;
    202             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
    203             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
    204 
    205             // If the surface has insets, it can't be opaque.
    206             setOpaque(false);
    207         } else {
    208             mHasInsets = false;
    209             mInsetLeft = 0;
    210             mInsetTop = 0;
    211             mSurfaceWidth = width;
    212             mSurfaceHeight = height;
    213         }
    214         mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
    215         nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
    216                 lightX, mLightY, mLightZ, mLightRadius,
    217                 mAmbientShadowAlpha, mSpotShadowAlpha);
    218     }
    219 
    220     @Override
    221     void setOpaque(boolean opaque) {
    222         nSetOpaque(mNativeProxy, opaque && !mHasInsets);
    223     }
    224 
    225     @Override
    226     int getWidth() {
    227         return mWidth;
    228     }
    229 
    230     @Override
    231     int getHeight() {
    232         return mHeight;
    233     }
    234 
    235     @Override
    236     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
    237         pw.flush();
    238         nDumpProfileInfo(mNativeProxy, fd);
    239     }
    240 
    241     private static int search(String[] values, String value) {
    242         for (int i = 0; i < values.length; i++) {
    243             if (values[i].equals(value)) return i;
    244         }
    245         return -1;
    246     }
    247 
    248     private static boolean checkIfProfilingRequested() {
    249         String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY);
    250         int graphType = search(VISUALIZERS, profiling);
    251         return (graphType >= 0) || Boolean.parseBoolean(profiling);
    252     }
    253 
    254     @Override
    255     boolean loadSystemProperties() {
    256         boolean changed = nLoadSystemProperties(mNativeProxy);
    257         boolean wantProfiling = checkIfProfilingRequested();
    258         if (wantProfiling != mProfilingEnabled) {
    259             mProfilingEnabled = wantProfiling;
    260             changed = true;
    261         }
    262         if (changed) {
    263             invalidateRoot();
    264         }
    265         return changed;
    266     }
    267 
    268     private void updateViewTreeDisplayList(View view) {
    269         view.mPrivateFlags |= View.PFLAG_DRAWN;
    270         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
    271                 == View.PFLAG_INVALIDATED;
    272         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    273         view.getDisplayList();
    274         view.mRecreateDisplayList = false;
    275     }
    276 
    277     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
    278         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
    279         updateViewTreeDisplayList(view);
    280 
    281         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
    282             HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
    283             try {
    284                 final int saveCount = canvas.save();
    285                 canvas.translate(mInsetLeft, mInsetTop);
    286                 callbacks.onHardwarePreDraw(canvas);
    287 
    288                 canvas.insertReorderBarrier();
    289                 canvas.drawRenderNode(view.getDisplayList());
    290                 canvas.insertInorderBarrier();
    291 
    292                 callbacks.onHardwarePostDraw(canvas);
    293                 canvas.restoreToCount(saveCount);
    294                 mRootNodeNeedsUpdate = false;
    295             } finally {
    296                 mRootNode.end(canvas);
    297             }
    298         }
    299         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    300     }
    301 
    302     @Override
    303     void invalidateRoot() {
    304         mRootNodeNeedsUpdate = true;
    305     }
    306 
    307     @Override
    308     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
    309         attachInfo.mIgnoreDirtyState = true;
    310         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
    311         attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
    312 
    313         long recordDuration = 0;
    314         if (mProfilingEnabled) {
    315             recordDuration = System.nanoTime();
    316         }
    317 
    318         updateRootDisplayList(view, callbacks);
    319 
    320         if (mProfilingEnabled) {
    321             recordDuration = System.nanoTime() - recordDuration;
    322         }
    323 
    324         attachInfo.mIgnoreDirtyState = false;
    325 
    326         // register animating rendernodes which started animating prior to renderer
    327         // creation, which is typical for animators started prior to first draw
    328         if (attachInfo.mPendingAnimatingRenderNodes != null) {
    329             final int count = attachInfo.mPendingAnimatingRenderNodes.size();
    330             for (int i = 0; i < count; i++) {
    331                 registerAnimatingRenderNode(
    332                         attachInfo.mPendingAnimatingRenderNodes.get(i));
    333             }
    334             attachInfo.mPendingAnimatingRenderNodes.clear();
    335             // We don't need this anymore as subsequent calls to
    336             // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
    337             attachInfo.mPendingAnimatingRenderNodes = null;
    338         }
    339 
    340         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
    341                 recordDuration, view.getResources().getDisplayMetrics().density);
    342         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
    343             setEnabled(false);
    344             attachInfo.mViewRootImpl.mSurface.release();
    345             // Invalidate since we failed to draw. This should fetch a Surface
    346             // if it is still needed or do nothing if we are no longer drawing
    347             attachInfo.mViewRootImpl.invalidate();
    348         }
    349         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
    350             attachInfo.mViewRootImpl.invalidate();
    351         }
    352     }
    353 
    354     static void invokeFunctor(long functor, boolean waitForCompletion) {
    355         nInvokeFunctor(functor, waitForCompletion);
    356     }
    357 
    358     @Override
    359     HardwareLayer createTextureLayer() {
    360         long layer = nCreateTextureLayer(mNativeProxy);
    361         return HardwareLayer.adoptTextureLayer(this, layer);
    362     }
    363 
    364     @Override
    365     void buildLayer(RenderNode node) {
    366         nBuildLayer(mNativeProxy, node.getNativeDisplayList());
    367     }
    368 
    369     @Override
    370     boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
    371         return nCopyLayerInto(mNativeProxy,
    372                 layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap);
    373     }
    374 
    375     @Override
    376     void pushLayerUpdate(HardwareLayer layer) {
    377         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
    378     }
    379 
    380     @Override
    381     void onLayerDestroyed(HardwareLayer layer) {
    382         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
    383     }
    384 
    385     @Override
    386     void setName(String name) {
    387     }
    388 
    389     @Override
    390     void fence() {
    391         nFence(mNativeProxy);
    392     }
    393 
    394     @Override
    395     void stopDrawing() {
    396         nStopDrawing(mNativeProxy);
    397     }
    398 
    399     @Override
    400     public void notifyFramePending() {
    401         nNotifyFramePending(mNativeProxy);
    402     }
    403 
    404     @Override
    405     void registerAnimatingRenderNode(RenderNode animator) {
    406         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
    407     }
    408 
    409     @Override
    410     protected void finalize() throws Throwable {
    411         try {
    412             nDeleteProxy(mNativeProxy);
    413             mNativeProxy = 0;
    414         } finally {
    415             super.finalize();
    416         }
    417     }
    418 
    419     static void trimMemory(int level) {
    420         nTrimMemory(level);
    421     }
    422 
    423     private static class AtlasInitializer {
    424         static AtlasInitializer sInstance = new AtlasInitializer();
    425 
    426         private boolean mInitialized = false;
    427 
    428         private AtlasInitializer() {}
    429 
    430         synchronized void init(Context context, long renderProxy) {
    431             if (mInitialized) return;
    432             IBinder binder = ServiceManager.getService("assetatlas");
    433             if (binder == null) return;
    434 
    435             IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
    436             try {
    437                 if (atlas.isCompatible(android.os.Process.myPpid())) {
    438                     GraphicBuffer buffer = atlas.getBuffer();
    439                     if (buffer != null) {
    440                         long[] map = atlas.getMap();
    441                         if (map != null) {
    442                             // TODO Remove after fixing b/15425820
    443                             validateMap(context, map);
    444                             nSetAtlas(renderProxy, buffer, map);
    445                             mInitialized = true;
    446                         }
    447                         // If IAssetAtlas is not the same class as the IBinder
    448                         // we are using a remote service and we can safely
    449                         // destroy the graphic buffer
    450                         if (atlas.getClass() != binder.getClass()) {
    451                             buffer.destroy();
    452                         }
    453                     }
    454                 }
    455             } catch (RemoteException e) {
    456                 Log.w(LOG_TAG, "Could not acquire atlas", e);
    457             }
    458         }
    459 
    460         private static void validateMap(Context context, long[] map) {
    461             Log.d("Atlas", "Validating map...");
    462             HashSet<Long> preloadedPointers = new HashSet<Long>();
    463 
    464             // We only care about drawables that hold bitmaps
    465             final Resources resources = context.getResources();
    466             final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
    467 
    468             final int count = drawables.size();
    469             ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
    470             for (int i = 0; i < count; i++) {
    471                 drawables.valueAt(i).addAtlasableBitmaps(tmpList);
    472                 for (int j = 0; j < tmpList.size(); j++) {
    473                     preloadedPointers.add(tmpList.get(j).mNativeBitmap);
    474                 }
    475                 tmpList.clear();
    476             }
    477 
    478             for (int i = 0; i < map.length; i += 4) {
    479                 if (!preloadedPointers.contains(map[i])) {
    480                     Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
    481                     map[i] = 0;
    482                 }
    483             }
    484         }
    485     }
    486 
    487     static native void setupShadersDiskCache(String cacheFile);
    488 
    489     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
    490 
    491     private static native long nCreateRootRenderNode();
    492     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
    493     private static native void nDeleteProxy(long nativeProxy);
    494 
    495     private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
    496     private static native boolean nLoadSystemProperties(long nativeProxy);
    497 
    498     private static native boolean nInitialize(long nativeProxy, Surface window);
    499     private static native void nUpdateSurface(long nativeProxy, Surface window);
    500     private static native boolean nPauseSurface(long nativeProxy, Surface window);
    501     private static native void nSetup(long nativeProxy, int width, int height,
    502             float lightX, float lightY, float lightZ, float lightRadius,
    503             int ambientShadowAlpha, int spotShadowAlpha);
    504     private static native void nSetOpaque(long nativeProxy, boolean opaque);
    505     private static native int nSyncAndDrawFrame(long nativeProxy,
    506             long frameTimeNanos, long recordDuration, float density);
    507     private static native void nDestroy(long nativeProxy);
    508     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
    509 
    510     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
    511 
    512     private static native long nCreateTextureLayer(long nativeProxy);
    513     private static native void nBuildLayer(long nativeProxy, long node);
    514     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
    515     private static native void nPushLayerUpdate(long nativeProxy, long layer);
    516     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
    517     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
    518 
    519     private static native void nDestroyHardwareResources(long nativeProxy);
    520     private static native void nTrimMemory(int level);
    521 
    522     private static native void nFence(long nativeProxy);
    523     private static native void nStopDrawing(long nativeProxy);
    524     private static native void nNotifyFramePending(long nativeProxy);
    525 
    526     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
    527 }
    528