APZUpdater.cpp (24155B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/layers/APZUpdater.h" 8 9 #include "APZCTreeManager.h" 10 #include "AsyncPanZoomController.h" 11 #include "base/task.h" 12 #include "mozilla/ClearOnShutdown.h" 13 #include "mozilla/layers/APZThreadUtils.h" 14 #include "mozilla/layers/CompositorThread.h" 15 #include "mozilla/layers/SynchronousTask.h" 16 #include "mozilla/layers/WebRenderScrollDataWrapper.h" 17 #include "mozilla/webrender/WebRenderAPI.h" 18 19 namespace mozilla { 20 namespace layers { 21 22 StaticMutex APZUpdater::sWindowIdLock; 23 StaticAutoPtr<std::unordered_map<uint64_t, APZUpdater*>> 24 APZUpdater::sWindowIdMap; 25 26 APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz, 27 bool aConnectedToWebRender) 28 : mApz(aApz), 29 mDestroyed(false), 30 mConnectedToWebRender(aConnectedToWebRender), 31 mThreadIdLock("APZUpdater::ThreadIdLock"), 32 mQueueLock("APZUpdater::QueueLock") { 33 MOZ_ASSERT(aApz); 34 mApz->SetUpdater(this); 35 } 36 37 APZUpdater::~APZUpdater() { 38 mApz->SetUpdater(nullptr); 39 40 StaticMutexAutoLock lock(sWindowIdLock); 41 if (mWindowId) { 42 MOZ_ASSERT(sWindowIdMap); 43 // Ensure that ClearTree was called and the task got run 44 MOZ_ASSERT(sWindowIdMap->find(wr::AsUint64(*mWindowId)) == 45 sWindowIdMap->end()); 46 } 47 } 48 49 bool APZUpdater::HasTreeManager(const RefPtr<APZCTreeManager>& aApz) { 50 return aApz.get() == mApz.get(); 51 } 52 53 void APZUpdater::SetWebRenderWindowId(const wr::WindowId& aWindowId) { 54 StaticMutexAutoLock lock(sWindowIdLock); 55 MOZ_ASSERT(!mWindowId); 56 mWindowId = Some(aWindowId); 57 if (!sWindowIdMap) { 58 sWindowIdMap = new std::unordered_map<uint64_t, APZUpdater*>(); 59 NS_DispatchToMainThread(NS_NewRunnableFunction( 60 "APZUpdater::ClearOnShutdown", [] { ClearOnShutdown(&sWindowIdMap); })); 61 } 62 (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this; 63 } 64 65 /*static*/ 66 void APZUpdater::SetUpdaterThread(const wr::WrWindowId& aWindowId) { 67 if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) { 68 MutexAutoLock lock(updater->mThreadIdLock); 69 updater->mUpdaterThreadId = Some(PlatformThread::CurrentId()); 70 } 71 } 72 73 // Takes a conditional lock! 74 /*static*/ 75 void APZUpdater::PrepareForSceneSwap(const wr::WrWindowId& aWindowId) 76 MOZ_NO_THREAD_SAFETY_ANALYSIS { 77 if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) { 78 updater->mApz->LockTree(); 79 } 80 } 81 82 // Assumes we took a conditional lock! 83 /*static*/ 84 void APZUpdater::CompleteSceneSwap(const wr::WrWindowId& aWindowId, 85 const wr::WrPipelineInfo& aInfo) { 86 RefPtr<APZUpdater> updater = GetUpdater(aWindowId); 87 if (!updater) { 88 // This should only happen in cases where PrepareForSceneSwap also got a 89 // null updater. No updater-thread tasks get run between PrepareForSceneSwap 90 // and this function, so there is no opportunity for the updater mapping 91 // to have gotten removed from sWindowIdMap in between the two calls. 92 return; 93 } 94 updater->mApz->mTreeLock.AssertCurrentThreadIn(); 95 96 for (const auto& removedPipeline : aInfo.removed_pipelines) { 97 LayersId layersId = wr::AsLayersId(removedPipeline.pipeline_id); 98 updater->mEpochData.erase(layersId); 99 } 100 // Reset the built info for all pipelines, then put it back for the ones 101 // that got built in this scene swap. 102 for (auto& i : updater->mEpochData) { 103 i.second.mBuilt = Nothing(); 104 } 105 for (const auto& epoch : aInfo.epochs) { 106 LayersId layersId = wr::AsLayersId(epoch.pipeline_id); 107 updater->mEpochData[layersId].mBuilt = Some(epoch.epoch); 108 } 109 110 // Run any tasks that got unblocked, then unlock the tree. The order is 111 // important because we want to run all the tasks up to and including the 112 // UpdateHitTestingTree calls corresponding to the built epochs, and we 113 // want to run those before we release the lock (i.e. atomically with the 114 // scene swap). This ensures that any hit-tests always encounter a consistent 115 // state between the APZ tree and the built scene in WR. 116 // 117 // While we could add additional information to the queued tasks to figure 118 // out the minimal set of tasks we want to run here, it's easier and harmless 119 // to just run all the queued and now-unblocked tasks inside the lock. 120 // 121 // Note that the ProcessQueue here might remove the window id -> APZUpdater 122 // mapping from sWindowIdMap, but we still unlock the tree successfully to 123 // leave things in a good state. 124 updater->ProcessQueue(); 125 126 updater->mApz->UnlockTree(); 127 } 128 129 /*static*/ 130 void APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId) { 131 if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) { 132 updater->ProcessQueue(); 133 } 134 } 135 136 void APZUpdater::ClearTree(LayersId aRootLayersId) { 137 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 138 RefPtr<APZUpdater> self = this; 139 RunOnUpdaterThread( 140 aRootLayersId, 141 NS_NewRunnableFunction("APZUpdater::ClearTree", 142 [=]() { 143 self->mApz->ClearTree(); 144 self->mDestroyed = true; 145 146 // Once ClearTree is called on the 147 // APZCTreeManager, we are in a shutdown phase. 148 // After this point it's ok if WebRender cannot 149 // get a hold of the updater via the window id, 150 // and it's a good point to remove the mapping 151 // and avoid leaving a dangling pointer to this 152 // object. 153 StaticMutexAutoLock lock(sWindowIdLock); 154 if (self->mWindowId) { 155 MOZ_ASSERT(sWindowIdMap); 156 sWindowIdMap->erase( 157 wr::AsUint64(*(self->mWindowId))); 158 } 159 }), 160 DuringShutdown::Yes); 161 } 162 163 void APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId, 164 LayersId aOriginatingLayersId, 165 const FocusTarget& aFocusTarget) { 166 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 167 RunOnUpdaterThread(aOriginatingLayersId, 168 NewRunnableMethod<LayersId, LayersId, FocusTarget>( 169 "APZUpdater::UpdateFocusState", mApz, 170 &APZCTreeManager::UpdateFocusState, aRootLayerTreeId, 171 aOriginatingLayersId, aFocusTarget)); 172 } 173 174 void APZUpdater::UpdateScrollDataAndTreeState( 175 LayersId aRootLayerTreeId, LayersId aOriginatingLayersId, 176 const wr::Epoch& aEpoch, WebRenderScrollData&& aScrollData) { 177 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 178 RefPtr<APZUpdater> self = this; 179 // Insert an epoch requirement update into the queue, so that 180 // tasks inserted into the queue after this point only get executed 181 // once the epoch requirement is satisfied. In particular, the 182 // UpdateHitTestingTree call below needs to wait until the epoch requirement 183 // is satisfied, which is why it is a separate task in the queue. 184 RunOnUpdaterThread( 185 aOriginatingLayersId, 186 NS_NewRunnableFunction("APZUpdater::UpdateEpochRequirement", [=]() { 187 if (aRootLayerTreeId == aOriginatingLayersId) { 188 self->mEpochData[aOriginatingLayersId].mIsRoot = true; 189 } 190 self->mEpochData[aOriginatingLayersId].mRequired = aEpoch; 191 })); 192 RunOnUpdaterThread( 193 aOriginatingLayersId, 194 NS_NewRunnableFunction( 195 "APZUpdater::UpdateHitTestingTree", 196 [=, aScrollData = std::move(aScrollData)]() mutable { 197 auto isFirstPaint = aScrollData.IsFirstPaint(); 198 auto paintSequenceNumber = aScrollData.GetPaintSequenceNumber(); 199 200 auto previous = self->mScrollData.find(aOriginatingLayersId); 201 // If there's the previous scroll data which hasn't yet been 202 // processed, we need to merge the previous scroll position updates 203 // into the latest one. 204 if (previous != self->mScrollData.end()) { 205 WebRenderScrollData& previousData = previous->second; 206 if (previousData.GetWasUpdateSkipped()) { 207 MOZ_ASSERT(previousData.IsFirstPaint()); 208 aScrollData.PrependUpdates(previousData); 209 } 210 } 211 212 self->mScrollData[aOriginatingLayersId] = std::move(aScrollData); 213 auto root = self->mScrollData.find(aRootLayerTreeId); 214 if (root == self->mScrollData.end()) { 215 return; 216 } 217 218 auto updatedIds = self->mApz->UpdateHitTestingTree( 219 WebRenderScrollDataWrapper(*self, &(root->second)), 220 aOriginatingLayersId, paintSequenceNumber); 221 bool originatingLayersIdWasSkipped = true; 222 for (auto id : updatedIds) { 223 if (id == aOriginatingLayersId) { 224 originatingLayersIdWasSkipped = false; 225 } 226 227 // Reset `mWasUpdateSkipped` flag forcibly here even if the 228 // `aOriginatingLayersId` is different from the layersId for the 229 // data, otherwise the skipped scroll position updates will be 230 // re-processed again. 231 self->mScrollData[id].SetWasUpdateSkipped(false); 232 } 233 234 if (isFirstPaint) { 235 if (originatingLayersIdWasSkipped) { 236 // If the given |aOriginatingLayersId| data wasn't used for 237 // updating, it's likely that the parent process hasn't yet 238 // received the LayersId as "ReferentId", thus we need to 239 // process it in a subsequent update where we got the 240 // "ReferentId". 241 // 242 // NOTE: We restrict the above previous scroll data prepending 243 // to the first paint case, otherwise the cumulative scroll data 244 // may be exploded if we have never received the "ReferenceId". 245 self->mScrollData[aOriginatingLayersId].SetWasUpdateSkipped( 246 true); 247 } else { 248 // Clobber the first-paint flag so that we will never run into 249 // the first-paint branch in 250 // AsyncPanZoomController::NotifyLayersUpdated even if the next 251 // transaction is a paint-skip transaction. 252 self->mScrollData[aOriginatingLayersId].SetIsFirstPaint(false); 253 } 254 } 255 })); 256 } 257 258 void APZUpdater::UpdateScrollOffsets(LayersId aRootLayerTreeId, 259 LayersId aOriginatingLayersId, 260 ScrollUpdatesMap&& aUpdates, 261 uint32_t aPaintSequenceNumber) { 262 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 263 RefPtr<APZUpdater> self = this; 264 RunOnUpdaterThread( 265 aOriginatingLayersId, 266 NS_NewRunnableFunction( 267 "APZUpdater::UpdateScrollOffsets", 268 [=, updates = std::move(aUpdates)]() mutable { 269 self->mScrollData[aOriginatingLayersId].ApplyUpdates( 270 std::move(updates), aPaintSequenceNumber); 271 auto root = self->mScrollData.find(aRootLayerTreeId); 272 if (root == self->mScrollData.end()) { 273 return; 274 } 275 self->mApz->UpdateHitTestingTree( 276 WebRenderScrollDataWrapper(*self, &(root->second)), 277 aOriginatingLayersId, aPaintSequenceNumber); 278 })); 279 } 280 281 void APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId, 282 const RefPtr<APZUpdater>& aOldUpdater) { 283 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 284 RunOnUpdaterThread(aLayersId, 285 NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>( 286 "APZUpdater::NotifyLayerTreeAdopted", mApz, 287 &APZCTreeManager::NotifyLayerTreeAdopted, aLayersId, 288 aOldUpdater ? aOldUpdater->mApz : nullptr)); 289 } 290 291 void APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId) { 292 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 293 RefPtr<APZUpdater> self = this; 294 RunOnUpdaterThread( 295 aLayersId, 296 NS_NewRunnableFunction("APZUpdater::NotifyLayerTreeRemoved", [=]() { 297 self->mEpochData.erase(aLayersId); 298 self->mScrollData.erase(aLayersId); 299 self->mApz->NotifyLayerTreeRemoved(aLayersId); 300 })); 301 } 302 303 bool APZUpdater::GetAPZTestData(LayersId aLayersId, APZTestData* aOutData) { 304 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 305 306 RefPtr<APZCTreeManager> apz = mApz; 307 bool ret = false; 308 SynchronousTask waiter("APZUpdater::GetAPZTestData"); 309 RunOnUpdaterThread( 310 aLayersId, NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() { 311 AutoCompleteTask notifier(&waiter); 312 ret = apz->GetAPZTestData(aLayersId, aOutData); 313 })); 314 315 // Wait until the task posted above has run and populated aOutData and ret 316 waiter.Wait(); 317 318 return ret; 319 } 320 321 void APZUpdater::SetTestAsyncScrollOffset( 322 LayersId aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, 323 const CSSPoint& aOffset) { 324 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 325 RefPtr<APZCTreeManager> apz = mApz; 326 RunOnUpdaterThread( 327 aLayersId, 328 NS_NewRunnableFunction("APZUpdater::SetTestAsyncScrollOffset", [=]() { 329 RefPtr<AsyncPanZoomController> apzc = 330 apz->GetTargetAPZC(aLayersId, aScrollId); 331 if (apzc) { 332 apzc->SetTestAsyncScrollOffset(aOffset); 333 } else { 334 NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset"); 335 } 336 })); 337 } 338 339 void APZUpdater::SetTestAsyncZoom(LayersId aLayersId, 340 const ScrollableLayerGuid::ViewID& aScrollId, 341 const LayerToParentLayerScale& aZoom) { 342 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 343 RefPtr<APZCTreeManager> apz = mApz; 344 RunOnUpdaterThread( 345 aLayersId, NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() { 346 RefPtr<AsyncPanZoomController> apzc = 347 apz->GetTargetAPZC(aLayersId, aScrollId); 348 if (apzc) { 349 apzc->SetTestAsyncZoom(aZoom); 350 } else { 351 NS_WARNING("Unable to find APZC in SetTestAsyncZoom"); 352 } 353 })); 354 } 355 356 const WebRenderScrollData* APZUpdater::GetScrollData(LayersId aLayersId) const { 357 AssertOnUpdaterThread(); 358 auto it = mScrollData.find(aLayersId); 359 return (it == mScrollData.end() ? nullptr : &(it->second)); 360 } 361 362 void APZUpdater::AssertOnUpdaterThread() const { 363 if (APZThreadUtils::GetThreadAssertionsEnabled()) { 364 MOZ_ASSERT(IsUpdaterThread()); 365 } 366 } 367 368 void APZUpdater::RunOnUpdaterThread(LayersId aLayersId, 369 already_AddRefed<Runnable> aTask, 370 DuringShutdown aDuringShutdown) { 371 RefPtr<Runnable> task = aTask; 372 373 // In the scenario where IsConnectedToWebRender() is true, this function 374 // might get called early (before mUpdaterThreadId is set). In that case 375 // IsUpdaterThread() will return false and we'll queue the task onto 376 // mUpdaterQueue. This is fine; the task is still guaranteed to run (barring 377 // catastrophic failure) because the WakeSceneBuilder call will still trigger 378 // the callback to run tasks. 379 380 if (IsUpdaterThread()) { 381 // This function should only be called from the updater thread in test 382 // scenarios where we are not connected to WebRender. If it were called from 383 // the updater thread when we are connected to WebRender, running the task 384 // right away would be incorrect (we'd need to check that |aLayersId| 385 // isn't blocked, and if it is then enqueue the task instead). 386 MOZ_ASSERT(!IsConnectedToWebRender()); 387 task->Run(); 388 return; 389 } 390 391 if (IsConnectedToWebRender()) { 392 // If the updater thread is a WebRender thread, and we're not on it 393 // right now, save the task in the queue. We will run tasks from the queue 394 // during the callback from the updater thread, which we trigger by the 395 // call to WakeSceneBuilder. 396 397 bool sendWakeMessage = aDuringShutdown == DuringShutdown::No; 398 { // scope lock 399 MutexAutoLock lock(mQueueLock); 400 if (sendWakeMessage) { 401 for (const auto& queuedTask : mUpdaterQueue) { 402 if (queuedTask.mLayersId == aLayersId) { 403 // If there's already a task in the queue with this layers id, then 404 // we must have previously sent a WakeSceneBuilder message (when 405 // adding the first task with this layers id to the queue). Either 406 // that hasn't been fully processed yet, or the layers id is blocked 407 // waiting for an epoch - in either case there's no point in sending 408 // another WakeSceneBuilder message. 409 sendWakeMessage = false; 410 break; 411 } 412 } 413 } 414 mUpdaterQueue.push_back(QueuedTask{aLayersId, task}); 415 } 416 if (sendWakeMessage) { 417 RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI(); 418 if (api) { 419 api->WakeSceneBuilder(); 420 } else { 421 // Not sure if this can happen, but it might be possible. If it does, 422 // the task is in the queue, but if we didn't get a WebRenderAPI it 423 // might never run, or it might run later if we manage to get a 424 // WebRenderAPI later. For now let's just emit a warning, this can 425 // probably be upgraded to an assert later. 426 NS_WARNING("Possibly dropping task posted to updater thread"); 427 } 428 } 429 return; 430 } 431 432 if (CompositorThread()) { 433 CompositorThread()->Dispatch(task.forget()); 434 } else { 435 // Could happen during startup 436 NS_WARNING("Dropping task posted to updater thread"); 437 } 438 } 439 440 bool APZUpdater::IsUpdaterThread() const { 441 if (IsConnectedToWebRender()) { 442 // If the updater thread id isn't set yet then we cannot be running on the 443 // updater thread (because we will have the thread id before we run any 444 // C++ code on it, and this function is only ever invoked from C++ code), 445 // so return false in that scenario. 446 MutexAutoLock lock(mThreadIdLock); 447 return mUpdaterThreadId && PlatformThread::CurrentId() == *mUpdaterThreadId; 448 } 449 return CompositorThreadHolder::IsInCompositorThread(); 450 } 451 452 void APZUpdater::RunOnControllerThread(LayersId aLayersId, 453 already_AddRefed<Runnable> aTask) { 454 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 455 456 RefPtr<Runnable> task = aTask; 457 458 RunOnUpdaterThread( 459 aLayersId, 460 NewRunnableFunction("APZUpdater::RunOnControllerThread", 461 &APZThreadUtils::RunOnControllerThread, 462 std::move(task), nsIThread::DISPATCH_NORMAL)); 463 } 464 465 bool APZUpdater::IsConnectedToWebRender() const { 466 return mConnectedToWebRender; 467 } 468 469 /*static*/ 470 already_AddRefed<APZUpdater> APZUpdater::GetUpdater( 471 const wr::WrWindowId& aWindowId) { 472 RefPtr<APZUpdater> updater; 473 StaticMutexAutoLock lock(sWindowIdLock); 474 if (sWindowIdMap) { 475 auto it = sWindowIdMap->find(wr::AsUint64(aWindowId)); 476 if (it != sWindowIdMap->end()) { 477 updater = it->second; 478 } 479 } 480 return updater.forget(); 481 } 482 483 void APZUpdater::ProcessQueue() { 484 MOZ_ASSERT(!mDestroyed); 485 486 { // scope lock to check for emptiness 487 MutexAutoLock lock(mQueueLock); 488 if (mUpdaterQueue.empty()) { 489 return; 490 } 491 } 492 493 std::deque<QueuedTask> blockedTasks; 494 while (true) { 495 QueuedTask task; 496 497 { // scope lock to extract a task 498 MutexAutoLock lock(mQueueLock); 499 if (mUpdaterQueue.empty()) { 500 // If we're done processing mUpdaterQueue, swap the tasks that are 501 // still blocked back in and finish 502 std::swap(mUpdaterQueue, blockedTasks); 503 break; 504 } 505 task = mUpdaterQueue.front(); 506 mUpdaterQueue.pop_front(); 507 } 508 509 // We check the task to see if it is blocked. Note that while this 510 // ProcessQueue function is executing, a particular layers id cannot go 511 // from blocked to unblocked, because only CompleteSceneSwap can unblock 512 // a layers id, and that also runs on the updater thread. If somehow 513 // a layers id gets unblocked while we're processing the queue, then it 514 // might result in tasks getting executed out of order. 515 516 auto it = mEpochData.find(task.mLayersId); 517 if (it != mEpochData.end() && it->second.IsBlocked()) { 518 // If this task is blocked, put it into the blockedTasks queue that 519 // we will replace mUpdaterQueue with 520 blockedTasks.push_back(task); 521 } else { 522 // Run and discard the task 523 task.mRunnable->Run(); 524 } 525 } 526 527 if (mDestroyed) { 528 // If we get here, then we must have just run the ClearTree task for 529 // this updater. There might be tasks in the queue from content subtrees 530 // of this window that are blocked due to stale epochs. This can happen 531 // if the tasks were queued after the root pipeline was removed in 532 // WebRender, which prevents scene builds (and therefore prevents us 533 // from getting updated epochs via CompleteSceneSwap). See bug 1465658 534 // comment 43 for some more context. 535 // To avoid leaking these tasks, we discard the contents of the queue. 536 // This happens during window shutdown so if we don't run the tasks it's 537 // not going to matter much. 538 MutexAutoLock lock(mQueueLock); 539 if (!mUpdaterQueue.empty()) { 540 mUpdaterQueue.clear(); 541 } 542 } 543 } 544 545 void APZUpdater::MarkAsDetached(LayersId aLayersId) { 546 mApz->MarkAsDetached(aLayersId); 547 } 548 549 APZUpdater::EpochState::EpochState() : mRequired{0}, mIsRoot(false) {} 550 551 bool APZUpdater::EpochState::IsBlocked() const { 552 // The root is a special case because we basically assume it is "visible" 553 // even before it is built for the first time. This is because building the 554 // scene automatically makes it visible, and we need to make sure the APZ 555 // scroll data gets applied atomically with that happening. 556 // 557 // Layer subtrees on the other hand do not automatically become visible upon 558 // being built, because there must be a another layer tree update to change 559 // the visibility (i.e. an ancestor layer tree update that adds the necessary 560 // reflayer to complete the chain of reflayers). 561 // 562 // So in the case of non-visible subtrees, we know that no hit-test will 563 // actually end up hitting that subtree either before or after the scene swap, 564 // because the subtree will remain non-visible. That in turns means that we 565 // can apply the APZ scroll data for that subtree epoch before the scene is 566 // built, because it's not going to get used anyway. And that means we don't 567 // need to block the queue for non-visible subtrees. Which is a good thing, 568 // because in practice it seems like we often have non-visible subtrees sent 569 // to the compositor from content. 570 if (mIsRoot && !mBuilt) { 571 return true; 572 } 573 return mBuilt && (*mBuilt < mRequired); 574 } 575 576 } // namespace layers 577 } // namespace mozilla 578 579 // Rust callback implementations 580 581 void apz_register_updater(mozilla::wr::WrWindowId aWindowId) { 582 mozilla::layers::APZUpdater::SetUpdaterThread(aWindowId); 583 } 584 585 void apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId) { 586 mozilla::layers::APZUpdater::PrepareForSceneSwap(aWindowId); 587 } 588 589 void apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId, 590 const mozilla::wr::WrPipelineInfo* aInfo) { 591 mozilla::layers::APZUpdater::CompleteSceneSwap(aWindowId, *aInfo); 592 } 593 594 void apz_run_updater(mozilla::wr::WrWindowId aWindowId) { 595 mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId); 596 } 597 598 void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId) { 599 // Run anything that's still left. 600 mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId); 601 }