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 "mozilla/layers/ProfilerScreenshots.h"
9 #include "mozilla/TimeStamp.h"
11 #include "GeckoProfiler.h"
13 #include "nsThreadUtils.h"
15 using namespace mozilla
;
16 using namespace mozilla::gfx
;
17 using namespace mozilla::layers
;
19 struct ScreenshotMarker
{
20 static constexpr mozilla::Span
<const char> MarkerTypeName() {
21 return mozilla::MakeStringSpan("CompositorScreenshot");
23 static void StreamJSONMarkerData(
24 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
25 const mozilla::ProfilerString8View
& aScreenshotDataURL
,
26 const mozilla::gfx::IntSize
& aWindowSize
, uint32_t aWindowIdentifier
) {
27 if (aScreenshotDataURL
.Length() != 0) {
28 aWriter
.UniqueStringProperty("url", aScreenshotDataURL
);
31 aWriter
.IntProperty("windowID", aWindowIdentifier
);
33 if (!aWindowSize
.IsEmpty()) {
34 aWriter
.DoubleProperty("windowWidth", aWindowSize
.width
);
35 aWriter
.DoubleProperty("windowHeight", aWindowSize
.height
);
38 static mozilla::MarkerSchema
MarkerTypeDisplay() {
39 return mozilla::MarkerSchema::SpecialFrontendLocation
{};
43 uint32_t ProfilerScreenshots::sWindowCounter
= 0;
45 ProfilerScreenshots::ProfilerScreenshots()
46 : mMutex("ProfilerScreenshots::mMutex"),
48 mWindowIdentifier(++sWindowCounter
) {}
50 ProfilerScreenshots::~ProfilerScreenshots() {
51 if (mWindowIdentifier
) {
52 profiler_add_marker("CompositorScreenshotWindowDestroyed",
53 geckoprofiler::category::GRAPHICS
,
54 MarkerThreadId::MainThread(), ScreenshotMarker
{},
55 /* aScreenshotDataURL */ "", mozilla::gfx::IntSize
{},
61 bool ProfilerScreenshots::IsEnabled() {
62 return profiler_feature_active(ProfilerFeature::Screenshots
);
65 void ProfilerScreenshots::SubmitScreenshot(
66 const gfx::IntSize
& aOriginalSize
, const IntSize
& aScaledSize
,
67 const TimeStamp
& aTimeStamp
,
68 const std::function
<bool(DataSourceSurface
*)>& aPopulateSurface
) {
69 RefPtr
<DataSourceSurface
> backingSurface
= TakeNextSurface();
70 if (!backingSurface
) {
74 MOZ_RELEASE_ASSERT(aScaledSize
<= backingSurface
->GetSize());
76 bool succeeded
= aPopulateSurface(backingSurface
);
79 PROFILER_MARKER_UNTYPED(
80 "NoCompositorScreenshot because aPopulateSurface callback failed",
82 ReturnSurface(backingSurface
);
86 NS_DispatchBackgroundTask(NS_NewRunnableFunction(
87 "ProfilerScreenshots::SubmitScreenshot",
88 [self
= RefPtr
<ProfilerScreenshots
>{this},
89 backingSurface
= std::move(backingSurface
),
90 windowIdentifier
= mWindowIdentifier
, originalSize
= aOriginalSize
,
91 scaledSize
= aScaledSize
, timeStamp
= aTimeStamp
]() {
92 // Create a new surface that wraps backingSurface's data but has the
94 DataSourceSurface::ScopedMap
scopedMap(backingSurface
,
95 DataSourceSurface::READ
);
96 RefPtr
<DataSourceSurface
> surf
=
97 Factory::CreateWrappingDataSourceSurface(
98 scopedMap
.GetData(), scopedMap
.GetStride(), scaledSize
,
99 SurfaceFormat::B8G8R8A8
);
101 // Encode surf to a JPEG data URL.
103 nsresult rv
= gfxUtils::EncodeSourceSurface(
104 surf
, ImageType::JPEG
, u
"quality=85"_ns
, gfxUtils::eDataURIEncode
,
106 if (NS_SUCCEEDED(rv
)) {
107 // Add a marker with the data URL.
109 "CompositorScreenshot", geckoprofiler::category::GRAPHICS
,
110 {MarkerThreadId::MainThread(),
111 MarkerTiming::InstantAt(timeStamp
)},
112 ScreenshotMarker
{}, dataURL
, originalSize
, windowIdentifier
);
115 // Return backingSurface back to the surface pool.
116 self
->ReturnSurface(backingSurface
);
120 already_AddRefed
<DataSourceSurface
> ProfilerScreenshots::TakeNextSurface() {
121 MutexAutoLock
mon(mMutex
);
122 if (!mAvailableSurfaces
.IsEmpty()) {
123 RefPtr
<DataSourceSurface
> surf
= mAvailableSurfaces
[0];
124 mAvailableSurfaces
.RemoveElementAt(0);
125 return surf
.forget();
127 if (mLiveSurfaceCount
>= 8) {
129 "already 8 surfaces in flight, skipping capture for this composite");
133 return Factory::CreateDataSourceSurface(ScreenshotSize(),
134 SurfaceFormat::B8G8R8A8
);
137 void ProfilerScreenshots::ReturnSurface(DataSourceSurface
* aSurface
) {
138 MutexAutoLock
mon(this->mMutex
);
139 mAvailableSurfaces
.AppendElement(aSurface
);