Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/Benchmark.cpp
Line
Count
Source (jump to first uncovered line)
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 "PDMFactory.h"
12
#include "VideoUtils.h"
13
#include "WebMDemuxer.h"
14
#include "gfxPrefs.h"
15
#include "mozilla/AbstractThread.h"
16
#include "mozilla/Preferences.h"
17
#include "mozilla/SharedThreadPool.h"
18
#include "mozilla/StaticMutex.h"
19
#include "mozilla/StaticPrefs.h"
20
#include "mozilla/SystemGroup.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 "nsIGfxInfo.h"
26
27
#ifndef MOZ_WIDGET_ANDROID
28
#include "WebMSample.h"
29
#endif
30
31
using namespace mozilla::gfx;
32
33
namespace mozilla {
34
35
// Update this version number to force re-running the benchmark. Such as when
36
// an improvement to FFVP9 or LIBVPX is deemed worthwhile.
37
const uint32_t VP9Benchmark::sBenchmarkVersionID = 5;
38
39
const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
40
const char* VP9Benchmark::sBenchmarkFpsVersionCheck = "media.benchmark.vp9.versioncheck";
41
bool VP9Benchmark::sHasRunTest = false;
42
43
// static
44
bool
45
VP9Benchmark::ShouldRun()
46
0
{
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 = services::GetGfxInfo();
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;
59
  }
60
  // Fall Through
61
#endif
62
  return true;
63
0
#endif
64
0
}
65
66
// static
67
uint32_t
68
VP9Benchmark::MediaBenchmarkVp9Fps()
69
0
{
70
0
  if (!ShouldRun()) {
71
0
    return 0;
72
0
  }
73
0
  return StaticPrefs::MediaBenchmarkVp9Threshold();
74
0
}
75
76
// static
77
bool
78
VP9Benchmark::IsVP9DecodeFast(bool aDefault)
79
0
{
80
#if defined(MOZ_WIDGET_ANDROID)
81
  return false;
82
#else
83
0
  if (!ShouldRun()) {
84
0
    return false;
85
0
  }
86
0
  static StaticMutex sMutex;
87
0
  uint32_t decodeFps = StaticPrefs::MediaBenchmarkVp9Fps();
88
0
  uint32_t hadRecentUpdate = StaticPrefs::MediaBenchmarkVp9Versioncheck();
89
0
  bool needBenchmark;
90
0
  {
91
0
    StaticMutexAutoLock lock(sMutex);
92
0
    needBenchmark = !sHasRunTest &&
93
0
                    (decodeFps == 0 || hadRecentUpdate != sBenchmarkVersionID);
94
0
    sHasRunTest = true;
95
0
  }
96
0
97
0
  if (needBenchmark) {
98
0
    RefPtr<WebMDemuxer> demuxer = new WebMDemuxer(
99
0
      new BufferMediaResource(sWebMSample, sizeof(sWebMSample)));
100
0
    RefPtr<Benchmark> estimiser = new Benchmark(
101
0
      demuxer,
102
0
      { StaticPrefs::MediaBenchmarkFrames(), // frames to measure
103
0
        1, // start benchmarking after decoding this frame.
104
0
        8, // loop after decoding that many frames.
105
0
        TimeDuration::FromMilliseconds(StaticPrefs::MediaBenchmarkTimeout()) });
106
0
    estimiser->Run()->Then(
107
0
      AbstractThread::MainThread(),
108
0
      __func__,
109
0
      [](uint32_t aDecodeFps) {
110
0
        if (XRE_IsContentProcess()) {
111
0
          dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
112
0
          if (contentChild) {
113
0
            contentChild->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"),
114
0
                                                    aDecodeFps);
115
0
          }
116
0
        } else {
117
0
          Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
118
0
          Preferences::SetUint(sBenchmarkFpsVersionCheck, sBenchmarkVersionID);
119
0
        }
120
0
        Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_VP9_BENCHMARK_FPS,
121
0
                              aDecodeFps);
122
0
      },
123
0
      []() {});
124
0
  }
