gecko-dev/gfx/layers/wr/OMTASampler.cpp
Boris Chiou 61ad62ffc0 Bug 1626165 - Part 2: Replace the start value and start time of the transition on the compositor. r=layout-reviewers,firefox-animation-reviewers,hiro
Although we re-compute the start value of the transition when sending it
to the compositor, we still want to use the last sampled animation value
as the new start value, to avoid any possible jittery.

Also, we replace the start time with the previous sample time on the compositor
as well to make sure we use the proper start time for the transition if
the main thread is busy.

Differential Revision: https://phabricator.services.mozilla.com/D209889
2024-06-26 21:33:10 +00:00

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);
MutexAutoLock timeLock(mSampleTimeLock);
mAnimStorage->SetAnimations(aId, aLayersId, aAnimations, mPreviousSampleTime);
}
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) {}