blob: 52e34791615dbc6e43707b6a7c792420ceb9b426 [file] [log] [blame]
// 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