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 "base/task.h"
8 #include "GeckoProfiler.h"
9 #include "GLContext.h"
10 #include "RenderThread.h"
11 #include "nsThreadUtils.h"
12 #include "mtransport/runnable_utils.h"
13 #include "mozilla/layers/AsyncImagePipelineManager.h"
14 #include "mozilla/gfx/gfxVars.h"
15 #include "mozilla/gfx/GPUParent.h"
16 #include "mozilla/layers/CompositorThread.h"
17 #include "mozilla/layers/CompositorBridgeParent.h"
18 #include "mozilla/layers/CompositorManagerParent.h"
19 #include "mozilla/layers/WebRenderBridgeParent.h"
20 #include "mozilla/layers/SharedSurfacesParent.h"
21 #include "mozilla/StaticPtr.h"
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/webrender/RendererOGL.h"
24 #include "mozilla/webrender/RenderTextureHost.h"
25 #include "mozilla/widget/CompositorWidget.h"
26 
27 #ifdef XP_WIN
28 #  include "GLLibraryEGL.h"
29 #  include "mozilla/widget/WinCompositorWindowThread.h"
30 #endif
31 
32 #ifdef MOZ_WIDGET_ANDROID
33 #  include "GLLibraryEGL.h"
34 #  include "mozilla/webrender/RenderAndroidSurfaceTextureHostOGL.h"
35 #endif
36 
37 #ifdef MOZ_WIDGET_GTK
38 #  include <gdk/gdkx.h>
39 #endif
40 
41 #ifdef MOZ_WAYLAND
42 #  include "GLLibraryEGL.h"
43 #endif
44 
45 using namespace mozilla;
46 
47 static already_AddRefed<gl::GLContext> CreateGLContext();
48 
49 MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderRendererMallocSizeOf)
50 
51 namespace mozilla {
52 namespace wr {
53 
54 static StaticRefPtr<RenderThread> sRenderThread;
55 
RenderThread(base::Thread * aThread)56 RenderThread::RenderThread(base::Thread* aThread)
57     : mThread(aThread),
58       mThreadPool(false),
59       mThreadPoolLP(true),
60       mWindowInfos("RenderThread.mWindowInfos"),
61       mRenderTextureMapLock("RenderThread.mRenderTextureMapLock"),
62       mHasShutdown(false),
63       mHandlingDeviceReset(false),
64       mHandlingWebRenderError(false) {}
65 
~RenderThread()66 RenderThread::~RenderThread() {
67   MOZ_ASSERT(mRenderTexturesDeferred.empty());
68   delete mThread;
69 }
70 
71 // static
Get()72 RenderThread* RenderThread::Get() { return sRenderThread; }
73 
74 // static
Start()75 void RenderThread::Start() {
76   MOZ_ASSERT(NS_IsMainThread());
77   MOZ_ASSERT(!sRenderThread);
78 
79   base::Thread* thread = new base::Thread("Renderer");
80 
81   base::Thread::Options options;
82   // TODO(nical): The compositor thread has a bunch of specific options, see
83   // which ones make sense here.
84   if (!thread->StartWithOptions(options)) {
85     delete thread;
86     return;
87   }
88 
89   sRenderThread = new RenderThread(thread);
90 #ifdef XP_WIN
91   widget::WinCompositorWindowThread::Start();
92 #endif
93   layers::SharedSurfacesParent::Initialize();
94 
95   RefPtr<Runnable> runnable = WrapRunnable(
96       RefPtr<RenderThread>(sRenderThread.get()), &RenderThread::InitDeviceTask);
97   sRenderThread->Loop()->PostTask(runnable.forget());
98 }
99 
100 // static
ShutDown()101 void RenderThread::ShutDown() {
102   MOZ_ASSERT(NS_IsMainThread());
103   MOZ_ASSERT(sRenderThread);
104 
105   {
106     MutexAutoLock lock(sRenderThread->mRenderTextureMapLock);
107     sRenderThread->mHasShutdown = true;
108   }
109 
110   layers::SynchronousTask task("RenderThread");
111   RefPtr<Runnable> runnable =
112       WrapRunnable(RefPtr<RenderThread>(sRenderThread.get()),
113                    &RenderThread::ShutDownTask, &task);
114   sRenderThread->Loop()->PostTask(runnable.forget());
115   task.Wait();
116 
117   sRenderThread = nullptr;
118 #ifdef XP_WIN
119   if (widget::WinCompositorWindowThread::Get()) {
120     widget::WinCompositorWindowThread::ShutDown();
121   }
122 #endif
123 }
124 
125 extern void ClearAllBlobImageResources();
126 
ShutDownTask(layers::SynchronousTask * aTask)127 void RenderThread::ShutDownTask(layers::SynchronousTask* aTask) {
128   layers::AutoCompleteTask complete(aTask);
129   MOZ_ASSERT(IsInRenderThread());
130 
131   // Let go of our handle to the (internally ref-counted) thread pool.
132   mThreadPool.Release();
133   mThreadPoolLP.Release();
134 
135   // Releasing on the render thread will allow us to avoid dispatching to remove
136   // remaining textures from the texture map.
137   layers::SharedSurfacesParent::Shutdown();
138 
139   ClearAllBlobImageResources();
140   ClearSharedGL();
141   ClearSharedSurfacePool();
142 }
143 
144 // static
Loop()145 MessageLoop* RenderThread::Loop() {
146   return sRenderThread ? sRenderThread->mThread->message_loop() : nullptr;
147 }
148 
149 // static
IsInRenderThread()150 bool RenderThread::IsInRenderThread() {
151   return sRenderThread &&
152          sRenderThread->mThread->thread_id() == PlatformThread::CurrentId();
153 }
154 
DoAccumulateMemoryReport(MemoryReport aReport,const RefPtr<MemoryReportPromise::Private> & aPromise)155 void RenderThread::DoAccumulateMemoryReport(
156     MemoryReport aReport,
157     const RefPtr<MemoryReportPromise::Private>& aPromise) {
158   MOZ_ASSERT(IsInRenderThread());
159 
160   for (auto& r : mRenderers) {
161     r.second->AccumulateMemoryReport(&aReport);
162   }
163 
164   // Note memory used by the shader cache, which is shared across all WR
165   // instances.
166   MOZ_ASSERT(aReport.shader_cache == 0);
167   if (mProgramCache) {
168     aReport.shader_cache = wr_program_cache_report_memory(
169         mProgramCache->Raw(), &WebRenderRendererMallocSizeOf);
170   }
171 
172   aPromise->Resolve(aReport, __func__);
173 }
174 
175 // static
AccumulateMemoryReport(MemoryReport aInitial)176 RefPtr<MemoryReportPromise> RenderThread::AccumulateMemoryReport(
177     MemoryReport aInitial) {
178   RefPtr<MemoryReportPromise::Private> p =
179       new MemoryReportPromise::Private(__func__);
180   MOZ_ASSERT(!IsInRenderThread());
181   if (!Get() || !Get()->Loop()) {
182     // This happens when the GPU process fails to start and we fall back to the
183     // basic compositor in the parent process. We could assert against this if
184     // we made the webrender detection code in gfxPlatform.cpp smarter. See bug
185     // 1494430 comment 12.
186     NS_WARNING("No render thread, returning empty memory report");
187     p->Resolve(aInitial, __func__);
188     return p;
189   }
190 
191   Get()->Loop()->PostTask(
192       NewRunnableMethod<MemoryReport, RefPtr<MemoryReportPromise::Private>>(
193           "wr::RenderThread::DoAccumulateMemoryReport", Get(),
194           &RenderThread::DoAccumulateMemoryReport, aInitial, p));
195 
196   return p;
197 }
198 
AddRenderer(wr::WindowId aWindowId,UniquePtr<RendererOGL> aRenderer)199 void RenderThread::AddRenderer(wr::WindowId aWindowId,
200                                UniquePtr<RendererOGL> aRenderer) {
201   MOZ_ASSERT(IsInRenderThread());
202 
203   if (mHasShutdown) {
204     return;
205   }
206 
207   mRenderers[aWindowId] = std::move(aRenderer);
208 
209   auto windows = mWindowInfos.Lock();
210   windows->emplace(AsUint64(aWindowId), new WindowInfo());
211 }
212 
RemoveRenderer(wr::WindowId aWindowId)213 void RenderThread::RemoveRenderer(wr::WindowId aWindowId) {
214   MOZ_ASSERT(IsInRenderThread());
215 
216   if (mHasShutdown) {
217     return;
218   }
219 
220   mRenderers.erase(aWindowId);
221   mCompositionRecorders.erase(aWindowId);
222 
223   if (mRenderers.size() == 0 && mHandlingDeviceReset) {
224     mHandlingDeviceReset = false;
225   }
226 
227   auto windows = mWindowInfos.Lock();
228   auto it = windows->find(AsUint64(aWindowId));
229   MOZ_ASSERT(it != windows->end());
230   WindowInfo* toDelete = it->second;
231   windows->erase(it);
232   delete toDelete;
233 }
234 
GetRenderer(wr::WindowId aWindowId)235 RendererOGL* RenderThread::GetRenderer(wr::WindowId aWindowId) {
236   MOZ_ASSERT(IsInRenderThread());
237 
238   auto it = mRenderers.find(aWindowId);
239   MOZ_ASSERT(it != mRenderers.end());
240 
241   if (it == mRenderers.end()) {
242     return nullptr;
243   }
244 
245   return it->second.get();
246 }
247 
RendererCount()248 size_t RenderThread::RendererCount() {
249   MOZ_ASSERT(IsInRenderThread());
250   return mRenderers.size();
251 }
252 
SetCompositionRecorderForWindow(wr::WindowId aWindowId,UniquePtr<layers::WebRenderCompositionRecorder> aCompositionRecorder)253 void RenderThread::SetCompositionRecorderForWindow(
254     wr::WindowId aWindowId,
255     UniquePtr<layers::WebRenderCompositionRecorder> aCompositionRecorder) {
256   MOZ_ASSERT(IsInRenderThread());
257   MOZ_ASSERT(GetRenderer(aWindowId));
258   MOZ_ASSERT(mCompositionRecorders.find(aWindowId) ==
259              mCompositionRecorders.end());
260 
261   mCompositionRecorders[aWindowId] = std::move(aCompositionRecorder);
262 }
263 
WriteCollectedFramesForWindow(wr::WindowId aWindowId)264 void RenderThread::WriteCollectedFramesForWindow(wr::WindowId aWindowId) {
265   MOZ_ASSERT(IsInRenderThread());
266 
267   RendererOGL* renderer = GetRenderer(aWindowId);
268   MOZ_ASSERT(renderer);
269 
270   auto it = mCompositionRecorders.find(aWindowId);
271   MOZ_DIAGNOSTIC_ASSERT(
272       it != mCompositionRecorders.end(),
273       "Attempted to write frames from a window that was not recording.");
274   if (it != mCompositionRecorders.end()) {
275     it->second->WriteCollectedFrames();
276 
277     if (renderer) {
278       wr_renderer_release_composition_recorder_structures(
279           renderer->GetRenderer());
280     }
281 
282     mCompositionRecorders.erase(it);
283   }
284 }
285 
GetCollectedFramesForWindow(wr::WindowId aWindowId)286 Maybe<layers::CollectedFrames> RenderThread::GetCollectedFramesForWindow(
287     wr::WindowId aWindowId) {
288   MOZ_ASSERT(IsInRenderThread());
289 
290   RendererOGL* renderer = GetRenderer(aWindowId);
291   MOZ_ASSERT(renderer);
292 
293   auto it = mCompositionRecorders.find(aWindowId);
294   MOZ_DIAGNOSTIC_ASSERT(
295       it != mCompositionRecorders.end(),
296       "Attempted to get frames from a window that was not recording.");
297 
298   Maybe<layers::CollectedFrames> maybeFrames;
299 
300   if (it != mCompositionRecorders.end()) {
301     maybeFrames.emplace(it->second->GetCollectedFrames());
302 
303     if (renderer) {
304       wr_renderer_release_composition_recorder_structures(
305           renderer->GetRenderer());
306     }
307 
308     mCompositionRecorders.erase(it);
309   }
310 
311   return maybeFrames;
312 }
313 
HandleFrameOneDoc(wr::WindowId aWindowId,bool aRender)314 void RenderThread::HandleFrameOneDoc(wr::WindowId aWindowId, bool aRender) {
315   if (mHasShutdown) {
316     return;
317   }
318 
319   if (!IsInRenderThread()) {
320     Loop()->PostTask(NewRunnableMethod<wr::WindowId, bool>(
321         "wr::RenderThread::HandleFrameOneDoc", this,
322         &RenderThread::HandleFrameOneDoc, aWindowId, aRender));
323     return;
324   }
325 
326   if (IsDestroyed(aWindowId)) {
327     return;
328   }
329 
330   if (mHandlingDeviceReset) {
331     return;
332   }
333 
334   bool render = false;
335   PendingFrameInfo frame;
336   {  // scope lock
337     auto windows = mWindowInfos.Lock();
338     auto it = windows->find(AsUint64(aWindowId));
339     if (it == windows->end()) {
340       MOZ_ASSERT(false);
341       return;
342     }
343 
344     WindowInfo* info = it->second;
345     PendingFrameInfo& frameInfo = info->mPendingFrames.front();
346     frameInfo.mFrameNeedsRender |= aRender;
347     render = frameInfo.mFrameNeedsRender;
348 
349     frame = frameInfo;
350   }
351 
352   // It is for ensuring that PrepareForUse() is called before
353   // RenderTextureHost::Lock().
354   HandlePrepareForUse();
355 
356   UpdateAndRender(aWindowId, frame.mStartId, frame.mStartTime, render,
357                   /* aReadbackSize */ Nothing(),
358                   /* aReadbackFormat */ Nothing(),
359                   /* aReadbackBuffer */ Nothing());
360 
361   {  // scope lock
362     auto windows = mWindowInfos.Lock();
363     auto it = windows->find(AsUint64(aWindowId));
364     if (it == windows->end()) {
365       MOZ_ASSERT(false);
366       return;
367     }
368     WindowInfo* info = it->second;
369     info->mPendingFrames.pop();
370   }
371 
372   // The start time is from WebRenderBridgeParent::CompositeToTarget. From that
373   // point until now (when the frame is finally pushed to the screen) is
374   // equivalent to the COMPOSITE_TIME metric in the non-WR codepath.
375   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
376                                           frame.mStartTime);
377 }
378 
WakeUp(wr::WindowId aWindowId)379 void RenderThread::WakeUp(wr::WindowId aWindowId) {
380   if (mHasShutdown) {
381     return;
382   }
383 
384   if (!IsInRenderThread()) {
385     Loop()->PostTask(NewRunnableMethod<wr::WindowId>(
386         "wr::RenderThread::WakeUp", this, &RenderThread::WakeUp, aWindowId));
387     return;
388   }
389 
390   if (IsDestroyed(aWindowId)) {
391     return;
392   }
393 
394   if (mHandlingDeviceReset) {
395     return;
396   }
397 
398   auto it = mRenderers.find(aWindowId);
399   MOZ_ASSERT(it != mRenderers.end());
400   if (it != mRenderers.end()) {
401     it->second->Update();
402   }
403 }
404 
RunEvent(wr::WindowId aWindowId,UniquePtr<RendererEvent> aEvent)405 void RenderThread::RunEvent(wr::WindowId aWindowId,
406                             UniquePtr<RendererEvent> aEvent) {
407   if (!IsInRenderThread()) {
408     Loop()->PostTask(
409         NewRunnableMethod<wr::WindowId, UniquePtr<RendererEvent>&&>(
410             "wr::RenderThread::RunEvent", this, &RenderThread::RunEvent,
411             aWindowId, std::move(aEvent)));
412     return;
413   }
414 
415   aEvent->Run(*this, aWindowId);
416   aEvent = nullptr;
417 }
418 
NotifyDidRender(layers::CompositorBridgeParent * aBridge,RefPtr<const WebRenderPipelineInfo> aInfo,VsyncId aCompositeStartId,TimeStamp aCompositeStart,TimeStamp aRenderStart,TimeStamp aEnd,bool aRender,RendererStats aStats)419 static void NotifyDidRender(layers::CompositorBridgeParent* aBridge,
420                             RefPtr<const WebRenderPipelineInfo> aInfo,
421                             VsyncId aCompositeStartId,
422                             TimeStamp aCompositeStart, TimeStamp aRenderStart,
423                             TimeStamp aEnd, bool aRender,
424                             RendererStats aStats) {
425   if (aRender && aBridge->GetWrBridge()) {
426     // We call this here to mimic the behavior in LayerManagerComposite, as to
427     // not change what Talos measures. That is, we do not record an empty frame
428     // as a frame.
429     aBridge->GetWrBridge()->RecordFrame();
430   }
431 
432   for (const auto& epoch : aInfo->Raw().epochs) {
433     aBridge->NotifyPipelineRendered(epoch.pipeline_id, epoch.epoch,
434                                     aCompositeStartId, aCompositeStart,
435                                     aRenderStart, aEnd, &aStats);
436   }
437 
438   if (aBridge->GetWrBridge()) {
439     aBridge->GetWrBridge()->CompositeIfNeeded();
440   }
441 }
442 
NotifyDidStartRender(layers::CompositorBridgeParent * aBridge)443 static void NotifyDidStartRender(layers::CompositorBridgeParent* aBridge) {
444   // Starting a render will change mIsRendering, and potentially
445   // change whether we can allow the bridge to intiate another frame.
446   if (aBridge->GetWrBridge()) {
447     aBridge->GetWrBridge()->CompositeIfNeeded();
448   }
449 }
450 
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)451 void RenderThread::UpdateAndRender(
452     wr::WindowId aWindowId, const VsyncId& aStartId,
453     const TimeStamp& aStartTime, bool aRender,
454     const Maybe<gfx::IntSize>& aReadbackSize,
455     const Maybe<wr::ImageFormat>& aReadbackFormat,
456     const Maybe<Range<uint8_t>>& aReadbackBuffer) {
457   AUTO_PROFILER_TRACING_MARKER("Paint", "Composite", GRAPHICS);
458   MOZ_ASSERT(IsInRenderThread());
459   MOZ_ASSERT(aRender || aReadbackBuffer.isNothing());
460 
461   auto it = mRenderers.find(aWindowId);
462   MOZ_ASSERT(it != mRenderers.end());
463   if (it == mRenderers.end()) {
464     return;
465   }
466 
467   TimeStamp start = TimeStamp::Now();
468 
469   auto& renderer = it->second;
470 
471   layers::CompositorThread()->Dispatch(
472       NewRunnableFunction("NotifyDidStartRenderRunnable", &NotifyDidStartRender,
473                           renderer->GetCompositorBridge()));
474 
475   wr::RenderedFrameId latestFrameId;
476   RendererStats stats = {0};
477   if (aRender) {
478     latestFrameId = renderer->UpdateAndRender(aReadbackSize, aReadbackFormat,
479                                               aReadbackBuffer, &stats);
480   } else {
481     renderer->Update();
482   }
483   // Check graphics reset status even when rendering is skipped.
484   renderer->CheckGraphicsResetStatus();
485 
486   TimeStamp end = TimeStamp::Now();
487   RefPtr<const WebRenderPipelineInfo> info = renderer->FlushPipelineInfo();
488 
489   layers::CompositorThread()->Dispatch(
490       NewRunnableFunction("NotifyDidRenderRunnable", &NotifyDidRender,
491                           renderer->GetCompositorBridge(), info, aStartId,
492                           aStartTime, start, end, aRender, stats));
493 
494   if (latestFrameId.IsValid()) {
495     auto recorderIt = mCompositionRecorders.find(aWindowId);
496     if (recorderIt != mCompositionRecorders.end() &&
497         renderer->EnsureAsyncScreenshot()) {
498       recorderIt->second->MaybeRecordFrame(renderer->GetRenderer(), info.get());
499     }
500   }
501 
502   if (latestFrameId.IsValid()) {
503     // Wait for GPU after posting NotifyDidRender, since the wait is not
504     // necessary for the NotifyDidRender.
505     // The wait is necessary for Textures recycling of AsyncImagePipelineManager
506     // and for avoiding GPU queue is filled with too much tasks.
507     // WaitForGPU's implementation is different for each platform.
508     renderer->WaitForGPU();
509   }
510 
511   if (!aRender) {
512     // Update frame id for NotifyPipelinesUpdated() when rendering does not
513     // happen.
514     latestFrameId = renderer->UpdateFrameId();
515   }
516 
517   RenderedFrameId lastCompletedFrameId = renderer->GetLastCompletedFrameId();
518 
519   RefPtr<layers::AsyncImagePipelineManager> pipelineMgr =
520       renderer->GetCompositorBridge()->GetAsyncImagePipelineManager();
521   // pipelineMgr should always be non-null here because it is only nulled out
522   // after the WebRenderAPI instance for the CompositorBridgeParent is
523   // destroyed, and that destruction blocks until the renderer thread has
524   // removed the relevant renderer. And after that happens we should never reach
525   // this code at all; it would bail out at the mRenderers.find check above.
526   MOZ_ASSERT(pipelineMgr);
527   pipelineMgr->NotifyPipelinesUpdated(info, latestFrameId,
528                                       lastCompletedFrameId);
529 }
530 
Pause(wr::WindowId aWindowId)531 void RenderThread::Pause(wr::WindowId aWindowId) {
532   MOZ_ASSERT(IsInRenderThread());
533 
534   auto it = mRenderers.find(aWindowId);
535   MOZ_ASSERT(it != mRenderers.end());
536   if (it == mRenderers.end()) {
537     return;
538   }
539   auto& renderer = it->second;
540   renderer->Pause();
541 }
542 
Resume(wr::WindowId aWindowId)543 bool RenderThread::Resume(wr::WindowId aWindowId) {
544   MOZ_ASSERT(IsInRenderThread());
545 
546   auto it = mRenderers.find(aWindowId);
547   MOZ_ASSERT(it != mRenderers.end());
548   if (it == mRenderers.end()) {
549     return false;
550   }
551   auto& renderer = it->second;
552   return renderer->Resume();
553 }
554 
TooManyPendingFrames(wr::WindowId aWindowId)555 bool RenderThread::TooManyPendingFrames(wr::WindowId aWindowId) {
556   const int64_t maxFrameCount = 1;
557 
558   // Too many pending frames if pending frames exit more than maxFrameCount
559   // or if RenderBackend is still processing a frame.
560 
561   auto windows = mWindowInfos.Lock();
562   auto it = windows->find(AsUint64(aWindowId));
563   if (it == windows->end()) {
564     MOZ_ASSERT(false);
565     return true;
566   }
567   WindowInfo* info = it->second;
568 
569   if (info->PendingCount() > maxFrameCount) {
570     return true;
571   }
572   // If there is no ongoing frame build, we accept a new frame.
573   return info->mPendingFrameBuild > 0;
574 }
575 
IsDestroyed(wr::WindowId aWindowId)576 bool RenderThread::IsDestroyed(wr::WindowId aWindowId) {
577   auto windows = mWindowInfos.Lock();
578   auto it = windows->find(AsUint64(aWindowId));
579   if (it == windows->end()) {
580     return true;
581   }
582 
583   return it->second->mIsDestroyed;
584 }
585 
SetDestroyed(wr::WindowId aWindowId)586 void RenderThread::SetDestroyed(wr::WindowId aWindowId) {
587   auto windows = mWindowInfos.Lock();
588   auto it = windows->find(AsUint64(aWindowId));
589   if (it == windows->end()) {
590     MOZ_ASSERT(false);
591     return;
592   }
593   it->second->mIsDestroyed = true;
594 }
595 
IncPendingFrameCount(wr::WindowId aWindowId,const VsyncId & aStartId,const TimeStamp & aStartTime)596 void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId,
597                                         const VsyncId& aStartId,
598                                         const TimeStamp& aStartTime) {
599   auto windows = mWindowInfos.Lock();
600   auto it = windows->find(AsUint64(aWindowId));
601   if (it == windows->end()) {
602     MOZ_ASSERT(false);
603     return;
604   }
605   it->second->mPendingFrameBuild++;
606   it->second->mPendingFrames.push(
607       PendingFrameInfo{aStartTime, aStartId, false});
608 }
609 
DecPendingFrameBuildCount(wr::WindowId aWindowId)610 void RenderThread::DecPendingFrameBuildCount(wr::WindowId aWindowId) {
611   auto windows = mWindowInfos.Lock();
612   auto it = windows->find(AsUint64(aWindowId));
613   if (it == windows->end()) {
614     MOZ_ASSERT(false);
615     return;
616   }
617   WindowInfo* info = it->second;
618   MOZ_RELEASE_ASSERT(info->mPendingFrameBuild >= 1);
619   info->mPendingFrameBuild--;
620 }
621 
RegisterExternalImage(uint64_t aExternalImageId,already_AddRefed<RenderTextureHost> aTexture)622 void RenderThread::RegisterExternalImage(
623     uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture) {
624   MutexAutoLock lock(mRenderTextureMapLock);
625 
626   if (mHasShutdown) {
627     return;
628   }
629   MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end());
630   mRenderTextures.emplace(aExternalImageId, std::move(aTexture));
631 }
632 
UnregisterExternalImage(uint64_t aExternalImageId)633 void RenderThread::UnregisterExternalImage(uint64_t aExternalImageId) {
634   MutexAutoLock lock(mRenderTextureMapLock);
635   if (mHasShutdown) {
636     return;
637   }
638   auto it = mRenderTextures.find(aExternalImageId);
639   MOZ_ASSERT(it != mRenderTextures.end());
640   if (it == mRenderTextures.end()) {
641     return;
642   }
643   if (!IsInRenderThread()) {
644     // The RenderTextureHost should be released in render thread. So, post the
645     // deletion task here.
646     // The shmem and raw buffer are owned by compositor ipc channel. It's
647     // possible that RenderTextureHost is still exist after the shmem/raw buffer
648     // deletion. Then the buffer in RenderTextureHost becomes invalid. It's fine
649     // for this situation. Gecko will only release the buffer if WR doesn't need
650     // it. So, no one will access the invalid buffer in RenderTextureHost.
651     RefPtr<RenderTextureHost> texture = it->second;
652     mRenderTextures.erase(it);
653     mRenderTexturesDeferred.emplace_back(std::move(texture));
654     Loop()->PostTask(NewRunnableMethod(
655         "RenderThread::DeferredRenderTextureHostDestroy", this,
656         &RenderThread::DeferredRenderTextureHostDestroy));
657   } else {
658     mRenderTextures.erase(it);
659   }
660 }
661 
PrepareForUse(uint64_t aExternalImageId)662 void RenderThread::PrepareForUse(uint64_t aExternalImageId) {
663   MOZ_ASSERT(!IsInRenderThread());
664 
665   MutexAutoLock lock(mRenderTextureMapLock);
666   if (mHasShutdown) {
667     return;
668   }
669 
670   auto it = mRenderTextures.find(aExternalImageId);
671   MOZ_ASSERT(it != mRenderTextures.end());
672   if (it == mRenderTextures.end()) {
673     return;
674   }
675 
676   RefPtr<RenderTextureHost> texture = it->second;
677   mRenderTexturesPrepareForUse.emplace_back(std::move(texture));
678   Loop()->PostTask(NewRunnableMethod("RenderThread::HandlePrepareForUse", this,
679                                      &RenderThread::HandlePrepareForUse));
680 }
681 
NotifyNotUsed(uint64_t aExternalImageId)682 void RenderThread::NotifyNotUsed(uint64_t aExternalImageId) {
683   MOZ_ASSERT(!IsInRenderThread());
684 
685   MutexAutoLock lock(mRenderTextureMapLock);
686   if (mHasShutdown) {
687     return;
688   }
689 
690   auto it = mRenderTextures.find(aExternalImageId);
691   MOZ_ASSERT(it != mRenderTextures.end());
692   if (it == mRenderTextures.end()) {
693     return;
694   }
695   RefPtr<RenderTextureHost> texture = it->second;
696   RefPtr<Runnable> task =
697       NS_NewRunnableFunction("RenderThread::DoNotifyNotUsed",
698                              [renderTexture = std::move(texture)]() -> void {
699                                renderTexture->NotifyNotUsed();
700                              });
701   Loop()->PostTask(task.forget());
702 }
703 
NofityForUse(uint64_t aExternalImageId)704 void RenderThread::NofityForUse(uint64_t aExternalImageId) {
705   MOZ_ASSERT(RenderThread::IsInRenderThread());
706 
707   HandlePrepareForUse();
708 
709   {
710     MutexAutoLock lock(mRenderTextureMapLock);
711     if (mHasShutdown) {
712       return;
713     }
714     auto it = mRenderTextures.find(aExternalImageId);
715     MOZ_ASSERT(it != mRenderTextures.end());
716     if (it == mRenderTextures.end()) {
717       return;
718     }
719     it->second->NofityForUse();
720   }
721 }
722 
UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId)723 void RenderThread::UnregisterExternalImageDuringShutdown(
724     uint64_t aExternalImageId) {
725   MOZ_ASSERT(IsInRenderThread());
726   MutexAutoLock lock(mRenderTextureMapLock);
727   MOZ_ASSERT(mHasShutdown);
728   MOZ_ASSERT(mRenderTextures.find(aExternalImageId) != mRenderTextures.end());
729   mRenderTextures.erase(aExternalImageId);
730 }
731 
HandlePrepareForUse()732 void RenderThread::HandlePrepareForUse() {
733   MOZ_ASSERT(IsInRenderThread());
734   MutexAutoLock lock(mRenderTextureMapLock);
735   for (auto& texture : mRenderTexturesPrepareForUse) {
736     texture->PrepareForUse();
737   }
738   mRenderTexturesPrepareForUse.clear();
739 }
740 
DeferredRenderTextureHostDestroy()741 void RenderThread::DeferredRenderTextureHostDestroy() {
742   MutexAutoLock lock(mRenderTextureMapLock);
743   mRenderTexturesDeferred.clear();
744 }
745 
GetRenderTexture(wr::ExternalImageId aExternalImageId)746 RenderTextureHost* RenderThread::GetRenderTexture(
747     wr::ExternalImageId aExternalImageId) {
748   MOZ_ASSERT(IsInRenderThread());
749 
750   MutexAutoLock lock(mRenderTextureMapLock);
751   auto it = mRenderTextures.find(AsUint64(aExternalImageId));
752   MOZ_ASSERT(it != mRenderTextures.end());
753   if (it == mRenderTextures.end()) {
754     return nullptr;
755   }
756   return it->second;
757 }
758 
InitDeviceTask()759 void RenderThread::InitDeviceTask() {
760   MOZ_ASSERT(IsInRenderThread());
761   MOZ_ASSERT(!mSharedGL);
762 
763   mSharedGL = CreateGLContext();
764   if (gfx::gfxVars::UseWebRenderProgramBinaryDisk() &&
765       !gfx::gfxVars::UseSoftwareWebRender()) {
766     mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw());
767   }
768   // Query the shared GL context to force the
769   // lazy initialization to happen now.
770   SharedGL();
771 }
772 
HandleDeviceReset(const char * aWhere,bool aNotify)773 void RenderThread::HandleDeviceReset(const char* aWhere, bool aNotify) {
774   MOZ_ASSERT(IsInRenderThread());
775 
776   if (mHandlingDeviceReset) {
777     return;
778   }
779 
780   if (aNotify) {
781     gfxCriticalNote << "GFX: RenderThread detected a device reset in "
782                     << aWhere;
783     if (XRE_IsGPUProcess()) {
784       gfx::GPUParent::GetSingleton()->NotifyDeviceReset();
785     }
786   }
787 
788   {
789     MutexAutoLock lock(mRenderTextureMapLock);
790     mRenderTexturesDeferred.clear();
791     for (const auto& entry : mRenderTextures) {
792       entry.second->ClearCachedResources();
793     }
794   }
795 
796   mHandlingDeviceReset = true;
797   // All RenderCompositors will be destroyed by
798   // GPUChild::RecvNotifyDeviceReset()
799 }
800 
IsHandlingDeviceReset()801 bool RenderThread::IsHandlingDeviceReset() {
802   MOZ_ASSERT(IsInRenderThread());
803   return mHandlingDeviceReset;
804 }
805 
SimulateDeviceReset()806 void RenderThread::SimulateDeviceReset() {
807   if (!IsInRenderThread()) {
808     Loop()->PostTask(NewRunnableMethod("RenderThread::SimulateDeviceReset",
809                                        this,
810                                        &RenderThread::SimulateDeviceReset));
811   } else {
812     // When this function is called GPUProcessManager::SimulateDeviceReset()
813     // already triggers destroying all CompositorSessions before re-creating
814     // them.
815     HandleDeviceReset("SimulateDeviceReset", /* aNotify */ false);
816   }
817 }
818 
DoNotifyWebRenderError(WebRenderError aError)819 static void DoNotifyWebRenderError(WebRenderError aError) {
820   layers::CompositorManagerParent::NotifyWebRenderError(aError);
821 }
822 
HandleWebRenderError(WebRenderError aError)823 void RenderThread::HandleWebRenderError(WebRenderError aError) {
824   if (mHandlingWebRenderError) {
825     return;
826   }
827 
828   layers::CompositorThread()->Dispatch(NewRunnableFunction(
829       "DoNotifyWebRenderErrorRunnable", &DoNotifyWebRenderError, aError));
830   {
831     MutexAutoLock lock(mRenderTextureMapLock);
832     mRenderTexturesDeferred.clear();
833     for (const auto& entry : mRenderTextures) {
834       entry.second->ClearCachedResources();
835     }
836   }
837   mHandlingWebRenderError = true;
838   // WebRender is going to be disabled by
839   // GPUProcessManager::NotifyWebRenderError()
840 }
841 
IsHandlingWebRenderError()842 bool RenderThread::IsHandlingWebRenderError() {
843   MOZ_ASSERT(IsInRenderThread());
844   return mHandlingWebRenderError;
845 }
846 
SharedGL()847 gl::GLContext* RenderThread::SharedGL() {
848   MOZ_ASSERT(IsInRenderThread());
849   if (!mSharedGL) {
850     mSharedGL = CreateGLContext();
851     mShaders = nullptr;
852   }
853   if (mSharedGL && !mShaders && !gfx::gfxVars::UseSoftwareWebRender()) {
854     mShaders = MakeUnique<WebRenderShaders>(mSharedGL, mProgramCache.get());
855   }
856 
857   return mSharedGL.get();
858 }
859 
ClearSharedGL()860 void RenderThread::ClearSharedGL() {
861   MOZ_ASSERT(IsInRenderThread());
862   if (mSurfacePool) {
863     mSurfacePool->DestroyGLResourcesForContext(mSharedGL);
864   }
865   mShaders = nullptr;
866   mSharedGL = nullptr;
867 }
868 
SharedSurfacePool()869 RefPtr<layers::SurfacePool> RenderThread::SharedSurfacePool() {
870 #ifdef XP_MACOSX
871   if (!mSurfacePool) {
872     size_t poolSizeLimit =
873         StaticPrefs::gfx_webrender_compositor_surface_pool_size_AtStartup();
874     mSurfacePool = layers::SurfacePool::Create(poolSizeLimit);
875   }
876 #endif
877   return mSurfacePool;
878 }
879 
ClearSharedSurfacePool()880 void RenderThread::ClearSharedSurfacePool() { mSurfacePool = nullptr; }
881 
DebugMessageCallback(GLenum aSource,GLenum aType,GLuint aId,GLenum aSeverity,GLsizei aLength,const GLchar * aMessage,const GLvoid * aUserParam)882 static void GLAPIENTRY DebugMessageCallback(GLenum aSource, GLenum aType,
883                                             GLuint aId, GLenum aSeverity,
884                                             GLsizei aLength,
885                                             const GLchar* aMessage,
886                                             const GLvoid* aUserParam) {
887   constexpr const char* kContextLost = "Context has been lost.";
888 
889   if (StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() &&
890       aSeverity == LOCAL_GL_DEBUG_SEVERITY_HIGH) {
891     auto message = std::string(aMessage, aLength);
892     // When content lost happned, error messages are flooded by its message.
893     if (message != kContextLost) {
894       gfxCriticalNote << message;
895     } else {
896       gfxCriticalNoteOnce << message;
897     }
898   }
899 
900   if (StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup()) {
901     gl::GLContext* gl = (gl::GLContext*)aUserParam;
902     gl->DebugCallback(aSource, aType, aId, aSeverity, aLength, aMessage);
903   }
904 }
905 
906 // static
MaybeEnableGLDebugMessage(gl::GLContext * aGLContext)907 void RenderThread::MaybeEnableGLDebugMessage(gl::GLContext* aGLContext) {
908   if (!aGLContext) {
909     return;
910   }
911 
912   bool enableDebugMessage =
913       StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() ||
914       StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup();
915 
916   if (enableDebugMessage &&
917       aGLContext->IsExtensionSupported(gl::GLContext::KHR_debug)) {
918     aGLContext->fEnable(LOCAL_GL_DEBUG_OUTPUT);
919     aGLContext->fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
920     aGLContext->fDebugMessageCallback(&DebugMessageCallback, (void*)aGLContext);
921     aGLContext->fDebugMessageControl(LOCAL_GL_DONT_CARE, LOCAL_GL_DONT_CARE,
922                                      LOCAL_GL_DONT_CARE, 0, nullptr, true);
923   }
924 }
925 
WebRenderShaders(gl::GLContext * gl,WebRenderProgramCache * programCache)926 WebRenderShaders::WebRenderShaders(gl::GLContext* gl,
927                                    WebRenderProgramCache* programCache) {
928   mGL = gl;
929   mShaders = wr_shaders_new(gl, programCache ? programCache->Raw() : nullptr);
930 }
931 
~WebRenderShaders()932 WebRenderShaders::~WebRenderShaders() {
933   wr_shaders_delete(mShaders, mGL.get());
934 }
935 
WebRenderThreadPool(bool low_priority)936 WebRenderThreadPool::WebRenderThreadPool(bool low_priority) {
937   mThreadPool = wr_thread_pool_new(low_priority);
938 }
939 
~WebRenderThreadPool()940 WebRenderThreadPool::~WebRenderThreadPool() { Release(); }
941 
Release()942 void WebRenderThreadPool::Release() {
943   if (mThreadPool) {
944     wr_thread_pool_delete(mThreadPool);
945     mThreadPool = nullptr;
946   }
947 }
948 
WebRenderProgramCache(wr::WrThreadPool * aThreadPool)949 WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool) {
950   MOZ_ASSERT(aThreadPool);
951 
952   nsAutoString path;
953   if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
954     path.Append(gfx::gfxVars::ProfDirectory());
955   }
956   mProgramCache = wr_program_cache_new(&path, aThreadPool);
957   if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
958     wr_try_load_startup_shaders_from_disk(mProgramCache);
959   }
960 }
961 
~WebRenderProgramCache()962 WebRenderProgramCache::~WebRenderProgramCache() {
963   wr_program_cache_delete(mProgramCache);
964 }
965 
966 }  // namespace wr
967 }  // namespace mozilla
968 
969 #ifdef XP_WIN
CreateGLContextANGLE()970 static already_AddRefed<gl::GLContext> CreateGLContextANGLE() {
971   nsCString discardFailureId;
972   if (!gl::GLLibraryEGL::EnsureInitialized(/* forceAccel */ true,
973                                            &discardFailureId)) {
974     gfxCriticalNote << "Failed to load EGL library: " << discardFailureId.get();
975     return nullptr;
976   }
977 
978   auto* egl = gl::GLLibraryEGL::Get();
979   gl::CreateContextFlags flags = gl::CreateContextFlags::PREFER_ES3 |
980                                  gl::CreateContextFlags::PREFER_ROBUSTNESS;
981 
982   if (egl->IsExtensionSupported(
983           gl::GLLibraryEGL::MOZ_create_context_provoking_vertex_dont_care)) {
984     flags |= gl::CreateContextFlags::PROVOKING_VERTEX_DONT_CARE;
985   }
986 
987   // Create GLContext with dummy EGLSurface, the EGLSurface is not used.
988   // Instread we override it with EGLSurface of SwapChain's back buffer.
989   RefPtr<gl::GLContext> gl =
990       gl::GLContextProviderEGL::CreateHeadless(flags, &discardFailureId);
991   if (!gl || !gl->IsANGLE()) {
992     gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: "
993                     << gfx::hexa(gl.get());
994     return nullptr;
995   }
996 
997   if (!gl->MakeCurrent()) {
998     gfxCriticalNote << "Failed GL context creation for WebRender: "
999                     << gfx::hexa(gl.get());
1000     return nullptr;
1001   }
1002 
1003   return gl.forget();
1004 }
1005 #endif
1006 
1007 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WAYLAND)
CreateGLContextEGL()1008 static already_AddRefed<gl::GLContext> CreateGLContextEGL() {
1009   nsCString discardFailureId;
1010   if (!gl::GLLibraryEGL::EnsureInitialized(/* forceAccel */ true,
1011                                            &discardFailureId)) {
1012     gfxCriticalNote << "Failed to load EGL library: " << discardFailureId.get();
1013     return nullptr;
1014   }
1015   // Create GLContext with dummy EGLSurface.
1016   RefPtr<gl::GLContext> gl =
1017       gl::GLContextProviderEGL::CreateForCompositorWidget(
1018           nullptr, /* aWebRender */ true, /* aForceAccelerated */ true);
1019   if (!gl || !gl->MakeCurrent()) {
1020     gfxCriticalNote << "Failed GL context creation for WebRender: "
1021                     << gfx::hexa(gl.get());
1022     return nullptr;
1023   }
1024   return gl.forget();
1025 }
1026 #endif
1027 
1028 #ifdef XP_MACOSX
CreateGLContextCGL()1029 static already_AddRefed<gl::GLContext> CreateGLContextCGL() {
1030   nsCString failureUnused;
1031   return gl::GLContextProvider::CreateHeadless(
1032       gl::CreateContextFlags::ALLOW_OFFLINE_RENDERER |
1033           gl::CreateContextFlags::FORCE_ENABLE_HARDWARE,
1034       &failureUnused);
1035 }
1036 #endif
1037 
CreateGLContext()1038 static already_AddRefed<gl::GLContext> CreateGLContext() {
1039   RefPtr<gl::GLContext> gl;
1040 
1041 #ifdef XP_WIN
1042   if (gfx::gfxVars::UseWebRenderANGLE()) {
1043     gl = CreateGLContextANGLE();
1044   }
1045 #elif defined(MOZ_WIDGET_ANDROID)
1046   gl = CreateGLContextEGL();
1047 #elif defined(MOZ_WAYLAND)
1048   if (gdk_display_get_default() &&
1049       !GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
1050     gl = CreateGLContextEGL();
1051   }
1052 #elif XP_MACOSX
1053   gl = CreateGLContextCGL();
1054 #endif
1055 
1056   wr::RenderThread::MaybeEnableGLDebugMessage(gl);
1057 
1058   return gl.forget();
1059 }
1060 
1061 extern "C" {
1062 
wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId)1063 void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId) {
1064   mozilla::wr::RenderThread::Get()->WakeUp(aWindowId);
1065 }
1066 
wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId)1067 void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId) {
1068   mozilla::wr::RenderThread::Get()->DecPendingFrameBuildCount(aWindowId);
1069   mozilla::wr::RenderThread::Get()->HandleFrameOneDoc(aWindowId,
1070                                                       /* aRender */ true);
1071 }
1072 
wr_notifier_nop_frame_done(mozilla::wr::WrWindowId aWindowId)1073 void wr_notifier_nop_frame_done(mozilla::wr::WrWindowId aWindowId) {
1074   mozilla::wr::RenderThread::Get()->DecPendingFrameBuildCount(aWindowId);
1075   mozilla::wr::RenderThread::Get()->HandleFrameOneDoc(aWindowId,
1076                                                       /* aRender */ false);
1077 }
1078 
wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,size_t aRawEvent)1079 void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,
1080                                 size_t aRawEvent) {
1081   mozilla::UniquePtr<mozilla::wr::RendererEvent> evt(
1082       reinterpret_cast<mozilla::wr::RendererEvent*>(aRawEvent));
1083   mozilla::wr::RenderThread::Get()->RunEvent(mozilla::wr::WindowId(aWindowId),
1084                                              std::move(evt));
1085 }
1086 
wr_schedule_render(mozilla::wr::WrWindowId aWindowId)1087 void wr_schedule_render(mozilla::wr::WrWindowId aWindowId) {
1088   RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
1089       CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
1090   if (cbp) {
1091     cbp->ScheduleRenderOnCompositorThread();
1092   }
1093 }
1094 
NotifyDidSceneBuild(RefPtr<layers::CompositorBridgeParent> aBridge,RefPtr<const wr::WebRenderPipelineInfo> aInfo)1095 static void NotifyDidSceneBuild(RefPtr<layers::CompositorBridgeParent> aBridge,
1096                                 RefPtr<const wr::WebRenderPipelineInfo> aInfo) {
1097   aBridge->NotifyDidSceneBuild(aInfo);
1098 }
1099 
wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId,mozilla::wr::WrPipelineInfo * aInfo)1100 void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId,
1101                              mozilla::wr::WrPipelineInfo* aInfo) {
1102   RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
1103       CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
1104   RefPtr<wr::WebRenderPipelineInfo> info = new wr::WebRenderPipelineInfo();
1105   info->Raw() = std::move(*aInfo);
1106   if (cbp) {
1107     layers::CompositorThread()->Dispatch(NewRunnableFunction(
1108         "NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp, info));
1109   }
1110 }
1111 
1112 }  // extern C
1113