summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderThread.h
blob: aa508e558180a4dcad619ce42da7d8776d2aad20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MOZILLA_LAYERS_RENDERTHREAD_H
#define MOZILLA_LAYERS_RENDERTHREAD_H

#include "base/basictypes.h"       // for DISALLOW_EVIL_CONSTRUCTORS
#include "base/platform_thread.h"  // for PlatformThreadId
#include "base/thread.h"           // for Thread
#include "base/message_loop.h"
#include "GLTypes.h"  // for GLenum
#include "nsISupportsImpl.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/MozPromise.h"
#include "mozilla/DataMutex.h"
#include "mozilla/Maybe.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "mozilla/layers/CompositionRecorder.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/VsyncDispatcher.h"

#include <list>
#include <queue>
#include <unordered_map>

namespace mozilla {
namespace gl {
class GLContext;
}  // namespace gl
namespace layers {
class CompositorBridgeParent;
class ShaderProgramOGLsHolder;
class SurfacePool;
}  // namespace layers
namespace wr {

typedef MozPromise<MemoryReport, bool, true> MemoryReportPromise;

class RendererOGL;
class RenderTextureHost;
class RenderThread;

/// A rayon thread pool that is shared by all WebRender instances within a
/// process.
class WebRenderThreadPool {
 public:
  explicit WebRenderThreadPool(bool low_priority);

  ~WebRenderThreadPool();

  wr::WrThreadPool* Raw() {
    // If this pointer is null we are likely at some late shutdown stage,
    // when threads are no longer safe to interact with.
    MOZ_RELEASE_ASSERT(mThreadPool);
    return mThreadPool;
  }

  /// Prematurely destroys this handle to the thread pool.
  /// After calling this the object is useless.
  void Release();

 protected:
  wr::WrThreadPool* mThreadPool;
};

class WebRenderProgramCache final {
 public:
  explicit WebRenderProgramCache(wr::WrThreadPool* aThreadPool);

  ~WebRenderProgramCache();

  wr::WrProgramCache* Raw() { return mProgramCache; }

 protected:
  wr::WrProgramCache* mProgramCache;
};

class WebRenderShaders final {
 public:
  WebRenderShaders(gl::GLContext* gl, WebRenderProgramCache* programCache);
  ~WebRenderShaders();

  wr::WrShaders* RawShaders() { return mShaders; }

 protected:
  RefPtr<gl::GLContext> mGL;
  wr::WrShaders* mShaders;
};

class WebRenderPipelineInfo final {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderPipelineInfo);

  const wr::WrPipelineInfo& Raw() const { return mPipelineInfo; }
  wr::WrPipelineInfo& Raw() { return mPipelineInfo; }

 protected:
  ~WebRenderPipelineInfo() = default;
  wr::WrPipelineInfo mPipelineInfo;
};

/// Base class for an event that can be scheduled to run on the render thread.
///
/// The event can be passed through the same channels as regular WebRender
/// messages to preserve ordering.
class RendererEvent {
 public:
  virtual ~RendererEvent() = default;
  virtual void Run(RenderThread& aRenderThread, wr::WindowId aWindow) = 0;
};

/// The render thread is where WebRender issues all of its GPU work, and as much
/// as possible this thread should only serve this purpose.
///
/// The render thread owns the different RendererOGLs (one per window) and
/// implements the RenderNotifier api exposed by the WebRender bindings.
///
/// Callers are not allowed to post tasks to the render thread's event loop
/// directly and must instead use the RendererEvent mechanism which avoids races
/// between the events and WebRender's own messages.
///
/// The GL context(s) should be created and used on this thread only.
/// XXX - I've tried to organize code so that we can potentially avoid making
/// this a singleton since this bad habit has a tendency to bite us later, but
/// I haven't gotten all the way there either, in order to focus on the more
/// important pieces first. So we are a bit in-between (this is totally a
/// singleton but in some places we pretend it's not). Hopefully we can evolve
/// this in a way that keeps the door open to removing the singleton bits.
class RenderThread final {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(RenderThread)

 public:
  /// Can be called from any thread.
  static RenderThread* Get();

  /// Can only be called from the main thread.
  static void Start(uint32_t aNamespace);

  /// Can only be called from the main thread.
  static void ShutDown();

  /// Can be called from any thread.
  static bool IsInRenderThread();

  /// Can be called from any thread.
  static already_AddRefed<nsIThread> GetRenderThread();

