Index: android_webview/browser/hardware_renderer.cc |
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/hardware_renderer.cc |
similarity index 17% |
rename from android_webview/browser/in_process_view_renderer.cc |
rename to android_webview/browser/hardware_renderer.cc |
index 48a43e18e2fe10854385d1750dadb52790790b99..a8a77df15ad77dd3277bb2ca4dc8a1e6e20d0ab2 100644 |
--- a/android_webview/browser/in_process_view_renderer.cc |
+++ b/android_webview/browser/hardware_renderer.cc |
@@ -1,958 +1,326 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
+// 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/in_process_view_renderer.h" |
+#include "android_webview/browser/hardware_renderer.h" |
#include "android_webview/browser/aw_gl_surface.h" |
+#include "android_webview/browser/browser_view_renderer.h" |
+#include "android_webview/browser/gl_view_renderer_manager.h" |
#include "android_webview/browser/scoped_app_gl_state_restore.h" |
#include "android_webview/common/aw_switches.h" |
#include "android_webview/public/browser/draw_gl.h" |
-#include "base/android/jni_android.h" |
-#include "base/auto_reset.h" |
#include "base/command_line.h" |
#include "base/debug/trace_event.h" |
-#include "base/lazy_instance.h" |
-#include "base/logging.h" |
#include "base/strings/string_number_conversions.h" |
-#include "base/strings/stringprintf.h" |
#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/web_contents.h" |
#include "content/public/common/content_switches.h" |
-#include "gpu/command_buffer/service/in_process_command_buffer.h" |
-#include "third_party/skia/include/core/SkBitmap.h" |
-#include "third_party/skia/include/core/SkBitmapDevice.h" |
-#include "third_party/skia/include/core/SkCanvas.h" |
-#include "third_party/skia/include/core/SkPicture.h" |
#include "ui/gfx/transform.h" |
-#include "ui/gfx/vector2d_conversions.h" |
-using base::android::AttachCurrentThread; |
-using base::android::JavaRef; |
-using base::android::ScopedJavaLocalRef; |
using content::BrowserThread; |
namespace android_webview { |
namespace { |
-const void* kUserDataKey = &kUserDataKey; |
- |
-class UserData : public content::WebContents::Data { |
- public: |
- UserData(InProcessViewRenderer* ptr) : instance_(ptr) {} |
- virtual ~UserData() { |
- instance_->WebContentsGone(); |
- } |
- |
- static InProcessViewRenderer* GetInstance(content::WebContents* contents) { |
- if (!contents) |
- return NULL; |
- UserData* data = reinterpret_cast<UserData*>( |
- contents->GetUserData(kUserDataKey)); |
- return data ? data->instance_ : NULL; |
- } |
- |
- private: |
- InProcessViewRenderer* instance_; |
-}; |
- |
-bool HardwareEnabled() { |
- static bool g_hw_enabled = !CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kDisableWebViewGLMode); |
- return g_hw_enabled; |
-} |
- |
-const int64 kFallbackTickTimeoutInMilliseconds = 20; |
- |
// Used to calculate memory and resource allocation. Determined experimentally. |
-size_t g_memory_multiplier = 10; |
-size_t g_num_gralloc_limit = 150; |
+const size_t g_memory_multiplier = 10; |
+const size_t g_num_gralloc_limit = 150; |
const size_t kBytesPerPixel = 4; |
const size_t kMemoryAllocationStep = 5 * 1024 * 1024; |
-base::LazyInstance<GLViewRendererManager>::Leaky g_view_renderer_manager = |
- LAZY_INSTANCE_INITIALIZER; |
- |
-class ScopedAllowGL { |
- public: |
- ScopedAllowGL(); |
- ~ScopedAllowGL(); |
- |
- static bool IsAllowed() { |
- return g_view_renderer_manager.Get().OnRenderThread() && allow_gl; |
- } |
- |
- private: |
- static bool allow_gl; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScopedAllowGL); |
-}; |
- |
-ScopedAllowGL::ScopedAllowGL() { |
- DCHECK(g_view_renderer_manager.Get().OnRenderThread()); |
- DCHECK(!allow_gl); |
- allow_gl = true; |
-} |
- |
-ScopedAllowGL::~ScopedAllowGL() { |
- allow_gl = false; |
-} |
- |
-bool ScopedAllowGL::allow_gl = false; |
- |
-void RequestProcessGLOnUIThread() { |
- if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, base::Bind(&RequestProcessGLOnUIThread)); |
- return; |
- } |
- |
- InProcessViewRenderer* renderer = static_cast<InProcessViewRenderer*>( |
- g_view_renderer_manager.Get().GetMostRecentlyDrawn()); |
- if (!renderer || !renderer->RequestProcessGL()) { |
- LOG(ERROR) << "Failed to request GL process. Deadlock likely: " |
- << !!renderer; |
- } |
-} |
- |
-class DeferredGpuCommandService |
- : public gpu::InProcessCommandBuffer::Service, |
- public base::RefCountedThreadSafe<DeferredGpuCommandService> { |
- public: |
- DeferredGpuCommandService(); |
- |
- virtual void ScheduleTask(const base::Closure& task) OVERRIDE; |
- virtual void ScheduleIdleWork(const base::Closure& task) OVERRIDE; |
- virtual bool UseVirtualizedGLContexts() OVERRIDE; |
- |
- void RunTasks(); |
- |
- virtual void AddRef() const OVERRIDE { |
- base::RefCountedThreadSafe<DeferredGpuCommandService>::AddRef(); |
- } |
- virtual void Release() const OVERRIDE { |
- base::RefCountedThreadSafe<DeferredGpuCommandService>::Release(); |
- } |
- |
- protected: |
- virtual ~DeferredGpuCommandService(); |
- friend class base::RefCountedThreadSafe<DeferredGpuCommandService>; |
- |
- private: |
- base::Lock tasks_lock_; |
- std::queue<base::Closure> tasks_; |
- DISALLOW_COPY_AND_ASSIGN(DeferredGpuCommandService); |
-}; |
- |
-DeferredGpuCommandService::DeferredGpuCommandService() {} |
- |
-DeferredGpuCommandService::~DeferredGpuCommandService() { |
- base::AutoLock lock(tasks_lock_); |
- DCHECK(tasks_.empty()); |
-} |
- |
-// Called from different threads! |
-void DeferredGpuCommandService::ScheduleTask(const base::Closure& task) { |
- { |
- base::AutoLock lock(tasks_lock_); |
- tasks_.push(task); |
- } |
- if (ScopedAllowGL::IsAllowed()) { |
- RunTasks(); |
- } else { |
- RequestProcessGLOnUIThread(); |
- } |
-} |
- |
-void DeferredGpuCommandService::ScheduleIdleWork( |
- const base::Closure& callback) { |
- // TODO(sievers): Should this do anything? |
-} |
- |
-bool DeferredGpuCommandService::UseVirtualizedGLContexts() { return true; } |
- |
-void 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; |
- } |
- |
- } |
-} |
- |
-base::LazyInstance<scoped_refptr<DeferredGpuCommandService> > g_service = |
- LAZY_INSTANCE_INITIALIZER; |
+base::LazyInstance<scoped_refptr<internal::DeferredGpuCommandService> > |
+ g_service = LAZY_INSTANCE_INITIALIZER; |
} // namespace |
-InProcessViewRenderer::InProcessViewRenderer( |
- BrowserViewRenderer::Client* client, |
- content::WebContents* web_contents) |
- : client_(client), |
- web_contents_(web_contents), |
- compositor_(NULL), |
- is_paused_(false), |
- view_visible_(false), |
- window_visible_(false), |
- attached_to_window_(false), |
- dip_scale_(0.0), |
- page_scale_factor_(1.0), |
- on_new_picture_enable_(false), |
- clear_view_(false), |
- compositor_needs_continuous_invalidate_(false), |
- block_invalidates_(false), |
- width_(0), |
- height_(0), |
- hardware_initialized_(false), |
- hardware_failed_(false), |
- last_egl_context_(NULL), |
- manager_key_(g_view_renderer_manager.Get().NullKey()) { |
- CHECK(web_contents_); |
- web_contents_->SetUserData(kUserDataKey, new UserData(this)); |
- content::SynchronousCompositor::SetClientForWebContents(web_contents_, this); |
- |
- // Currently the logic in this class relies on |compositor_| remaining NULL |
- // until the DidInitializeCompositor() call, hence it is not set here. |
-} |
- |
-InProcessViewRenderer::~InProcessViewRenderer() { |
- CHECK(web_contents_); |
- content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL); |
- web_contents_->SetUserData(kUserDataKey, NULL); |
- NoLongerExpectsDrawGL(); |
- DCHECK(web_contents_ == NULL); // WebContentsGone should have been called. |
-} |
- |
-void InProcessViewRenderer::NoLongerExpectsDrawGL() { |
- GLViewRendererManager& mru = g_view_renderer_manager.Get(); |
- if (manager_key_ != mru.NullKey()) { |
- mru.NoLongerExpectsDrawGL(manager_key_); |
- manager_key_ = mru.NullKey(); |
- } |
-} |
- |
-// static |
-InProcessViewRenderer* InProcessViewRenderer::FromWebContents( |
- content::WebContents* contents) { |
- return UserData::GetInstance(contents); |
-} |
+DrawGLResult::DrawGLResult() |
+ : did_draw(false), clip_contains_visible_rect(false) {} |
-void InProcessViewRenderer::WebContentsGone() { |
- web_contents_ = NULL; |
- compositor_ = NULL; |
+HardwareRenderer::HardwareRenderer(content::SynchronousCompositor* compositor, |
+ BrowserViewRendererClient* client) |
+ : compositor_(compositor), |
+ client_(client), |
+ last_egl_context_(eglGetCurrentContext()), |
+ manager_key_(GLViewRendererManager::GetInstance()->NullKey()) { |
+ DCHECK(compositor_); |
+ DCHECK(last_egl_context_); |
} |
-// static |
-void InProcessViewRenderer::CalculateTileMemoryPolicy() { |
- CommandLine* cl = CommandLine::ForCurrentProcess(); |
- if (cl->HasSwitch(switches::kTileMemoryMultiplier)) { |
- std::string string_value = |
- cl->GetSwitchValueASCII(switches::kTileMemoryMultiplier); |
- int int_value = 0; |
- if (base::StringToInt(string_value, &int_value) && |
- int_value >= 2 && int_value <= 50) { |
- g_memory_multiplier = int_value; |
- } |
+HardwareRenderer::~HardwareRenderer() { |
+ GLViewRendererManager* mru = GLViewRendererManager::GetInstance(); |
+ if (manager_key_ != mru->NullKey()) { |
+ mru->NoLongerExpectsDrawGL(manager_key_); |
+ manager_key_ = mru->NullKey(); |
} |
- if (cl->HasSwitch(switches::kNumGrallocBuffersPerWebview)) { |
- std::string string_value = |
- cl->GetSwitchValueASCII(switches::kNumGrallocBuffersPerWebview); |
- int int_value = 0; |
- if (base::StringToInt(string_value, &int_value) && |
- int_value >= 50 && int_value <= 500) { |
- g_num_gralloc_limit = int_value; |
- } |
- } |
- |
- const char kDefaultTileSize[] = "384"; |
- if (!cl->HasSwitch(switches::kDefaultTileWidth)) |
- cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); |
- |
- if (!cl->HasSwitch(switches::kDefaultTileHeight)) |
- cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); |
-} |
- |
-bool InProcessViewRenderer::RequestProcessGL() { |
- return client_->RequestDrawGL(NULL); |
-} |
- |
-void InProcessViewRenderer::TrimMemory(int level) { |
- // Constants from Android ComponentCallbacks2. |
- enum { |
- TRIM_MEMORY_RUNNING_LOW = 10, |
- TRIM_MEMORY_UI_HIDDEN = 20, |
- TRIM_MEMORY_BACKGROUND = 40, |
- }; |
- |
- // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because |
- // it does not indicate memory pressure, but merely that the app is |
- // backgrounded. |
- if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN) |
- return; |
- |
- // Nothing to drop. |
- if (!attached_to_window_ || !hardware_initialized_ || !compositor_) |
- return; |
- |
- // Do not release resources on view we expect to get DrawGL soon. |
- if (level < TRIM_MEMORY_BACKGROUND) { |
- client_->UpdateGlobalVisibleRect(); |
- if (view_visible_ && window_visible_ && |
- !cached_global_visible_rect_.IsEmpty()) { |
- return; |
- } |
- } |
- |
- if (!eglGetCurrentContext()) { |
- NOTREACHED(); |
- return; |
- } |
- |
- // Just set the memory limit to 0 and drop all tiles. This will be reset to |
- // normal levels in the next DrawGL call. |
- content::SynchronousCompositorMemoryPolicy policy; |
- policy.bytes_limit = 0; |
- policy.num_resources_limit = 0; |
- if (memory_policy_ == policy) |
- return; |
- |
- TRACE_EVENT0("android_webview", "InProcessViewRenderer::TrimMemory"); |
- ScopedAppGLStateRestore state_restore( |
- ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); |
- g_service.Get()->RunTasks(); |
- ScopedAllowGL allow_gl; |
- |
- SetMemoryPolicy(policy); |
- ForceFakeCompositeSW(); |
-} |
- |
-void InProcessViewRenderer::SetMemoryPolicy( |
- content::SynchronousCompositorMemoryPolicy& new_policy) { |
- if (memory_policy_ == new_policy) |
- return; |
- |
- memory_policy_ = new_policy; |
- compositor_->SetMemoryPolicy(memory_policy_); |
-} |
- |
-void InProcessViewRenderer::UpdateCachedGlobalVisibleRect() { |
- client_->UpdateGlobalVisibleRect(); |
-} |
+ if (gl_surface_) { |
+ ScopedAppGLStateRestore state_restore( |
+ ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); |
+ internal::ScopedAllowGL allow_gl; |
-bool InProcessViewRenderer::OnDraw(jobject java_canvas, |
- bool is_hardware_canvas, |
- const gfx::Vector2d& scroll, |
- const gfx::Rect& clip) { |
- scroll_at_start_of_frame_ = scroll; |
- if (clear_view_) |
- return false; |
- if (is_hardware_canvas && attached_to_window_ && HardwareEnabled()) { |
- // We should be performing a hardware draw here. If we don't have the |
- // comositor yet or if RequestDrawGL fails, it means we failed this draw and |
- // thus return false here to clear to background color for this draw. |
- return compositor_ && client_->RequestDrawGL(java_canvas); |
+ compositor_->ReleaseHwDraw(); |
+ gl_surface_ = NULL; |
} |
- // Perform a software draw |
- return DrawSWInternal(java_canvas, clip); |
+ DCHECK(manager_key_ == GLViewRendererManager::GetInstance()->NullKey()); |
} |
-bool InProcessViewRenderer::InitializeHwDraw() { |
- TRACE_EVENT0("android_webview", "InitializeHwDraw"); |
- DCHECK(!gl_surface_); |
- gl_surface_ = new AwGLSurface; |
+bool HardwareRenderer::InitializeHardwareDraw() { |
+ TRACE_EVENT0("android_webview", "InitializeHardwareDraw"); |
if (!g_service.Get()) { |
- g_service.Get() = new DeferredGpuCommandService; |
+ g_service.Get() = new internal::DeferredGpuCommandService; |
content::SynchronousCompositor::SetGpuService(g_service.Get()); |
} |
- hardware_failed_ = !compositor_->InitializeHwDraw(gl_surface_); |
- hardware_initialized_ = true; |
- if (hardware_failed_) |
- gl_surface_ = NULL; |
+ bool success = true; |
+ if (!gl_surface_) { |
+ scoped_refptr<AwGLSurface> gl_surface = new AwGLSurface; |
+ success = compositor_->InitializeHwDraw(gl_surface); |
+ if (success) |
+ gl_surface_ = gl_surface; |
+ } |
- return !hardware_failed_; |
+ return success; |
} |
-void InProcessViewRenderer::DrawGL(AwDrawGLInfo* draw_info) { |
- TRACE_EVENT0("android_webview", "InProcessViewRenderer::DrawGL"); |
- |
- manager_key_ = g_view_renderer_manager.Get().DidDrawGL(manager_key_, this); |
+DrawGLResult HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info, |
+ const DrawGLInput& input) { |
+ TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL"); |
+ manager_key_ = |
+ GLViewRendererManager::GetInstance()->DidDrawGL(manager_key_, this); |
+ DrawGLResult result; |
// We need to watch if the current Android context has changed and enforce |
// a clean-up in the compositor. |
EGLContext current_context = eglGetCurrentContext(); |
if (!current_context) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_NullEGLContext", TRACE_EVENT_SCOPE_THREAD); |
- return; |
+ DLOG(ERROR) << "DrawGL called without EGLContext"; |
+ return result; |
} |
- ScopedAppGLStateRestore state_restore(ScopedAppGLStateRestore::MODE_DRAW); |
- if (g_service.Get()) |
- g_service.Get()->RunTasks(); |
- ScopedAllowGL allow_gl; |
- |
- if (!attached_to_window_) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_NotAttached", TRACE_EVENT_SCOPE_THREAD); |
- return; |
- } |
- |
- if (draw_info->mode == AwDrawGLInfo::kModeProcess) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_ModeProcess", TRACE_EVENT_SCOPE_THREAD); |
- return; |
- } |
- |
- if (compositor_ && !hardware_initialized_) { |
- if (InitializeHwDraw()) { |
- last_egl_context_ = current_context; |
- } else { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_HwInitFail", TRACE_EVENT_SCOPE_THREAD); |
- LOG(ERROR) << "WebView hardware initialization failed"; |
- return; |
- } |
- } |
+ // TODO(boliu): Handle context loss. |
+ if (last_egl_context_ != current_context) |
+ DLOG(WARNING) << "EGLContextChanged"; |
- UpdateCachedGlobalVisibleRect(); |
- if (cached_global_visible_rect_.IsEmpty()) { |
- TRACE_EVENT_INSTANT0("android_webview", |
- "EarlyOut_EmptyVisibleRect", |
- TRACE_EVENT_SCOPE_THREAD); |
- return; |
- } |
+ ScopedAppGLStateRestore state_restore(ScopedAppGLStateRestore::MODE_DRAW); |
+ internal::ScopedAllowGL allow_gl; |
- if (last_egl_context_ != current_context) { |
- // TODO(boliu): Handle context lost |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EGLContextChanged", TRACE_EVENT_SCOPE_THREAD); |
- } |
+ if (draw_info->mode == AwDrawGLInfo::kModeProcess) |
+ return result; |
- if (!compositor_) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD); |
- return; |
+ if (!InitializeHardwareDraw()) { |
+ DLOG(ERROR) << "WebView hardware initialization failed"; |
+ return result; |
} |
- // DrawGL may be called without OnDraw, so cancel |fallback_tick_| here as |
- // well just to be safe. |
- fallback_tick_.Cancel(); |
- |
// Update memory budget. This will no-op in compositor if the policy has not |
// changed since last draw. |
content::SynchronousCompositorMemoryPolicy policy; |
policy.bytes_limit = g_memory_multiplier * kBytesPerPixel * |
- cached_global_visible_rect_.width() * |
- cached_global_visible_rect_.height(); |
+ input.global_visible_rect.width() * |
+ input.global_visible_rect.height(); |
// Round up to a multiple of kMemoryAllocationStep. |
policy.bytes_limit = |
(policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; |
policy.num_resources_limit = g_num_gralloc_limit; |
SetMemoryPolicy(policy); |
- DCHECK(gl_surface_); |
gl_surface_->SetBackingFrameBufferObject( |
state_restore.framebuffer_binding_ext()); |
gfx::Transform transform; |
transform.matrix().setColMajorf(draw_info->transform); |
- transform.Translate(scroll_at_start_of_frame_.x(), |
- scroll_at_start_of_frame_.y()); |
+ transform.Translate(input.scroll.x(), input.scroll.y()); |
gfx::Rect clip_rect(draw_info->clip_left, |
draw_info->clip_top, |
draw_info->clip_right - draw_info->clip_left, |
draw_info->clip_bottom - draw_info->clip_top); |
- // Assume we always draw the full visible rect if we are drawing into a layer. |
- bool drew_full_visible_rect = true; |
- |
gfx::Rect viewport_rect; |
- if (!draw_info->is_layer) { |
- viewport_rect = cached_global_visible_rect_; |
- clip_rect.Intersect(viewport_rect); |
- drew_full_visible_rect = clip_rect.Contains(viewport_rect); |
- } else { |
+ if (draw_info->is_layer) { |
viewport_rect = clip_rect; |
+ } else { |
+ viewport_rect = input.global_visible_rect; |
+ clip_rect.Intersect(viewport_rect); |
} |
+ result.clip_contains_visible_rect = clip_rect.Contains(viewport_rect); |
- block_invalidates_ = true; |
- // TODO(joth): Check return value. |
- compositor_->DemandDrawHw(gfx::Size(draw_info->width, draw_info->height), |
- transform, |
- viewport_rect, |
- clip_rect, |
- state_restore.stencil_enabled()); |
- block_invalidates_ = false; |
+ result.did_draw = |
+ compositor_->DemandDrawHw(gfx::Size(draw_info->width, draw_info->height), |
+ transform, |
+ viewport_rect, |
+ clip_rect, |
+ state_restore.stencil_enabled()); |
gl_surface_->ResetBackingFrameBufferObject(); |
- EnsureContinuousInvalidation(draw_info, !drew_full_visible_rect); |
-} |
- |
-void InProcessViewRenderer::SetGlobalVisibleRect( |
- const gfx::Rect& visible_rect) { |
- cached_global_visible_rect_ = visible_rect; |
+ return result; |
} |
-bool InProcessViewRenderer::DrawSWInternal(jobject java_canvas, |
- const gfx::Rect& clip) { |
- if (clip.IsEmpty()) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_EmptyClip", TRACE_EVENT_SCOPE_THREAD); |
- return true; |
- } |
+bool HardwareRenderer::TrimMemory(int level, bool visible) { |
+ // Constants from Android ComponentCallbacks2. |
+ enum { |
+ TRIM_MEMORY_RUNNING_LOW = 10, |
+ TRIM_MEMORY_UI_HIDDEN = 20, |
+ TRIM_MEMORY_BACKGROUND = 40, |
+ }; |
- if (!compositor_) { |
- TRACE_EVENT_INSTANT0( |
- "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD); |
+ // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because |
+ // it does not indicate memory pressure, but merely that the app is |
+ // backgrounded. |
+ if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN) |
return false; |
- } |
- |
- return JavaHelper::GetInstance()->RenderViaAuxilaryBitmapIfNeeded( |
- java_canvas, |
- scroll_at_start_of_frame_, |
- clip, |
- base::Bind(&InProcessViewRenderer::CompositeSW, base::Unretained(this))); |
-} |
-skia::RefPtr<SkPicture> InProcessViewRenderer::CapturePicture(int width, |
- int height) { |
- TRACE_EVENT0("android_webview", "InProcessViewRenderer::CapturePicture"); |
+ // Do not release resources on view we expect to get DrawGL soon. |
+ if (level < TRIM_MEMORY_BACKGROUND && visible) |
+ return false; |
- // Return empty Picture objects for empty SkPictures. |
- skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); |
- if (width <= 0 || height <= 0) { |
- return picture; |
+ if (!eglGetCurrentContext()) { |
+ NOTREACHED(); |
+ return false; |
} |
- // Reset scroll back to the origin, will go back to the old |
- // value when scroll_reset is out of scope. |
- base::AutoReset<gfx::Vector2dF> scroll_reset(&scroll_offset_dip_, |
- gfx::Vector2d()); |
+ DCHECK_EQ(last_egl_context_, eglGetCurrentContext()); |
- SkCanvas* rec_canvas = picture->beginRecording(width, height, 0); |
- if (compositor_) |
- CompositeSW(rec_canvas); |
- picture->endRecording(); |
- return picture; |
-} |
- |
-void InProcessViewRenderer::EnableOnNewPicture(bool enabled) { |
- on_new_picture_enable_ = enabled; |
- EnsureContinuousInvalidation(NULL, false); |
-} |
- |
-void InProcessViewRenderer::ClearView() { |
- TRACE_EVENT_INSTANT0("android_webview", |
- "InProcessViewRenderer::ClearView", |
- TRACE_EVENT_SCOPE_THREAD); |
- if (clear_view_) |
- return; |
- |
- clear_view_ = true; |
- // Always invalidate ignoring the compositor to actually clear the webview. |
- EnsureContinuousInvalidation(NULL, true); |
-} |
+ // Just set the memory limit to 0 and drop all tiles. This will be reset to |
+ // normal levels in the next DrawGL call. |
+ content::SynchronousCompositorMemoryPolicy policy; |
+ policy.bytes_limit = 0; |
+ policy.num_resources_limit = 0; |
+ if (memory_policy_ == policy) |
+ return false; |
-void InProcessViewRenderer::SetIsPaused(bool paused) { |
- TRACE_EVENT_INSTANT1("android_webview", |
- "InProcessViewRenderer::SetIsPaused", |
- TRACE_EVENT_SCOPE_THREAD, |
- "paused", |
- paused); |
- is_paused_ = paused; |
- EnsureContinuousInvalidation(NULL, false); |
-} |
+ TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory"); |
+ ScopedAppGLStateRestore state_restore( |
+ ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); |
+ internal::ScopedAllowGL allow_gl; |
-void InProcessViewRenderer::SetViewVisibility(bool view_visible) { |
- TRACE_EVENT_INSTANT1("android_webview", |
- "InProcessViewRenderer::SetViewVisibility", |
- TRACE_EVENT_SCOPE_THREAD, |
- "view_visible", |
- view_visible); |
- view_visible_ = view_visible; |
+ SetMemoryPolicy(policy); |
+ return true; |
} |
-void InProcessViewRenderer::SetWindowVisibility(bool window_visible) { |
- TRACE_EVENT_INSTANT1("android_webview", |
- "InProcessViewRenderer::SetWindowVisibility", |
- TRACE_EVENT_SCOPE_THREAD, |
- "window_visible", |
- window_visible); |
- window_visible_ = window_visible; |
- EnsureContinuousInvalidation(NULL, false); |
-} |
+void HardwareRenderer::SetMemoryPolicy( |
+ content::SynchronousCompositorMemoryPolicy& new_policy) { |
+ if (memory_policy_ == new_policy) |
+ return; |
-void InProcessViewRenderer::OnSizeChanged(int width, int height) { |
- TRACE_EVENT_INSTANT2("android_webview", |
- "InProcessViewRenderer::OnSizeChanged", |
- TRACE_EVENT_SCOPE_THREAD, |
- "width", |
- width, |
- "height", |
- height); |
- width_ = width; |
- height_ = height; |
+ memory_policy_ = new_policy; |
+ compositor_->SetMemoryPolicy(memory_policy_); |
} |
-void InProcessViewRenderer::OnAttachedToWindow(int width, int height) { |
- TRACE_EVENT2("android_webview", |
- "InProcessViewRenderer::OnAttachedToWindow", |
- "width", |
- width, |
- "height", |
- height); |
- attached_to_window_ = true; |
- width_ = width; |
- height_ = height; |
+bool HardwareRenderer::RequestDrawGL() { |
+ return client_->RequestDrawGL(NULL); |
} |
-void InProcessViewRenderer::OnDetachedFromWindow() { |
- TRACE_EVENT0("android_webview", |
- "InProcessViewRenderer::OnDetachedFromWindow"); |
- |
- NoLongerExpectsDrawGL(); |
- if (hardware_initialized_) { |
- DCHECK(compositor_); |
+// static |
+void HardwareRenderer::CalculateTileMemoryPolicy() { |
+ CommandLine* cl = CommandLine::ForCurrentProcess(); |
- ScopedAppGLStateRestore state_restore( |
- ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); |
- g_service.Get()->RunTasks(); |
- ScopedAllowGL allow_gl; |
- compositor_->ReleaseHwDraw(); |
- hardware_initialized_ = false; |
- } |
+ const char kDefaultTileSize[] = "384"; |
+ if (!cl->HasSwitch(switches::kDefaultTileWidth)) |
+ cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); |
- gl_surface_ = NULL; |
- attached_to_window_ = false; |
+ if (!cl->HasSwitch(switches::kDefaultTileHeight)) |
+ cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); |
} |
-bool InProcessViewRenderer::IsAttachedToWindow() { |
- return attached_to_window_; |
-} |
+namespace internal { |
-bool InProcessViewRenderer::IsVisible() { |
- // Ignore |window_visible_| if |attached_to_window_| is false. |
- return view_visible_ && (!attached_to_window_ || window_visible_); |
-} |
+bool ScopedAllowGL::allow_gl = false; |
-gfx::Rect InProcessViewRenderer::GetScreenRect() { |
- return gfx::Rect(client_->GetLocationOnScreen(), gfx::Size(width_, height_)); |
+// static |
+bool ScopedAllowGL::IsAllowed() { |
+ return GLViewRendererManager::GetInstance()->OnRenderThread() && allow_gl; |
} |
-void InProcessViewRenderer::DidInitializeCompositor( |
- content::SynchronousCompositor* compositor) { |
- TRACE_EVENT0("android_webview", |
- "InProcessViewRenderer::DidInitializeCompositor"); |
- DCHECK(compositor && compositor_ == NULL); |
- compositor_ = compositor; |
- hardware_initialized_ = false; |
- hardware_failed_ = false; |
-} |
+ScopedAllowGL::ScopedAllowGL() { |
+ DCHECK(GLViewRendererManager::GetInstance()->OnRenderThread()); |
+ DCHECK(!allow_gl); |
+ allow_gl = true; |
-void InProcessViewRenderer::DidDestroyCompositor( |
- content::SynchronousCompositor* compositor) { |
- TRACE_EVENT0("android_webview", |
- "InProcessViewRenderer::DidDestroyCompositor"); |
- DCHECK(compositor_ == compositor); |
- |
- // This can fail if Apps call destroy while the webview is still attached |
- // to the view tree. This is an illegal operation that will lead to leaks. |
- // Log for now. Consider a proper fix if this becomes a problem. |
- LOG_IF(ERROR, hardware_initialized_) |
- << "Destroy called before OnDetachedFromWindow. May Leak GL resources"; |
- compositor_ = NULL; |
+ if (g_service.Get()) |
+ g_service.Get()->RunTasks(); |
} |
-void InProcessViewRenderer::SetContinuousInvalidate(bool invalidate) { |
- if (compositor_needs_continuous_invalidate_ == invalidate) |
- return; |
+ScopedAllowGL::~ScopedAllowGL() { allow_gl = false; } |
- TRACE_EVENT_INSTANT1("android_webview", |
- "InProcessViewRenderer::SetContinuousInvalidate", |
- TRACE_EVENT_SCOPE_THREAD, |
- "invalidate", |
- invalidate); |
- compositor_needs_continuous_invalidate_ = invalidate; |
- EnsureContinuousInvalidation(NULL, false); |
-} |
- |
-void InProcessViewRenderer::SetDipScale(float dip_scale) { |
- dip_scale_ = dip_scale; |
- CHECK(dip_scale_ > 0); |
-} |
+DeferredGpuCommandService::DeferredGpuCommandService() {} |
-gfx::Vector2d InProcessViewRenderer::max_scroll_offset() const { |
- DCHECK_GT(dip_scale_, 0); |
- return gfx::ToCeiledVector2d(gfx::ScaleVector2d( |
- max_scroll_offset_dip_, dip_scale_ * page_scale_factor_)); |
+DeferredGpuCommandService::~DeferredGpuCommandService() { |
+ base::AutoLock lock(tasks_lock_); |
+ DCHECK(tasks_.empty()); |
} |
-void InProcessViewRenderer::ScrollTo(gfx::Vector2d scroll_offset) { |
- gfx::Vector2d max_offset = max_scroll_offset(); |
- gfx::Vector2dF scroll_offset_dip; |
- // To preserve the invariant that scrolling to the maximum physical pixel |
- // value also scrolls to the maximum dip pixel value we transform the physical |
- // offset into the dip offset by using a proportion (instead of dividing by |
- // dip_scale * page_scale_factor). |
- if (max_offset.x()) { |
- scroll_offset_dip.set_x((scroll_offset.x() * max_scroll_offset_dip_.x()) / |
- max_offset.x()); |
- } |
- if (max_offset.y()) { |
- scroll_offset_dip.set_y((scroll_offset.y() * max_scroll_offset_dip_.y()) / |
- max_offset.y()); |
+namespace { |
+void RequestProcessGL() { |
+ HardwareRenderer* renderer = |
+ GLViewRendererManager::GetInstance()->GetMostRecentlyDrawn(); |
+ if (!renderer || !renderer->RequestDrawGL()) { |
+ LOG(ERROR) << "Failed to request GL process. Deadlock likely: " |
+ << !!renderer; |
} |
- |
- DCHECK_LE(0, scroll_offset_dip.x()); |
- DCHECK_LE(0, scroll_offset_dip.y()); |
- DCHECK_LE(scroll_offset_dip.x(), max_scroll_offset_dip_.x()); |
- DCHECK_LE(scroll_offset_dip.y(), max_scroll_offset_dip_.y()); |
- |
- if (scroll_offset_dip_ == scroll_offset_dip) |
- return; |
- |
- scroll_offset_dip_ = scroll_offset_dip; |
- |
- if (compositor_) |
- compositor_->DidChangeRootLayerScrollOffset(); |
} |
+} // namespace |
-void InProcessViewRenderer::DidUpdateContent() { |
- TRACE_EVENT_INSTANT0("android_webview", |
- "InProcessViewRenderer::DidUpdateContent", |
- TRACE_EVENT_SCOPE_THREAD); |
- clear_view_ = false; |
- EnsureContinuousInvalidation(NULL, false); |
- if (on_new_picture_enable_) |
- client_->OnNewPicture(); |
-} |
- |
-void InProcessViewRenderer::SetMaxRootLayerScrollOffset( |
- gfx::Vector2dF new_value_dip) { |
- DCHECK_GT(dip_scale_, 0); |
- |
- max_scroll_offset_dip_ = new_value_dip; |
- DCHECK_LE(0, max_scroll_offset_dip_.x()); |
- DCHECK_LE(0, max_scroll_offset_dip_.y()); |
- |
- client_->SetMaxContainerViewScrollOffset(max_scroll_offset()); |
+// static |
+void DeferredGpuCommandService::RequestProcessGLOnUIThread() { |
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ RequestProcessGL(); |
+ } else { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, base::Bind(&RequestProcessGL)); |
+ } |
} |
-void InProcessViewRenderer::SetTotalRootLayerScrollOffset( |
- gfx::Vector2dF scroll_offset_dip) { |
- // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during |
- // DrawGl when http://crbug.com/249972 is fixed. |
- if (scroll_offset_dip_ == scroll_offset_dip) |
- return; |
- |
- scroll_offset_dip_ = scroll_offset_dip; |
- |
- gfx::Vector2d max_offset = max_scroll_offset(); |
- gfx::Vector2d scroll_offset; |
- // For an explanation as to why this is done this way see the comment in |
- // InProcessViewRenderer::ScrollTo. |
- if (max_scroll_offset_dip_.x()) { |
- scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) / |
- max_scroll_offset_dip_.x()); |
+// Called from different threads! |
+void DeferredGpuCommandService::ScheduleTask(const base::Closure& task) { |
+ { |
+ base::AutoLock lock(tasks_lock_); |
+ tasks_.push(task); |
} |
- |
- if (max_scroll_offset_dip_.y()) { |
- scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) / |
- max_scroll_offset_dip_.y()); |
+ if (ScopedAllowGL::IsAllowed()) { |
+ RunTasks(); |
+ } else { |
+ RequestProcessGLOnUIThread(); |
} |
- |
- DCHECK(0 <= scroll_offset.x()); |
- DCHECK(0 <= scroll_offset.y()); |
- // Disabled because the conditions are being violated while running |
- // AwZoomTest.testMagnification, see http://crbug.com/340648 |
- // DCHECK(scroll_offset.x() <= max_offset.x()); |
- // DCHECK(scroll_offset.y() <= max_offset.y()); |
- |
- client_->ScrollContainerViewTo(scroll_offset); |
-} |
- |
-gfx::Vector2dF InProcessViewRenderer::GetTotalRootLayerScrollOffset() { |
- return scroll_offset_dip_; |
-} |
- |
-bool InProcessViewRenderer::IsExternalFlingActive() const { |
- return client_->IsFlingActive(); |
} |
-void InProcessViewRenderer::SetRootLayerPageScaleFactorAndLimits( |
- float page_scale_factor, |
- float min_page_scale_factor, |
- float max_page_scale_factor) { |
- page_scale_factor_ = page_scale_factor; |
- DCHECK_GT(page_scale_factor_, 0); |
- client_->SetPageScaleFactorAndLimits( |
- page_scale_factor, min_page_scale_factor, max_page_scale_factor); |
-} |
- |
-void InProcessViewRenderer::SetRootLayerScrollableSize( |
- gfx::SizeF scrollable_size) { |
- client_->SetContentsSize(scrollable_size); |
-} |
- |
-void InProcessViewRenderer::DidOverscroll( |
- gfx::Vector2dF accumulated_overscroll, |
- gfx::Vector2dF latest_overscroll_delta, |
- gfx::Vector2dF current_fling_velocity) { |
- const float physical_pixel_scale = dip_scale_ * page_scale_factor_; |
- if (accumulated_overscroll == latest_overscroll_delta) |
- overscroll_rounding_error_ = gfx::Vector2dF(); |
- gfx::Vector2dF scaled_overscroll_delta = |
- gfx::ScaleVector2d(latest_overscroll_delta, physical_pixel_scale); |
- gfx::Vector2d rounded_overscroll_delta = gfx::ToRoundedVector2d( |
- scaled_overscroll_delta + overscroll_rounding_error_); |
- overscroll_rounding_error_ = |
- scaled_overscroll_delta - rounded_overscroll_delta; |
- client_->DidOverscroll(rounded_overscroll_delta); |
+void DeferredGpuCommandService::ScheduleIdleWork( |
+ const base::Closure& callback) { |
+ // TODO(sievers): Should this do anything? |
} |
-void InProcessViewRenderer::EnsureContinuousInvalidation( |
- AwDrawGLInfo* draw_info, |
- bool invalidate_ignore_compositor) { |
- // This method should be called again when any of these conditions change. |
- bool need_invalidate = |
- compositor_needs_continuous_invalidate_ || invalidate_ignore_compositor; |
- if (!need_invalidate || block_invalidates_) |
- return; |
+bool DeferredGpuCommandService::UseVirtualizedGLContexts() { return true; } |
- // Always call view invalidate. We rely the Android framework to ignore the |
- // invalidate when it's not needed such as when view is not visible. |
- if (draw_info) { |
- draw_info->dirty_left = cached_global_visible_rect_.x(); |
- draw_info->dirty_top = cached_global_visible_rect_.y(); |
- draw_info->dirty_right = cached_global_visible_rect_.right(); |
- draw_info->dirty_bottom = cached_global_visible_rect_.bottom(); |
- draw_info->status_mask |= AwDrawGLInfo::kStatusMaskDraw; |
- } else { |
- client_->PostInvalidate(); |
+void DeferredGpuCommandService::RunTasks() { |
+ bool has_more_tasks; |
+ { |
+ base::AutoLock lock(tasks_lock_); |
+ has_more_tasks = tasks_.size() > 0; |
} |
- // Stop fallback ticks when one of these is true. |
- // 1) Webview is paused. Also need to check we are not in clear view since |
- // paused, offscreen still expect clear view to recover. |
- // 2) If we are attached to window and the window is not visible (eg when |
- // app is in the background). We are sure in this case the webview is used |
- // "on-screen" but that updates are not needed when in the background. |
- bool throttle_fallback_tick = |
- (is_paused_ && !clear_view_) || (attached_to_window_ && !window_visible_); |
- if (throttle_fallback_tick) |
- return; |
- |
- block_invalidates_ = compositor_needs_continuous_invalidate_; |
- |
- // Unretained here is safe because the callback is cancelled when |
- // |fallback_tick_| is destroyed. |
- fallback_tick_.Reset(base::Bind(&InProcessViewRenderer::FallbackTickFired, |
- base::Unretained(this))); |
- |
- // No need to reschedule fallback tick if compositor does not need to be |
- // ticked. This can happen if this is reached because |
- // invalidate_ignore_compositor is true. |
- if (compositor_needs_continuous_invalidate_) { |
- BrowserThread::PostDelayedTask( |
- BrowserThread::UI, |
- FROM_HERE, |
- fallback_tick_.callback(), |
- base::TimeDelta::FromMilliseconds( |
- kFallbackTickTimeoutInMilliseconds)); |
+ 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 InProcessViewRenderer::FallbackTickFired() { |
- TRACE_EVENT1("android_webview", |
- "InProcessViewRenderer::FallbackTickFired", |
- "compositor_needs_continuous_invalidate_", |
- compositor_needs_continuous_invalidate_); |
- |
- // This should only be called if OnDraw or DrawGL did not come in time, which |
- // means block_invalidates_ must still be true. |
- DCHECK(block_invalidates_); |
- if (compositor_needs_continuous_invalidate_ && compositor_) |
- ForceFakeCompositeSW(); |
-} |
- |
-void InProcessViewRenderer::ForceFakeCompositeSW() { |
- DCHECK(compositor_); |
- SkBitmapDevice device(SkBitmap::kARGB_8888_Config, 1, 1); |
- SkCanvas canvas(&device); |
- CompositeSW(&canvas); |
+void DeferredGpuCommandService::AddRef() const { |
+ base::RefCountedThreadSafe<DeferredGpuCommandService>::AddRef(); |
} |
-bool InProcessViewRenderer::CompositeSW(SkCanvas* canvas) { |
- DCHECK(compositor_); |
- |
- fallback_tick_.Cancel(); |
- block_invalidates_ = true; |
- bool result = compositor_->DemandDrawSw(canvas); |
- block_invalidates_ = false; |
- EnsureContinuousInvalidation(NULL, false); |
- return result; |
+void DeferredGpuCommandService::Release() const { |
+ base::RefCountedThreadSafe<DeferredGpuCommandService>::Release(); |
} |
-std::string InProcessViewRenderer::ToString(AwDrawGLInfo* draw_info) const { |
- std::string str; |
- base::StringAppendF(&str, "is_paused: %d ", is_paused_); |
- base::StringAppendF(&str, "view_visible: %d ", view_visible_); |
- base::StringAppendF(&str, "window_visible: %d ", window_visible_); |
- base::StringAppendF(&str, "dip_scale: %f ", dip_scale_); |
- base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_); |
- base::StringAppendF(&str, |
- "compositor_needs_continuous_invalidate: %d ", |
- compositor_needs_continuous_invalidate_); |
- base::StringAppendF(&str, "block_invalidates: %d ", block_invalidates_); |
- base::StringAppendF(&str, "view width height: [%d %d] ", width_, height_); |
- base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_); |
- base::StringAppendF(&str, "hardware_initialized: %d ", hardware_initialized_); |
- base::StringAppendF(&str, "hardware_failed: %d ", hardware_failed_); |
- base::StringAppendF(&str, |
- "global visible rect: %s ", |
- cached_global_visible_rect_.ToString().c_str()); |
- base::StringAppendF(&str, |
- "scroll_at_start_of_frame: %s ", |
- scroll_at_start_of_frame_.ToString().c_str()); |
- base::StringAppendF( |
- &str, "scroll_offset_dip: %s ", scroll_offset_dip_.ToString().c_str()); |
- base::StringAppendF(&str, |
- "overscroll_rounding_error_: %s ", |
- overscroll_rounding_error_.ToString().c_str()); |
- base::StringAppendF( |
- &str, "on_new_picture_enable: %d ", on_new_picture_enable_); |
- base::StringAppendF(&str, "clear_view: %d ", clear_view_); |
- if (draw_info) { |
- base::StringAppendF(&str, |
- "clip left top right bottom: [%d %d %d %d] ", |
- draw_info->clip_left, |
- draw_info->clip_top, |
- draw_info->clip_right, |
- draw_info->clip_bottom); |
- base::StringAppendF(&str, |
- "surface width height: [%d %d] ", |
- draw_info->width, |
- draw_info->height); |
- base::StringAppendF(&str, "is_layer: %d ", draw_info->is_layer); |
- } |
- return str; |
-} |
+} // namespace internal |
} // namespace android_webview |