Bug 1449132 [wpt PR 10194] - [css-grid] Fix resolution of percentage paddings and...
[gecko.git] / dom / media / Benchmark.cpp
blobf94e25037e1f2b89d79cad6b23102fc3a0a39cb4
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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/. */
7 #include "Benchmark.h"
9 #include "BufferMediaResource.h"
10 #include "MediaData.h"
11 #include "MediaPrefs.h"
12 #include "PDMFactory.h"
13 #include "VideoUtils.h"
14 #include "WebMDemuxer.h"
15 #include "gfxPrefs.h"
16 #include "mozilla/AbstractThread.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/SystemGroup.h"
20 #include "mozilla/TaskQueue.h"
21 #include "mozilla/Telemetry.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/gfx/gfxVars.h"
25 #ifndef MOZ_WIDGET_ANDROID
26 #include "WebMSample.h"
27 #endif
29 using namespace mozilla::gfx;
31 namespace mozilla {
33 // Update this version number to force re-running the benchmark. Such as when
34 // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
35 const uint32_t VP9Benchmark::sBenchmarkVersionID = 3;
37 const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
38 const char* VP9Benchmark::sBenchmarkFpsVersionCheck = "media.benchmark.vp9.versioncheck";
39 bool VP9Benchmark::sHasRunTest = false;
41 // static
42 bool
43 VP9Benchmark::IsVP9DecodeFast()
45 MOZ_ASSERT(NS_IsMainThread());
47 // Disable VP9 estimizer on Mac, see bug 1400787.
48 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_APPLEMEDIA)
49 return false;
50 #else
51 bool hasPref = Preferences::HasUserValue(sBenchmarkFpsPref);
52 uint32_t hadRecentUpdate = Preferences::GetUint(sBenchmarkFpsVersionCheck, 0U);
54 if (!sHasRunTest && (!hasPref || hadRecentUpdate != sBenchmarkVersionID)) {
55 sHasRunTest = true;
57 RefPtr<WebMDemuxer> demuxer = new WebMDemuxer(
58 new BufferMediaResource(sWebMSample, sizeof(sWebMSample)));
59 RefPtr<Benchmark> estimiser =
60 new Benchmark(demuxer,
62 Preferences::GetInt("media.benchmark.frames", 300), // frames to measure
63 1, // start benchmarking after decoding this frame.
64 8, // loop after decoding that many frames.
65 TimeDuration::FromMilliseconds(
66 Preferences::GetUint("media.benchmark.timeout", 1000))
67 });
68 estimiser->Run()->Then(
69 SystemGroup::AbstractMainThreadFor(TaskCategory::Other), __func__,
70 [](uint32_t aDecodeFps) {
71 if (XRE_IsContentProcess()) {
72 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
73 if (contentChild) {
74 contentChild->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"),
75 aDecodeFps);
77 } else {
78 Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
79 Preferences::SetUint(sBenchmarkFpsVersionCheck, sBenchmarkVersionID);
81 Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_VP9_BENCHMARK_FPS, aDecodeFps);
83 []() { });
86 if (!hasPref) {
87 return false;
90 uint32_t decodeFps = Preferences::GetUint(sBenchmarkFpsPref);
91 uint32_t threshold =
92 Preferences::GetUint("media.benchmark.vp9.threshold", 150);
94 return decodeFps >= threshold;
95 #endif
98 Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
99 : QueueObject(AbstractThread::MainThread())
100 , mParameters(aParameters)
101 , mKeepAliveUntilComplete(this)
102 , mPlaybackState(this, aDemuxer)
104 MOZ_COUNT_CTOR(Benchmark);
105 MOZ_ASSERT(Thread(), "Must be run in task queue");
108 Benchmark::~Benchmark()
110 MOZ_COUNT_DTOR(Benchmark);
113 RefPtr<Benchmark::BenchmarkPromise>
114 Benchmark::Run()
116 MOZ_ASSERT(OnThread());
118 RefPtr<BenchmarkPromise> p = mPromise.Ensure(__func__);
119 RefPtr<Benchmark> self = this;
120 mPlaybackState.Dispatch(NS_NewRunnableFunction(
121 "Benchmark::Run", [self]() { self->mPlaybackState.DemuxSamples(); }));
122 return p;
125 void
126 Benchmark::ReturnResult(uint32_t aDecodeFps)
128 MOZ_ASSERT(OnThread());
130 mPromise.ResolveIfExists(aDecodeFps, __func__);
133 void
134 Benchmark::ReturnError(const MediaResult& aError)
136 MOZ_ASSERT(OnThread());
138 mPromise.RejectIfExists(aError, __func__);
141 void
142 Benchmark::Dispose()
144 MOZ_ASSERT(OnThread());
146 mKeepAliveUntilComplete = nullptr;
149 void
150 Benchmark::Init()
152 MOZ_ASSERT(NS_IsMainThread());
153 gfxVars::Initialize();
154 gfxPrefs::GetSingleton();
155 MediaPrefs::GetSingleton();
158 BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState,
159 MediaDataDemuxer* aDemuxer)
160 : QueueObject(new TaskQueue(
161 GetMediaThreadPool(MediaThreadType::PLAYBACK),
162 "BenchmarkPlayback::QueueObject"))
163 , mMainThreadState(aMainThreadState)
164 , mDecoderTaskQueue(new TaskQueue(
165 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
166 "BenchmarkPlayback::mDecoderTaskQueue"))
167 , mDemuxer(aDemuxer)
168 , mSampleIndex(0)
169 , mFrameCount(0)
170 , mFinished(false)
171 , mDrained(false)
173 MOZ_ASSERT(static_cast<Benchmark*>(mMainThreadState)->OnThread());
176 void
177 BenchmarkPlayback::DemuxSamples()
179 MOZ_ASSERT(OnThread());
181 RefPtr<Benchmark> ref(mMainThreadState);
182 mDemuxer->Init()->Then(
183 Thread(), __func__,
184 [this, ref](nsresult aResult) {
185 MOZ_ASSERT(OnThread());
186 if (mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack)) {
187 mTrackDemuxer =
188 mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
189 } else if (mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack)) {
190 mTrackDemuxer =
191 mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
193 if (!mTrackDemuxer) {
194 Error(MediaResult(NS_ERROR_FAILURE, "Can't create track demuxer"));
195 return;
197 DemuxNextSample();
199 [this, ref](const MediaResult& aError) { Error(aError); });
202 void
203 BenchmarkPlayback::DemuxNextSample()
205 MOZ_ASSERT(OnThread());
207 RefPtr<Benchmark> ref(mMainThreadState);
208 RefPtr<MediaTrackDemuxer::SamplesPromise> promise = mTrackDemuxer->GetSamples();
209 promise->Then(
210 Thread(), __func__,
211 [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
212 mSamples.AppendElements(Move(aHolder->mSamples));
213 if (ref->mParameters.mStopAtFrame &&
214 mSamples.Length() == (size_t)ref->mParameters.mStopAtFrame.ref()) {
215 InitDecoder(Move(*mTrackDemuxer->GetInfo()));
216 } else {
217 Dispatch(NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
218 [this, ref]() { DemuxNextSample(); }));
221 [this, ref](const MediaResult& aError) {
222 switch (aError.Code()) {
223 case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
224 InitDecoder(Move(*mTrackDemuxer->GetInfo()));
225 break;
226 default:
227 Error(aError);
228 break;
233 void
234 BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
236 MOZ_ASSERT(OnThread());
238 RefPtr<PDMFactory> platform = new PDMFactory();
239 mDecoder = platform->CreateDecoder({ aInfo, mDecoderTaskQueue });
240 if (!mDecoder) {
241 Error(MediaResult(NS_ERROR_FAILURE, "Failed to create decoder"));
242 return;
244 RefPtr<Benchmark> ref(mMainThreadState);
245 mDecoder->Init()->Then(
246 Thread(), __func__,
247 [this, ref](TrackInfo::TrackType aTrackType) { InputExhausted(); },
248 [this, ref](const MediaResult& aError) { Error(aError); });
251 void
252 BenchmarkPlayback::FinalizeShutdown()
254 MOZ_ASSERT(OnThread());
256 MOZ_ASSERT(!mDecoder, "mDecoder must have been shutdown already");
257 mDecoderTaskQueue->BeginShutdown();
258 mDecoderTaskQueue->AwaitShutdownAndIdle();
259 mDecoderTaskQueue = nullptr;
261 if (mTrackDemuxer) {
262 mTrackDemuxer->Reset();
263 mTrackDemuxer->BreakCycles();
264 mTrackDemuxer = nullptr;
266 mDemuxer = nullptr;
268 RefPtr<Benchmark> ref(mMainThreadState);
269 Thread()->AsTaskQueue()->BeginShutdown()->Then(
270 ref->Thread(), __func__,
271 [ref]() { ref->Dispose(); },
272 []() { MOZ_CRASH("not reached"); });
275 void
276 BenchmarkPlayback::MainThreadShutdown()
278 MOZ_ASSERT(OnThread());
280 MOZ_ASSERT(!mFinished, "We've already shutdown");
282 mFinished = true;
284 if (mDecoder) {
285 RefPtr<Benchmark> ref(mMainThreadState);
286 mDecoder->Flush()->Then(
287 Thread(), __func__,
288 [ref, this]() {
289 mDecoder->Shutdown()->Then(
290 Thread(), __func__,
291 [ref, this]() {
292 FinalizeShutdown();
294 []() { MOZ_CRASH("not reached"); });
295 mDecoder = nullptr;
297 []() { MOZ_CRASH("not reached"); });
298 } else {
299 FinalizeShutdown();
303 void
304 BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
306 MOZ_ASSERT(OnThread());
307 MOZ_ASSERT(!mFinished);
309 RefPtr<Benchmark> ref(mMainThreadState);
310 mFrameCount += aResults.Length();
311 if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
312 mDecodeStartTime = Some(TimeStamp::Now());
314 TimeStamp now = TimeStamp::Now();
315 int32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
316 TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
317 if (((frames == ref->mParameters.mFramesToMeasure) && frames > 0) ||
318 elapsedTime >= ref->mParameters.mTimeout || mDrained) {
319 uint32_t decodeFps = frames / elapsedTime.ToSeconds();
320 MainThreadShutdown();
321 ref->Dispatch(
322 NS_NewRunnableFunction("BenchmarkPlayback::Output", [ref, decodeFps]() {
323 ref->ReturnResult(decodeFps);
324 }));
328 void
329 BenchmarkPlayback::Error(const MediaResult& aError)
331 MOZ_ASSERT(OnThread());
333 RefPtr<Benchmark> ref(mMainThreadState);
334 MainThreadShutdown();
335 ref->Dispatch(NS_NewRunnableFunction(
336 "BenchmarkPlayback::Error",
337 [ref, aError]() { ref->ReturnError(aError); }));
340 void
341 BenchmarkPlayback::InputExhausted()
343 MOZ_ASSERT(OnThread());
344 MOZ_ASSERT(!mFinished);
346 if (mSampleIndex >= mSamples.Length()) {
347 Error(MediaResult(NS_ERROR_FAILURE, "Nothing left to decode"));
348 return;
351 RefPtr<MediaRawData> sample = mSamples[mSampleIndex];
352 RefPtr<Benchmark> ref(mMainThreadState);
353 RefPtr<MediaDataDecoder::DecodePromise> p = mDecoder->Decode(sample);
355 mSampleIndex++;
356 if (mSampleIndex == mSamples.Length() && !ref->mParameters.mStopAtFrame) {
357 // Complete current frame decode then drain if still necessary.
358 p->Then(Thread(), __func__,
359 [ref, this](const MediaDataDecoder::DecodedData& aResults) {
360 Output(aResults);
361 if (!mFinished) {
362 mDecoder->Drain()->Then(
363 Thread(), __func__,
364 [ref, this](const MediaDataDecoder::DecodedData& aResults) {
365 mDrained = true;
366 Output(aResults);
367 MOZ_ASSERT(mFinished, "We must be done now");
369 [ref, this](const MediaResult& aError) { Error(aError); });
372 [ref, this](const MediaResult& aError) { Error(aError); });
373 } else {
374 if (mSampleIndex == mSamples.Length() && ref->mParameters.mStopAtFrame) {
375 mSampleIndex = 0;
377 // Continue decoding
378 p->Then(Thread(), __func__,
379 [ref, this](const MediaDataDecoder::DecodedData& aResults) {
380 Output(aResults);
381 if (!mFinished) {
382 InputExhausted();
385 [ref, this](const MediaResult& aError) { Error(aError); });
389 } // namespace mozilla