Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / dom / media / Benchmark.cpp
blob37bb2dcc2266043f896f1889ee3c1b84e2a28c78
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 "MediaDataDecoderProxy.h"
12 #include "PDMFactory.h"
13 #include "VideoUtils.h"
14 #include "WebMDemuxer.h"
15 #include "mozilla/AbstractThread.h"
16 #include "mozilla/Components.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/StaticMutex.h"
20 #include "mozilla/StaticPrefs_media.h"
21 #include "mozilla/TaskQueue.h"
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/dom/ContentChild.h"
24 #include "mozilla/gfx/gfxVars.h"
25 #include "nsGkAtoms.h"
26 #include "nsIGfxInfo.h"
28 #ifndef MOZ_WIDGET_ANDROID
29 # include "WebMSample.h"
30 #endif
32 using namespace mozilla::gfx;
34 namespace mozilla {
36 // Update this version number to force re-running the benchmark. Such as when
37 // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
38 const uint32_t VP9Benchmark::sBenchmarkVersionID = 5;
40 const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
41 const char* VP9Benchmark::sBenchmarkFpsVersionCheck =
42 "media.benchmark.vp9.versioncheck";
43 bool VP9Benchmark::sHasRunTest = false;
45 // static
46 bool VP9Benchmark::ShouldRun() {
47 #if defined(MOZ_WIDGET_ANDROID)
48 // Assume that the VP9 software decoder will always be too slow.
49 return false;
50 #else
51 # if defined(MOZ_APPLEMEDIA)
52 const nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
53 nsString vendorID, deviceID;
54 gfxInfo->GetAdapterVendorID(vendorID);
55 // We won't run the VP9 benchmark on mac using an Intel GPU as performance are
56 // poor, see bug 1404042.
57 if (vendorID.EqualsLiteral("0x8086")) {
58 return false;
60 // Fall Through
61 # endif
62 return true;
63 #endif
66 // static
67 uint32_t VP9Benchmark::MediaBenchmarkVp9Fps() {
68 if (!ShouldRun()) {
69 return 0;
71 return StaticPrefs::media_benchmark_vp9_fps();
74 // static
75 bool VP9Benchmark::IsVP9DecodeFast(bool aDefault) {
76 #if defined(MOZ_WIDGET_ANDROID)
77 return false;
78 #else
79 if (!ShouldRun()) {
80 return false;
82 static StaticMutex sMutex MOZ_UNANNOTATED;
83 uint32_t decodeFps = StaticPrefs::media_benchmark_vp9_fps();
84 uint32_t hadRecentUpdate = StaticPrefs::media_benchmark_vp9_versioncheck();
85 bool needBenchmark;
87 StaticMutexAutoLock lock(sMutex);
88 needBenchmark = !sHasRunTest &&
89 (decodeFps == 0 || hadRecentUpdate != sBenchmarkVersionID);
90 sHasRunTest = true;
93 if (needBenchmark) {
94 RefPtr<WebMDemuxer> demuxer = new WebMDemuxer(
95 new BufferMediaResource(sWebMSample, sizeof(sWebMSample)));
96 RefPtr<Benchmark> estimiser = new Benchmark(
97 demuxer,
98 {StaticPrefs::media_benchmark_frames(), // frames to measure
99 1, // start benchmarking after decoding this frame.
100 8, // loop after decoding that many frames.
101 TimeDuration::FromMilliseconds(
102 StaticPrefs::media_benchmark_timeout())});
103 estimiser->Run()->Then(
104 AbstractThread::MainThread(), __func__,
105 [](uint32_t aDecodeFps) {
106 if (XRE_IsContentProcess()) {
107 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
108 if (contentChild) {
109 contentChild->SendNotifyBenchmarkResult(u"VP9"_ns, aDecodeFps);
111 } else {
112 Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
113 Preferences::SetUint(sBenchmarkFpsVersionCheck,
114 sBenchmarkVersionID);
117 []() {});
120 if (decodeFps == 0) {
121 return aDefault;
124 return decodeFps >= StaticPrefs::media_benchmark_vp9_threshold();
125 #endif
128 Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
129 : QueueObject(
130 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
131 "Benchmark::QueueObject")),
132 mParameters(aParameters),
133 mKeepAliveUntilComplete(this),
134 mPlaybackState(this, aDemuxer) {
135 MOZ_COUNT_CTOR(Benchmark);
138 Benchmark::~Benchmark() { MOZ_COUNT_DTOR(Benchmark); }
140 RefPtr<Benchmark::BenchmarkPromise> Benchmark::Run() {
141 RefPtr<Benchmark> self = this;
142 return InvokeAsync(Thread(), __func__, [self] {
143 RefPtr<BenchmarkPromise> p = self->mPromise.Ensure(__func__);
144 self->mPlaybackState.Dispatch(NS_NewRunnableFunction(
145 "Benchmark::Run", [self]() { self->mPlaybackState.DemuxSamples(); }));
146 return p;
150 void Benchmark::ReturnResult(uint32_t aDecodeFps) {
151 MOZ_ASSERT(OnThread());
153 mPromise.ResolveIfExists(aDecodeFps, __func__);
156 void Benchmark::ReturnError(const MediaResult& aError) {
157 MOZ_ASSERT(OnThread());
159 mPromise.RejectIfExists(aError, __func__);
162 void Benchmark::Dispose() {
163 MOZ_ASSERT(OnThread());
165 mKeepAliveUntilComplete = nullptr;
168 void Benchmark::Init() {
169 MOZ_ASSERT(NS_IsMainThread());
170 gfxVars::Initialize();
173 BenchmarkPlayback::BenchmarkPlayback(Benchmark* aGlobalState,
174 MediaDataDemuxer* aDemuxer)
175 : QueueObject(
176 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
177 "BenchmarkPlayback::QueueObject")),
178 mGlobalState(aGlobalState),
179 mDecoderTaskQueue(TaskQueue::Create(
180 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
181 "BenchmarkPlayback::mDecoderTaskQueue")),
182 mDemuxer(aDemuxer),
183 mSampleIndex(0),
184 mFrameCount(0),
185 mFinished(false),
186 mDrained(false) {}
188 void BenchmarkPlayback::DemuxSamples() {
189 MOZ_ASSERT(OnThread());
191 RefPtr<Benchmark> ref(mGlobalState);
192 mDemuxer->Init()->Then(
193 Thread(), __func__,
194 [this, ref](nsresult aResult) {
195 MOZ_ASSERT(OnThread());
196 if (mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack)) {
197 mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
198 } else if (mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack)) {
199 mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
201 if (!mTrackDemuxer) {
202 Error(MediaResult(NS_ERROR_FAILURE, "Can't create track demuxer"));
203 return;
205 DemuxNextSample();
207 [this, ref](const MediaResult& aError) { Error(aError); });
210 void BenchmarkPlayback::DemuxNextSample() {
211 MOZ_ASSERT(OnThread());
213 RefPtr<Benchmark> ref(mGlobalState);
214 RefPtr<MediaTrackDemuxer::SamplesPromise> promise =
215 mTrackDemuxer->GetSamples();
216 promise->Then(
217 Thread(), __func__,
218 [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
219 mSamples.AppendElements(std::move(aHolder->GetMovableSamples()));
220 if (ref->mParameters.mStopAtFrame &&
221 mSamples.Length() == ref->mParameters.mStopAtFrame.ref()) {
222 InitDecoder(mTrackDemuxer->GetInfo());
223 } else {
224 Dispatch(
225 NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
226 [this, ref]() { DemuxNextSample(); }));
229 [this, ref](const MediaResult& aError) {
230 switch (aError.Code()) {
231 case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
232 InitDecoder(mTrackDemuxer->GetInfo());
233 break;
234 default:
235 Error(aError);
236 break;
241 void BenchmarkPlayback::InitDecoder(UniquePtr<TrackInfo>&& aInfo) {
242 MOZ_ASSERT(OnThread());
244 if (!aInfo) {
245 Error(MediaResult(NS_ERROR_FAILURE, "Invalid TrackInfo"));
246 return;
249 RefPtr<PDMFactory> platform = new PDMFactory();
250 mInfo = std::move(aInfo);
251 RefPtr<Benchmark> ref(mGlobalState);
252 platform->CreateDecoder(CreateDecoderParams{*mInfo})
253 ->Then(
254 Thread(), __func__,
255 [this, ref](RefPtr<MediaDataDecoder>&& aDecoder) {
256 mDecoder = new MediaDataDecoderProxy(
257 aDecoder.forget(), do_AddRef(mDecoderTaskQueue.get()));
258 mDecoder->Init()->Then(
259 Thread(), __func__,
260 [this, ref](TrackInfo::TrackType aTrackType) {
261 InputExhausted();
263 [this, ref](const MediaResult& aError) { Error(aError); });
265 [this, ref](const MediaResult& aError) { Error(aError); });
268 void BenchmarkPlayback::FinalizeShutdown() {
269 MOZ_ASSERT(OnThread());
271 MOZ_ASSERT(mFinished, "GlobalShutdown must have been run");
272 MOZ_ASSERT(!mDecoder, "mDecoder must have been shutdown already");
273 MOZ_ASSERT(!mDemuxer, "mDemuxer must have been shutdown already");
274 MOZ_DIAGNOSTIC_ASSERT(mDecoderTaskQueue->IsEmpty());
275 mDecoderTaskQueue = nullptr;
277 RefPtr<Benchmark> ref(mGlobalState);
278 ref->Thread()->Dispatch(NS_NewRunnableFunction(
279 "BenchmarkPlayback::FinalizeShutdown", [ref]() { ref->Dispose(); }));
282 void BenchmarkPlayback::GlobalShutdown() {
283 MOZ_ASSERT(OnThread());
285 MOZ_ASSERT(!mFinished, "We've already shutdown");
287 mFinished = true;
289 if (mTrackDemuxer) {
290 mTrackDemuxer->Reset();
291 mTrackDemuxer->BreakCycles();
292 mTrackDemuxer = nullptr;
294 mDemuxer = nullptr;
296 if (mDecoder) {
297 RefPtr<Benchmark> ref(mGlobalState);
298 mDecoder->Flush()->Then(
299 Thread(), __func__,
300 [ref, this]() {
301 mDecoder->Shutdown()->Then(
302 Thread(), __func__, [ref, this]() { FinalizeShutdown(); },
303 []() { MOZ_CRASH("not reached"); });
304 mDecoder = nullptr;
305 mInfo = nullptr;
307 []() { MOZ_CRASH("not reached"); });
308 } else {
309 FinalizeShutdown();
313 void BenchmarkPlayback::Output(MediaDataDecoder::DecodedData&& aResults) {
314 MOZ_ASSERT(OnThread());
315 MOZ_ASSERT(!mFinished);
317 RefPtr<Benchmark> ref(mGlobalState);
318 mFrameCount += aResults.Length();
319 if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
320 mDecodeStartTime = Some(TimeStamp::Now());
322 TimeStamp now = TimeStamp::Now();
323 uint32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
324 TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
325 if (((frames == ref->mParameters.mFramesToMeasure) &&
326 mFrameCount > ref->mParameters.mStartupFrame && frames > 0) ||
327 elapsedTime >= ref->mParameters.mTimeout || mDrained) {
328 uint32_t decodeFps = frames / elapsedTime.ToSeconds();
329 GlobalShutdown();
330 ref->Dispatch(NS_NewRunnableFunction(
331 "BenchmarkPlayback::Output",
332 [ref, decodeFps]() { ref->ReturnResult(decodeFps); }));
336 void BenchmarkPlayback::Error(const MediaResult& aError) {
337 MOZ_ASSERT(OnThread());
339 RefPtr<Benchmark> ref(mGlobalState);
340 GlobalShutdown();
341 ref->Dispatch(
342 NS_NewRunnableFunction("BenchmarkPlayback::Error",
343 [ref, aError]() { ref->ReturnError(aError); }));
346 void BenchmarkPlayback::InputExhausted() {
347 MOZ_ASSERT(OnThread());
348 MOZ_ASSERT(!mFinished);
350 if (mSampleIndex >= mSamples.Length()) {
351 Error(MediaResult(NS_ERROR_FAILURE, "Nothing left to decode"));
352 return;
355 RefPtr<MediaRawData> sample = mSamples[mSampleIndex];
356 RefPtr<Benchmark> ref(mGlobalState);
357 RefPtr<MediaDataDecoder::DecodePromise> p = mDecoder->Decode(sample);
359 mSampleIndex++;
360 if (mSampleIndex == mSamples.Length() && !ref->mParameters.mStopAtFrame) {
361 // Complete current frame decode then drain if still necessary.
362 p->Then(
363 Thread(), __func__,
364 [ref, this](MediaDataDecoder::DecodedData&& aResults) {
365 Output(std::move(aResults));
366 if (!mFinished) {
367 mDecoder->Drain()->Then(
368 Thread(), __func__,
369 [ref, this](MediaDataDecoder::DecodedData&& aResults) {
370 mDrained = true;
371 Output(std::move(aResults));
372 MOZ_ASSERT(mFinished, "We must be done now");
374 [ref, this](const MediaResult& aError) { Error(aError); });
377 [ref, this](const MediaResult& aError) { Error(aError); });
378 } else {
379 if (mSampleIndex == mSamples.Length() && ref->mParameters.mStopAtFrame) {
380 mSampleIndex = 0;
382 // Continue decoding
383 p->Then(
384 Thread(), __func__,
385 [ref, this](MediaDataDecoder::DecodedData&& aResults) {
386 Output(std::move(aResults));
387 if (!mFinished) {
388 InputExhausted();
391 [ref, this](const MediaResult& aError) { Error(aError); });
395 } // namespace mozilla