tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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