125
0
126
0
  if (decodeFps == 0) {
127
0
    return aDefault;
128
0
  }
129
0
130
0
  return decodeFps >= StaticPrefs::MediaBenchmarkVp9Threshold();
131
0
#endif
132
0
}
133
134
Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
135
  : QueueObject(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
136
                              "Benchmark::QueueObject"))
137
  , mParameters(aParameters)
138
  , mKeepAliveUntilComplete(this)
139
  , mPlaybackState(this, aDemuxer)
140
0
{
141
0
  MOZ_COUNT_CTOR(Benchmark);
142
0
}
143
144
Benchmark::~Benchmark()
145
0
{
146
0
  MOZ_COUNT_DTOR(Benchmark);
147
0
}
148
149
RefPtr<Benchmark::BenchmarkPromise>
150
Benchmark::Run()
151
0
{
152
0
  RefPtr<Benchmark> self = this;
153
0
  return InvokeAsync(Thread(), __func__, [self] {
154
0
    RefPtr<BenchmarkPromise> p = self->mPromise.Ensure(__func__);
155
0
    self->mPlaybackState.Dispatch(NS_NewRunnableFunction(
156
0
      "Benchmark::Run", [self]() { self->mPlaybackState.DemuxSamples(); }));
157
0
    return p;
158
0
  });
159
0
}
160
161
void
162
Benchmark::ReturnResult(uint32_t aDecodeFps)
163
0
{
164
0
  MOZ_ASSERT(OnThread());
165
0
166
0
  mPromise.ResolveIfExists(aDecodeFps, __func__);
167
0
}
168
169
void
170
Benchmark::ReturnError(const MediaResult& aError)
171
0
{
172
0
  MOZ_ASSERT(OnThread());
173
0
174
0
  mPromise.RejectIfExists(aError, __func__);
175
0
}
176
177
void
178
Benchmark::Dispose()
179
0
{
180
0
  MOZ_ASSERT(OnThread());
181
0
182
0
  mKeepAliveUntilComplete = nullptr;
183
0
}
184
185
void
186
Benchmark::Init()
187
0
{
188
0
  MOZ_ASSERT(NS_IsMainThread());
189
0
  gfxVars::Initialize();
190
0
  gfxPrefs::GetSingleton();
191
0
}
192
193
BenchmarkPlayback::BenchmarkPlayback(Benchmark* aGlobalState,
194
                                     MediaDataDemuxer* aDemuxer)
195
  : QueueObject(new TaskQueue(
196
      GetMediaThreadPool(MediaThreadType::PLAYBACK),
197
      "BenchmarkPlayback::QueueObject"))
198
  , mGlobalState(aGlobalState)
199
  , mDecoderTaskQueue(new TaskQueue(
200
      GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
201
      "BenchmarkPlayback::mDecoderTaskQueue"))
202
  , mDemuxer(aDemuxer)
203
  , mSampleIndex(0)
204
  , mFrameCount(0)
205
  , mFinished(false)
206
  , mDrained(false)
207
0
{
208
0
}
209
210
void
211
BenchmarkPlayback::DemuxSamples()
212
0
{
213
0
  MOZ_ASSERT(OnThread());
214
0
215
0
  RefPtr<Benchmark> ref(mGlobalState);
216
0
  mDemuxer->Init()->Then(
217
0
    Thread(), __func__,
218
0
    [this, ref](nsresult aResult) {
219
0
      MOZ_ASSERT(OnThread());
220
0
      if (mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack)) {
221
0
        mTrackDemuxer =
222
0
          mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
223
0
      } else if (mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack)) {
224
0
        mTrackDemuxer =
225
0
          mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
226
0
      }
227
0
      if (!mTrackDemuxer) {
228
0
        Error(MediaResult(NS_ERROR_FAILURE, "Can't create track demuxer"));
229
0
        return;
230
0
      }
231
0
      DemuxNextSample();
232
0
    },
233
0
    [this, ref](const MediaResult& aError) { Error(aError); });
