Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/ipc/CompositorVsyncScheduler.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "mozilla/layers/CompositorVsyncScheduler.h"
8
9
#include <stdio.h>                      // for fprintf, stdout
10
#include <stdint.h>                     // for uint64_t
11
#include "base/task.h"                  // for CancelableTask, etc
12
#include "base/thread.h"                // for Thread
13
#include "gfxPlatform.h"                // for gfxPlatform
14
#ifdef MOZ_WIDGET_GTK
15
#include "gfxPlatformGtk.h"             // for gfxPlatform
16
#endif
17
#include "gfxPrefs.h"                   // for gfxPrefs
18
#include "mozilla/AutoRestore.h"        // for AutoRestore
19
#include "mozilla/DebugOnly.h"          // for DebugOnly
20
#include "mozilla/gfx/2D.h"             // for DrawTarget
21
#include "mozilla/gfx/Point.h"          // for IntSize
22
#include "mozilla/gfx/Rect.h"           // for IntSize
23
#include "mozilla/layers/CompositorThread.h"
24
#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
25
#include "mozilla/mozalloc.h"           // for operator new, etc
26
#include "nsCOMPtr.h"                   // for already_AddRefed
27
#include "nsDebug.h"                    // for NS_ASSERTION, etc
28
#include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
29
#include "nsIWidget.h"                  // for nsIWidget
30
#include "nsThreadUtils.h"              // for NS_IsMainThread
31
#include "mozilla/Telemetry.h"
32
#include "mozilla/VsyncDispatcher.h"
33
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
34
#include "VsyncSource.h"
35
#endif
36
#include "mozilla/widget/CompositorWidget.h"
37
#include "VRManager.h"
38
#include "VRThread.h"
39
40
namespace mozilla {
41
42
namespace layers {
43
44
using namespace mozilla::gfx;
45
using namespace std;
46
47
CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
48
  : mMutex("CompositorVsyncScheduler.Observer.Mutex")
49
  , mOwner(aOwner)
50
0
{
51
0
}
52
53
CompositorVsyncScheduler::Observer::~Observer()
54
0
{
55
0
  MOZ_ASSERT(!mOwner);
56
0
}
57
58
bool
59
CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp)
60
0
{
61
0
  MutexAutoLock lock(mMutex);
62
0
  if (!mOwner) {
63
0
    return false;
64
0
  }
65
0
  return mOwner->NotifyVsync(aVsyncTimestamp);
66
0
}
67
68
void
69
CompositorVsyncScheduler::Observer::Destroy()
70
0
{
71
0
  MutexAutoLock lock(mMutex);
72
0
  mOwner = nullptr;
73
0
}
74
75
CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
76
                                                   widget::CompositorWidget* aWidget)
77
  : mVsyncSchedulerOwner(aVsyncSchedulerOwner)
78
  , mLastCompose(TimeStamp::Now())
79
  , mIsObservingVsync(false)
80
  , mNeedsComposite(0)
81
  , mVsyncNotificationsSkipped(0)
82
  , mWidget(aWidget)
83
  , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
84
  , mCurrentCompositeTask(nullptr)
85
  , mCurrentVRListenerTaskMonitor("CurrentVRTaskMonitor")
86
  , mCurrentVRListenerTask(nullptr)
87
0
{
88
0
  mVsyncObserver = new Observer(this);
89
0
90
0
  // mAsapScheduling is set on the main thread during init,
91
0
  // but is only accessed after on the compositor thread.
92
0
  mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
93
0
                    gfxPlatform::IsInLayoutAsapMode() ||
94
0
                    recordreplay::IsRecordingOrReplaying();
95
0
}
96
97
CompositorVsyncScheduler::~CompositorVsyncScheduler()
98
0
{
99
0
  MOZ_ASSERT(!mIsObservingVsync);
100
0
  MOZ_ASSERT(!mVsyncObserver);
101
0
  // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
102
0
  mVsyncSchedulerOwner = nullptr;
103
0
}
104
105
void
106
CompositorVsyncScheduler::Destroy()
107
0
{
108
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
109
0
110
0
  if (!mVsyncObserver) {
111
0
    // Destroy was already called on this object.
112
0
    return;
113
0
  }
114
0
  UnobserveVsync();
115
0
  mVsyncObserver->Destroy();
116
0
  mVsyncObserver = nullptr;
117
0
118
0
  mNeedsComposite = 0;
119
0
  CancelCurrentCompositeTask();
120
0
}
121
122
void
123
CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
124
0
{
125
0
  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
126
0
  if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
127
0
    RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
128
0
      "layers::CompositorVsyncScheduler::Composite",
129
0
      this,
130
0
      &CompositorVsyncScheduler::Composite,
131
0
      aCompositeTimestamp);
