// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "android_webview/browser/shared_renderer_state.h" #include "android_webview/browser/browser_view_renderer.h" #include "android_webview/browser/child_frame.h" #include "android_webview/browser/deferred_gpu_command_service.h" #include "android_webview/browser/hardware_renderer.h" #include "android_webview/browser/scoped_app_gl_state_restore.h" #include "android_webview/public/browser/draw_gl.h" #include "base/bind.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/trace_event/trace_event_argument.h" namespace android_webview { namespace internal { class RequestDrawGLTracker { public: RequestDrawGLTracker(); bool ShouldRequestOnNonUiThread(SharedRendererState* state); bool ShouldRequestOnUiThread(SharedRendererState* state); void ResetPending(); void SetQueuedFunctorOnUi(SharedRendererState* state); private: base::Lock lock_; SharedRendererState* pending_ui_; SharedRendererState* pending_non_ui_; }; RequestDrawGLTracker::RequestDrawGLTracker() : pending_ui_(NULL), pending_non_ui_(NULL) { } bool RequestDrawGLTracker::ShouldRequestOnNonUiThread( SharedRendererState* state) { base::AutoLock lock(lock_); if (pending_ui_ || pending_non_ui_) return false; pending_non_ui_ = state; return true; } bool RequestDrawGLTracker::ShouldRequestOnUiThread(SharedRendererState* state) { base::AutoLock lock(lock_); if (pending_non_ui_) { pending_non_ui_->ResetRequestDrawGLCallback(); pending_non_ui_ = NULL; } // At this time, we could have already called RequestDrawGL on the UI thread, // but the corresponding GL mode process hasn't happened yet. In this case, // don't schedule another requestDrawGL on the UI thread. if (pending_ui_) return false; pending_ui_ = state; return true; } void RequestDrawGLTracker::ResetPending() { base::AutoLock lock(lock_); pending_non_ui_ = NULL; pending_ui_ = NULL; } void RequestDrawGLTracker::SetQueuedFunctorOnUi(SharedRendererState* state) { base::AutoLock lock(lock_); DCHECK(state); pending_ui_ = state; pending_non_ui_ = NULL; } } // namespace internal namespace { base::LazyInstance g_request_draw_gl_tracker = LAZY_INSTANCE_INITIALIZER; } SharedRendererState::SharedRendererState( const scoped_refptr& ui_loop, BrowserViewRenderer* browser_view_renderer) : ui_loop_(ui_loop), browser_view_renderer_(browser_view_renderer), renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()), inside_hardware_release_(false), weak_factory_on_ui_thread_(this) { DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(browser_view_renderer_); ui_thread_weak_ptr_ = weak_factory_on_ui_thread_.GetWeakPtr(); ResetRequestDrawGLCallback(); } SharedRendererState::~SharedRendererState() { DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(!hardware_renderer_.get()); } void SharedRendererState::ClientRequestDrawGL() { if (ui_loop_->BelongsToCurrentThread()) { if (!g_request_draw_gl_tracker.Get().ShouldRequestOnUiThread(this)) return; ClientRequestDrawGLOnUI(); } else { if (!g_request_draw_gl_tracker.Get().ShouldRequestOnNonUiThread(this)) return; base::Closure callback; { base::AutoLock lock(lock_); callback = request_draw_gl_closure_; } ui_loop_->PostTask(FROM_HERE, callback); } } void SharedRendererState::DidDrawGLProcess() { g_request_draw_gl_tracker.Get().ResetPending(); } void SharedRendererState::ResetRequestDrawGLCallback() { DCHECK(ui_loop_->BelongsToCurrentThread()); base::AutoLock lock(lock_); request_draw_gl_cancelable_closure_.Reset(base::Bind( &SharedRendererState::ClientRequestDrawGLOnUI, base::Unretained(this))); request_draw_gl_closure_ = request_draw_gl_cancelable_closure_.callback(); } void SharedRendererState::ClientRequestDrawGLOnUI() { DCHECK(ui_loop_->BelongsToCurrentThread()); ResetRequestDrawGLCallback(); g_request_draw_gl_tracker.Get().SetQueuedFunctorOnUi(this); if (!browser_view_renderer_->RequestDrawGL(false)) { g_request_draw_gl_tracker.Get().ResetPending(); LOG(ERROR) << "Failed to request GL process. Deadlock likely"; } } void SharedRendererState::UpdateParentDrawConstraintsOnUI() { DCHECK(ui_loop_->BelongsToCurrentThread()); browser_view_renderer_->UpdateParentDrawConstraints(); } void SharedRendererState::SetScrollOffsetOnUI(gfx::Vector2d scroll_offset) { base::AutoLock lock(lock_); scroll_offset_ = scroll_offset; } gfx::Vector2d SharedRendererState::GetScrollOffsetOnRT() { base::AutoLock lock(lock_); return scroll_offset_; } void SharedRendererState::SetCompositorFrameOnUI(scoped_ptr frame) { base::AutoLock lock(lock_); DCHECK(!child_frame_.get()); child_frame_ = frame.Pass(); } scoped_ptr SharedRendererState::PassCompositorFrameOnRT() { base::AutoLock lock(lock_); return child_frame_.Pass(); } scoped_ptr SharedRendererState::PassUncommittedFrameOnUI() { base::AutoLock lock(lock_); return child_frame_.Pass(); } void SharedRendererState::PostExternalDrawConstraintsToChildCompositorOnRT( const ParentCompositorDrawConstraints& parent_draw_constraints) { { base::AutoLock lock(lock_); parent_draw_constraints_ = parent_draw_constraints; } // No need to hold the lock_ during the post task. ui_loop_->PostTask( FROM_HERE, base::Bind(&SharedRendererState::UpdateParentDrawConstraintsOnUI, ui_thread_weak_ptr_)); } ParentCompositorDrawConstraints SharedRendererState::GetParentDrawConstraintsOnUI() const { base::AutoLock lock(lock_); return parent_draw_constraints_; } void SharedRendererState::SetInsideHardwareRelease(bool inside) { base::AutoLock lock(lock_); inside_hardware_release_ = inside; } bool SharedRendererState::IsInsideHardwareRelease() const { base::AutoLock lock(lock_); return inside_hardware_release_; } void SharedRendererState::InsertReturnedResourcesOnRT( const cc::ReturnedResourceArray& resources) { base::AutoLock lock(lock_); returned_resources_.insert( returned_resources_.end(), resources.begin(), resources.end()); } void SharedRendererState::SwapReturnedResourcesOnUI( cc::ReturnedResourceArray* resources) { DCHECK(resources->empty()); base::AutoLock lock(lock_); resources->swap(returned_resources_); } bool SharedRendererState::ReturnedResourcesEmptyOnUI() const { base::AutoLock lock(lock_); return returned_resources_.empty(); } void SharedRendererState::DrawGL(AwDrawGLInfo* draw_info) { TRACE_EVENT0("android_webview", "DrawFunctor"); if (draw_info->mode == AwDrawGLInfo::kModeSync) { TRACE_EVENT_INSTANT0("android_webview", "kModeSync", TRACE_EVENT_SCOPE_THREAD); if (hardware_renderer_) hardware_renderer_->CommitFrame(); return; } // kModeProcessNoContext should never happen because we tear down hardware // in onTrimMemory. However that guarantee is maintained outside of chromium // code. Not notifying shared state in kModeProcessNoContext can lead to // immediate deadlock, which is slightly more catastrophic than leaks or // corruption. if (draw_info->mode == AwDrawGLInfo::kModeProcess || draw_info->mode == AwDrawGLInfo::kModeProcessNoContext) { DidDrawGLProcess(); } { GLViewRendererManager* manager = GLViewRendererManager::GetInstance(); base::AutoLock lock(lock_); if (renderer_manager_key_ != manager->NullKey()) { manager->DidDrawGL(renderer_manager_key_); } } ScopedAppGLStateRestore state_restore( draw_info->mode == AwDrawGLInfo::kModeDraw ? ScopedAppGLStateRestore::MODE_DRAW : ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); ScopedAllowGL allow_gl; if (draw_info->mode == AwDrawGLInfo::kModeProcessNoContext) { LOG(ERROR) << "Received unexpected kModeProcessNoContext"; } if (IsInsideHardwareRelease()) { hardware_renderer_.reset(); // Flush the idle queue in tear down. DeferredGpuCommandService::GetInstance()->PerformAllIdleWork(); return; } if (draw_info->mode != AwDrawGLInfo::kModeDraw) { if (draw_info->mode == AwDrawGLInfo::kModeProcess) { DeferredGpuCommandService::GetInstance()->PerformIdleWork(true); } return; } if (!hardware_renderer_) { hardware_renderer_.reset(new HardwareRenderer(this)); hardware_renderer_->CommitFrame(); } hardware_renderer_->DrawGL(state_restore.stencil_enabled(), state_restore.framebuffer_binding_ext(), draw_info); DeferredGpuCommandService::GetInstance()->PerformIdleWork(false); } void SharedRendererState::ReleaseHardwareDrawIfNeededOnUI() { ReleaseCompositorResourcesIfNeededOnUI(true); } void SharedRendererState::DeleteHardwareRendererOnUI() { ReleaseCompositorResourcesIfNeededOnUI(false); } void SharedRendererState::ReleaseCompositorResourcesIfNeededOnUI( bool release_hardware_draw) { DCHECK(ui_loop_->BelongsToCurrentThread()); InsideHardwareReleaseReset auto_inside_hardware_release_reset(this); browser_view_renderer_->DetachFunctorFromView(); bool hardware_initialized = browser_view_renderer_->hardware_enabled(); // If the WebView gets onTrimMemory >= MODERATE twice in a row, the 2nd // onTrimMemory will result in an unnecessary Render Thread DrawGL call. if (hardware_initialized) { bool draw_functor_succeeded = browser_view_renderer_->RequestDrawGL(true); if (!draw_functor_succeeded) { LOG(ERROR) << "Unable to free GL resources. Has the Window leaked?"; // Calling release on wrong thread intentionally. AwDrawGLInfo info; info.mode = AwDrawGLInfo::kModeProcess; DrawGL(&info); } if (release_hardware_draw) browser_view_renderer_->ReleaseHardware(); } GLViewRendererManager* manager = GLViewRendererManager::GetInstance(); { base::AutoLock lock(lock_); if (renderer_manager_key_ != manager->NullKey()) { manager->Remove(renderer_manager_key_); renderer_manager_key_ = manager->NullKey(); } } if (hardware_initialized) { // Flush any invoke functors that's caused by ReleaseHardware. browser_view_renderer_->RequestDrawGL(true); } } void SharedRendererState::InitializeHardwareDrawIfNeededOnUI() { DCHECK(ui_loop_->BelongsToCurrentThread()); GLViewRendererManager* manager = GLViewRendererManager::GetInstance(); base::AutoLock lock(lock_); if (renderer_manager_key_ == manager->NullKey()) { renderer_manager_key_ = manager->PushBack(this); DeferredGpuCommandService::SetInstance(); } } SharedRendererState::InsideHardwareReleaseReset::InsideHardwareReleaseReset( SharedRendererState* shared_renderer_state) : shared_renderer_state_(shared_renderer_state) { DCHECK(!shared_renderer_state_->IsInsideHardwareRelease()); shared_renderer_state_->SetInsideHardwareRelease(true); } SharedRendererState::InsideHardwareReleaseReset::~InsideHardwareReleaseReset() { shared_renderer_state_->SetInsideHardwareRelease(false); } } // namespace android_webview