diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
commit | 10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch) | |
tree | 8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/view/ThreadedRenderer.java | |
parent | 677516fb6b6f207d373984757d3d9450474b6b00 (diff) | |
download | android-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz |
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \
--bid 4335822 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4335822.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/view/ThreadedRenderer.java')
-rw-r--r-- | android/view/ThreadedRenderer.java | 1075 |
1 files changed, 1075 insertions, 0 deletions
diff --git a/android/view/ThreadedRenderer.java b/android/view/ThreadedRenderer.java new file mode 100644 index 00000000..2166f6e4 --- /dev/null +++ b/android/view/ThreadedRenderer.java @@ -0,0 +1,1075 @@ +/* + * Copyright (C) 2013 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.view; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemProperties; +import android.os.Trace; +import android.util.Log; +import android.view.Surface.OutOfResourcesException; +import android.view.View.AttachInfo; + +import com.android.internal.R; +import com.android.internal.util.VirtualRefBasePtr; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Threaded renderer that proxies the rendering to a render thread. Most calls + * are currently synchronous. + * + * The UI thread can block on the RenderThread, but RenderThread must never + * block on the UI thread. + * + * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates + * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed + * by the lifecycle of the RenderProxy. + * + * Note that although currently the EGL context & surfaces are created & managed + * by the render thread, the goal is to move that into a shared structure that can + * be managed by both threads. EGLSurface creation & deletion should ideally be + * done on the UI thread and not the RenderThread to avoid stalling the + * RenderThread with surface buffer allocation. + * + * @hide + */ +public final class ThreadedRenderer { + private static final String LOG_TAG = "ThreadedRenderer"; + + /** + * Name of the file that holds the shaders cache. + */ + private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache"; + + /** + * System property used to enable or disable threaded rendering profiling. + * The default value of this property is assumed to be false. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "true", to enable profiling + * "visual_bars", to enable profiling and visualize the results on screen + * "false", to disable profiling + * + * @see #PROFILE_PROPERTY_VISUALIZE_BARS + * + * @hide + */ + public static final String PROFILE_PROPERTY = "debug.hwui.profile"; + + /** + * Value for {@link #PROFILE_PROPERTY}. When the property is set to this + * value, profiling data will be visualized on screen as a bar chart. + * + * @hide + */ + public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars"; + + /** + * System property used to specify the number of frames to be used + * when doing threaded rendering profiling. + * The default value of this property is #PROFILE_MAX_FRAMES. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "60", to set the limit of frames to 60 + */ + static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes"; + + /** + * System property used to debug EGL configuration choice. + * + * Possible values: + * "choice", print the chosen configuration only + * "all", print all possible configurations + */ + static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config"; + + /** + * Turn on to draw dirty regions every other frame. + * + * Possible values: + * "true", to enable dirty regions debugging + * "false", to disable dirty regions debugging + * + * @hide + */ + public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions"; + + /** + * Turn on to flash hardware layers when they update. + * + * Possible values: + * "true", to enable hardware layers updates debugging + * "false", to disable hardware layers updates debugging + * + * @hide + */ + public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY = + "debug.hwui.show_layers_updates"; + + /** + * Controls overdraw debugging. + * + * Possible values: + * "false", to disable overdraw debugging + * "show", to show overdraw areas on screen + * "count", to display an overdraw counter + * + * @hide + */ + public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw"; + + /** + * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this + * value, overdraw will be shown on screen by coloring pixels. + * + * @hide + */ + public static final String OVERDRAW_PROPERTY_SHOW = "show"; + + /** + * Defines the rendering pipeline to be used by the ThreadedRenderer. + * + * Possible values: + * "opengl", will use the existing OpenGL renderer + * "skiagl", will use Skia's OpenGL renderer + * "skiavk", will use Skia's Vulkan renderer + * + * @hide + */ + public static final String DEBUG_RENDERER_PROPERTY = "debug.hwui.renderer"; + + /** + * Turn on to debug non-rectangular clip operations. + * + * Possible values: + * "hide", to disable this debug mode + * "highlight", highlight drawing commands tested against a non-rectangular clip + * "stencil", renders the clip region on screen when set + * + * @hide + */ + public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY = + "debug.hwui.show_non_rect_clip"; + + static { + // Try to check OpenGL support early if possible. + isAvailable(); + } + + /** + * A process can set this flag to false to prevent the use of threaded + * rendering. + * + * @hide + */ + public static boolean sRendererDisabled = false; + + /** + * Further threaded renderer disabling for the system process. + * + * @hide + */ + public static boolean sSystemRendererDisabled = false; + + /** + * Invoke this method to disable threaded rendering in the current process. + * + * @hide + */ + public static void disable(boolean system) { + sRendererDisabled = true; + if (system) { + sSystemRendererDisabled = true; + } + } + + public static boolean sTrimForeground = false; + + /** + * Controls whether or not the renderer should aggressively trim + * memory. Note that this must not be set for any process that uses + * WebView! This should be only used by system_process or similar + * that do not go into the background. + */ + public static void enableForegroundTrimming() { + sTrimForeground = true; + } + + private static Boolean sSupportsOpenGL; + + /** + * Indicates whether threaded rendering is available under any form for + * the view hierarchy. + * + * @return True if the view hierarchy can potentially be defer rendered, + * false otherwise + */ + public static boolean isAvailable() { + if (sSupportsOpenGL != null) { + return sSupportsOpenGL.booleanValue(); + } + if (SystemProperties.getInt("ro.kernel.qemu", 0) == 0) { + // Device is not an emulator. + sSupportsOpenGL = true; + return true; + } + int qemu_gles = SystemProperties.getInt("qemu.gles", -1); + if (qemu_gles == -1) { + // In this case, the value of the qemu.gles property is not ready + // because the SurfaceFlinger service may not start at this point. + return false; + } + // In the emulator this property will be set > 0 when OpenGL ES 2.0 is + // enabled, 0 otherwise. On old emulator versions it will be undefined. + sSupportsOpenGL = qemu_gles > 0; + return sSupportsOpenGL.booleanValue(); + } + + /** + * Sets the directory to use as a persistent storage for threaded rendering + * resources. + * + * @param cacheDir A directory the current process can write to + * + * @hide + */ + public static void setupDiskCache(File cacheDir) { + ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); + } + + /** + * Creates a threaded renderer using OpenGL. + * + * @param translucent True if the surface is translucent, false otherwise + * + * @return A threaded renderer backed by OpenGL. + */ + public static ThreadedRenderer create(Context context, boolean translucent, String name) { + ThreadedRenderer renderer = null; + if (isAvailable()) { + renderer = new ThreadedRenderer(context, translucent, name); + } + return renderer; + } + + /** + * Invoke this method when the system is running out of memory. This + * method will attempt to recover as much memory as possible, based on + * the specified hint. + * + * @param level Hint about the amount of memory that should be trimmed, + * see {@link android.content.ComponentCallbacks} + */ + public static void trimMemory(int level) { + nTrimMemory(level); + } + + public static void overrideProperty(@NonNull String name, @NonNull String value) { + if (name == null || value == null) { + throw new IllegalArgumentException("name and value must be non-null"); + } + nOverrideProperty(name, value); + } + + // Keep in sync with DrawFrameTask.h SYNC_* flags + // Nothing interesting to report + private static final int SYNC_OK = 0; + // Needs a ViewRoot invalidate + private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0; + // Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; + // setStopped is true, drawing is false + // TODO: Remove this and SYNC_LOST_SURFACE_REWARD_IF_FOUND? + // This flag isn't really used as there's nothing that we care to do + // in response, so it really just exists to differentiate from LOST_SURFACE + // but possibly both can just be deleted. + private static final int SYNC_CONTEXT_IS_STOPPED = 1 << 2; + + private static final String[] VISUALIZERS = { + PROFILE_PROPERTY_VISUALIZE_BARS, + }; + + private static final int FLAG_DUMP_FRAMESTATS = 1 << 0; + private static final int FLAG_DUMP_RESET = 1 << 1; + + @IntDef(flag = true, value = { + FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET }) + @Retention(RetentionPolicy.SOURCE) + public @interface DumpFlags {} + + // Size of the rendered content. + private int mWidth, mHeight; + + // Actual size of the drawing surface. + private int mSurfaceWidth, mSurfaceHeight; + + // Insets between the drawing surface and rendered content. These are + // applied as translation when updating the root render node. + private int mInsetTop, mInsetLeft; + + // Whether the surface has insets. Used to protect opacity. + private boolean mHasInsets; + + // Light and shadow properties specified by the theme. + private final float mLightY; + private final float mLightZ; + private final float mLightRadius; + private final int mAmbientShadowAlpha; + private final int mSpotShadowAlpha; + + private long mNativeProxy; + private boolean mInitialized = false; + private RenderNode mRootNode; + private boolean mRootNodeNeedsUpdate; + + private boolean mEnabled; + private boolean mRequested = true; + private boolean mIsOpaque = false; + + ThreadedRenderer(Context context, boolean translucent, String name) { + final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); + mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); + mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); + mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0); + mAmbientShadowAlpha = + (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f); + mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f); + a.recycle(); + + long rootNodePtr = nCreateRootRenderNode(); + mRootNode = RenderNode.adopt(rootNodePtr); + mRootNode.setClipToBounds(false); + mIsOpaque = !translucent; + mNativeProxy = nCreateProxy(translucent, rootNodePtr); + nSetName(mNativeProxy, name); + + ProcessInitializer.sInstance.init(context, mNativeProxy); + + loadSystemProperties(); + } + + /** + * Destroys the threaded rendering context. + */ + void destroy() { + mInitialized = false; + updateEnabledState(null); + nDestroy(mNativeProxy, mRootNode.mNativeRenderNode); + } + + /** + * Indicates whether threaded rendering is currently enabled. + * + * @return True if threaded rendering is in use, false otherwise. + */ + boolean isEnabled() { + return mEnabled; + } + + /** + * Indicates whether threaded rendering is currently enabled. + * + * @param enabled True if the threaded renderer is in use, false otherwise. + */ + void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + /** + * Indicates whether threaded rendering is currently request but not + * necessarily enabled yet. + * + * @return True if requested, false otherwise. + */ + boolean isRequested() { + return mRequested; + } + + /** + * Indicates whether threaded rendering is currently requested but not + * necessarily enabled yet. + */ + void setRequested(boolean requested) { + mRequested = requested; + } + + private void updateEnabledState(Surface surface) { + if (surface == null || !surface.isValid()) { + setEnabled(false); + } else { + setEnabled(mInitialized); + } + } + + /** + * Initializes the threaded renderer for the specified surface. + * + * @param surface The surface to render + * + * @return True if the initialization was successful, false otherwise. + */ + boolean initialize(Surface surface) throws OutOfResourcesException { + boolean status = !mInitialized; + mInitialized = true; + updateEnabledState(surface); + nInitialize(mNativeProxy, surface); + return status; + } + + /** + * Initializes the threaded renderer for the specified surface and setup the + * renderer for drawing, if needed. This is invoked when the ViewAncestor has + * potentially lost the threaded renderer. The threaded renderer should be + * reinitialized and setup when the render {@link #isRequested()} and + * {@link #isEnabled()}. + * + * @param width The width of the drawing surface. + * @param height The height of the drawing surface. + * @param attachInfo Information about the window. + * @param surface The surface to render + * @param surfaceInsets The drawing surface insets to apply + * + * @return true if the surface was initialized, false otherwise. Returning + * false might mean that the surface was already initialized. + */ + boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, + Surface surface, Rect surfaceInsets) throws OutOfResourcesException { + if (isRequested()) { + // We lost the gl context, so recreate it. + if (!isEnabled()) { + if (initialize(surface)) { + setup(width, height, attachInfo, surfaceInsets); + return true; + } + } + } + return false; + } + + /** + * Updates the threaded renderer for the specified surface. + * + * @param surface The surface to render + */ + void updateSurface(Surface surface) throws OutOfResourcesException { + updateEnabledState(surface); + nUpdateSurface(mNativeProxy, surface); + } + + /** + * Halts any current rendering into the surface. Use this if it is unclear whether + * or not the surface used by the ThreadedRenderer will be changing. It + * Suspends any rendering into the surface, but will not do any destruction. + * + * Any subsequent draws will override the pause, resuming normal operation. + */ + boolean pauseSurface(Surface surface) { + return nPauseSurface(mNativeProxy, surface); + } + + /** + * Hard stops or resumes rendering into the surface. This flag is used to + * determine whether or not it is safe to use the given surface *at all* + */ + void setStopped(boolean stopped) { + nSetStopped(mNativeProxy, stopped); + } + + /** + * Destroys all hardware rendering resources associated with the specified + * view hierarchy. + * + * @param view The root of the view hierarchy + */ + void destroyHardwareResources(View view) { + destroyResources(view); + nDestroyHardwareResources(mNativeProxy); + } + + private static void destroyResources(View view) { + view.destroyHardwareResources(); + } + + /** + * Detaches the layer's surface texture from the GL context and releases + * the texture id + */ + void detachSurfaceTexture(long hardwareLayer) { + nDetachSurfaceTexture(mNativeProxy, hardwareLayer); + } + + /** + * Sets up the renderer for drawing. + * + * @param width The width of the drawing surface. + * @param height The height of the drawing surface. + * @param attachInfo Information about the window. + * @param surfaceInsets The drawing surface insets to apply + */ + void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) { + mWidth = width; + mHeight = height; + + if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 + || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { + mHasInsets = true; + mInsetLeft = surfaceInsets.left; + mInsetTop = surfaceInsets.top; + mSurfaceWidth = width + mInsetLeft + surfaceInsets.right; + mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom; + + // If the surface has insets, it can't be opaque. + setOpaque(false); + } else { + mHasInsets = false; + mInsetLeft = 0; + mInsetTop = 0; + mSurfaceWidth = width; + mSurfaceHeight = height; + } + + mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight); + nSetup(mNativeProxy, mLightRadius, + mAmbientShadowAlpha, mSpotShadowAlpha); + + setLightCenter(attachInfo); + } + + /** + * Updates the light position based on the position of the window. + * + * @param attachInfo Information about the window. + */ + void setLightCenter(AttachInfo attachInfo) { + // Adjust light position for window offsets. + final Point displaySize = attachInfo.mPoint; + attachInfo.mDisplay.getRealSize(displaySize); + final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft; + final float lightY = mLightY - attachInfo.mWindowTop; + + nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ); + } + + /** + * Change the ThreadedRenderer's opacity + */ + void setOpaque(boolean opaque) { + mIsOpaque = opaque && !mHasInsets; + nSetOpaque(mNativeProxy, mIsOpaque); + } + + boolean isOpaque() { + return mIsOpaque; + } + + /** + * Enable/disable wide gamut rendering on this renderer. + */ + void setWideGamut(boolean wideGamut) { + nSetWideGamut(mNativeProxy, wideGamut); + } + + /** + * Gets the current width of the surface. This is the width that the surface + * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}. + * + * @return the current width of the surface + */ + int getWidth() { + return mWidth; + } + + /** + * Gets the current height of the surface. This is the height that the surface + * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}. + * + * @return the current width of the surface + */ + int getHeight() { + return mHeight; + } + + /** + * Outputs extra debugging information in the specified file descriptor. + */ + void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) { + pw.flush(); + int flags = 0; + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "framestats": + flags |= FLAG_DUMP_FRAMESTATS; + break; + case "reset": + flags |= FLAG_DUMP_RESET; + break; + } + } + nDumpProfileInfo(mNativeProxy, fd, flags); + } + + /** + * Loads system properties used by the renderer. This method is invoked + * whenever system properties are modified. Implementations can use this + * to trigger live updates of the renderer based on properties. + * + * @return True if a property has changed. + */ + boolean loadSystemProperties() { + boolean changed = nLoadSystemProperties(mNativeProxy); + if (changed) { + invalidateRoot(); + } + return changed; + } + + private void updateViewTreeDisplayList(View view) { + view.mPrivateFlags |= View.PFLAG_DRAWN; + view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) + == View.PFLAG_INVALIDATED; + view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; + view.updateDisplayListIfDirty(); + view.mRecreateDisplayList = false; + } + + private void updateRootDisplayList(View view, DrawCallbacks callbacks) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()"); + updateViewTreeDisplayList(view); + + if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { + DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); + try { + final int saveCount = canvas.save(); + canvas.translate(mInsetLeft, mInsetTop); + callbacks.onPreDraw(canvas); + + canvas.insertReorderBarrier(); + canvas.drawRenderNode(view.updateDisplayListIfDirty()); + canvas.insertInorderBarrier(); + + callbacks.onPostDraw(canvas); + canvas.restoreToCount(saveCount); + mRootNodeNeedsUpdate = false; + } finally { + mRootNode.end(canvas); + } + } + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + + /** + * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the + * rendernode of the UI thread. + * @param node The node to add. + * @param placeFront If true, the render node will be placed in front of the content node, + * otherwise behind the content node. + */ + public void addRenderNode(RenderNode node, boolean placeFront) { + nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront); + } + + /** + * Only especially added render nodes can be removed. + * @param node The node which was added via addRenderNode which should get removed again. + */ + public void removeRenderNode(RenderNode node) { + nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode); + } + + /** + * Draws a particular render node. If the node is not the content node, only the additional + * nodes will get drawn and the content remains untouched. + * @param node The node to be drawn. + */ + public void drawRenderNode(RenderNode node) { + nDrawRenderNode(mNativeProxy, node.mNativeRenderNode); + } + + /** + * To avoid unnecessary overdrawing of the main content all additionally passed render nodes + * will be prevented to overdraw this area. It will be synchronized with the draw call. + * This should be updated in the content view's draw call. + * @param left The left side of the protected bounds. + * @param top The top side of the protected bounds. + * @param right The right side of the protected bounds. + * @param bottom The bottom side of the protected bounds. + */ + public void setContentDrawBounds(int left, int top, int right, int bottom) { + nSetContentDrawBounds(mNativeProxy, left, top, right, bottom); + } + + /** + * Interface used to receive callbacks whenever a view is drawn by + * a threaded renderer instance. + */ + interface DrawCallbacks { + /** + * Invoked before a view is drawn by a threaded renderer. + * This method can be used to apply transformations to the + * canvas but no drawing command should be issued. + * + * @param canvas The Canvas used to render the view. + */ + void onPreDraw(DisplayListCanvas canvas); + + /** + * Invoked after a view is drawn by a threaded renderer. + * It is safe to invoke drawing commands from this method. + * + * @param canvas The Canvas used to render the view. + */ + void onPostDraw(DisplayListCanvas canvas); + } + + /** + * Indicates that the content drawn by DrawCallbacks needs to + * be updated, which will be done by the next call to draw() + */ + void invalidateRoot() { + mRootNodeNeedsUpdate = true; + } + + /** + * Draws the specified view. + * + * @param view The view to draw. + * @param attachInfo AttachInfo tied to the specified view. + * @param callbacks Callbacks invoked when drawing happens. + */ + void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { + attachInfo.mIgnoreDirtyState = true; + + final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; + choreographer.mFrameInfo.markDrawStart(); + + updateRootDisplayList(view, callbacks); + + attachInfo.mIgnoreDirtyState = false; + + // register animating rendernodes which started animating prior to renderer + // creation, which is typical for animators started prior to first draw + if (attachInfo.mPendingAnimatingRenderNodes != null) { + final int count = attachInfo.mPendingAnimatingRenderNodes.size(); + for (int i = 0; i < count; i++) { + registerAnimatingRenderNode( + attachInfo.mPendingAnimatingRenderNodes.get(i)); + } + attachInfo.mPendingAnimatingRenderNodes.clear(); + // We don't need this anymore as subsequent calls to + // ViewRootImpl#attachRenderNodeAnimator will go directly to us. + attachInfo.mPendingAnimatingRenderNodes = null; + } + + final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo; + int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); + if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { + setEnabled(false); + attachInfo.mViewRootImpl.mSurface.release(); + // Invalidate since we failed to draw. This should fetch a Surface + // if it is still needed or do nothing if we are no longer drawing + attachInfo.mViewRootImpl.invalidate(); + } + if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { + attachInfo.mViewRootImpl.invalidate(); + } + } + + static void invokeFunctor(long functor, boolean waitForCompletion) { + nInvokeFunctor(functor, waitForCompletion); + } + + /** + * Creates a new hardware layer. A hardware layer built by calling this + * method will be treated as a texture layer, instead of as a render target. + * + * @return A hardware layer + */ + HardwareLayer createTextureLayer() { + long layer = nCreateTextureLayer(mNativeProxy); + return HardwareLayer.adoptTextureLayer(this, layer); + } + + + void buildLayer(RenderNode node) { + nBuildLayer(mNativeProxy, node.getNativeDisplayList()); + } + + + boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { + return nCopyLayerInto(mNativeProxy, + layer.getDeferredLayerUpdater(), bitmap); + } + + /** + * Indicates that the specified hardware layer needs to be updated + * as soon as possible. + * + * @param layer The hardware layer that needs an update + */ + void pushLayerUpdate(HardwareLayer layer) { + nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); + } + + /** + * Tells the HardwareRenderer that the layer is destroyed. The renderer + * should remove the layer from any update queues. + */ + void onLayerDestroyed(HardwareLayer layer) { + nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); + } + + /** + * Blocks until all previously queued work has completed. + */ + void fence() { + nFence(mNativeProxy); + } + + /** + * Prevents any further drawing until draw() is called. This is a signal + * that the contents of the RenderNode tree are no longer safe to play back. + * In practice this usually means that there are Functor pointers in the + * display list that are no longer valid. + */ + void stopDrawing() { + nStopDrawing(mNativeProxy); + } + + /** + * Called by {@link ViewRootImpl} when a new performTraverals is scheduled. + */ + public void notifyFramePending() { + nNotifyFramePending(mNativeProxy); + } + + + void registerAnimatingRenderNode(RenderNode animator) { + nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); + } + + void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode, + animator.getAnimatorNativePtr()); + } + + public void serializeDisplayListTree() { + nSerializeDisplayListTree(mNativeProxy); + } + + public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) { + if (srcRect == null) { + // Empty rect means entire surface + return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap); + } else { + return nCopySurfaceInto(surface, srcRect.left, srcRect.top, + srcRect.right, srcRect.bottom, bitmap); + } + } + + /** + * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given + * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and + * not the RenderNode from a View. + **/ + public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) { + return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height); + } + + /** + * Sets whether or not high contrast text rendering is enabled. The setting is global + * but only affects content rendered after the change is made. + */ + public static void setHighContrastText(boolean highContrastText) { + nSetHighContrastText(highContrastText); + } + + @Override + protected void finalize() throws Throwable { + try { + nDeleteProxy(mNativeProxy); + mNativeProxy = 0; + } finally { + super.finalize(); + } + } + + private static class ProcessInitializer { + static ProcessInitializer sInstance = new ProcessInitializer(); + + private boolean mInitialized = false; + + private Context mAppContext; + private IGraphicsStats mGraphicsStatsService; + private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() { + @Override + public void onRotateGraphicsStatsBuffer() throws RemoteException { + rotateBuffer(); + } + }; + + private ProcessInitializer() {} + + synchronized void init(Context context, long renderProxy) { + if (mInitialized) return; + mInitialized = true; + mAppContext = context.getApplicationContext(); + initSched(renderProxy); + initGraphicsStats(); + } + + private void initSched(long renderProxy) { + try { + int tid = nGetRenderThreadTid(renderProxy); + ActivityManager.getService().setRenderThread(tid); + } catch (Throwable t) { + Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t); + } + } + + private void initGraphicsStats() { + try { + IBinder binder = ServiceManager.getService("graphicsstats"); + if (binder == null) return; + mGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder); + requestBuffer(); + } catch (Throwable t) { + Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); + } + } + + private void rotateBuffer() { + nRotateProcessStatsBuffer(); + requestBuffer(); + } + + private void requestBuffer() { + try { + final String pkg = mAppContext.getApplicationInfo().packageName; + ParcelFileDescriptor pfd = mGraphicsStatsService + .requestBufferForProcess(pkg, mGraphicsStatsCallback); + nSetProcessStatsBuffer(pfd.getFd()); + pfd.close(); + } catch (Throwable t) { + Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); + } + } + } + + void addFrameMetricsObserver(FrameMetricsObserver observer) { + long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer); + observer.mNative = new VirtualRefBasePtr(nativeObserver); + } + + void removeFrameMetricsObserver(FrameMetricsObserver observer) { + nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get()); + observer.mNative = null; + } + + /** Not actually public - internal use only. This doc to make lint happy */ + public static native void disableVsync(); + + static native void setupShadersDiskCache(String cacheFile); + + private static native void nRotateProcessStatsBuffer(); + private static native void nSetProcessStatsBuffer(int fd); + private static native int nGetRenderThreadTid(long nativeProxy); + + private static native long nCreateRootRenderNode(); + private static native long nCreateProxy(boolean translucent, long rootRenderNode); + private static native void nDeleteProxy(long nativeProxy); + + private static native boolean nLoadSystemProperties(long nativeProxy); + private static native void nSetName(long nativeProxy, String name); + + private static native void nInitialize(long nativeProxy, Surface window); + private static native void nUpdateSurface(long nativeProxy, Surface window); + private static native boolean nPauseSurface(long nativeProxy, Surface window); + private static native void nSetStopped(long nativeProxy, boolean stopped); + private static native void nSetup(long nativeProxy, + float lightRadius, int ambientShadowAlpha, int spotShadowAlpha); + private static native void nSetLightCenter(long nativeProxy, + float lightX, float lightY, float lightZ); + private static native void nSetOpaque(long nativeProxy, boolean opaque); + private static native void nSetWideGamut(long nativeProxy, boolean wideGamut); + private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); + private static native void nDestroy(long nativeProxy, long rootRenderNode); + private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); + private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator); + + private static native void nInvokeFunctor(long functor, boolean waitForCompletion); + + private static native long nCreateTextureLayer(long nativeProxy); + private static native void nBuildLayer(long nativeProxy, long node); + private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap); + private static native void nPushLayerUpdate(long nativeProxy, long layer); + private static native void nCancelLayerUpdate(long nativeProxy, long layer); + private static native void nDetachSurfaceTexture(long nativeProxy, long layer); + + private static native void nDestroyHardwareResources(long nativeProxy); + private static native void nTrimMemory(int level); + private static native void nOverrideProperty(String name, String value); + + private static native void nFence(long nativeProxy); + private static native void nStopDrawing(long nativeProxy); + private static native void nNotifyFramePending(long nativeProxy); + + private static native void nSerializeDisplayListTree(long nativeProxy); + + private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, + @DumpFlags int dumpFlags); + + private static native void nAddRenderNode(long nativeProxy, long rootRenderNode, + boolean placeFront); + private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode); + private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); + private static native void nSetContentDrawBounds(long nativeProxy, int left, + int top, int right, int bottom); + + private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer); + private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver); + + private static native int nCopySurfaceInto(Surface surface, + int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap); + + private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height); + private static native void nSetHighContrastText(boolean enabled); +} |