Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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) {}