  // Can be called from any thread. Dispatches an event to the Renderer thread
  // to iterate over all Renderers, accumulates memory statistics, and resolves
  // the return promise.
  static RefPtr<MemoryReportPromise> AccumulateMemoryReport(
      MemoryReport aInitial);

  /// Can only be called from the render thread.
  void AddRenderer(wr::WindowId aWindowId, UniquePtr<RendererOGL> aRenderer);

  /// Can only be called from the render thread.
  void RemoveRenderer(wr::WindowId aWindowId);

  /// Can only be called from the render thread.
  RendererOGL* GetRenderer(wr::WindowId aWindowId);

  /// Automatically forwarded to the render thread.
  void SetClearColor(wr::WindowId aWindowId, wr::ColorF aColor);

  /// Automatically forwarded to the render thread.
  void SetProfilerUI(wr::WindowId aWindowId, const nsACString& aUI);

  /// Automatically forwarded to the render thread.
  void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId,
                           float aWidth, float aHeight);

  /// Post RendererEvent to the render thread.
  void PostEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent);

  /// Can only be called from the render thread.
  void SetFramePublishId(wr::WindowId aWindowId, FramePublishId aPublishId);

  /// Can only be called from the render thread.
  void UpdateAndRender(wr::WindowId aWindowId, const VsyncId& aStartId,
                       const TimeStamp& aStartTime, bool aRender,
                       const Maybe<gfx::IntSize>& aReadbackSize,
                       const Maybe<wr::ImageFormat>& aReadbackFormat,
                       const Maybe<Range<uint8_t>>& aReadbackBuffer,
                       bool* aNeedsYFlip = nullptr);

  void Pause(wr::WindowId aWindowId);
  bool Resume(wr::WindowId aWindowId);

  /// Can be called from any thread.
  void RegisterExternalImage(const wr::ExternalImageId& aExternalImageId,
                             already_AddRefed<RenderTextureHost> aTexture);

  /// Can be called from any thread.
  void UnregisterExternalImage(const wr::ExternalImageId& aExternalImageId);

  /// Can be called from any thread.
  void DestroyExternalImagesSyncWait(
      const std::vector<wr::ExternalImageId>&& aIds);

  /// Can be called from any thread.
  void PrepareForUse(const wr::ExternalImageId& aExternalImageId);

  /// Can be called from any thread.
  void NotifyNotUsed(const wr::ExternalImageId& aExternalImageId);

  /// Can be called from any thread.
  void NotifyForUse(const wr::ExternalImageId& aExternalImageId);

  void HandleRenderTextureOps();

  /// Can only be called from the render thread.
  void UnregisterExternalImageDuringShutdown(
      const wr::ExternalImageId& aExternalImageId);

  /// Can only be called from the render thread.
  RenderTextureHost* GetRenderTexture(
      const wr::ExternalImageId& aExternalImageId);

  /// Can be called from any thread.
  bool IsDestroyed(wr::WindowId aWindowId);
  /// Can be called from any thread.
  void SetDestroyed(wr::WindowId aWindowId);
  /// Can be called from any thread.
  bool TooManyPendingFrames(wr::WindowId aWindowId);
  /// Can be called from any thread.
  void IncPendingFrameCount(wr::WindowId aWindowId, const VsyncId& aStartId,
                            const TimeStamp& aStartTime);
  /// Can be called from any thread.
  void DecPendingFrameBuildCount(wr::WindowId aWindowId);
  void DecPendingFrameCount(wr::WindowId aWindowId);

  // RenderNotifier implementation
  void WrNotifierEvent_WakeUp(WrWindowId aWindowId, bool aCompositeNeeded);
  void WrNotifierEvent_NewFrameReady(WrWindowId aWindowId,
                                     bool aCompositeNeeded,
                                     FramePublishId aPublishId);
  void WrNotifierEvent_ExternalEvent(WrWindowId aWindowId, size_t aRawEvent);

  /// Can be called from any thread.
  WebRenderThreadPool& ThreadPool() { return mThreadPool; }

  /// Thread pool for low priority scene building
  /// Can be called from any thread.
  WebRenderThreadPool& ThreadPoolLP() { return mThreadPoolLP; }

  /// Returns the cache used to serialize shader programs to disk, if enabled.
  ///
  /// Can only be called from the render thread.
  WebRenderProgramCache* GetProgramCache() {
    MOZ_ASSERT(IsInRenderThread());
    return mProgramCache.get();
  }

  /// Can only be called from the render thread.
  WebRenderShaders* GetShaders() {
    MOZ_ASSERT(IsInRenderThread());
    return mShaders.get();
  }

  /// Can only be called from the render thread.
  gl::GLContext* SingletonGL(nsACString& aError);
  gl::GLContext* SingletonGL();
  gl::GLContext* SingletonGLForCompositorOGL();
  void ClearSingletonGL();
  RefPtr<layers::SurfacePool> SharedSurfacePool();
  void ClearSharedSurfacePool();

  RefPtr<layers::ShaderProgramOGLsHolder> GetProgramsForCompositorOGL();

  /// Can only be called from the render thread.
  void HandleDeviceReset(const char* aWhere, GLenum aReason);
  /// Can only be called from the render thread.
  bool IsHandlingDeviceReset();
  /// Can be called from any thread.
  void SimulateDeviceReset();

  /// Can only be called from the render thread.
  void NotifyWebRenderError(WebRenderError aError);

  /// Can only be called from the render thread.
  void HandleWebRenderError(WebRenderError aError);
  /// Can only be called from the render thread.
  bool IsHandlingWebRenderError();

  /// Can only be called from the render thread.
  bool SyncObjectNeeded();

  size_t RendererCount() const;
  size_t ActiveRendererCount() const;

  void BeginRecordingForWindow(wr::WindowId aWindowId,
                               const TimeStamp& aRecordingStart,
                               wr::PipelineId aRootPipelineId);

  Maybe<layers::FrameRecording> EndRecordingForWindow(wr::WindowId aWindowId);

  static void MaybeEnableGLDebugMessage(gl::GLContext* aGLContext);

 private:
  enum class RenderTextureOp {
    PrepareForUse,
    NotifyForUse,
    NotifyNotUsed,
  };
  class WrNotifierEvent {
   public:
    enum class Tag {
      WakeUp,
      NewFrameReady,
      ExternalEvent,
    };
    const Tag mTag;

   private:
    WrNotifierEvent(const Tag aTag, const bool aCompositeNeeded)
        : mTag(aTag), mCompositeNeeded(aCompositeNeeded) {
      MOZ_ASSERT(mTag == Tag::WakeUp);
    }
    WrNotifierEvent(const Tag aTag, bool aCompositeNeeded,
                    FramePublishId aPublishId)
        : mTag(aTag),
          mCompositeNeeded(aCompositeNeeded),
          mPublishId(aPublishId) {
      MOZ_ASSERT(mTag == Tag::NewFrameReady);
    }
    WrNotifierEvent(const Tag aTag, UniquePtr<RendererEvent>&& aRendererEvent)
        : mTag(aTag), mRendererEvent(std::move(aRendererEvent)) {
      MOZ_ASSERT(mTag == Tag::ExternalEvent);
    }

    const bool mCompositeNeeded = false;
    const FramePublishId mPublishId = FramePublishId::INVALID;
    UniquePtr<RendererEvent> mRendererEvent;

   public:
    static WrNotifierEvent WakeUp(const bool aCompositeNeeded) {
      return WrNotifierEvent(Tag::WakeUp, aCompositeNeeded);
    }

    static WrNotifierEvent NewFrameReady(const bool aCompositeNeeded,
                                         const FramePublishId aPublishId) {
      return WrNotifierEvent(Tag::NewFrameReady, aCompositeNeeded, aPublishId);
    }

    static WrNotifierEvent ExternalEvent(
        UniquePtr<RendererEvent>&& aRendererEvent) {
      return WrNotifierEvent(Tag::ExternalEvent, std::move(aRendererEvent));
    }

    bool CompositeNeeded() {
      if (mTag == Tag::WakeUp || mTag == Tag::NewFrameReady) {
        return mCompositeNeeded;
      }
      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
      return false;
    }
    FramePublishId PublishId() {
      if (mTag == Tag::NewFrameReady) {
        return mPublishId;
      }
      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
      return FramePublishId::INVALID;
    }
    UniquePtr<RendererEvent> ExternalEvent() {
      if (mTag == Tag::ExternalEvent) {
        MOZ_ASSERT(mRendererEvent);
        return std::move(mRendererEvent);
      }
      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
      return nullptr;
    }
  };

  explicit RenderThread(RefPtr<nsIThread> aThread);

  void HandleFrameOneDocInner(wr::WindowId aWindowId, bool aRender,
                              bool aTrackedFrame,
                              Maybe<FramePublishId> aPublishId);

  void DeferredRenderTextureHostDestroy();
  void ShutDownTask();
  void InitDeviceTask();
  void HandleFrameOneDoc(wr::WindowId aWindowId, bool aRender,
                         bool aTrackedFrame, Maybe<FramePublishId> aPublishId);
  void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent);
  void PostRunnable(already_AddRefed<nsIRunnable> aRunnable);

  void DoAccumulateMemoryReport(MemoryReport,
                                const RefPtr<MemoryReportPromise::Private>&);

  void AddRenderTextureOp(RenderTextureOp aOp,
                          const wr::ExternalImageId& aExternalImageId);

  void CreateSingletonGL(nsACString& aError);

  void DestroyExternalImages(const std::vector<wr::ExternalImageId>&& aIds);

  struct WindowInfo;

  void PostWrNotifierEvents(WrWindowId aWindowId);
  void PostWrNotifierEvents(WrWindowId aWindowId, WindowInfo* aInfo);
  void HandleWrNotifierEvents(WrWindowId aWindowId);
  void WrNotifierEvent_HandleWakeUp(wr::WindowId aWindowId,
                                    bool aCompositeNeeded);
  void WrNotifierEvent_HandleNewFrameReady(wr::WindowId aWindowId,
                                           bool aCompositeNeeded,
                                           FramePublishId aPublishId);
  void WrNotifierEvent_HandleExternalEvent(
      wr::WindowId aWindowId, UniquePtr<RendererEvent> aRendererEvent);

  ~RenderThread();

  RefPtr<nsIThread> const mThread;

  WebRenderThreadPool mThreadPool;
  WebRenderThreadPool mThreadPoolLP;

  UniquePtr<WebRenderProgramCache> mProgramCache;
  UniquePtr<WebRenderShaders> mShaders;
  RefPtr<layers::ShaderProgramOGLsHolder> mProgramsForCompositorOGL;

  // An optional shared GLContext to be used for all
  // windows.
  RefPtr<gl::GLContext> mSingletonGL;
  bool mSingletonGLIsForHardwareWebRender;

  RefPtr<layers::SurfacePool> mSurfacePool;

  std::map<wr::WindowId, UniquePtr<RendererOGL>> mRenderers;

  struct PendingFrameInfo {
    TimeStamp mStartTime;
    VsyncId mStartId;
  };

  struct WindowInfo {
    int64_t PendingCount() { return mPendingFrames.size(); }
    std::queue<PendingFrameInfo> mPendingFrames;
    uint8_t mPendingFrameBuild = 0;
    bool mIsDestroyed = false;
    RefPtr<nsIRunnable> mWrNotifierEventsRunnable;
    std::queue<WrNotifierEvent> mPendingWrNotifierEvents;
  };

  DataMutex<std::unordered_map<uint64_t, UniquePtr<WindowInfo>>> mWindowInfos;

  std::unordered_map<uint64_t, UniquePtr<std::queue<WrNotifierEvent>>>
      mWrNotifierEventsQueues;

  struct ExternalImageIdHashFn {
    std::size_t operator()(const wr::ExternalImageId& aId) const {
      return HashGeneric(wr::AsUint64(aId));
    }
  };

  Mutex mRenderTextureMapLock;
  std::unordered_map<wr::ExternalImageId, RefPtr<RenderTextureHost>,
                     ExternalImageIdHashFn>
      mRenderTextures MOZ_GUARDED_BY(mRenderTextureMapLock);
  std::unordered_map<wr::ExternalImageId, RefPtr<RenderTextureHost>,
                     ExternalImageIdHashFn>
      mSyncObjectNeededRenderTextures MOZ_GUARDED_BY(mRenderTextureMapLock);
  std::list<std::pair<RenderTextureOp, RefPtr<RenderTextureHost>>>
      mRenderTextureOps MOZ_GUARDED_BY(mRenderTextureMapLock);

  // Used to remove all RenderTextureHost that are going to be removed by
  // a deferred callback and remove them right away without waiting for the
  // callback. On device reset we have to remove all GL related resources right
  // away.
  std::list<RefPtr<RenderTextureHost>> mRenderTexturesDeferred
      MOZ_GUARDED_BY(mRenderTextureMapLock);

  RefPtr<nsIRunnable> mRenderTextureOpsRunnable
      MOZ_GUARDED_BY(mRenderTextureMapLock);

  // Set from MainThread, read from either MainThread or RenderThread
  bool mHasShutdown;

  // Only accessed from the RenderThread
  bool mHandlingDeviceReset;
  bool mHandlingWebRenderError;
};

}  // namespace wr
}  // namespace mozilla

#endif