1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "WindowRenderer.h"
8 
9 #include "gfxPlatform.h"
10 #include "mozilla/dom/Animation.h"  // for Animation
11 #include "mozilla/dom/AnimationEffect.h"
12 #include "mozilla/EffectSet.h"
13 #include "mozilla/layers/PersistentBufferProvider.h"  // for PersistentBufferProviderBasic, PersistentBufferProvider (ptr only)
14 #include "nsDisplayList.h"
15 
16 using namespace mozilla::gfx;
17 using namespace mozilla::layers;
18 
19 namespace mozilla {
20 
21 /**
22  * StartFrameTimeRecording, together with StopFrameTimeRecording
23  * enable recording of frame intervals.
24  *
25  * To allow concurrent consumers, a cyclic array is used which serves all
26  * consumers, practically stateless with regard to consumers.
27  *
28  * To save resources, the buffer is allocated on first call to
29  * StartFrameTimeRecording and recording is paused if no consumer which called
30  * StartFrameTimeRecording is able to get valid results (because the cyclic
31  * buffer was overwritten since that call).
32  *
33  * To determine availability of the data upon StopFrameTimeRecording:
34  * - mRecording.mNextIndex increases on each RecordFrame, and never resets.
35  * - Cyclic buffer position is realized as mNextIndex % bufferSize.
36  * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is
37  *   called, the required start index is passed as an arg, and we're able to
38  *   calculate the required length. If this length is bigger than bufferSize, it
39  *   means data was overwritten.  otherwise, we can return the entire sequence.
40  * - To determine if we need to pause, mLatestStartIndex is updated to
41  *   mNextIndex on each call to StartFrameTimeRecording. If this index gets
42  *   overwritten, it means that all earlier start indices obtained via
43  *   StartFrameTimeRecording were also overwritten, hence, no point in
44  *   recording, so pause.
45  * - mCurrentRunStartIndex indicates the oldest index of the recording after
46  *   which the recording was not paused. If StopFrameTimeRecording is invoked
47  *   with a start index older than this, it means that some frames were not
48  *   recorded, so data is invalid.
49  */
StartFrameTimeRecording(int32_t aBufferSize)50 uint32_t FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize) {
51   if (mRecording.mIsPaused) {
52     mRecording.mIsPaused = false;
53 
54     if (!mRecording.mIntervals.Length()) {  // Initialize recording buffers
55       mRecording.mIntervals.SetLength(aBufferSize);
56     }
57 
58     // After being paused, recent values got invalid. Update them to now.
59     mRecording.mLastFrameTime = TimeStamp::Now();
60 
61     // Any recording which started before this is invalid, since we were paused.
62     mRecording.mCurrentRunStartIndex = mRecording.mNextIndex;
63   }
64 
65   // If we'll overwrite this index, there are no more consumers with aStartIndex
66   // for which we're able to provide the full recording, so no point in keep
67   // recording.
68   mRecording.mLatestStartIndex = mRecording.mNextIndex;
69   return mRecording.mNextIndex;
70 }
71 
RecordFrame()72 void FrameRecorder::RecordFrame() {
73   if (!mRecording.mIsPaused) {
74     TimeStamp now = TimeStamp::Now();
75     uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length();
76     mRecording.mIntervals[i] =
77         static_cast<float>((now - mRecording.mLastFrameTime).ToMilliseconds());
78     mRecording.mNextIndex++;
79     mRecording.mLastFrameTime = now;
80 
81     if (mRecording.mNextIndex >
82         (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) {
83       // We've just overwritten the most recent recording start -> pause.
84       mRecording.mIsPaused = true;
85     }
86   }
87 }
88 
StopFrameTimeRecording(uint32_t aStartIndex,nsTArray<float> & aFrameIntervals)89 void FrameRecorder::StopFrameTimeRecording(uint32_t aStartIndex,
90                                            nsTArray<float>& aFrameIntervals) {
91   uint32_t bufferSize = mRecording.mIntervals.Length();
92   uint32_t length = mRecording.mNextIndex - aStartIndex;
93   if (mRecording.mIsPaused || length > bufferSize ||
94       aStartIndex < mRecording.mCurrentRunStartIndex) {
95     // aStartIndex is too old. Also if aStartIndex was issued before
96     // mRecordingNextIndex overflowed (uint32_t)
97     //   and stopped after the overflow (would happen once every 828 days of
98     //   constant 60fps).
99     length = 0;
100   }
101 
102   if (!length) {
103     aFrameIntervals.Clear();
104     return;  // empty recording, return empty arrays.
105   }
106   // Set length in advance to avoid possibly repeated reallocations
107   aFrameIntervals.SetLength(length);
108 
109   uint32_t cyclicPos = aStartIndex % bufferSize;
110   for (uint32_t i = 0; i < length; i++, cyclicPos++) {
111     if (cyclicPos == bufferSize) {
112       cyclicPos = 0;
113     }
114     aFrameIntervals[i] = mRecording.mIntervals[cyclicPos];
115   }
116 }
117 
118 already_AddRefed<PersistentBufferProvider>
CreatePersistentBufferProvider(const mozilla::gfx::IntSize & aSize,mozilla::gfx::SurfaceFormat aFormat)119 WindowRenderer::CreatePersistentBufferProvider(
120     const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat) {
121   RefPtr<PersistentBufferProviderBasic> bufferProvider;
122   // If we are using remote canvas we don't want to use acceleration in
123   // non-remote layer managers, so we always use the fallback software one.
124   if (!gfxPlatform::UseRemoteCanvas() ||
125       !gfxPlatform::IsBackendAccelerated(
126           gfxPlatform::GetPlatform()->GetPreferredCanvasBackend())) {
127     bufferProvider = PersistentBufferProviderBasic::Create(
128         aSize, aFormat,
129         gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
130   }
131 
132   if (!bufferProvider) {
133     bufferProvider = PersistentBufferProviderBasic::Create(
134         aSize, aFormat, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
135   }
136 
137   return bufferProvider.forget();
138 }
139 
AddPartialPrerenderedAnimation(uint64_t aCompositorAnimationId,dom::Animation * aAnimation)140 void WindowRenderer::AddPartialPrerenderedAnimation(
141     uint64_t aCompositorAnimationId, dom::Animation* aAnimation) {
142   mPartialPrerenderedAnimations.InsertOrUpdate(aCompositorAnimationId,
143                                                RefPtr{aAnimation});
144   aAnimation->SetPartialPrerendered(aCompositorAnimationId);
145 }
RemovePartialPrerenderedAnimation(uint64_t aCompositorAnimationId,dom::Animation * aAnimation)146 void WindowRenderer::RemovePartialPrerenderedAnimation(
147     uint64_t aCompositorAnimationId, dom::Animation* aAnimation) {
148   MOZ_ASSERT(aAnimation);
149 #ifdef DEBUG
150   RefPtr<dom::Animation> animation;
151   if (mPartialPrerenderedAnimations.Remove(aCompositorAnimationId,
152                                            getter_AddRefs(animation)) &&
153       // It may be possible that either animation's effect has already been
154       // nulled out via Animation::SetEffect() so ignore such cases.
155       aAnimation->GetEffect() && aAnimation->GetEffect()->AsKeyframeEffect() &&
156       animation->GetEffect() && animation->GetEffect()->AsKeyframeEffect()) {
157     MOZ_ASSERT(EffectSet::GetEffectSetForEffect(
158                    aAnimation->GetEffect()->AsKeyframeEffect()) ==
159                EffectSet::GetEffectSetForEffect(
160                    animation->GetEffect()->AsKeyframeEffect()));
161   }
162 #else
163   mPartialPrerenderedAnimations.Remove(aCompositorAnimationId);
164 #endif
165   aAnimation->ResetPartialPrerendered();
166 }
UpdatePartialPrerenderedAnimations(const nsTArray<uint64_t> & aJankedAnimations)167 void WindowRenderer::UpdatePartialPrerenderedAnimations(
168     const nsTArray<uint64_t>& aJankedAnimations) {
169   for (uint64_t id : aJankedAnimations) {
170     RefPtr<dom::Animation> animation;
171     if (mPartialPrerenderedAnimations.Remove(id, getter_AddRefs(animation))) {
172       animation->UpdatePartialPrerendered();
173     }
174   }
175 }
176 
SetTarget(gfxContext * aTarget,layers::BufferMode aDoubleBuffering)177 void FallbackRenderer::SetTarget(gfxContext* aTarget,
178                                  layers::BufferMode aDoubleBuffering) {
179   mTarget = aTarget;
180   mBufferMode = aDoubleBuffering;
181 }
182 
BeginTransaction(const nsCString & aURL)183 bool FallbackRenderer::BeginTransaction(const nsCString& aURL) {
184   if (!mTarget) {
185     return false;
186   }
187 
188   return true;
189 }
190 
EndTransactionWithColor(const nsIntRect & aRect,const gfx::DeviceColor & aColor)191 void FallbackRenderer::EndTransactionWithColor(const nsIntRect& aRect,
192                                                const gfx::DeviceColor& aColor) {
193   mTarget->GetDrawTarget()->FillRect(Rect(aRect), ColorPattern(aColor));
194   mAnimationReadyTime = TimeStamp::Now();
195 }
196 
EndTransactionWithList(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,int32_t aAppUnitsPerDevPixel,EndTransactionFlags aFlags)197 void FallbackRenderer::EndTransactionWithList(nsDisplayListBuilder* aBuilder,
198                                               nsDisplayList* aList,
199                                               int32_t aAppUnitsPerDevPixel,
200                                               EndTransactionFlags aFlags) {
201   if (aFlags & EndTransactionFlags::END_NO_COMPOSITE) {
202     return;
203   }
204 
205   DrawTarget* dt = mTarget->GetDrawTarget();
206 
207   BackendType backend = gfxPlatform::GetPlatform()->GetContentBackendFor(
208       LayersBackend::LAYERS_NONE);
209   RefPtr<DrawTarget> dest =
210       gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
211           backend, dt->GetSize(), dt->GetFormat());
212   RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dest);
213 
214   nsRegion opaque = aList->GetOpaqueRegion(aBuilder);
215   if (opaque.Contains(aList->GetComponentAlphaBounds(aBuilder))) {
216     dest->SetPermitSubpixelAA(true);
217   }
218 
219   aList->Paint(aBuilder, ctx, aAppUnitsPerDevPixel);
220 
221   RefPtr<SourceSurface> snapshot = dest->Snapshot();
222   dt->DrawSurface(snapshot, Rect(dest->GetRect()), Rect(dest->GetRect()),
223                   DrawSurfaceOptions(),
224                   DrawOptions(1.0f, CompositionOp::OP_SOURCE));
225   mAnimationReadyTime = TimeStamp::Now();
226 }
227 
228 }  // namespace mozilla
229