Make HardwareRenderer public API

Bug: 123661129
Test: HardwareRenderer CTS tests
Change-Id: Ic7ff69c9489d00e3f525eec761a84d06cf81be7a
This commit is contained in:
John Reck
2019-01-17 17:01:32 -08:00
parent 699905e5c7
commit fe5dfcacfc
10 changed files with 424 additions and 169 deletions

View File

@@ -14099,6 +14099,34 @@ package android.graphics {
ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
}
public class HardwareRenderer {
ctor public HardwareRenderer();
method public void clearContent();
method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
method public void destroy();
method public boolean isOpaque();
method public void notifyFramePending();
method public void setContentRoot(@Nullable android.graphics.RenderNode);
method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setLightSourceGeometry(float, float, float, float);
method public void setName(String);
method public void setOpaque(boolean);
method public void setStopped(boolean);
method public void setSurface(@Nullable android.view.Surface);
field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4
field public static final int SYNC_FRAME_DROPPED = 8; // 0x8
field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2
field public static final int SYNC_OK = 0; // 0x0
field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1
}
public final class HardwareRenderer.FrameRenderRequest {
method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long);
method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean);
method public int syncAndDraw();
}
public final class ImageDecoder implements java.lang.AutoCloseable {
method public void close();
method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int);

View File

@@ -25,6 +25,7 @@ import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.os.Build;
import android.os.Handler;
import android.util.SparseIntArray;
import com.android.internal.util.VirtualRefBasePtr;
@@ -84,6 +85,7 @@ public class RenderNodeAnimator extends Animator {
private VirtualRefBasePtr mNativePtr;
private Handler mHandler;
private RenderNode mTarget;
private View mViewTarget;
private int mRenderProperty = -1;
@@ -222,6 +224,9 @@ public class RenderNodeAnimator extends Animator {
private void moveToRunningState() {
mState = STATE_RUNNING;
if (mNativePtr != null) {
if (mHandler == null) {
mHandler = new Handler();
}
nStart(mNativePtr.get());
}
notifyStartListeners();
@@ -497,7 +502,7 @@ public class RenderNodeAnimator extends Animator {
// Called by native
@UnsupportedAppUsage
private static void callOnFinished(RenderNodeAnimator animator) {
animator.onFinished();
animator.mHandler.post(animator::onFinished);
}
@Override

View File

@@ -453,7 +453,7 @@ public final class ThreadedRenderer extends HardwareRenderer {
*/
void destroyHardwareResources(View view) {
destroyResources(view);
destroyHardwareResources();
clearContent();
}
private static void destroyResources(View view) {
@@ -735,7 +735,9 @@ public final class ThreadedRenderer extends HardwareRenderer {
if (callback != null) {
setFrameCallback(callback);
}
syncAndDrawFrame(vsync);
createRenderRequest()
.setVsyncTime(vsync)
.syncAndDraw();
}
}
}

View File

@@ -3404,21 +3404,25 @@ public final class ViewRootImpl implements ViewParent,
.captureFrameCommitCallbacks();
if (mReportNextDraw) {
usingAsyncReport = true;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
// TODO: Use the frame number
pendingDrawFinished();
if (commitCallbacks != null) {
for (int i = 0; i < commitCallbacks.size(); i++) {
commitCallbacks.get(i).run();
}
}
});
final Handler handler = mAttachInfo.mHandler;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
handler.post(() -> {
// TODO: Use the frame number
pendingDrawFinished();
if (commitCallbacks != null) {
for (int i = 0; i < commitCallbacks.size(); i++) {
commitCallbacks.get(i).run();
}
}
}));
} else if (commitCallbacks != null && commitCallbacks.size() > 0) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
for (int i = 0; i < commitCallbacks.size(); i++) {
commitCallbacks.get(i).run();
}
});
final Handler handler = mAttachInfo.mHandler;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
handler.post(() -> {
for (int i = 0; i < commitCallbacks.size(); i++) {
commitCallbacks.get(i).run();
}
}));
}
}

View File

