PerformanceRecorder.h (13368B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et ft=cpp : */ 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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_PerformanceRecorder_h 8 #define mozilla_PerformanceRecorder_h 9 10 #include <utility> 11 12 #include "mozilla/BaseProfilerMarkersPrerequisites.h" 13 #include "mozilla/DefineEnum.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/Mutex.h" 16 #include "mozilla/ProfilerMarkerTypes.h" 17 #include "mozilla/ProfilerMarkers.h" 18 #include "mozilla/TimeStamp.h" 19 #include "mozilla/TypedEnumBits.h" 20 #include "nsStringFwd.h" 21 #include "nsTPriorityQueue.h" 22 23 namespace mozilla { 24 namespace gfx { 25 enum class YUVColorSpace : uint8_t; 26 enum class ColorDepth : uint8_t; 27 enum class ColorRange : uint8_t; 28 } // namespace gfx 29 30 struct TrackingId { 31 MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( 32 Source, uint8_t, 33 (Unimplemented, AudioDestinationNode, Camera, Canvas, ChannelDecoder, 34 HLSDecoder, MediaCapabilities, MediaElementDecoder, MediaElementStream, 35 MSEDecoder, RTCRtpReceiver, Screen, Tab, Window, LAST)); 36 enum class TrackAcrossProcesses : uint8_t { 37 Yes, 38 No, 39 }; 40 TrackingId(); 41 TrackingId(Source aSource, uint32_t aUniqueInProcId, 42 TrackAcrossProcesses aTrack = TrackAcrossProcesses::No); 43 44 nsCString ToString() const; 45 46 Source mSource; 47 uint32_t mUniqueInProcId; 48 Maybe<uint32_t> mProcId; 49 }; 50 51 enum class MediaInfoFlag : uint16_t { 52 None = (0 << 0), 53 NonKeyFrame = (1 << 0), 54 KeyFrame = (1 << 1), 55 SoftwareDecoding = (1 << 2), 56 HardwareDecoding = (1 << 3), 57 VIDEO_AV1 = (1 << 4), 58 VIDEO_H264 = (1 << 5), 59 VIDEO_VP8 = (1 << 6), 60 VIDEO_VP9 = (1 << 7), 61 VIDEO_HEVC = (1 << 9), 62 }; 63 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MediaInfoFlag) 64 65 /** 66 * This represents the different stages that a media data will go through 67 * within the playback journey. 68 * 69 * |---| |---| |------| 70 * Copy Demuxed Copy Demuxed Copy Decoded 71 * Data Data Video 72 * |------------- | |-----------------------------------| 73 * Request Demux Request Decode 74 * |-----------------------------------------------------------| 75 * Request Data 76 * 77 * RequestData : Record the time where MediaDecoderStateMachine(MDSM) starts 78 * asking for a decoded data to MDSM receives a decoded data. 79 * 80 * RequestDemux : Record the time where MediaFormatReader(MFR) starts asking 81 * a demuxed sample to MFR received a demuxed sample. This stage is a sub- 82 * stage of RequestData. 83 * 84 * CopyDemuxedData : On some situations, we will need to copy the demuxed 85 * data, which is still not decoded yet so its size is still small. This 86 * records the time which we spend on copying data. This stage could happen 87 * multiple times, either being a sub-stage of RequestDemux (in MSE case), 88 * or being a sub-stage of RequestDecode (when sending data via IPC). 89 * 90 * RequestDecode : Record the time where MFR starts asking decoder to return 91 * a decoded data to MFR receives a decoded data. As the decoder might be 92 * remote, this stage might include the time spending on IPC trips. This 93 * stage is a sub-stage of RequestData. 94 * 95 * CopyDecodedVideo : If we can't reuse same decoder texture to the 96 * compositor, then we have to copy video data to to another sharable 97 * texture. This records the time which we spend on copying data. This stage 98 * is a sub- stage of RequestDecode. 99 */ 100 MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AND_TOSTRING(MediaStage, uint8_t, 101 (Invalid, RequestData, 102 RequestDemux, CopyDemuxedData, 103 RequestDecode, CopyDecodedVideo)); 104 105 class StageBase { 106 public: 107 virtual void AddMarker(MarkerOptions&& aOption) { 108 profiler_add_marker(Name(), Category(), 109 std::forward<MarkerOptions&&>(aOption)); 110 } 111 112 protected: 113 virtual ProfilerString8View Name() const = 0; 114 virtual const MarkerCategory& Category() const = 0; 115 }; 116 117 class PlaybackStage : public StageBase { 118 public: 119 explicit PlaybackStage(MediaStage aStage, int32_t aHeight = 0, 120 MediaInfoFlag aFlag = MediaInfoFlag::None) 121 : mStage(aStage), mHeight(aHeight), mFlag(aFlag) { 122 MOZ_ASSERT(aStage != MediaStage::Invalid); 123 } 124 125 ProfilerString8View Name() const override; 126 const MarkerCategory& Category() const override { 127 return baseprofiler::category::MEDIA_PLAYBACK; 128 } 129 void AddMarker(MarkerOptions&& aOption) override; 130 131 void SetStartTimeAndEndTime(uint64_t aStartTime, uint64_t aEndTime) { 132 mStartAndEndTimeUs = 133 Some(std::pair<uint64_t, uint64_t>{aStartTime, aEndTime}); 134 } 135 136 void AddFlag(MediaInfoFlag aFlag); 137 138 MediaStage mStage; 139 int32_t mHeight; 140 MediaInfoFlag mFlag; 141 142 Maybe<std::pair<uint64_t, uint64_t>> mStartAndEndTimeUs; 143 144 private: 145 mutable Maybe<nsCString> mName; 146 }; 147 148 class CaptureStage : public StageBase { 149 public: 150 MOZ_DEFINE_ENUM_CLASS_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( 151 ImageType, uint8_t, (Unknown, I420, YUY2, YV12, UYVY, NV12, NV21, MJPEG)); 152 153 CaptureStage(nsCString aSource, TrackingId aTrackingId, int32_t aWidth, 154 int32_t aHeight, ImageType aImageType) 155 : mSource(std::move(aSource)), 156 mTrackingId(std::move(aTrackingId)), 157 mWidth(aWidth), 158 mHeight(aHeight), 159 mImageType(aImageType) {} 160 161 ProfilerString8View Name() const override; 162 const MarkerCategory& Category() const override { 163 return baseprofiler::category::MEDIA_RT; 164 } 165 166 nsCString mSource; 167 TrackingId mTrackingId; 168 int32_t mWidth; 169 int32_t mHeight; 170 ImageType mImageType; 171 172 private: 173 mutable Maybe<nsCString> mName; 174 }; 175 176 class CopyVideoStage : public StageBase { 177 public: 178 CopyVideoStage(nsCString aSource, TrackingId aTrackingId, int32_t aWidth, 179 int32_t aHeight) 180 : mSource(std::move(aSource)), 181 mTrackingId(std::move(aTrackingId)), 182 mWidth(aWidth), 183 mHeight(aHeight) {} 184 185 ProfilerString8View Name() const override; 186 const MarkerCategory& Category() const override { 187 return baseprofiler::category::MEDIA_RT; 188 } 189 190 // The name of the source that performs this stage. 191 nsCString mSource; 192 // A unique id identifying the source of the video frame this stage is 193 // performed for. 194 TrackingId mTrackingId; 195 int32_t mWidth; 196 int32_t mHeight; 197 198 private: 199 mutable Maybe<nsCString> mName; 200 }; 201 202 class DecodeStage : public StageBase { 203 public: 204 MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( 205 ImageFormat, uint8_t, 206 (YUV420P, YUV422P, YUV444P, NV12, YV12, NV21, P010, P016, RGBA32, RGB24, 207 GBRP, ANDROID_SURFACE, VAAPI_SURFACE, D3D11_SURFACE)); 208 209 DecodeStage(nsCString aSource, TrackingId aTrackingId, MediaInfoFlag aFlag) 210 : mSource(std::move(aSource)), 211 mTrackingId(std::move(aTrackingId)), 212 mFlag(aFlag) {} 213 ProfilerString8View Name() const override; 214 const MarkerCategory& Category() const override { 215 return baseprofiler::category::MEDIA_PLAYBACK; 216 } 217 218 void SetResolution(int aWidth, int aHeight) { 219 mWidth = Some(aWidth); 220 mHeight = Some(aHeight); 221 } 222 void SetImageFormat(ImageFormat aFormat) { mImageFormat = Some(aFormat); } 223 void SetYUVColorSpace(gfx::YUVColorSpace aColorSpace) { 224 mYUVColorSpace = Some(aColorSpace); 225 } 226 void SetColorRange(gfx::ColorRange aColorRange) { 227 mColorRange = Some(aColorRange); 228 } 229 void SetColorDepth(gfx::ColorDepth aColorDepth) { 230 mColorDepth = Some(aColorDepth); 231 } 232 void SetStartTimeAndEndTime(uint64_t aStartTime, uint64_t aEndTime) { 233 mStartAndEndTimeUs = 234 Some(std::pair<uint64_t, uint64_t>{aStartTime, aEndTime}); 235 } 236 void AddMarker(MarkerOptions&& aOption) override; 237 238 // The name of the source that performs this stage. 239 nsCString mSource; 240 // A unique id identifying the source of the video frame this stage is 241 // performed for. 242 TrackingId mTrackingId; 243 MediaInfoFlag mFlag; 244 Maybe<int> mWidth; 245 Maybe<int> mHeight; 246 Maybe<ImageFormat> mImageFormat; 247 Maybe<gfx::YUVColorSpace> mYUVColorSpace; 248 Maybe<gfx::ColorRange> mColorRange; 249 Maybe<gfx::ColorDepth> mColorDepth; 250 mutable Maybe<nsCString> mName; 251 Maybe<std::pair<uint64_t, uint64_t>> mStartAndEndTimeUs; 252 }; 253 254 class PerformanceRecorderBase { 255 public: 256 static bool IsMeasurementEnabled(); 257 static TimeStamp GetCurrentTimeForMeasurement(); 258 259 // Return the resolution range for the given height. Eg. V:1080<h<=1440. 260 static const char* FindMediaResolution(int32_t aHeight); 261 262 protected: 263 // We would enable the measurement on testing. 264 static inline bool sEnableMeasurementForTesting = false; 265 }; 266 267 template <typename StageType> 268 class PerformanceRecorderImpl : public PerformanceRecorderBase { 269 public: 270 ~PerformanceRecorderImpl() = default; 271 272 PerformanceRecorderImpl(PerformanceRecorderImpl&& aRhs) noexcept 273 : mStages(std::move(aRhs.mStages)) {} 274 PerformanceRecorderImpl& operator=(PerformanceRecorderImpl&&) = delete; 275 PerformanceRecorderImpl(const PerformanceRecorderImpl&) = delete; 276 PerformanceRecorderImpl& operator=(const PerformanceRecorderImpl&) = delete; 277 278 protected: 279 PerformanceRecorderImpl() = default; 280 281 // Stores the stage with the current time as its start time, associated with 282 // aId. 283 template <typename... Args> 284 void Start(int64_t aId, Args... aArgs) { 285 if (IsMeasurementEnabled()) { 286 MutexAutoLock lock(mMutex); 287 mStages.Push(std::make_tuple(aId, GetCurrentTimeForMeasurement(), 288 StageType(std::move(aArgs)...))); 289 } 290 } 291 292 // Return the passed time since creation of the aId stage in microseconds if 293 // it has not yet been recorded. Other stages with lower ids will be 294 // discarded. Otherwise, return 0. 295 template <typename F> 296 float Record(int64_t aId, F&& aStageMutator) { 297 Maybe<Entry> entry; 298 { 299 MutexAutoLock lock(mMutex); 300 while (!mStages.IsEmpty() && std::get<0>(mStages.Top()) < aId) { 301 mStages.Pop(); 302 } 303 if (mStages.IsEmpty()) { 304 return 0.0; 305 } 306 if (std::get<0>(mStages.Top()) != aId) { 307 return 0.0; 308 } 309 entry = Some(mStages.Pop()); 310 } 311 const auto& startTime = std::get<1>(*entry); 312 auto& stage = std::get<2>(*entry); 313 MOZ_ASSERT(std::get<0>(*entry) == aId); 314 double elapsedTimeUs = 0.0; 315 if (!startTime.IsNull() && IsMeasurementEnabled()) { 316 const auto now = TimeStamp::Now(); 317 elapsedTimeUs = (now - startTime).ToMicroseconds(); 318 MOZ_ASSERT(elapsedTimeUs >= 0, "Elapsed time can't be less than 0!"); 319 aStageMutator(stage); 320 AUTO_PROFILER_STATS(PROFILER_MARKER_UNTYPED); 321 stage.AddMarker(MarkerOptions(MarkerTiming::Interval(startTime, now))); 322 } 323 return static_cast<float>(elapsedTimeUs); 324 } 325 float Record(int64_t aId) { 326 return Record(aId, [](auto&) {}); 327 } 328 329 protected: 330 using Entry = std::tuple<int64_t, TimeStamp, StageType>; 331 332 struct IdComparator { 333 bool LessThan(const Entry& aTupleA, const Entry& aTupleB) { 334 return std::get<0>(aTupleA) < std::get<0>(aTupleB); 335 } 336 }; 337 338 Mutex mMutex{"PerformanceRecorder::mMutex"}; 339 nsTPriorityQueue<Entry, IdComparator> mStages MOZ_GUARDED_BY(mMutex); 340 }; 341 342 /** 343 * This class is used to record the time spent on different stages in the media 344 * pipeline. `Record()` needs to be called explicitly to record a profiler 345 * marker registering the time passed since creation. A stage may be mutated in 346 * `Record()` in case data has become available since the recorder started. 347 * 348 * This variant is intended to be created on the stack when a stage starts, then 349 * recorded with `Record()` when the stage is finished. 350 */ 351 template <typename StageType> 352 class PerformanceRecorder : public PerformanceRecorderImpl<StageType> { 353 using Super = PerformanceRecorderImpl<StageType>; 354 355 public: 356 template <typename... Args> 357 explicit PerformanceRecorder(Args... aArgs) { 358 Start(std::move(aArgs)...); 359 }; 360 361 private: 362 template <typename... Args> 363 void Start(Args... aArgs) { 364 Super::Start(0, std::move(aArgs)...); 365 } 366 367 public: 368 template <typename F> 369 float Record(F&& aStageMutator) { 370 return Super::Record(0, std::forward<F>(aStageMutator)); 371 } 372 float Record() { return Super::Record(0); } 373 }; 374 375 /** 376 * This class is used to record the time spent on different stages in the media 377 * pipeline. `Start()` and `Record()` needs to be called explicitly to record a 378 * profiler marker registering the time passed since creation. A stage may be 379 * mutated in `Record()` in case data has become available since the recorder 380 * started. 381 * 382 * This variant is intended to be kept as a member in a class and supports async 383 * stages. The async stages may overlap each other. To distinguish different 384 * stages from each other, an int64_t is used as identifier. This is often a 385 * timestamp in microseconds, see TimeUnit::ToMicroseconds. 386 */ 387 template <typename StageType> 388 class PerformanceRecorderMulti : public PerformanceRecorderImpl<StageType> { 389 using Super = PerformanceRecorderImpl<StageType>; 390 391 public: 392 PerformanceRecorderMulti() = default; 393 394 using Super::Record; 395 using Super::Start; 396 }; 397 398 } // namespace mozilla 399 400 #endif // mozilla_PerformanceRecorder_h