diff options
author | 2016-08-01 14:39:24 -0700 | |
---|---|---|
committer | 2016-08-01 16:36:14 -0700 | |
commit | 4387190d8ec9fe4e953fcfeb093a644b82cf85ed (patch) | |
tree | c717cbed2691386f68b6c97ba09d64ac85137871 | |
parent | fbd93027e4f8024642271652e490653a4d0c2b55 (diff) |
Eliminate recents upload jank
Bug: 30342017
Upload recents thumbnails in the
dead gaps between frames instead of
at the start of a frame. This eliminates
jank caused by the large texture
upload.
Change-Id: I507cd286d199109c7a9a1511d68ba5ab5d28069f
-rwxr-xr-x | core/jni/android/graphics/Bitmap.cpp | 10 | ||||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 7 | ||||
-rw-r--r-- | libs/hwui/TextureCache.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/TextureCache.h | 7 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 39 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 1 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java | 4 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java | 1 |
8 files changed, 70 insertions, 3 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index d68124660a51..d7550a4d9695 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -18,6 +18,7 @@ #include "CreateJavaOutputStreamAdaptor.h" #include <Caches.h> #include <hwui/Paint.h> +#include <renderthread/RenderProxy.h> #include "core_jni_helpers.h" @@ -1361,6 +1362,14 @@ static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { return reinterpret_cast<jlong>(pixelRef); } +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + if (!bitmapHandle.valid()) return; + SkBitmap bitmap; + bitmapHandle->getSkBitmap(&bitmap); + android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { @@ -1404,6 +1413,7 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, }; int register_android_graphics_Bitmap(JNIEnv* env) diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 1fdc1f575bd4..49721cf42666 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1667,10 +1667,10 @@ public final class Bitmap implements Parcelable { * and therefore is harmless. */ public void prepareToDraw() { - // TODO: Consider having this start an async upload? - // With inPurgeable no-op'd there's currently no use for this - // method, but it could have interesting future uses. checkRecycled("Can't prepareToDraw on a recycled bitmap!"); + // Kick off an update/upload of the bitmap outside of the normal + // draw path. + nativePrepareToDraw(mNativePtr); } /** @@ -1741,4 +1741,5 @@ public final class Bitmap implements Parcelable { private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap); private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1); private static native long nativeRefPixelRef(long nativeBitmap); + private static native void nativePrepareToDraw(long nativeBitmap); } diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index ade8600ab78b..523924af5ef1 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -167,6 +167,10 @@ bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap return texture; } +bool TextureCache::prefetch(const SkBitmap* bitmap) { + return getCachedTexture(bitmap, AtlasUsageType::Use); +} + Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { Texture* texture = getCachedTexture(bitmap, atlasUsageType); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index a4317cee73fd..0a61b6b1a522 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -78,6 +78,13 @@ public: bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap); /** + * Attempts to precache the SkBitmap. Returns true if a Texture was successfully + * acquired for the bitmap, false otherwise. Does not mark the Texture + * as in use and won't update currently in-use Textures. + */ + bool prefetch(const SkBitmap* bitmap); + + /** * Returns the texture associated with the specified bitmap from either within the cache, or * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated. */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 06a24b248bf6..fb1c8962328b 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -22,9 +22,11 @@ #include "Readback.h" #include "Rect.h" #include "renderthread/CanvasContext.h" +#include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/Macros.h" +#include "utils/TimeUtils.h" namespace android { namespace uirenderer { @@ -44,6 +46,8 @@ namespace renderthread { typedef struct { \ a1; a2; a3; a4; a5; a6; a7; a8; \ } ARGS(name); \ + static_assert(std::is_trivially_destructible<ARGS(name)>::value, \ + "Error, ARGS must be trivially destructible!"); \ static void* Bridge_ ## name(ARGS(name)* args) #define SETUP_TASK(method) \ @@ -636,6 +640,41 @@ int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) { reinterpret_cast<intptr_t>( staticPostAndWait(task) )); } +CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) { + if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) { + ATRACE_NAME("Bitmap#prepareToDraw task"); + Caches::getInstance().textureCache.prefetch(args->bitmap); + } + delete args->bitmap; + args->bitmap = nullptr; + return nullptr; +} + +void RenderProxy::prepareToDraw(const SkBitmap& bitmap) { + // If we haven't spun up a hardware accelerated window yet, there's no + // point in precaching these bitmaps as it can't impact jank. + // We also don't know if we even will spin up a hardware-accelerated + // window or not. + if (!RenderThread::hasInstance()) return; + RenderThread* renderThread = &RenderThread::getInstance(); + SETUP_TASK(prepareToDraw); + args->thread = renderThread; + args->bitmap = new SkBitmap(bitmap); + nsecs_t lastVsync = renderThread->timeLord().latestVsync(); + nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); + nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); + // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to + // VSYNC+12ms or so, so aim for the gap during which RT is expected to + // be idle + // TODO: Make this concept a first-class supported thing? RT could use + // knowledge of pending draws to better schedule this task + if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) { + renderThread->queueAt(task, estimatedNextVsync + 8_ms); + } else { + renderThread->queue(task); + } +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index e31062c83734..bb111bd0de25 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -129,6 +129,7 @@ public: ANDROID_API long getDroppedFrameReportCount(); ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap); + ANDROID_API static void prepareToDraw(const SkBitmap& bitmap); private: RenderThread& mRenderThread; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index ca59831786b4..ba31e3e835c0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -200,6 +200,10 @@ class BackgroundTaskLoader implements Runnable { if (cachedThumbnailData.thumbnail == null) { cachedThumbnailData.thumbnail = mDefaultThumbnail; + } else { + // Kick off an early upload of the bitmap to GL so + // that this won't jank the first frame it's drawn in. + cachedThumbnailData.thumbnail.prepareToDraw(); } // When svelte, we trim the memory to just the visible thumbnails when diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 319375950916..c46adf15861f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -164,6 +164,7 @@ public class TaskViewThumbnail extends View { /** Sets the thumbnail to a given bitmap. */ void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) { if (bm != null) { + bm.prepareToDraw(); mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mDrawPaint.setShader(mBitmapShader); mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight()); |