Files
frameworks_base/graphics/java/android/graphics/HardwareRenderer.java
Derek Sollenberger 1863d94e9a Ensure SkiaPipeline always has a valid colorspace.
Previously we didn't assign a colorspace to the pipeline until it
was provided a surface to render into.  This resulted in undefined
behavior if the application attempted to render an offscreen layer
before the OS provided the main window with its surface. Now instead
of deferring setting whether or not the application is wide gamut we
do initialize it to a default setting when the pipeline is created.

Bug: 148042673
Test: apct/device_boot_health_check_extra_postsubmit
Change-Id: I84d743511e949ac977486470bb14eec936de7f88
2020-02-06 07:41:50 -05:00

1193 lines
45 KiB
Java

/*
* 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.util.TimeUtils;
import android.view.IGraphicsStats;
import android.view.IGraphicsStatsCallback;
import android.view.NativeVectorDrawableAnimator;
import android.view.PixelCopy;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureLayer;
import android.view.animation.AnimationUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
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>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>
*/
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.
*
* <p>If this is returned from syncAndDraw the expectation is that syncAndDraw
* 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#release()} was called. The user should no longer
* attempt to call syncAndDraw until a new surface has been provided by calling
* setSurface.
*
* <p>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;
/** @hide */
@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 boolean mIsWideGamut = false;
/**
* 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, mIsWideGamut, 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}. This must
* be called when this HardwareRenderer is no longer needed.
*
* <p>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 by issuing a new {@link FrameRenderRequest}.
*
* <p>It is recommended to call this in response to callbacks such as
* {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}.
*
* <p>Note that if there are any outstanding frame commit callbacks they may 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(@NonNull 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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>These values are typically provided by the current theme, see
* {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
*
* <p>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 a new {@link FrameRenderRequest}
* is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}.
*
* @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.beginRecording();
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) {
setSurface(surface, false);
}
/**
* See {@link #setSurface(Surface)}
*
* @hide
* @param discardBuffer determines whether the surface will attempt to preserve its contents
* between frames. If set to true the renderer will attempt to preserve
* the contents of the buffer between frames if the implementation allows
* it. If set to false no attempt will be made to preserve the buffer's
* contents between frames.
*/
public void setSurface(@Nullable Surface surface, boolean discardBuffer) {
if (surface != null && !surface.isValid()) {
throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
}
nSetSurface(mNativeProxy, surface, discardBuffer);
}
/**
* Sets the parameters that can be used to control a render request for a
* {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
* than a single frame request.
*/
public final class FrameRenderRequest {
private FrameInfo mFrameInfo = new FrameInfo();
private boolean mWaitForPresent;
private FrameRenderRequest() { }
private void reset() {
mWaitForPresent = false;
// Default to the animation time which, if choreographer is in play, will default to the
// current vsync time. Otherwise it will be 'now'.
mRenderRequest.setVsyncTime(
AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS);
}
/** @hide */
public void setFrameInfo(FrameInfo info) {
System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length);
}
/**
* Sets the vsync time that represents the start point of this frame. Typically this
* comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time
* sources include {@link System#nanoTime()}, however if the result is being displayed
* on-screen then using {@link android.view.Choreographer} is strongly recommended to
* ensure smooth animations.
*
* <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven
* directly by RenderThread will not be synchronized properly with the current frame.
*
* @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds
* and should come from a CLOCK_MONOTONIC source.
*
* @return this instance
*/
public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
mFrameInfo.setVsync(vsyncTime, vsyncTime);
mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
return this;
}
/**
* Adds a frame commit callback. This callback will be invoked when the current rendering
* content has been rendered into a frame and submitted to the swap chain. The frame may
* not currently be visible on the display when this is invoked, but it has been submitted.
* This callback is useful in combination with {@link PixelCopy} to capture the current
* rendered content of the UI reliably.
*
* @param executor The executor to run the callback on. It is strongly recommended that
* this executor post to a different thread, as the calling thread is
* highly sensitive to being blocked.
* @param frameCommitCallback The callback to invoke when the frame content has been drawn.
* Will be invoked on the given {@link Executor}.
*
* @return this instance
*/
public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
@NonNull Runnable frameCommitCallback) {
setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
return this;
}
/**
* Sets whether or not {@link #syncAndDraw()} should block until the frame has been
* presented. If this is true and {@link #syncAndDraw()} does not return
* {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned
* the frame has been submitted to the {@link Surface}. The default and typically
* recommended value is false, as blocking for present will prevent pipelining from
* happening, reducing overall throughput. This is useful for situations such as
* {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired
* to block until a frame has been presented to ensure first-frame consistency with
* other Surfaces.
*
* @param shouldWait If true the next call to {@link #syncAndDraw()} will block until
* completion.
* @return this instance
*/
public @NonNull FrameRenderRequest setWaitForPresent(boolean shouldWait) {
mWaitForPresent = shouldWait;
return this;
}
/**
* Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This
* {@link FrameRenderRequest} instance should no longer be used after calling this method.
* The system internally may reuse instances of {@link FrameRenderRequest} to reduce
* allocation churn.
*
* @return The result of the sync operation.
*/
@SyncAndDrawResult
public int syncAndDraw() {
int syncResult = syncAndDrawFrame(mFrameInfo);
if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) {
fence();
}
return syncResult;
}
}
private FrameRenderRequest mRenderRequest = new FrameRenderRequest();
/**
* Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used
* to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with
* the RenderThread and then renders a single frame to the Surface set with
* {@link #setSurface(Surface)}.
*
* @return An instance of {@link FrameRenderRequest}. The instance may be reused for every
* frame, so the caller should not hold onto it for longer than a single render request.
*/
public @NonNull FrameRenderRequest createRenderRequest() {
mRenderRequest.reset();
return mRenderRequest;
}
/**
* 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);
}
/**
* 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.
*
* <p>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 #createRenderRequest()}
* and {@link FrameRenderRequest#syncAndDraw()} 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 FrameRenderRequest#syncAndDraw()} will
* still sync over the latest rendering content, however they will not render and instead
* {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
*
* <p>If false is passed then rendering will resume as normal. Any pending rendering requests
* will produce a new frame at the next vsync signal.
*
* <p>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
* @hide
*/
public void setStopped(boolean stopped) {
nSetStopped(mNativeProxy, stopped);
}
/**
* Hard stops rendering into the surface. If the renderer is stopped it will
* block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
* still sync over the latest rendering content, however they will not render and instead
* {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
*
* <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}.
* See {@link #start()} for resuming rendering.
*/
public void stop() {
nSetStopped(mNativeProxy, true);
}
/**
* Resumes rendering into the surface. Any pending rendering requests
* will produce a new frame at the next vsync signal.
*
* <p>This is useful in combination with lifecycle events such as {@link Activity#onStart()}.
* See {@link #stop()} for stopping rendering.
*/
public void start() {
nSetStopped(mNativeProxy, false);
}
/**
* Destroys all the display lists 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, along with re-recording the display lists for the
* RenderNode tree.
*
* <p>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 #stop()}.
*/
public void clearContent() {
nDestroyHardwareResources(mNativeProxy);
}
/**
* Whether or not the force-dark feature should be used for this renderer.
* @hide
*/
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.
*
* <p>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.
*
* <p>Must be called after a {@link Surface} has been set.
*
* TODO: Figure out if we even need/want this. Should HWUI just be doing this in response
* to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public
* @hide
*/
public void allocateBuffers() {
nAllocateBuffers(mNativeProxy);
}
/**
* Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} 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.
*
* <p>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 addObserver(HardwareRendererObserver observer) {
nAddObserver(mNativeProxy, observer.getNativeInstance());
}
/**
* TODO: Public API this?
*
* @hide
*/
public void removeObserver(HardwareRendererObserver observer) {
nRemoveObserver(mNativeProxy, observer.getNativeInstance());
}
/**
* 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) {
mIsWideGamut = 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 FrameRenderRequest#syncAndDraw()} 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.getNativeInstance());
}
/**
* 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);
}
/** @hide */
public void setPictureCaptureCallback(@Nullable PictureCapturedCallback callback) {
nSetPictureCaptureCallback(mNativeProxy, callback);
}
/** @hide */
public boolean isWideGamut() {
return mIsWideGamut;
}
/** called by native */
static void invokePictureCapturedCallback(long picturePtr, PictureCapturedCallback callback) {
Picture picture = new Picture(picturePtr);
callback.onPictureCaptured(picture);
}
/**
* 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);
}
/**
* Interface for listening to picture captures
* @hide
*/
public interface PictureCapturedCallback {
/** @hide */
void onPictureCaptured(Picture picture);
}
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.getNativeInstance());
} else {
return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
srcRect.right, srcRect.bottom, bitmap.getNativeInstance());
}
}
/**
* 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();
/**
* Start render thread and initialize EGL or Vulkan.
*
* Initializing EGL involves loading and initializing the graphics driver. Some drivers take
* several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
* its first frame adds directly to user-visible app launch latency.
*
* Should only be called after GraphicsEnvironment.chooseDriver().
* @hide
*/
public static native void preload();
/** @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, boolean isWideGamut,
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, boolean discardBuffer);
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, long bitmapHandle);
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 nSetPictureCaptureCallback(long nativeProxy,
PictureCapturedCallback callback);
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
private static native void nSetFrameCompleteCallback(long nativeProxy,
FrameCompleteCallback callback);
private static native void nAddObserver(long nativeProxy, long nativeObserver);
private static native void nRemoveObserver(long nativeProxy, long nativeObserver);
private static native int nCopySurfaceInto(Surface surface,
int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
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);
}