132
0
    mCurrentCompositeTask = task;
133
0
    ScheduleTask(task.forget());
134
0
  }
135
0
}
136
137
void
138
CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp)
139
0
{
140
0
  MonitorAutoLock lockVR(mCurrentVRListenerTaskMonitor);
141
0
  if (mCurrentVRListenerTask == nullptr && VRListenerThreadHolder::Loop()) {
142
0
    RefPtr<Runnable> task = NewRunnableMethod<TimeStamp>(
143
0
      "layers::CompositorVsyncScheduler::DispatchVREvents",
144
0
      this,
145
0
      &CompositorVsyncScheduler::DispatchVREvents,
146
0
      aTimestamp);
147
0
    mCurrentVRListenerTask = task;
148
0
    VRListenerThreadHolder::Loop()->PostDelayedTask(task.forget(), 0);
149
0
  }
150
0
}
151
152
void
153
CompositorVsyncScheduler::ScheduleComposition()
154
0
{
155
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
156
0
  if (!mVsyncObserver) {
157
0
    // Destroy was already called on this object.
158
0
    return;
159
0
  }
160
0
161
0
  if (mAsapScheduling) {
162
0
    // Used only for performance testing purposes, and when recording/replaying
163
0
    // to ensure that graphics are up to date.
164
0
    PostCompositeTask(TimeStamp::Now());
165
#ifdef MOZ_WIDGET_ANDROID
166
  } else if (mNeedsComposite >= 2 && mIsObservingVsync) {
167
    // uh-oh, we already requested a composite at least twice so far, and a
168
    // composite hasn't happened yet. It is possible that the vsync observation
169
    // is blocked on the main thread, so let's just composite ASAP and not
170
    // wait for the vsync. Note that this should only ever happen on Fennec
171
    // because there content runs in the same process as the compositor, and so
172
    // content can actually block the main thread in this process.
173
    PostCompositeTask(TimeStamp::Now());
174
#endif
175
0
  } else {
176
0
    mNeedsComposite++;
177
0
    if (!mIsObservingVsync && mNeedsComposite) {
178
0
      ObserveVsync();
179
0
      // Starting to observe vsync is an async operation that goes
180
0
      // through the main thread of the UI process. It's possible that
181
0
      // we're blocking there waiting on a composite, so schedule an initial
182
0
      // one now to get things started.
183
0
      PostCompositeTask(TimeStamp::Now());
184
0
    }
185
0
  }
186
0
}
187
188
bool
189
CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
190
0
{
191
0
  // Called from the vsync dispatch thread. When in the GPU Process, that's
192
0
  // the same as the compositor thread.
193
0
  MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
194
0
  MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
195
0
  MOZ_ASSERT(!NS_IsMainThread());
196
0
  PostCompositeTask(aVsyncTimestamp);
197
0
  PostVRTask(aVsyncTimestamp);
198
0
  return true;
199
0
}
200
201
void
202
CompositorVsyncScheduler::CancelCurrentCompositeTask()
203
0
{
204
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
205
0
  MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
206
0
  if (mCurrentCompositeTask) {
207
0
    mCurrentCompositeTask->Cancel();
208
0
    mCurrentCompositeTask = nullptr;
209
0
  }
210
0
}
211
212
void
213
CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
214
0
{
215
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
216
0
  MOZ_ASSERT(mVsyncSchedulerOwner);
217
0
218
0
  { // scope lock
219
0
    MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
220
0
    mCurrentCompositeTask = nullptr;
221
0
  }
222
0
223
0
  if (!mAsapScheduling) {
224
0
    // Some early exit conditions if we're not in ASAP mode
225
0
    if (aVsyncTimestamp < mLastCompose) {
226
0
      // We can sometimes get vsync timestamps that are in the past
227
0
      // compared to the last compose with force composites.
228
0
      // In those cases, wait until the next vsync;
229
0
      return;
230
0
    }
231
0
232
0
    if (mVsyncSchedulerOwner->IsPendingComposite()) {
233
0
      // If previous composite is still on going, finish it and wait for the
234
0
      // next vsync.
235
0
      mVsyncSchedulerOwner->FinishPendingComposite();
236
0
      return;
237
0
    }
238
0
  }
239
0
240
0
  if (mNeedsComposite || mAsapScheduling) {
241
0
    mNeedsComposite = 0;
242
0
    mLastCompose = aVsyncTimestamp;
243
0
244
0
    // Tell the owner to do a composite
245
0
    mVsyncSchedulerOwner->CompositeToTarget(nullptr, nullptr);
246
0
247
0
    mVsyncNotificationsSkipped = 0;
248
0
249
0
    TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
250
0
    mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
251
0
                                   compositeFrameTotal.ToMilliseconds());
252
0
  } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
