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 android.annotation.IntDef;
     20 import android.annotation.NonNull;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Point;
     25 import android.graphics.Rect;
     26 import android.os.Binder;
     27 import android.os.IBinder;
     28 import android.os.ParcelFileDescriptor;
     29 import android.os.RemoteException;
     30 import android.os.ServiceManager;
     31 import android.os.Trace;
     32 import android.util.Log;
     33 import android.view.Surface.OutOfResourcesException;
     34 import android.view.View.AttachInfo;
     35 
     36 import com.android.internal.R;
     37 
     38 import java.io.FileDescriptor;
     39 import java.io.PrintWriter;
     40 import java.lang.annotation.Retention;
     41 import java.lang.annotation.RetentionPolicy;
     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     private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
     78     private static final int FLAG_DUMP_RESET        = 1 << 1;
     79 
     80     @IntDef(flag = true, value = {
     81             FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
     82     @Retention(RetentionPolicy.SOURCE)
     83     public @interface DumpFlags {}
     84 
     85     // Size of the rendered content.
     86     private int mWidth, mHeight;
     87 
     88     // Actual size of the drawing surface.
     89     private int mSurfaceWidth, mSurfaceHeight;
     90 
     91     // Insets between the drawing surface and rendered content. These are
     92     // applied as translation when updating the root render node.
     93     private int mInsetTop, mInsetLeft;
     94 
     95     // Whether the surface has insets. Used to protect opacity.
     96     private boolean mHasInsets;
     97 
     98     // Light and shadow properties specified by the theme.
     99     private final float mLightY;
    100     private final float mLightZ;
    101     private final float mLightRadius;
    102     private final int mAmbientShadowAlpha;
    103     private final int mSpotShadowAlpha;
    104 
    105     private long mNativeProxy;
    106     private boolean mInitialized = false;
    107     private RenderNode mRootNode;
    108     private Choreographer mChoreographer;
    109     private boolean mRootNodeNeedsUpdate;
    110 
    111     ThreadedRenderer(Context context, boolean translucent) {
    112         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
    113         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
    114         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
    115         mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
    116         mAmbientShadowAlpha =
    117                 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
    118         mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
    119         a.recycle();
    120 
    121         long rootNodePtr = nCreateRootRenderNode();
    122         mRootNode = RenderNode.adopt(rootNodePtr);
    123         mRootNode.setClipToBounds(false);
    124         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
    125 
    126         ProcessInitializer.sInstance.init(context, mNativeProxy);
    127 
    128         loadSystemProperties();
    129     }
    130 
    131     @Override
    132     void destroy() {
    133         mInitialized = false;
    134         updateEnabledState(null);
    135         nDestroy(mNativeProxy);
    136     }
    137 
    138     private void updateEnabledState(Surface surface) {
    139         if (surface == null || !surface.isValid()) {
    140             setEnabled(false);
    141         } else {
    142             setEnabled(mInitialized);
    143         }
    144     }
    145 
    146     @Override
    147     boolean initialize(Surface surface) throws OutOfResourcesException {
    148         mInitialized = true;
    149         updateEnabledState(surface);
    150         boolean status = nInitialize(mNativeProxy, surface);
    151         return status;
    152     }
    153 
    154     @Override
    155     void updateSurface(Surface surface) throws OutOfResourcesException {
    156         updateEnabledState(surface);
    157         nUpdateSurface(mNativeProxy, surface);
    158     }
    159 
    160     @Override
    161     boolean pauseSurface(Surface surface) {
    162         return nPauseSurface(mNativeProxy, surface);
    163     }
    164 
    165     @Override
    166     void destroyHardwareResources(View view) {
    167         destroyResources(view);
    168         nDestroyHardwareResources(mNativeProxy);
    169     }
    170 
    171     private static void destroyResources(View view) {
    172         view.destroyHardwareResources();
    173 
    174         if (view instanceof ViewGroup) {
    175             ViewGroup group = (ViewGroup) view;
    176 
    177             int count = group.getChildCount();
    178             for (int i = 0; i < count; i++) {
    179                 destroyResources(group.getChildAt(i));
    180             }
    181         }
    182     }
    183 
    184     @Override
    185     void invalidate(Surface surface) {
    186         updateSurface(surface);
    187     }
    188 
    189     @Override
    190     void detachSurfaceTexture(long hardwareLayer) {
    191         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
    192     }
    193 
    194     @Override
    195     void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) {
    196         mWidth = width;
    197         mHeight = height;
    198 
    199         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
    200                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
    201             mHasInsets = true;
    202             mInsetLeft = surfaceInsets.left;
    203             mInsetTop = surfaceInsets.top;
    204             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
    205             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
    206 
    207             // If the surface has insets, it can't be opaque.
    208             setOpaque(false);
    209         } else {
    210             mHasInsets = false;
    211             mInsetLeft = 0;
    212             mInsetTop = 0;
    213             mSurfaceWidth = width;
    214             mSurfaceHeight = height;
    215         }
    216 
    217         mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
    218         nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, mLightRadius,
    219                 mAmbientShadowAlpha, mSpotShadowAlpha);
    220 
    221         setLightCenter(attachInfo);
    222     }
    223 
    224     @Override
    225     void setLightCenter(AttachInfo attachInfo) {
    226         // Adjust light position for window offsets.
    227         final Point displaySize = attachInfo.mPoint;
    228         attachInfo.mDisplay.getRealSize(displaySize);
    229         final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft;
    230         final float lightY = mLightY - attachInfo.mWindowTop;
    231 
    232         nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
    233     }
    234 
    235     @Override
    236     void setOpaque(boolean opaque) {
    237         nSetOpaque(mNativeProxy, opaque && !mHasInsets);
    238     }
    239 
    240     @Override
    241     int getWidth() {
    242         return mWidth;
    243     }
    244 
    245     @Override
    246     int getHeight() {
    247         return mHeight;
    248     }
    249 
    250     @Override
    251     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
    252         pw.flush();
    253         int flags = 0;
    254         for (int i = 0; i < args.length; i++) {
    255             switch (args[i]) {
    256                 case "framestats":
    257                     flags |= FLAG_DUMP_FRAMESTATS;
    258                     break;
    259                 case "reset":
    260                     flags |= FLAG_DUMP_RESET;
    261                     break;
    262             }
    263         }
    264         nDumpProfileInfo(mNativeProxy, fd, flags);
    265     }
    266 
    267     @Override
    268     boolean loadSystemProperties() {
    269         boolean changed = nLoadSystemProperties(mNativeProxy);
    270         if (changed) {
    271             invalidateRoot();
    272         }
    273         return changed;
    274     }
    275 
    276     private void updateViewTreeDisplayList(View view) {
    277         view.mPrivateFlags |= View.PFLAG_DRAWN;
    278         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
    279                 == View.PFLAG_INVALIDATED;
    280         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    281         view.updateDisplayListIfDirty();
    282         view.mRecreateDisplayList = false;
    283     }
    284 
    285     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
    286         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
    287         updateViewTreeDisplayList(view);
    288 
    289         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
    290             DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
    291             try {
    292                 final int saveCount = canvas.save();
    293                 canvas.translate(mInsetLeft, mInsetTop);
    294                 callbacks.onHardwarePreDraw(canvas);
    295 
    296                 canvas.insertReorderBarrier();
    297                 canvas.drawRenderNode(view.updateDisplayListIfDirty());
    298                 canvas.insertInorderBarrier();
    299 
    300                 callbacks.onHardwarePostDraw(canvas);
    301                 canvas.restoreToCount(saveCount);
    302                 mRootNodeNeedsUpdate = false;
    303             } finally {
    304                 mRootNode.end(canvas);
    305             }
    306         }
    307         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    308     }
    309 
    310     @Override
    311     void invalidateRoot() {
    312         mRootNodeNeedsUpdate = true;
    313     }
    314 
    315     @Override
    316     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
    317         attachInfo.mIgnoreDirtyState = true;
    318 
    319         final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
    320         choreographer.mFrameInfo.markDrawStart();
    321 
    322         updateRootDisplayList(view, callbacks);
    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         final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
    341         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
    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);
    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         nSetName(mNativeProxy, name);
    388     }
    389 
    390     @Override
    391     void fence() {
    392         nFence(mNativeProxy);
    393     }
    394 
    395     @Override
    396     void stopDrawing() {
    397         nStopDrawing(mNativeProxy);
    398     }
    399 
    400     @Override
    401     public void notifyFramePending() {
    402         nNotifyFramePending(mNativeProxy);
    403     }
    404 
    405     @Override
    406     void registerAnimatingRenderNode(RenderNode animator) {
    407         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
    408     }
    409 
    410     @Override
    411     protected void finalize() throws Throwable {
    412         try {
    413             nDeleteProxy(mNativeProxy);
    414             mNativeProxy = 0;
    415         } finally {
    416             super.finalize();
    417         }
    418     }
    419 
    420     static void trimMemory(int level) {
    421         nTrimMemory(level);
    422     }
    423 
    424     public static void overrideProperty(@NonNull String name, @NonNull String value) {
    425         if (name == null || value == null) {
    426             throw new IllegalArgumentException("name and value must be non-null");
    427         }
    428         nOverrideProperty(name, value);
    429     }
    430 
    431     public static void dumpProfileData(byte[] data, FileDescriptor fd) {
    432         nDumpProfileData(data, fd);
    433     }
    434 
    435     private static class ProcessInitializer {
    436         static ProcessInitializer sInstance = new ProcessInitializer();
    437         private static IBinder sProcToken;
    438 
    439         private boolean mInitialized = false;
    440 
    441         private ProcessInitializer() {}
    442 
    443         synchronized void init(Context context, long renderProxy) {
    444             if (mInitialized) return;
    445             mInitialized = true;
    446             initGraphicsStats(context, renderProxy);
    447             initAssetAtlas(context, renderProxy);
    448         }
    449 
    450         private static void initGraphicsStats(Context context, long renderProxy) {
    451             try {
    452                 IBinder binder = ServiceManager.getService("graphicsstats");
    453                 if (binder == null) return;
    454                 IGraphicsStats graphicsStatsService = IGraphicsStats.Stub
    455                         .asInterface(binder);
    456                 sProcToken = new Binder();
    457                 final String pkg = context.getApplicationInfo().packageName;
    458                 ParcelFileDescriptor pfd = graphicsStatsService.
    459                         requestBufferForProcess(pkg, sProcToken);
    460                 nSetProcessStatsBuffer(renderProxy, pfd.getFd());
    461                 pfd.close();
    462             } catch (Throwable t) {
    463                 Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
    464             }
    465         }
    466 
    467         private static void initAssetAtlas(Context context, long renderProxy) {
    468             IBinder binder = ServiceManager.getService("assetatlas");
    469             if (binder == null) return;
    470 
    471             IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
    472             try {
    473                 if (atlas.isCompatible(android.os.Process.myPpid())) {
    474                     GraphicBuffer buffer = atlas.getBuffer();
    475                     if (buffer != null) {
    476                         long[] map = atlas.getMap();
    477                         if (map != null) {
    478                             nSetAtlas(renderProxy, buffer, map);
    479                         }
    480                         // If IAssetAtlas is not the same class as the IBinder
    481                         // we are using a remote service and we can safely
    482                         // destroy the graphic buffer
    483                         if (atlas.getClass() != binder.getClass()) {
    484                             buffer.destroy();
    485                         }
    486                     }
    487                 }
    488             } catch (RemoteException e) {
    489                 Log.w(LOG_TAG, "Could not acquire atlas", e);
    490             }
    491         }
    492     }
    493 
    494     static native void setupShadersDiskCache(String cacheFile);
    495 
    496     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
    497     private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
    498 
    499     private static native long nCreateRootRenderNode();
    500     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
    501     private static native void nDeleteProxy(long nativeProxy);
    502 
    503     private static native boolean nLoadSystemProperties(long nativeProxy);
    504     private static native void nSetName(long nativeProxy, String name);
    505 
    506     private static native boolean nInitialize(long nativeProxy, Surface window);
    507     private static native void nUpdateSurface(long nativeProxy, Surface window);
    508     private static native boolean nPauseSurface(long nativeProxy, Surface window);
    509     private static native void nSetup(long nativeProxy, int width, int height,
    510             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
    511     private static native void nSetLightCenter(long nativeProxy,
    512             float lightX, float lightY, float lightZ);
    513     private static native void nSetOpaque(long nativeProxy, boolean opaque);
    514     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
    515     private static native void nDestroy(long nativeProxy);
    516     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
    517 
    518     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
    519 
    520     private static native long nCreateTextureLayer(long nativeProxy);
    521     private static native void nBuildLayer(long nativeProxy, long node);
    522     private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
    523     private static native void nPushLayerUpdate(long nativeProxy, long layer);
    524     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
    525     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
    526 
    527     private static native void nDestroyHardwareResources(long nativeProxy);
    528     private static native void nTrimMemory(int level);
    529     private static native void nOverrideProperty(String name, String value);
    530 
    531     private static native void nFence(long nativeProxy);
    532     private static native void nStopDrawing(long nativeProxy);
    533     private static native void nNotifyFramePending(long nativeProxy);
    534 
    535     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
    536             @DumpFlags int dumpFlags);
    537     private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
    538 }
    539