1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/ProfilerScreenshots.h"
#include "mozilla/TimeStamp.h"
#include "GeckoProfiler.h"
#include "gfxUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
struct ScreenshotMarker {
static constexpr mozilla::Span<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("CompositorScreenshot");
}
static void StreamJSONMarkerData(
mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
const mozilla::ProfilerString8View& aScreenshotDataURL,
const mozilla::gfx::IntSize& aWindowSize, uint32_t aWindowIdentifier) {
if (aScreenshotDataURL.Length() != 0) {
aWriter.UniqueStringProperty("url", aScreenshotDataURL);
}
aWriter.IntProperty("windowID", aWindowIdentifier);
if (!aWindowSize.IsEmpty()) {
aWriter.DoubleProperty("windowWidth", aWindowSize.width);
aWriter.DoubleProperty("windowHeight", aWindowSize.height);
}
}
static mozilla::MarkerSchema MarkerTypeDisplay() {
return mozilla::MarkerSchema::SpecialFrontendLocation{};
}
};
uint32_t ProfilerScreenshots::sWindowCounter = 0;
ProfilerScreenshots::ProfilerScreenshots()
: mMutex("ProfilerScreenshots::mMutex"),
mLiveSurfaceCount(0),
mWindowIdentifier(++sWindowCounter) {}
ProfilerScreenshots::~ProfilerScreenshots() {
if (mWindowIdentifier) {
profiler_add_marker("CompositorScreenshotWindowDestroyed",
geckoprofiler::category::GRAPHICS,
MarkerThreadId::MainThread(), ScreenshotMarker{},
/* aScreenshotDataURL */ "", mozilla::gfx::IntSize{},
mWindowIdentifier);
}
}
/* static */
bool ProfilerScreenshots::IsEnabled() {
return profiler_feature_active(ProfilerFeature::Screenshots);
}
void ProfilerScreenshots::SubmitScreenshot(
const gfx::IntSize& aOriginalSize, const IntSize& aScaledSize,
const TimeStamp& aTimeStamp,
const std::function<bool(DataSourceSurface*)>& aPopulateSurface) {
RefPtr<DataSourceSurface> backingSurface = TakeNextSurface();
if (!backingSurface) {
return;
}
MOZ_RELEASE_ASSERT(aScaledSize <= backingSurface->GetSize());
bool succeeded = aPopulateSurface(backingSurface);
if (!succeeded) {
PROFILER_MARKER_UNTYPED(
"NoCompositorScreenshot because aPopulateSurface callback failed",
GRAPHICS);
ReturnSurface(backingSurface);
return;
}
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"ProfilerScreenshots::SubmitScreenshot",
[self = RefPtr<ProfilerScreenshots>{this},
backingSurface = std::move(backingSurface),
windowIdentifier = mWindowIdentifier, originalSize = aOriginalSize,
scaledSize = aScaledSize, timeStamp = aTimeStamp]() {
// Create a new surface that wraps backingSurface's data but has the
// correct size.
DataSourceSurface::ScopedMap scopedMap(backingSurface,
DataSourceSurface::READ);
RefPtr<DataSourceSurface> surf =
Factory::CreateWrappingDataSourceSurface(
scopedMap.GetData(), scopedMap.GetStride(), scaledSize,
SurfaceFormat::B8G8R8A8);
// Encode surf to a JPEG data URL.
nsCString dataURL;
nsresult rv = gfxUtils::EncodeSourceSurface(
surf, ImageType::JPEG, u"quality=85"_ns, gfxUtils::eDataURIEncode,
nullptr, &dataURL);
if (NS_SUCCEEDED(rv)) {
// Add a marker with the data URL.
profiler_add_marker(
"CompositorScreenshot", geckoprofiler::category::GRAPHICS,
{MarkerThreadId::MainThread(),
MarkerTiming::InstantAt(timeStamp)},
ScreenshotMarker{}, dataURL, originalSize, windowIdentifier);
}
// Return backingSurface back to the surface pool.
self->ReturnSurface(backingSurface);
}));
}
already_AddRefed<DataSourceSurface> ProfilerScreenshots::TakeNextSurface() {
MutexAutoLock mon(mMutex);
if (!mAvailableSurfaces.IsEmpty()) {
RefPtr<DataSourceSurface> surf = mAvailableSurfaces[0];
mAvailableSurfaces.RemoveElementAt(0);
return surf.forget();
}
if (mLiveSurfaceCount >= 8) {
NS_WARNING(
"already 8 surfaces in flight, skipping capture for this composite");
return nullptr;
}
mLiveSurfaceCount++;
return Factory::CreateDataSourceSurface(ScreenshotSize(),
SurfaceFormat::B8G8R8A8);
}
void ProfilerScreenshots::ReturnSurface(DataSourceSurface* aSurface) {
MutexAutoLock mon(this->mMutex);
mAvailableSurfaces.AppendElement(aSurface);
}
|