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/. */
7 #include "WindowRenderer.h"
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"
16 using namespace mozilla::gfx
;
17 using namespace mozilla::layers
;
22 * StartFrameTimeRecording, together with StopFrameTimeRecording
23 * enable recording of frame intervals.
25 * To allow concurrent consumers, a cyclic array is used which serves all
26 * consumers, practically stateless with regard to consumers.
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).
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.
50 uint32_t FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize
) {
51 if (mRecording
.mIsPaused
) {
52 mRecording
.mIsPaused
= false;
54 if (!mRecording
.mIntervals
.Length()) { // Initialize recording buffers
55 mRecording
.mIntervals
.SetLength(aBufferSize
);
58 // After being paused, recent values got invalid. Update them to now.
59 mRecording
.mLastFrameTime
= TimeStamp::Now();
61 // Any recording which started before this is invalid, since we were paused.
62 mRecording
.mCurrentRunStartIndex
= mRecording
.mNextIndex
;
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
68 mRecording
.mLatestStartIndex
= mRecording
.mNextIndex
;
69 return mRecording
.mNextIndex
;
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
;
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;
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
103 aFrameIntervals
.Clear();
104 return; // empty recording, return empty arrays.
106 // Set length in advance to avoid possibly repeated reallocations
107 aFrameIntervals
.SetLength(length
);
109 uint32_t cyclicPos
= aStartIndex
% bufferSize
;
110 for (uint32_t i
= 0; i
< length
; i
++, cyclicPos
++) {
111 if (cyclicPos
== bufferSize
) {
114 aFrameIntervals
[i
] = mRecording
.mIntervals
[cyclicPos
];
118 already_AddRefed
<PersistentBufferProvider
>
119 WindowRenderer::CreatePersistentBufferProvider(
120 const mozilla::gfx::IntSize
& aSize
, mozilla::gfx::SurfaceFormat aFormat
,
121 bool aWillReadFrequently
) {
122 RefPtr
<PersistentBufferProviderBasic
> bufferProvider
;
123 // If we are using remote canvas we don't want to use acceleration in
124 // non-remote layer managers, so we always use the fallback software one.
125 // If will-read-frequently is set, avoid using the preferred backend in
126 // favor of the fallback backend in case the preferred backend provides
128 if (!aWillReadFrequently
&&
129 (!gfxPlatform::UseRemoteCanvas() ||
130 !gfxPlatform::IsBackendAccelerated(
131 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()))) {
132 bufferProvider
= PersistentBufferProviderBasic::Create(
134 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
137 if (!bufferProvider
) {
138 bufferProvider
= PersistentBufferProviderBasic::Create(
139 aSize
, aFormat
, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
142 return bufferProvider
.forget();
145 void WindowRenderer::AddPartialPrerenderedAnimation(
146 uint64_t aCompositorAnimationId
, dom::Animation
* aAnimation
) {
147 mPartialPrerenderedAnimations
.InsertOrUpdate(aCompositorAnimationId
,
149 aAnimation
->SetPartialPrerendered(aCompositorAnimationId
);
151 void WindowRenderer::RemovePartialPrerenderedAnimation(
152 uint64_t aCompositorAnimationId
, dom::Animation
* aAnimation
) {
153 MOZ_ASSERT(aAnimation
);
155 RefPtr
<dom::Animation
> animation
;
156 if (mPartialPrerenderedAnimations
.Remove(aCompositorAnimationId
,
157 getter_AddRefs(animation
)) &&
158 // It may be possible that either animation's effect has already been
159 // nulled out via Animation::SetEffect() so ignore such cases.
160 aAnimation
->GetEffect() && aAnimation
->GetEffect()->AsKeyframeEffect() &&
161 animation
->GetEffect() && animation
->GetEffect()->AsKeyframeEffect()) {
163 EffectSet::GetForEffect(aAnimation
->GetEffect()->AsKeyframeEffect()) ==
164 EffectSet::GetForEffect(animation
->GetEffect()->AsKeyframeEffect()));
167 mPartialPrerenderedAnimations
.Remove(aCompositorAnimationId
);
169 aAnimation
->ResetPartialPrerendered();
171 void WindowRenderer::UpdatePartialPrerenderedAnimations(
172 const nsTArray
<uint64_t>& aJankedAnimations
) {
173 for (uint64_t id
: aJankedAnimations
) {
174 RefPtr
<dom::Animation
> animation
;
175 if (mPartialPrerenderedAnimations
.Remove(id
, getter_AddRefs(animation
))) {
176 animation
->UpdatePartialPrerendered();
181 void FallbackRenderer::SetTarget(gfxContext
* aTarget
,
182 layers::BufferMode aDoubleBuffering
) {
184 mBufferMode
= aDoubleBuffering
;
187 bool FallbackRenderer::BeginTransaction(const nsCString
& aURL
) {
195 void FallbackRenderer::EndTransactionWithColor(const nsIntRect
& aRect
,
196 const gfx::DeviceColor
& aColor
) {
197 mTarget
->GetDrawTarget()->FillRect(Rect(aRect
), ColorPattern(aColor
));
200 void FallbackRenderer::EndTransactionWithList(nsDisplayListBuilder
* aBuilder
,
201 nsDisplayList
* aList
,
202 int32_t aAppUnitsPerDevPixel
,
203 EndTransactionFlags aFlags
) {
204 if (aFlags
& EndTransactionFlags::END_NO_COMPOSITE
) {
208 DrawTarget
* dt
= mTarget
->GetDrawTarget();
210 BackendType backend
= gfxPlatform::GetPlatform()->GetContentBackendFor(
211 LayersBackend::LAYERS_NONE
);
212 RefPtr
<DrawTarget
> dest
=
213 gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
214 backend
, dt
->GetSize(), dt
->GetFormat());
216 gfxContext
ctx(dest
, /* aPreserveTransform */ true);
218 nsRegion opaque
= aList
->GetOpaqueRegion(aBuilder
);
219 if (opaque
.Contains(aList
->GetComponentAlphaBounds(aBuilder
))) {
220 dest
->SetPermitSubpixelAA(true);
223 aList
->Paint(aBuilder
, &ctx
, aAppUnitsPerDevPixel
);
225 RefPtr
<SourceSurface
> snapshot
= dest
->Snapshot();
226 dt
->DrawSurface(snapshot
, Rect(dest
->GetRect()), Rect(dest
->GetRect()),
227 DrawSurfaceOptions(),
228 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
232 } // namespace mozilla