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