summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author John Reck <jreck@google.com> 2016-08-01 14:39:24 -0700
committer John Reck <jreck@google.com> 2016-08-01 16:36:14 -0700
commit4387190d8ec9fe4e953fcfeb093a644b82cf85ed (patch)
treec717cbed2691386f68b6c97ba09d64ac85137871
parentfbd93027e4f8024642271652e490653a4d0c2b55 (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-xcore/jni/android/graphics/Bitmap.cpp10
-rw-r--r--graphics/java/android/graphics/Bitmap.java7
-rw-r--r--libs/hwui/TextureCache.cpp4
-rw-r--r--libs/hwui/TextureCache.h7
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp39
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java1
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());