Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/ipc/CompositorManagerParent.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/CompositorManagerParent.h"
8
#include "mozilla/gfx/GPUParent.h"
9
#include "mozilla/webrender/RenderThread.h"
10
#include "mozilla/layers/CompositorBridgeParent.h"
11
#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
12
#include "mozilla/layers/CompositorThread.h"
13
#include "mozilla/layers/SharedSurfacesParent.h"
14
#include "nsAutoPtr.h"
15
#include "VsyncSource.h"
16
17
namespace mozilla {
18
namespace layers {
19
20
StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance;
21
StaticMutex CompositorManagerParent::sMutex;
22
23
#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
24
StaticAutoPtr<nsTArray<CompositorManagerParent*>> CompositorManagerParent::sActiveActors;
25
#endif
26
27
/* static */ already_AddRefed<CompositorManagerParent>
28
CompositorManagerParent::CreateSameProcess()
29
0
{
30
0
  MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
31
0
  MOZ_ASSERT(NS_IsMainThread());
32
0
  StaticMutexAutoLock lock(sMutex);
33
0
34
0
  // We are creating a manager for the UI process, inside the combined GPU/UI
35
0
  // process. It is created more-or-less the same but we retain a reference to
36
0
  // the parent to access state.
37
0
  if (NS_WARN_IF(sInstance)) {
38
0
    MOZ_ASSERT_UNREACHABLE("Already initialized");
39
0
    return nullptr;
40
0
  }
41
0
42
0
  // The child is responsible for setting up the IPC channel in the same
43
0
  // process case because if we open from the child perspective, we can do it
44
0
  // on the main thread and complete before we return the manager handles.
45
0
  RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
46
0
  parent->SetOtherProcessId(base::GetCurrentProcId());
47
0
  return parent.forget();
48
0
}
49
50
/* static */ void
51
CompositorManagerParent::Create(Endpoint<PCompositorManagerParent>&& aEndpoint)
52
0
{
53
0
  MOZ_ASSERT(NS_IsMainThread());
54
0
55
0
  // We are creating a manager for the another process, inside the GPU process
56
0
  // (or UI process if it subsumbed the GPU process).
57
0
  MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
58
0
59
0
  RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
60
0
61
0
  RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PCompositorManagerParent>&&>(
62
0
    "CompositorManagerParent::Bind",
63
0
    bridge,
64
0
    &CompositorManagerParent::Bind,
65
0
    std::move(aEndpoint));
66
0
  CompositorThreadHolder::Loop()->PostTask(runnable.forget());
67
0
}
68
69
/* static */ already_AddRefed<CompositorBridgeParent>
70
CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,
71
                                                                 const CompositorOptions& aOptions,
72
                                                                 bool aUseExternalSurfaceSize,
73
                                                                 const gfx::IntSize& aSurfaceSize)
74
0
{
75
0
  MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
76
0
  MOZ_ASSERT(NS_IsMainThread());
77
0
78
0
  // When we are in a combined UI / GPU process, InProcessCompositorSession
79
0
  // requires both the parent and child PCompositorBridge actors for its own
80
0
  // construction, which is done on the main thread. Normally
81
0
  // CompositorBridgeParent is created on the compositor thread via the IPDL
82
0
  // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to
83
0
  // actually get a reference to the parent, we would need to block on the
84
0
  // compositor thread until it handles our constructor message. Because only
85
0
  // one one IPDL constructor is permitted per parent and child protocol, we
86
0
  // cannot make the normal case async and this case sync. Instead what we do
87
0
  // is leave the constructor async (a boon to the content process setup) and
88
0
  // create the parent ahead of time. It will pull the preinitialized parent
89
0
  // from the queue when it receives the message and give that to IPDL.
90
0
91
0
  // Note that the static mutex not only is used to protect sInstance, but also
92
0
  // mPendingCompositorBridges.
93
0
  StaticMutexAutoLock lock(sMutex);
94
0
  if (NS_WARN_IF(!sInstance)) {
95
0
    return nullptr;
96
0
  }
97
0
98
0
  TimeDuration vsyncRate =
99
0
    gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
100
0
101
0
  RefPtr<CompositorBridgeParent> bridge =
102
0
    new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
103
0
                               aUseExternalSurfaceSize, aSurfaceSize);
104
0
105
0
  sInstance->mPendingCompositorBridges.AppendElement(bridge);
106
0
  return bridge.forget();
107
0
}
108
109
CompositorManagerParent::CompositorManagerParent()
110
  : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
