diff options
Diffstat (limited to 'layout/painting/WindowRenderer.cpp')
-rw-r--r-- | layout/painting/WindowRenderer.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/layout/painting/WindowRenderer.cpp b/layout/painting/WindowRenderer.cpp new file mode 100644 index 0000000000..698e42f731 --- /dev/null +++ b/layout/painting/WindowRenderer.cpp @@ -0,0 +1,228 @@ +/* -*- 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 "WindowRenderer.h" + +#include "gfxPlatform.h" +#include "mozilla/dom/Animation.h" // for Animation +#include "mozilla/dom/AnimationEffect.h" +#include "mozilla/EffectSet.h" +#include "mozilla/layers/PersistentBufferProvider.h" // for PersistentBufferProviderBasic, PersistentBufferProvider (ptr only) +#include "nsDisplayList.h" + +using namespace mozilla::gfx; +using namespace mozilla::layers; + +namespace mozilla { + +/** + * StartFrameTimeRecording, together with StopFrameTimeRecording + * enable recording of frame intervals. + * + * To allow concurrent consumers, a cyclic array is used which serves all + * consumers, practically stateless with regard to consumers. + * + * To save resources, the buffer is allocated on first call to + * StartFrameTimeRecording and recording is paused if no consumer which called + * StartFrameTimeRecording is able to get valid results (because the cyclic + * buffer was overwritten since that call). + * + * To determine availability of the data upon StopFrameTimeRecording: + * - mRecording.mNextIndex increases on each RecordFrame, and never resets. + * - Cyclic buffer position is realized as mNextIndex % bufferSize. + * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is + * called, the required start index is passed as an arg, and we're able to + * calculate the required length. If this length is bigger than bufferSize, it + * means data was overwritten. otherwise, we can return the entire sequence. + * - To determine if we need to pause, mLatestStartIndex is updated to + * mNextIndex on each call to StartFrameTimeRecording. If this index gets + * overwritten, it means that all earlier start indices obtained via + * StartFrameTimeRecording were also overwritten, hence, no point in + * recording, so pause. + * - mCurrentRunStartIndex indicates the oldest index of the recording after + * which the recording was not paused. If StopFrameTimeRecording is invoked + * with a start index older than this, it means that some frames were not + * recorded, so data is invalid. + */ +uint32_t FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize) { + if (mRecording.mIsPaused) { + mRecording.mIsPaused = false; + + if (!mRecording.mIntervals.Length()) { // Initialize recording buffers + mRecording.mIntervals.SetLength(aBufferSize); + } + + // After being paused, recent values got invalid. Update them to now. + mRecording.mLastFrameTime = TimeStamp::Now(); + + // Any recording which started before this is invalid, since we were paused. + mRecording.mCurrentRunStartIndex = mRecording.mNextIndex; + } + + // If we'll overwrite this index, there are no more consumers with aStartIndex + // for which we're able to provide the full recording, so no point in keep + // recording. + mRecording.mLatestStartIndex = mRecording.mNextIndex; + return mRecording.mNextIndex; +} + +void FrameRecorder::RecordFrame() { + if (!mRecording.mIsPaused) { + TimeStamp now = TimeStamp::Now(); + uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length(); + mRecording.mIntervals[i] = + static_cast<float>((now - mRecording.mLastFrameTime).ToMilliseconds()); + mRecording.mNextIndex++; + mRecording.mLastFrameTime = now; + + if (mRecording.mNextIndex > + (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) { + // We've just overwritten the most recent recording start -> pause. + mRecording.mIsPaused = true; + } + } +} + +void FrameRecorder::StopFrameTimeRecording(uint32_t aStartIndex, + nsTArray<float>& aFrameIntervals) { + uint32_t bufferSize = mRecording.mIntervals.Length(); + uint32_t length = mRecording.mNextIndex - aStartIndex; + if (mRecording.mIsPaused || length > bufferSize || + aStartIndex < mRecording.mCurrentRunStartIndex) { + // aStartIndex is too old. Also if aStartIndex was issued before + // mRecordingNextIndex overflowed (uint32_t) + // and stopped after the overflow (would happen once every 828 days of + // constant 60fps). + length = 0; + } + + if (!length) { + aFrameIntervals.Clear(); + return; // empty recording, return empty arrays. + } + // Set length in advance to avoid possibly repeated reallocations + aFrameIntervals.SetLength(length); + + uint32_t cyclicPos = aStartIndex % bufferSize; + for (uint32_t i = 0; i < length; i++, cyclicPos++) { + if (cyclicPos == bufferSize) { + cyclicPos = 0; + } + aFrameIntervals[i] = mRecording.mIntervals[cyclicPos]; + } +} + +already_AddRefed<PersistentBufferProvider> +WindowRenderer::CreatePersistentBufferProvider( + const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat) { + RefPtr<PersistentBufferProviderBasic> bufferProvider; + // If we are using remote canvas we don't want to use acceleration in + // non-remote layer managers, so we always use the fallback software one. + if (!gfxPlatform::UseRemoteCanvas() || + !gfxPlatform::IsBackendAccelerated( + gfxPlatform::GetPlatform()->GetPreferredCanvasBackend())) { + bufferProvider = PersistentBufferProviderBasic::Create( + aSize, aFormat, + gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()); + } + + if (!bufferProvider) { + bufferProvider = PersistentBufferProviderBasic::Create( + aSize, aFormat, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend()); + } + + return bufferProvider.forget(); +} + +void WindowRenderer::AddPartialPrerenderedAnimation( + uint64_t aCompositorAnimationId, dom::Animation* aAnimation) { + mPartialPrerenderedAnimations.InsertOrUpdate(aCompositorAnimationId, + RefPtr{aAnimation}); + aAnimation->SetPartialPrerendered(aCompositorAnimationId); +} +void WindowRenderer::RemovePartialPrerenderedAnimation( + uint64_t aCompositorAnimationId, dom::Animation* aAnimation) { + MOZ_ASSERT(aAnimation); +#ifdef DEBUG + RefPtr<dom::Animation> animation; + if (mPartialPrerenderedAnimations.Remove(aCompositorAnimationId, + getter_AddRefs(animation)) && + // It may be possible that either animation's effect has already been + // nulled out via Animation::SetEffect() so ignore such cases. + aAnimation->GetEffect() && aAnimation->GetEffect()->AsKeyframeEffect() && + animation->GetEffect() && animation->GetEffect()->AsKeyframeEffect()) { + MOZ_ASSERT(EffectSet::GetEffectSetForEffect( + aAnimation->GetEffect()->AsKeyframeEffect()) == + EffectSet::GetEffectSetForEffect( + animation->GetEffect()->AsKeyframeEffect())); + } +#else + mPartialPrerenderedAnimations.Remove(aCompositorAnimationId); +#endif + aAnimation->ResetPartialPrerendered(); +} +void WindowRenderer::UpdatePartialPrerenderedAnimations( + const nsTArray<uint64_t>& aJankedAnimations) { + for (uint64_t id : aJankedAnimations) { + RefPtr<dom::Animation> animation; + if (mPartialPrerenderedAnimations.Remove(id, getter_AddRefs(animation))) { + animation->UpdatePartialPrerendered(); + } + } +} + +void FallbackRenderer::SetTarget(gfxContext* aTarget, + layers::BufferMode aDoubleBuffering) { + mTarget = aTarget; + mBufferMode = aDoubleBuffering; +} + +bool FallbackRenderer::BeginTransaction(const nsCString& aURL) { + if (!mTarget) { + return false; + } + + return true; +} + +void FallbackRenderer::EndTransactionWithColor(const nsIntRect& aRect, + const gfx::DeviceColor& aColor) { + mTarget->GetDrawTarget()->FillRect(Rect(aRect), ColorPattern(aColor)); + mAnimationReadyTime = TimeStamp::Now(); +} + +void FallbackRenderer::EndTransactionWithList(nsDisplayListBuilder* aBuilder, + nsDisplayList* aList, + int32_t aAppUnitsPerDevPixel, + EndTransactionFlags aFlags) { + if (aFlags & EndTransactionFlags::END_NO_COMPOSITE) { + return; + } + + DrawTarget* dt = mTarget->GetDrawTarget(); + + BackendType backend = gfxPlatform::GetPlatform()->GetContentBackendFor( + LayersBackend::LAYERS_NONE); + RefPtr<DrawTarget> dest = + gfxPlatform::GetPlatform()->CreateDrawTargetForBackend( + backend, dt->GetSize(), dt->GetFormat()); + RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dest); + + nsRegion opaque = aList->GetOpaqueRegion(aBuilder); + if (opaque.Contains(aList->GetComponentAlphaBounds(aBuilder))) { + dest->SetPermitSubpixelAA(true); + } + + aList->Paint(aBuilder, ctx, aAppUnitsPerDevPixel); + + RefPtr<SourceSurface> snapshot = dest->Snapshot(); + dt->DrawSurface(snapshot, Rect(dest->GetRect()), Rect(dest->GetRect()), + DrawSurfaceOptions(), + DrawOptions(1.0f, CompositionOp::OP_SOURCE)); + mAnimationReadyTime = TimeStamp::Now(); +} + +} // namespace mozilla |