// 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/browser_view_renderer.h" #include "android_webview/browser/browser_view_renderer_client.h" #include "android_webview/browser/shared_renderer_state.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/json/json_writer.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "cc/output/compositor_frame.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/gpu_switches.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "ui/gfx/vector2d_conversions.h" using base::android::AttachCurrentThread; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; using content::BrowserThread; using content::SynchronousCompositorMemoryPolicy; namespace android_webview { namespace { const int64 kFallbackTickTimeoutInMilliseconds = 100; // Used to calculate memory allocation. Determined experimentally. const size_t kMemoryMultiplier = 20; const size_t kBytesPerPixel = 4; const size_t kMemoryAllocationStep = 5 * 1024 * 1024; uint64 g_memory_override_in_bytes = 0u; // Used to calculate tile allocation. Determined experimentally. const size_t kTileMultiplier = 12; const size_t kTileAllocationStep = 20; // This will be set by static function CalculateTileMemoryPolicy() during init. // See AwMainDelegate::BasicStartupComplete. size_t g_tile_area; class TracedValue : public base::debug::ConvertableToTraceFormat { public: explicit TracedValue(base::Value* value) : value_(value) {} static scoped_refptr FromValue( base::Value* value) { return scoped_refptr( new TracedValue(value)); } virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE { std::string tmp; base::JSONWriter::Write(value_.get(), &tmp); *out += tmp; } private: virtual ~TracedValue() {} scoped_ptr value_; DISALLOW_COPY_AND_ASSIGN(TracedValue); }; } // namespace // static void BrowserViewRenderer::CalculateTileMemoryPolicy(bool use_zero_copy) { CommandLine* cl = CommandLine::ForCurrentProcess(); // If the value was overridden on the command line, use the specified value. bool client_hard_limit_bytes_overridden = cl->HasSwitch(switches::kForceGpuMemAvailableMb); if (client_hard_limit_bytes_overridden) { base::StringToUint64( base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kForceGpuMemAvailableMb), &g_memory_override_in_bytes); g_memory_override_in_bytes *= 1024 * 1024; } if (!use_zero_copy) { // Use chrome's default tile size, which varies from 256 to 512. // Be conservative here and use the smallest tile size possible. g_tile_area = 256 * 256; // Also use a high tile limit since there are no file descriptor issues. // There is no need to limit number of tiles, so use an effectively // unlimited value as the limit. GlobalTileManager::GetInstance()->SetTileLimit(10 * 1000 * 1000); return; } const char kDefaultTileSize[] = "384"; if (!cl->HasSwitch(switches::kDefaultTileWidth)) cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); if (!cl->HasSwitch(switches::kDefaultTileHeight)) cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); size_t tile_size; base::StringToSizeT(kDefaultTileSize, &tile_size); g_tile_area = tile_size * tile_size; } BrowserViewRenderer::BrowserViewRenderer( BrowserViewRendererClient* client, SharedRendererState* shared_renderer_state, content::WebContents* web_contents, const scoped_refptr& ui_task_runner) : client_(client), shared_renderer_state_(shared_renderer_state), web_contents_(web_contents), ui_task_runner_(ui_task_runner), compositor_(NULL), is_paused_(false), view_visible_(false), window_visible_(false), attached_to_window_(false), hardware_enabled_(false), dip_scale_(0.0), page_scale_factor_(1.0), on_new_picture_enable_(false), clear_view_(false), compositor_needs_continuous_invalidate_(false), invalidate_after_composite_(false), block_invalidates_(false), fallback_tick_pending_(false), width_(0), height_(0) { CHECK(web_contents_); 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. } BrowserViewRenderer::~BrowserViewRenderer() { content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL); // OnDetachedFromWindow should be called before the destructor, so the memory // policy should have already been updated. } // This function updates the resource allocation in GlobalTileManager. void BrowserViewRenderer::TrimMemory(const int level, const bool visible) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // 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; // Do not release resources on view we expect to get DrawGL soon. if (level < TRIM_MEMORY_BACKGROUND && visible) return; // Just set the memory limit to 0 and drop all tiles. This will be reset to // normal levels in the next DrawGL call. SynchronousCompositorMemoryPolicy zero_policy; if (memory_policy_ == zero_policy) return; TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory"); DCHECK(hardware_enabled_); DCHECK(compositor_); RequestMemoryPolicy(zero_policy); EnforceMemoryPolicyImmediately(zero_policy); } SynchronousCompositorMemoryPolicy BrowserViewRenderer::CalculateDesiredMemoryPolicy() { SynchronousCompositorMemoryPolicy policy; size_t width = last_on_draw_global_visible_rect_.width(); size_t height = last_on_draw_global_visible_rect_.height(); policy.bytes_limit = kMemoryMultiplier * kBytesPerPixel * width * height; // Round up to a multiple of kMemoryAllocationStep. policy.bytes_limit = (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; if (g_memory_override_in_bytes) policy.bytes_limit = static_cast(g_memory_override_in_bytes); size_t tiles = width * height * kTileMultiplier / g_tile_area; // Round up to a multiple of kTileAllocationStep. The minimum number of tiles // is also kTileAllocationStep. tiles = (tiles / kTileAllocationStep + 1) * kTileAllocationStep; policy.num_resources_limit = tiles; return policy; } // This function updates the cached memory policy in shared renderer state, as // well as the tile resource allocation in GlobalTileManager. void BrowserViewRenderer::RequestMemoryPolicy( SynchronousCompositorMemoryPolicy& new_policy) { DCHECK(compositor_); GlobalTileManager* manager = GlobalTileManager::GetInstance(); // The following line will call BrowserViewRenderer::SetMemoryPolicy(). manager->RequestTiles(new_policy, tile_manager_key_); } void BrowserViewRenderer::SetMemoryPolicy( SynchronousCompositorMemoryPolicy new_policy, bool effective_immediately) { memory_policy_ = new_policy; if (effective_immediately) EnforceMemoryPolicyImmediately(memory_policy_); } void BrowserViewRenderer::EnforceMemoryPolicyImmediately( SynchronousCompositorMemoryPolicy new_policy) { compositor_->SetMemoryPolicy(new_policy); ForceFakeCompositeSW(); } SynchronousCompositorMemoryPolicy BrowserViewRenderer::GetMemoryPolicy() const { return memory_policy_; } bool BrowserViewRenderer::OnDraw(jobject java_canvas, bool is_hardware_canvas, const gfx::Vector2d& scroll, const gfx::Rect& global_visible_rect) { last_on_draw_scroll_offset_ = scroll; last_on_draw_global_visible_rect_ = global_visible_rect; if (clear_view_) return false; if (is_hardware_canvas && attached_to_window_ && !switches::ForceAuxiliaryBitmap()) { return OnDrawHardware(java_canvas); } // Perform a software draw return OnDrawSoftware(java_canvas); } bool BrowserViewRenderer::OnDrawHardware(jobject java_canvas) { TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware"); if (!compositor_) return false; shared_renderer_state_->SetScrollOffset(last_on_draw_scroll_offset_); if (!hardware_enabled_) { hardware_enabled_ = compositor_->InitializeHwDraw(); if (hardware_enabled_) { tile_manager_key_ = GlobalTileManager::GetInstance()->PushBack(this); } } if (!hardware_enabled_) return false; if (last_on_draw_global_visible_rect_.IsEmpty() && parent_draw_constraints_.surface_rect.IsEmpty()) { TRACE_EVENT_INSTANT0("android_webview", "EarlyOut_EmptyVisibleRect", TRACE_EVENT_SCOPE_THREAD); shared_renderer_state_->SetForceInvalidateOnNextDrawGL(true); return client_->RequestDrawGL(java_canvas, false); } ReturnResourceFromParent(); if (shared_renderer_state_->HasCompositorFrame()) { TRACE_EVENT_INSTANT0("android_webview", "EarlyOut_PreviousFrameUnconsumed", TRACE_EVENT_SCOPE_THREAD); DidSkipCompositeInDraw(); return client_->RequestDrawGL(java_canvas, false); } scoped_ptr frame = CompositeHw(); if (!frame.get()) return false; shared_renderer_state_->SetCompositorFrame(frame.Pass(), false); GlobalTileManager::GetInstance()->DidUse(tile_manager_key_); return client_->RequestDrawGL(java_canvas, false); } scoped_ptr BrowserViewRenderer::CompositeHw() { SynchronousCompositorMemoryPolicy new_policy = CalculateDesiredMemoryPolicy(); RequestMemoryPolicy(new_policy); compositor_->SetMemoryPolicy(memory_policy_); parent_draw_constraints_ = shared_renderer_state_->ParentDrawConstraints(); gfx::Size surface_size(width_, height_); gfx::Rect viewport(surface_size); gfx::Rect clip = viewport; gfx::Transform transform_for_tile_priority = parent_draw_constraints_.transform; // If the WebView is on a layer, WebView does not know what transform is // applied onto the layer so global visible rect does not make sense here. // In this case, just use the surface rect for tiling. gfx::Rect viewport_rect_for_tile_priority; if (parent_draw_constraints_.is_layer || last_on_draw_global_visible_rect_.IsEmpty()) { viewport_rect_for_tile_priority = parent_draw_constraints_.surface_rect; } else { viewport_rect_for_tile_priority = last_on_draw_global_visible_rect_; } scoped_ptr frame = compositor_->DemandDrawHw(surface_size, gfx::Transform(), viewport, clip, viewport_rect_for_tile_priority, transform_for_tile_priority); if (frame.get()) DidComposite(); return frame.Pass(); } void BrowserViewRenderer::UpdateParentDrawConstraints() { // Post an invalidate if the parent draw constraints are stale and there is // no pending invalidate. bool needs_force_invalidate = shared_renderer_state_->NeedsForceInvalidateOnNextDrawGL(); if (needs_force_invalidate || !parent_draw_constraints_.Equals( shared_renderer_state_->ParentDrawConstraints())) { shared_renderer_state_->SetForceInvalidateOnNextDrawGL(false); EnsureContinuousInvalidation(true, needs_force_invalidate); } } void BrowserViewRenderer::ReturnUnusedResource( scoped_ptr frame) { if (!frame.get()) return; cc::CompositorFrameAck frame_ack; cc::TransferableResource::ReturnResources( frame->delegated_frame_data->resource_list, &frame_ack.resources); if (compositor_ && !frame_ack.resources.empty()) compositor_->ReturnResources(frame_ack); } void BrowserViewRenderer::ReturnResourceFromParent() { cc::CompositorFrameAck frame_ack; shared_renderer_state_->SwapReturnedResources(&frame_ack.resources); if (compositor_ && !frame_ack.resources.empty()) { compositor_->ReturnResources(frame_ack); } } void BrowserViewRenderer::DidSkipCommitFrame() { // Treat it the same way as skipping onDraw. DidSkipCompositeInDraw(); } bool BrowserViewRenderer::OnDrawSoftware(jobject java_canvas) { if (!compositor_) { TRACE_EVENT_INSTANT0( "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD); return false; } // TODO(hush): right now webview size is passed in as the auxiliary bitmap // size, which might hurt performace (only for software draws with auxiliary // bitmap). For better performance, get global visible rect, transform it // from screen space to view space, then intersect with the webview in // viewspace. Use the resulting rect as the auxiliary // bitmap. return BrowserViewRendererJavaHelper::GetInstance() ->RenderViaAuxilaryBitmapIfNeeded( java_canvas, last_on_draw_scroll_offset_, gfx::Size(width_, height_), base::Bind(&BrowserViewRenderer::CompositeSW, base::Unretained(this))); } skia::RefPtr BrowserViewRenderer::CapturePicture(int width, int height) { TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture"); // Return empty Picture objects for empty SkPictures. if (width <= 0 || height <= 0) { SkPictureRecorder emptyRecorder; emptyRecorder.beginRecording(0, 0); return skia::AdoptRef(emptyRecorder.endRecording()); } // Reset scroll back to the origin, will go back to the old // value when scroll_reset is out of scope. base::AutoReset scroll_reset(&scroll_offset_dip_, gfx::Vector2dF()); SkPictureRecorder recorder; SkCanvas* rec_canvas = recorder.beginRecording(width, height, NULL, 0); if (compositor_) CompositeSW(rec_canvas); return skia::AdoptRef(recorder.endRecording()); } void BrowserViewRenderer::EnableOnNewPicture(bool enabled) { on_new_picture_enable_ = enabled; } void BrowserViewRenderer::ClearView() { TRACE_EVENT_INSTANT0("android_webview", "BrowserViewRenderer::ClearView", TRACE_EVENT_SCOPE_THREAD); if (clear_view_) return; clear_view_ = true; // Always invalidate ignoring the compositor to actually clear the webview. EnsureContinuousInvalidation(true, false); } void BrowserViewRenderer::SetIsPaused(bool paused) { TRACE_EVENT_INSTANT1("android_webview", "BrowserViewRenderer::SetIsPaused", TRACE_EVENT_SCOPE_THREAD, "paused", paused); is_paused_ = paused; EnsureContinuousInvalidation(false, false); } void BrowserViewRenderer::SetViewVisibility(bool view_visible) { TRACE_EVENT_INSTANT1("android_webview", "BrowserViewRenderer::SetViewVisibility", TRACE_EVENT_SCOPE_THREAD, "view_visible", view_visible); view_visible_ = view_visible; } void BrowserViewRenderer::SetWindowVisibility(bool window_visible) { TRACE_EVENT_INSTANT1("android_webview", "BrowserViewRenderer::SetWindowVisibility", TRACE_EVENT_SCOPE_THREAD, "window_visible", window_visible); window_visible_ = window_visible; EnsureContinuousInvalidation(false, false); } void BrowserViewRenderer::OnSizeChanged(int width, int height) { TRACE_EVENT_INSTANT2("android_webview", "BrowserViewRenderer::OnSizeChanged", TRACE_EVENT_SCOPE_THREAD, "width", width, "height", height); width_ = width; height_ = height; } void BrowserViewRenderer::OnAttachedToWindow(int width, int height) { TRACE_EVENT2("android_webview", "BrowserViewRenderer::OnAttachedToWindow", "width", width, "height", height); attached_to_window_ = true; width_ = width; height_ = height; } void BrowserViewRenderer::OnDetachedFromWindow() { TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow"); attached_to_window_ = false; DCHECK(!hardware_enabled_); } void BrowserViewRenderer::ReleaseHardware() { DCHECK(hardware_enabled_); ReturnUnusedResource(shared_renderer_state_->PassCompositorFrame()); ReturnResourceFromParent(); DCHECK(shared_renderer_state_->ReturnedResourcesEmpty()); if (compositor_) { compositor_->ReleaseHwDraw(); SynchronousCompositorMemoryPolicy zero_policy; RequestMemoryPolicy(zero_policy); } hardware_enabled_ = false; GlobalTileManager::GetInstance()->Remove(tile_manager_key_); } bool BrowserViewRenderer::IsVisible() const { // Ignore |window_visible_| if |attached_to_window_| is false. return view_visible_ && (!attached_to_window_ || window_visible_); } gfx::Rect BrowserViewRenderer::GetScreenRect() const { return gfx::Rect(client_->GetLocationOnScreen(), gfx::Size(width_, height_)); } void BrowserViewRenderer::DidInitializeCompositor( content::SynchronousCompositor* compositor) { TRACE_EVENT0("android_webview", "BrowserViewRenderer::DidInitializeCompositor"); DCHECK(compositor); DCHECK(!compositor_); compositor_ = compositor; } void BrowserViewRenderer::DidDestroyCompositor( content::SynchronousCompositor* compositor) { TRACE_EVENT0("android_webview", "BrowserViewRenderer::DidDestroyCompositor"); DCHECK(compositor_); SynchronousCompositorMemoryPolicy zero_policy; if (hardware_enabled_) { RequestMemoryPolicy(zero_policy); } DCHECK(memory_policy_ == zero_policy); compositor_ = NULL; } void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate) { if (compositor_needs_continuous_invalidate_ == invalidate) return; TRACE_EVENT_INSTANT1("android_webview", "BrowserViewRenderer::SetContinuousInvalidate", TRACE_EVENT_SCOPE_THREAD, "invalidate", invalidate); compositor_needs_continuous_invalidate_ = invalidate; EnsureContinuousInvalidation(false, false); } void BrowserViewRenderer::SetDipScale(float dip_scale) { dip_scale_ = dip_scale; CHECK_GT(dip_scale_, 0); } gfx::Vector2d BrowserViewRenderer::max_scroll_offset() const { DCHECK_GT(dip_scale_, 0); return gfx::ToCeiledVector2d(gfx::ScaleVector2d( max_scroll_offset_dip_, dip_scale_ * page_scale_factor_)); } void BrowserViewRenderer::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()); } 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; TRACE_EVENT_INSTANT2("android_webview", "BrowserViewRenderer::ScrollTo", TRACE_EVENT_SCOPE_THREAD, "x", scroll_offset_dip.x(), "y", scroll_offset_dip.y()); if (compositor_) compositor_->DidChangeRootLayerScrollOffset(); } void BrowserViewRenderer::DidUpdateContent() { TRACE_EVENT_INSTANT0("android_webview", "BrowserViewRenderer::DidUpdateContent", TRACE_EVENT_SCOPE_THREAD); clear_view_ = false; if (on_new_picture_enable_) client_->OnNewPicture(); } void BrowserViewRenderer::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 // BrowserViewRenderer::ScrollTo. if (max_scroll_offset_dip_.x()) { scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) / max_scroll_offset_dip_.x()); } if (max_scroll_offset_dip_.y()) { scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) / max_scroll_offset_dip_.y()); } DCHECK_LE(0, scroll_offset.x()); DCHECK_LE(0, scroll_offset.y()); DCHECK_LE(scroll_offset.x(), max_offset.x()); DCHECK_LE(scroll_offset.y(), max_offset.y()); client_->ScrollContainerViewTo(scroll_offset); } gfx::Vector2dF BrowserViewRenderer::GetTotalRootLayerScrollOffset() { return scroll_offset_dip_; } bool BrowserViewRenderer::IsExternalFlingActive() const { return client_->IsFlingActive(); } void BrowserViewRenderer::UpdateRootLayerState( const gfx::Vector2dF& total_scroll_offset_dip, const gfx::Vector2dF& max_scroll_offset_dip, const gfx::SizeF& scrollable_size_dip, float page_scale_factor, float min_page_scale_factor, float max_page_scale_factor) { TRACE_EVENT_INSTANT1( "android_webview", "BrowserViewRenderer::UpdateRootLayerState", TRACE_EVENT_SCOPE_THREAD, "state", TracedValue::FromValue( RootLayerStateAsValue(total_scroll_offset_dip, scrollable_size_dip) .release())); DCHECK_GT(dip_scale_, 0); max_scroll_offset_dip_ = max_scroll_offset_dip; DCHECK_LE(0, max_scroll_offset_dip_.x()); DCHECK_LE(0, max_scroll_offset_dip_.y()); page_scale_factor_ = page_scale_factor; DCHECK_GT(page_scale_factor_, 0); client_->UpdateScrollState(max_scroll_offset(), scrollable_size_dip, page_scale_factor, min_page_scale_factor, max_page_scale_factor); SetTotalRootLayerScrollOffset(total_scroll_offset_dip); } scoped_ptr BrowserViewRenderer::RootLayerStateAsValue( const gfx::Vector2dF& total_scroll_offset_dip, const gfx::SizeF& scrollable_size_dip) { scoped_ptr state(new base::DictionaryValue); state->SetDouble("total_scroll_offset_dip.x", total_scroll_offset_dip.x()); state->SetDouble("total_scroll_offset_dip.y", total_scroll_offset_dip.y()); state->SetDouble("max_scroll_offset_dip.x", max_scroll_offset_dip_.x()); state->SetDouble("max_scroll_offset_dip.y", max_scroll_offset_dip_.y()); state->SetDouble("scrollable_size_dip.width", scrollable_size_dip.width()); state->SetDouble("scrollable_size_dip.height", scrollable_size_dip.height()); state->SetDouble("page_scale_factor", page_scale_factor_); return state.PassAs(); } void BrowserViewRenderer::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 BrowserViewRenderer::EnsureContinuousInvalidation( bool force_invalidate, bool skip_reschedule_tick) { if (force_invalidate) invalidate_after_composite_ = true; // This method should be called again when any of these conditions change. bool need_invalidate = compositor_needs_continuous_invalidate_ || invalidate_after_composite_; if (!need_invalidate || block_invalidates_) return; if (!compositor_needs_continuous_invalidate_ && invalidate_after_composite_) invalidate_after_composite_ = false; // 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. client_->PostInvalidate(); // 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_; if (skip_reschedule_tick && fallback_tick_pending_) return; // Unretained here is safe because the callbacks are cancelled when // they are destroyed. post_fallback_tick_.Reset(base::Bind(&BrowserViewRenderer::PostFallbackTick, base::Unretained(this))); fallback_tick_fired_.Cancel(); fallback_tick_pending_ = false; // No need to reschedule fallback tick if compositor does not need to be // ticked. This can happen if this is reached because force_invalidate is // true. if (compositor_needs_continuous_invalidate_) { fallback_tick_pending_ = true; ui_task_runner_->PostTask(FROM_HERE, post_fallback_tick_.callback()); } } void BrowserViewRenderer::PostFallbackTick() { DCHECK(fallback_tick_fired_.IsCancelled()); fallback_tick_fired_.Reset(base::Bind(&BrowserViewRenderer::FallbackTickFired, base::Unretained(this))); if (compositor_needs_continuous_invalidate_) { ui_task_runner_->PostDelayedTask( FROM_HERE, fallback_tick_fired_.callback(), base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds)); } else { // Pretend we just composited to unblock further invalidates. DidComposite(); } } void BrowserViewRenderer::FallbackTickFired() { TRACE_EVENT1("android_webview", "BrowserViewRenderer::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_); fallback_tick_pending_ = false; if (compositor_needs_continuous_invalidate_ && compositor_) { if (hardware_enabled_) { ReturnResourceFromParent(); ReturnUnusedResource(shared_renderer_state_->PassCompositorFrame()); scoped_ptr frame = CompositeHw(); if (frame.get()) { shared_renderer_state_->SetCompositorFrame(frame.Pass(), true); } } else { ForceFakeCompositeSW(); } } else { // Pretend we just composited to unblock further invalidates. DidComposite(); } } void BrowserViewRenderer::ForceFakeCompositeSW() { DCHECK(compositor_); SkBitmap bitmap; bitmap.allocN32Pixels(1, 1); bitmap.eraseColor(0); SkCanvas canvas(bitmap); CompositeSW(&canvas); } bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) { DCHECK(compositor_); ReturnResourceFromParent(); bool result = compositor_->DemandDrawSw(canvas); DidComposite(); return result; } void BrowserViewRenderer::DidComposite() { block_invalidates_ = false; post_fallback_tick_.Cancel(); fallback_tick_fired_.Cancel(); fallback_tick_pending_ = false; EnsureContinuousInvalidation(false, false); } void BrowserViewRenderer::DidSkipCompositeInDraw() { block_invalidates_ = false; EnsureContinuousInvalidation(true, true); } std::string BrowserViewRenderer::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, "global visible rect: %s ", last_on_draw_global_visible_rect_.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 android_webview