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