| // 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/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" |
| |
| namespace android_webview { |
| |
| namespace internal { |
| |
| class RequestDrawGLTracker { |
| public: |
| RequestDrawGLTracker(); |
| bool ShouldRequestOnNonUiThread(SharedRendererState* state); |
| bool ShouldRequestOnUiThread(SharedRendererState* state); |
| void DidRequestOnUiThread(); |
| void ResetPending(); |
| |
| 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; |
| } |
| if (pending_ui_) |
| return false; |
| pending_ui_ = state; |
| return true; |
| } |
| |
| void RequestDrawGLTracker::ResetPending() { |
| base::AutoLock lock(lock_); |
| pending_non_ui_ = NULL; |
| pending_ui_ = NULL; |
| } |
| |
| } // namespace internal |
| |
| namespace { |
| |
| base::LazyInstance<internal::RequestDrawGLTracker> g_request_draw_gl_tracker = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } |
| |
| SharedRendererState::SharedRendererState( |
| const scoped_refptr<base::SingleThreadTaskRunner>& ui_loop, |
| BrowserViewRenderer* browser_view_renderer) |
| : ui_loop_(ui_loop), |
| browser_view_renderer_(browser_view_renderer), |
| renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()), |
| force_commit_(false), |
| inside_hardware_release_(false), |
| needs_force_invalidate_on_next_draw_gl_(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(); |
| 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_; |
| } |
| |
| bool SharedRendererState::HasCompositorFrameOnUI() const { |
| base::AutoLock lock(lock_); |
| return compositor_frame_.get(); |
| } |
| |
| void SharedRendererState::SetCompositorFrameOnUI( |
| scoped_ptr<cc::CompositorFrame> frame, |
| bool force_commit) { |
| base::AutoLock lock(lock_); |
| DCHECK(!compositor_frame_.get()); |
| compositor_frame_ = frame.Pass(); |
| force_commit_ = force_commit; |
| } |
| |
| scoped_ptr<cc::CompositorFrame> SharedRendererState::PassCompositorFrameOnRT() { |
| base::AutoLock lock(lock_); |
| return compositor_frame_.Pass(); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> |
| SharedRendererState::PassUncommittedFrameOnUI() { |
| base::AutoLock lock(lock_); |
| return compositor_frame_.Pass(); |
| } |
| |
| bool SharedRendererState::ForceCommitOnRT() const { |
| base::AutoLock lock(lock_); |
| return force_commit_; |
| } |
| |
| bool SharedRendererState::UpdateDrawConstraintsOnRT( |
| const ParentCompositorDrawConstraints& parent_draw_constraints) { |
| base::AutoLock lock(lock_); |
| if (needs_force_invalidate_on_next_draw_gl_ || |
| !parent_draw_constraints_.Equals(parent_draw_constraints)) { |
| parent_draw_constraints_ = parent_draw_constraints; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void SharedRendererState::PostExternalDrawConstraintsToChildCompositorOnRT( |
| const ParentCompositorDrawConstraints& parent_draw_constraints) { |
| if (UpdateDrawConstraintsOnRT(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_)); |
| } |
| } |
| |
| void SharedRendererState::DidSkipCommitFrameOnRT() { |
| ui_loop_->PostTask(FROM_HERE, |
| base::Bind(&SharedRendererState::DidSkipCommitFrameOnUI, |
| ui_thread_weak_ptr_)); |
| } |
| |
| void SharedRendererState::DidSkipCommitFrameOnUI() { |
| DCHECK(ui_loop_->BelongsToCurrentThread()); |
| browser_view_renderer_->DidSkipCommitFrame(); |
| } |
| |
| ParentCompositorDrawConstraints |
| SharedRendererState::GetParentDrawConstraintsOnUI() const { |
| base::AutoLock lock(lock_); |
| return parent_draw_constraints_; |
| } |
| |
| void SharedRendererState::SetForceInvalidateOnNextDrawGLOnUI( |
| bool needs_force_invalidate_on_next_draw_gl) { |
| base::AutoLock lock(lock_); |
| needs_force_invalidate_on_next_draw_gl_ = |
| needs_force_invalidate_on_next_draw_gl; |
| } |
| |
| bool SharedRendererState::NeedsForceInvalidateOnNextDrawGLOnUI() const { |
| base::AutoLock lock(lock_); |
| return needs_force_invalidate_on_next_draw_gl_; |
| } |
| |
| 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) { |
| if (draw_info->mode == AwDrawGLInfo::kModeSync) { |
| if (hardware_renderer_) |
| hardware_renderer_->CommitFrame(); |
| return; |
| } |
| |
| { |
| 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"; |
| } |
| |
| // 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(); |
| } |
| |
| 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() { |
| DCHECK(ui_loop_->BelongsToCurrentThread()); |
| InsideHardwareReleaseReset auto_inside_hardware_release_reset(this); |
| |
| browser_view_renderer_->InvalidateOnFunctorDestroy(); |
| bool hardware_initialized = browser_view_renderer_->hardware_enabled(); |
| 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); |
| } |
| |
| 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 |