Source code

Revision control

Other Tools

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 aIsUsingWebRender)
28
: mApz(aApz),
29
mDestroyed(false),
30
mIsUsingWebRender(aIsUsingWebRender),
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
/*static*/
74
void APZUpdater::PrepareForSceneSwap(const wr::WrWindowId& aWindowId) {
75
if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
76
updater->mApz->LockTree();
77
}
78
}
79
80
/*static*/
81
void APZUpdater::CompleteSceneSwap(const wr::WrWindowId& aWindowId,
82
const wr::WrPipelineInfo& aInfo) {
83
RefPtr<APZUpdater> updater = GetUpdater(aWindowId);
84
if (!updater) {
85
// This should only happen in cases where PrepareForSceneSwap also got a
86
// null updater. No updater-thread tasks get run between PrepareForSceneSwap
87
// and this function, so there is no opportunity for the updater mapping
88
// to have gotten removed from sWindowIdMap in between the two calls.
89
return;
90
}
91
92
for (const auto& removedPipeline : aInfo.removed_pipelines) {
93
LayersId layersId = wr::AsLayersId(removedPipeline.pipeline_id);
94
updater->mEpochData.erase(layersId);
95
}
96
// Reset the built info for all pipelines, then put it back for the ones
97
// that got built in this scene swap.
98
for (auto& i : updater->mEpochData) {
99
i.second.mBuilt = Nothing();
100
}
101
for (const auto& epoch : aInfo.epochs) {
102
LayersId layersId = wr::AsLayersId(epoch.pipeline_id);
103
updater->mEpochData[layersId].mBuilt = Some(epoch.epoch);
104
}
105
106
// Run any tasks that got unblocked, then unlock the tree. The order is
107
// important because we want to run all the tasks up to and including the
108
// UpdateHitTestingTree calls corresponding to the built epochs, and we
109
// want to run those before we release the lock (i.e. atomically with the
110
// scene swap). This ensures that any hit-tests always encounter a consistent
111
// state between the APZ tree and the built scene in WR.
112
//
113
// While we could add additional information to the queued tasks to figure
114
// out the minimal set of tasks we want to run here, it's easier and harmless
115
// to just run all the queued and now-unblocked tasks inside the lock.
116
//
117
// Note that the ProcessQueue here might remove the window id -> APZUpdater
118
// mapping from sWindowIdMap, but we still unlock the tree successfully to
119
// leave things in a good state.
120
updater->ProcessQueue();
121
122
updater->mApz->UnlockTree();
123
}
124
125
/*static*/
126
void APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId) {
127
if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
128
updater->ProcessQueue();
129
}
130
}
131
132
void APZUpdater::ClearTree(LayersId aRootLayersId) {
133
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
134
RefPtr<APZUpdater> self = this;
135
RunOnUpdaterThread(aRootLayersId,
136
NS_NewRunnableFunction("APZUpdater::ClearTree", [=]() {
137
self->mApz->ClearTree();
138
self->mDestroyed = true;
139
140
// Once ClearTree is called on the APZCTreeManager, we
141
// are in a shutdown phase. After this point it's ok if
142
// WebRender cannot get a hold of the updater via the
143
// window id, and it's a good point to remove the mapping
144
// and avoid leaving a dangling pointer to this object.
145
StaticMutexAutoLock lock(sWindowIdLock);
146
if (self->mWindowId) {
147
MOZ_ASSERT(sWindowIdMap);
148
sWindowIdMap->erase(wr::AsUint64(*(self->mWindowId)));
149
}
150
}));
151
}
152
153
void APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
154
LayersId aOriginatingLayersId,
155
const FocusTarget& aFocusTarget) {
156
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
157
RunOnUpdaterThread(aOriginatingLayersId,
158
NewRunnableMethod<LayersId, LayersId, FocusTarget>(
159
"APZUpdater::UpdateFocusState", mApz,
160
&APZCTreeManager::UpdateFocusState, aRootLayerTreeId,
161
aOriginatingLayersId, aFocusTarget));
162
}
163
164
void APZUpdater::UpdateHitTestingTree(Layer* aRoot, bool aIsFirstPaint,
165
LayersId aOriginatingLayersId,
166
uint32_t aPaintSequenceNumber) {
167
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
168
AssertOnUpdaterThread();
169
mApz->UpdateHitTestingTree(aRoot, aIsFirstPaint, aOriginatingLayersId,
170
aPaintSequenceNumber);
171
}
172
173
void APZUpdater::UpdateScrollDataAndTreeState(
174
LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
175
const wr::Epoch& aEpoch, WebRenderScrollData&& aScrollData) {
176
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
177
RefPtr<APZUpdater> self = this;
178
// Insert an epoch requirement update into the queue, so that
179
// tasks inserted into the queue after this point only get executed
180
// once the epoch requirement is satisfied. In particular, the
181
// UpdateHitTestingTree call below needs to wait until the epoch requirement
182
// is satisfied, which is why it is a separate task in the queue.
183
RunOnUpdaterThread(
184
aOriginatingLayersId,
185
NS_NewRunnableFunction("APZUpdater::UpdateEpochRequirement", [=]() {
186
if (aRootLayerTreeId == aOriginatingLayersId) {
187
self->mEpochData[aOriginatingLayersId].mIsRoot = true;
188
}
189
self->mEpochData[aOriginatingLayersId].mRequired = aEpoch;
190
}));
191
RunOnUpdaterThread(
192
aOriginatingLayersId,
193
NS_NewRunnableFunction(
194
"APZUpdater::UpdateHitTestingTree",
195
[=, aScrollData = std::move(aScrollData)]() mutable {
196
auto isFirstPaint = aScrollData.IsFirstPaint();
197
auto paintSequenceNumber = aScrollData.GetPaintSequenceNumber();
198
199
self->mScrollData[aOriginatingLayersId] = std::move(aScrollData);
200
auto root = self->mScrollData.find(aRootLayerTreeId);
201
if (root == self->mScrollData.end()) {
202
return;
203
}
204
self->mApz->UpdateHitTestingTree(
205
WebRenderScrollDataWrapper(*self, &(root->second)),
206
isFirstPaint, aOriginatingLayersId, paintSequenceNumber);
207
}));
208
}
209
210
void APZUpdater::UpdateScrollOffsets(LayersId aRootLayerTreeId,
211
LayersId aOriginatingLayersId,
212
ScrollUpdatesMap&& aUpdates,
213
uint32_t aPaintSequenceNumber) {
214
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
215
RefPtr<APZUpdater> self = this;
216
RunOnUpdaterThread(
217
aOriginatingLayersId,
218
NS_NewRunnableFunction(
219
"APZUpdater::UpdateScrollOffsets",
220
[=, updates = std::move(aUpdates)]() mutable {
221
self->mScrollData[aOriginatingLayersId].ApplyUpdates(
222
updates, aPaintSequenceNumber);
223
auto root = self->mScrollData.find(aRootLayerTreeId);
224
if (root == self->mScrollData.end()) {
225
return;
226
}
227
self->mApz->UpdateHitTestingTree(
228
WebRenderScrollDataWrapper(*self, &(root->second)),
229
/*isFirstPaint*/ false, aOriginatingLayersId,
230
aPaintSequenceNumber);
231
}));
232
}
233
234
void APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
235
const RefPtr<APZUpdater>& aOldUpdater) {
236
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
237
RunOnUpdaterThread(aLayersId,
238
NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
239
"APZUpdater::NotifyLayerTreeAdopted", mApz,
240
&APZCTreeManager::NotifyLayerTreeAdopted, aLayersId,
241
aOldUpdater ? aOldUpdater->mApz : nullptr));
242
}
243
244
void APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId) {
245
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
246
RefPtr<APZUpdater> self = this;
247
RunOnUpdaterThread(
248
aLayersId,
249
NS_NewRunnableFunction("APZUpdater::NotifyLayerTreeRemoved", [=]() {
250
self->mEpochData.erase(aLayersId);
251
self->mScrollData.erase(aLayersId);
252
self->mApz->NotifyLayerTreeRemoved(aLayersId);
253
}));
254
}
255
256
bool APZUpdater::GetAPZTestData(LayersId aLayersId, APZTestData* aOutData) {
257
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
258
259
RefPtr<APZCTreeManager> apz = mApz;
260
bool ret = false;
261
SynchronousTask waiter("APZUpdater::GetAPZTestData");
262
RunOnUpdaterThread(
263
aLayersId, NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() {
264
AutoCompleteTask notifier(&waiter);
265
ret = apz->GetAPZTestData(aLayersId, aOutData);
266
}));
267
268
// Wait until the task posted above has run and populated aOutData and ret
269
waiter.Wait();
270
271
return ret;
272
}
273
274
void APZUpdater::SetTestAsyncScrollOffset(
275
LayersId aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
276
const CSSPoint& aOffset) {
277
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
278
RefPtr<APZCTreeManager> apz = mApz;
279
RunOnUpdaterThread(
280
aLayersId,
281
NS_NewRunnableFunction("APZUpdater::SetTestAsyncScrollOffset", [=]() {
282
RefPtr<AsyncPanZoomController> apzc =
283
apz->GetTargetAPZC(aLayersId, aScrollId);
284
if (apzc) {
285
apzc->SetTestAsyncScrollOffset(aOffset);
286
} else {
287
NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
288
}
289
}));
290
}
291
292
void APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
293
const ScrollableLayerGuid::ViewID& aScrollId,
294
const LayerToParentLayerScale& aZoom) {
295
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
296
RefPtr<APZCTreeManager> apz = mApz;
297
RunOnUpdaterThread(
298
aLayersId, NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() {
299
RefPtr<AsyncPanZoomController> apzc =
300
apz->GetTargetAPZC(aLayersId, aScrollId);
301
if (apzc) {
302
apzc->SetTestAsyncZoom(aZoom);
303
} else {
304
NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
305
}
306
}));
307
}
308
309
const WebRenderScrollData* APZUpdater::GetScrollData(LayersId aLayersId) const {
310
AssertOnUpdaterThread();
311
auto it = mScrollData.find(aLayersId);
312
return (it == mScrollData.end() ? nullptr : &(it->second));
313
}
314
315
void APZUpdater::AssertOnUpdaterThread() const {
316
if (APZThreadUtils::GetThreadAssertionsEnabled()) {
317
MOZ_ASSERT(IsUpdaterThread());
318
}
319
}
320
321
void APZUpdater::RunOnUpdaterThread(LayersId aLayersId,
322
already_AddRefed<Runnable> aTask) {
323
RefPtr<Runnable> task = aTask;
324
325
// In the scenario where UsingWebRenderUpdaterThread() is true, this function
326
// might get called early (before mUpdaterThreadId is set). In that case
327
// IsUpdaterThread() will return false and we'll queue the task onto
328
// mUpdaterQueue. This is fine; the task is still guaranteed to run (barring
329
// catastrophic failure) because the WakeSceneBuilder call will still trigger
330
// the callback to run tasks.
331
332
if (IsUpdaterThread()) {
333
task->Run();
334
return;
335
}
336
337
if (UsingWebRenderUpdaterThread()) {
338
// If the updater thread is a WebRender thread, and we're not on it
339
// right now, save the task in the queue. We will run tasks from the queue
340
// during the callback from the updater thread, which we trigger by the
341
// call to WakeSceneBuilder.
342
343
bool sendWakeMessage = true;
344
{ // scope lock
345
MutexAutoLock lock(mQueueLock);
346
for (const auto& queuedTask : mUpdaterQueue) {
347
if (queuedTask.mLayersId == aLayersId) {
348
// If there's already a task in the queue with this layers id, then
349
// we must have previously sent a WakeSceneBuilder message (when
350
// adding the first task with this layers id to the queue). Either
351
// that hasn't been fully processed yet, or the layers id is blocked
352
// waiting for an epoch - in either case there's no point in sending
353
// another WakeSceneBuilder message.
354
sendWakeMessage = false;
355
break;
356
}
357
}
358
mUpdaterQueue.push_back(QueuedTask{aLayersId, task});
359
}
360
if (sendWakeMessage) {
361
RefPtr<wr::WebRenderAPI> api =
362
mApz->GetWebRenderAPI(wr::RenderRoot::Default);
363
if (api) {
364
api->WakeSceneBuilder();
365
} else {
366
// Not sure if this can happen, but it might be possible. If it does,
367
// the task is in the queue, but if we didn't get a WebRenderAPI it
368
// might never run, or it might run later if we manage to get a
369
// WebRenderAPI later. For now let's just emit a warning, this can
370
// probably be upgraded to an assert later.
371
NS_WARNING("Possibly dropping task posted to updater thread");
372
}
373
}
374
return;
375
}
376
377
if (MessageLoop* loop = CompositorThreadHolder::Loop()) {
378
loop->PostTask(task.forget());
379
} else {
380
// Could happen during startup
381
NS_WARNING("Dropping task posted to updater thread");
382
}
383
}
384
385
bool APZUpdater::IsUpdaterThread() const {
386
if (UsingWebRenderUpdaterThread()) {
387
// If the updater thread id isn't set yet then we cannot be running on the
388
// updater thread (because we will have the thread id before we run any
389
// C++ code on it, and this function is only ever invoked from C++ code),
390
// so return false in that scenario.
391
MutexAutoLock lock(mThreadIdLock);
392
return mUpdaterThreadId && PlatformThread::CurrentId() == *mUpdaterThreadId;
393
}
394
return CompositorThreadHolder::IsInCompositorThread();
395
}
396
397
void APZUpdater::RunOnControllerThread(LayersId aLayersId,
398
already_AddRefed<Runnable> aTask) {
399
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
400
401
RefPtr<Runnable> task = aTask;
402
403
RunOnUpdaterThread(aLayersId,
404
NewRunnableFunction("APZUpdater::RunOnControllerThread",
405
&APZThreadUtils::RunOnControllerThread,
406
std::move(task)));
407
}
408
409
bool APZUpdater::UsingWebRenderUpdaterThread() const {
410
return mIsUsingWebRender;
411
}
412
413
/*static*/
414
already_AddRefed<APZUpdater> APZUpdater::GetUpdater(
415
const wr::WrWindowId& aWindowId) {
416
RefPtr<APZUpdater> updater;
417
StaticMutexAutoLock lock(sWindowIdLock);
418
if (sWindowIdMap) {
419
auto it = sWindowIdMap->find(wr::AsUint64(aWindowId));
420
if (it != sWindowIdMap->end()) {
421
updater = it->second;
422
}
423
}
424
return updater.forget();
425
}
426
427
void APZUpdater::ProcessQueue() {
428
MOZ_ASSERT(!mDestroyed);
429
430
{ // scope lock to check for emptiness
431
MutexAutoLock lock(mQueueLock);
432
if (mUpdaterQueue.empty()) {
433
return;
434
}
435
}
436
437
std::deque<QueuedTask> blockedTasks;
438
while (true) {
439
QueuedTask task;
440
441
{ // scope lock to extract a task
442
MutexAutoLock lock(mQueueLock);
443
if (mUpdaterQueue.empty()) {
444
// If we're done processing mUpdaterQueue, swap the tasks that are
445
// still blocked back in and finish
446
std::swap(mUpdaterQueue, blockedTasks);
447
break;
448
}
449
task = mUpdaterQueue.front();
450
mUpdaterQueue.pop_front();
451
}
452
453
// We check the task to see if it is blocked. Note that while this
454
// ProcessQueue function is executing, a particular layers id cannot go
455
// from blocked to unblocked, because only CompleteSceneSwap can unblock
456
// a layers id, and that also runs on the updater thread. If somehow
457
// a layers id gets unblocked while we're processing the queue, then it
458
// might result in tasks getting executed out of order.
459
460
auto it = mEpochData.find(task.mLayersId);
461
if (it != mEpochData.end() && it->second.IsBlocked()) {
462
// If this task is blocked, put it into the blockedTasks queue that
463
// we will replace mUpdaterQueue with
464
blockedTasks.push_back(task);
465
} else {
466
// Run and discard the task
467
task.mRunnable->Run();
468
}
469
}
470
471
if (mDestroyed) {
472
// If we get here, then we must have just run the ClearTree task for
473
// this updater. There might be tasks in the queue from content subtrees
474
// of this window that are blocked due to stale epochs. This can happen
475
// if the tasks were queued after the root pipeline was removed in
476
// WebRender, which prevents scene builds (and therefore prevents us
477
// from getting updated epochs via CompleteSceneSwap). See bug 1465658
478
// comment 43 for some more context.
479
// To avoid leaking these tasks, we discard the contents of the queue.
480
// This happens during window shutdown so if we don't run the tasks it's
481
// not going to matter much.
482
MutexAutoLock lock(mQueueLock);
483
if (!mUpdaterQueue.empty()) {
484
mUpdaterQueue.clear();
485
}
486
}
487
}
488
489
void APZUpdater::MarkAsDetached(LayersId aLayersId) {
490
mApz->MarkAsDetached(aLayersId);
491
}
492
493
APZUpdater::EpochState::EpochState() : mRequired{0}, mIsRoot(false) {}
494
495
bool APZUpdater::EpochState::IsBlocked() const {
496
// The root is a special case because we basically assume it is "visible"
497
// even before it is built for the first time. This is because building the
498
// scene automatically makes it visible, and we need to make sure the APZ
499
// scroll data gets applied atomically with that happening.
500
//
501
// Layer subtrees on the other hand do not automatically become visible upon
502
// being built, because there must be a another layer tree update to change
503
// the visibility (i.e. an ancestor layer tree update that adds the necessary
504
// reflayer to complete the chain of reflayers).
505
//
506
// So in the case of non-visible subtrees, we know that no hit-test will
507
// actually end up hitting that subtree either before or after the scene swap,
508
// because the subtree will remain non-visible. That in turns means that we
509
// can apply the APZ scroll data for that subtree epoch before the scene is
510
// built, because it's not going to get used anyway. And that means we don't
511
// need to block the queue for non-visible subtrees. Which is a good thing,
512
// because in practice it seems like we often have non-visible subtrees sent
513
// to the compositor from content.
514
if (mIsRoot && !mBuilt) {
515
return true;
516
}
517
return mBuilt && (*mBuilt < mRequired);
518
}
519
520
} // namespace layers
521
} // namespace mozilla
522
523
// Rust callback implementations
524
525
void apz_register_updater(mozilla::wr::WrWindowId aWindowId) {
526
mozilla::layers::APZUpdater::SetUpdaterThread(aWindowId);
527
}
528
529
void apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId) {
530
mozilla::layers::APZUpdater::PrepareForSceneSwap(aWindowId);
531
}
532
533
void apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId,
534
const mozilla::wr::WrPipelineInfo* aInfo) {
535
mozilla::layers::APZUpdater::CompleteSceneSwap(aWindowId, *aInfo);
536
}
537
538
void apz_run_updater(mozilla::wr::WrWindowId aWindowId) {
539
mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
540
}
541
542
void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId) {
543
// Run anything that's still left.
544
mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
545
}