Bug 1835804 - Completely block from doing audio decoding on Content and RDD r=alwu
[gecko.git] / gfx / layers / ScreenshotGrabber.cpp
blob3719afa3364597e198ec51d68c41e8f5b9880295
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"
18 #include "nsTArray.h"
20 namespace mozilla {
22 using namespace gfx;
24 namespace layers {
25 namespace profiler_screenshots {
27 /**
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 {
33 public:
34 explicit ScreenshotGrabberImpl(const IntSize& aBufferSize);
35 ~ScreenshotGrabberImpl();
37 void GrabScreenshot(Window& aWindow, const IntSize& aWindowSize);
38 void ProcessQueue();
40 private:
41 struct QueueItem final {
42 mozilla::TimeStamp mTimeStamp;
43 RefPtr<AsyncReadbackBuffer> mScreenshotBuffer;
44 IntSize mScreenshotSize;
45 IntSize mWindowSize;
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()) {
72 if (!mImpl) {
73 mImpl = MakeUnique<profiler_screenshots::ScreenshotGrabberImpl>(
74 ProfilerScreenshots::ScreenshotSize());
76 mImpl->GrabScreenshot(aWindow, aWindowSize);
77 } else if (mImpl) {
78 Destroy();
82 void ScreenshotGrabber::MaybeProcessQueue() {
83 if (ProfilerScreenshots::IsEnabled()) {
84 if (!mImpl) {
85 mImpl = MakeUnique<profiler_screenshots::ScreenshotGrabberImpl>(
86 ProfilerScreenshots::ScreenshotSize());
88 mImpl->ProcessQueue();
89 } else if (mImpl) {
90 Destroy();
94 void ScreenshotGrabber::NotifyEmptyFrame() {
95 PROFILER_MARKER_UNTYPED("NoCompositorScreenshot because nothing changed",
96 GRAPHICS);
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);
135 if (renderSource) {
136 if (mCachedLevels[aLevel]->DownscaleFrom(
137 renderSource, IntRect({}, sourceSize), IntRect({}, aDestSize))) {
138 return mCachedLevels[aLevel]->AsRenderSource();
141 return nullptr;
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 "
152 "configuration",
153 GRAPHICS);
154 return;
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);
164 if (!scaledTarget) {
165 PROFILER_MARKER_UNTYPED(
166 "NoCompositorScreenshot because ScaleDownWindowRenderSourceToSize "
167 "failed",
168 GRAPHICS);
169 return;
172 RefPtr<AsyncReadbackBuffer> buffer = TakeNextBuffer(aWindow);
173 if (!buffer) {
174 PROFILER_MARKER_UNTYPED(
175 "NoCompositorScreenshot because AsyncReadbackBuffer creation failed",
176 GRAPHICS);
177 return;
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(
191 Window& aWindow) {
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);
219 mQueue.Clear();
221 if (mCurrentFrameQueueItem) {
222 mQueue.AppendElement(std::move(*mCurrentFrameQueueItem));
223 mCurrentFrameQueueItem = Nothing();
227 } // namespace profiler_screenshots
228 } // namespace layers
229 } // namespace mozilla