diff options
Diffstat (limited to 'graphics/java')
-rw-r--r-- | graphics/java/android/graphics/FrameInfo.java | 128 | ||||
-rw-r--r-- | graphics/java/android/graphics/HardwareRenderer.java | 1030 |
2 files changed, 1158 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java new file mode 100644 index 000000000000..42a5cc43eaa0 --- /dev/null +++ b/graphics/java/android/graphics/FrameInfo.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 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.graphics; + +import android.annotation.LongDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Class that contains all the timing information for the current frame. This + * is used in conjunction with the hardware renderer to provide + * continous-monitoring jank events + * + * All times in nanoseconds from CLOCK_MONOTONIC/System.nanoTime() + * + * To minimize overhead from System.nanoTime() calls we infer durations of + * things by knowing the ordering of the events. For example, to know how + * long layout & measure took it's displayListRecordStart - performTraversalsStart. + * + * These constants must be kept in sync with FrameInfo.h in libhwui and are + * used for indexing into AttachInfo's frameInfo long[], which is intended + * to be quick to pass down to native via JNI, hence a pre-packed format + * + * @hide + */ +public final class FrameInfo { + + public long[] frameInfo = new long[9]; + + // Various flags set to provide extra metadata about the current frame + private static final int FLAGS = 0; + + // Is this the first-draw following a window layout? + public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1; + + // A renderer associated with just a Surface, not with a ViewRootImpl instance. + public static final long FLAG_SURFACE_CANVAS = 1 << 2; + + @LongDef(flag = true, value = { + FLAG_WINDOW_LAYOUT_CHANGED, FLAG_SURFACE_CANVAS }) + @Retention(RetentionPolicy.SOURCE) + public @interface FrameInfoFlags {} + + // The intended vsync time, unadjusted by jitter + private static final int INTENDED_VSYNC = 1; + + // Jitter-adjusted vsync time, this is what was used as input into the + // animation & drawing system + private static final int VSYNC = 2; + + // The time of the oldest input event + private static final int OLDEST_INPUT_EVENT = 3; + + // The time of the newest input event + private static final int NEWEST_INPUT_EVENT = 4; + + // When input event handling started + private static final int HANDLE_INPUT_START = 5; + + // When animation evaluations started + private static final int ANIMATION_START = 6; + + // When ViewRootImpl#performTraversals() started + private static final int PERFORM_TRAVERSALS_START = 7; + + // When View:draw() started + private static final int DRAW_START = 8; + + /** checkstyle */ + public void setVsync(long intendedVsync, long usedVsync) { + frameInfo[INTENDED_VSYNC] = intendedVsync; + frameInfo[VSYNC] = usedVsync; + frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE; + frameInfo[NEWEST_INPUT_EVENT] = 0; + frameInfo[FLAGS] = 0; + } + + /** checkstyle */ + public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) { + if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) { + frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime; + } + if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) { + frameInfo[NEWEST_INPUT_EVENT] = inputEventTime; + } + } + + /** checkstyle */ + public void markInputHandlingStart() { + frameInfo[HANDLE_INPUT_START] = System.nanoTime(); + } + + /** checkstyle */ + public void markAnimationsStart() { + frameInfo[ANIMATION_START] = System.nanoTime(); + } + + /** checkstyle */ + public void markPerformTraversalsStart() { + frameInfo[PERFORM_TRAVERSALS_START] = System.nanoTime(); + } + + /** checkstyle */ + public void markDrawStart() { + frameInfo[DRAW_START] = System.nanoTime(); + } + + /** checkstyle */ + public void addFlags(@FrameInfoFlags long flags) { + frameInfo[FLAGS] |= flags; + } + +} diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java new file mode 100644 index 000000000000..e4020554786b --- /dev/null +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2018 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.graphics; + +import android.annotation.FloatRange; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Activity; +import android.app.ActivityManager; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.view.FrameMetricsObserver; +import android.view.IGraphicsStats; +import android.view.IGraphicsStatsCallback; +import android.view.NativeVectorDrawableAnimator; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.TextureLayer; + +import com.android.internal.util.VirtualRefBasePtr; + +import java.io.File; +import java.io.FileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import sun.misc.Cleaner; + +/** + * <p>Creates an instance of a hardware-accelerated renderer. This is used to render a scene built + * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many + * HardwareRenderer instances as desired.</p> + * + * <h3>Threading</h3> + * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be + * created & used from a single thread. It does not matter what thread is used, however + * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same + * thread, although they can.</p> + * + * <h3>Resources & lifecycle</h3> + * <p>All HardwareRenderer instances share a common render thread. The render thread contains + * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first + * HardwareRenderer created comes with the cost of also creating the associated GPU contexts, + * however each incremental HardwareRenderer thereafter is fairly cheap. The expected usage + * is to have a HardwareRenderer instance for every active {@link Surface}. For example + * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both + * of which may be drawing at the same time.</p> + * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that + * any {@link Surface} used must have a prompt, reliable consuming side. System-provided + * consumers such as {@link android.view.SurfaceView}, + * {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)}, + * or {@link android.view.TextureView} all fit this requirement. However if custom consumers + * are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader} + * it is the app's responsibility to ensure that they consume updates promptly and rapidly. + * Failure to do so will cause the render thread to stall on that surface, blocking all + * HardwareRenderer instances.</p> + * + * @hide + */ +public class HardwareRenderer { + private static final String LOG_TAG = "HardwareRenderer"; + + // Keep in sync with DrawFrameTask.h SYNC_* flags + /** + * Nothing interesting to report. Sync & draw kicked off + */ + public static final int SYNC_OK = 0; + + /** + * The renderer is requesting a redraw. This can occur if there's an animation that's running + * in the RenderNode tree and the hardware renderer is unable to self-animate. + * + * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame + * will be called again on the next vsync signal. + */ + public static final int SYNC_REDRAW_REQUESTED = 1 << 0; + + /** + * The hardware renderer no longer has a valid {@link android.view.Surface} to render to. + * This can happen if {@link Surface#destroy()} was called. The user should no longer + * attempt to call syncAndDrawFrame until a new surface has been provided by calling + * setSurface. + * + * Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + */ + public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; + + /** + * The hardware renderer has been set to a "stopped" state. If this is returned then the + * rendering content has been synced, however a frame was not produced. + */ + public static final int SYNC_CONTEXT_IS_STOPPED = 1 << 2; + + /** + * The content was synced but the renderer has declined to produce a frame in this vsync + * interval. This can happen if a frame was already drawn in this vsync or if the renderer + * is outrunning the frame consumer. The renderer will internally re-schedule itself + * to render a frame in the next vsync signal, so the caller does not need to do anything + * in response to this signal. + */ + public static final int SYNC_FRAME_DROPPED = 1 << 3; + + @IntDef(value = { + SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND, + SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED}) + @Retention(RetentionPolicy.SOURCE) + public @interface SyncAndDrawResult { + } + + /** @hide */ + public static final int FLAG_DUMP_FRAMESTATS = 1 << 0; + /** @hide */ + public static final int FLAG_DUMP_RESET = 1 << 1; + /** @hide */ + public static final int FLAG_DUMP_ALL = FLAG_DUMP_FRAMESTATS; + + /** @hide */ + @IntDef(flag = true, prefix = {"FLAG_DUMP_"}, value = { + FLAG_DUMP_FRAMESTATS, + FLAG_DUMP_RESET + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DumpFlags { + } + + /** + * Name of the file that holds the shaders cache. + */ + private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache"; + private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache"; + + private final long mNativeProxy; + /** @hide */ + protected RenderNode mRootNode; + private boolean mOpaque = true; + private boolean mForceDark = false; + private FrameInfo mScratchInfo; + + /** + * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default + * to opaque with no light source configured. + */ + public HardwareRenderer() { + mRootNode = RenderNode.adopt(nCreateRootRenderNode()); + mRootNode.setClipToBounds(false); + mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode); + if (mNativeProxy == 0) { + throw new OutOfMemoryError("Unable to create hardware renderer"); + } + Cleaner.create(this, new DestroyContextRunnable(mNativeProxy)); + ProcessInitializer.sInstance.init(mNativeProxy); + } + + /** + * Destroys the rendering context of this HardwareRenderer. This destroys the resources + * associated with this renderer and releases the currently set {@link Surface}. + * + * The renderer may be restored from this state by setting a new {@link Surface}, setting + * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming + * rendering with {@link #syncAndDrawFrame(long)}. + * + * It is suggested to call this in response to callbacks such as + * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}. + * + * Note that if there are any outstanding frame commit callbacks they may end up never being + * invoked if the frame was deferred to a later vsync. + */ + public void destroy() { + nDestroy(mNativeProxy, mRootNode.mNativeRenderNode); + } + + /** + * Sets a name for this renderer. This is used to identify this renderer instance + * when reporting debug information such as the per-window frame time metrics + * reported by 'adb shell dumpsys gfxinfo [package] framestats' + * + * @param name The debug name to use for this HardwareRenderer instance + */ + public void setName(String name) { + nSetName(mNativeProxy, name); + } + + /** + * Sets the center of the light source. The light source point controls the directionality + * and shape of shadows rendered by RenderNode Z & elevation. + * + * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set + * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp. + * + * The light source should be setup both as part of initial configuration, and whenever + * the window moves to ensure the light source stays anchored in display space instead + * of in window space. + * + * This must be set at least once along with {@link #setLightSourceAlpha(float, float)} + * before shadows will work. + * + * @param lightX The X position of the light source + * @param lightY The Y position of the light source + * @param lightZ The Z position of the light source. Must be >= 0. + * @param lightRadius The radius of the light source. Smaller radius will have sharper edges, + * larger radius will have softer shadows. + */ + public void setLightSourceGeometry(float lightX, float lightY, float lightZ, + float lightRadius) { + validateFinite(lightX, "lightX"); + validateFinite(lightY, "lightY"); + validatePositive(lightZ, "lightZ"); + validatePositive(lightRadius, "lightRadius"); + nSetLightGeometry(mNativeProxy, lightX, lightY, lightZ, lightRadius); + } + + /** + * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow + * has max alpha, and ramps down from the values provided to zero. + * + * These values are typically provided by the current theme, see + * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}. + * + * This must be set at least once along with + * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work. + * + * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default + * is 0.039f. + * @param spotShadowAlpha The alpha for the spot shadow. If unsure, a reasonable default is + * 0.19f. + */ + public void setLightSourceAlpha(@FloatRange(from = 0.0f, to = 1.0f) float ambientShadowAlpha, + @FloatRange(from = 0.0f, to = 1.0f) float spotShadowAlpha) { + validateAlpha(ambientShadowAlpha, "ambientShadowAlpha"); + validateAlpha(spotShadowAlpha, "spotShadowAlpha"); + nSetLightAlpha(mNativeProxy, ambientShadowAlpha, spotShadowAlpha); + } + + /** + * Sets the content root to render. It is not necessary to call this whenever the content + * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's + * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)} + * is called. + * + * @param content The content to set as the root RenderNode. If null the content root is removed + * and the renderer will draw nothing. + */ + public void setContentRoot(@Nullable RenderNode content) { + RecordingCanvas canvas = mRootNode.startRecording(); + if (content != null) { + canvas.drawRenderNode(content); + } + mRootNode.endRecording(); + } + + /** + * <p>The surface to render into. The surface is assumed to be associated with the display and + * as such is still driven by vsync signals such as those from + * {@link android.view.Choreographer} and that it has a native refresh rate matching that of + * the display's (typically 60hz).</p> + * + * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that + * any {@link Surface} used must have a prompt, reliable consuming side. System-provided + * consumers such as {@link android.view.SurfaceView}, + * {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)}, + * or {@link android.view.TextureView} all fit this requirement. However if custom consumers + * are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader} + * it is the app's responsibility to ensure that they consume updates promptly and rapidly. + * Failure to do so will cause the render thread to stall on that surface, blocking all + * HardwareRenderer instances.</p> + * + * @param surface The surface to render into. If null then rendering will be stopped. If + * non-null then {@link Surface#isValid()} must be true. + */ + public void setSurface(@Nullable Surface surface) { + if (surface != null && !surface.isValid()) { + throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false."); + } + nSetSurface(mNativeProxy, surface); + } + + /** + * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. + * + * @hide + */ + @SyncAndDrawResult + public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { + return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); + } + + /** + * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. + * + * @param vsyncTime The vsync timestamp for this frame. Typically this comes from + * {@link android.view.Choreographer.FrameCallback}. Must be set and be valid + * as the renderer uses this time internally to drive animations. + * @return The result of the sync operation. See {@link SyncAndDrawResult}. + */ + @SyncAndDrawResult + public int syncAndDrawFrame(long vsyncTime) { + if (mScratchInfo == null) { + mScratchInfo = new FrameInfo(); + } + mScratchInfo.setVsync(vsyncTime, vsyncTime); + mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS); + return syncAndDrawFrame(mScratchInfo); + } + + /** + * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. + * frameCommitCallback callback will be invoked when the current rendering content has been + * rendered into a frame and submitted to the swap chain. + * + * @param vsyncTime The vsync timestamp for this frame. Typically this comes from + * {@link android.view.Choreographer.FrameCallback}. Must be set and + * be valid as the renderer uses this time internally to drive + * animations. + * @param frameCommitCallback The callback to invoke when the frame content has been drawn. + * Will be invoked on the current {@link android.os.Looper} thread. + * @return The result of the sync operation. See {@link SyncAndDrawResult}. + */ + @SyncAndDrawResult + public int syncAndDrawFrame(long vsyncTime, + @Nullable Runnable frameCommitCallback) { + if (frameCommitCallback != null) { + setFrameCompleteCallback(frameNr -> frameCommitCallback.run()); + } + return syncAndDrawFrame(vsyncTime); + } + + /** + * Suspends any current rendering into the surface but do not do any destruction. This + * is useful to temporarily suspend using the active Surface in order to do any Surface + * mutations necessary. + * + * Any subsequent draws will override the pause, resuming normal operation. + * + * @return true if there was an outstanding render request, false otherwise. If this is true + * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest + * possible time to resume normal operation. + * + * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old + * Surface before getting a new one. However things like SurfaceView will ensure that + * the old surface remains un-destroyed until after a new frame has been produced with + * the new surface. + * @hide + */ + public boolean pause() { + return nPause(mNativeProxy); + } + + /** + * Hard stops rendering into the surface. If the renderer is stopped it will + * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still + * sync over the latest rendering content, however they will not render and instead + * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned. + * + * If false is passed then rendering will resume as normal. Any pending rendering requests + * will produce a new frame at the next vsync signal. + * + * This is useful in combination with lifecycle events such as {@link Activity#onStop()} + * and {@link Activity#onStart()}. + * + * @param stopped true to stop all rendering, false to resume + */ + public void setStopped(boolean stopped) { + nSetStopped(mNativeProxy, stopped); + } + + /** + * Destroys all hardware rendering resources associated with the current rendering content. + * This includes releasing a reference to the current content root RenderNode. It will + * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume + * rendering after calling this. + * + * It is recommended, but not necessary, to use this in combination with lifecycle events + * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to + * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as + * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} + * + * See also {@link #setStopped(boolean)} + */ + public void destroyHardwareResources() { + nDestroyHardwareResources(mNativeProxy); + } + + /** + * Whether or not the force-dark feature should be used for this renderer. + */ + public boolean setForceDark(boolean enable) { + if (mForceDark != enable) { + mForceDark = enable; + nSetForceDark(mNativeProxy, enable); + return true; + } + return false; + } + + /** + * Allocate buffers ahead of time to avoid allocation delays during rendering. + * + * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the + * memory usage of Surfaces that render rarely or never hit triple buffering. However + * for UI it can result in a slight bit of jank on first launch. This hint will + * tell the HardwareRenderer that now is a good time to allocate the 3 buffers + * necessary for typical rendering. + * + * Must be called after a {@link Surface} has been set. + */ + public void allocateBuffers() { + nAllocateBuffers(mNativeProxy); + } + + /** + * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will + * be coming soon. This is used to help schedule when RenderThread-driven animations will + * happen as the renderer wants to avoid producing more than one frame per vsync signal. + */ + public void notifyFramePending() { + nNotifyFramePending(mNativeProxy); + } + + /** + * Change the HardwareRenderer's opacity. Will take effect on the next frame produced. + * + * If the renderer is set to opaque it is the app's responsibility to ensure that the + * content renders to every pixel of the Surface, otherwise corruption may result. Note that + * this includes ensuring that the first draw of any given pixel does not attempt to blend + * against the destination. If this is false then the hardware renderer will clear to + * transparent at the start of every frame. + * + * @param opaque true if the content rendered is opaque, false if the renderer should clear + * to transparent before rendering + */ + public void setOpaque(boolean opaque) { + if (mOpaque != opaque) { + mOpaque = opaque; + nSetOpaque(mNativeProxy, mOpaque); + } + } + + /** + * Whether or not the renderer is set to be opaque. See {@link #setOpaque(boolean)} + * + * @return true if the renderer is opaque, false otherwise + */ + public boolean isOpaque() { + return mOpaque; + } + + /** @hide */ + public void setFrameCompleteCallback(FrameCompleteCallback callback) { + nSetFrameCompleteCallback(mNativeProxy, callback); + } + + /** + * TODO: Public API this? + * + * @hide + */ + public void addFrameMetricsObserver(FrameMetricsObserver observer) { + long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer); + observer.mNative = new VirtualRefBasePtr(nativeObserver); + } + + /** + * TODO: Public API this? + * + * @hide + */ + public void removeFrameMetricsObserver(FrameMetricsObserver observer) { + nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get()); + observer.mNative = null; + } + + /** + * Enable/disable wide gamut rendering on this renderer. Whether or not the actual rendering + * will be wide gamut depends on the hardware support for such rendering. + * + * @param wideGamut true if this renderer should render in wide gamut, false if it should + * render in sRGB + * TODO: Figure out color... + * @hide + */ + public void setWideGamut(boolean wideGamut) { + nSetWideGamut(mNativeProxy, wideGamut); + } + + /** + * Blocks until all previously queued work has completed. + * + * TODO: Only used for draw finished listeners, but the FrameCompleteCallback does that + * better + * + * @hide + */ + public void fence() { + nFence(mNativeProxy); + } + + /** @hide */ + public void registerAnimatingRenderNode(RenderNode animator) { + nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); + } + + /** @hide */ + public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) { + nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode, + animator.getAnimatorNativePtr()); + } + + /** + * Prevents any further drawing until {@link #syncAndDrawFrame(long)} 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. + * + * TODO: Can we get webview off of this? + * + * @hide + */ + public void stopDrawing() { + nStopDrawing(mNativeProxy); + } + + /** + * 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 + * @hide + */ + public TextureLayer createTextureLayer() { + long layer = nCreateTextureLayer(mNativeProxy); + return TextureLayer.adoptTextureLayer(this, layer); + } + + /** + * Detaches the layer's surface texture from the GL context and releases + * the texture id + * + * @hide + */ + public void detachSurfaceTexture(long hardwareLayer) { + nDetachSurfaceTexture(mNativeProxy, hardwareLayer); + } + + + /** @hide */ + public void buildLayer(RenderNode node) { + if (node.hasDisplayList()) { + nBuildLayer(mNativeProxy, node.mNativeRenderNode); + } + } + + /** @hide */ + public boolean copyLayerInto(final TextureLayer 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 + * @hide + */ + public void pushLayerUpdate(TextureLayer layer) { + nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); + } + + /** + * Tells the HardwareRenderer that the layer is destroyed. The renderer + * should remove the layer from any update queues. + * + * @hide + */ + public void onLayerDestroyed(TextureLayer layer) { + nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); + } + + /** @hide */ + public void setFrameCallback(FrameDrawingCallback callback) { + nSetFrameCallback(mNativeProxy, callback); + } + + /** + * 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. + * @hide + */ + 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. + * @hide + */ + 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. + * @hide + */ + public void drawRenderNode(RenderNode node) { + nDrawRenderNode(mNativeProxy, node.mNativeRenderNode); + } + + /** + * 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. + * @hide + */ + public boolean loadSystemProperties() { + return nLoadSystemProperties(mNativeProxy); + } + + /** + * @hide + */ + public void dumpProfileInfo(FileDescriptor fd, @DumpFlags int dumpFlags) { + nDumpProfileInfo(mNativeProxy, fd, dumpFlags); + } + + /** + * 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. + * @hide + */ + public void setContentDrawBounds(int left, int top, int right, int bottom) { + nSetContentDrawBounds(mNativeProxy, left, top, right, bottom); + } + + /** + * Interface used to receive callbacks when a frame is being drawn. + * + * @hide + */ + public interface FrameDrawingCallback { + /** + * Invoked during a frame drawing. + * + * @param frame The id of the frame being drawn. + */ + void onFrameDraw(long frame); + } + + /** + * Interface used to be notified when a frame has finished rendering + * + * @hide + */ + public interface FrameCompleteCallback { + /** + * Invoked after a frame draw + * + * @param frameNr The id of the frame that was drawn. + */ + void onFrameComplete(long frameNr); + } + + private static void validateAlpha(float alpha, String argumentName) { + if (!(alpha >= 0.0f && alpha <= 1.0f)) { + throw new IllegalArgumentException(argumentName + " must be a valid alpha, " + + alpha + " is not in the range of 0.0f to 1.0f"); + } + } + + private static void validatePositive(float f, String argumentName) { + if (!(Float.isFinite(f) && f >= 0.0f)) { + throw new IllegalArgumentException(argumentName + + " must be a finite positive, given=" + f); + } + } + + private static void validateFinite(float f, String argumentName) { + if (!Float.isFinite(f)) { + throw new IllegalArgumentException(argumentName + " must be finite, given=" + f); + } + } + + /** @hide */ + public static void invokeFunctor(long functor, boolean waitForCompletion) { + nInvokeFunctor(functor, waitForCompletion); + } + + /** + * b/68769804: For low FPS experiments. + * + * @hide + */ + public static void setFPSDivisor(int divisor) { + nHackySetRTAnimationsEnabled(divisor <= 1); + } + + /** + * Changes the OpenGL context priority if IMG_context_priority extension is available. Must be + * called before any OpenGL context is created. + * + * @param priority The priority to use. Must be one of EGL_CONTEXT_PRIORITY_* values. + * @hide + */ + public static void setContextPriority(int priority) { + nSetContextPriority(priority); + } + + /** + * Sets whether or not high contrast text rendering is enabled. The setting is global + * but only affects content rendered after the change is made. + * + * @hide + */ + public static void setHighContrastText(boolean highContrastText) { + nSetHighContrastText(highContrastText); + } + + /** + * If set RenderThread will avoid doing any IPC using instead a fake vsync & DisplayInfo source + * + * @hide + */ + public static void setIsolatedProcess(boolean isIsolated) { + nSetIsolatedProcess(isIsolated); + } + + /** + * If set extra graphics debugging abilities will be enabled such as dumping skp + * + * @hide + */ + public static void setDebuggingEnabled(boolean enable) { + nSetDebuggingEnabled(enable); + } + + /** @hide */ + 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. + * + * @hide + **/ + public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) { + return nCreateHardwareBitmap(node.mNativeRenderNode, width, height); + } + + /** + * 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} + * @hide + */ + public static void trimMemory(int level) { + nTrimMemory(level); + } + + /** @hide */ + 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); + } + + /** + * 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) { + setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(), + new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath()); + } + + /** @hide */ + public static void setPackageName(String packageName) { + ProcessInitializer.sInstance.setPackageName(packageName); + } + + private static final class DestroyContextRunnable implements Runnable { + private final long mNativeInstance; + + DestroyContextRunnable(long nativeInstance) { + mNativeInstance = nativeInstance; + } + + @Override + public void run() { + nDeleteProxy(mNativeInstance); + } + } + + private static class ProcessInitializer { + static ProcessInitializer sInstance = new ProcessInitializer(); + + private boolean mInitialized = false; + + private String mPackageName; + private IGraphicsStats mGraphicsStatsService; + private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() { + @Override + public void onRotateGraphicsStatsBuffer() throws RemoteException { + rotateBuffer(); + } + }; + + private ProcessInitializer() { + } + + synchronized void setPackageName(String name) { + if (mInitialized) return; + mPackageName = name; + } + + synchronized void init(long renderProxy) { + if (mInitialized) return; + mInitialized = true; + + 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() { + if (mPackageName == null) return; + + 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 { + ParcelFileDescriptor pfd = mGraphicsStatsService + .requestBufferForProcess(mPackageName, mGraphicsStatsCallback); + nSetProcessStatsBuffer(pfd.getFd()); + pfd.close(); + } catch (Throwable t) { + Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); + } + } + } + + /** + * @hide + */ + public static native void disableVsync(); + + /** @hide */ + protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile); + + 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 nSetSurface(long nativeProxy, Surface window); + + private static native boolean nPause(long nativeProxy); + + private static native void nSetStopped(long nativeProxy, boolean stopped); + + private static native void nSetLightGeometry(long nativeProxy, + float lightX, float lightY, float lightZ, float lightRadius); + + private static native void nSetLightAlpha(long nativeProxy, float ambientShadowAlpha, + float spotShadowAlpha); + + 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 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 void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback); + + private static native void nSetFrameCompleteCallback(long nativeProxy, + FrameCompleteCallback callback); + + 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); + + // For temporary experimentation b/66945974 + private static native void nHackySetRTAnimationsEnabled(boolean enabled); + + private static native void nSetDebuggingEnabled(boolean enabled); + + private static native void nSetIsolatedProcess(boolean enabled); + + private static native void nSetContextPriority(int priority); + + private static native void nAllocateBuffers(long nativeProxy); + + private static native void nSetForceDark(long nativeProxy, boolean enabled); +} |