OMTASampler.cpp (8525B)
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/OMTASampler.h" 8 9 #include "mozilla/ClearOnShutdown.h" 10 #include "mozilla/layers/CompositorAnimationStorage.h" 11 #include "mozilla/layers/CompositorThread.h" 12 #include "mozilla/layers/OMTAController.h" 13 #include "mozilla/layers/SynchronousTask.h" 14 #include "mozilla/layers/WebRenderBridgeParent.h" 15 #include "mozilla/webrender/WebRenderAPI.h" 16 17 namespace mozilla { 18 namespace layers { 19 20 StaticMutex OMTASampler::sWindowIdLock; 21 StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<OMTASampler>>> 22 OMTASampler::sWindowIdMap; 23 24 OMTASampler::OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage, 25 LayersId aRootLayersId) 26 : mAnimStorage(aAnimStorage), 27 mStorageLock("OMTASampler::mStorageLock"), 28 mThreadIdLock("OMTASampler::mThreadIdLock"), 29 mSampleTimeLock("OMTASampler::mSampleTimeLock"), 30 mIsInTestMode(false) { 31 mController = new OMTAController(aRootLayersId); 32 } 33 34 void OMTASampler::Destroy() { 35 StaticMutexAutoLock lock(sWindowIdLock); 36 if (mWindowId) { 37 MOZ_ASSERT(sWindowIdMap); 38 sWindowIdMap->erase(wr::AsUint64(*mWindowId)); 39 } 40 } 41 42 void OMTASampler::SetWebRenderWindowId(const wr::WrWindowId& aWindowId) { 43 StaticMutexAutoLock lock(sWindowIdLock); 44 MOZ_ASSERT(!mWindowId); 45 mWindowId = Some(aWindowId); 46 if (!sWindowIdMap) { 47 sWindowIdMap = new std::unordered_map<uint64_t, RefPtr<OMTASampler>>(); 48 NS_DispatchToMainThread( 49 NS_NewRunnableFunction("OMTASampler::ClearOnShutdown", 50 [] { ClearOnShutdown(&sWindowIdMap); })); 51 } 52 (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this; 53 } 54 55 /*static*/ 56 void OMTASampler::SetSamplerThread(const wr::WrWindowId& aWindowId) { 57 if (RefPtr<OMTASampler> sampler = GetSampler(aWindowId)) { 58 MutexAutoLock lock(sampler->mThreadIdLock); 59 sampler->mSamplerThreadId = Some(PlatformThread::CurrentId()); 60 } 61 } 62 63 /*static*/ 64 void OMTASampler::Sample(const wr::WrWindowId& aWindowId, 65 wr::Transaction* aTransaction) { 66 if (RefPtr<OMTASampler> sampler = GetSampler(aWindowId)) { 67 wr::TransactionWrapper txn(aTransaction); 68 sampler->Sample(txn); 69 } 70 } 71 72 void OMTASampler::SetSampleTime(const TimeStamp& aSampleTime) { 73 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 74 75 const bool hasAnimations = HasAnimations(); 76 77 MutexAutoLock lock(mSampleTimeLock); 78 79 // Reset the previous time stamp if we don't already have any running 80 // animations to avoid using the time which is far behind for newly 81 // started animations. 82 mPreviousSampleTime = hasAnimations ? std::move(mSampleTime) : TimeStamp(); 83 mSampleTime = aSampleTime; 84 } 85 86 void OMTASampler::ResetPreviousSampleTime() { 87 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 88 MutexAutoLock lock(mSampleTimeLock); 89 90 mPreviousSampleTime = TimeStamp(); 91 } 92 93 void OMTASampler::Sample(wr::TransactionWrapper& aTxn) { 94 MOZ_ASSERT(IsSamplerThread()); 95 96 // If we are in test mode, don't sample with the current time stamp, it will 97 // skew cached animation values. 98 if (mIsInTestMode) { 99 return; 100 } 101 102 TimeStamp sampleTime; 103 TimeStamp previousSampleTime; 104 { // scope lock 105 MutexAutoLock lock(mSampleTimeLock); 106 107 // If mSampleTime is null we're in a startup phase where the 108 // WebRenderBridgeParent hasn't yet provided us with a sample time. 109 // If we're that early there probably aren't any OMTA animations happening 110 // anyway, so using Timestamp::Now() should be fine. 111 sampleTime = mSampleTime.IsNull() ? TimeStamp::Now() : mSampleTime; 112 previousSampleTime = mPreviousSampleTime; 113 } 114 115 WrAnimations animations = SampleAnimations(previousSampleTime, sampleTime); 116 117 aTxn.AppendDynamicProperties(animations.mOpacityArrays, 118 animations.mTransformArrays, 119 animations.mColorArrays); 120 } 121 122 WrAnimations OMTASampler::SampleAnimations(const TimeStamp& aPreviousSampleTime, 123 const TimeStamp& aSampleTime) { 124 MOZ_ASSERT(IsSamplerThread()); 125 126 MutexAutoLock lock(mStorageLock); 127 128 mAnimStorage->SampleAnimations(mController, aPreviousSampleTime, aSampleTime); 129 130 return mAnimStorage->CollectWebRenderAnimations(); 131 } 132 133 OMTAValue OMTASampler::GetOMTAValue(const uint64_t& aId) const { 134 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 135 MutexAutoLock lock(mStorageLock); 136 137 return mAnimStorage->GetOMTAValue(aId); 138 } 139 140 void OMTASampler::SampleForTesting(const Maybe<TimeStamp>& aTestingSampleTime) { 141 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 142 143 TimeStamp sampleTime; 144 TimeStamp previousSampleTime; 145 { // scope lock 146 MutexAutoLock timeLock(mSampleTimeLock); 147 if (aTestingSampleTime) { 148 // If we are on testing refresh mode, use the testing time stamp for both 149 // of the previous sample time and the current sample time since unlike 150 // normal refresh mode, the testing mode animations on the compositor are 151 // synchronously composed, so we don't need to worry about the time gap 152 // between the main thread and compositor thread. 153 sampleTime = *aTestingSampleTime; 154 previousSampleTime = *aTestingSampleTime; 155 } else { 156 sampleTime = mSampleTime; 157 previousSampleTime = mPreviousSampleTime; 158 } 159 } 160 161 MutexAutoLock storageLock(mStorageLock); 162 mAnimStorage->SampleAnimations(mController, previousSampleTime, sampleTime); 163 } 164 165 void OMTASampler::SetAnimations( 166 uint64_t aId, const LayersId& aLayersId, 167 const nsTArray<layers::Animation>& aAnimations) { 168 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 169 MutexAutoLock lock(mStorageLock); 170 MutexAutoLock timeLock(mSampleTimeLock); 171 mAnimStorage->SetAnimations(aId, aLayersId, aAnimations, mPreviousSampleTime); 172 } 173 174 bool OMTASampler::HasAnimations() const { 175 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 176 MutexAutoLock lock(mStorageLock); 177 178 return mAnimStorage->HasAnimations(); 179 } 180 181 void OMTASampler::ClearActiveAnimations( 182 std::unordered_map<uint64_t, wr::WrEpoch>& aActiveAnimations) { 183 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 184 MutexAutoLock lock(mStorageLock); 185 for (const auto& id : aActiveAnimations) { 186 mAnimStorage->ClearById(id.first); 187 } 188 } 189 190 void OMTASampler::RemoveEpochDataPriorTo( 191 std::queue<CompositorAnimationIdsForEpoch>& aCompositorAnimationsToDelete, 192 std::unordered_map<uint64_t, wr::WrEpoch>& aActiveAnimations, 193 const wr::WrEpoch& aRenderedEpoch) { 194 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 195 MutexAutoLock lock(mStorageLock); 196 197 while (!aCompositorAnimationsToDelete.empty()) { 198 if (aRenderedEpoch < aCompositorAnimationsToDelete.front().mEpoch) { 199 break; 200 } 201 for (uint64_t id : aCompositorAnimationsToDelete.front().mIds) { 202 const auto activeAnim = aActiveAnimations.find(id); 203 if (activeAnim == aActiveAnimations.end()) { 204 NS_ERROR("Tried to delete invalid animation"); 205 continue; 206 } 207 // Check if animation delete request is still valid. 208 if (activeAnim->second <= aCompositorAnimationsToDelete.front().mEpoch) { 209 mAnimStorage->ClearById(id); 210 aActiveAnimations.erase(activeAnim); 211 } 212 } 213 aCompositorAnimationsToDelete.pop(); 214 } 215 } 216 217 bool OMTASampler::IsSamplerThread() const { 218 MutexAutoLock lock(mThreadIdLock); 219 return mSamplerThreadId && PlatformThread::CurrentId() == *mSamplerThreadId; 220 } 221 222 /*static*/ 223 already_AddRefed<OMTASampler> OMTASampler::GetSampler( 224 const wr::WrWindowId& aWindowId) { 225 RefPtr<OMTASampler> sampler; 226 StaticMutexAutoLock lock(sWindowIdLock); 227 if (sWindowIdMap) { 228 auto it = sWindowIdMap->find(wr::AsUint64(aWindowId)); 229 if (it != sWindowIdMap->end()) { 230 sampler = it->second; 231 } 232 } 233 return sampler.forget(); 234 } 235 236 } // namespace layers 237 } // namespace mozilla 238 239 void omta_register_sampler(mozilla::wr::WrWindowId aWindowId) { 240 mozilla::layers::OMTASampler::SetSamplerThread(aWindowId); 241 } 242 243 void omta_sample(mozilla::wr::WrWindowId aWindowId, 244 mozilla::wr::Transaction* aTransaction) { 245 mozilla::layers::OMTASampler::Sample(aWindowId, aTransaction); 246 } 247 248 void omta_deregister_sampler(mozilla::wr::WrWindowId aWindowId) {}