mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
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:
parent
373c7f77e9
commit
8143e2385f
@ -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(
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
},
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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() &&
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user