Bug: 125027187 Bug: 125026102 Bug: 125026678 Bug: 125026234 Bug: 125027586 Bug: 125026476 Bug: 125026103 Bug: 125026237 Bug: 125027248 Bug: 125026475 Bug: 125027487 Test: RenderNodeTests Change-Id: Ic63ea7a3cfe359a7dff0c1b46e534e499f7e928b
1158 lines
44 KiB
Java
1158 lines
44 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.FrameMetricsObserver;
|
|
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 com.android.internal.util.VirtualRefBasePtr;
|
|
|
|
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, 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}.
|
|
*
|
|
* <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 suggested 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(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) {
|
|
if (surface != null && !surface.isValid()) {
|
|
throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
|
|
}
|
|
nSetSurface(mNativeProxy, surface);
|
|
}
|
|
|
|
/**
|
|
* 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 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 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 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. See {@link SyncAndDrawResult}.
|
|
*/
|
|
@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 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
|
|
*/
|
|
public void setStopped(boolean stopped) {
|
|
nSetStopped(mNativeProxy, stopped);
|
|
}
|
|
|
|
/**
|
|
* 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 #setStopped(boolean)}
|
|
*/
|
|
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 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) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
} 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();
|
|
|
|
/**
|
|
* 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, 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 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 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);
|
|
}
|