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