// 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/deferred_gpu_command_service.h" #include "android_webview/browser/gl_view_renderer_manager.h" #include "android_webview/browser/shared_renderer_state.h" #include "base/lazy_instance.h" #include "base/synchronization/lock.h" #include "base/trace_event/trace_event.h" #include "content/public/browser/android/synchronous_compositor.h" #include "gpu/command_buffer/service/shader_translator_cache.h" namespace android_webview { namespace { base::LazyInstance > g_service = LAZY_INSTANCE_INITIALIZER; } // namespace base::LazyInstance ScopedAllowGL::allow_gl; // static bool ScopedAllowGL::IsAllowed() { return allow_gl.Get().Get(); } ScopedAllowGL::ScopedAllowGL() { DCHECK(!allow_gl.Get().Get()); allow_gl.Get().Set(true); if (g_service.Get().get()) g_service.Get()->RunTasks(); } ScopedAllowGL::~ScopedAllowGL() { allow_gl.Get().Set(false); DeferredGpuCommandService* service = g_service.Get().get(); if (service) { service->RunTasks(); if (service->IdleQueueSize()) { service->RequestProcessGL(); } } } // static void DeferredGpuCommandService::SetInstance() { if (!g_service.Get().get()) { g_service.Get() = new DeferredGpuCommandService; content::SynchronousCompositor::SetGpuService(g_service.Get()); } } // static DeferredGpuCommandService* DeferredGpuCommandService::GetInstance() { DCHECK(g_service.Get().get()); return g_service.Get().get(); } DeferredGpuCommandService::DeferredGpuCommandService() {} DeferredGpuCommandService::~DeferredGpuCommandService() { base::AutoLock lock(tasks_lock_); DCHECK(tasks_.empty()); } // This method can be called on any thread. // static void DeferredGpuCommandService::RequestProcessGL() { SharedRendererState* renderer_state = GLViewRendererManager::GetInstance()->GetMostRecentlyDrawn(); if (!renderer_state) { LOG(ERROR) << "No hardware renderer. Deadlock likely"; return; } renderer_state->ClientRequestDrawGL(); } // Called from different threads! void DeferredGpuCommandService::ScheduleTask(const base::Closure& task) { { base::AutoLock lock(tasks_lock_); tasks_.push(task); } if (ScopedAllowGL::IsAllowed()) { RunTasks(); } else { RequestProcessGL(); } } size_t DeferredGpuCommandService::IdleQueueSize() { base::AutoLock lock(tasks_lock_); return idle_tasks_.size(); } void DeferredGpuCommandService::ScheduleIdleWork( const base::Closure& callback) { { base::AutoLock lock(tasks_lock_); idle_tasks_.push(std::make_pair(base::Time::Now(), callback)); } RequestProcessGL(); } void DeferredGpuCommandService::PerformIdleWork(bool is_idle) { TRACE_EVENT1("android_webview", "DeferredGpuCommandService::PerformIdleWork", "is_idle", is_idle); DCHECK(ScopedAllowGL::IsAllowed()); static const base::TimeDelta kMaxIdleAge = base::TimeDelta::FromMilliseconds(16); const base::Time now = base::Time::Now(); size_t queue_size = IdleQueueSize(); while (queue_size--) { base::Closure task; { base::AutoLock lock(tasks_lock_); if (!is_idle) { // Only run old tasks if we are not really idle right now. base::TimeDelta age(now - idle_tasks_.front().first); if (age < kMaxIdleAge) break; } task = idle_tasks_.front().second; idle_tasks_.pop(); } task.Run(); } } void DeferredGpuCommandService::PerformAllIdleWork() { TRACE_EVENT0("android_webview", "DeferredGpuCommandService::PerformAllIdleWork"); while (IdleQueueSize()) { PerformIdleWork(true); } } bool DeferredGpuCommandService::UseVirtualizedGLContexts() { return true; } scoped_refptr DeferredGpuCommandService::shader_translator_cache() { if (!shader_translator_cache_.get()) shader_translator_cache_ = new gpu::gles2::ShaderTranslatorCache; return shader_translator_cache_; } void DeferredGpuCommandService::RunTasks() { TRACE_EVENT0("android_webview", "DeferredGpuCommandService::RunTasks"); bool has_more_tasks; { base::AutoLock lock(tasks_lock_); has_more_tasks = tasks_.size() > 0; } while (has_more_tasks) { base::Closure task; { base::AutoLock lock(tasks_lock_); task = tasks_.front(); tasks_.pop(); } task.Run(); { base::AutoLock lock(tasks_lock_); has_more_tasks = tasks_.size() > 0; } } } void DeferredGpuCommandService::AddRef() const { base::RefCountedThreadSafe::AddRef(); } void DeferredGpuCommandService::Release() const { base::RefCountedThreadSafe::Release(); } } // namespace android_webview