WindowRenderer.cpp (9985B)
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/EffectSet.h" 11 #include "mozilla/dom/Animation.h" // for Animation 12 #include "mozilla/dom/AnimationEffect.h" 13 #include "mozilla/gfx/GPUProcessManager.h" 14 #include "mozilla/layers/PersistentBufferProvider.h" // for PersistentBufferProviderBasic, PersistentBufferProvider (ptr only) 15 #include "nsDisplayList.h" 16 17 using namespace mozilla::gfx; 18 using namespace mozilla::layers; 19 20 namespace mozilla { 21 22 /** 23 * StartFrameTimeRecording, together with StopFrameTimeRecording 24 * enable recording of frame intervals. 25 * 26 * To allow concurrent consumers, a cyclic array is used which serves all 27 * consumers, practically stateless with regard to consumers. 28 * 29 * To save resources, the buffer is allocated on first call to 30 * StartFrameTimeRecording and recording is paused if no consumer which called 31 * StartFrameTimeRecording is able to get valid results (because the cyclic 32 * buffer was overwritten since that call). 33 * 34 * To determine availability of the data upon StopFrameTimeRecording: 35 * - mRecording.mNextIndex increases on each RecordFrame, and never resets. 36 * - Cyclic buffer position is realized as mNextIndex % bufferSize. 37 * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is 38 * called, the required start index is passed as an arg, and we're able to 39 * calculate the required length. If this length is bigger than bufferSize, it 40 * means data was overwritten. otherwise, we can return the entire sequence. 41 * - To determine if we need to pause, mLatestStartIndex is updated to 42 * mNextIndex on each call to StartFrameTimeRecording. If this index gets 43 * overwritten, it means that all earlier start indices obtained via 44 * StartFrameTimeRecording were also overwritten, hence, no point in 45 * recording, so pause. 46 * - mCurrentRunStartIndex indicates the oldest index of the recording after 47 * which the recording was not paused. If StopFrameTimeRecording is invoked 48 * with a start index older than this, it means that some frames were not 49 * recorded, so data is invalid. 50 */ 51 uint32_t FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize) { 52 if (mRecording.mIsPaused) { 53 mRecording.mIsPaused = false; 54 55 if (!mRecording.mIntervals.Length()) { // Initialize recording buffers 56 mRecording.mIntervals.SetLength(aBufferSize); 57 } 58 59 // After being paused, recent values got invalid. Update them to now. 60 mRecording.mLastFrameTime = TimeStamp::Now(); 61 62 // Any recording which started before this is invalid, since we were paused. 63 mRecording.mCurrentRunStartIndex = mRecording.mNextIndex; 64 } 65 66 // If we'll overwrite this index, there are no more consumers with aStartIndex 67 // for which we're able to provide the full recording, so no point in keep 68 // recording. 69 mRecording.mLatestStartIndex = mRecording.mNextIndex; 70 return mRecording.mNextIndex; 71 } 72 73 void FrameRecorder::RecordFrame() { 74 if (!mRecording.mIsPaused) { 75 TimeStamp now = TimeStamp::Now(); 76 uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length(); 77 mRecording.mIntervals[i] = 78 static_cast<float>((now - mRecording.mLastFrameTime).ToMilliseconds()); 79 mRecording.mNextIndex++; 80 mRecording.mLastFrameTime = now; 81 82 if (mRecording.mNextIndex > 83 (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) { 84 // We've just overwritten the most recent recording start -> pause. 85 mRecording.mIsPaused = true; 86 } 87 } 88 } 89 90 void FrameRecorder::StopFrameTimeRecording(uint32_t aStartIndex, 91 nsTArray<float>& aFrameIntervals) { 92 uint32_t bufferSize = mRecording.mIntervals.Length(); 93 uint32_t length = mRecording.mNextIndex - aStartIndex; 94 if (mRecording.mIsPaused || length > bufferSize || 95 aStartIndex < mRecording.mCurrentRunStartIndex) { 96 // aStartIndex is too old. Also if aStartIndex was issued before 97 // mRecordingNextIndex overflowed (uint32_t) 98 // and stopped after the overflow (would happen once every 828 days of 99 // constant 60fps). 100 length = 0; 101 } 102 103 if (!length) { 104 aFrameIntervals.Clear(); 105 return; // empty recording, return empty arrays. 106 } 107 // Set length in advance to avoid possibly repeated reallocations 108 aFrameIntervals.SetLength(length); 109 110 uint32_t cyclicPos = aStartIndex % bufferSize; 111 for (uint32_t i = 0; i < length; i++, cyclicPos++) { 112 if (cyclicPos == bufferSize) { 113 cyclicPos = 0; 114 } 115 aFrameIntervals[i] = mRecording.mIntervals[cyclicPos]; 116 } 117 } 118 119 already_AddRefed<PersistentBufferProvider> 120 WindowRenderer::CreatePersistentBufferProvider( 121 const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat, 122 bool aWillReadFrequently) { 123 RefPtr<PersistentBufferProviderBasic> bufferProvider; 124 // If we are using remote canvas we don't want to use acceleration in 125 // non-remote layer managers, so we always use the fallback software one. 126 // If will-read-frequently is set, avoid using the preferred backend in 127 // favor of the fallback backend in case the preferred backend provides 128 // acceleration. 129 if (!aWillReadFrequently && 130 (!gfxPlatform::UseRemoteCanvas() || 131 !gfxPlatform::IsBackendAccelerated( 132 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()))) { 133 bufferProvider = PersistentBufferProviderBasic::Create( 134 aSize, aFormat, 135 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()); 136 } 137 138 if (!bufferProvider) { 139 bufferProvider = PersistentBufferProviderBasic::Create( 140 aSize, aFormat, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend()); 141 } 142 143 return bufferProvider.forget(); 144 } 145 146 void WindowRenderer::AddPartialPrerenderedAnimation( 147 uint64_t aCompositorAnimationId, dom::Animation* aAnimation) { 148 mPartialPrerenderedAnimations.InsertOrUpdate(aCompositorAnimationId, 149 RefPtr{aAnimation}); 150 aAnimation->SetPartialPrerendered(aCompositorAnimationId); 151 } 152 void WindowRenderer::RemovePartialPrerenderedAnimation( 153 uint64_t aCompositorAnimationId, dom::Animation* aAnimation) { 154 MOZ_ASSERT(aAnimation); 155 #ifdef DEBUG 156 RefPtr<dom::Animation> animation; 157 if (mPartialPrerenderedAnimations.Remove(aCompositorAnimationId, 158 getter_AddRefs(animation)) && 159 // It may be possible that either animation's effect has already been 160 // nulled out via Animation::SetEffect() so ignore such cases. 161 aAnimation->GetEffect() && aAnimation->GetEffect()->AsKeyframeEffect() && 162 animation->GetEffect() && animation->GetEffect()->AsKeyframeEffect()) { 163 MOZ_ASSERT( 164 EffectSet::GetForEffect(aAnimation->GetEffect()->AsKeyframeEffect()) == 165 EffectSet::GetForEffect(animation->GetEffect()->AsKeyframeEffect())); 166 } 167 #else 168 mPartialPrerenderedAnimations.Remove(aCompositorAnimationId); 169 #endif 170 aAnimation->ResetPartialPrerendered(); 171 } 172 void WindowRenderer::UpdatePartialPrerenderedAnimations( 173 const nsTArray<uint64_t>& aJankedAnimations) { 174 for (uint64_t id : aJankedAnimations) { 175 RefPtr<dom::Animation> animation; 176 if (mPartialPrerenderedAnimations.Remove(id, getter_AddRefs(animation))) { 177 animation->UpdatePartialPrerendered(); 178 } 179 } 180 } 181 182 void FallbackRenderer::SetTarget(gfxContext* aTarget) { mTarget = aTarget; } 183 184 bool FallbackRenderer::BeginTransaction(const nsCString& aURL) { 185 if (!mTarget) { 186 return false; 187 } 188 189 return true; 190 } 191 192 void FallbackRenderer::EndTransactionWithColor(const nsIntRect& aRect, 193 const gfx::DeviceColor& aColor) { 194 mTarget->GetDrawTarget()->FillRect(Rect(aRect), ColorPattern(aColor)); 195 } 196 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 if (dest) { 213 gfxContext ctx(dest, /* aPreserveTransform */ true); 214 215 nsRegion opaque = aList->GetOpaqueRegion(aBuilder); 216 if (opaque.Contains(aList->GetComponentAlphaBounds(aBuilder))) { 217 dest->SetPermitSubpixelAA(true); 218 } 219 220 aList->Paint(aBuilder, &ctx, aAppUnitsPerDevPixel); 221 222 RefPtr<SourceSurface> snapshot = dest->Snapshot(); 223 dt->DrawSurface(snapshot, Rect(dest->GetRect()), Rect(dest->GetRect()), 224 DrawSurfaceOptions(), 225 DrawOptions(1.0f, CompositionOp::OP_SOURCE)); 226 } 227 } 228 229 BackgroundedFallbackRenderer::BackgroundedFallbackRenderer(nsIWidget* aWidget) 230 : mWidget(aWidget) { 231 MOZ_ASSERT(mWidget); 232 if (auto* gpm = gfx::GPUProcessManager::Get()) { 233 gpm->AddListener(this); 234 } 235 } 236 237 BackgroundedFallbackRenderer::~BackgroundedFallbackRenderer() { Destroy(); } 238 239 void BackgroundedFallbackRenderer::Destroy() { 240 if (!mWidget) { 241 return; 242 } 243 244 if (auto* gpm = gfx::GPUProcessManager::Get()) { 245 gpm->RemoveListener(this); 246 } 247 248 mWidget = nullptr; 249 } 250 251 void BackgroundedFallbackRenderer::OnCompositorDestroyBackgrounded() { 252 // We may get freed after this but the caller has a strong reference. 253 if (RefPtr<nsIWidget> widget = mWidget) { 254 widget->NotifyCompositorSessionLost(/* aSession */ nullptr); 255 } 256 } 257 258 } // namespace mozilla