ProfilerScreenshots.cpp (5033B)
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 "mozilla/layers/ProfilerScreenshots.h" 8 9 #include "mozilla/TimeStamp.h" 10 11 #include "GeckoProfiler.h" 12 #include "gfxUtils.h" 13 #include "nsThreadUtils.h" 14 15 using namespace mozilla; 16 using namespace mozilla::gfx; 17 using namespace mozilla::layers; 18 19 struct ScreenshotMarker { 20 static constexpr mozilla::Span<const char> MarkerTypeName() { 21 return mozilla::MakeStringSpan("CompositorScreenshot"); 22 } 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); 29 } 30 31 aWriter.IntProperty("windowID", aWindowIdentifier); 32 33 if (!aWindowSize.IsEmpty()) { 34 aWriter.DoubleProperty("windowWidth", aWindowSize.width); 35 aWriter.DoubleProperty("windowHeight", aWindowSize.height); 36 } 37 } 38 static mozilla::MarkerSchema MarkerTypeDisplay() { 39 return mozilla::MarkerSchema::SpecialFrontendLocation{}; 40 } 41 }; 42 43 uint32_t ProfilerScreenshots::sWindowCounter = 0; 44 45 ProfilerScreenshots::ProfilerScreenshots() 46 : mMutex("ProfilerScreenshots::mMutex"), 47 mLiveSurfaceCount(0), 48 mWindowIdentifier(++sWindowCounter) {} 49 50 ProfilerScreenshots::~ProfilerScreenshots() { 51 if (mWindowIdentifier) { 52 profiler_add_marker("CompositorScreenshotWindowDestroyed", 53 geckoprofiler::category::GRAPHICS, 54 MarkerThreadId::MainThread(), ScreenshotMarker{}, 55 /* aScreenshotDataURL */ "", mozilla::gfx::IntSize{}, 56 mWindowIdentifier); 57 } 58 } 59 60 /* static */ 61 bool ProfilerScreenshots::IsEnabled() { 62 return profiler_feature_active(ProfilerFeature::Screenshots); 63 } 64 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) { 71 return; 72 } 73 74 MOZ_RELEASE_ASSERT(aScaledSize <= backingSurface->GetSize()); 75 76 bool succeeded = aPopulateSurface(backingSurface); 77 78 if (!succeeded) { 79 PROFILER_MARKER_UNTYPED( 80 "NoCompositorScreenshot because aPopulateSurface callback failed", 81 GRAPHICS); 82 ReturnSurface(backingSurface); 83 return; 84 } 85 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 93 // correct size. 94 DataSourceSurface::ScopedMap scopedMap(backingSurface, 95 DataSourceSurface::READ); 96 RefPtr<DataSourceSurface> surf = 97 Factory::CreateWrappingDataSourceSurface( 98 scopedMap.GetData(), scopedMap.GetStride(), scaledSize, 99 SurfaceFormat::B8G8R8A8); 100 101 // Encode surf to a JPEG data URL. 102 nsCString dataURL; 103 nsresult rv = gfxUtils::EncodeSourceSurface( 104 surf, ImageType::JPEG, u"quality=85"_ns, gfxUtils::eDataURIEncode, 105 nullptr, &dataURL); 106 if (NS_SUCCEEDED(rv)) { 107 // Add a marker with the data URL. 108 profiler_add_marker( 109 "CompositorScreenshot", geckoprofiler::category::GRAPHICS, 110 {MarkerThreadId::MainThread(), 111 MarkerTiming::InstantAt(timeStamp)}, 112 ScreenshotMarker{}, dataURL, originalSize, windowIdentifier); 113 } 114 115 // Return backingSurface back to the surface pool. 116 self->ReturnSurface(backingSurface); 117 })); 118 } 119 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(); 126 } 127 if (mLiveSurfaceCount >= 8) { 128 NS_WARNING( 129 "already 8 surfaces in flight, skipping capture for this composite"); 130 return nullptr; 131 } 132 mLiveSurfaceCount++; 133 return Factory::CreateDataSourceSurface(ScreenshotSize(), 134 SurfaceFormat::B8G8R8A8); 135 } 136 137 void ProfilerScreenshots::ReturnSurface(DataSourceSurface* aSurface) { 138 MutexAutoLock mon(this->mMutex); 139 mAvailableSurfaces.AppendElement(aSurface); 140 }