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 51 namespace { 52 53 const unsigned int kMaxSwapBuffers = 2U; 54 55 // Used to override capabilities_.adjust_deadline_for_parent to false 56 class 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 77 class 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 }; 140 base::LazyInstance<SurfaceTextureTrackerImpl> g_surface_texture_tracker = 141 LAZY_INSTANCE_INITIALIZER; 142 143 static bool g_initialized = false; 144 145 } // anonymous namespace 146 147 namespace content { 148 149 // static 150 Compositor* Compositor::Create(CompositorClient* client, 151 gfx::NativeWindow root_window) { 152 return client ? new CompositorImpl(client, root_window) : NULL; 153 } 154 155 // static 156 void 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 165 bool CompositorImpl::IsInitialized() { 166 return g_initialized; 167 } 168 169 // static 170 int 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 183 void CompositorImpl::DestroyAllSurfaceTextures(int child_process_id) { 184 g_surface_texture_tracker.Pointer()->RemoveAllSurfaceTextures( 185 child_process_id); 186 } 187 188 CompositorImpl::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 211 CompositorImpl::~CompositorImpl() { 212 root_window_->DetachCompositor(); 213 ImageTransportFactoryAndroid::RemoveObserver(this); 214 // Clean-up any surface references. 215 SetSurface(NULL); 216 } 217 218 void 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 278 void 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 335 void CompositorImpl::OnGpuChannelEstablished() { 336 ScheduleComposite(); 337 } 338 339 UIResourceProvider& CompositorImpl::GetUIResourceProvider() { 340 return ui_resource_provider_; 341 } 342 343 void 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 354 void 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 376 void 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 401 void 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 439 void CompositorImpl::setDeviceScaleFactor(float factor) { 440 device_scale_factor_ = factor; 441 if (host_) 442 host_->SetDeviceScaleFactor(factor); 443 } 444 445 void 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 455 void CompositorImpl::SetHasTransparentBackground(bool flag) { 456 has_transparent_background_ = flag; 457 if (host_) 458 host_->set_has_transparent_background(flag); 459 } 460 461 void CompositorImpl::SetNeedsComposite() { 462 if (!host_.get()) 463 return; 464 DCHECK(!needs_composite_ || WillComposite()); 465 466 needs_composite_ = true; 467 PostComposite(COMPOSITE_IMMEDIATELY); 468 } 469 470 static scoped_ptr<WebGraphicsContext3DCommandBufferImpl> 471 CreateGpuProcessViewContext( 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 502 void 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 509 scoped_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 537 void CompositorImpl::OnLostResources() { 538 client_->DidLoseResources(); 539 ui_resource_provider_.UIResourcesAreInvalid(); 540 } 541 542 void 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 554 void 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 567 void CompositorImpl::DidPostSwapBuffers() { 568 TRACE_EVENT0("compositor", "CompositorImpl::DidPostSwapBuffers"); 569 did_post_swapbuffers_ = true; 570 } 571 572 void 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 580 void 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 588 void CompositorImpl::DidCommit() { 589 root_window_->OnCompositingDidCommit(); 590 } 591 592 void CompositorImpl::AttachLayerForReadback(scoped_refptr<cc::Layer> layer) { 593 root_layer_->AddChild(layer); 594 } 595 596 void CompositorImpl::RequestCopyOfOutputOnRootLayer( 597 scoped_ptr<cc::CopyOutputRequest> request) { 598 root_layer_->RequestCopyOfOutput(request.Pass()); 599 } 600 601 void 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 624 void CompositorImpl::SetNeedsAnimate() { 625 if (!host_) 626 return; 627 628 host_->SetNeedsAnimate(); 629 } 630 631 } // namespace content 632