Merge "AnimatedImageDrawable: Eliminate unnecessary calls to redraw" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a2113aa4ad
@@ -239,11 +239,6 @@ static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*
|
|||||||
return drawable->byteSize();
|
return drawable->byteSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
|
|
||||||
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
|
|
||||||
drawable->markInvisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
|
static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
|
||||||
jboolean mirrored) {
|
jboolean mirrored) {
|
||||||
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
|
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
|
||||||
@@ -264,7 +259,6 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
|
|||||||
{ "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount },
|
{ "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount },
|
||||||
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
|
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
|
||||||
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
|
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
|
||||||
{ "nMarkInvisible", "(J)V", (void*) AnimatedImageDrawable_nMarkInvisible },
|
|
||||||
{ "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
|
{ "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.graphics.PixelFormat;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
@@ -348,7 +349,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
|
|||||||
if (mRunnable == null) {
|
if (mRunnable == null) {
|
||||||
mRunnable = this::invalidateSelf;
|
mRunnable = this::invalidateSelf;
|
||||||
}
|
}
|
||||||
scheduleSelf(mRunnable, nextUpdate);
|
scheduleSelf(mRunnable, nextUpdate + SystemClock.uptimeMillis());
|
||||||
} else if (nextUpdate == FINISHED) {
|
} else if (nextUpdate == FINISHED) {
|
||||||
// This means the animation was drawn in software mode and ended.
|
// This means the animation was drawn in software mode and ended.
|
||||||
postOnAnimationEnd();
|
postOnAnimationEnd();
|
||||||
@@ -430,23 +431,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
|
|||||||
return mState.mAutoMirrored;
|
return mState.mAutoMirrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setVisible(boolean visible, boolean restart) {
|
|
||||||
if (!super.setVisible(visible, restart)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mState.mNativePtr == 0) {
|
|
||||||
throw new IllegalStateException("called setVisible on empty AnimatedImageDrawable");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!visible) {
|
|
||||||
nMarkInvisible(mState.mNativePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animatable overrides
|
// Animatable overrides
|
||||||
/**
|
/**
|
||||||
* Return whether the animation is currently running.
|
* Return whether the animation is currently running.
|
||||||
@@ -616,7 +600,5 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
|
|||||||
@FastNative
|
@FastNative
|
||||||
private static native long nNativeByteSize(long nativePtr);
|
private static native long nNativeByteSize(long nativePtr);
|
||||||
@FastNative
|
@FastNative
|
||||||
private static native void nMarkInvisible(long nativePtr);
|
|
||||||
@FastNative
|
|
||||||
private static native void nSetMirrored(long nativePtr, boolean mirror);
|
private static native void nSetMirrored(long nativePtr, boolean mirror);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ public:
|
|||||||
// *OR* will post itself for the next vsync automatically, use this
|
// *OR* will post itself for the next vsync automatically, use this
|
||||||
// only to avoid calling draw()
|
// only to avoid calling draw()
|
||||||
bool canDrawThisFrame = true;
|
bool canDrawThisFrame = true;
|
||||||
|
// Sentinel for animatedImageDelay meaning there is no need to post such
|
||||||
|
// a message.
|
||||||
|
static constexpr nsecs_t kNoAnimatedImageDelay = -1;
|
||||||
|
// This is used to post a message to redraw when it is time to draw the
|
||||||
|
// next frame of an AnimatedImageDrawable.
|
||||||
|
nsecs_t animatedImageDelay = kNoAnimatedImageDelay;
|
||||||
} out;
|
} out;
|
||||||
|
|
||||||
// This flag helps to disable projection for receiver nodes that do not have any backward
|
// This flag helps to disable projection for receiver nodes that do not have any backward
|
||||||
|
|||||||
@@ -22,13 +22,12 @@
|
|||||||
#include <SkPicture.h>
|
#include <SkPicture.h>
|
||||||
#include <SkRefCnt.h>
|
#include <SkRefCnt.h>
|
||||||
#include <SkTLazy.h>
|
#include <SkTLazy.h>
|
||||||
#include <SkTime.h>
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
|
AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
|
||||||
: mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
|
: mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
|
||||||
mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
|
mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageDrawable::syncProperties() {
|
void AnimatedImageDrawable::syncProperties() {
|
||||||
@@ -62,28 +61,42 @@ bool AnimatedImageDrawable::nextSnapshotReady() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only called on the RenderThread while UI thread is locked.
|
// Only called on the RenderThread while UI thread is locked.
|
||||||
bool AnimatedImageDrawable::isDirty() {
|
bool AnimatedImageDrawable::isDirty(nsecs_t* outDelay) {
|
||||||
const double currentTime = SkTime::GetMSecs();
|
*outDelay = 0;
|
||||||
const double lastWallTime = mLastWallTime;
|
const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC);
|
||||||
|
const nsecs_t lastWallTime = mLastWallTime;
|
||||||
|
|
||||||
mLastWallTime = currentTime;
|
mLastWallTime = currentTime;
|
||||||
if (!mRunning) {
|
if (!mRunning) {
|
||||||
mDidDraw = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock lock{mSwapLock};
|
std::unique_lock lock{mSwapLock};
|
||||||
if (mDidDraw) {
|
|
||||||
mCurrentTime += currentTime - lastWallTime;
|
mCurrentTime += currentTime - lastWallTime;
|
||||||
mDidDraw = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mNextSnapshot.valid()) {
|
if (!mNextSnapshot.valid()) {
|
||||||
// Need to trigger onDraw in order to start decoding the next frame.
|
// Need to trigger onDraw in order to start decoding the next frame.
|
||||||
|
*outDelay = mTimeToShowNextSnapshot - mCurrentTime;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nextSnapshotReady() && mCurrentTime >= mTimeToShowNextSnapshot;
|
if (mTimeToShowNextSnapshot > mCurrentTime) {
|
||||||
|
*outDelay = mTimeToShowNextSnapshot - mCurrentTime;
|
||||||
|
} else if (nextSnapshotReady()) {
|
||||||
|
// We have not yet updated mTimeToShowNextSnapshot. Read frame duration
|
||||||
|
// directly from mSkAnimatedImage.
|
||||||
|
lock.unlock();
|
||||||
|
std::unique_lock imageLock{mImageLock};
|
||||||
|
*outDelay = ms2ns(mSkAnimatedImage->currentFrameDuration());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// The next snapshot has not yet been decoded, but we've already passed
|
||||||
|
// time to draw it. There's not a good way to know when decoding will
|
||||||
|
// finish, so request an update immediately.
|
||||||
|
*outDelay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only called on the AnimatedImageThread.
|
// Only called on the AnimatedImageThread.
|
||||||
@@ -91,7 +104,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
|
|||||||
Snapshot snap;
|
Snapshot snap;
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mImageLock};
|
std::unique_lock lock{mImageLock};
|
||||||
snap.mDuration = mSkAnimatedImage->decodeNextFrame();
|
snap.mDurationMS = mSkAnimatedImage->decodeNextFrame();
|
||||||
snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
|
snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +118,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
|
|||||||
std::unique_lock lock{mImageLock};
|
std::unique_lock lock{mImageLock};
|
||||||
mSkAnimatedImage->reset();
|
mSkAnimatedImage->reset();
|
||||||
snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
|
snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
|
||||||
snap.mDuration = mSkAnimatedImage->currentFrameDuration();
|
snap.mDurationMS = mSkAnimatedImage->currentFrameDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
return snap;
|
return snap;
|
||||||
@@ -127,8 +140,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
|
|||||||
canvas->scale(-1, 1);
|
canvas->scale(-1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
mDidDraw = true;
|
|
||||||
|
|
||||||
const bool starting = mStarting;
|
const bool starting = mStarting;
|
||||||
mStarting = false;
|
mStarting = false;
|
||||||
|
|
||||||
@@ -157,12 +168,12 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
|
|||||||
std::unique_lock lock{mSwapLock};
|
std::unique_lock lock{mSwapLock};
|
||||||
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
||||||
mSnapshot = mNextSnapshot.get();
|
mSnapshot = mNextSnapshot.get();
|
||||||
const double timeToShowCurrentSnap = mTimeToShowNextSnapshot;
|
const nsecs_t timeToShowCurrentSnap = mTimeToShowNextSnapshot;
|
||||||
if (mSnapshot.mDuration == SkAnimatedImage::kFinished) {
|
if (mSnapshot.mDurationMS == SkAnimatedImage::kFinished) {
|
||||||
finalFrame = true;
|
finalFrame = true;
|
||||||
mRunning = false;
|
mRunning = false;
|
||||||
} else {
|
} else {
|
||||||
mTimeToShowNextSnapshot += mSnapshot.mDuration;
|
mTimeToShowNextSnapshot += ms2ns(mSnapshot.mDurationMS);
|
||||||
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
||||||
// This would mean showing the current frame very briefly. It's
|
// This would mean showing the current frame very briefly. It's
|
||||||
// possible that not being displayed for a time resulted in
|
// possible that not being displayed for a time resulted in
|
||||||
@@ -192,7 +203,7 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
|
int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
|
||||||
SkAutoCanvasRestore acr(canvas, false);
|
SkAutoCanvasRestore acr(canvas, false);
|
||||||
if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
|
if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
@@ -211,69 +222,69 @@ double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
|
|||||||
// to redraw.
|
// to redraw.
|
||||||
std::unique_lock lock{mImageLock};
|
std::unique_lock lock{mImageLock};
|
||||||
canvas->drawDrawable(mSkAnimatedImage.get());
|
canvas->drawDrawable(mSkAnimatedImage.get());
|
||||||
return 0.0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStarting) {
|
if (mStarting) {
|
||||||
mStarting = false;
|
mStarting = false;
|
||||||
double duration = 0.0;
|
int durationMS = 0;
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mImageLock};
|
std::unique_lock lock{mImageLock};
|
||||||
mSkAnimatedImage->reset();
|
mSkAnimatedImage->reset();
|
||||||
duration = mSkAnimatedImage->currentFrameDuration();
|
durationMS = mSkAnimatedImage->currentFrameDuration();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mSwapLock};
|
std::unique_lock lock{mSwapLock};
|
||||||
mLastWallTime = 0.0;
|
mLastWallTime = 0;
|
||||||
mTimeToShowNextSnapshot = duration;
|
// The current time will be added later, below.
|
||||||
|
mTimeToShowNextSnapshot = ms2ns(durationMS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool update = false;
|
bool update = false;
|
||||||
{
|
{
|
||||||
const double currentTime = SkTime::GetMSecs();
|
const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC);
|
||||||
std::unique_lock lock{mSwapLock};
|
std::unique_lock lock{mSwapLock};
|
||||||
// mLastWallTime starts off at 0. If it is still 0, just update it to
|
// mLastWallTime starts off at 0. If it is still 0, just update it to
|
||||||
// the current time and avoid updating
|
// the current time and avoid updating
|
||||||
if (mLastWallTime == 0.0) {
|
if (mLastWallTime == 0) {
|
||||||
mCurrentTime = currentTime;
|
mCurrentTime = currentTime;
|
||||||
// mTimeToShowNextSnapshot is already set to the duration of the
|
// mTimeToShowNextSnapshot is already set to the duration of the
|
||||||
// first frame.
|
// first frame.
|
||||||
mTimeToShowNextSnapshot += currentTime;
|
mTimeToShowNextSnapshot += currentTime;
|
||||||
} else if (mRunning && mDidDraw) {
|
} else if (mRunning) {
|
||||||
mCurrentTime += currentTime - mLastWallTime;
|
mCurrentTime += currentTime - mLastWallTime;
|
||||||
update = mCurrentTime >= mTimeToShowNextSnapshot;
|
update = mCurrentTime >= mTimeToShowNextSnapshot;
|
||||||
}
|
}
|
||||||
mLastWallTime = currentTime;
|
mLastWallTime = currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
double duration = 0.0;
|
int durationMS = 0;
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mImageLock};
|
std::unique_lock lock{mImageLock};
|
||||||
if (update) {
|
if (update) {
|
||||||
duration = mSkAnimatedImage->decodeNextFrame();
|
durationMS = mSkAnimatedImage->decodeNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas->drawDrawable(mSkAnimatedImage.get());
|
canvas->drawDrawable(mSkAnimatedImage.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
mDidDraw = true;
|
|
||||||
|
|
||||||
std::unique_lock lock{mSwapLock};
|
std::unique_lock lock{mSwapLock};
|
||||||
if (update) {
|
if (update) {
|
||||||
if (duration == SkAnimatedImage::kFinished) {
|
if (durationMS == SkAnimatedImage::kFinished) {
|
||||||
mRunning = false;
|
mRunning = false;
|
||||||
return duration;
|
return SkAnimatedImage::kFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
const double timeToShowCurrentSnapshot = mTimeToShowNextSnapshot;
|
const nsecs_t timeToShowCurrentSnapshot = mTimeToShowNextSnapshot;
|
||||||
mTimeToShowNextSnapshot += duration;
|
mTimeToShowNextSnapshot += ms2ns(durationMS);
|
||||||
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
if (mCurrentTime >= mTimeToShowNextSnapshot) {
|
||||||
// As in onDraw, prevent speedy catch-up behavior.
|
// As in onDraw, prevent speedy catch-up behavior.
|
||||||
mCurrentTime = timeToShowCurrentSnapshot;
|
mCurrentTime = timeToShowCurrentSnapshot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mTimeToShowNextSnapshot;
|
|
||||||
|
return ns2ms(mTimeToShowNextSnapshot - mCurrentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <cutils/compiler.h>
|
#include <cutils/compiler.h>
|
||||||
#include <utils/Macros.h>
|
#include <utils/Macros.h>
|
||||||
#include <utils/RefBase.h>
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils/Timers.h>
|
||||||
|
|
||||||
#include <SkAnimatedImage.h>
|
#include <SkAnimatedImage.h>
|
||||||
#include <SkCanvas.h>
|
#include <SkCanvas.h>
|
||||||
@@ -50,12 +51,15 @@ public:
|
|||||||
AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
|
AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This updates the internal time and returns true if the animation needs
|
* This updates the internal time and returns true if the image needs
|
||||||
* to be redrawn.
|
* to be redrawn this frame.
|
||||||
*
|
*
|
||||||
* This is called on RenderThread, while the UI thread is locked.
|
* This is called on RenderThread, while the UI thread is locked.
|
||||||
|
*
|
||||||
|
* @param outDelay Nanoseconds in the future when the following frame
|
||||||
|
* will need to be drawn. 0 if not running.
|
||||||
*/
|
*/
|
||||||
bool isDirty();
|
bool isDirty(nsecs_t* outDelay);
|
||||||
|
|
||||||
int getStagingAlpha() const { return mStagingProperties.mAlpha; }
|
int getStagingAlpha() const { return mStagingProperties.mAlpha; }
|
||||||
void setStagingAlpha(int alpha) { mStagingProperties.mAlpha = alpha; }
|
void setStagingAlpha(int alpha) { mStagingProperties.mAlpha = alpha; }
|
||||||
@@ -68,7 +72,9 @@ public:
|
|||||||
virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
|
virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
|
||||||
|
|
||||||
// Draw to software canvas, and return time to next draw.
|
// Draw to software canvas, and return time to next draw.
|
||||||
double drawStaging(SkCanvas* canvas);
|
// 0 means the animation is not running.
|
||||||
|
// -1 means the animation advanced to the final frame.
|
||||||
|
int drawStaging(SkCanvas* canvas);
|
||||||
|
|
||||||
// Returns true if the animation was started; false otherwise (e.g. it was
|
// Returns true if the animation was started; false otherwise (e.g. it was
|
||||||
// already running)
|
// already running)
|
||||||
@@ -84,11 +90,9 @@ public:
|
|||||||
mEndListener = std::move(listener);
|
mEndListener = std::move(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markInvisible() { mDidDraw = false; }
|
|
||||||
|
|
||||||
struct Snapshot {
|
struct Snapshot {
|
||||||
sk_sp<SkPicture> mPic;
|
sk_sp<SkPicture> mPic;
|
||||||
int mDuration;
|
int mDurationMS;
|
||||||
|
|
||||||
Snapshot() = default;
|
Snapshot() = default;
|
||||||
|
|
||||||
@@ -124,16 +128,13 @@ private:
|
|||||||
bool nextSnapshotReady() const;
|
bool nextSnapshotReady() const;
|
||||||
|
|
||||||
// When to switch from mSnapshot to mNextSnapshot.
|
// When to switch from mSnapshot to mNextSnapshot.
|
||||||
double mTimeToShowNextSnapshot = 0.0;
|
nsecs_t mTimeToShowNextSnapshot = 0;
|
||||||
|
|
||||||
// The current time for the drawable itself.
|
// The current time for the drawable itself.
|
||||||
double mCurrentTime = 0.0;
|
nsecs_t mCurrentTime = 0;
|
||||||
|
|
||||||
// The wall clock of the last time we called isDirty.
|
// The wall clock of the last time we called isDirty.
|
||||||
double mLastWallTime = 0.0;
|
nsecs_t mLastWallTime = 0;
|
||||||
|
|
||||||
// Whether we drew since the last call to isDirty.
|
|
||||||
bool mDidDraw = false;
|
|
||||||
|
|
||||||
// Locked when assigning snapshots and times. Operations while this is held
|
// Locked when assigning snapshots and times. Operations while this is held
|
||||||
// should be short.
|
// should be short.
|
||||||
|
|||||||
@@ -93,12 +93,18 @@ bool SkiaDisplayList::prepareListAndChildren(
|
|||||||
|
|
||||||
bool isDirty = false;
|
bool isDirty = false;
|
||||||
for (auto& animatedImage : mAnimatedImages) {
|
for (auto& animatedImage : mAnimatedImages) {
|
||||||
|
nsecs_t timeTilNextFrame = TreeInfo::Out::kNoAnimatedImageDelay;
|
||||||
// If any animated image in the display list needs updated, then damage the node.
|
// If any animated image in the display list needs updated, then damage the node.
|
||||||
if (animatedImage->isDirty()) {
|
if (animatedImage->isDirty(&timeTilNextFrame)) {
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
if (animatedImage->isRunning()) {
|
|
||||||
info.out.hasAnimations = true;
|
if (animatedImage->isRunning() &&
|
||||||
|
timeTilNextFrame != TreeInfo::Out::kNoAnimatedImageDelay) {
|
||||||
|
auto& delay = info.out.animatedImageDelay;
|
||||||
|
if (delay == TreeInfo::Out::kNoAnimatedImageDelay || timeTilNextFrame < delay) {
|
||||||
|
delay = timeTilNextFrame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
|
|||||||
IContextFactory* contextFactory,
|
IContextFactory* contextFactory,
|
||||||
std::unique_ptr<IRenderPipeline> renderPipeline)
|
std::unique_ptr<IRenderPipeline> renderPipeline)
|
||||||
: mRenderThread(thread)
|
: mRenderThread(thread)
|
||||||
|
, mGenerationID(0)
|
||||||
, mOpaque(!translucent)
|
, mOpaque(!translucent)
|
||||||
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
|
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
|
||||||
, mJankTracker(&thread.globalProfileData(), thread.mainDisplayInfo())
|
, mJankTracker(&thread.globalProfileData(), thread.mainDisplayInfo())
|
||||||
@@ -196,6 +197,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) {
|
|||||||
mSwapHistory.clear();
|
mSwapHistory.clear();
|
||||||
} else {
|
} else {
|
||||||
mRenderThread.removeFrameCallback(this);
|
mRenderThread.removeFrameCallback(this);
|
||||||
|
mGenerationID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +206,7 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CanvasContext::pauseSurface() {
|
bool CanvasContext::pauseSurface() {
|
||||||
|
mGenerationID++;
|
||||||
return mRenderThread.removeFrameCallback(this);
|
return mRenderThread.removeFrameCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +214,7 @@ void CanvasContext::setStopped(bool stopped) {
|
|||||||
if (mStopped != stopped) {
|
if (mStopped != stopped) {
|
||||||
mStopped = stopped;
|
mStopped = stopped;
|
||||||
if (mStopped) {
|
if (mStopped) {
|
||||||
|
mGenerationID++;
|
||||||
mRenderThread.removeFrameCallback(this);
|
mRenderThread.removeFrameCallback(this);
|
||||||
mRenderPipeline->onStop();
|
mRenderPipeline->onStop();
|
||||||
} else if (mIsDirty && hasSurface()) {
|
} else if (mIsDirty && hasSurface()) {
|
||||||
@@ -383,6 +387,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
|
|||||||
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
|
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool postedFrameCallback = false;
|
||||||
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
|
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
|
||||||
if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
|
if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
|
||||||
info.out.requiresUiRedraw = true;
|
info.out.requiresUiRedraw = true;
|
||||||
@@ -391,6 +396,24 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
|
|||||||
// If animationsNeedsRedraw is set don't bother posting for an RT anim
|
// If animationsNeedsRedraw is set don't bother posting for an RT anim
|
||||||
// as we will just end up fighting the UI thread.
|
// as we will just end up fighting the UI thread.
|
||||||
mRenderThread.postFrameCallback(this);
|
mRenderThread.postFrameCallback(this);
|
||||||
|
postedFrameCallback = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!postedFrameCallback &&
|
||||||
|
info.out.animatedImageDelay != TreeInfo::Out::kNoAnimatedImageDelay) {
|
||||||
|
// Subtract the time of one frame so it can be displayed on time.
|
||||||
|
const nsecs_t kFrameTime = mRenderThread.timeLord().frameIntervalNanos();
|
||||||
|
if (info.out.animatedImageDelay <= kFrameTime) {
|
||||||
|
mRenderThread.postFrameCallback(this);
|
||||||
|
} else {
|
||||||
|
const auto delay = info.out.animatedImageDelay - kFrameTime;
|
||||||
|
int genId = mGenerationID;
|
||||||
|
mRenderThread.queue().postDelayed(delay, [this, genId]() {
|
||||||
|
if (mGenerationID == genId) {
|
||||||
|
mRenderThread.postFrameCallback(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,6 +421,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
|
|||||||
void CanvasContext::stopDrawing() {
|
void CanvasContext::stopDrawing() {
|
||||||
mRenderThread.removeFrameCallback(this);
|
mRenderThread.removeFrameCallback(this);
|
||||||
mAnimationContext->pauseAnimators();
|
mAnimationContext->pauseAnimators();
|
||||||
|
mGenerationID++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContext::notifyFramePending() {
|
void CanvasContext::notifyFramePending() {
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ private:
|
|||||||
// stopped indicates the CanvasContext will reject actual redraw operations,
|
// stopped indicates the CanvasContext will reject actual redraw operations,
|
||||||
// and defer repaint until it is un-stopped
|
// and defer repaint until it is un-stopped
|
||||||
bool mStopped = false;
|
bool mStopped = false;
|
||||||
|
// Incremented each time the CanvasContext is stopped. Used to ignore
|
||||||
|
// delayed messages that are triggered after stopping.
|
||||||
|
int mGenerationID;
|
||||||
// CanvasContext is dirty if it has received an update that it has not
|
// CanvasContext is dirty if it has received an update that it has not
|
||||||
// painted onto its surface.
|
// painted onto its surface.
|
||||||
bool mIsDirty = false;
|
bool mIsDirty = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user