111
0
{
112
0
}
113
114
CompositorManagerParent::~CompositorManagerParent()
115
0
{
116
0
}
117
118
void
119
CompositorManagerParent::Bind(Endpoint<PCompositorManagerParent>&& aEndpoint)
120
0
{
121
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
122
0
  if (NS_WARN_IF(!aEndpoint.Bind(this))) {
123
0
    return;
124
0
  }
125
0
126
0
  BindComplete();
127
0
}
128
129
void
130
CompositorManagerParent::BindComplete()
131
0
{
132
0
  // Add the IPDL reference to ourself, so we can't get freed until IPDL is
133
0
  // done with us.
134
0
  AddRef();
135
0
136
0
  StaticMutexAutoLock lock(sMutex);
137
0
  if (OtherPid() == base::GetCurrentProcId()) {
138
0
    sInstance = this;
139
0
  }
140
0
141
0
#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
142
0
  if (!sActiveActors) {
143
0
    sActiveActors = new nsTArray<CompositorManagerParent*>();
144
0
  }
145
0
  sActiveActors->AppendElement(this);
146
0
#endif
147
0
}
148
149
void
150
CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason)
151
0
{
152
0
  SharedSurfacesParent::DestroyProcess(OtherPid());
153
0
154
0
  StaticMutexAutoLock lock(sMutex);
155
0
  if (sInstance == this) {
156
0
    sInstance = nullptr;
157
0
  }
158
0
}
159
160
void
161
CompositorManagerParent::DeallocPCompositorManagerParent()
162
0
{
163
0
  MessageLoop::current()->PostTask(
164
0
          NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
165
0
                            this,
166
0
                            &CompositorManagerParent::DeferredDestroy));
167
0
168
0
#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
169
0
  StaticMutexAutoLock lock(sMutex);
170
0
  if (sActiveActors) {
171
0
    sActiveActors->RemoveElement(this);
172
0
  }
173
0
#endif
174
0
  Release();
175
0
}
176
177
void
178
CompositorManagerParent::DeferredDestroy()
179
0
{
180
0
  mCompositorThreadHolder = nullptr;
181
0
}
182
183
#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
184
/* static */ void
185
CompositorManagerParent::ShutdownInternal()
186
0
{
187
0
  nsAutoPtr<nsTArray<CompositorManagerParent*>> actors;
188
0
189
0
  // We move here because we may attempt to acquire the same lock during the
190
0
  // destroy to remove the reference in sActiveActors.
191
0
  {
192
0
    StaticMutexAutoLock lock(sMutex);
193
0
    actors = sActiveActors.forget();
194
0
  }
195
0
196
0
  if (actors) {
197
0
    for (auto& actor : *actors) {
198
0
      actor->Close();
199
0
    }
200
0
  }
201
0
}
202
#endif // COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
203
204
/* static */ void
205
CompositorManagerParent::Shutdown()
206
0
{
207
0
  MOZ_ASSERT(NS_IsMainThread());
208
0
209
0
#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
210
0
  CompositorThreadHolder::Loop()->PostTask(
211
0
      NS_NewRunnableFunction("layers::CompositorManagerParent::Shutdown", []() -> void {
212
0
        CompositorManagerParent::ShutdownInternal();
213
0
      }));
214
0
#endif
215
0
}
216
217
PCompositorBridgeParent*
218
CompositorManagerParent::AllocPCompositorBridgeParent(const CompositorBridgeOptions& aOpt)
219
0
{
220
0
  switch (aOpt.type()) {
221
0
    case CompositorBridgeOptions::TContentCompositorOptions: {
222
0
      CrossProcessCompositorBridgeParent* bridge =
223
0
        new CrossProcessCompositorBridgeParent(this);
224
0
      bridge->AddRef();
225
0
      return bridge;
226
0
    }
227
0
    case CompositorBridgeOptions::TWidgetCompositorOptions: {
228
0
      // Only the UI process is allowed to create widget compositors in the
229
0
      // compositor process.
230
0
      gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
231
0
      if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) {
232
0
        MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!");
233
0
        break;
234
0
      }
235
0
236
0
      const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions();
237
0
      CompositorBridgeParent* bridge =
238
0
        new CompositorBridgeParent(this, opt.scale(), opt.vsyncRate(),
239
0
                                   opt.options(), opt.useExternalSurfaceSize(),
240
0
                                   opt.surfaceSize());
241
0
      bridge->AddRef();
242
0
      return bridge;
243
0
    }
244
0
    case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: {
245
0
      // If the GPU and UI process are combined, we actually already created the
246
0
      // CompositorBridgeParent, so we need to reuse that to inject it into the
247
0
      // IPDL framework.
248
0
      if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) {
249
0
        MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!");
250
0
        break;
251
0
      }
252
0
253
0
      // Note that the static mutex not only is used to protect sInstance, but
254
0
      // also mPendingCompositorBridges.
255
0
      StaticMutexAutoLock lock(sMutex);
256
0
      MOZ_ASSERT(!mPendingCompositorBridges.IsEmpty());
257
0
258
0
      CompositorBridgeParent* bridge = mPendingCompositorBridges[0];
259
0
      bridge->AddRef();
260
0
      mPendingCompositorBridges.RemoveElementAt(0);
261
0
      return bridge;
262
0
    }
