Coverage Report

Created: 2018-09-25 14:53

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