diff options
Diffstat (limited to 'gfx/layers/wr/OMTASampler.cpp')
-rw-r--r-- | gfx/layers/wr/OMTASampler.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/gfx/layers/wr/OMTASampler.cpp b/gfx/layers/wr/OMTASampler.cpp new file mode 100644 index 0000000000..c9616e87e1 --- /dev/null +++ b/gfx/layers/wr/OMTASampler.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/layers/OMTASampler.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/layers/CompositorAnimationStorage.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/OMTAController.h" +#include "mozilla/layers/SynchronousTask.h" +#include "mozilla/layers/WebRenderBridgeParent.h" +#include "mozilla/webrender/WebRenderAPI.h" + +namespace mozilla { +namespace layers { + +StaticMutex OMTASampler::sWindowIdLock; +StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<OMTASampler>>> + OMTASampler::sWindowIdMap; + +OMTASampler::OMTASampler(const RefPtr<CompositorAnimationStorage>& aAnimStorage, + LayersId aRootLayersId) + : mAnimStorage(aAnimStorage), + mStorageLock("OMTASampler::mStorageLock"), + mThreadIdLock("OMTASampler::mThreadIdLock"), + mSampleTimeLock("OMTASampler::mSampleTimeLock"), + mIsInTestMode(false) { + mController = new OMTAController(aRootLayersId); +} + +void OMTASampler::Destroy() { + StaticMutexAutoLock lock(sWindowIdLock); + if (mWindowId) { + MOZ_ASSERT(sWindowIdMap); + sWindowIdMap->erase(wr::AsUint64(*mWindowId)); + } +} + +void OMTASampler::SetWebRenderWindowId(const wr::WrWindowId& aWindowId) { + StaticMutexAutoLock lock(sWindowIdLock); + MOZ_ASSERT(!mWindowId); + mWindowId = Some(aWindowId); + if (!sWindowIdMap) { + sWindowIdMap = new std::unordered_map<uint64_t, RefPtr<OMTASampler>>(); + NS_DispatchToMainThread( + NS_NewRunnableFunction("OMTASampler::ClearOnShutdown", + [] { ClearOnShutdown(&sWindowIdMap); })); + } + (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this; +} + +/*static*/ +void OMTASampler::SetSamplerThread(const wr::WrWindowId& aWindowId) { + if (RefPtr<OMTASampler> sampler = GetSampler(aWindowId)) { + MutexAutoLock lock(sampler->mThreadIdLock); + sampler->mSamplerThreadId = Some(PlatformThread::CurrentId()); + } +} + +/*static*/ +void OMTASampler::Sample(const wr::WrWindowId& aWindowId, + wr::Transaction* aTransaction) { + if (RefPtr<OMTASampler> sampler = GetSampler(aWindowId)) { + wr::TransactionWrapper txn(aTransaction); + sampler->Sample(txn); + } +} + +void OMTASampler::SetSampleTime(const TimeStamp& aSampleTime) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + const bool hasAnimations = HasAnimations(); + + MutexAutoLock lock(mSampleTimeLock); + + // Reset the previous time stamp if we don't already have any running + // animations to avoid using the time which is far behind for newly + // started animations. + mPreviousSampleTime = hasAnimations ? std::move(mSampleTime) : TimeStamp(); + mSampleTime = aSampleTime; +} + +void OMTASampler::ResetPreviousSampleTime() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mSampleTimeLock); + + mPreviousSampleTime = TimeStamp(); +} + +void OMTASampler::Sample(wr::TransactionWrapper& aTxn) { + MOZ_ASSERT(IsSamplerThread()); + + // If we are in test mode, don't sample with the current time stamp, it will + // skew cached animation values. + if (mIsInTestMode) { + return; + } + + TimeStamp sampleTime; + TimeStamp previousSampleTime; + { // scope lock + MutexAutoLock lock(mSampleTimeLock); + + // If mSampleTime is null we're in a startup phase where the + // WebRenderBridgeParent hasn't yet provided us with a sample time. + // If we're that early there probably aren't any OMTA animations happening + // anyway, so using Timestamp::Now() should be fine. + sampleTime = mSampleTime.IsNull() ? TimeStamp::Now() : mSampleTime; + previousSampleTime = mPreviousSampleTime; + } + + WrAnimations animations = SampleAnimations(previousSampleTime, sampleTime); + + aTxn.AppendDynamicProperties(animations.mOpacityArrays, + animations.mTransformArrays, + animations.mColorArrays); +} + +WrAnimations OMTASampler::SampleAnimations(const TimeStamp& aPreviousSampleTime, + const TimeStamp& aSampleTime) { + MOZ_ASSERT(IsSamplerThread()); + + MutexAutoLock lock(mStorageLock); + + mAnimStorage->SampleAnimations(mController, aPreviousSampleTime, aSampleTime); + + return mAnimStorage->CollectWebRenderAnimations(); +} + +OMTAValue OMTASampler::GetOMTAValue(const uint64_t& aId) const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mStorageLock); + + return mAnimStorage->GetOMTAValue(aId); +} + +void OMTASampler::SampleForTesting(const Maybe<TimeStamp>& aTestingSampleTime) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + TimeStamp sampleTime; + TimeStamp previousSampleTime; + { // scope lock + MutexAutoLock timeLock(mSampleTimeLock); + if (aTestingSampleTime) { + // If we are on testing refresh mode, use the testing time stamp for both + // of the previous sample time and the current sample time since unlike + // normal refresh mode, the testing mode animations on the compositor are + // synchronously composed, so we don't need to worry about the time gap + // between the main thread and compositor thread. + sampleTime = *aTestingSampleTime; + previousSampleTime = *aTestingSampleTime; + } else { + sampleTime = mSampleTime; + previousSampleTime = mPreviousSampleTime; + } + } + + MutexAutoLock storageLock(mStorageLock); + mAnimStorage->SampleAnimations(mController, previousSampleTime, sampleTime); +} + +void OMTASampler::SetAnimations( + uint64_t aId, const LayersId& aLayersId, + const nsTArray<layers::Animation>& aAnimations) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mStorageLock); + + mAnimStorage->SetAnimations(aId, aLayersId, aAnimations); +} + +bool OMTASampler::HasAnimations() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mStorageLock); + + return mAnimStorage->HasAnimations(); +} + +void OMTASampler::ClearActiveAnimations( + std::unordered_map<uint64_t, wr::WrEpoch>& aActiveAnimations) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mStorageLock); + for (const auto& id : aActiveAnimations) { + mAnimStorage->ClearById(id.first); + } +} + +void OMTASampler::RemoveEpochDataPriorTo( + std::queue<CompositorAnimationIdsForEpoch>& aCompositorAnimationsToDelete, + std::unordered_map<uint64_t, wr::WrEpoch>& aActiveAnimations, + const wr::WrEpoch& aRenderedEpoch) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mStorageLock); + + while (!aCompositorAnimationsToDelete.empty()) { + if (aRenderedEpoch < aCompositorAnimationsToDelete.front().mEpoch) { + break; + } + for (uint64_t id : aCompositorAnimationsToDelete.front().mIds) { + const auto activeAnim = aActiveAnimations.find(id); + if (activeAnim == aActiveAnimations.end()) { + NS_ERROR("Tried to delete invalid animation"); + continue; + } + // Check if animation delete request is still valid. + if (activeAnim->second <= aCompositorAnimationsToDelete.front().mEpoch) { + mAnimStorage->ClearById(id); + aActiveAnimations.erase(activeAnim); + } + } + aCompositorAnimationsToDelete.pop(); + } +} + +bool OMTASampler::IsSamplerThread() const { + MutexAutoLock lock(mThreadIdLock); + return mSamplerThreadId && PlatformThread::CurrentId() == *mSamplerThreadId; +} + +/*static*/ +already_AddRefed<OMTASampler> OMTASampler::GetSampler( + const wr::WrWindowId& aWindowId) { + RefPtr<OMTASampler> sampler; + StaticMutexAutoLock lock(sWindowIdLock); + if (sWindowIdMap) { + auto it = sWindowIdMap->find(wr::AsUint64(aWindowId)); + if (it != sWindowIdMap->end()) { + sampler = it->second; + } + } + return sampler.forget(); +} + +} // namespace layers +} // namespace mozilla + +void omta_register_sampler(mozilla::wr::WrWindowId aWindowId) { + mozilla::layers::OMTASampler::SetSamplerThread(aWindowId); +} + +void omta_sample(mozilla::wr::WrWindowId aWindowId, + mozilla::wr::Transaction* aTransaction) { + mozilla::layers::OMTASampler::Sample(aWindowId, aTransaction); +} + +void omta_deregister_sampler(mozilla::wr::WrWindowId aWindowId) {} |