263
0
    default:
264
0
      break;
265
0
  }
266
0
267
0
  return nullptr;
268
0
}
269
270
bool
271
CompositorManagerParent::DeallocPCompositorBridgeParent(PCompositorBridgeParent* aActor)
272
0
{
273
0
  static_cast<CompositorBridgeParentBase*>(aActor)->Release();
274
0
  return true;
275
0
}
276
277
mozilla::ipc::IPCResult
278
CompositorManagerParent::RecvAddSharedSurface(const wr::ExternalImageId& aId,
279
                                              const SurfaceDescriptorShared& aDesc)
280
0
{
281
0
  SharedSurfacesParent::Add(aId, aDesc, OtherPid());
282
0
  return IPC_OK();
283
0
}
284
285
mozilla::ipc::IPCResult
286
CompositorManagerParent::RecvRemoveSharedSurface(const wr::ExternalImageId& aId)
287
0
{
288
0
  SharedSurfacesParent::Remove(aId);
289
0
  return IPC_OK();
290
0
}
291
292
mozilla::ipc::IPCResult
293
CompositorManagerParent::RecvNotifyMemoryPressure()
294
0
{
295
0
  nsTArray<PCompositorBridgeParent*> compositorBridges;
296
0
  ManagedPCompositorBridgeParent(compositorBridges);
297
0
  for (auto bridge : compositorBridges) {
298
0
    static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure();
299
0
  }
300
0
  return IPC_OK();
301
0
}
302
303
mozilla::ipc::IPCResult
304
CompositorManagerParent::RecvReportMemory(ReportMemoryResolver&& aResolver)
305
0
{
306
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
307
0
  MemoryReport aggregate;
308
0
  PodZero(&aggregate);
309
0
310
0
  // Accumulate RenderBackend usage.
311
0
  nsTArray<PCompositorBridgeParent*> compositorBridges;
312
0
  ManagedPCompositorBridgeParent(compositorBridges);
313
0
  for (auto bridge : compositorBridges) {
314
0
    static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport(&aggregate);
315
0
  }
316
0
317
0
  // Accumulate Renderer usage asynchronously, and resolve.
318
0
  //
319
0
  // Note that the IPDL machinery requires aResolver to be called on this
320
0
  // thread, so we can't just pass it over to the renderer thread. We use
321
0
  // an intermediate MozPromise instead.
322
0
  wr::RenderThread::AccumulateMemoryReport(aggregate)->Then(
323
0
    CompositorThreadHolder::Loop()->SerialEventTarget(), __func__,
324
0
    [resolver = std::move(aResolver)](MemoryReport aReport) {
325
0
      resolver(aReport);
326
0
    },
327
0
    [](bool) {
328
0
      MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected");
329
0
    }
330
0
  );
331
0
332
0
  return IPC_OK();
333
0
}
334
335
} // namespace layers
336
} // namespace mozilla