Line data Source code
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/. */
6 :
7 : #include "Benchmark.h"
8 :
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/TaskQueue.h"
20 : #include "mozilla/Telemetry.h"
21 : #include "mozilla/dom/ContentChild.h"
22 : #include "mozilla/gfx/gfxVars.h"
23 :
24 : #ifndef MOZ_WIDGET_ANDROID
25 : #include "WebMSample.h"
26 : #endif
27 :
28 : using namespace mozilla::gfx;
29 :
30 : namespace mozilla {
31 :
32 : // Update this version number to force re-running the benchmark. Such as when
33 : // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
34 : const uint32_t VP9Benchmark::sBenchmarkVersionID = 2;
35 :
36 : const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
37 : const char* VP9Benchmark::sBenchmarkFpsVersionCheck = "media.benchmark.vp9.versioncheck";
38 : bool VP9Benchmark::sHasRunTest = false;
39 :
40 : // static
41 : bool
42 0 : VP9Benchmark::IsVP9DecodeFast()
43 : {
44 0 : MOZ_ASSERT(NS_IsMainThread());
45 :
46 : #ifdef MOZ_WIDGET_ANDROID
47 : return false;
48 : #else
49 0 : bool hasPref = Preferences::HasUserValue(sBenchmarkFpsPref);
50 0 : uint32_t hadRecentUpdate = Preferences::GetUint(sBenchmarkFpsVersionCheck, 0U);
51 :
52 0 : if (!sHasRunTest && (!hasPref || hadRecentUpdate != sBenchmarkVersionID)) {
53 0 : sHasRunTest = true;
54 :
55 : RefPtr<WebMDemuxer> demuxer = new WebMDemuxer(
56 0 : new BufferMediaResource(sWebMSample, sizeof(sWebMSample), nullptr));
57 : RefPtr<Benchmark> estimiser =
58 : new Benchmark(demuxer,
59 : {
60 : Preferences::GetInt("media.benchmark.frames", 300), // frames to measure
61 : 1, // start benchmarking after decoding this frame.
62 : 8, // loop after decoding that many frames.
63 0 : TimeDuration::FromMilliseconds(
64 0 : Preferences::GetUint("media.benchmark.timeout", 1000))
65 0 : });
66 0 : estimiser->Run()->Then(
67 0 : SystemGroup::AbstractMainThreadFor(TaskCategory::Other), __func__,
68 0 : [](uint32_t aDecodeFps) {
69 0 : if (XRE_IsContentProcess()) {
70 0 : dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
71 0 : if (contentChild) {
72 0 : contentChild->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"),
73 0 : aDecodeFps);
74 : }
75 : } else {
76 0 : Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
77 0 : Preferences::SetUint(sBenchmarkFpsVersionCheck, sBenchmarkVersionID);
78 : }
79 0 : Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_VP9_BENCHMARK_FPS, aDecodeFps);
80 0 : },
81 0 : []() { });
82 : }
83 :
84 0 : if (!hasPref) {
85 0 : return false;
86 : }
87 :
88 0 : uint32_t decodeFps = Preferences::GetUint(sBenchmarkFpsPref);
89 : uint32_t threshold =
90 0 : Preferences::GetUint("media.benchmark.vp9.threshold", 150);
91 :
92 0 : return decodeFps >= threshold;
93 : #endif
94 : }
95 :
96 0 : Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
97 : : QueueObject(AbstractThread::MainThread())
98 : , mParameters(aParameters)
99 : , mKeepAliveUntilComplete(this)
100 0 : , mPlaybackState(this, aDemuxer)
101 : {
102 0 : MOZ_COUNT_CTOR(Benchmark);
103 0 : MOZ_ASSERT(Thread(), "Must be run in task queue");
104 0 : }
105 :
106 0 : Benchmark::~Benchmark()
107 : {
108 0 : MOZ_COUNT_DTOR(Benchmark);
109 0 : }
110 :
111 : RefPtr<Benchmark::BenchmarkPromise>
112 0 : Benchmark::Run()
113 : {
114 0 : MOZ_ASSERT(OnThread());
115 :
116 0 : RefPtr<BenchmarkPromise> p = mPromise.Ensure(__func__);
117 0 : RefPtr<Benchmark> self = this;
118 0 : mPlaybackState.Dispatch(NS_NewRunnableFunction(
119 0 : "Benchmark::Run", [self]() { self->mPlaybackState.DemuxSamples(); }));
120 0 : return p;
121 : }
122 :
123 : void
124 0 : Benchmark::ReturnResult(uint32_t aDecodeFps)
125 : {
126 0 : MOZ_ASSERT(OnThread());
127 :
128 0 : mPromise.ResolveIfExists(aDecodeFps, __func__);
129 0 : }
130 :
131 : void
132 0 : Benchmark::Dispose()
133 : {
134 0 : MOZ_ASSERT(OnThread());
135 :
136 0 : mKeepAliveUntilComplete = nullptr;
137 0 : mPromise.RejectIfExists(false, __func__);
138 0 : }
139 :
140 : void
141 0 : Benchmark::Init()
142 : {
143 0 : MOZ_ASSERT(NS_IsMainThread());
144 0 : gfxVars::Initialize();
145 0 : gfxPrefs::GetSingleton();
146 0 : MediaPrefs::GetSingleton();
147 0 : }
148 :
149 0 : BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState,
150 0 : MediaDataDemuxer* aDemuxer)
151 : : QueueObject(new TaskQueue(
152 0 : GetMediaThreadPool(MediaThreadType::PLAYBACK),
153 0 : "BenchmarkPlayback::QueueObject"))
154 : , mMainThreadState(aMainThreadState)
155 : , mDecoderTaskQueue(new TaskQueue(
156 0 : GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
157 0 : "BenchmarkPlayback::mDecoderTaskQueue"))
158 : , mDemuxer(aDemuxer)
159 : , mSampleIndex(0)
160 : , mFrameCount(0)
161 : , mFinished(false)
162 0 : , mDrained(false)
163 : {
164 0 : MOZ_ASSERT(static_cast<Benchmark*>(mMainThreadState)->OnThread());
165 0 : }
166 :
167 : void
168 0 : BenchmarkPlayback::DemuxSamples()
169 : {
170 0 : MOZ_ASSERT(OnThread());
171 :
172 0 : RefPtr<Benchmark> ref(mMainThreadState);
173 0 : mDemuxer->Init()->Then(
174 0 : Thread(), __func__,
175 0 : [this, ref](nsresult aResult) {
176 0 : MOZ_ASSERT(OnThread());
177 : mTrackDemuxer =
178 0 : mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
179 0 : if (!mTrackDemuxer) {
180 0 : MainThreadShutdown();
181 0 : return;
182 : }
183 0 : DemuxNextSample();
184 : },
185 0 : [this, ref](const MediaResult& aError) { MainThreadShutdown(); });
186 0 : }
187 :
188 : void
189 0 : BenchmarkPlayback::DemuxNextSample()
190 : {
191 0 : MOZ_ASSERT(OnThread());
192 :
193 0 : RefPtr<Benchmark> ref(mMainThreadState);
194 0 : RefPtr<MediaTrackDemuxer::SamplesPromise> promise = mTrackDemuxer->GetSamples();
195 : promise->Then(
196 0 : Thread(), __func__,
197 0 : [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
198 0 : mSamples.AppendElements(Move(aHolder->mSamples));
199 0 : if (ref->mParameters.mStopAtFrame
200 0 : && mSamples.Length() == (size_t)ref->mParameters.mStopAtFrame.ref()) {
201 0 : InitDecoder(Move(*mTrackDemuxer->GetInfo()));
202 : } else {
203 0 : Dispatch(NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
204 0 : [this, ref]() { DemuxNextSample(); }));
205 : }
206 0 : },
207 0 : [this, ref](const MediaResult& aError) {
208 0 : switch (aError.Code()) {
209 : case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
210 0 : InitDecoder(Move(*mTrackDemuxer->GetInfo()));
211 0 : break;
212 : default:
213 0 : MainThreadShutdown();
214 : }
215 0 : });
216 0 : }
217 :
218 : void
219 0 : BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
220 : {
221 0 : MOZ_ASSERT(OnThread());
222 :
223 0 : RefPtr<PDMFactory> platform = new PDMFactory();
224 0 : mDecoder = platform->CreateDecoder({ aInfo, mDecoderTaskQueue });
225 0 : if (!mDecoder) {
226 0 : MainThreadShutdown();
227 0 : return;
228 : }
229 0 : RefPtr<Benchmark> ref(mMainThreadState);
230 0 : mDecoder->Init()->Then(
231 0 : Thread(), __func__,
232 0 : [this, ref](TrackInfo::TrackType aTrackType) {
233 0 : InputExhausted();
234 0 : },
235 0 : [this, ref](const MediaResult& aError) {
236 0 : MainThreadShutdown();
237 0 : });
238 : }
239 :
240 : void
241 0 : BenchmarkPlayback::MainThreadShutdown()
242 : {
243 0 : MOZ_ASSERT(OnThread());
244 :
245 0 : if (mFinished) {
246 : // Nothing more to do.
247 0 : return;
248 : }
249 0 : mFinished = true;
250 :
251 0 : if (mDecoder) {
252 0 : RefPtr<Benchmark> ref(mMainThreadState);
253 0 : mDecoder->Flush()->Then(
254 0 : Thread(), __func__,
255 0 : [ref, this]() {
256 0 : mDecoder->Shutdown()->Then(
257 0 : Thread(), __func__,
258 0 : [ref, this]() {
259 0 : mDecoderTaskQueue->BeginShutdown();
260 0 : mDecoderTaskQueue->AwaitShutdownAndIdle();
261 0 : mDecoderTaskQueue = nullptr;
262 :
263 0 : if (mTrackDemuxer) {
264 0 : mTrackDemuxer->Reset();
265 0 : mTrackDemuxer->BreakCycles();
266 0 : mTrackDemuxer = nullptr;
267 : }
268 :
269 0 : Thread()->AsTaskQueue()->BeginShutdown()->Then(
270 0 : ref->Thread(), __func__,
271 0 : [ref]() { ref->Dispose(); },
272 0 : []() { MOZ_CRASH("not reached"); });
273 0 : },
274 0 : []() { MOZ_CRASH("not reached"); });
275 0 : mDecoder = nullptr;
276 0 : },
277 0 : []() { MOZ_CRASH("not reached"); });
278 : }
279 : }
280 :
281 : void
282 0 : BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
283 : {
284 0 : MOZ_ASSERT(OnThread());
285 0 : RefPtr<Benchmark> ref(mMainThreadState);
286 0 : mFrameCount += aResults.Length();
287 0 : if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
288 0 : mDecodeStartTime = Some(TimeStamp::Now());
289 : }
290 0 : TimeStamp now = TimeStamp::Now();
291 0 : int32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
292 0 : TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
293 0 : if (!mFinished
294 0 : && (((frames == ref->mParameters.mFramesToMeasure) && frames > 0)
295 0 : || elapsedTime >= ref->mParameters.mTimeout
296 0 : || mDrained)) {
297 0 : uint32_t decodeFps = frames / elapsedTime.ToSeconds();
298 0 : MainThreadShutdown();
299 0 : ref->Dispatch(
300 0 : NS_NewRunnableFunction("BenchmarkPlayback::Output", [ref, decodeFps]() {
301 0 : ref->ReturnResult(decodeFps);
302 0 : }));
303 : }
304 0 : }
305 :
306 : void
307 0 : BenchmarkPlayback::InputExhausted()
308 : {
309 0 : MOZ_ASSERT(OnThread());
310 0 : if (mFinished || mSampleIndex >= mSamples.Length()) {
311 0 : return;
312 : }
313 0 : RefPtr<Benchmark> ref(mMainThreadState);
314 0 : mDecoder->Decode(mSamples[mSampleIndex])
315 0 : ->Then(Thread(), __func__,
316 0 : [ref, this](const MediaDataDecoder::DecodedData& aResults) {
317 0 : Output(aResults);
318 0 : InputExhausted();
319 0 : },
320 0 : [ref, this](const MediaResult& aError) { MainThreadShutdown(); });
321 0 : mSampleIndex++;
322 0 : if (mSampleIndex == mSamples.Length()) {
323 0 : if (ref->mParameters.mStopAtFrame) {
324 0 : mSampleIndex = 0;
325 : } else {
326 0 : mDecoder->Drain()->Then(
327 0 : Thread(), __func__,
328 0 : [ref, this](const MediaDataDecoder::DecodedData& aResults) {
329 0 : mDrained = true;
330 0 : Output(aResults);
331 0 : },
332 0 : [ref, this](const MediaResult& aError) { MainThreadShutdown(); });
333 : }
334 : }
335 : }
336 :
337 : } // namespace mozilla
|