summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-01-30 18:43:06 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-01-30 18:43:06 +0000
commit8307014768ed59432d9af734817ba0fb73fbc496 (patch)
treec0b1b19aded70ea83e0be0bdd14268473c0ca972
parentcf40a9ab819a3a6a38e69149d20d76c0d691f9ae (diff)
parent5b7f426ff04820f81877ccb696bf6245dede89e7 (diff)
Merge "Use a separate thread to decode AnimatedImageDrawable"
-rw-r--r--core/jni/android/graphics/AnimatedImageDrawable.cpp18
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java48
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp247
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.h94
-rw-r--r--libs/hwui/hwui/AnimatedImageThread.cpp45
-rw-r--r--libs/hwui/hwui/AnimatedImageThread.h47
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp10
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h11
10 files changed, 370 insertions, 153 deletions
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 0e562c03b762..c88cf5cff04d 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -16,16 +16,16 @@
#include "GraphicsJNI.h"
#include "ImageDecoder.h"
-#include "core_jni_helpers.h"
#include "Utils.h"
+#include "core_jni_helpers.h"
-#include <hwui/AnimatedImageDrawable.h>
-#include <hwui/Canvas.h>
#include <SkAndroidCodec.h>
#include <SkAnimatedImage.h>
#include <SkColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <hwui/AnimatedImageDrawable.h>
+#include <hwui/Canvas.h>
using namespace android;
@@ -85,6 +85,9 @@ static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
}
+// Java's FINISHED relies on this being -1
+static_assert(SkAnimatedImage::kFinished == -1);
+
static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jlong canvasPtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
@@ -115,9 +118,6 @@ static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/,
return drawable->isRunning();
}
-// Java's NOT_RUNNING relies on this being -2.0.
-static_assert(SkAnimatedImage::kNotRunning == -2.0);
-
static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
return drawable->start();
@@ -172,6 +172,11 @@ static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/
return sizeof(drawable);
}
+static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+ auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+ drawable->markInvisible();
+}
+
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{ "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate },
{ "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
@@ -185,6 +190,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
+ { "nMarkInvisible", "(J)V", (void*) AnimatedImageDrawable_nMarkInvisible },
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 0ec19f9a4aee..4328109937c1 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -16,6 +16,8 @@
package android.graphics.drawable;
+import dalvik.annotation.optimization.FastNative;
+
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,7 +62,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
private int mIntrinsicHeight;
private boolean mStarting;
- private boolean mRunning;
private Handler mHandler;
@@ -222,8 +223,8 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
return mIntrinsicHeight;
}
- // nDraw returns -2 if the animation is not running.
- private static final int NOT_RUNNING = -2;
+ // nDraw returns -1 if the animation has finished.
+ private static final int FINISHED = -1;
@Override
public void draw(@NonNull Canvas canvas) {
@@ -235,8 +236,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
mStarting = false;
postOnAnimationStart();
-
- mRunning = true;
}
long nextUpdate = nDraw(mState.mNativePtr, canvas.getNativeCanvasWrapper());
@@ -244,12 +243,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
// will manage the animation
if (nextUpdate > 0) {
scheduleSelf(mRunnable, nextUpdate);
- } else if (nextUpdate == NOT_RUNNING) {
- // -2 means the animation ended, when drawn in software mode.
- if (mRunning) {
- postOnAnimationEnd();
- mRunning = false;
- }
+ } else if (nextUpdate == FINISHED) {
+ // This means the animation was drawn in software mode and ended.
+ postOnAnimationEnd();
}
}
@@ -292,6 +288,19 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
return PixelFormat.TRANSLUCENT;
}
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ if (!super.setVisible(visible, restart)) {
+ return false;
+ }
+
+ if (!visible) {
+ nMarkInvisible(mState.mNativePtr);
+ }
+
+ return true;
+ }
+
// Animatable overrides
/**
* Return whether the animation is currently running.
@@ -301,7 +310,10 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
*/
@Override
public boolean isRunning() {
- return mRunning;
+ if (mState == null) {
+ throw new IllegalStateException("called isRunning on empty AnimatedImageDrawable");
+ }
+ return nIsRunning(mState.mNativePtr);
}
/**
@@ -336,7 +348,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
}
nStop(mState.mNativePtr);
- mRunning = false;
}
// Animatable2 overrides
@@ -405,18 +416,29 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
throws IOException;
+ @FastNative
private static native long nGetNativeFinalizer();
private static native long nDraw(long nativePtr, long canvasNativePtr);
+ @FastNative
private static native void nSetAlpha(long nativePtr, int alpha);
+ @FastNative
private static native int nGetAlpha(long nativePtr);
+ @FastNative
private static native void nSetColorFilter(long nativePtr, long nativeFilter);
+ @FastNative
private static native boolean nIsRunning(long nativePtr);
// Return whether the animation started.
+ @FastNative
private static native boolean nStart(long nativePtr);
+ @FastNative
private static native void nStop(long nativePtr);
+ @FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
private static native void nSetOnAnimationEndListener(long nativePtr,
@Nullable AnimatedImageDrawable drawable);
+ @FastNative
private static native long nNativeByteSize(long nativePtr);
+ @FastNative
+ private static native void nMarkInvisible(long nativePtr);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17f9b7cd62fc..3323bce8b5ad 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -138,6 +138,7 @@ cc_defaults {
srcs: [
"hwui/AnimatedImageDrawable.cpp",
+ "hwui/AnimatedImageThread.cpp",
"hwui/Bitmap.cpp",
"font/CacheTexture.cpp",
"font/Font.cpp",
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index e01bf3d003e6..264b95e0e6dc 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -15,21 +15,21 @@
*/
#include "AnimatedImageDrawable.h"
+#include "AnimatedImageThread.h"
-#include "thread/Task.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
#include "utils/TraceUtils.h"
#include <SkPicture.h>
#include <SkRefCnt.h>
-#include <SkTime.h>
#include <SkTLazy.h>
+#include <SkTime.h>
namespace android {
AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage)
- : mSkAnimatedImage(std::move(animatedImage)) { }
+ : mSkAnimatedImage(std::move(animatedImage)) {
+ mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
+}
void AnimatedImageDrawable::syncProperties() {
mAlpha = mStagingAlpha;
@@ -37,88 +37,78 @@ void AnimatedImageDrawable::syncProperties() {
}
bool AnimatedImageDrawable::start() {
- SkAutoExclusive lock(mLock);
- if (mSkAnimatedImage->isRunning()) {
+ if (mRunning) {
return false;
}
- if (!mSnapshot) {
- mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
- }
-
- // While stopped, update() does not decode, but it does advance the time.
- // This prevents us from skipping ahead when we resume.
- const double currentTime = SkTime::GetMSecs();
- mSkAnimatedImage->update(currentTime);
- mSkAnimatedImage->start();
- return mSkAnimatedImage->isRunning();
+ mRunning = true;
+ return true;
}
void AnimatedImageDrawable::stop() {
- SkAutoExclusive lock(mLock);
- mSkAnimatedImage->stop();
+ mRunning = false;
}
bool AnimatedImageDrawable::isRunning() {
- return mSkAnimatedImage->isRunning();
+ return mRunning;
}
-// This is really a Task<void> but that doesn't really work when Future<>
-// expects to be able to get/set a value
-class AnimatedImageDrawable::AnimatedImageTask : public uirenderer::Task<bool> {
-public:
- AnimatedImageTask(AnimatedImageDrawable* animatedImageDrawable)
- : mAnimatedImageDrawable(sk_ref_sp(animatedImageDrawable)) {}
-
- sk_sp<AnimatedImageDrawable> mAnimatedImageDrawable;
- bool mIsCompleted = false;
-};
-
-class AnimatedImageDrawable::AnimatedImageTaskProcessor : public uirenderer::TaskProcessor<bool> {
-public:
- explicit AnimatedImageTaskProcessor(uirenderer::TaskManager* taskManager)
- : uirenderer::TaskProcessor<bool>(taskManager) {}
- ~AnimatedImageTaskProcessor() {}
-
- virtual void onProcess(const sp<uirenderer::Task<bool>>& task) override {
- ATRACE_NAME("Updating AnimatedImageDrawables");
- AnimatedImageTask* t = static_cast<AnimatedImageTask*>(task.get());
- t->mAnimatedImageDrawable->update();
- t->mIsCompleted = true;
- task->setResult(true);
- };
-};
-
-void AnimatedImageDrawable::scheduleUpdate(uirenderer::TaskManager* taskManager) {
- if (!mSkAnimatedImage->isRunning()
- || (mDecodeTask.get() != nullptr && !mDecodeTask->mIsCompleted)) {
- return;
- }
-
- if (!mDecodeTaskProcessor.get()) {
- mDecodeTaskProcessor = new AnimatedImageTaskProcessor(taskManager);
- }
-
- // TODO get one frame ahead and only schedule updates when you need to replenish
- mDecodeTask = new AnimatedImageTask(this);
- mDecodeTaskProcessor->add(mDecodeTask);
+bool AnimatedImageDrawable::nextSnapshotReady() const {
+ return mNextSnapshot.valid() &&
+ mNextSnapshot.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
-void AnimatedImageDrawable::update() {
- SkAutoExclusive lock(mLock);
+// Only called on the RenderThread while UI thread is locked.
+bool AnimatedImageDrawable::isDirty() {
+ const double currentTime = SkTime::GetMSecs();
+ const double lastWallTime = mLastWallTime;
- if (!mSkAnimatedImage->isRunning()) {
- return;
+ mLastWallTime = currentTime;
+ if (!mRunning) {
+ mDidDraw = false;
+ return false;
}
- const double currentTime = SkTime::GetMSecs();
- if (currentTime >= mNextFrameTime) {
- mNextFrameTime = mSkAnimatedImage->update(currentTime);
- mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
- mIsDirty = true;
+ std::unique_lock lock{mSwapLock};
+ if (mDidDraw) {
+ mCurrentTime += currentTime - lastWallTime;
+ mDidDraw = false;
}
+
+ if (!mNextSnapshot.valid()) {
+ // Need to trigger onDraw in order to start decoding the next frame.
+ return true;
+ }
+
+ return nextSnapshotReady() && mCurrentTime >= mTimeToShowNextSnapshot;
}
+// Only called on the AnimatedImageThread.
+AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
+ Snapshot snap;
+ {
+ std::unique_lock lock{mImageLock};
+ snap.mDuration = mSkAnimatedImage->decodeNextFrame();
+ snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+ }
+
+ return snap;
+}
+
+// Only called on the AnimatedImageThread.
+AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
+ Snapshot snap;
+ {
+ std::unique_lock lock{mImageLock};
+ mSkAnimatedImage->reset();
+ snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
+ snap.mDuration = mSkAnimatedImage->currentFrameDuration();
+ }
+
+ return snap;
+}
+
+// Only called on the RenderThread.
void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
SkTLazy<SkPaint> lazyPaint;
if (mAlpha != SK_AlphaOPAQUE || mColorFilter.get()) {
@@ -128,25 +118,71 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
lazyPaint.get()->setFilterQuality(kLow_SkFilterQuality);
}
- SkAutoExclusive lock(mLock);
- if (mSnapshot) {
- canvas->drawPicture(mSnapshot, nullptr, lazyPaint.getMaybeNull());
- } else {
- // TODO: we could potentially keep the cached surface around if there is a paint and we know
- // the drawable is attached to the view system
+ mDidDraw = true;
+
+ bool drewDirectly = false;
+ if (!mSnapshot.mPic) {
+ // The image is not animating, and never was. Draw directly from
+ // mSkAnimatedImage.
SkAutoCanvasRestore acr(canvas, false);
if (lazyPaint.isValid()) {
canvas->saveLayer(mSkAnimatedImage->getBounds(), lazyPaint.get());
}
+
+ std::unique_lock lock{mImageLock};
mSkAnimatedImage->draw(canvas);
+ drewDirectly = true;
}
- mIsDirty = false;
+ if (mRunning && mFinished) {
+ auto& thread = uirenderer::AnimatedImageThread::getInstance();
+ mNextSnapshot = thread.reset(sk_ref_sp(this));
+ mFinished = false;
+ }
+
+ bool finalFrame = false;
+ if (mRunning && nextSnapshotReady()) {
+ std::unique_lock lock{mSwapLock};
+ if (mCurrentTime >= mTimeToShowNextSnapshot) {
+ mSnapshot = mNextSnapshot.get();
+ const double timeToShowCurrentSnap = mTimeToShowNextSnapshot;
+ if (mSnapshot.mDuration == SkAnimatedImage::kFinished) {
+ finalFrame = true;
+ mRunning = false;
+ mFinished = true;
+ } else {
+ mTimeToShowNextSnapshot += mSnapshot.mDuration;
+ if (mCurrentTime >= mTimeToShowNextSnapshot) {
+ // This would mean showing the current frame very briefly. It's
+ // possible that not being displayed for a time resulted in
+ // mCurrentTime being far ahead. Prevent showing many frames
+ // rapidly by going back to the beginning of this frame time.
+ mCurrentTime = timeToShowCurrentSnap;
+ }
+ }
+ }
+ }
+
+ if (mRunning && !mNextSnapshot.valid()) {
+ auto& thread = uirenderer::AnimatedImageThread::getInstance();
+ mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this));
+ }
+
+ if (!drewDirectly) {
+ // No other thread will modify mCurrentSnap so this should be safe to
+ // use without locking.
+ canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint.getMaybeNull());
+ }
+
+ if (finalFrame) {
+ if (mEndListener) {
+ mEndListener->onAnimationEnd();
+ mEndListener = nullptr;
+ }
+ }
}
double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
- // update the drawable with the current time
- double nextUpdate = mSkAnimatedImage->update(SkTime::GetMSecs());
SkAutoCanvasRestore acr(canvas, false);
if (mStagingAlpha != SK_AlphaOPAQUE || mStagingColorFilter.get()) {
SkPaint paint;
@@ -154,8 +190,59 @@ double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
paint.setColorFilter(mStagingColorFilter);
canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint);
}
- canvas->drawDrawable(mSkAnimatedImage.get());
- return nextUpdate;
+
+ if (mFinished && !mRunning) {
+ // Continue drawing the last frame, and return 0 to indicate no need to
+ // redraw.
+ std::unique_lock lock{mImageLock};
+ canvas->drawDrawable(mSkAnimatedImage.get());
+ return 0.0;
+ }
+
+ bool update = false;
+ {
+ const double currentTime = SkTime::GetMSecs();
+ std::unique_lock lock{mSwapLock};
+ // mLastWallTime starts off at 0. If it is still 0, just update it to
+ // the current time and avoid updating
+ if (mLastWallTime == 0.0) {
+ mCurrentTime = currentTime;
+ } else if (mRunning) {
+ if (mFinished) {
+ mCurrentTime = currentTime;
+ {
+ std::unique_lock lock{mImageLock};
+ mSkAnimatedImage->reset();
+ }
+ mTimeToShowNextSnapshot = currentTime + mSkAnimatedImage->currentFrameDuration();
+ } else {
+ mCurrentTime += currentTime - mLastWallTime;
+ update = mCurrentTime >= mTimeToShowNextSnapshot;
+ }
+ }
+ mLastWallTime = currentTime;
+ }
+
+ double duration = 0.0;
+ {
+ std::unique_lock lock{mImageLock};
+ if (update) {
+ duration = mSkAnimatedImage->decodeNextFrame();
+ }
+
+ canvas->drawDrawable(mSkAnimatedImage.get());
+ }
+
+ std::unique_lock lock{mSwapLock};
+ if (update) {
+ if (duration == SkAnimatedImage::kFinished) {
+ mRunning = false;
+ mFinished = true;
+ } else {
+ mTimeToShowNextSnapshot += duration;
+ }
+ }
+ return mTimeToShowNextSnapshot;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index df8a5e48d18e..9d84ed5f4470 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -17,22 +17,20 @@
#pragma once
#include <cutils/compiler.h>
+#include <utils/Macros.h>
#include <utils/RefBase.h>
#include <SkAnimatedImage.h>
#include <SkCanvas.h>
#include <SkColorFilter.h>
#include <SkDrawable.h>
-#include <SkMutex.h>
+#include <SkPicture.h>
-class SkPicture;
+#include <future>
+#include <mutex>
namespace android {
-namespace uirenderer {
-class TaskManager;
-}
-
class OnAnimationEndListener {
public:
virtual ~OnAnimationEndListener() {}
@@ -41,68 +39,102 @@ public:
};
/**
- * Native component of android.graphics.drawable.AnimatedImageDrawables.java. This class can be
- * drawn into Canvas.h and maintains the state needed to drive the animation from the RenderThread.
+ * Native component of android.graphics.drawable.AnimatedImageDrawables.java.
+ * This class can be drawn into Canvas.h and maintains the state needed to drive
+ * the animation from the RenderThread.
*/
class ANDROID_API AnimatedImageDrawable : public SkDrawable {
public:
AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage);
/**
- * This returns true if the animation has updated and signals that the next draw will contain
- * new content.
+ * This updates the internal time and returns true if the animation needs
+ * to be redrawn.
+ *
+ * This is called on RenderThread, while the UI thread is locked.
*/
- bool isDirty() const { return mIsDirty; }
+ bool isDirty();
int getStagingAlpha() const { return mStagingAlpha; }
void setStagingAlpha(int alpha) { mStagingAlpha = alpha; }
void setStagingColorFilter(sk_sp<SkColorFilter> filter) { mStagingColorFilter = filter; }
void syncProperties();
- virtual SkRect onGetBounds() override {
- return mSkAnimatedImage->getBounds();
- }
+ virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
+ // Draw to software canvas, and return time to next draw.
double drawStaging(SkCanvas* canvas);
- // Returns true if the animation was started; false otherwise (e.g. it was already running)
+ // Returns true if the animation was started; false otherwise (e.g. it was
+ // already running)
bool start();
void stop();
bool isRunning();
- void setRepetitionCount(int count) {
- mSkAnimatedImage->setRepetitionCount(count);
- }
+ void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
mEndListener = std::move(listener);
}
- void scheduleUpdate(uirenderer::TaskManager* taskManager);
+ void markInvisible() { mDidDraw = false; }
+
+ struct Snapshot {
+ sk_sp<SkPicture> mPic;
+ int mDuration;
+
+ Snapshot() = default;
+
+ Snapshot(Snapshot&&) = default;
+ Snapshot& operator=(Snapshot&&) = default;
+
+ PREVENT_COPY_AND_ASSIGN(Snapshot);
+ };
+
+ // These are only called on AnimatedImageThread.
+ Snapshot decodeNextFrame();
+ Snapshot reset();
protected:
virtual void onDraw(SkCanvas* canvas) override;
private:
- void update();
-
sk_sp<SkAnimatedImage> mSkAnimatedImage;
- sk_sp<SkPicture> mSnapshot;
- SkMutex mLock;
+ bool mRunning = false;
+ bool mFinished = false;
+
+ // A snapshot of the current frame to draw.
+ Snapshot mSnapshot;
+
+ std::future<Snapshot> mNextSnapshot;
+
+ bool nextSnapshotReady() const;
+
+ // When to switch from mSnapshot to mNextSnapshot.
+ double mTimeToShowNextSnapshot = 0.0;
+
+ // The current time for the drawable itself.
+ double mCurrentTime = 0.0;
+
+ // The wall clock of the last time we called isDirty.
+ double mLastWallTime = 0.0;
+
+ // Whether we drew since the last call to isDirty.
+ bool mDidDraw = false;
+
+ // Locked when assigning snapshots and times. Operations while this is held
+ // should be short.
+ std::mutex mSwapLock;
+
+ // Locked when mSkAnimatedImage is being updated or drawn.
+ std::mutex mImageLock;
int mStagingAlpha = SK_AlphaOPAQUE;
sk_sp<SkColorFilter> mStagingColorFilter;
int mAlpha = SK_AlphaOPAQUE;
sk_sp<SkColorFilter> mColorFilter;
- double mNextFrameTime = 0.0;
- bool mIsDirty = false;
-
- class AnimatedImageTask;
- class AnimatedImageTaskProcessor;
- sp<AnimatedImageTask> mDecodeTask;
- sp<AnimatedImageTaskProcessor> mDecodeTaskProcessor;
std::unique_ptr<OnAnimationEndListener> mEndListener;
};
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageThread.cpp b/libs/hwui/hwui/AnimatedImageThread.cpp
new file mode 100644
index 000000000000..c8990039875e
--- /dev/null
+++ b/libs/hwui/hwui/AnimatedImageThread.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include "AnimatedImageThread.h"
+
+#include <sys/resource.h>
+
+namespace android {
+namespace uirenderer {
+
+AnimatedImageThread& AnimatedImageThread::getInstance() {
+ static AnimatedImageThread* sInstance = new AnimatedImageThread();
+ return *sInstance;
+}
+
+AnimatedImageThread::AnimatedImageThread() {
+ setpriority(PRIO_PROCESS, 0, PRIORITY_NORMAL + PRIORITY_MORE_FAVORABLE);
+ start("AnimatedImageThread");
+}
+
+std::future<AnimatedImageDrawable::Snapshot> AnimatedImageThread::decodeNextFrame(
+ const sk_sp<AnimatedImageDrawable>& drawable) {
+ return queue().async([drawable]() { return drawable->decodeNextFrame(); });
+}
+
+std::future<AnimatedImageDrawable::Snapshot> AnimatedImageThread::reset(
+ const sk_sp<AnimatedImageDrawable>& drawable) {
+ return queue().async([drawable]() { return drawable->reset(); });
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageThread.h b/libs/hwui/hwui/AnimatedImageThread.h
new file mode 100644
index 000000000000..9e3537430d5a
--- /dev/null
+++ b/libs/hwui/hwui/AnimatedImageThread.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef ANIMATEDIMAGETHREAD_H_
+#define ANIMATEDIMAGETHREAD_H_
+
+#include "AnimatedImageDrawable.h"
+#include "thread/ThreadBase.h"
+
+#include <SkRefCnt.h>
+
+namespace android {
+
+namespace uirenderer {
+
+class AnimatedImageThread : private ThreadBase {
+ PREVENT_COPY_AND_ASSIGN(AnimatedImageThread);
+
+public:
+ static AnimatedImageThread& getInstance();
+
+ std::future<AnimatedImageDrawable::Snapshot> decodeNextFrame(
+ const sk_sp<AnimatedImageDrawable>&);
+ std::future<AnimatedImageDrawable::Snapshot> reset(const sk_sp<AnimatedImageDrawable>&);
+
+private:
+ AnimatedImageThread();
+};
+
+} // namespace uirenderer
+
+} // namespace android
+
+#endif // ANIMATEDIMAGETHREAD_H_
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index cf0b6a4d1dcc..aa14699ae4c2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -98,8 +98,6 @@ bool SkiaDisplayList::prepareListAndChildren(
isDirty = true;
}
if (animatedImage->isRunning()) {
- static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
- ->scheduleDeferredUpdate(animatedImage);
info.out.hasAnimations = true;
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 534782a5dc02..9db39d954e4c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -40,7 +40,6 @@ uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
- mAnimatedImageDrawables.reserve(30);
mVectorDrawables.reserve(30);
}
@@ -327,15 +326,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli
ATRACE_NAME("flush commands");
surface->getCanvas()->flush();
-
- // TODO move to another method
- if (!mAnimatedImageDrawables.empty()) {
- ATRACE_NAME("Update AnimatedImageDrawables");
- for (auto animatedImage : mAnimatedImageDrawables) {
- animatedImage->scheduleUpdate(getTaskManager());
- }
- mAnimatedImageDrawables.clear();
- }
}
namespace {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index cc75e9c5b38d..3800194440f9 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -55,12 +55,6 @@ public:
std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
- void scheduleDeferredUpdate(AnimatedImageDrawable* imageDrawable) {
- mAnimatedImageDrawables.push_back(imageDrawable);
- }
-
- std::vector<AnimatedImageDrawable*>* getAnimatingImages() { return &mAnimatedImageDrawables; }
-
static void destroyLayer(RenderNode* node);
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
@@ -144,11 +138,6 @@ private:
*/
std::vector<VectorDrawableRoot*> mVectorDrawables;
- /**
- * populated by prepareTree with images with active animations
- */
- std::vector<AnimatedImageDrawable*> mAnimatedImageDrawables;
-
// Block of properties used only for debugging to record a SkPicture and save it in a file.
/**
* mCapturedFile is used to enforce we don't capture more than once for a given name (cause