@@ -33,7 +33,6 @@
#include <private/EGL/cache.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
@@ -144,52 +143,22 @@ private:
uint32_t mRequestId;
};
class RenderingException : public MessageHandler {
class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
public:
RenderingException(JavaVM* vm, const std::string& message)
: mVm(vm)
, mMessage(message) {
}
virtual void handleMessage(const Message&) {
throwException(mVm, mMessage);
}
static void throwException(JavaVM* vm, const std::string& message) {
JNIEnv* env = getenv(vm);
jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
}
private:
JavaVM* mVm;
std::string mMessage;
};
class FrameCompleteWrapper : public MessageHandler {
public:
FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
mLooper = Looper::getForThread();
LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!");
explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
virtual ~FrameCompleteWrapper() {
~FrameCompleteWrapper() {
releaseObject();
}
void postFrameComplete(int64_t frameNr) {
void onFrameComplete(int64_t frameNr) {
if (mObject) {
mFrameNr = frameNr;
mLooper->sendMessage(this, 0);
}
}
virtual void handleMessage(const Message&) {
if (mObject) {
ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr);
getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr);
ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
releaseObject();
}
}
@@ -197,8 +166,6 @@ public:
private:
JavaVM* mVm;
jobject mObject;
sp<Looper> mLooper;
int64_t mFrameNr = -1;
void releaseObject() {
if (mObject) {
@@ -211,16 +178,14 @@ private:
class RootRenderNode : public RenderNode, ErrorHandler {
public:
explicit RootRenderNode(JNIEnv* env) : RenderNode() {
mLooper = Looper::getForThread();
LOG_ALWAYS_FATAL_IF(!mLooper.get(),
"Must create RootRenderNode on a thread with a looper!");
env->GetJavaVM(&mVm);
}
virtual ~RootRenderNode() {}
virtual void onError(const std::string& message) override {
mLooper->sendMessage(new RenderingException(mVm, message), 0);
JNIEnv* env = getenv(mVm);
jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
}
virtual void prepareTree(TreeInfo& info) override {
@@ -249,14 +214,6 @@ public:
info.errorHandler = nullptr;
}
void sendMessage(const sp<MessageHandler>& handler) {
mLooper->sendMessage(handler, 0);
}
void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
}
void attachAnimatingNode(RenderNode* animatingNode) {
mPendingAnimatingRenderNodes.push_back(animatingNode);
}
@@ -404,7 +361,6 @@ public:
}
private:
sp<Looper> mLooper;
JavaVM* mVm;
std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
@@ -435,7 +391,9 @@ private:
// the onFinished callback will then be ignored.
sp<FinishAndInvokeListener> message
= new FinishAndInvokeListener(anim);
sendMessageDelayed(message, remainingTimeInMs);
auto looper = Looper::getForThread();
LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
anim->clearOneShotListener();
}
}
@@ -463,7 +421,6 @@ public:
virtual void runRemainingAnimations(TreeInfo& info) {
AnimationContext::runRemainingAnimations(info);
mRootNode->runVectorDrawableAnimators(this, info);
postOnFinishedEvents();
}
virtual void pauseAnimators() override {
@@ -471,27 +428,16 @@ public:
}
virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
OnFinishedEvent event(animator, listener);
mOnFinishedEvents.push_back(event);
listener->onAnimationFinished(animator);
}
virtual void destroy() {
AnimationContext::destroy();
mRootNode->detachAnimators();
postOnFinishedEvents();
}
private:
sp<RootRenderNode> mRootNode;
std::vector<OnFinishedEvent> mOnFinishedEvents;
void postOnFinishedEvents() {
if (mOnFinishedEvents.size()) {
sp<InvokeAnimationListeners> message
= new InvokeAnimationListeners(mOnFinishedEvents);
mRootNode->sendMessage(message);
}
}
};
class ContextFactoryImpl : public IContextFactory {
@@ -958,7 +904,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
} else {
sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
wrapper->postFrameComplete(frameNr);
wrapper->onFrameComplete(frameNr);
});
}
}

View File

