Bug 1796069 - Simplify and make more flexible the PerformanceRecorder API. r=alwu

Differential Revision: https://phabricator.services.mozilla.com/D160751
This commit is contained in:
Andreas Pehrson 2022-12-01 09:52:44 +00:00
parent 373c7f77e9
commit 8143e2385f
9 changed files with 338 additions and 215 deletions

View File

@ -658,15 +658,14 @@ void ExternalEngineStateMachine::OnRequestAudio() {
}
LOGV("Start requesting audio");
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestData);
RefPtr<ExternalEngineStateMachine> self = this;
mReader->RequestAudioData()
->Then(
OwnerThread(), __func__,
[this, self, perfRecorder(std::move(perfRecorder))](
const RefPtr<AudioData>& aAudio) mutable {
perfRecorder.End();
perfRecorder.Record();
mAudioDataRequest.Complete();
LOGV("Completed requesting audio");
AUTO_PROFILER_LABEL(
@ -718,16 +717,15 @@ void ExternalEngineStateMachine::OnRequestVideo() {
}
LOGV("Start requesting video");
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData,
Info().mVideo.mImage.height);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestData,
Info().mVideo.mImage.height);
RefPtr<ExternalEngineStateMachine> self = this;
mReader->RequestVideoData(GetVideoThreshold(), false)
->Then(
OwnerThread(), __func__,
[this, self, perfRecorder(std::move(perfRecorder))](
const RefPtr<VideoData>& aVideo) mutable {
perfRecorder.End();
perfRecorder.Record();
mVideoDataRequest.Complete();
LOGV("Completed requesting video");
AUTO_PROFILER_LABEL(

View File

@ -326,9 +326,8 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
return nullptr;
}
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::CopyDecodedVideo,
aInfo.mImage.height);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDecodedVideo,
aInfo.mImage.height);
RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe,
aTimecode, aInfo.mDisplay, 0));
@ -349,7 +348,7 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
: aAllocator,
aContainer, data)) {
v->mImage = d3d11Image;
perfRecorder.End();
perfRecorder.Record();
return v.forget();
}
}
@ -361,7 +360,7 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
if (ioImage->SetData(aContainer, data)) {
v->mImage = ioImage;
perfRecorder.End();
perfRecorder.Record();
return v.forget();
}
}
@ -383,7 +382,7 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
return nullptr;
}
perfRecorder.End();
perfRecorder.Record();
return v.forget();
}
@ -496,9 +495,8 @@ already_AddRefed<MediaRawData> MediaRawData::Clone() const {
if (mTrackInfo && mTrackInfo->GetAsVideoInfo()) {
sampleHeight = mTrackInfo->GetAsVideoInfo()->mImage.height;
}
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::CopyDemuxedData,
sampleHeight);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData,
sampleHeight);
RefPtr<MediaRawData> s = new MediaRawData;
s->mTimecode = mTimecode;
s->mTime = mTime;
@ -516,7 +514,7 @@ already_AddRefed<MediaRawData> MediaRawData::Clone() const {
if (!s->mAlphaBuffer.Append(mAlphaBuffer.Data(), mAlphaBuffer.Length())) {
return nullptr;
}
perfRecorder.End();
perfRecorder.Record();
return s.forget();
}

View File