234
0
}
235
236
void
237
BenchmarkPlayback::DemuxNextSample()
238
0
{
239
0
  MOZ_ASSERT(OnThread());
240
0
241
0
  RefPtr<Benchmark> ref(mGlobalState);
242
0
  RefPtr<MediaTrackDemuxer::SamplesPromise> promise = mTrackDemuxer->GetSamples();
243
0
  promise->Then(
244
0
    Thread(), __func__,
245
0
    [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
246
0
      mSamples.AppendElements(std::move(aHolder->mSamples));
247
0
      if (ref->mParameters.mStopAtFrame &&
248
0
          mSamples.Length() == ref->mParameters.mStopAtFrame.ref()) {
249
0
        InitDecoder(std::move(*mTrackDemuxer->GetInfo()));
250
0
      } else {
251
0
        Dispatch(NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
252
0
                                        [this, ref]() { DemuxNextSample(); }));
253
0
      }
254
0
    },
255
0
    [this, ref](const MediaResult& aError) {
256
0
      switch (aError.Code()) {
257
0
        case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
258
0
          InitDecoder(std::move(*mTrackDemuxer->GetInfo()));
259
0
          break;
260
0
        default:
261
0
          Error(aError);
262
0
          break;
263
0
      }
264
0
    });
265
0
}
266
267
void
268
BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
269
0
{
270
0
  MOZ_ASSERT(OnThread());
271
0
272
0
  RefPtr<PDMFactory> platform = new PDMFactory();
273
0
  mDecoder = platform->CreateDecoder({ aInfo, mDecoderTaskQueue });
274
0
  if (!mDecoder) {
275
0
    Error(MediaResult(NS_ERROR_FAILURE, "Failed to create decoder"));
276
0
    return;
277
0
  }
278
0
  RefPtr<Benchmark> ref(mGlobalState);
279
0
  mDecoder->Init()->Then(
280
0
    Thread(), __func__,
281
0
    [this, ref](TrackInfo::TrackType aTrackType) { InputExhausted(); },
282
0
    [this, ref](const MediaResult& aError) { Error(aError); });
283
0
}
284
285
void
286
BenchmarkPlayback::FinalizeShutdown()
287
0
{
288
0
  MOZ_ASSERT(OnThread());
289
0
290
0
  MOZ_ASSERT(!mDecoder, "mDecoder must have been shutdown already");
291
0
  mDecoderTaskQueue->BeginShutdown();
292
0
  mDecoderTaskQueue->AwaitShutdownAndIdle();
293
0
  mDecoderTaskQueue = nullptr;
294
0
295
0
  if (mTrackDemuxer) {
296
0
    mTrackDemuxer->Reset();
297
0
    mTrackDemuxer->BreakCycles();
298
0
    mTrackDemuxer = nullptr;
299
0
  }
300
0
  mDemuxer = nullptr;
301
0
302
0
  RefPtr<Benchmark> ref(mGlobalState);
303
0
  Thread()->AsTaskQueue()->BeginShutdown()->Then(
304
0
    ref->Thread(), __func__,
305
0
    [ref]() { ref->Dispose(); },
306
0
    []() { MOZ_CRASH("not reached"); });
307
0
}
308
309
void
310
BenchmarkPlayback::GlobalShutdown()
311
0
{
312
0
  MOZ_ASSERT(OnThread());
313
0
314
0
  MOZ_ASSERT(!mFinished, "We've already shutdown");
315
0
316
0
  mFinished = true;
317
0
318
0
  if (mDecoder) {
319
0
    RefPtr<Benchmark> ref(mGlobalState);
320
0
    mDecoder->Flush()->Then(
321
0
      Thread(), __func__,
322
0
      [ref, this]() {
323
0
        mDecoder->Shutdown()->Then(
324
0
          Thread(), __func__,
325
0
          [ref, this]() {
326
0
            FinalizeShutdown();
327
0
          },
328
0
          []() { MOZ_CRASH("not reached"); });
329
0
        mDecoder = nullptr;
330
0
      },
331
0
      []() { MOZ_CRASH("not reached"); });
332
0
  } else {
333
0
    FinalizeShutdown();
334
0
  }
335
0
}
336
337
void
338
BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData& aResults)
339
0
{
340
0
  MOZ_ASSERT(OnThread());
341
0
  MOZ_ASSERT(!mFinished);
342
0
343
0
  RefPtr<Benchmark> ref(mGlobalState);
344
0
  mFrameCount += aResults.Length();
345
0
  if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
346
0
    mDecodeStartTime = Some(TimeStamp::Now());
347
0
  }
