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 "ScreenshotGrabber.h"
9 #include "mozilla/ProfilerMarkers.h"
10 #include "mozilla/RefPtr.h"
11 #include "mozilla/TimeStamp.h"
12 #include "mozilla/UniquePtr.h"
14 #include "mozilla/layers/Compositor.h"
15 #include "mozilla/layers/ProfilerScreenshots.h"
16 #include "mozilla/layers/TextureHost.h"
17 #include "mozilla/gfx/Point.h"
25 namespace profiler_screenshots
{
28 * The actual implementation of screenshot grabbing.
29 * The ScreenshotGrabberImpl object is destroyed if the profiler is
30 * disabled and MaybeGrabScreenshot notices it.
32 class ScreenshotGrabberImpl final
{
34 explicit ScreenshotGrabberImpl(const IntSize
& aBufferSize
);
35 ~ScreenshotGrabberImpl();
37 void GrabScreenshot(Window
& aWindow
, const IntSize
& aWindowSize
);
41 struct QueueItem final
{
42 mozilla::TimeStamp mTimeStamp
;
43 RefPtr
<AsyncReadbackBuffer
> mScreenshotBuffer
;
44 IntSize mScreenshotSize
;
48 RefPtr
<RenderSource
> ScaleDownWindowRenderSourceToSize(
49 Window
& aWindow
, const IntSize
& aDestSize
,
50 RenderSource
* aWindowRenderSource
, size_t aLevel
);
52 already_AddRefed
<AsyncReadbackBuffer
> TakeNextBuffer(Window
& aWindow
);
53 void ReturnBuffer(AsyncReadbackBuffer
* aBuffer
);
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
;
63 } // namespace profiler_screenshots
65 ScreenshotGrabber::ScreenshotGrabber() = default;
67 ScreenshotGrabber::~ScreenshotGrabber() = default;
69 void ScreenshotGrabber::MaybeGrabScreenshot(
70 profiler_screenshots::Window
& aWindow
, const IntSize
& aWindowSize
) {
71 if (ProfilerScreenshots::IsEnabled()) {
73 mImpl
= MakeUnique
<profiler_screenshots::ScreenshotGrabberImpl
>(
74 ProfilerScreenshots::ScreenshotSize());
76 mImpl
->GrabScreenshot(aWindow
, aWindowSize
);
82 void ScreenshotGrabber::MaybeProcessQueue() {
83 if (ProfilerScreenshots::IsEnabled()) {
85 mImpl
= MakeUnique
<profiler_screenshots::ScreenshotGrabberImpl
>(
86 ProfilerScreenshots::ScreenshotSize());
88 mImpl
->ProcessQueue();
94 void ScreenshotGrabber::NotifyEmptyFrame() {
95 PROFILER_MARKER_UNTYPED("NoCompositorScreenshot because nothing changed",
99 void ScreenshotGrabber::Destroy() { mImpl
= nullptr; }
101 namespace profiler_screenshots
{
103 ScreenshotGrabberImpl::ScreenshotGrabberImpl(const IntSize
& aBufferSize
)
104 : mBufferSize(aBufferSize
) {}
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.
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
)));
125 MOZ_RELEASE_ASSERT(aLevel
< mCachedLevels
.Length());
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);
136 if (mCachedLevels
[aLevel
]->DownscaleFrom(
137 renderSource
, IntRect({}, sourceSize
), IntRect({}, aDestSize
))) {
138 return mCachedLevels
[aLevel
]->AsRenderSource();
144 void ScreenshotGrabberImpl::GrabScreenshot(Window
& aWindow
,
145 const IntSize
& aWindowSize
) {
146 RefPtr
<RenderSource
> windowRenderSource
=
147 aWindow
.GetWindowContents(aWindowSize
);
149 if (!windowRenderSource
) {
150 PROFILER_MARKER_UNTYPED(
151 "NoCompositorScreenshot because of unsupported compositor "
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);
165 PROFILER_MARKER_UNTYPED(
166 "NoCompositorScreenshot because ScaleDownWindowRenderSourceToSize "
172 RefPtr
<AsyncReadbackBuffer
> buffer
= TakeNextBuffer(aWindow
);
174 PROFILER_MARKER_UNTYPED(
175 "NoCompositorScreenshot because AsyncReadbackBuffer creation failed",
180 buffer
->CopyFrom(scaledTarget
);
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()});
190 already_AddRefed
<AsyncReadbackBuffer
> ScreenshotGrabberImpl::TakeNextBuffer(
192 if (!mAvailableBuffers
.IsEmpty()) {
193 RefPtr
<AsyncReadbackBuffer
> buffer
= mAvailableBuffers
[0];
194 mAvailableBuffers
.RemoveElementAt(0);
195 return buffer
.forget();
197 return aWindow
.CreateAsyncReadbackBuffer(mBufferSize
);
200 void ScreenshotGrabberImpl::ReturnBuffer(AsyncReadbackBuffer
* aBuffer
) {
201 mAvailableBuffers
.AppendElement(aBuffer
);
204 void ScreenshotGrabberImpl::ProcessQueue() {
205 if (!mQueue
.IsEmpty()) {
206 if (!mProfilerScreenshots
) {
207 mProfilerScreenshots
= new ProfilerScreenshots();
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
);
216 ReturnBuffer(item
.mScreenshotBuffer
);
221 if (mCurrentFrameQueueItem
) {
222 mQueue
.AppendElement(std::move(*mCurrentFrameQueueItem
));
223 mCurrentFrameQueueItem
= Nothing();
227 } // namespace profiler_screenshots
228 } // namespace layers
229 } // namespace mozilla