@ -3786,15 +3786,14 @@ void MediaDecoderStateMachine::RequestAudioData() {
LOGV("Queueing audio task - queued=%zu, decoder-queued=%zu",
AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestData);
RefPtr<MediaDecoderStateMachine> self = this;
mReader->RequestAudioData()
->Then(
OwnerThread(), __func__,
[this, self, perfRecorder(std::move(perfRecorder))](
const RefPtr<AudioData>& aAudio) mutable {
perfRecorder.End();
perfRecorder.Record();
AUTO_PROFILER_LABEL(
"MediaDecoderStateMachine::RequestAudioData:Resolved",
MEDIA_PLAYBACK);
@ -3847,9 +3846,8 @@ void MediaDecoderStateMachine::RequestVideoData(
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(),
aCurrentTime.ToMicroseconds(), mBypassingSkipToNextKeyFrameCheck);
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData,
Info().mVideo.mImage.height);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestData,
Info().mVideo.mImage.height);
RefPtr<MediaDecoderStateMachine> self = this;
mReader
->RequestVideoData(
@ -3859,7 +3857,7 @@ void MediaDecoderStateMachine::RequestVideoData(
OwnerThread(), __func__,
[this, self, perfRecorder(std::move(perfRecorder))](
const RefPtr<VideoData>& aVideo) mutable {
perfRecorder.End();
perfRecorder.Record();
AUTO_PROFILER_LABEL(
"MediaDecoderStateMachine::RequestVideoData:Resolved",
MEDIA_PLAYBACK);

View File

@ -1512,10 +1512,9 @@ void MediaFormatReader::DoDemuxVideo() {
using SamplesPromise = MediaTrackDemuxer::SamplesPromise;
DDLOG(DDLogCategory::Log, "video_demuxing", DDNoValue{});
PerformanceRecorder perfRecorder(
PerformanceRecorder::Stage::RequestDemux,
PerformanceRecorder<PlaybackStage> perfRecorder(
MediaStage::RequestDemux,
mVideo.GetCurrentInfo()->GetAsVideoInfo()->mImage.height);
perfRecorder.Start();
auto p = mVideo.mTrackDemuxer->GetSamples(1);
RefPtr<MediaFormatReader> self = this;
@ -1544,7 +1543,7 @@ void MediaFormatReader::DoDemuxVideo() {
OwnerThread(), __func__,
[self, perfRecorder(std::move(perfRecorder))](
RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) mutable {
perfRecorder.End();
perfRecorder.Record();
self->OnVideoDemuxCompleted(std::move(aSamples));
},
[self](const MediaResult& aError) { self->OnVideoDemuxFailed(aError); })
@ -1610,8 +1609,7 @@ void MediaFormatReader::DoDemuxAudio() {
using SamplesPromise = MediaTrackDemuxer::SamplesPromise;
DDLOG(DDLogCategory::Log, "audio_demuxing", DDNoValue{});
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestDemux);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestDemux);
auto p = mAudio.mTrackDemuxer->GetSamples(1);
RefPtr<MediaFormatReader> self = this;
@ -1640,7 +1638,7 @@ void MediaFormatReader::DoDemuxAudio() {
OwnerThread(), __func__,
[self, perfRecorder(std::move(perfRecorder))](
RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) mutable {
perfRecorder.End();
perfRecorder.Record();
self->OnAudioDemuxCompleted(std::move(aSamples));
},
[self](const MediaResult& aError) { self->OnAudioDemuxFailed(aError); })
@ -1969,15 +1967,14 @@ void MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
}
#endif
}
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestDecode,
height, flag);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestDecode,
height, flag);
decoder.mDecoder->Decode(aSample)
->Then(
mTaskQueue, __func__,
[self, aTrack, &decoder, perfRecorder(std::move(perfRecorder))](
MediaDataDecoder::DecodedData&& aResults) mutable {
perfRecorder.End();
perfRecorder.Record();
decoder.mDecodeRequest.Complete();
self->NotifyNewOutput(aTrack, std::move(aResults));
},

View File

@ -149,9 +149,8 @@ bool ArrayOfRemoteMediaRawData::Fill(
entry->mEOS, height, entry->mDiscardPadding,
entry->mOriginalPresentationWindow});
}
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::CopyDemuxedData,
height);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData,
height);
mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator);
if (!mBuffers.IsValid()) {
return false;
@ -164,7 +163,7 @@ bool ArrayOfRemoteMediaRawData::Fill(
if (!mExtraDatas.IsValid()) {
return false;
}
perfRecorder.End();
perfRecorder.Record();
return true;
}
@ -179,9 +178,8 @@ already_AddRefed<MediaRawData> ArrayOfRemoteMediaRawData::ElementAt(
mExtraDatas.Count() == Count(),
"Something ain't right here");
const auto& sample = mSamples[aIndex];
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::CopyDemuxedData,
sample.mHeight);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData,
sample.mHeight);
AlignedByteBuffer data = mBuffers.AlignedBufferAt<uint8_t>(aIndex);
if (mBuffers.SizeAt(aIndex) && !data) {
// OOM
@ -206,7 +204,7 @@ already_AddRefed<MediaRawData> ArrayOfRemoteMediaRawData::ElementAt(
rawData->mEOS = sample.mEOS;
rawData->mDiscardPadding = sample.mDiscardPadding;
rawData->mExtraData = mExtraDatas.MediaByteBufferAt(aIndex);
perfRecorder.End();
perfRecorder.Record();
return rawData.forget();
}

View File

@ -1098,9 +1098,8 @@ D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
}
UINT height = std::min(inDesc.Height, outDesc.Height);
PerformanceRecorder perfRecorder(
PerformanceRecorder::Stage::CopyDecodedVideo, height);
perfRecorder.Start();
PerformanceRecorder<PlaybackStage> perfRecorder(
MediaStage::CopyDecodedVideo, height);
// The D3D11TextureClientAllocator may return a different texture format
// than preferred. In which case the destination texture will be BGRA32.
if (outDesc.Format == inDesc.Format) {
@ -1129,7 +1128,7 @@ D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
[&]() -> void { hr = mTransform->Output(&sample); });
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
}
perfRecorder.End();
perfRecorder.Record();
}
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice() &&