253
0
    UnobserveVsync();
254
0
  }
255
0
}
256
257
void
258
CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
259
0
{
260
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
261
0
262
0
  /**
263
0
   * bug 1138502 - There are cases such as during long-running window resizing
264
0
   * events where we receive many force-composites. We also continue to get
265
0
   * vsync notifications. Because the force-composites trigger compositing and
266
0
   * clear the mNeedsComposite counter, the vsync notifications will not need
267
0
   * to do anything and so will increment the mVsyncNotificationsSkipped counter
268
0
   * to indicate the vsync was ignored. If this happens enough times, we will
269
0
   * disable listening for vsync entirely. On the next force-composite we will
270
0
   * enable listening for vsync again, and continued force-composites and vsyncs
271
0
   * will cause oscillation between observing vsync and not.
272
0
   * On some platforms, enabling/disabling vsync is not free and this
273
0
   * oscillating behavior causes a performance hit. In order to avoid this
274
0
   * problem, we reset the mVsyncNotificationsSkipped counter to keep vsync
275
0
   * enabled.
276
0
   */
277
0
  mVsyncNotificationsSkipped = 0;
278
0
279
0
  mLastCompose = TimeStamp::Now();
280
0
  MOZ_ASSERT(mVsyncSchedulerOwner);
281
0
  mVsyncSchedulerOwner->CompositeToTarget(aTarget, aRect);
282
0
}
283
284
bool
285
CompositorVsyncScheduler::NeedsComposite()
286
0
{
287
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
288
0
  return mNeedsComposite;
289
0
}
290
291
bool
292
CompositorVsyncScheduler::FlushPendingComposite()
293
0
{
294
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
295
0
  if (mNeedsComposite) {
296
0
    CancelCurrentCompositeTask();
297
0
    ForceComposeToTarget(nullptr, nullptr);
298
0
    return true;
299
0
  }
300
0
  return false;
301
0
}
302
303
void
304
CompositorVsyncScheduler::ObserveVsync()
305
0
{
306
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
307
0
  mWidget->ObserveVsync(mVsyncObserver);
308
0
  mIsObservingVsync = true;
309
0
}
310
311
void
312
CompositorVsyncScheduler::UnobserveVsync()
313
0
{
314
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
315
0
  mWidget->ObserveVsync(nullptr);
316
0
  mIsObservingVsync = false;
317
0
}
318
319
void
320
CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
321
0
{
322
0
  {
323
0
    MonitorAutoLock lock(mCurrentVRListenerTaskMonitor);
324
0
    mCurrentVRListenerTask = nullptr;
325
0
  }
326
0
  // This only allows to be called by CompositorVsyncScheduler::PostVRTask()
327
0
  // When the process is going to shutdown, the runnable has chance to be executed
328
0
  // by other threads, we only want it to be run at VRListenerThread.
329
0
  if (!VRListenerThreadHolder::IsInVRListenerThread()) {
330
0
    return;
331
0
  }
332
0
333
0
  VRManager* vm = VRManager::Get();
334
0
  vm->NotifyVsync(aVsyncTimestamp);
335
0
}
336
337
void
338
CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask)
339
0
{
340
0
  MOZ_ASSERT(CompositorThreadHolder::Loop());
341
0
  CompositorThreadHolder::Loop()->PostDelayedTask(std::move(aTask), 0);
342
0
}
343
344
const TimeStamp&
345
CompositorVsyncScheduler::GetLastComposeTime() const
346
0
{
347
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
348
0
  return mLastCompose;
349
0
}
350
351
void
352
CompositorVsyncScheduler::UpdateLastComposeTime()
353
0
{
354
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
355
0
  mLastCompose = TimeStamp::Now();
356
0
}
357
358
} // namespace layers
359
} // namespace mozilla