Index: content/browser/renderer_host/render_widget_host_view_android.cc |
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc |
index e9af1c07fcbb6af60f8ed85627206601a9b94af2..3c7de59f07aa8d434966576bfadd6c21bf8aa02d 100644 |
--- a/content/browser/renderer_host/render_widget_host_view_android.cc |
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc |
@@ -8,6 +8,7 @@ |
#include <utility> |
+#include "base/android/application_status_listener.h" |
#include "base/android/build_info.h" |
#include "base/android/context_utils.h" |
#include "base/bind.h" |
@@ -16,6 +17,7 @@ |
#include "base/location.h" |
#include "base/logging.h" |
#include "base/macros.h" |
+#include "base/memory/ref_counted.h" |
#include "base/metrics/histogram_macros.h" |
#include "base/single_thread_task_runner.h" |
#include "base/strings/utf_string_conversions.h" |
@@ -100,6 +102,29 @@ const int kUndefinedCompositorFrameSinkId = -1; |
static const char kAsyncReadBackString[] = "Compositing.CopyFromSurfaceTime"; |
+class PendingReadbackLock; |
+ |
+PendingReadbackLock* g_pending_readback_lock = nullptr; |
+ |
+class PendingReadbackLock : public base::RefCounted<PendingReadbackLock> { |
+ public: |
+ PendingReadbackLock() { |
+ DCHECK_EQ(g_pending_readback_lock, nullptr); |
+ g_pending_readback_lock = this; |
+ } |
+ |
+ private: |
+ friend class base::RefCounted<PendingReadbackLock>; |
+ |
+ ~PendingReadbackLock() { |
+ DCHECK_EQ(g_pending_readback_lock, this); |
+ g_pending_readback_lock = nullptr; |
+ } |
+}; |
+ |
+using base::android::ApplicationState; |
+using base::android::ApplicationStatusListener; |
+ |
class GLHelperHolder { |
public: |
static GLHelperHolder* Create(); |
@@ -111,17 +136,32 @@ class GLHelperHolder { |
return provider_->ContextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR; |
} |
+ void ReleaseIfPossible(); |
+ |
private: |
- GLHelperHolder() = default; |
+ GLHelperHolder(); |
void Initialize(); |
void OnContextLost(); |
+ void OnApplicationStatusChanged(ApplicationState new_state); |
scoped_refptr<ContextProviderCommandBuffer> provider_; |
std::unique_ptr<display_compositor::GLHelper> gl_helper_; |
+ // Set to |false| if there are only stopped activities (or none). |
+ bool has_running_or_paused_activities_; |
+ |
+ std::unique_ptr<ApplicationStatusListener> app_status_listener_; |
+ |
DISALLOW_COPY_AND_ASSIGN(GLHelperHolder); |
}; |
+GLHelperHolder::GLHelperHolder() |
+ : has_running_or_paused_activities_( |
+ (ApplicationStatusListener::GetState() == |
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) || |
+ (ApplicationStatusListener::GetState() == |
+ base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES)) {} |
+ |
GLHelperHolder* GLHelperHolder::Create() { |
GLHelperHolder* holder = new GLHelperHolder; |
holder->Initialize(); |
@@ -184,20 +224,43 @@ void GLHelperHolder::Initialize() { |
base::Bind(&GLHelperHolder::OnContextLost, base::Unretained(this))); |
gl_helper_.reset(new display_compositor::GLHelper( |
provider_->ContextGL(), provider_->ContextSupport())); |
+ |
+ // Unretained() is safe because |this| owns the following two callbacks. |
+ app_status_listener_.reset(new ApplicationStatusListener(base::Bind( |
+ &GLHelperHolder::OnApplicationStatusChanged, base::Unretained(this)))); |
+} |
+ |
+void GLHelperHolder::ReleaseIfPossible() { |
+ if (!has_running_or_paused_activities_ && !g_pending_readback_lock) { |
+ gl_helper_.reset(); |
+ provider_ = nullptr; |
+ // Make sure this will get recreated on next use. |
+ DCHECK(IsLost()); |
+ } |
} |
void GLHelperHolder::OnContextLost() { |
+ app_status_listener_.reset(); |
+ gl_helper_.reset(); |
// Need to post a task because the command buffer client cannot be deleted |
// from within this callback. |
base::ThreadTaskRunnerHandle::Get()->PostTask( |
FROM_HERE, base::Bind(&RenderWidgetHostViewAndroid::OnContextLost)); |
} |
-// This can only be used for readback postprocessing. It may return null if the |
-// channel was lost and not reestablished yet. |
-display_compositor::GLHelper* GetPostReadbackGLHelper() { |
+void GLHelperHolder::OnApplicationStatusChanged(ApplicationState new_state) { |
+ has_running_or_paused_activities_ = |
+ new_state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES || |
+ new_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES; |
+ ReleaseIfPossible(); |
+} |
+ |
+GLHelperHolder* GetPostReadbackGLHelperHolder(bool create_if_necessary) { |
static GLHelperHolder* g_readback_helper_holder = nullptr; |
+ if (!create_if_necessary && !g_readback_helper_holder) |
+ return nullptr; |
+ |
if (g_readback_helper_holder && g_readback_helper_holder->IsLost()) { |
delete g_readback_helper_holder; |
g_readback_helper_holder = nullptr; |
@@ -206,7 +269,21 @@ display_compositor::GLHelper* GetPostReadbackGLHelper() { |
if (!g_readback_helper_holder) |
g_readback_helper_holder = GLHelperHolder::Create(); |
- return g_readback_helper_holder->gl_helper(); |
+ return g_readback_helper_holder; |
+} |
+ |
+display_compositor::GLHelper* GetPostReadbackGLHelper() { |
+ bool create_if_necessary = true; |
+ return GetPostReadbackGLHelperHolder(create_if_necessary)->gl_helper(); |
+} |
+ |
+void ReleaseGLHelper() { |
+ bool create_if_necessary = false; |
+ GLHelperHolder* holder = GetPostReadbackGLHelperHolder(create_if_necessary); |
+ |
+ if (holder) { |
+ holder->ReleaseIfPossible(); |
+ } |
} |
void CopyFromCompositingSurfaceFinished( |
@@ -215,6 +292,7 @@ void CopyFromCompositingSurfaceFinished( |
std::unique_ptr<SkBitmap> bitmap, |
const base::TimeTicks& start_time, |
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock, |
+ scoped_refptr<PendingReadbackLock> readback_lock, |
bool result) { |
TRACE_EVENT0( |
"cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceFinished"); |
@@ -225,6 +303,12 @@ void CopyFromCompositingSurfaceFinished( |
if (gl_helper) |
gl_helper->GenerateSyncToken(&sync_token); |
} |
+ |
+ // PostTask() to make sure the |readback_lock| is released. Also do this |
+ // synchronous GPU operation in a clean callstack. |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ReleaseGLHelper)); |
+ |
const bool lost_resource = !sync_token.HasData(); |
release_callback->Run(sync_token, lost_resource); |
UMA_HISTOGRAM_TIMES(kAsyncReadBackString, |
@@ -268,7 +352,67 @@ gfx::RectF GetSelectionRect(const ui::TouchSelectionController& controller) { |
return rect; |
} |
-} // anonymous namespace |
+// TODO(wjmaclean): There is significant overlap between |
+// PrepareTextureCopyOutputResult and CopyFromCompositingSurfaceFinished in |
+// this file, and the versions in surface_utils.cc. They should |
+// be merged. See https://crbug.com/582955 |
+void PrepareTextureCopyOutputResult( |
+ const gfx::Size& dst_size_in_pixel, |
+ SkColorType color_type, |
+ const base::TimeTicks& start_time, |
+ const ReadbackRequestCallback& callback, |
+ scoped_refptr<PendingReadbackLock> readback_lock, |
+ std::unique_ptr<cc::CopyOutputResult> result) { |
+ base::ScopedClosureRunner scoped_callback_runner( |
+ base::Bind(callback, SkBitmap(), READBACK_FAILED)); |
+ TRACE_EVENT0("cc", |
+ "RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult"); |
+ if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty()) |
+ return; |
+ cc::TextureMailbox texture_mailbox; |
+ std::unique_ptr<cc::SingleReleaseCallback> release_callback; |
+ result->TakeTexture(&texture_mailbox, &release_callback); |
+ DCHECK(texture_mailbox.IsTexture()); |
+ if (!texture_mailbox.IsTexture()) |
+ return; |
+ display_compositor::GLHelper* gl_helper = GetPostReadbackGLHelper(); |
+ if (!gl_helper) |
+ return; |
+ if (!gl_helper->IsReadbackConfigSupported(color_type)) |
+ color_type = kRGBA_8888_SkColorType; |
+ |
+ gfx::Size output_size_in_pixel; |
+ if (dst_size_in_pixel.IsEmpty()) |
+ output_size_in_pixel = result->size(); |
+ else |
+ output_size_in_pixel = dst_size_in_pixel; |
+ |
+ std::unique_ptr<SkBitmap> bitmap(new SkBitmap); |
+ if (!bitmap->tryAllocPixels(SkImageInfo::Make(output_size_in_pixel.width(), |
+ output_size_in_pixel.height(), |
+ color_type, |
+ kOpaque_SkAlphaType))) { |
+ scoped_callback_runner.ReplaceClosure( |
+ base::Bind(callback, SkBitmap(), READBACK_BITMAP_ALLOCATION_FAILURE)); |
+ return; |
+ } |
+ |
+ std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock( |
+ new SkAutoLockPixels(*bitmap)); |
+ uint8_t* pixels = static_cast<uint8_t*>(bitmap->getPixels()); |
+ |
+ ignore_result(scoped_callback_runner.Release()); |
+ |
+ gl_helper->CropScaleReadbackAndCleanMailbox( |
+ texture_mailbox.mailbox(), texture_mailbox.sync_token(), result->size(), |
+ gfx::Rect(result->size()), output_size_in_pixel, pixels, color_type, |
+ base::Bind(&CopyFromCompositingSurfaceFinished, callback, |
+ base::Passed(&release_callback), base::Passed(&bitmap), |
+ start_time, base::Passed(&bitmap_pixels_lock), readback_lock), |
+ display_compositor::GLHelper::SCALER_QUALITY_GOOD); |
+} |
+ |
+} // namespace |
RenderWidgetHostViewAndroid::LastFrameInfo::LastFrameInfo( |
uint32_t compositor_frame_sink_id, |
@@ -864,10 +1008,13 @@ void RenderWidgetHostViewAndroid::CopyFromCompositingSurface( |
content_view_core_->GetWindowAndroid()->GetCompositor(); |
DCHECK(compositor); |
DCHECK(delegated_frame_host_); |
+ scoped_refptr<PendingReadbackLock> readback_lock( |
+ g_pending_readback_lock ? g_pending_readback_lock |
+ : new PendingReadbackLock); |
delegated_frame_host_->RequestCopyOfSurface( |
compositor, src_subrect_in_pixel, |
base::Bind(&PrepareTextureCopyOutputResult, dst_size_in_pixel, |
- preferred_color_type, start_time, callback)); |
+ preferred_color_type, start_time, callback, readback_lock)); |
} |
void RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceToVideoFrame( |
@@ -1791,67 +1938,6 @@ void RenderWidgetHostViewAndroid::OnLostResources() { |
DCHECK(ack_callbacks_.empty()); |
} |
-// TODO(wjmaclean): There is significant overlap between |
-// PrepareTextureCopyOutputResult and CopyFromCompositingSurfaceFinished in |
-// this file, and the versions in surface_utils.cc. They should |
-// be merged. See https://crbug.com/582955 |
- |
-// static |
-void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult( |
- const gfx::Size& dst_size_in_pixel, |
- SkColorType color_type, |
- const base::TimeTicks& start_time, |
- const ReadbackRequestCallback& callback, |
- std::unique_ptr<cc::CopyOutputResult> result) { |
- base::ScopedClosureRunner scoped_callback_runner( |
- base::Bind(callback, SkBitmap(), READBACK_FAILED)); |
- TRACE_EVENT0("cc", |
- "RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult"); |
- if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty()) |
- return; |
- cc::TextureMailbox texture_mailbox; |
- std::unique_ptr<cc::SingleReleaseCallback> release_callback; |
- result->TakeTexture(&texture_mailbox, &release_callback); |
- DCHECK(texture_mailbox.IsTexture()); |
- if (!texture_mailbox.IsTexture()) |
- return; |
- display_compositor::GLHelper* gl_helper = GetPostReadbackGLHelper(); |
- if (!gl_helper) |
- return; |
- if (!gl_helper->IsReadbackConfigSupported(color_type)) |
- color_type = kRGBA_8888_SkColorType; |
- |
- gfx::Size output_size_in_pixel; |
- if (dst_size_in_pixel.IsEmpty()) |
- output_size_in_pixel = result->size(); |
- else |
- output_size_in_pixel = dst_size_in_pixel; |
- |
- std::unique_ptr<SkBitmap> bitmap(new SkBitmap); |
- if (!bitmap->tryAllocPixels(SkImageInfo::Make(output_size_in_pixel.width(), |
- output_size_in_pixel.height(), |
- color_type, |
- kOpaque_SkAlphaType))) { |
- scoped_callback_runner.ReplaceClosure( |
- base::Bind(callback, SkBitmap(), READBACK_BITMAP_ALLOCATION_FAILURE)); |
- return; |
- } |
- |
- std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock( |
- new SkAutoLockPixels(*bitmap)); |
- uint8_t* pixels = static_cast<uint8_t*>(bitmap->getPixels()); |
- |
- ignore_result(scoped_callback_runner.Release()); |
- |
- gl_helper->CropScaleReadbackAndCleanMailbox( |
- texture_mailbox.mailbox(), texture_mailbox.sync_token(), result->size(), |
- gfx::Rect(result->size()), output_size_in_pixel, pixels, color_type, |
- base::Bind(&CopyFromCompositingSurfaceFinished, callback, |
- base::Passed(&release_callback), base::Passed(&bitmap), |
- start_time, base::Passed(&bitmap_pixels_lock)), |
- display_compositor::GLHelper::SCALER_QUALITY_GOOD); |
-} |
- |
void RenderWidgetHostViewAndroid::OnStylusSelectBegin(float x0, |
float y0, |
float x1, |