1// Copyright (c) 2012 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 "content/browser/renderer_host/compositor_impl_android.h"
6
7#include <android/bitmap.h>
8#include <android/native_window_jni.h>
9
10#include "base/android/jni_android.h"
11#include "base/android/scoped_java_ref.h"
12#include "base/bind.h"
13#include "base/command_line.h"
14#include "base/containers/hash_tables.h"
15#include "base/lazy_instance.h"
16#include "base/logging.h"
17#include "base/single_thread_task_runner.h"
18#include "base/synchronization/lock.h"
19#include "base/threading/thread.h"
20#include "base/threading/thread_checker.h"
21#include "cc/base/switches.h"
22#include "cc/input/input_handler.h"
23#include "cc/layers/layer.h"
24#include "cc/output/compositor_frame.h"
25#include "cc/output/context_provider.h"
26#include "cc/output/output_surface.h"
27#include "cc/trees/layer_tree_host.h"
28#include "content/browser/android/child_process_launcher_android.h"
29#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
30#include "content/browser/gpu/gpu_surface_tracker.h"
31#include "content/common/gpu/client/command_buffer_proxy_impl.h"
32#include "content/common/gpu/client/context_provider_command_buffer.h"
33#include "content/common/gpu/client/gl_helper.h"
34#include "content/common/gpu/client/gpu_channel_host.h"
35#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
36#include "content/common/gpu/gpu_process_launch_causes.h"
37#include "content/common/host_shared_bitmap_manager.h"
38#include "content/public/browser/android/compositor_client.h"
39#include "gpu/command_buffer/client/gles2_interface.h"
40#include "third_party/khronos/GLES2/gl2.h"
41#include "third_party/khronos/GLES2/gl2ext.h"
42#include "third_party/skia/include/core/SkMallocPixelRef.h"
43#include "ui/base/android/window_android.h"
44#include "ui/gfx/android/device_display_info.h"
45#include "ui/gfx/frame_time.h"
46#include "ui/gl/android/surface_texture.h"
47#include "ui/gl/android/surface_texture_tracker.h"
48#include "webkit/common/gpu/context_provider_in_process.h"
49#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
50
51namespace {
52
53const unsigned int kMaxSwapBuffers = 2U;
54
55// Used to override capabilities_.adjust_deadline_for_parent to false
56class OutputSurfaceWithoutParent : public cc::OutputSurface {
57 public:
58  OutputSurfaceWithoutParent(const scoped_refptr<
59      content::ContextProviderCommandBuffer>& context_provider)
60      : cc::OutputSurface(context_provider) {
61    capabilities_.adjust_deadline_for_parent = false;
62  }
63
64  virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE {
65    content::ContextProviderCommandBuffer* provider_command_buffer =
66        static_cast<content::ContextProviderCommandBuffer*>(
67            context_provider_.get());
68    content::CommandBufferProxyImpl* command_buffer_proxy =
69        provider_command_buffer->GetCommandBufferProxy();
70    DCHECK(command_buffer_proxy);
71    command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info);
72
73    OutputSurface::SwapBuffers(frame);
74  }
75};
76
77class SurfaceTextureTrackerImpl : public gfx::SurfaceTextureTracker {
78 public:
79  SurfaceTextureTrackerImpl() : next_surface_texture_id_(1) {
80    thread_checker_.DetachFromThread();
81  }
82
83  // Overridden from gfx::SurfaceTextureTracker:
84  virtual scoped_refptr<gfx::SurfaceTexture> AcquireSurfaceTexture(
85      int primary_id,
86      int secondary_id) OVERRIDE {
87    base::AutoLock lock(surface_textures_lock_);
88    SurfaceTextureMapKey key(primary_id, secondary_id);
89    SurfaceTextureMap::iterator it = surface_textures_.find(key);
90    if (it == surface_textures_.end())
91      return scoped_refptr<gfx::SurfaceTexture>();
92    scoped_refptr<gfx::SurfaceTexture> surface_texture = it->second;
93    surface_textures_.erase(it);
94    return surface_texture;
95  }
96
97  int AddSurfaceTexture(gfx::SurfaceTexture* surface_texture,
98                        int child_process_id) {
99    DCHECK(thread_checker_.CalledOnValidThread());
100    int surface_texture_id = next_surface_texture_id_++;
101    if (next_surface_texture_id_ == INT_MAX)
102      next_surface_texture_id_ = 1;
103
104    base::AutoLock lock(surface_textures_lock_);
105    SurfaceTextureMapKey key(surface_texture_id, child_process_id);
106    DCHECK(surface_textures_.find(key) == surface_textures_.end());
107    surface_textures_[key] = surface_texture;
108    content::RegisterChildProcessSurfaceTexture(
109        surface_texture_id,
110        child_process_id,
111        surface_texture->j_surface_texture().obj());
112    return surface_texture_id;
113  }
114
115  void RemoveAllSurfaceTextures(int child_process_id) {
116    DCHECK(thread_checker_.CalledOnValidThread());
117    base::AutoLock lock(surface_textures_lock_);
118    SurfaceTextureMap::iterator it = surface_textures_.begin();
119    while (it != surface_textures_.end()) {
120      if (it->first.second == child_process_id) {
121        content::UnregisterChildProcessSurfaceTexture(it->first.first,
122                                                      it->first.second);
123        surface_textures_.erase(it++);
124      } else {
125        ++it;
126      }
127    }
128  }
129
130 private:
131  typedef std::pair<int, int> SurfaceTextureMapKey;
132  typedef base::hash_map<SurfaceTextureMapKey,
133                         scoped_refptr<gfx::SurfaceTexture> >
134      SurfaceTextureMap;
135  SurfaceTextureMap surface_textures_;
136  mutable base::Lock surface_textures_lock_;
137  int next_surface_texture_id_;
138  base::ThreadChecker thread_checker_;
139};
140base::LazyInstance<SurfaceTextureTrackerImpl> g_surface_texture_tracker =
141    LAZY_INSTANCE_INITIALIZER;
142
143static bool g_initialized = false;
144
145} // anonymous namespace
146
147namespace content {
148
149// static
150Compositor* Compositor::Create(CompositorClient* client,
151                               gfx::NativeWindow root_window) {
152  return client ? new CompositorImpl(client, root_window) : NULL;
153}
154
155// static
156void Compositor::Initialize() {
157  DCHECK(!CompositorImpl::IsInitialized());
158  // SurfaceTextureTracker instance must be set before we create a GPU thread
159  // that could be using it to initialize GLImage instances.
160  gfx::SurfaceTextureTracker::InitInstance(g_surface_texture_tracker.Pointer());
161  g_initialized = true;
162}
163
164// static
165bool CompositorImpl::IsInitialized() {
166  return g_initialized;
167}
168
169// static
170int CompositorImpl::CreateSurfaceTexture(int child_process_id) {
171  // Note: this needs to be 0 as the surface texture implemenation will take
172  // ownership of the texture and call glDeleteTextures when the GPU service
173  // attaches the surface texture to a real texture id. glDeleteTextures
174  // silently ignores 0.
175  const int kDummyTextureId = 0;
176  scoped_refptr<gfx::SurfaceTexture> surface_texture =
177      gfx::SurfaceTexture::Create(kDummyTextureId);
178  return g_surface_texture_tracker.Pointer()->AddSurfaceTexture(
179      surface_texture.get(), child_process_id);
180}
181
182// static
183void CompositorImpl::DestroyAllSurfaceTextures(int child_process_id) {
184  g_surface_texture_tracker.Pointer()->RemoveAllSurfaceTextures(
185      child_process_id);
186}
187
188CompositorImpl::CompositorImpl(CompositorClient* client,
189                               gfx::NativeWindow root_window)
190    : root_layer_(cc::Layer::Create()),
191      has_transparent_background_(false),
192      device_scale_factor_(1),
193      window_(NULL),
194      surface_id_(0),
195      client_(client),
196      root_window_(root_window),
197      did_post_swapbuffers_(false),
198      ignore_schedule_composite_(false),
199      needs_composite_(false),
200      needs_animate_(false),
201      will_composite_immediately_(false),
202      composite_on_vsync_trigger_(DO_NOT_COMPOSITE),
203      pending_swapbuffers_(0U),
204      weak_factory_(this) {
205  DCHECK(client);
206  DCHECK(root_window);
207  ImageTransportFactoryAndroid::AddObserver(this);
208  root_window->AttachCompositor(this);
209}
210
211CompositorImpl::~CompositorImpl() {
212  root_window_->DetachCompositor();
213  ImageTransportFactoryAndroid::RemoveObserver(this);
214  // Clean-up any surface references.
215  SetSurface(NULL);
216}
217
218void CompositorImpl::PostComposite(CompositingTrigger trigger) {
219  DCHECK(needs_composite_);
220  DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY);
221
222  if (will_composite_immediately_ ||
223      (trigger == COMPOSITE_EVENTUALLY && WillComposite())) {
224    // We will already composite soon enough.
225    DCHECK(WillComposite());
226    return;
227  }
228
229  if (DidCompositeThisFrame()) {
230    DCHECK(!WillCompositeThisFrame());
231    if (composite_on_vsync_trigger_ != COMPOSITE_IMMEDIATELY) {
232      composite_on_vsync_trigger_ = trigger;
233      root_window_->RequestVSyncUpdate();
234    }
235    DCHECK(WillComposite());
236    return;
237  }
238
239  base::TimeDelta delay;
240  if (trigger == COMPOSITE_IMMEDIATELY) {
241    will_composite_immediately_ = true;
242    composite_on_vsync_trigger_ = DO_NOT_COMPOSITE;
243  } else {
244    DCHECK(!WillComposite());
245    const base::TimeDelta estimated_composite_time = vsync_period_ / 4;
246    const base::TimeTicks now = base::TimeTicks::Now();
247
248    if (!last_vsync_.is_null() && (now - last_vsync_) < vsync_period_) {
249      base::TimeTicks next_composite =
250          last_vsync_ + vsync_period_ - estimated_composite_time;
251      if (next_composite < now) {
252        // It's too late, we will reschedule composite as needed on the next
253        // vsync.
254        composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY;
255        root_window_->RequestVSyncUpdate();
256        DCHECK(WillComposite());
257        return;
258      }
259
260      delay = next_composite - now;
261    }
262  }
263  TRACE_EVENT2("cc", "CompositorImpl::PostComposite",
264               "trigger", trigger,
265               "delay", delay.InMillisecondsF());
266
267  DCHECK(composite_on_vsync_trigger_ == DO_NOT_COMPOSITE);
268  if (current_composite_task_)
269    current_composite_task_->Cancel();
270
271  // Unretained because we cancel the task on shutdown.
272  current_composite_task_.reset(new base::CancelableClosure(
273      base::Bind(&CompositorImpl::Composite, base::Unretained(this), trigger)));
274  base::MessageLoop::current()->PostDelayedTask(
275      FROM_HERE, current_composite_task_->callback(), delay);
276}
277
278void CompositorImpl::Composite(CompositingTrigger trigger) {
279  BrowserGpuChannelHostFactory* factory =
280      BrowserGpuChannelHostFactory::instance();
281  if (!factory->GetGpuChannel() || factory->GetGpuChannel()->IsLost()) {
282    CauseForGpuLaunch cause =
283        CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
284    factory->EstablishGpuChannel(
285        cause,
286        base::Bind(&CompositorImpl::OnGpuChannelEstablished,
287                   weak_factory_.GetWeakPtr()));
288    return;
289  }
290
291  DCHECK(host_);
292  DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY);
293  DCHECK(needs_composite_);
294  DCHECK(!DidCompositeThisFrame());
295
296  if (trigger == COMPOSITE_IMMEDIATELY)
297    will_composite_immediately_ = false;
298
299  DCHECK_LE(pending_swapbuffers_, kMaxSwapBuffers);
300  if (pending_swapbuffers_ == kMaxSwapBuffers) {
301    TRACE_EVENT0("compositor", "CompositorImpl_SwapLimit");
302    return;
303  }
304
305  // Reset state before Layout+Composite since that might create more
306  // requests to Composite that we need to respect.
307  needs_composite_ = false;
308
309  // Only allow compositing once per vsync.
310  current_composite_task_->Cancel();
311  DCHECK(DidCompositeThisFrame() && !WillComposite());
312
313  // Ignore ScheduleComposite() from layer tree changes during layout and
314  // animation updates that will already be reflected in the current frame
315  // we are about to draw.
316  ignore_schedule_composite_ = true;
317  client_->Layout();
318
319  const base::TimeTicks frame_time = gfx::FrameTime::Now();
320  if (needs_animate_) {
321    needs_animate_ = false;
322    root_window_->Animate(frame_time);
323  }
324  ignore_schedule_composite_ = false;
325
326  did_post_swapbuffers_ = false;
327  host_->Composite(frame_time);
328  if (did_post_swapbuffers_)
329    pending_swapbuffers_++;
330
331  // Need to track vsync to avoid compositing more than once per frame.
332  root_window_->RequestVSyncUpdate();
333}
334
335void CompositorImpl::OnGpuChannelEstablished() {
336  ScheduleComposite();
337}
338
339UIResourceProvider& CompositorImpl::GetUIResourceProvider() {
340  return ui_resource_provider_;
341}
342
343void CompositorImpl::SetRootLayer(scoped_refptr<cc::Layer> root_layer) {
344  if (subroot_layer_) {
345    subroot_layer_->RemoveFromParent();
346    subroot_layer_ = NULL;
347  }
348  if (root_layer) {
349    subroot_layer_ = root_layer;
350    root_layer_->AddChild(root_layer);
351  }
352}
353
354void CompositorImpl::SetWindowSurface(ANativeWindow* window) {
355  GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get();
356
357  if (window_) {
358    tracker->RemoveSurface(surface_id_);
359    ANativeWindow_release(window_);
360    window_ = NULL;
361    surface_id_ = 0;
362    SetVisible(false);
363  }
364
365  if (window) {
366    window_ = window;
367    ANativeWindow_acquire(window);
368    surface_id_ = tracker->AddSurfaceForNativeWidget(window);
369    tracker->SetSurfaceHandle(
370        surface_id_,
371        gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT));
372    SetVisible(true);
373  }
374}
375
376void CompositorImpl::SetSurface(jobject surface) {
377  JNIEnv* env = base::android::AttachCurrentThread();
378  base::android::ScopedJavaLocalRef<jobject> j_surface(env, surface);
379
380  // First, cleanup any existing surface references.
381  if (surface_id_)
382    content::UnregisterViewSurface(surface_id_);
383  SetWindowSurface(NULL);
384
385  // Now, set the new surface if we have one.
386  ANativeWindow* window = NULL;
387  if (surface) {
388    // Note: This ensures that any local references used by
389    // ANativeWindow_fromSurface are released immediately. This is needed as a
390    // workaround for https://code.google.com/p/android/issues/detail?id=68174
391    base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
392    window = ANativeWindow_fromSurface(env, surface);
393  }
394  if (window) {
395    SetWindowSurface(window);
396    ANativeWindow_release(window);
397    content::RegisterViewSurface(surface_id_, j_surface.obj());
398  }
399}
400
401void CompositorImpl::SetVisible(bool visible) {
402  if (!visible) {
403    if (WillComposite())
404      CancelComposite();
405    ui_resource_provider_.SetLayerTreeHost(NULL);
406    host_.reset();
407  } else if (!host_) {
408    DCHECK(!WillComposite());
409    needs_composite_ = false;
410    needs_animate_ = false;
411    pending_swapbuffers_ = 0;
412    cc::LayerTreeSettings settings;
413    settings.refresh_rate = 60.0;
414    settings.impl_side_painting = false;
415    settings.allow_antialiasing = false;
416    settings.calculate_top_controls_position = false;
417    settings.top_controls_height = 0.f;
418    settings.highp_threshold_min = 2048;
419
420    CommandLine* command_line = CommandLine::ForCurrentProcess();
421    settings.initial_debug_state.SetRecordRenderingStats(
422        command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking));
423    settings.initial_debug_state.show_fps_counter =
424        command_line->HasSwitch(cc::switches::kUIShowFPSCounter);
425
426    host_ = cc::LayerTreeHost::CreateSingleThreaded(
427        this, this, HostSharedBitmapManager::current(), settings);
428    host_->SetRootLayer(root_layer_);
429
430    host_->SetVisible(true);
431    host_->SetLayerTreeHostClientReady();
432    host_->SetViewportSize(size_);
433    host_->set_has_transparent_background(has_transparent_background_);
434    host_->SetDeviceScaleFactor(device_scale_factor_);
435    ui_resource_provider_.SetLayerTreeHost(host_.get());
436  }
437}
438
439void CompositorImpl::setDeviceScaleFactor(float factor) {
440  device_scale_factor_ = factor;
441  if (host_)
442    host_->SetDeviceScaleFactor(factor);
443}
444
445void CompositorImpl::SetWindowBounds(const gfx::Size& size) {
446  if (size_ == size)
447    return;
448
449  size_ = size;
450  if (host_)
451    host_->SetViewportSize(size);
452  root_layer_->SetBounds(size);
453}
454
455void CompositorImpl::SetHasTransparentBackground(bool flag) {
456  has_transparent_background_ = flag;
457  if (host_)
458    host_->set_has_transparent_background(flag);
459}
460
461void CompositorImpl::SetNeedsComposite() {
462  if (!host_.get())
463    return;
464  DCHECK(!needs_composite_ || WillComposite());
465
466  needs_composite_ = true;
467  PostComposite(COMPOSITE_IMMEDIATELY);
468}
469
470static scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
471CreateGpuProcessViewContext(
472    const scoped_refptr<GpuChannelHost>& gpu_channel_host,
473    const blink::WebGraphicsContext3D::Attributes attributes,
474    int surface_id) {
475  DCHECK(gpu_channel_host);
476
477  GURL url("chrome://gpu/Compositor::createContext3D");
478  static const size_t kBytesPerPixel = 4;
479  gfx::DeviceDisplayInfo display_info;
480  size_t full_screen_texture_size_in_bytes =
481      display_info.GetDisplayHeight() *
482      display_info.GetDisplayWidth() *
483      kBytesPerPixel;
484  WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits limits;
485  limits.command_buffer_size = 64 * 1024;
486  limits.start_transfer_buffer_size = 64 * 1024;
487  limits.min_transfer_buffer_size = 64 * 1024;
488  limits.max_transfer_buffer_size = std::min(
489      3 * full_screen_texture_size_in_bytes, kDefaultMaxTransferBufferSize);
490  limits.mapped_memory_reclaim_limit = 2 * 1024 * 1024;
491  bool lose_context_when_out_of_memory = true;
492  return make_scoped_ptr(
493      new WebGraphicsContext3DCommandBufferImpl(surface_id,
494                                                url,
495                                                gpu_channel_host.get(),
496                                                attributes,
497                                                lose_context_when_out_of_memory,
498                                                limits,
499                                                NULL));
500}
501
502void CompositorImpl::Layout() {
503  // TODO: If we get this callback from the SingleThreadProxy, we need
504  // to stop calling it ourselves in CompositorImpl::Composite().
505  NOTREACHED();
506  client_->Layout();
507}
508
509scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface(
510    bool fallback) {
511  blink::WebGraphicsContext3D::Attributes attrs;
512  attrs.shareResources = true;
513  attrs.noAutomaticFlushes = true;
514  pending_swapbuffers_ = 0;
515
516  DCHECK(window_);
517  DCHECK(surface_id_);
518
519  scoped_refptr<ContextProviderCommandBuffer> context_provider;
520  BrowserGpuChannelHostFactory* factory =
521      BrowserGpuChannelHostFactory::instance();
522  scoped_refptr<GpuChannelHost> gpu_channel_host = factory->GetGpuChannel();
523  if (gpu_channel_host && !gpu_channel_host->IsLost()) {
524    context_provider = ContextProviderCommandBuffer::Create(
525        CreateGpuProcessViewContext(gpu_channel_host, attrs, surface_id_),
526        "BrowserCompositor");
527  }
528  if (!context_provider.get()) {
529    LOG(ERROR) << "Failed to create 3D context for compositor.";
530    return scoped_ptr<cc::OutputSurface>();
531  }
532
533  return scoped_ptr<cc::OutputSurface>(
534      new OutputSurfaceWithoutParent(context_provider));
535}
536
537void CompositorImpl::OnLostResources() {
538  client_->DidLoseResources();
539  ui_resource_provider_.UIResourcesAreInvalid();
540}
541
542void CompositorImpl::ScheduleComposite() {
543  DCHECK(!needs_composite_ || WillComposite());
544  if (ignore_schedule_composite_)
545    return;
546
547  needs_composite_ = true;
548  // We currently expect layer tree invalidations at most once per frame
549  // during normal operation and therefore try to composite immediately
550  // to minimize latency.
551  PostComposite(COMPOSITE_IMMEDIATELY);
552}
553
554void CompositorImpl::ScheduleAnimation() {
555  DCHECK(!needs_animate_ || needs_composite_);
556  DCHECK(!needs_composite_ || WillComposite());
557  needs_animate_ = true;
558
559  if (needs_composite_)
560    return;
561
562  TRACE_EVENT0("cc", "CompositorImpl::ScheduleAnimation");
563  needs_composite_ = true;
564  PostComposite(COMPOSITE_EVENTUALLY);
565}
566
567void CompositorImpl::DidPostSwapBuffers() {
568  TRACE_EVENT0("compositor", "CompositorImpl::DidPostSwapBuffers");
569  did_post_swapbuffers_ = true;
570}
571
572void CompositorImpl::DidCompleteSwapBuffers() {
573  TRACE_EVENT0("compositor", "CompositorImpl::DidCompleteSwapBuffers");
574  DCHECK_GT(pending_swapbuffers_, 0U);
575  if (pending_swapbuffers_-- == kMaxSwapBuffers && needs_composite_)
576    PostComposite(COMPOSITE_IMMEDIATELY);
577  client_->OnSwapBuffersCompleted(pending_swapbuffers_);
578}
579
580void CompositorImpl::DidAbortSwapBuffers() {
581  TRACE_EVENT0("compositor", "CompositorImpl::DidAbortSwapBuffers");
582  // This really gets called only once from
583  // SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() when the
584  // context was lost.
585  client_->OnSwapBuffersCompleted(0);
586}
587
588void CompositorImpl::DidCommit() {
589  root_window_->OnCompositingDidCommit();
590}
591
592void CompositorImpl::AttachLayerForReadback(scoped_refptr<cc::Layer> layer) {
593  root_layer_->AddChild(layer);
594}
595
596void CompositorImpl::RequestCopyOfOutputOnRootLayer(
597    scoped_ptr<cc::CopyOutputRequest> request) {
598  root_layer_->RequestCopyOfOutput(request.Pass());
599}
600
601void CompositorImpl::OnVSync(base::TimeTicks frame_time,
602                             base::TimeDelta vsync_period) {
603  vsync_period_ = vsync_period;
604  last_vsync_ = frame_time;
605
606  if (WillCompositeThisFrame()) {
607    // We somehow missed the last vsync interval, so reschedule for deadline.
608    // We cannot schedule immediately, or will get us out-of-phase with new
609    // renderer frames.
610    CancelComposite();
611    composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY;
612  } else {
613    current_composite_task_.reset();
614  }
615
616  DCHECK(!DidCompositeThisFrame() && !WillCompositeThisFrame());
617  if (composite_on_vsync_trigger_ != DO_NOT_COMPOSITE) {
618    CompositingTrigger trigger = composite_on_vsync_trigger_;
619    composite_on_vsync_trigger_ = DO_NOT_COMPOSITE;
620    PostComposite(trigger);
621  }
622}
623
624void CompositorImpl::SetNeedsAnimate() {
625  if (!host_)
626    return;
627
628  host_->SetNeedsAnimate();
629}
630
631}  // namespace content
632