348
0
  TimeStamp now = TimeStamp::Now();
349
0
  uint32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
350
0
  TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
351
0
  if (((frames == ref->mParameters.mFramesToMeasure) &&
352
0
       mFrameCount > ref->mParameters.mStartupFrame && frames > 0) ||
353
0
      elapsedTime >= ref->mParameters.mTimeout || mDrained) {
354
0
    uint32_t decodeFps = frames / elapsedTime.ToSeconds();
355
0
    GlobalShutdown();
356
0
    ref->Dispatch(
357
0
      NS_NewRunnableFunction("BenchmarkPlayback::Output", [ref, decodeFps]() {
358
0
        ref->ReturnResult(decodeFps);
359
0
      }));
360
0
  }
361
0
}
362
363
void
364
BenchmarkPlayback::Error(const MediaResult& aError)
365
0
{
366
0
  MOZ_ASSERT(OnThread());
367
0
368
0
  RefPtr<Benchmark> ref(mGlobalState);
369
0
  GlobalShutdown();
370
0
  ref->Dispatch(NS_NewRunnableFunction(
371
0
    "BenchmarkPlayback::Error",
372
0
    [ref, aError]() { ref->ReturnError(aError); }));
373
0
}
374
375
void
376
BenchmarkPlayback::InputExhausted()
377
0
{
378
0
  MOZ_ASSERT(OnThread());
379
0
  MOZ_ASSERT(!mFinished);
380
0
381
0
  if (mSampleIndex >= mSamples.Length()) {
382
0
    Error(MediaResult(NS_ERROR_FAILURE, "Nothing left to decode"));
383
0
    return;
384
0
  }
385
0
386
0
  RefPtr<MediaRawData> sample = mSamples[mSampleIndex];
387
0
  RefPtr<Benchmark> ref(mGlobalState);
388
0
  RefPtr<MediaDataDecoder::DecodePromise> p = mDecoder->Decode(sample);
389
0
390
0
  mSampleIndex++;
391
0
  if (mSampleIndex == mSamples.Length() && !ref->mParameters.mStopAtFrame) {
392
0
    // Complete current frame decode then drain if still necessary.
393
0
    p->Then(Thread(), __func__,
394
0
            [ref, this](const MediaDataDecoder::DecodedData& aResults) {
395
0
              Output(aResults);
396
0
              if (!mFinished) {
397
0
                mDecoder->Drain()->Then(
398
0
                  Thread(), __func__,
399
0
                  [ref, this](const MediaDataDecoder::DecodedData& aResults) {
400
0
                    mDrained = true;
401
0
                    Output(aResults);
402
0
                    MOZ_ASSERT(mFinished, "We must be done now");
403
0
                  },
404
0
                  [ref, this](const MediaResult& aError) { Error(aError); });
405
0
              }
406
0
            },
407
0
            [ref, this](const MediaResult& aError) { Error(aError); });
408
0
  } else {
409
0
    if (mSampleIndex == mSamples.Length() && ref->mParameters.mStopAtFrame) {
410
0
      mSampleIndex = 0;
411
0
    }
412
0
    // Continue decoding
413
0
    p->Then(Thread(), __func__,
414
0
            [ref, this](const MediaDataDecoder::DecodedData& aResults) {
415
0
              Output(aResults);
416
0
              if (!mFinished) {
417
0
                InputExhausted();
418
0
              }
419
0
            },
420
0
            [ref, this](const MediaResult& aError) { Error(aError); });
421
0
  }
422
0
}
423
424
} // namespace mozilla