@@ -20,7 +20,6 @@ import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.os.IBinder;
@@ -28,13 +27,16 @@ 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;
@@ -42,6 +44,7 @@ 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;
@@ -50,13 +53,8 @@ import sun.misc.Cleaner;
* 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,
@@ -64,6 +62,7 @@ import sun.misc.Cleaner;
* 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},
@@ -73,8 +72,6 @@ import sun.misc.Cleaner;
* 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";
@@ -89,18 +86,18 @@ public class HardwareRenderer {
* 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
* <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#destroy()} was called. The user should no longer
* attempt to call syncAndDrawFrame until a new surface has been provided by calling
* 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.
*
* Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
* <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
*/
public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
@@ -119,6 +116,7 @@ public class HardwareRenderer {
*/
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})
@@ -153,7 +151,6 @@ public class HardwareRenderer {
protected RenderNode mRootNode;
private boolean mOpaque = true;
private boolean mForceDark = false;
private FrameInfo mScratchInfo;
private boolean mIsWideGamut = false;
/**
@@ -175,14 +172,14 @@ public class HardwareRenderer {
* 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
* <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 with {@link #syncAndDrawFrame(long)}.
* rendering by issuing a new {@link FrameRenderRequest}.
*
* It is suggested to call this in response to callbacks such as
* <p>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
* <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() {
@@ -204,14 +201,14 @@ public class HardwareRenderer {
* 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
* <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.
*
* The light source should be setup both as part of initial configuration, and whenever
* <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.
*
* This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
* <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
@@ -233,10 +230,10 @@ public class HardwareRenderer {
* 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
* <p>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
* <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
@@ -254,8 +251,8 @@ public class HardwareRenderer {
/**
* 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.
* 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.
@@ -294,6 +291,126 @@ public class HardwareRenderer {
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.
*
@@ -304,55 +421,16 @@ public class HardwareRenderer {
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.
* <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 #syncAndDrawFrame(long)} is called at the soonest
* 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
@@ -367,14 +445,14 @@ public class HardwareRenderer {
/**
* 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
* 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.
*
* If false is passed then rendering will resume as normal. Any pending rendering requests
* <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.
*
* This is useful in combination with lifecycle events such as {@link Activity#onStop()}
* <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
@@ -384,24 +462,26 @@ public class HardwareRenderer {
}
/**
* Destroys all hardware rendering resources associated with the current rendering content.
* 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.
* rendering after calling this, along with re-recording the display lists for the
* RenderNode tree.
*
* It is recommended, but not necessary, to use this in combination with lifecycle events
* <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 destroyHardwareResources() {
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) {
@@ -415,20 +495,24 @@ public class HardwareRenderer {
/**
* 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
* <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.
*
* Must be called after a {@link Surface} has been set.
* <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 #syncAndDrawFrame(long)} will
* 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.
*/
@@ -439,7 +523,7 @@ public class HardwareRenderer {
/**
* 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
* <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
@@ -527,7 +611,7 @@ public class HardwareRenderer {
}
/**
* Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called.
* 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.
@@ -718,10 +802,8 @@ public class HardwareRenderer {
* Interface for listening to picture captures
* @hide
*/
@TestApi
public interface PictureCapturedCallback {
/** @hide */
@TestApi
void onPictureCaptured(Picture picture);
}

View File

@@ -43,6 +43,7 @@ import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.os.Build;
import android.os.Handler;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.IntArray;
@@ -1241,6 +1242,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
private Handler mHandler;
private AnimatorListener mListener = null;
private final LongArray mStartDelays = new LongArray();
private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1671,6 +1673,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
.mRootName);
}
mStarted = true;
if (mHandler == null) {
mHandler = new Handler();
}
nStart(mSetPtr, this, ++mLastListenerId);
invalidateOwningView();
if (mListener != null) {
@@ -1780,7 +1785,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// onFinished: should be called from native
@UnsupportedAppUsage
private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
set.onAnimationEnd(id);
set.mHandler.post(() -> set.onAnimationEnd(id));
}
private void transferPendingActions(VectorDrawableAnimator animatorSet) {

View File

@@ -1028,8 +1028,28 @@
</activity>
<activity
android:name="PositionListenerActivity"
android:label="RenderNode/PositionListener"
android:name="PositionListenerActivity"
android:label="RenderNode/PositionListener"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
<activity
android:name="CustomRenderer"
android:label="HardwareRenderer/HelloTakeSurface"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
<activity
android:name="MyLittleTextureView"
android:label="HardwareRenderer/MyLittleTextureView"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2019 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 com.android.test.hwui;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.HardwareRenderer;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
public class CustomRenderer extends Activity {
private RenderNode mContent = new RenderNode("CustomRenderer");
private HardwareRenderer mRenderer = new HardwareRenderer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().takeSurface(mSurfaceCallbacks);
}
private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() {
@Override
public void surfaceRedrawNeeded(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mContent.setLeftTopRightBottom(0, 0, width, height);
RecordingCanvas canvas = mContent.startRecording();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(Math.min(width, height) * .05f);
canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint);
mContent.endRecording();
mRenderer.setContentRoot(mContent);
mRenderer.setSurface(holder.getSurface());
mRenderer.createRenderRequest()
.setVsyncTime(System.nanoTime())
.setFrameCommitCallback(Runnable::run, () -> {
Log.d("CustomRenderer", "Frame committed!");
})
.syncAndDraw();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mRenderer.destroy();
}
};
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 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 com.android.test.hwui;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorSpace;
import android.graphics.HardwareRenderer;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.widget.ImageView;
public class MyLittleTextureView extends Activity {
private RenderNode mContent = new RenderNode("CustomRenderer");
private HardwareRenderer mRenderer = new HardwareRenderer();
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageView = new ImageView(this);
mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
setContentView(mImageView);
ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
mRenderer.setSurface(reader.getSurface());
mRenderer.setLightSourceAlpha(0.0f, 1.0f);
mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f);
mContent.setLeftTopRightBottom(0, 0, 100, 100);
Rect childRect = new Rect(25, 25, 65, 65);
RenderNode childNode = new RenderNode("shadowCaster");
childNode.setLeftTopRightBottom(childRect.left, childRect.top,
childRect.right, childRect.bottom);
Outline outline = new Outline();
outline.setRect(new Rect(0, 0, childRect.width(), childRect.height()));
outline.setAlpha(1f);
childNode.setOutline(outline);
{
Canvas canvas = childNode.startRecording();
canvas.drawColor(Color.BLUE);
}
childNode.endRecording();
childNode.setElevation(20f);
{
Canvas canvas = mContent.startRecording();
canvas.drawColor(Color.WHITE);
canvas.enableZ();
canvas.drawRenderNode(childNode);
canvas.disableZ();
}
mContent.endRecording();
mRenderer.setContentRoot(mContent);
mRenderer.createRenderRequest()
.setWaitForPresent(true)
.syncAndDraw();
Image image = reader.acquireNextImage();
Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(),
ColorSpace.get(ColorSpace.Named.SRGB));
mImageView.setImageBitmap(bitmap);
image.close();
}
}