diff options
| author | 2018-01-30 18:43:06 +0000 | |
|---|---|---|
| committer | 2018-01-30 18:43:06 +0000 | |
| commit | 8307014768ed59432d9af734817ba0fb73fbc496 (patch) | |
| tree | c0b1b19aded70ea83e0be0bdd14268473c0ca972 | |
| parent | cf40a9ab819a3a6a38e69149d20d76c0d691f9ae (diff) | |
| parent | 5b7f426ff04820f81877ccb696bf6245dede89e7 (diff) | |
Merge "Use a separate thread to decode AnimatedImageDrawable"
| -rw-r--r-- | core/jni/android/graphics/AnimatedImageDrawable.cpp | 18 | ||||
| -rw-r--r-- | graphics/java/android/graphics/drawable/AnimatedImageDrawable.java | 48 | ||||
| -rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
| -rw-r--r-- | libs/hwui/hwui/AnimatedImageDrawable.cpp | 247 | ||||
| -rw-r--r-- | libs/hwui/hwui/AnimatedImageDrawable.h | 94 | ||||
| -rw-r--r-- | libs/hwui/hwui/AnimatedImageThread.cpp | 45 | ||||
| -rw-r--r-- | libs/hwui/hwui/AnimatedImageThread.h | 47 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 10 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.h | 11 |
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 |