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
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
#include "CreateJavaOutputStreamAdaptor.h"
|
#include "CreateJavaOutputStreamAdaptor.h"
|
||||||
#include <Caches.h>
|
#include <Caches.h>
|
||||||
#include <hwui/Paint.h>
|
#include <hwui/Paint.h>
|
||||||
|
#include <renderthread/RenderProxy.h>
|
||||||
|
|
||||||
#include "core_jni_helpers.h"
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
@@ -1361,6 +1362,14 @@ static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
|
|||||||
return reinterpret_cast<jlong>(pixelRef);
|
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[] = {
|
static const JNINativeMethod gBitmapMethods[] = {
|
||||||
@@ -1404,6 +1413,7 @@ static const JNINativeMethod gBitmapMethods[] = {
|
|||||||
(void*)Bitmap_copyPixelsFromBuffer },
|
(void*)Bitmap_copyPixelsFromBuffer },
|
||||||
{ "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
|
{ "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
|
||||||
{ "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef },
|
{ "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef },
|
||||||
|
{ "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_graphics_Bitmap(JNIEnv* env)
|
int register_android_graphics_Bitmap(JNIEnv* env)
|
||||||
|
|||||||
@@ -1667,10 +1667,10 @@ public final class Bitmap implements Parcelable {
|
|||||||
* and therefore is harmless.
|
* and therefore is harmless.
|
||||||
*/
|
*/
|
||||||
public void prepareToDraw() {
|
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!");
|
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 void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
|
||||||
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
|
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
|
||||||
private static native long nativeRefPixelRef(long nativeBitmap);
|
private static native long nativeRefPixelRef(long nativeBitmap);
|
||||||
|
private static native void nativePrepareToDraw(long nativeBitmap);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,10 @@ bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TextureCache::prefetch(const SkBitmap* bitmap) {
|
||||||
|
return getCachedTexture(bitmap, AtlasUsageType::Use);
|
||||||
|
}
|
||||||
|
|
||||||
Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
|
Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
|
||||||
Texture* texture = getCachedTexture(bitmap, atlasUsageType);
|
Texture* texture = getCachedTexture(bitmap, atlasUsageType);
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap);
|
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
|
* 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.
|
* the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated.
|
||||||
|
|||||||
@@ -22,9 +22,11 @@
|
|||||||
#include "Readback.h"
|
#include "Readback.h"
|
||||||
#include "Rect.h"
|
#include "Rect.h"
|
||||||
#include "renderthread/CanvasContext.h"
|
#include "renderthread/CanvasContext.h"
|
||||||
|
#include "renderthread/EglManager.h"
|
||||||
#include "renderthread/RenderTask.h"
|
#include "renderthread/RenderTask.h"
|
||||||
#include "renderthread/RenderThread.h"
|
#include "renderthread/RenderThread.h"
|
||||||
#include "utils/Macros.h"
|
#include "utils/Macros.h"
|
||||||
|
#include "utils/TimeUtils.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace uirenderer {
|
namespace uirenderer {
|
||||||
@@ -44,6 +46,8 @@ namespace renderthread {
|
|||||||
typedef struct { \
|
typedef struct { \
|
||||||
a1; a2; a3; a4; a5; a6; a7; a8; \
|
a1; a2; a3; a4; a5; a6; a7; a8; \
|
||||||
} ARGS(name); \
|
} ARGS(name); \
|
||||||
|
static_assert(std::is_trivially_destructible<ARGS(name)>::value, \
|
||||||
|
"Error, ARGS must be trivially destructible!"); \
|
||||||
static void* Bridge_ ## name(ARGS(name)* args)
|
static void* Bridge_ ## name(ARGS(name)* args)
|
||||||
|
|
||||||
#define SETUP_TASK(method) \
|
#define SETUP_TASK(method) \
|
||||||
@@ -636,6 +640,41 @@ int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
|
|||||||
reinterpret_cast<intptr_t>( staticPostAndWait(task) ));
|
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) {
|
void RenderProxy::post(RenderTask* task) {
|
||||||
mRenderThread.queue(task);
|
mRenderThread.queue(task);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ public:
|
|||||||
ANDROID_API long getDroppedFrameReportCount();
|
ANDROID_API long getDroppedFrameReportCount();
|
||||||
|
|
||||||
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
|
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
|
||||||
|
ANDROID_API static void prepareToDraw(const SkBitmap& bitmap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderThread& mRenderThread;
|
RenderThread& mRenderThread;
|
||||||
|
|||||||
@@ -200,6 +200,10 @@ class BackgroundTaskLoader implements Runnable {
|
|||||||
|
|
||||||
if (cachedThumbnailData.thumbnail == null) {
|
if (cachedThumbnailData.thumbnail == null) {
|
||||||
cachedThumbnailData.thumbnail = mDefaultThumbnail;
|
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
|
// When svelte, we trim the memory to just the visible thumbnails when
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public class TaskViewThumbnail extends View {
|
|||||||
/** Sets the thumbnail to a given bitmap. */
|
/** Sets the thumbnail to a given bitmap. */
|
||||||
void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
|
void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
|
||||||
if (bm != null) {
|
if (bm != null) {
|
||||||
|
bm.prepareToDraw();
|
||||||
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||||
mDrawPaint.setShader(mBitmapShader);
|
mDrawPaint.setShader(mBitmapShader);
|
||||||
mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
|
mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
|
||||||
|
|||||||
Reference in New Issue
Block a user