Commit e0b037c2 authored by Wei-Yin Chen (陳威尹)'s avatar Wei-Yin Chen (陳威尹) Committed by Commit Bot

Optimize thumbnail capturing for Tab-to-Grid transition animation

Split the forced live thumbnail capturing out of the normal caching
path. The forced live thumbnail is captured as low-res, and won't be
saved to the cache. This makes it faster so that using it during
the Tab-to-Grid transition animation won't affect the frame rate too
much.

Bug: 968829
Change-Id: I32ffa8553b5aefb29944781404292a8b891d8d80
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1637979
Commit-Queue: Wei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#665691}
parent ce03dbf9
......@@ -525,7 +525,6 @@ class TabListMediator {
mModel.get(i).set(TabProperties.IS_SELECTED, isSelected);
if (mThumbnailProvider != null && isSelected) {
// TODO(crbug.com/968829): make force updating faster.
ThumbnailFetcher callback = new ThumbnailFetcher(mThumbnailProvider, tab, true);
mModel.get(i).set(TabProperties.THUMBNAIL_FETCHER, callback);
}
......
......@@ -11,7 +11,6 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
......@@ -268,7 +267,7 @@ public class TabContentManager {
* @param forceUpdate Whether to obtain the thumbnail from the live content.
*/
public void getTabThumbnailWithCallback(
Tab tab, Callback<Bitmap> callback, boolean forceUpdate) {
@NonNull Tab tab, @NonNull Callback<Bitmap> callback, boolean forceUpdate) {
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
if (!forceUpdate) {
......@@ -280,11 +279,11 @@ public class TabContentManager {
// that first even if |forceUpdate|.
nativeGetTabThumbnailWithCallback(mNativeTabContentManager, tab.getId(), (diskBitmap) -> {
callback.onResult(diskBitmap);
cacheTabThumbnail(tab, (bitmap) -> {
captureDownsampledThumbnail(tab, (bitmap) -> {
// Null check to avoid having a Bitmap from nativeGetTabThumbnailWithCallback() but
// cleared here.
// If invalidation is not needed, cacheTabThumbnail() might not do anything and
// send back null.
// If invalidation is not needed, captureDownsampledThumbnail() might not do
// anything and send back null.
if (bitmap != null) {
callback.onResult(bitmap);
}
......@@ -297,40 +296,58 @@ public class TabContentManager {
* @param tab The tab whose content we will cache.
*/
public void cacheTabThumbnail(@NonNull final Tab tab) {
cacheTabThumbnail(tab, null);
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
if (tab.getNativePage() != null || isNativeViewShowing(tab)) {
cacheNativeTabThumbnail(tab);
} else {
if (tab.getWebContents() == null) return;
nativeCacheTab(mNativeTabContentManager, tab, mThumbnailScale);
}
}
private Bitmap cacheNativeTabThumbnail(final Tab tab) {
assert tab.getNativePage() != null || isNativeViewShowing(tab);
Bitmap nativeBitmap = readbackNativeBitmap(tab, mThumbnailScale);
if (nativeBitmap == null) return null;
nativeCacheTabWithBitmap(mNativeTabContentManager, tab, nativeBitmap, mThumbnailScale);
return nativeBitmap;
}
/**
* Cache the content of a tab as a thumbnail.
* @param tab The tab whose content we will cache.
* Capture the downsampled content of a tab as a thumbnail.
* @param tab The tab whose content we will capture.
* @param callback The callback to send the {@link Bitmap} with.
*/
public void cacheTabThumbnail(@NonNull final Tab tab, @Nullable Callback<Bitmap> callback) {
if (mNativeTabContentManager != 0 && mSnapshotsEnabled) {
private void captureDownsampledThumbnail(final Tab tab, @NonNull Callback<Bitmap> callback) {
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
final float downsamplingScale = 0.5f;
if (tab.getNativePage() != null || isNativeViewShowing(tab)) {
Bitmap nativeBitmap = readbackNativeBitmap(tab, mThumbnailScale);
if (nativeBitmap == null) {
if (callback != null) callback.onResult(null);
// If we use readbackNativeBitmap() with a downsampled scale and not saving it through
// nativeCacheTabWithBitmap(), the logic of InvalidationAwareThumbnailProvider
// might prevent cacheTabThumbnail() from getting the latest thumbnail.
// Therefore, we have to also call cacheNativeTabThumbnail(), and do the downsampling
// here ourselves. This is less efficient than capturing a downsampled bitmap, but
// the performance here is not the bottleneck.
Bitmap bitmap = cacheNativeTabThumbnail(tab);
if (bitmap == null) {
callback.onResult(null);
return;
}
nativeCacheTabWithBitmap(
mNativeTabContentManager, tab, nativeBitmap, mThumbnailScale);
if (callback != null) {
// In portrait mode, we want to show thumbnails in squares.
// Therefore, the thumbnail saved in portrait mode needs to be cropped to
// a square, or it would become too tall and break the layout.
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
Bitmap resized = Bitmap.createBitmap(nativeBitmap, 0, 0,
nativeBitmap.getWidth(),
min(nativeBitmap.getWidth(), nativeBitmap.getHeight()), matrix, true);
matrix.setScale(downsamplingScale, downsamplingScale);
Bitmap resized = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
min(bitmap.getWidth(), bitmap.getHeight()), matrix, true);
callback.onResult(resized);
}
nativeBitmap.recycle();
} else {
if (tab.getWebContents() == null) return;
nativeCacheTab(mNativeTabContentManager, tab, mThumbnailScale, callback);
}
nativeCaptureThumbnail(
mNativeTabContentManager, tab, mThumbnailScale * downsamplingScale, callback);
}
}
......@@ -402,8 +419,10 @@ public class TabContentManager {
private native void nativeAttachTab(long nativeTabContentManager, Tab tab, int tabId);
private native void nativeDetachTab(long nativeTabContentManager, Tab tab, int tabId);
private native boolean nativeHasFullCachedThumbnail(long nativeTabContentManager, int tabId);
private native void nativeCacheTab(long nativeTabContentManager, Object tab,
private native void nativeCaptureThumbnail(long nativeTabContentManager, Object tab,
float thumbnailScale, Callback<Bitmap> callback);
private native void nativeCacheTab(
long nativeTabContentManager, Object tab, float thumbnailScale);
private native void nativeCacheTabWithBitmap(long nativeTabContentManager, Object tab,
Object bitmap, float thumbnailScale);
private native void nativeInvalidateIfChanged(long nativeTabContentManager, int tabId,
......
......@@ -243,14 +243,10 @@ content::RenderWidgetHostView* TabContentManager::GetRwhvForTab(
if (!rwhv || !rwhv->IsSurfaceAvailableForCopy())
return nullptr;
if (!thumbnail_cache_->CheckAndUpdateThumbnailMetaData(tab_id,
tab_android->GetURL()))
return nullptr;
return rwhv;
}
void TabContentManager::CacheTab(
void TabContentManager::CaptureThumbnail(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& tab,
......@@ -268,12 +264,34 @@ void TabContentManager::CacheTab(
const int tab_id = tab_android->GetAndroidId();
TabReadbackCallback readback_done_callback = base::BindOnce(
&TabContentManager::PutThumbnailIntoCache, weak_factory_.GetWeakPtr(),
&TabContentManager::SendThumbnailToJava, weak_factory_.GetWeakPtr(),
tab_id, base::android::ScopedJavaGlobalRef<jobject>(j_callback));
pending_tab_readbacks_[tab_id] = std::make_unique<TabReadbackRequest>(
rwhv, thumbnail_scale, std::move(readback_done_callback));
}
void TabContentManager::CacheTab(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& tab,
jfloat thumbnail_scale) {
content::RenderWidgetHostView* rwhv = GetRwhvForTab(env, obj, tab);
if (!rwhv) {
return;
}
TabAndroid* tab_android = TabAndroid::GetNativeTab(env, tab);
DCHECK(tab_android);
const int tab_id = tab_android->GetAndroidId();
if (!thumbnail_cache_->CheckAndUpdateThumbnailMetaData(
tab_id, tab_android->GetURL())) {
return;
}
TabReadbackCallback readback_done_callback =
base::BindOnce(&TabContentManager::PutThumbnailIntoCache,
weak_factory_.GetWeakPtr(), tab_id);
pending_tab_readbacks_[tab_id] = std::make_unique<TabReadbackRequest>(
rwhv, thumbnail_scale, std::move(readback_done_callback));
}
void TabContentManager::CacheTabWithBitmap(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& tab,
......@@ -289,7 +307,7 @@ void TabContentManager::CacheTabWithBitmap(JNIEnv* env,
skbitmap.setImmutable();
if (thumbnail_cache_->CheckAndUpdateThumbnailMetaData(tab_id, url))
PutThumbnailIntoCache(tab_id, nullptr, thumbnail_scale, skbitmap);
PutThumbnailIntoCache(tab_id, thumbnail_scale, skbitmap);
}
void TabContentManager::InvalidateIfChanged(JNIEnv* env,
......@@ -338,7 +356,8 @@ void TabContentManager::GetTabThumbnailWithCallback(
tab_id,
base::BindRepeating(
&TabContentManager::TabThumbnailAvailable, weak_factory_.GetWeakPtr(),
base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
base::android::ScopedJavaGlobalRef<jobject>(j_callback),
/* need_downsampling */ true));
}
void TabContentManager::OnUIResourcesWereEvicted() {
......@@ -351,7 +370,20 @@ void TabContentManager::OnFinishedThumbnailRead(int tab_id) {
env, weak_java_tab_content_manager_.get(env), tab_id);
}
void TabContentManager::PutThumbnailIntoCache(
void TabContentManager::PutThumbnailIntoCache(int tab_id,
float thumbnail_scale,
const SkBitmap& bitmap) {
TabReadbackRequestMap::iterator readback_iter =
pending_tab_readbacks_.find(tab_id);
if (readback_iter != pending_tab_readbacks_.end())
pending_tab_readbacks_.erase(tab_id);
if (thumbnail_scale > 0 && !bitmap.empty())
thumbnail_cache_->Put(tab_id, bitmap, thumbnail_scale);
}
void TabContentManager::SendThumbnailToJava(
int tab_id,
base::android::ScopedJavaGlobalRef<jobject> j_callback,
float thumbnail_scale,
......@@ -363,15 +395,15 @@ void TabContentManager::PutThumbnailIntoCache(
pending_tab_readbacks_.erase(tab_id);
if (j_callback) {
TabThumbnailAvailable(j_callback, true, bitmap);
TabThumbnailAvailable(j_callback, /*need_downsampling */ false, true,
bitmap);
return;
}
if (thumbnail_scale > 0 && !bitmap.empty())
thumbnail_cache_->Put(tab_id, bitmap, thumbnail_scale);
}
void TabContentManager::TabThumbnailAvailable(
base::android::ScopedJavaGlobalRef<jobject> j_callback,
bool need_downsampling,
bool result,
SkBitmap bitmap) {
ScopedJavaLocalRef<jobject> j_bitmap;
......@@ -382,11 +414,12 @@ void TabContentManager::TabThumbnailAvailable(
// be hidden.
// It's fine to horizontally center-align thumbnail saved in landscape
// mode.
SkIRect dest_subset = {0, 0, bitmap.width() / 2,
std::min(bitmap.width(), bitmap.height()) / 2};
int scale = need_downsampling ? 2 : 1;
SkIRect dest_subset = {0, 0, bitmap.width() / scale,
std::min(bitmap.width(), bitmap.height()) / scale};
SkBitmap result_bitmap = skia::ImageOperations::Resize(
bitmap, skia::ImageOperations::RESIZE_BETTER, bitmap.width() / 2,
bitmap.height() / 2, dest_subset);
bitmap, skia::ImageOperations::RESIZE_BETTER, bitmap.width() / scale,
bitmap.height() / scale, dest_subset);
j_bitmap = gfx::ConvertToJavaBitmap(&result_bitmap);
}
RunObjectCallbackAndroid(j_callback, j_bitmap);
......
......@@ -81,11 +81,15 @@ class TabContentManager : public ThumbnailCacheObserver {
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint tab_id);
void CacheTab(JNIEnv* env,
void CaptureThumbnail(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& tab,
jfloat thumbnail_scale,
const base::android::JavaParamRef<jobject>& j_callback);
void CacheTab(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& tab,
jfloat thumbnail_scale);
void CacheTabWithBitmap(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& tab,
......@@ -126,7 +130,10 @@ class TabContentManager : public ThumbnailCacheObserver {
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& tab);
void PutThumbnailIntoCache(
void PutThumbnailIntoCache(int tab_id,
float thumbnail_scale,
const SkBitmap& bitmap);
void SendThumbnailToJava(
int tab_id,
base::android::ScopedJavaGlobalRef<jobject> j_callback,
float thumbnail_scale,
......@@ -134,6 +141,7 @@ class TabContentManager : public ThumbnailCacheObserver {
void TabThumbnailAvailable(
base::android::ScopedJavaGlobalRef<jobject> j_callback,
bool need_downsampling,
bool result,
SkBitmap bitmap);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment