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