In order to support scroll-linked animations, we need the scrolling information for sampling omt animations, so we would like to do APZ animations first. Now we clear the dynamic properties before sampling APZ and OMTA, so we can make sure the pending properties are empty before appending new ones. Therefore, let's just replace set_properties with add_properties because OMTA sampler shouldn't override the current pending properties which may be added by APZ sampler. Differential Revision: https://phabricator.services.mozilla.com/D122308
249 lines
8.3 KiB
C++
249 lines
8.3 KiB
C++
/* -*- 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) {}
|