APZSampler.cpp (7924B)
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/APZSampler.h" 8 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" 17 18 namespace mozilla { 19 namespace layers { 20 21 StaticMutex APZSampler::sWindowIdLock; 22 StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<APZSampler>>> 23 APZSampler::sWindowIdMap; 24 25 APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz, 26 bool aIsUsingWebRender) 27 : mApz(aApz), 28 mIsUsingWebRender(aIsUsingWebRender), 29 mThreadIdLock("APZSampler::mThreadIdLock"), 30 mSampleTimeLock("APZSampler::mSampleTimeLock") { 31 MOZ_ASSERT(aApz); 32 mApz->SetSampler(this); 33 } 34 35 APZSampler::~APZSampler() { mApz->SetSampler(nullptr); } 36 37 void APZSampler::Destroy() { 38 StaticMutexAutoLock lock(sWindowIdLock); 39 if (mWindowId) { 40 MOZ_ASSERT(sWindowIdMap); 41 sWindowIdMap->erase(wr::AsUint64(*mWindowId)); 42 } 43 } 44 45 void APZSampler::SetWebRenderWindowId(const wr::WindowId& aWindowId) { 46 StaticMutexAutoLock lock(sWindowIdLock); 47 MOZ_ASSERT(!mWindowId); 48 mWindowId = Some(aWindowId); 49 if (!sWindowIdMap) { 50 sWindowIdMap = new std::unordered_map<uint64_t, RefPtr<APZSampler>>(); 51 NS_DispatchToMainThread(NS_NewRunnableFunction( 52 "APZSampler::ClearOnShutdown", [] { ClearOnShutdown(&sWindowIdMap); })); 53 } 54 (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this; 55 } 56 57 /*static*/ 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()); 62 } 63 } 64 65 /*static*/ 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); 74 } 75 } 76 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; 84 } 85 86 void APZSampler::SampleForWebRender(const Maybe<VsyncId>& aVsyncId, 87 wr::TransactionWrapper& aTxn) { 88 AssertOnSamplerThread(); 89 SampleTime sampleTime; 90 { // scope lock 91 MutexAutoLock lock(mSampleTimeLock); 92 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; 99 } 100 mApz->SampleForWebRender(aVsyncId, aTxn, sampleTime); 101 } 102 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(); 109 110 RefPtr<AsyncPanZoomController> apzc = 111 mApz->GetTargetAPZC(aLayersId, aScrollId, aProofOfMapLock); 112 if (!apzc) { 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{}; 119 } 120 121 return apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing, 122 aComponents); 123 } 124 125 ParentLayerRect APZSampler::GetCompositionBounds( 126 const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, 127 const MutexAutoLock& aProofOfMapLock) const { 128 AssertOnSamplerThread(); 129 130 RefPtr<AsyncPanZoomController> apzc = 131 mApz->GetTargetAPZC(aLayersId, aScrollId, aProofOfMapLock); 132 if (!apzc) { 133 // It's possible that this function can get called even after the target 134 // APZC has been already destroyed because destroying the animation which 135 // triggers this function call is basically processed later than the APZC 136 // one, i.e. queue mCompositorAnimationsToDelete in WebRenderBridgeParent 137 // and then remove them in WebRenderBridgeParent::RemoveEpochDataPriorTo. 138 return ParentLayerRect(); 139 } 140 141 return apzc->GetCompositionBounds(); 142 } 143 144 Maybe<APZSampler::ScrollOffsetAndRange> 145 APZSampler::GetCurrentScrollOffsetAndRange( 146 const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, 147 const MutexAutoLock& aProofOfMapLock) const { 148 // Note: This is called from OMTA Sampler thread, or Compositor thread for 149 // testing. 150 151 RefPtr<AsyncPanZoomController> apzc = 152 mApz->GetTargetAPZC(aLayersId, aScrollId, aProofOfMapLock); 153 if (!apzc) { 154 return Nothing(); 155 } 156 157 return Some(ScrollOffsetAndRange{ 158 // FIXME: Use the one-frame delayed offset now. This doesn't take 159 // scroll-linked effets into accounts, so we have to fix this in the 160 // future. 161 apzc->GetCurrentAsyncVisualViewport( 162 AsyncTransformConsumer::eForCompositing) 163 .TopLeft(), 164 apzc->GetCurrentScrollRangeInCssPixels()}); 165 } 166 167 void APZSampler::AssertOnSamplerThread() const { 168 if (APZThreadUtils::GetThreadAssertionsEnabled()) { 169 MOZ_ASSERT(IsSamplerThread()); 170 } 171 } 172 173 bool APZSampler::IsSamplerThread() const { 174 if (mIsUsingWebRender) { 175 // If the sampler thread id isn't set yet then we cannot be running on the 176 // sampler thread (because we will have the thread id before we run any 177 // other C++ code on it, and this function is only ever invoked from C++ 178 // code), so return false in that scenario. 179 MutexAutoLock lock(mThreadIdLock); 180 return mSamplerThreadId && PlatformThread::CurrentId() == *mSamplerThreadId; 181 } 182 return CompositorThreadHolder::IsInCompositorThread(); 183 } 184 185 /*static*/ 186 already_AddRefed<APZSampler> APZSampler::GetSampler( 187 const wr::WrWindowId& aWindowId) { 188 RefPtr<APZSampler> sampler; 189 StaticMutexAutoLock lock(sWindowIdLock); 190 if (sWindowIdMap) { 191 auto it = sWindowIdMap->find(wr::AsUint64(aWindowId)); 192 if (it != sWindowIdMap->end()) { 193 sampler = it->second; 194 } 195 } 196 return sampler.forget(); 197 } 198 199 } // namespace layers 200 } // namespace mozilla 201 202 void apz_register_sampler(mozilla::wr::WrWindowId aWindowId) { 203 mozilla::layers::APZSampler::SetSamplerThread(aWindowId); 204 } 205 206 void apz_sample_transforms(mozilla::wr::WrWindowId aWindowId, 207 const uint64_t* aGeneratedFrameId, 208 mozilla::wr::Transaction* aTransaction) { 209 mozilla::layers::APZSampler::SampleForWebRender(aWindowId, aGeneratedFrameId, 210 aTransaction); 211 } 212 213 void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId) {}