View File

@ -7,77 +7,26 @@
#include "PerformanceRecorder.h"
#include "mozilla/Logging.h"
#include "mozilla/ProfilerMarkers.h"
#include "nsString.h"
namespace mozilla {
const char* StageToStr(PerformanceRecorder::Stage aStage) {
const char* StageToStr(MediaStage aStage) {
switch (aStage) {
case PerformanceRecorder::Stage::RequestData:
case MediaStage::RequestData:
return "RequestData";
case PerformanceRecorder::Stage::RequestDemux:
case MediaStage::RequestDemux:
return "RequestDemux";
case PerformanceRecorder::Stage::CopyDemuxedData:
case MediaStage::CopyDemuxedData:
return "CopyDemuxedData";
case PerformanceRecorder::Stage::RequestDecode:
case MediaStage::RequestDecode:
return "RequestDecode";
case PerformanceRecorder::Stage::CopyDecodedVideo:
case MediaStage::CopyDecodedVideo:
return "CopyDecodedVideo";
default:
return "InvalidStage";
}
}
/* static */
const char* PerformanceRecorder::FindMediaResolution(int32_t aHeight) {
static const struct {
const int32_t mH;
const nsCString mRes;
} sResolutions[] = {{0, "A:0"_ns}, // other followings are for video
{240, "V:0<h<=240"_ns},
{480, "V:240<h<=480"_ns},
{576, "V:480<h<=576"_ns},
{720, "V:576<h<=720"_ns},
{1080, "V:720<h<=1080"_ns},
{1440, "V:1080<h<=1440"_ns},
{2160, "V:1440<h<=2160"_ns},
{INT_MAX, "V:h>2160"_ns}};
const char* resolution = sResolutions[0].mRes.get();
for (auto&& res : sResolutions) {
if (aHeight <= res.mH) {
resolution = res.mRes.get();
break;
}
}
return resolution;
}
/* static */
bool PerformanceRecorder::IsMeasurementEnabled() {
return profiler_thread_is_being_profiled_for_markers() ||
PerformanceRecorder::sEnableMeasurementForTesting;
}
/* static */
TimeStamp PerformanceRecorder::GetCurrentTimeForMeasurement() {
// The system call to get the clock is rather expensive on Windows. As we
// only report the measurement report via markers, if the marker isn't enabled
// then we won't do any measurement in order to save CPU time.
return IsMeasurementEnabled() ? TimeStamp::Now() : TimeStamp();
}
void PerformanceRecorder::Start() {
MOZ_ASSERT(mStage != Stage::Invalid);
MOZ_ASSERT(!mStartTime);
mStartTime = Some(GetCurrentTimeForMeasurement());
}
void PerformanceRecorder::Reset() {
mStartTime.reset();
mStage = Stage::Invalid;
}
void AppendMediaInfoFlagToName(nsCString& aName, MediaInfoFlag aFlag) {
if (aFlag & MediaInfoFlag::KeyFrame) {
aName.Append("kf,");
@ -100,26 +49,53 @@ void AppendMediaInfoFlagToName(nsCString& aName, MediaInfoFlag aFlag) {
}
}
float PerformanceRecorder::End() {
double elapsedTimeUs = 0.0;
if (mStartTime && !mStartTime->IsNull()) {
MOZ_ASSERT(mStage != Stage::Invalid);
if (IsMeasurementEnabled()) {
const auto now = TimeStamp::Now();
elapsedTimeUs = (now - *mStartTime).ToMicroseconds();
MOZ_ASSERT(elapsedTimeUs >= 0, "Elapsed time can't be less than 0!");
nsAutoCString name(StageToStr(mStage));
name.Append(":");
name.Append(FindMediaResolution(mHeight));
name.Append(":");
AppendMediaInfoFlagToName(name, mFlag);
PROFILER_MARKER_UNTYPED(
name, MEDIA_PLAYBACK,
MarkerOptions(MarkerTiming::Interval(*mStartTime, now)));
/* static */
const char* FindMediaResolution(int32_t aHeight) {
static const struct {
const int32_t mH;
const nsCString mRes;
} sResolutions[] = {{0, "A:0"_ns}, // other followings are for video
{240, "V:0<h<=240"_ns},
{480, "V:240<h<=480"_ns},
{576, "V:480<h<=576"_ns},
{720, "V:576<h<=720"_ns},
{1080, "V:720<h<=1080"_ns},
{1440, "V:1080<h<=1440"_ns},
{2160, "V:1440<h<=2160"_ns},
{INT_MAX, "V:h>2160"_ns}};
const char* resolution = sResolutions[0].mRes.get();
for (auto&& res : sResolutions) {
if (aHeight <= res.mH) {
resolution = res.mRes.get();
break;
}
Reset();
}
return elapsedTimeUs;
return resolution;
}
/* static */
bool PerformanceRecorderBase::IsMeasurementEnabled() {
return profiler_thread_is_being_profiled_for_markers() ||
PerformanceRecorderBase::sEnableMeasurementForTesting;
}
/* static */
TimeStamp PerformanceRecorderBase::GetCurrentTimeForMeasurement() {
// The system call to get the clock is rather expensive on Windows. As we
// only report the measurement report via markers, if the marker isn't enabled
// then we won't do any measurement in order to save CPU time.
return IsMeasurementEnabled() ? TimeStamp::Now() : TimeStamp();
}
ProfilerString8View PlaybackStage::Name() const {
if (!mName) {
mName.emplace(StageToStr(mStage));
mName->Append(":");
mName->Append(FindMediaResolution(mHeight));
mName->Append(":");
AppendMediaInfoFlagToName(*mName, mFlag);
}
return *mName;
}
} // namespace mozilla

View File

@ -7,11 +7,17 @@
#ifndef mozilla_PerformanceRecorder_h
#define mozilla_PerformanceRecorder_h
#include <type_traits>
#include "mozilla/Attributes.h"
#include "mozilla/BaseProfilerMarkersPrerequisites.h"
#include "mozilla/Maybe.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypedEnumBits.h"
#include "nsPrintfCString.h"
#include "nsStringFwd.h"
#include "nsTPriorityQueue.h"
#include "mozilla/ProfilerMarkers.h"
namespace mozilla {
@ -29,106 +35,210 @@ enum class MediaInfoFlag : uint16_t {
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MediaInfoFlag)
/**
* This class is used to record the passed time on the different stages in the
* media playback pipeline. It needs to call `Start()` and `End()` explicitly
* in order to record the passed time between these two calls.
* This represents the different stages that a media data will go through
* within the playback journey.
*
* |---| |---| |------|
* Copy Demuxed Copy Demuxed Copy Decoded
* Data Data Video
* |------------- | |-----------------------------------|
* Request Demux Request Decode
* |-----------------------------------------------------------|
* Request Data
*
* RequestData : Record the time where MediaDecoderStateMachine(MDSM) starts
* asking for a decoded data to MDSM receives a decoded data.
*
* RequestDemux : Record the time where MediaFormatReader(MFR) starts asking
* a demuxed sample to MFR received a demuxed sample. This stage is a sub-
* stage of RequestData.
*
* CopyDemuxedData : On some situations, we will need to copy the demuxed
* data, which is still not decoded yet so its size is still small. This
* records the time which we spend on copying data. This stage could happen
* multiple times, either being a sub-stage of RequestDemux (in MSE case),
* or being a sub-stage of RequestDecode (when sending data via IPC).
*
* RequestDecode : Record the time where MFR starts asking decoder to return
* a decoded data to MFR receives a decoded data. As the decoder might be
* remote, this stage might include the time spending on IPC trips. This
* stage is a sub-stage of RequestData.
*
* CopyDecodedVideo : If we can't reuse same decoder texture to the
* compositor, then we have to copy video data to to another sharable
* texture. This records the time which we spend on copying data. This stage
* is a sub- stage of RequestDecode.
*/
class PerformanceRecorder {
enum class MediaStage : uint8_t {
Invalid,
RequestData,
RequestDemux,
CopyDemuxedData,
RequestDecode,
CopyDecodedVideo,
};
class PlaybackStage {
public:
/**
* This represents the different stages that a media data will go through
* within the playback journey.
*
* |---| |---| |------|
* Copy Demuxed Copy Demuxed Copy Decoded
* Data Data Video
* |------------- | |-----------------------------------|
* Request Demux Request Decode
* |-----------------------------------------------------------|
* Request Data
*
* RequestData : Record the time where MediaDecoderStateMachine(MDSM) starts
* asking for a decoded data to MDSM receives a decoded data.
*
* RequestDemux : Record the time where MediaFormatReader(MFR) starts asking
* a demuxed sample to MFR received a demuxed sample. This stage is a sub-
* stage of RequestData.
*
* CopyDemuxedData : On some situations, we will need to copy the demuxed
* data, which is still not decoded yet so its size is still small. This
* records the time which we spend on copying data. This stage could happen
* multiple times, either being a sub-stage of RequestDemux (in MSE case), or
* being a sub-stage of RequestDecode (when sending data via IPC).
*
* RequestDecode : Record the time where MFR starts asking decoder to return
* a decoded data to MFR receives a decoded data. As the decoder might be
* remote, this stage might include the time spending on IPC trips. This stage
* is a sub-stage of RequestData.
*
* CopyDecodedVideo : If we can't reuse same decoder texture to the
* compositor, then we have to copy video data to to another sharable texture.
* This records the time which we spend on copying data. This stage is a sub-
* stage of RequestDecode.
*/
enum class Stage : uint8_t {
Invalid,
RequestData,
RequestDemux,
CopyDemuxedData,
RequestDecode,
CopyDecodedVideo,
};
explicit PerformanceRecorder(Stage aStage, int32_t aHeight = 0,
MediaInfoFlag aFlag = MediaInfoFlag::None)
: mStage(aStage), mHeight(aHeight), mFlag(aFlag) {}
~PerformanceRecorder() = default;
PerformanceRecorder(PerformanceRecorder&& aRhs) noexcept {
mStage = aRhs.mStage;
mHeight = aRhs.mHeight;
mStartTime = std::move(aRhs.mStartTime);
mFlag = aRhs.mFlag;
aRhs.mStage = Stage::Invalid;
explicit PlaybackStage(MediaStage aStage, int32_t aHeight = 0,
MediaInfoFlag aFlag = MediaInfoFlag::None)
: mStage(aStage), mHeight(aHeight), mFlag(aFlag) {
MOZ_ASSERT(aStage != MediaStage::Invalid);
}
PerformanceRecorder& operator=(PerformanceRecorder&& aRhs) noexcept {
MOZ_ASSERT(&aRhs != this, "self-moves are prohibited");
mStage = aRhs.mStage;
mHeight = aRhs.mHeight;
mStartTime = std::move(aRhs.mStartTime);
mFlag = aRhs.mFlag;
aRhs.mStage = Stage::Invalid;
return *this;
ProfilerString8View Name() const;
const MarkerCategory& Category() const {
return baseprofiler::category::MEDIA_PLAYBACK;
}
PerformanceRecorder(const PerformanceRecorder&) = delete;
PerformanceRecorder& operator=(const PerformanceRecorder&) = delete;
MediaStage mStage;
int32_t mHeight;
MediaInfoFlag mFlag;
void Start();
// Return the passed time if it has started and still valid. Otherwise,
// return 0.
float End();
protected:
void Reset();
private:
mutable Maybe<nsCString> mName;
};
class PerformanceRecorderBase {
public:
static bool IsMeasurementEnabled();
static TimeStamp GetCurrentTimeForMeasurement();
// Return the resolution range for the given height. Eg. V:1080<h<=1440.
static const char* FindMediaResolution(int32_t aHeight);
Stage mStage = Stage::Invalid;
int32_t mHeight;
MediaInfoFlag mFlag = MediaInfoFlag::None;
Maybe<TimeStamp> mStartTime;
protected:
// We would enable the measurement on testing.
static inline bool sEnableMeasurementForTesting = false;
};
template <typename StageType>
class PerformanceRecorderImpl : public PerformanceRecorderBase {
public:
~PerformanceRecorderImpl() = default;
PerformanceRecorderImpl(PerformanceRecorderImpl&& aRhs) noexcept
: mStages(std::move(aRhs.mStages)) {}
PerformanceRecorderImpl& operator=(PerformanceRecorderImpl&&) = delete;
PerformanceRecorderImpl(const PerformanceRecorderImpl&) = delete;
PerformanceRecorderImpl& operator=(const PerformanceRecorderImpl&) = delete;
protected:
PerformanceRecorderImpl() = default;
// Stores the stage with the current time as its start time, associated with
// aId.
template <typename... Args>
void Start(int64_t aId, Args... aArgs) {
if (IsMeasurementEnabled()) {
mStages.Push(MakeTuple(aId, GetCurrentTimeForMeasurement(),
StageType(std::move(aArgs)...)));
}
}
// Return the passed time since creation of the aId stage in microseconds if
// it has not yet been recorded. Other stages with lower ids will be
// discarded. Otherwise, return 0.
template <typename F>
float Record(int64_t aId, F&& aStageMutator) {
while (!mStages.IsEmpty() && Get<0>(mStages.Top()) < aId) {
mStages.Pop();
}
if (mStages.IsEmpty()) {
return 0.0;
}
if (Get<0>(mStages.Top()) != aId) {
return 0.0;
}
Entry entry = mStages.Pop();
const auto& startTime = Get<1>(entry);
auto& stage = Get<2>(entry);
MOZ_ASSERT(Get<0>(entry) == aId);
double elapsedTimeUs = 0.0;
if (!startTime.IsNull() && IsMeasurementEnabled()) {
const auto now = TimeStamp::Now();
elapsedTimeUs = (now - startTime).ToMicroseconds();
MOZ_ASSERT(elapsedTimeUs >= 0, "Elapsed time can't be less than 0!");
aStageMutator(stage);
AUTO_PROFILER_STATS(PROFILER_MARKER_UNTYPED);
::profiler_add_marker(
stage.Name(), stage.Category(),
MarkerOptions(MarkerTiming::Interval(startTime, now)));
}
return static_cast<float>(elapsedTimeUs);
}
float Record(int64_t aId) {
return Record(aId, [](auto&) {});
}
protected:
using Entry = Tuple<int64_t, TimeStamp, StageType>;
struct IdComparator {
bool LessThan(const Entry& aTupleA, const Entry& aTupleB) {
return Get<0>(aTupleA) < Get<0>(aTupleB);
}
};
nsTPriorityQueue<Entry, IdComparator> mStages;
};
/**
* This class is used to record the time spent on different stages in the media
* pipeline. `Record()` needs to be called explicitly to record a profiler
* marker registering the time passed since creation. A stage may be mutated in
* `Record()` in case data has become available since the recorder started.
*
* This variant is intended to be created on the stack when a stage starts, then
* recorded with `Record()` when the stage is finished.
*/
template <typename StageType>
class PerformanceRecorder : public PerformanceRecorderImpl<StageType> {
using Super = PerformanceRecorderImpl<StageType>;
public:
template <typename... Args>
explicit PerformanceRecorder(Args... aArgs) {
Start(std::move(aArgs)...);
};
private:
template <typename... Args>
void Start(Args... aArgs) {
Super::Start(0, std::move(aArgs)...);
}
public:
template <typename F>
float Record(F&& aStageMutator) {
return Super::Record(0, std::forward<F>(aStageMutator));
}
float Record() { return Super::Record(0); }
};
/**
* This class is used to record the time spent on different stages in the media
* pipeline. `Start()` and `Record()` needs to be called explicitly to record a
* profiler marker registering the time passed since creation. A stage may be
* mutated in `Record()` in case data has become available since the recorder
* started.
*
* This variant is intended to be kept as a member in a class and supports async
* stages. The async stages may overlap each other. To distinguish different
* stages from each other, an int64_t is used as identifier. This is often a
* timestamp in microseconds, see TimeUnit::ToMicroseconds.
*/
template <typename StageType>
class PerformanceRecorderMulti : public PerformanceRecorderImpl<StageType> {
using Super = PerformanceRecorderImpl<StageType>;
public:
PerformanceRecorderMulti() = default;
using Super::Record;
using Super::Start;
};
} // namespace mozilla
#endif // mozilla_PerformanceRecorder_h

View File

@ -8,15 +8,13 @@
#include "gtest/gtest.h"
#include "nsString.h"
using Stage = mozilla::PerformanceRecorder::Stage;
using namespace mozilla;
class PerformanceRecorderWrapper : public mozilla::PerformanceRecorder {
class PerformanceRecorderWrapper : public PerformanceRecorder<PlaybackStage> {
public:
PerformanceRecorderWrapper(Stage aStage, int32_t aHeight)
PerformanceRecorderWrapper(MediaStage aStage, int32_t aHeight)
: PerformanceRecorder(aStage, aHeight) {}
const char* Resolution() const { return FindMediaResolution(mHeight); }
static void EnableMeasurementOnNonMarkerSituation() {
sEnableMeasurementForTesting = true;
}
@ -24,6 +22,8 @@ class PerformanceRecorderWrapper : public mozilla::PerformanceRecorder {
TEST(PerformanceRecorder, TestResolution)
{
PerformanceRecorderWrapper::EnableMeasurementOnNonMarkerSituation();
static const struct {
const int32_t mH;
const char* mRes;
@ -37,10 +37,12 @@ TEST(PerformanceRecorder, TestResolution)
{2160, "V:1440<h<=2160"},
{4320, "V:h>2160"}};
const Stage stage = Stage::RequestDecode;
const MediaStage stage = MediaStage::RequestDecode;
for (auto&& res : resolutions) {
PerformanceRecorderWrapper w(stage, res.mH);
ASSERT_STREQ(w.Resolution(), res.mRes);
nsCString name;
w.Record([&](auto& aStage) { name = nsCString(aStage.Name()); });
ASSERT_NE(name.Find(res.mRes), kNotFound);
}
}
@ -48,14 +50,61 @@ TEST(PerformanceRecorder, TestMoveOperation)
{
PerformanceRecorderWrapper::EnableMeasurementOnNonMarkerSituation();
const Stage stage = Stage::RequestDecode;
const MediaStage stage = MediaStage::RequestDecode;
const uint32_t resolution = 1080;
PerformanceRecorderWrapper w1(stage, resolution);
w1.Start();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// w1 has been moved which won't continue measuring data.
PerformanceRecorderWrapper w2(std::move(w1));
ASSERT_DOUBLE_EQ(w1.End(), 0.0);
ASSERT_TRUE(w2.End() > 0.0);
ASSERT_DOUBLE_EQ(w1.Record(), 0.0);
ASSERT_TRUE(w2.Record() > 0.0);
}
TEST(PerformanceRecorder, TestRecordInvalidation)
{
PerformanceRecorderWrapper::EnableMeasurementOnNonMarkerSituation();
const MediaStage stage = MediaStage::RequestDecode;
const uint32_t resolution = 1080;
PerformanceRecorderWrapper w(stage, resolution);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
ASSERT_TRUE(w.Record() > 0.0);
w.Record();
// w has been recorded and won't continue measuring data.
ASSERT_DOUBLE_EQ(w.Record(), 0.0);
}
TEST(PerformanceRecorder, TestMultipleRecords)
{
PerformanceRecorderWrapper::EnableMeasurementOnNonMarkerSituation();
const MediaStage stage = MediaStage::RequestDecode;
PerformanceRecorderMulti<PlaybackStage> r;
r.Start(1, stage, 1);
r.Start(2, stage, 2);
r.Start(3, stage, 3);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// id 0 wasn't started
EXPECT_DOUBLE_EQ(r.Record(0), 0.0);
// id 1 gets recorded normally
EXPECT_TRUE(r.Record(1) > 0.0);
// id 1 was already recorded
EXPECT_DOUBLE_EQ(r.Record(1), 0.0);
// id 2 gets recorded normally
EXPECT_TRUE(r.Record(2) > 0.0);
// id 4 wasn't started
EXPECT_DOUBLE_EQ(r.Record(4), 0.0);
// All lower ids got discarded
EXPECT_DOUBLE_EQ(r.Record(3), 0.0);
}