no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / painting / WindowRenderer.cpp
blobc76ba79c4fd7d3c14c3ee8d681d309bb66fe4ae5
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;
19 namespace mozilla {
21 /**
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
67 // recording.
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
98 // constant 60fps).
99 length = 0;
102 if (!length) {
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) {
112 cyclicPos = 0;
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
127 // acceleration.
128 if (!aWillReadFrequently &&
129 (!gfxPlatform::UseRemoteCanvas() ||
130 !gfxPlatform::IsBackendAccelerated(
131 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()))) {
132 bufferProvider = PersistentBufferProviderBasic::Create(
133 aSize, aFormat,
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,
148 RefPtr{aAnimation});
149 aAnimation->SetPartialPrerendered(aCompositorAnimationId);
151 void WindowRenderer::RemovePartialPrerenderedAnimation(
152 uint64_t aCompositorAnimationId, dom::Animation* aAnimation) {
153 MOZ_ASSERT(aAnimation);
154 #ifdef DEBUG
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()) {
162 MOZ_ASSERT(
163 EffectSet::GetForEffect(aAnimation->GetEffect()->AsKeyframeEffect()) ==
164 EffectSet::GetForEffect(animation->GetEffect()->AsKeyframeEffect()));
166 #else
167 mPartialPrerenderedAnimations.Remove(aCompositorAnimationId);
168 #endif
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) {
183 mTarget = aTarget;
184 mBufferMode = aDoubleBuffering;
187 bool FallbackRenderer::BeginTransaction(const nsCString& aURL) {
188 if (!mTarget) {
189 return false;
192 return true;
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) {
205 return;
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());
215 if (dest) {
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