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