tor-browser

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

ScreenshotGrabber.cpp (7581B)


      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 "ScreenshotGrabber.h"
      8 
      9 #include "mozilla/ProfilerMarkers.h"
     10 #include "mozilla/RefPtr.h"
     11 #include "mozilla/TimeStamp.h"
     12 #include "mozilla/UniquePtr.h"
     13 
     14 #include "mozilla/layers/Compositor.h"
     15 #include "mozilla/layers/ProfilerScreenshots.h"
     16 #include "mozilla/layers/TextureHost.h"
     17 #include "mozilla/gfx/Point.h"
     18 #include "nsTArray.h"
     19 
     20 namespace mozilla {
     21 
     22 using namespace gfx;
     23 
     24 namespace layers {
     25 namespace profiler_screenshots {
     26 
     27 /**
     28 * The actual implementation of screenshot grabbing.
     29 * The ScreenshotGrabberImpl object is destroyed if the profiler is
     30 * disabled and MaybeGrabScreenshot notices it.
     31 */
     32 class ScreenshotGrabberImpl final {
     33 public:
     34  explicit ScreenshotGrabberImpl(const IntSize& aBufferSize);
     35  ~ScreenshotGrabberImpl();
     36 
     37  void GrabScreenshot(Window& aWindow, const IntSize& aWindowSize);
     38  void ProcessQueue();
     39 
     40 private:
     41  struct QueueItem final {
     42    mozilla::TimeStamp mTimeStamp;
     43    RefPtr<AsyncReadbackBuffer> mScreenshotBuffer;
     44    IntSize mScreenshotSize;
     45    IntSize mWindowSize;
     46  };
     47 
     48  RefPtr<RenderSource> ScaleDownWindowRenderSourceToSize(
     49      Window& aWindow, const IntSize& aDestSize,
     50      RenderSource* aWindowRenderSource, size_t aLevel);
     51 
     52  already_AddRefed<AsyncReadbackBuffer> TakeNextBuffer(Window& aWindow);
     53  void ReturnBuffer(AsyncReadbackBuffer* aBuffer);
     54 
     55  nsTArray<RefPtr<DownscaleTarget>> mCachedLevels;
     56  nsTArray<RefPtr<AsyncReadbackBuffer>> mAvailableBuffers;
     57  Maybe<QueueItem> mCurrentFrameQueueItem;
     58  nsTArray<QueueItem> mQueue;
     59  RefPtr<ProfilerScreenshots> mProfilerScreenshots;
     60  const IntSize mBufferSize;
     61 };
     62 
     63 }  // namespace profiler_screenshots
     64 
     65 ScreenshotGrabber::ScreenshotGrabber() = default;
     66 
     67 ScreenshotGrabber::~ScreenshotGrabber() = default;
     68 
     69 void ScreenshotGrabber::MaybeGrabScreenshot(
     70    profiler_screenshots::Window& aWindow, const IntSize& aWindowSize) {
     71  if (ProfilerScreenshots::IsEnabled()) {
     72    if (!mImpl) {
     73      mImpl = MakeUnique<profiler_screenshots::ScreenshotGrabberImpl>(
     74          ProfilerScreenshots::ScreenshotSize());
     75    }
     76    mImpl->GrabScreenshot(aWindow, aWindowSize);
     77  } else if (mImpl) {
     78    Destroy();
     79  }
     80 }
     81 
     82 void ScreenshotGrabber::MaybeProcessQueue() {
     83  if (ProfilerScreenshots::IsEnabled()) {
     84    if (!mImpl) {
     85      mImpl = MakeUnique<profiler_screenshots::ScreenshotGrabberImpl>(
     86          ProfilerScreenshots::ScreenshotSize());
     87    }
     88    mImpl->ProcessQueue();
     89  } else if (mImpl) {
     90    Destroy();
     91  }
     92 }
     93 
     94 void ScreenshotGrabber::NotifyEmptyFrame() {
     95  PROFILER_MARKER_UNTYPED("NoCompositorScreenshot because nothing changed",
     96                          GRAPHICS);
     97 }
     98 
     99 void ScreenshotGrabber::Destroy() { mImpl = nullptr; }
    100 
    101 namespace profiler_screenshots {
    102 
    103 ScreenshotGrabberImpl::ScreenshotGrabberImpl(const IntSize& aBufferSize)
    104    : mBufferSize(aBufferSize) {}
    105 
    106 ScreenshotGrabberImpl::~ScreenshotGrabberImpl() {
    107  // Any queue items in mQueue or mCurrentFrameQueueItem will be lost.
    108  // That's ok: Either the profiler has stopped and we don't care about these
    109  // screenshots, or the window is closing and we don't really need the last
    110  // few frames from the window.
    111 }
    112 
    113 // Scale down aWindowRenderSource into a RenderSource of size
    114 // mBufferSize * (1 << aLevel) and return that RenderSource.
    115 // Don't scale down by more than a factor of 2 with a single scaling operation,
    116 // because it'll look bad. If higher scales are needed, use another
    117 // intermediate target by calling this function recursively with aLevel + 1.
    118 RefPtr<RenderSource> ScreenshotGrabberImpl::ScaleDownWindowRenderSourceToSize(
    119    Window& aWindow, const IntSize& aDestSize,
    120    RenderSource* aWindowRenderSource, size_t aLevel) {
    121  if (aLevel == mCachedLevels.Length()) {
    122    mCachedLevels.AppendElement(
    123        aWindow.CreateDownscaleTarget(mBufferSize * (1 << aLevel)));
    124  }
    125  MOZ_RELEASE_ASSERT(aLevel < mCachedLevels.Length());
    126 
    127  RefPtr<RenderSource> renderSource = aWindowRenderSource;
    128  IntSize sourceSize = aWindowRenderSource->Size();
    129  if (sourceSize.width > aDestSize.width * 2) {
    130    sourceSize = aDestSize * 2;
    131    renderSource = ScaleDownWindowRenderSourceToSize(
    132        aWindow, sourceSize, aWindowRenderSource, aLevel + 1);
    133  }
    134 
    135  if (renderSource) {
    136    if (mCachedLevels[aLevel]->DownscaleFrom(
    137            renderSource, IntRect({}, sourceSize), IntRect({}, aDestSize))) {
    138      return mCachedLevels[aLevel]->AsRenderSource();
    139    }
    140  }
    141  return nullptr;
    142 }
    143 
    144 void ScreenshotGrabberImpl::GrabScreenshot(Window& aWindow,
    145                                           const IntSize& aWindowSize) {
    146  RefPtr<RenderSource> windowRenderSource =
    147      aWindow.GetWindowContents(aWindowSize);
    148 
    149  if (!windowRenderSource) {
    150    PROFILER_MARKER_UNTYPED(
    151        "NoCompositorScreenshot because of unsupported compositor "
    152        "configuration",
    153        GRAPHICS);
    154    return;
    155  }
    156 
    157  Size windowSize(aWindowSize);
    158  float scale = std::min(mBufferSize.width / windowSize.width,
    159                         mBufferSize.height / windowSize.height);
    160  IntSize scaledSize = IntSize::Round(windowSize * scale);
    161  RefPtr<RenderSource> scaledTarget = ScaleDownWindowRenderSourceToSize(
    162      aWindow, scaledSize, windowRenderSource, 0);
    163 
    164  if (!scaledTarget) {
    165    PROFILER_MARKER_UNTYPED(
    166        "NoCompositorScreenshot because ScaleDownWindowRenderSourceToSize "
    167        "failed",
    168        GRAPHICS);
    169    return;
    170  }
    171 
    172  RefPtr<AsyncReadbackBuffer> buffer = TakeNextBuffer(aWindow);
    173  if (!buffer) {
    174    PROFILER_MARKER_UNTYPED(
    175        "NoCompositorScreenshot because AsyncReadbackBuffer creation failed",
    176        GRAPHICS);
    177    return;
    178  }
    179 
    180  buffer->CopyFrom(scaledTarget);
    181 
    182  // This QueueItem will be added to the queue at the end of the next call to
    183  // ProcessQueue(). This ensures that the buffer isn't mapped into main memory
    184  // until the next frame. If we did it in this frame, we'd block on the GPU.
    185  mCurrentFrameQueueItem =
    186      Some(QueueItem{TimeStamp::Now(), std::move(buffer), scaledSize,
    187                     windowRenderSource->Size()});
    188 }
    189 
    190 already_AddRefed<AsyncReadbackBuffer> ScreenshotGrabberImpl::TakeNextBuffer(
    191    Window& aWindow) {
    192  if (!mAvailableBuffers.IsEmpty()) {
    193    RefPtr<AsyncReadbackBuffer> buffer = mAvailableBuffers[0];
    194    mAvailableBuffers.RemoveElementAt(0);
    195    return buffer.forget();
    196  }
    197  return aWindow.CreateAsyncReadbackBuffer(mBufferSize);
    198 }
    199 
    200 void ScreenshotGrabberImpl::ReturnBuffer(AsyncReadbackBuffer* aBuffer) {
    201  mAvailableBuffers.AppendElement(aBuffer);
    202 }
    203 
    204 void ScreenshotGrabberImpl::ProcessQueue() {
    205  if (!mQueue.IsEmpty()) {
    206    if (!mProfilerScreenshots) {
    207      mProfilerScreenshots = new ProfilerScreenshots();
    208    }
    209    for (const auto& item : mQueue) {
    210      mProfilerScreenshots->SubmitScreenshot(
    211          item.mWindowSize, item.mScreenshotSize, item.mTimeStamp,
    212          [&item](DataSourceSurface* aTargetSurface) {
    213            return item.mScreenshotBuffer->MapAndCopyInto(aTargetSurface,
    214                                                          item.mScreenshotSize);
    215          });
    216      ReturnBuffer(item.mScreenshotBuffer);
    217    }
    218  }
    219  mQueue.Clear();
    220 
    221  if (mCurrentFrameQueueItem) {
    222    mQueue.AppendElement(std::move(*mCurrentFrameQueueItem));
    223    mCurrentFrameQueueItem = Nothing();
    224  }
    225 }
    226 
    227 }  // namespace profiler_screenshots
    228 }  // namespace layers
    229 }  // namespace mozilla