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/. */
7 #include "mozilla/layers/APZSampler.h"
9 #include "AsyncPanZoomController.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/layers/APZThreadUtils.h"
12 #include "mozilla/layers/APZUtils.h"
13 #include "mozilla/layers/CompositorThread.h"
14 #include "mozilla/layers/SynchronousTask.h"
15 #include "TreeTraversal.h"
16 #include "mozilla/webrender/WebRenderAPI.h"
21 StaticMutex
APZSampler::sWindowIdLock
;
22 StaticAutoPtr
<std::unordered_map
<uint64_t, RefPtr
<APZSampler
>>>
23 APZSampler::sWindowIdMap
;
25 APZSampler::APZSampler(const RefPtr
<APZCTreeManager
>& aApz
,
26 bool aIsUsingWebRender
)
28 mIsUsingWebRender(aIsUsingWebRender
),
29 mThreadIdLock("APZSampler::mThreadIdLock"),
30 mSampleTimeLock("APZSampler::mSampleTimeLock") {
32 mApz
->SetSampler(this);
35 APZSampler::~APZSampler() { mApz
->SetSampler(nullptr); }
37 void APZSampler::Destroy() {
38 StaticMutexAutoLock
lock(sWindowIdLock
);
40 MOZ_ASSERT(sWindowIdMap
);
41 sWindowIdMap
->erase(wr::AsUint64(*mWindowId
));
45 void APZSampler::SetWebRenderWindowId(const wr::WindowId
& aWindowId
) {
46 StaticMutexAutoLock
lock(sWindowIdLock
);
47 MOZ_ASSERT(!mWindowId
);
48 mWindowId
= Some(aWindowId
);
50 sWindowIdMap
= new std::unordered_map
<uint64_t, RefPtr
<APZSampler
>>();
51 NS_DispatchToMainThread(NS_NewRunnableFunction(
52 "APZSampler::ClearOnShutdown", [] { ClearOnShutdown(&sWindowIdMap
); }));
54 (*sWindowIdMap
)[wr::AsUint64(aWindowId
)] = this;
58 void APZSampler::SetSamplerThread(const wr::WrWindowId
& aWindowId
) {
59 if (RefPtr
<APZSampler
> sampler
= GetSampler(aWindowId
)) {
60 MutexAutoLock
lock(sampler
->mThreadIdLock
);
61 sampler
->mSamplerThreadId
= Some(PlatformThread::CurrentId());
66 void APZSampler::SampleForWebRender(const wr::WrWindowId
& aWindowId
,
67 const uint64_t* aGeneratedFrameId
,
68 wr::Transaction
* aTransaction
) {
69 if (RefPtr
<APZSampler
> sampler
= GetSampler(aWindowId
)) {
70 wr::TransactionWrapper
txn(aTransaction
);
71 Maybe
<VsyncId
> vsyncId
=
72 aGeneratedFrameId
? Some(VsyncId
{*aGeneratedFrameId
}) : Nothing();
73 sampler
->SampleForWebRender(vsyncId
, txn
);
77 void APZSampler::SetSampleTime(const SampleTime
& aSampleTime
) {
78 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
79 MutexAutoLock
lock(mSampleTimeLock
);
80 // This only gets called with WR, and the time provided is going to be
81 // the time at which the current vsync interval ends. i.e. it is the timestamp
82 // for the next vsync that will occur.
83 mSampleTime
= aSampleTime
;
86 void APZSampler::SampleForWebRender(const Maybe
<VsyncId
>& aVsyncId
,
87 wr::TransactionWrapper
& aTxn
) {
88 AssertOnSamplerThread();
89 SampleTime sampleTime
;
91 MutexAutoLock
lock(mSampleTimeLock
);
93 // If mSampleTime is null we're in a startup phase where the
94 // WebRenderBridgeParent hasn't yet provided us with a sample time.
95 // If we're that early there probably aren't any APZ animations happening
96 // anyway, so using Timestamp::Now() should be fine.
97 SampleTime now
= SampleTime::FromNow();
98 sampleTime
= mSampleTime
.IsNull() ? now
: mSampleTime
;
100 mApz
->SampleForWebRender(aVsyncId
, aTxn
, sampleTime
);
103 AsyncTransform
APZSampler::GetCurrentAsyncTransform(
104 const LayersId
& aLayersId
, const ScrollableLayerGuid::ViewID
& aScrollId
,
105 AsyncTransformComponents aComponents
,
106 const MutexAutoLock
& aProofOfMapLock
) const {
107 MOZ_ASSERT(!CompositorThreadHolder::IsInCompositorThread());
108 AssertOnSamplerThread();
110 RefPtr
<AsyncPanZoomController
> apzc
=
111 mApz
->GetTargetAPZC(aLayersId
, aScrollId
, aProofOfMapLock
);
113 // It's possible that this function can get called even after the target
114 // APZC has been already destroyed because destroying the animation which
115 // triggers this function call is basically processed later than the APZC,
116 // i.e. queue mCompositorAnimationsToDelete in WebRenderBridgeParent and
117 // then remove in WebRenderBridgeParent::RemoveEpochDataPriorTo.
118 return AsyncTransform
{};
121 return apzc
->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing
,
125 ParentLayerRect
APZSampler::GetCompositionBounds(
126 const LayersId
& aLayersId
, const ScrollableLayerGuid::ViewID
& aScrollId
,
127 const MutexAutoLock
& aProofOfMapLock
) const {
128 // This function can get called on the compositor in case of non WebRender
129 // get called on the sampler thread in case of WebRender.
130 AssertOnSamplerThread();
132 RefPtr
<AsyncPanZoomController
> apzc
=
133 mApz
->GetTargetAPZC(aLayersId
, aScrollId
, aProofOfMapLock
);
135 // On WebRender it's possible that this function can get called even after
136 // the target APZC has been already destroyed because destroying the
137 // animation which triggers this function call is basically processed later
138 // than the APZC one, i.e. queue mCompositorAnimationsToDelete in
139 // WebRenderBridgeParent and then remove them in
140 // WebRenderBridgeParent::RemoveEpochDataPriorTo.
141 return ParentLayerRect();
144 return apzc
->GetCompositionBounds();
147 Maybe
<APZSampler::ScrollOffsetAndRange
>
148 APZSampler::GetCurrentScrollOffsetAndRange(
149 const LayersId
& aLayersId
, const ScrollableLayerGuid::ViewID
& aScrollId
,
150 const MutexAutoLock
& aProofOfMapLock
) const {
151 // Note: This is called from OMTA Sampler thread, or Compositor thread for
154 RefPtr
<AsyncPanZoomController
> apzc
=
155 mApz
->GetTargetAPZC(aLayersId
, aScrollId
, aProofOfMapLock
);
160 return Some(ScrollOffsetAndRange
{
161 // FIXME: Use the one-frame delayed offset now. This doesn't take
162 // scroll-linked effets into accounts, so we have to fix this in the
164 apzc
->GetCurrentAsyncVisualViewport(
165 AsyncTransformConsumer::eForCompositing
)
167 apzc
->GetCurrentScrollRangeInCssPixels()});
170 void APZSampler::AssertOnSamplerThread() const {
171 if (APZThreadUtils::GetThreadAssertionsEnabled()) {
172 MOZ_ASSERT(IsSamplerThread());
176 bool APZSampler::IsSamplerThread() const {
177 if (mIsUsingWebRender
) {
178 // If the sampler thread id isn't set yet then we cannot be running on the
179 // sampler thread (because we will have the thread id before we run any
180 // other C++ code on it, and this function is only ever invoked from C++
181 // code), so return false in that scenario.
182 MutexAutoLock
lock(mThreadIdLock
);
183 return mSamplerThreadId
&& PlatformThread::CurrentId() == *mSamplerThreadId
;
185 return CompositorThreadHolder::IsInCompositorThread();
189 already_AddRefed
<APZSampler
> APZSampler::GetSampler(
190 const wr::WrWindowId
& aWindowId
) {
191 RefPtr
<APZSampler
> sampler
;
192 StaticMutexAutoLock
lock(sWindowIdLock
);
194 auto it
= sWindowIdMap
->find(wr::AsUint64(aWindowId
));
195 if (it
!= sWindowIdMap
->end()) {
196 sampler
= it
->second
;
199 return sampler
.forget();
202 } // namespace layers
203 } // namespace mozilla
205 void apz_register_sampler(mozilla::wr::WrWindowId aWindowId
) {
206 mozilla::layers::APZSampler::SetSamplerThread(aWindowId
);
209 void apz_sample_transforms(mozilla::wr::WrWindowId aWindowId
,
210 const uint64_t* aGeneratedFrameId
,
211 mozilla::wr::Transaction
* aTransaction
) {
212 mozilla::layers::APZSampler::SampleForWebRender(aWindowId
, aGeneratedFrameId
,
216 void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId
) {}