1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "android_webview/browser/in_process_view_renderer.h" 6 7#include <android/bitmap.h> 8 9#include "android_webview/browser/aw_gl_surface.h" 10#include "android_webview/browser/scoped_app_gl_state_restore.h" 11#include "android_webview/common/aw_switches.h" 12#include "android_webview/public/browser/draw_gl.h" 13#include "android_webview/public/browser/draw_sw.h" 14#include "base/android/jni_android.h" 15#include "base/auto_reset.h" 16#include "base/command_line.h" 17#include "base/debug/trace_event.h" 18#include "base/lazy_instance.h" 19#include "base/logging.h" 20#include "base/strings/string_number_conversions.h" 21#include "base/strings/stringprintf.h" 22#include "content/public/browser/browser_thread.h" 23#include "content/public/browser/web_contents.h" 24#include "content/public/common/content_switches.h" 25#include "gpu/command_buffer/service/in_process_command_buffer.h" 26#include "third_party/skia/include/core/SkBitmap.h" 27#include "third_party/skia/include/core/SkCanvas.h" 28#include "third_party/skia/include/core/SkDevice.h" 29#include "third_party/skia/include/core/SkGraphics.h" 30#include "third_party/skia/include/core/SkPicture.h" 31#include "third_party/skia/include/utils/SkCanvasStateUtils.h" 32#include "ui/gfx/skia_util.h" 33#include "ui/gfx/transform.h" 34#include "ui/gfx/vector2d_conversions.h" 35#include "ui/gfx/vector2d_f.h" 36 37using base::android::AttachCurrentThread; 38using base::android::JavaRef; 39using base::android::ScopedJavaLocalRef; 40using content::BrowserThread; 41 42namespace android_webview { 43 44namespace { 45 46 47const void* kUserDataKey = &kUserDataKey; 48 49class UserData : public content::WebContents::Data { 50 public: 51 UserData(InProcessViewRenderer* ptr) : instance_(ptr) {} 52 virtual ~UserData() { 53 instance_->WebContentsGone(); 54 } 55 56 static InProcessViewRenderer* GetInstance(content::WebContents* contents) { 57 if (!contents) 58 return NULL; 59 UserData* data = reinterpret_cast<UserData*>( 60 contents->GetUserData(kUserDataKey)); 61 return data ? data->instance_ : NULL; 62 } 63 64 private: 65 InProcessViewRenderer* instance_; 66}; 67 68bool RasterizeIntoBitmap(JNIEnv* env, 69 const JavaRef<jobject>& jbitmap, 70 int scroll_x, 71 int scroll_y, 72 const InProcessViewRenderer::RenderMethod& renderer) { 73 DCHECK(jbitmap.obj()); 74 75 AndroidBitmapInfo bitmap_info; 76 if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) { 77 LOG(ERROR) << "Error getting java bitmap info."; 78 return false; 79 } 80 81 void* pixels = NULL; 82 if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) { 83 LOG(ERROR) << "Error locking java bitmap pixels."; 84 return false; 85 } 86 87 bool succeeded; 88 { 89 SkBitmap bitmap; 90 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 91 bitmap_info.width, 92 bitmap_info.height, 93 bitmap_info.stride); 94 bitmap.setPixels(pixels); 95 96 SkDevice device(bitmap); 97 SkCanvas canvas(&device); 98 canvas.translate(-scroll_x, -scroll_y); 99 succeeded = renderer.Run(&canvas); 100 } 101 102 if (AndroidBitmap_unlockPixels(env, jbitmap.obj()) < 0) { 103 LOG(ERROR) << "Error unlocking java bitmap pixels."; 104 return false; 105 } 106 107 return succeeded; 108} 109 110bool RenderPictureToCanvas(SkPicture* picture, SkCanvas* canvas) { 111 canvas->drawPicture(*picture); 112 return true; 113} 114 115class ScopedPixelAccess { 116 public: 117 ScopedPixelAccess(JNIEnv* env, jobject java_canvas) { 118 AwDrawSWFunctionTable* sw_functions = 119 BrowserViewRenderer::GetAwDrawSWFunctionTable(); 120 pixels_ = sw_functions ? 121 sw_functions->access_pixels(env, java_canvas) : NULL; 122 } 123 ~ScopedPixelAccess() { 124 if (pixels_) 125 BrowserViewRenderer::GetAwDrawSWFunctionTable()->release_pixels(pixels_); 126 } 127 AwPixelInfo* pixels() { return pixels_; } 128 129 private: 130 AwPixelInfo* pixels_; 131 132 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPixelAccess); 133}; 134 135bool HardwareEnabled() { 136 static bool g_hw_enabled = !CommandLine::ForCurrentProcess()->HasSwitch( 137 switches::kDisableWebViewGLMode); 138 return g_hw_enabled; 139} 140 141// Provides software rendering functions from the Android glue layer. 142// Allows preventing extra copies of data when rendering. 143AwDrawSWFunctionTable* g_sw_draw_functions = NULL; 144 145const int64 kFallbackTickTimeoutInMilliseconds = 20; 146 147 148// Used to calculate memory and resource allocation. Determined experimentally. 149size_t g_memory_multiplier = 10; 150size_t g_num_gralloc_limit = 150; 151const size_t kBytesPerPixel = 4; 152const size_t kMemoryAllocationStep = 5 * 1024 * 1024; 153 154class ScopedAllowGL { 155 public: 156 ScopedAllowGL(); 157 ~ScopedAllowGL(); 158 159 static bool IsAllowed() { 160 return BrowserThread::CurrentlyOn(BrowserThread::UI) && allow_gl; 161 } 162 163 private: 164 static bool allow_gl; 165 166 DISALLOW_COPY_AND_ASSIGN(ScopedAllowGL); 167}; 168 169ScopedAllowGL::ScopedAllowGL() { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 171 DCHECK(!allow_gl); 172 allow_gl = true; 173} 174 175ScopedAllowGL::~ScopedAllowGL() { 176 allow_gl = false; 177} 178 179bool ScopedAllowGL::allow_gl = false; 180 181base::LazyInstance<GLViewRendererManager>::Leaky g_view_renderer_manager; 182 183void RequestProcessGLOnUIThread() { 184 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 185 BrowserThread::PostTask( 186 BrowserThread::UI, FROM_HERE, base::Bind(&RequestProcessGLOnUIThread)); 187 return; 188 } 189 190 InProcessViewRenderer* renderer = static_cast<InProcessViewRenderer*>( 191 g_view_renderer_manager.Get().GetMostRecentlyDrawn()); 192 if (!renderer || !renderer->RequestProcessGL()) { 193 LOG(ERROR) << "Failed to request GL process. Deadlock likely: " 194 << !!renderer; 195 } 196} 197 198} // namespace 199 200// Called from different threads! 201static void ScheduleGpuWork() { 202 if (ScopedAllowGL::IsAllowed()) { 203 gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread(); 204 } else { 205 RequestProcessGLOnUIThread(); 206 } 207} 208 209// static 210void BrowserViewRenderer::SetAwDrawSWFunctionTable( 211 AwDrawSWFunctionTable* table) { 212 g_sw_draw_functions = table; 213 gpu::InProcessCommandBuffer::SetScheduleCallback( 214 base::Bind(&ScheduleGpuWork)); 215} 216 217// static 218AwDrawSWFunctionTable* BrowserViewRenderer::GetAwDrawSWFunctionTable() { 219 return g_sw_draw_functions; 220} 221 222InProcessViewRenderer::InProcessViewRenderer( 223 BrowserViewRenderer::Client* client, 224 JavaHelper* java_helper, 225 content::WebContents* web_contents) 226 : client_(client), 227 java_helper_(java_helper), 228 web_contents_(web_contents), 229 compositor_(NULL), 230 is_paused_(false), 231 view_visible_(false), 232 window_visible_(false), 233 attached_to_window_(false), 234 dip_scale_(0.0), 235 page_scale_factor_(1.0), 236 on_new_picture_enable_(false), 237 compositor_needs_continuous_invalidate_(false), 238 block_invalidates_(false), 239 width_(0), 240 height_(0), 241 hardware_initialized_(false), 242 hardware_failed_(false), 243 last_egl_context_(NULL), 244 manager_key_(g_view_renderer_manager.Get().NullKey()) { 245 CHECK(web_contents_); 246 web_contents_->SetUserData(kUserDataKey, new UserData(this)); 247 content::SynchronousCompositor::SetClientForWebContents(web_contents_, this); 248 249 // Currently the logic in this class relies on |compositor_| remaining NULL 250 // until the DidInitializeCompositor() call, hence it is not set here. 251} 252 253InProcessViewRenderer::~InProcessViewRenderer() { 254 CHECK(web_contents_); 255 content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL); 256 web_contents_->SetUserData(kUserDataKey, NULL); 257 NoLongerExpectsDrawGL(); 258 DCHECK(web_contents_ == NULL); // WebContentsGone should have been called. 259} 260 261 262// TODO(boliu): Should also call this when we know for sure we are no longer, 263// for example, when visible rect becomes 0. 264void InProcessViewRenderer::NoLongerExpectsDrawGL() { 265 GLViewRendererManager& mru = g_view_renderer_manager.Get(); 266 if (manager_key_ != mru.NullKey()) { 267 mru.NoLongerExpectsDrawGL(manager_key_); 268 manager_key_ = mru.NullKey(); 269 270 // TODO(boliu): If this is the first one and there are GL pending, 271 // requestDrawGL on next one. 272 // TODO(boliu): If this is the last one, lose all global contexts. 273 } 274} 275 276// static 277InProcessViewRenderer* InProcessViewRenderer::FromWebContents( 278 content::WebContents* contents) { 279 return UserData::GetInstance(contents); 280} 281 282void InProcessViewRenderer::WebContentsGone() { 283 web_contents_ = NULL; 284 compositor_ = NULL; 285} 286 287// static 288void InProcessViewRenderer::CalculateTileMemoryPolicy() { 289 CommandLine* cl = CommandLine::ForCurrentProcess(); 290 if (cl->HasSwitch(switches::kTileMemoryMultiplier)) { 291 std::string string_value = 292 cl->GetSwitchValueASCII(switches::kTileMemoryMultiplier); 293 int int_value = 0; 294 if (base::StringToInt(string_value, &int_value) && 295 int_value >= 2 && int_value <= 50) { 296 g_memory_multiplier = int_value; 297 } 298 } 299 300 if (cl->HasSwitch(switches::kNumGrallocBuffersPerWebview)) { 301 std::string string_value = 302 cl->GetSwitchValueASCII(switches::kNumGrallocBuffersPerWebview); 303 int int_value = 0; 304 if (base::StringToInt(string_value, &int_value) && 305 int_value >= 50 && int_value <= 500) { 306 g_num_gralloc_limit = int_value; 307 } 308 } 309 310 const char kDefaultTileSize[] = "384"; 311 if (!cl->HasSwitch(switches::kDefaultTileWidth)) 312 cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize); 313 314 if (!cl->HasSwitch(switches::kDefaultTileHeight)) 315 cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize); 316} 317 318bool InProcessViewRenderer::RequestProcessGL() { 319 return client_->RequestDrawGL(NULL); 320} 321 322void InProcessViewRenderer::TrimMemory(int level) { 323 // Constants from Android ComponentCallbacks2. 324 enum { 325 TRIM_MEMORY_RUNNING_LOW = 10, 326 TRIM_MEMORY_UI_HIDDEN = 20, 327 TRIM_MEMORY_BACKGROUND = 40, 328 }; 329 330 // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because 331 // it does not indicate memory pressure, but merely that the app is 332 // backgrounded. 333 if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN) 334 return; 335 336 // Nothing to drop. 337 if (!attached_to_window_ || !hardware_initialized_ || !compositor_) 338 return; 339 340 // Do not release resources on view we expect to get DrawGL soon. 341 if (level < TRIM_MEMORY_BACKGROUND) { 342 client_->UpdateGlobalVisibleRect(); 343 if (view_visible_ && window_visible_ && 344 !cached_global_visible_rect_.IsEmpty()) { 345 return; 346 } 347 } 348 349 if (!eglGetCurrentContext()) { 350 NOTREACHED(); 351 return; 352 } 353 354 // Just set the memory limit to 0 and drop all tiles. This will be reset to 355 // normal levels in the next DrawGL call. 356 content::SynchronousCompositorMemoryPolicy policy; 357 policy.bytes_limit = 0; 358 policy.num_resources_limit = 0; 359 if (memory_policy_ == policy) 360 return; 361 362 TRACE_EVENT0("android_webview", "InProcessViewRenderer::TrimMemory"); 363 ScopedAppGLStateRestore state_restore( 364 ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); 365 gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread(); 366 ScopedAllowGL allow_gl; 367 368 SetMemoryPolicy(policy); 369 ForceFakeCompositeSW(); 370} 371 372void InProcessViewRenderer::SetMemoryPolicy( 373 content::SynchronousCompositorMemoryPolicy& new_policy) { 374 if (memory_policy_ == new_policy) 375 return; 376 377 memory_policy_ = new_policy; 378 compositor_->SetMemoryPolicy(memory_policy_); 379} 380 381void InProcessViewRenderer::UpdateCachedGlobalVisibleRect() { 382 client_->UpdateGlobalVisibleRect(); 383} 384 385bool InProcessViewRenderer::OnDraw(jobject java_canvas, 386 bool is_hardware_canvas, 387 const gfx::Vector2d& scroll, 388 const gfx::Rect& clip) { 389 scroll_at_start_of_frame_ = scroll; 390 if (is_hardware_canvas && attached_to_window_ && HardwareEnabled()) { 391 // We should be performing a hardware draw here. If we don't have the 392 // comositor yet or if RequestDrawGL fails, it means we failed this draw and 393 // thus return false here to clear to background color for this draw. 394 return compositor_ && client_->RequestDrawGL(java_canvas); 395 } 396 // Perform a software draw 397 return DrawSWInternal(java_canvas, clip); 398} 399 400bool InProcessViewRenderer::InitializeHwDraw() { 401 TRACE_EVENT0("android_webview", "InitializeHwDraw"); 402 DCHECK(!gl_surface_); 403 gl_surface_ = new AwGLSurface; 404 hardware_failed_ = !compositor_->InitializeHwDraw(gl_surface_); 405 hardware_initialized_ = true; 406 407 if (hardware_failed_) 408 gl_surface_ = NULL; 409 410 return !hardware_failed_; 411} 412 413void InProcessViewRenderer::DrawGL(AwDrawGLInfo* draw_info) { 414 TRACE_EVENT0("android_webview", "InProcessViewRenderer::DrawGL"); 415 416 manager_key_ = g_view_renderer_manager.Get().DidDrawGL(manager_key_, this); 417 418 // We need to watch if the current Android context has changed and enforce 419 // a clean-up in the compositor. 420 EGLContext current_context = eglGetCurrentContext(); 421 if (!current_context) { 422 TRACE_EVENT_INSTANT0( 423 "android_webview", "EarlyOut_NullEGLContext", TRACE_EVENT_SCOPE_THREAD); 424 return; 425 } 426 427 ScopedAppGLStateRestore state_restore(ScopedAppGLStateRestore::MODE_DRAW); 428 gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread(); 429 ScopedAllowGL allow_gl; 430 431 if (!attached_to_window_) { 432 TRACE_EVENT_INSTANT0( 433 "android_webview", "EarlyOut_NotAttached", TRACE_EVENT_SCOPE_THREAD); 434 return; 435 } 436 437 if (draw_info->mode == AwDrawGLInfo::kModeProcess) { 438 TRACE_EVENT_INSTANT0( 439 "android_webview", "EarlyOut_ModeProcess", TRACE_EVENT_SCOPE_THREAD); 440 return; 441 } 442 443 if (compositor_ && !hardware_initialized_) { 444 if (InitializeHwDraw()) { 445 last_egl_context_ = current_context; 446 } else { 447 TRACE_EVENT_INSTANT0( 448 "android_webview", "EarlyOut_HwInitFail", TRACE_EVENT_SCOPE_THREAD); 449 LOG(ERROR) << "WebView hardware initialization failed"; 450 return; 451 } 452 } 453 454 UpdateCachedGlobalVisibleRect(); 455 if (cached_global_visible_rect_.IsEmpty()) { 456 TRACE_EVENT_INSTANT0("android_webview", 457 "EarlyOut_EmptyVisibleRect", 458 TRACE_EVENT_SCOPE_THREAD); 459 return; 460 } 461 462 if (last_egl_context_ != current_context) { 463 // TODO(boliu): Handle context lost 464 TRACE_EVENT_INSTANT0( 465 "android_webview", "EGLContextChanged", TRACE_EVENT_SCOPE_THREAD); 466 } 467 468 if (!compositor_) { 469 TRACE_EVENT_INSTANT0( 470 "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD); 471 return; 472 } 473 474 // DrawGL may be called without OnDraw, so cancel |fallback_tick_| here as 475 // well just to be safe. 476 fallback_tick_.Cancel(); 477 478 // Update memory budget. This will no-op in compositor if the policy has not 479 // changed since last draw. 480 content::SynchronousCompositorMemoryPolicy policy; 481 policy.bytes_limit = g_memory_multiplier * kBytesPerPixel * 482 cached_global_visible_rect_.width() * 483 cached_global_visible_rect_.height(); 484 // Round up to a multiple of kMemoryAllocationStep. 485 policy.bytes_limit = 486 (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; 487 policy.num_resources_limit = g_num_gralloc_limit; 488 SetMemoryPolicy(policy); 489 490 DCHECK(gl_surface_); 491 gl_surface_->SetBackingFrameBufferObject( 492 state_restore.framebuffer_binding_ext()); 493 494 gfx::Transform transform; 495 transform.matrix().setColMajorf(draw_info->transform); 496 transform.Translate(scroll_at_start_of_frame_.x(), 497 scroll_at_start_of_frame_.y()); 498 gfx::Rect clip_rect(draw_info->clip_left, 499 draw_info->clip_top, 500 draw_info->clip_right - draw_info->clip_left, 501 draw_info->clip_bottom - draw_info->clip_top); 502 503 // Assume we always draw the full visible rect if we are drawing into a layer. 504 bool drew_full_visible_rect = true; 505 506 gfx::Rect viewport_rect; 507 if (!draw_info->is_layer) { 508 viewport_rect = cached_global_visible_rect_; 509 clip_rect.Intersect(viewport_rect); 510 drew_full_visible_rect = clip_rect.Contains(viewport_rect); 511 } else { 512 viewport_rect = clip_rect; 513 } 514 515 block_invalidates_ = true; 516 // TODO(joth): Check return value. 517 compositor_->DemandDrawHw(gfx::Size(draw_info->width, draw_info->height), 518 transform, 519 viewport_rect, 520 clip_rect, 521 state_restore.stencil_enabled()); 522 block_invalidates_ = false; 523 gl_surface_->ResetBackingFrameBufferObject(); 524 525 EnsureContinuousInvalidation(draw_info, !drew_full_visible_rect); 526} 527 528void InProcessViewRenderer::SetGlobalVisibleRect( 529 const gfx::Rect& visible_rect) { 530 cached_global_visible_rect_ = visible_rect; 531} 532 533bool InProcessViewRenderer::DrawSWInternal(jobject java_canvas, 534 const gfx::Rect& clip) { 535 if (clip.IsEmpty()) { 536 TRACE_EVENT_INSTANT0( 537 "android_webview", "EarlyOut_EmptyClip", TRACE_EVENT_SCOPE_THREAD); 538 return true; 539 } 540 541 if (!compositor_) { 542 TRACE_EVENT_INSTANT0( 543 "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD); 544 return false; 545 } 546 547 return RenderViaAuxilaryBitmapIfNeeded( 548 java_canvas, 549 java_helper_, 550 scroll_at_start_of_frame_, 551 clip, 552 base::Bind(&InProcessViewRenderer::CompositeSW, 553 base::Unretained(this)), 554 web_contents_); 555} 556 557// static 558bool InProcessViewRenderer::RenderViaAuxilaryBitmapIfNeeded( 559 jobject java_canvas, 560 BrowserViewRenderer::JavaHelper* java_helper, 561 const gfx::Vector2d& scroll_correction, 562 const gfx::Rect& clip, 563 InProcessViewRenderer::RenderMethod render_source, 564 void* owner_key) { 565 TRACE_EVENT0("android_webview", 566 "InProcessViewRenderer::RenderViaAuxilaryBitmapIfNeeded"); 567 568 JNIEnv* env = AttachCurrentThread(); 569 ScopedPixelAccess auto_release_pixels(env, java_canvas); 570 AwPixelInfo* pixels = auto_release_pixels.pixels(); 571 if (pixels && pixels->state) { 572 skia::RefPtr<SkCanvas> canvas = skia::AdoptRef( 573 SkCanvasStateUtils::CreateFromCanvasState(pixels->state)); 574 575 // Workarounds for http://crbug.com/271096: SW draw only supports 576 // translate & scale transforms, and a simple rectangular clip. 577 if (canvas && (!canvas->getTotalClip().isRect() || 578 (canvas->getTotalMatrix().getType() & 579 ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)))) { 580 canvas.clear(); 581 } 582 if (canvas) { 583 canvas->translate(scroll_correction.x(), scroll_correction.y()); 584 return render_source.Run(canvas.get()); 585 } 586 } 587 588 // Render into an auxiliary bitmap if pixel info is not available. 589 ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas); 590 TRACE_EVENT0("android_webview", "RenderToAuxBitmap"); 591 ScopedJavaLocalRef<jobject> jbitmap(java_helper->CreateBitmap( 592 env, clip.width(), clip.height(), jcanvas, owner_key)); 593 if (!jbitmap.obj()) { 594 TRACE_EVENT_INSTANT0("android_webview", 595 "EarlyOut_BitmapAllocFail", 596 TRACE_EVENT_SCOPE_THREAD); 597 return false; 598 } 599 600 if (!RasterizeIntoBitmap(env, jbitmap, 601 clip.x() - scroll_correction.x(), 602 clip.y() - scroll_correction.y(), 603 render_source)) { 604 TRACE_EVENT_INSTANT0("android_webview", 605 "EarlyOut_RasterizeFail", 606 TRACE_EVENT_SCOPE_THREAD); 607 return false; 608 } 609 610 java_helper->DrawBitmapIntoCanvas(env, jbitmap, jcanvas, 611 clip.x(), clip.y()); 612 return true; 613} 614 615skia::RefPtr<SkPicture> InProcessViewRenderer::CapturePicture(int width, 616 int height) { 617 TRACE_EVENT0("android_webview", "InProcessViewRenderer::CapturePicture"); 618 619 // Return empty Picture objects for empty SkPictures. 620 skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); 621 if (width <= 0 || height <= 0) { 622 return picture; 623 } 624 625 // Reset scroll back to the origin, will go back to the old 626 // value when scroll_reset is out of scope. 627 base::AutoReset<gfx::Vector2dF> scroll_reset(&scroll_offset_dip_, 628 gfx::Vector2d()); 629 630 SkCanvas* rec_canvas = picture->beginRecording(width, height, 0); 631 if (compositor_) 632 CompositeSW(rec_canvas); 633 picture->endRecording(); 634 return picture; 635} 636 637void InProcessViewRenderer::EnableOnNewPicture(bool enabled) { 638 on_new_picture_enable_ = enabled; 639 EnsureContinuousInvalidation(NULL, false); 640} 641 642void InProcessViewRenderer::SetIsPaused(bool paused) { 643 TRACE_EVENT_INSTANT1("android_webview", 644 "InProcessViewRenderer::SetIsPaused", 645 TRACE_EVENT_SCOPE_THREAD, 646 "paused", 647 paused); 648 is_paused_ = paused; 649 EnsureContinuousInvalidation(NULL, false); 650} 651 652void InProcessViewRenderer::SetViewVisibility(bool view_visible) { 653 TRACE_EVENT_INSTANT1("android_webview", 654 "InProcessViewRenderer::SetViewVisibility", 655 TRACE_EVENT_SCOPE_THREAD, 656 "view_visible", 657 view_visible); 658 view_visible_ = view_visible; 659} 660 661void InProcessViewRenderer::SetWindowVisibility(bool window_visible) { 662 TRACE_EVENT_INSTANT1("android_webview", 663 "InProcessViewRenderer::SetWindowVisibility", 664 TRACE_EVENT_SCOPE_THREAD, 665 "window_visible", 666 window_visible); 667 window_visible_ = window_visible; 668 EnsureContinuousInvalidation(NULL, false); 669} 670 671void InProcessViewRenderer::OnSizeChanged(int width, int height) { 672 TRACE_EVENT_INSTANT2("android_webview", 673 "InProcessViewRenderer::OnSizeChanged", 674 TRACE_EVENT_SCOPE_THREAD, 675 "width", 676 width, 677 "height", 678 height); 679 width_ = width; 680 height_ = height; 681} 682 683void InProcessViewRenderer::OnAttachedToWindow(int width, int height) { 684 TRACE_EVENT2("android_webview", 685 "InProcessViewRenderer::OnAttachedToWindow", 686 "width", 687 width, 688 "height", 689 height); 690 attached_to_window_ = true; 691 width_ = width; 692 height_ = height; 693} 694 695void InProcessViewRenderer::OnDetachedFromWindow() { 696 TRACE_EVENT0("android_webview", 697 "InProcessViewRenderer::OnDetachedFromWindow"); 698 699 NoLongerExpectsDrawGL(); 700 if (hardware_initialized_) { 701 DCHECK(compositor_); 702 703 ScopedAppGLStateRestore state_restore( 704 ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT); 705 gpu::InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread(); 706 ScopedAllowGL allow_gl; 707 compositor_->ReleaseHwDraw(); 708 hardware_initialized_ = false; 709 } 710 711 gl_surface_ = NULL; 712 attached_to_window_ = false; 713} 714 715bool InProcessViewRenderer::IsAttachedToWindow() { 716 return attached_to_window_; 717} 718 719bool InProcessViewRenderer::IsVisible() { 720 // Ignore |window_visible_| if |attached_to_window_| is false. 721 return view_visible_ && (!attached_to_window_ || window_visible_); 722} 723 724gfx::Rect InProcessViewRenderer::GetScreenRect() { 725 return gfx::Rect(client_->GetLocationOnScreen(), gfx::Size(width_, height_)); 726} 727 728void InProcessViewRenderer::DidInitializeCompositor( 729 content::SynchronousCompositor* compositor) { 730 TRACE_EVENT0("android_webview", 731 "InProcessViewRenderer::DidInitializeCompositor"); 732 DCHECK(compositor && compositor_ == NULL); 733 compositor_ = compositor; 734 hardware_initialized_ = false; 735 hardware_failed_ = false; 736} 737 738void InProcessViewRenderer::DidDestroyCompositor( 739 content::SynchronousCompositor* compositor) { 740 TRACE_EVENT0("android_webview", 741 "InProcessViewRenderer::DidDestroyCompositor"); 742 DCHECK(compositor_ == compositor); 743 744 // This can fail if Apps call destroy while the webview is still attached 745 // to the view tree. This is an illegal operation that will lead to leaks. 746 // Log for now. Consider a proper fix if this becomes a problem. 747 LOG_IF(ERROR, hardware_initialized_) 748 << "Destroy called before OnDetachedFromWindow. May Leak GL resources"; 749 compositor_ = NULL; 750} 751 752void InProcessViewRenderer::SetContinuousInvalidate(bool invalidate) { 753 if (compositor_needs_continuous_invalidate_ == invalidate) 754 return; 755 756 TRACE_EVENT_INSTANT1("android_webview", 757 "InProcessViewRenderer::SetContinuousInvalidate", 758 TRACE_EVENT_SCOPE_THREAD, 759 "invalidate", 760 invalidate); 761 compositor_needs_continuous_invalidate_ = invalidate; 762 EnsureContinuousInvalidation(NULL, false); 763} 764 765void InProcessViewRenderer::SetDipScale(float dip_scale) { 766 dip_scale_ = dip_scale; 767 CHECK(dip_scale_ > 0); 768} 769 770gfx::Vector2d InProcessViewRenderer::max_scroll_offset() const { 771 DCHECK_GT(dip_scale_, 0); 772 return gfx::ToCeiledVector2d(gfx::ScaleVector2d( 773 max_scroll_offset_dip_, dip_scale_ * page_scale_factor_)); 774} 775 776void InProcessViewRenderer::ScrollTo(gfx::Vector2d scroll_offset) { 777 gfx::Vector2d max_offset = max_scroll_offset(); 778 gfx::Vector2dF scroll_offset_dip; 779 // To preserve the invariant that scrolling to the maximum physical pixel 780 // value also scrolls to the maximum dip pixel value we transform the physical 781 // offset into the dip offset by using a proportion (instead of dividing by 782 // dip_scale * page_scale_factor). 783 if (max_offset.x()) { 784 scroll_offset_dip.set_x((scroll_offset.x() * max_scroll_offset_dip_.x()) / 785 max_offset.x()); 786 } 787 if (max_offset.y()) { 788 scroll_offset_dip.set_y((scroll_offset.y() * max_scroll_offset_dip_.y()) / 789 max_offset.y()); 790 } 791 792 DCHECK_LE(0, scroll_offset_dip.x()); 793 DCHECK_LE(0, scroll_offset_dip.y()); 794 DCHECK_LE(scroll_offset_dip.x(), max_scroll_offset_dip_.x()); 795 DCHECK_LE(scroll_offset_dip.y(), max_scroll_offset_dip_.y()); 796 797 if (scroll_offset_dip_ == scroll_offset_dip) 798 return; 799 800 scroll_offset_dip_ = scroll_offset_dip; 801 802 if (compositor_) 803 compositor_->DidChangeRootLayerScrollOffset(); 804} 805 806void InProcessViewRenderer::DidUpdateContent() { 807 if (on_new_picture_enable_) 808 client_->OnNewPicture(); 809} 810 811void InProcessViewRenderer::SetMaxRootLayerScrollOffset( 812 gfx::Vector2dF new_value_dip) { 813 DCHECK_GT(dip_scale_, 0); 814 815 max_scroll_offset_dip_ = new_value_dip; 816 DCHECK_LE(0, max_scroll_offset_dip_.x()); 817 DCHECK_LE(0, max_scroll_offset_dip_.y()); 818 819 client_->SetMaxContainerViewScrollOffset(max_scroll_offset()); 820} 821 822void InProcessViewRenderer::SetTotalRootLayerScrollOffset( 823 gfx::Vector2dF scroll_offset_dip) { 824 // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during 825 // DrawGl when http://crbug.com/249972 is fixed. 826 if (scroll_offset_dip_ == scroll_offset_dip) 827 return; 828 829 scroll_offset_dip_ = scroll_offset_dip; 830 831 gfx::Vector2d max_offset = max_scroll_offset(); 832 gfx::Vector2d scroll_offset; 833 // For an explanation as to why this is done this way see the comment in 834 // InProcessViewRenderer::ScrollTo. 835 if (max_scroll_offset_dip_.x()) { 836 scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) / 837 max_scroll_offset_dip_.x()); 838 } 839 840 if (max_scroll_offset_dip_.y()) { 841 scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) / 842 max_scroll_offset_dip_.y()); 843 } 844 845 DCHECK(0 <= scroll_offset.x()); 846 DCHECK(0 <= scroll_offset.y()); 847 DCHECK(scroll_offset.x() <= max_offset.x()); 848 DCHECK(scroll_offset.y() <= max_offset.y()); 849 850 client_->ScrollContainerViewTo(scroll_offset); 851} 852 853gfx::Vector2dF InProcessViewRenderer::GetTotalRootLayerScrollOffset() { 854 return scroll_offset_dip_; 855} 856 857bool InProcessViewRenderer::IsExternalFlingActive() const { 858 return client_->IsFlingActive(); 859} 860 861void InProcessViewRenderer::SetRootLayerPageScaleFactor( 862 float page_scale_factor) { 863 page_scale_factor_ = page_scale_factor; 864 DCHECK_GT(page_scale_factor_, 0); 865 client_->SetPageScaleFactor(page_scale_factor); 866} 867 868void InProcessViewRenderer::SetRootLayerScrollableSize( 869 gfx::SizeF scrollable_size) { 870 client_->SetContentsSize(scrollable_size); 871} 872 873void InProcessViewRenderer::DidOverscroll( 874 gfx::Vector2dF accumulated_overscroll, 875 gfx::Vector2dF latest_overscroll_delta, 876 gfx::Vector2dF current_fling_velocity) { 877 DCHECK(current_fling_velocity.IsZero()); 878 const float physical_pixel_scale = dip_scale_ * page_scale_factor_; 879 if (accumulated_overscroll == latest_overscroll_delta) 880 overscroll_rounding_error_ = gfx::Vector2dF(); 881 gfx::Vector2dF scaled_overscroll_delta = 882 gfx::ScaleVector2d(latest_overscroll_delta, physical_pixel_scale); 883 gfx::Vector2d rounded_overscroll_delta = gfx::ToRoundedVector2d( 884 scaled_overscroll_delta + overscroll_rounding_error_); 885 overscroll_rounding_error_ = 886 scaled_overscroll_delta - rounded_overscroll_delta; 887 client_->DidOverscroll(rounded_overscroll_delta); 888} 889 890void InProcessViewRenderer::EnsureContinuousInvalidation( 891 AwDrawGLInfo* draw_info, 892 bool invalidate_ignore_compositor) { 893 // This method should be called again when any of these conditions change. 894 bool need_invalidate = 895 compositor_needs_continuous_invalidate_ || invalidate_ignore_compositor; 896 if (!need_invalidate || block_invalidates_) 897 return; 898 899 if (draw_info) { 900 draw_info->dirty_left = cached_global_visible_rect_.x(); 901 draw_info->dirty_top = cached_global_visible_rect_.y(); 902 draw_info->dirty_right = cached_global_visible_rect_.right(); 903 draw_info->dirty_bottom = cached_global_visible_rect_.bottom(); 904 draw_info->status_mask |= AwDrawGLInfo::kStatusMaskDraw; 905 } else { 906 client_->PostInvalidate(); 907 } 908 909 bool throttle_fallback_tick = (is_paused_ && !on_new_picture_enable_) || 910 (attached_to_window_ && !window_visible_); 911 if (throttle_fallback_tick) 912 return; 913 914 block_invalidates_ = compositor_needs_continuous_invalidate_; 915 916 // Unretained here is safe because the callback is cancelled when 917 // |fallback_tick_| is destroyed. 918 fallback_tick_.Reset(base::Bind(&InProcessViewRenderer::FallbackTickFired, 919 base::Unretained(this))); 920 921 // No need to reschedule fallback tick if compositor does not need to be 922 // ticked. This can happen if this is reached because 923 // invalidate_ignore_compositor is true. 924 if (compositor_needs_continuous_invalidate_) { 925 BrowserThread::PostDelayedTask( 926 BrowserThread::UI, 927 FROM_HERE, 928 fallback_tick_.callback(), 929 base::TimeDelta::FromMilliseconds( 930 kFallbackTickTimeoutInMilliseconds)); 931 } 932} 933 934void InProcessViewRenderer::FallbackTickFired() { 935 TRACE_EVENT1("android_webview", 936 "InProcessViewRenderer::FallbackTickFired", 937 "compositor_needs_continuous_invalidate_", 938 compositor_needs_continuous_invalidate_); 939 940 // This should only be called if OnDraw or DrawGL did not come in time, which 941 // means block_invalidates_ must still be true. 942 DCHECK(block_invalidates_); 943 if (compositor_needs_continuous_invalidate_ && compositor_) 944 ForceFakeCompositeSW(); 945} 946 947void InProcessViewRenderer::ForceFakeCompositeSW() { 948 DCHECK(compositor_); 949 SkDevice device(SkBitmap::kARGB_8888_Config, 1, 1); 950 SkCanvas canvas(&device); 951 CompositeSW(&canvas); 952} 953 954bool InProcessViewRenderer::CompositeSW(SkCanvas* canvas) { 955 DCHECK(compositor_); 956 957 fallback_tick_.Cancel(); 958 block_invalidates_ = true; 959 bool result = compositor_->DemandDrawSw(canvas); 960 block_invalidates_ = false; 961 EnsureContinuousInvalidation(NULL, false); 962 return result; 963} 964 965std::string InProcessViewRenderer::ToString(AwDrawGLInfo* draw_info) const { 966 std::string str; 967 base::StringAppendF(&str, "is_paused: %d ", is_paused_); 968 base::StringAppendF(&str, "view_visible: %d ", view_visible_); 969 base::StringAppendF(&str, "window_visible: %d ", window_visible_); 970 base::StringAppendF(&str, "dip_scale: %f ", dip_scale_); 971 base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_); 972 base::StringAppendF(&str, 973 "compositor_needs_continuous_invalidate: %d ", 974 compositor_needs_continuous_invalidate_); 975 base::StringAppendF(&str, "block_invalidates: %d ", block_invalidates_); 976 base::StringAppendF(&str, "view width height: [%d %d] ", width_, height_); 977 base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_); 978 base::StringAppendF(&str, "hardware_initialized: %d ", hardware_initialized_); 979 base::StringAppendF(&str, "hardware_failed: %d ", hardware_failed_); 980 base::StringAppendF(&str, 981 "global visible rect: %s ", 982 cached_global_visible_rect_.ToString().c_str()); 983 base::StringAppendF(&str, 984 "scroll_at_start_of_frame: %s ", 985 scroll_at_start_of_frame_.ToString().c_str()); 986 base::StringAppendF( 987 &str, "scroll_offset_dip: %s ", scroll_offset_dip_.ToString().c_str()); 988 base::StringAppendF(&str, 989 "overscroll_rounding_error_: %s ", 990 overscroll_rounding_error_.ToString().c_str()); 991 base::StringAppendF( 992 &str, "on_new_picture_enable: %d ", on_new_picture_enable_); 993 if (draw_info) { 994 base::StringAppendF(&str, 995 "clip left top right bottom: [%d %d %d %d] ", 996 draw_info->clip_left, 997 draw_info->clip_top, 998 draw_info->clip_right, 999 draw_info->clip_bottom); 1000 base::StringAppendF(&str, 1001 "surface width height: [%d %d] ", 1002 draw_info->width, 1003 draw_info->height); 1004 base::StringAppendF(&str, "is_layer: %d ", draw_info->is_layer); 1005 } 1006 return str; 1007} 1008 1009} // namespace android_webview 1010