/src/mozilla-central/gfx/webrender_bindings/RenderThread.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "base/task.h" |
8 | | #include "GeckoProfiler.h" |
9 | | #include "RenderThread.h" |
10 | | #include "nsThreadUtils.h" |
11 | | #include "mtransport/runnable_utils.h" |
12 | | #include "mozilla/layers/AsyncImagePipelineManager.h" |
13 | | #include "mozilla/gfx/GPUParent.h" |
14 | | #include "mozilla/layers/CompositorThread.h" |
15 | | #include "mozilla/layers/CompositorBridgeParent.h" |
16 | | #include "mozilla/layers/WebRenderBridgeParent.h" |
17 | | #include "mozilla/layers/SharedSurfacesParent.h" |
18 | | #include "mozilla/StaticPtr.h" |
19 | | #include "mozilla/Telemetry.h" |
20 | | #include "mozilla/webrender/RendererOGL.h" |
21 | | #include "mozilla/webrender/RenderTextureHost.h" |
22 | | #include "mozilla/widget/CompositorWidget.h" |
23 | | |
24 | | #ifdef XP_WIN |
25 | | #include "mozilla/widget/WinCompositorWindowThread.h" |
26 | | #endif |
27 | | |
28 | | namespace mozilla { |
29 | | namespace wr { |
30 | | |
31 | | static StaticRefPtr<RenderThread> sRenderThread; |
32 | | |
33 | | RenderThread::RenderThread(base::Thread* aThread) |
34 | | : mThread(aThread) |
35 | | , mFrameCountMapLock("RenderThread.mFrameCountMapLock") |
36 | | , mRenderTextureMapLock("RenderThread.mRenderTextureMapLock") |
37 | | , mHasShutdown(false) |
38 | | , mHandlingDeviceReset(false) |
39 | 0 | { |
40 | 0 |
|
41 | 0 | } |
42 | | |
43 | | RenderThread::~RenderThread() |
44 | 0 | { |
45 | 0 | MOZ_ASSERT(mRenderTexturesDeferred.empty()); |
46 | 0 | delete mThread; |
47 | 0 | } |
48 | | |
49 | | // static |
50 | | RenderThread* |
51 | | RenderThread::Get() |
52 | 0 | { |
53 | 0 | return sRenderThread; |
54 | 0 | } |
55 | | |
56 | | // static |
57 | | void |
58 | | RenderThread::Start() |
59 | 0 | { |
60 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
61 | 0 | MOZ_ASSERT(!sRenderThread); |
62 | 0 |
|
63 | 0 | base::Thread* thread = new base::Thread("Renderer"); |
64 | 0 |
|
65 | 0 | base::Thread::Options options; |
66 | 0 | // TODO(nical): The compositor thread has a bunch of specific options, see |
67 | 0 | // which ones make sense here. |
68 | 0 | if (!thread->StartWithOptions(options)) { |
69 | 0 | delete thread; |
70 | 0 | return; |
71 | 0 | } |
72 | 0 | |
73 | 0 | sRenderThread = new RenderThread(thread); |
74 | | #ifdef XP_WIN |
75 | | widget::WinCompositorWindowThread::Start(); |
76 | | #endif |
77 | | layers::SharedSurfacesParent::Initialize(); |
78 | 0 |
|
79 | 0 | if (XRE_IsGPUProcess() && |
80 | 0 | gfx::gfxVars::UseWebRenderProgramBinary()) { |
81 | 0 | MOZ_ASSERT(gfx::gfxVars::UseWebRender()); |
82 | 0 | // Initialize program cache if necessary |
83 | 0 | RefPtr<Runnable> runnable = WrapRunnable( |
84 | 0 | RefPtr<RenderThread>(sRenderThread.get()), |
85 | 0 | &RenderThread::ProgramCacheTask); |
86 | 0 | sRenderThread->Loop()->PostTask(runnable.forget()); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | // static |
91 | | void |
92 | | RenderThread::ShutDown() |
93 | 0 | { |
94 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
95 | 0 | MOZ_ASSERT(sRenderThread); |
96 | 0 |
|
97 | 0 | { |
98 | 0 | MutexAutoLock lock(sRenderThread->mRenderTextureMapLock); |
99 | 0 | sRenderThread->mHasShutdown = true; |
100 | 0 | } |
101 | 0 |
|
102 | 0 | layers::SynchronousTask task("RenderThread"); |
103 | 0 | RefPtr<Runnable> runnable = WrapRunnable( |
104 | 0 | RefPtr<RenderThread>(sRenderThread.get()), |
105 | 0 | &RenderThread::ShutDownTask, |
106 | 0 | &task); |
107 | 0 | sRenderThread->Loop()->PostTask(runnable.forget()); |
108 | 0 | task.Wait(); |
109 | 0 |
|
110 | 0 | sRenderThread = nullptr; |
111 | | #ifdef XP_WIN |
112 | | widget::WinCompositorWindowThread::ShutDown(); |
113 | | #endif |
114 | | } |
115 | | |
116 | | extern void ClearAllBlobImageResources(); |
117 | | |
118 | | void |
119 | | RenderThread::ShutDownTask(layers::SynchronousTask* aTask) |
120 | 0 | { |
121 | 0 | layers::AutoCompleteTask complete(aTask); |
122 | 0 | MOZ_ASSERT(IsInRenderThread()); |
123 | 0 |
|
124 | 0 | // Releasing on the render thread will allow us to avoid dispatching to remove |
125 | 0 | // remaining textures from the texture map. |
126 | 0 | layers::SharedSurfacesParent::Shutdown(); |
127 | 0 |
|
128 | 0 | ClearAllBlobImageResources(); |
129 | 0 | } |
130 | | |
131 | | // static |
132 | | MessageLoop* |
133 | | RenderThread::Loop() |
134 | 0 | { |
135 | 0 | return sRenderThread ? sRenderThread->mThread->message_loop() : nullptr; |
136 | 0 | } |
137 | | |
138 | | // static |
139 | | bool |
140 | | RenderThread::IsInRenderThread() |
141 | 0 | { |
142 | 0 | return sRenderThread && sRenderThread->mThread->thread_id() == PlatformThread::CurrentId(); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | RenderThread::DoAccumulateMemoryReport(MemoryReport aReport, const RefPtr<MemoryReportPromise::Private>& aPromise) |
147 | 0 | { |
148 | 0 | MOZ_ASSERT(IsInRenderThread()); |
149 | 0 | for (auto& r: mRenderers) { |
150 | 0 | wr_renderer_accumulate_memory_report(r.second->GetRenderer(), &aReport); |
151 | 0 | } |
152 | 0 |
|
153 | 0 | aPromise->Resolve(aReport, __func__); |
154 | 0 | } |
155 | | |
156 | | // static |
157 | | RefPtr<MemoryReportPromise> |
158 | | RenderThread::AccumulateMemoryReport(MemoryReport aInitial) |
159 | 0 | { |
160 | 0 | RefPtr<MemoryReportPromise::Private> p = new MemoryReportPromise::Private(__func__); |
161 | 0 | MOZ_ASSERT(!IsInRenderThread()); |
162 | 0 | MOZ_ASSERT(Get()); |
163 | 0 | Get()->Loop()->PostTask( |
164 | 0 | NewRunnableMethod<MemoryReport, RefPtr<MemoryReportPromise::Private>>( |
165 | 0 | "wr::RenderThread::DoAccumulateMemoryReport", |
166 | 0 | Get(), |
167 | 0 | &RenderThread::DoAccumulateMemoryReport, |
168 | 0 | aInitial, p |
169 | 0 | ) |
170 | 0 | ); |
171 | 0 |
|
172 | 0 | return p; |
173 | 0 | } |
174 | | |
175 | | void |
176 | | RenderThread::AddRenderer(wr::WindowId aWindowId, UniquePtr<RendererOGL> aRenderer) |
177 | 0 | { |
178 | 0 | MOZ_ASSERT(IsInRenderThread()); |
179 | 0 |
|
180 | 0 | if (mHasShutdown) { |
181 | 0 | return; |
182 | 0 | } |
183 | 0 | |
184 | 0 | mRenderers[aWindowId] = std::move(aRenderer); |
185 | 0 |
|
186 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
187 | 0 | mWindowInfos.emplace(AsUint64(aWindowId), new WindowInfo()); |
188 | 0 | } |
189 | | |
190 | | void |
191 | | RenderThread::RemoveRenderer(wr::WindowId aWindowId) |
192 | 0 | { |
193 | 0 | MOZ_ASSERT(IsInRenderThread()); |
194 | 0 |
|
195 | 0 | if (mHasShutdown) { |
196 | 0 | return; |
197 | 0 | } |
198 | 0 | |
199 | 0 | mRenderers.erase(aWindowId); |
200 | 0 |
|
201 | 0 | if (mRenderers.size() == 0 && mHandlingDeviceReset) { |
202 | 0 | mHandlingDeviceReset = false; |
203 | 0 | } |
204 | 0 |
|
205 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
206 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
207 | 0 | MOZ_ASSERT(it != mWindowInfos.end()); |
208 | 0 | WindowInfo* toDelete = it->second; |
209 | 0 | mWindowInfos.erase(it); |
210 | 0 | delete toDelete; |
211 | 0 | } |
212 | | |
213 | | RendererOGL* |
214 | | RenderThread::GetRenderer(wr::WindowId aWindowId) |
215 | 0 | { |
216 | 0 | MOZ_ASSERT(IsInRenderThread()); |
217 | 0 |
|
218 | 0 | auto it = mRenderers.find(aWindowId); |
219 | 0 | MOZ_ASSERT(it != mRenderers.end()); |
220 | 0 |
|
221 | 0 | if (it == mRenderers.end()) { |
222 | 0 | return nullptr; |
223 | 0 | } |
224 | 0 | |
225 | 0 | return it->second.get(); |
226 | 0 | } |
227 | | |
228 | | size_t |
229 | | RenderThread::RendererCount() |
230 | 0 | { |
231 | 0 | MOZ_ASSERT(IsInRenderThread()); |
232 | 0 | return mRenderers.size(); |
233 | 0 | } |
234 | | |
235 | | void |
236 | | RenderThread::NewFrameReady(wr::WindowId aWindowId) |
237 | 0 | { |
238 | 0 | if (mHasShutdown) { |
239 | 0 | return; |
240 | 0 | } |
241 | 0 | |
242 | 0 | if (!IsInRenderThread()) { |
243 | 0 | Loop()->PostTask( |
244 | 0 | NewRunnableMethod<wr::WindowId>("wr::RenderThread::NewFrameReady", |
245 | 0 | this, |
246 | 0 | &RenderThread::NewFrameReady, |
247 | 0 | aWindowId)); |
248 | 0 | return; |
249 | 0 | } |
250 | 0 | |
251 | 0 | if (IsDestroyed(aWindowId)) { |
252 | 0 | return; |
253 | 0 | } |
254 | 0 | |
255 | 0 | if (mHandlingDeviceReset) { |
256 | 0 | return; |
257 | 0 | } |
258 | 0 | |
259 | 0 | TimeStamp startTime; |
260 | 0 |
|
261 | 0 | { // scope lock |
262 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
263 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
264 | 0 | MOZ_ASSERT(it != mWindowInfos.end()); |
265 | 0 | WindowInfo* info = it->second; |
266 | 0 | MOZ_ASSERT(info->mPendingCount > 0); |
267 | 0 | startTime = info->mStartTimes.front(); |
268 | 0 | } |
269 | 0 |
|
270 | 0 | UpdateAndRender(aWindowId, startTime); |
271 | 0 | FrameRenderingComplete(aWindowId); |
272 | 0 | } |
273 | | |
274 | | void |
275 | | RenderThread::WakeUp(wr::WindowId aWindowId) |
276 | 0 | { |
277 | 0 | if (mHasShutdown) { |
278 | 0 | return; |
279 | 0 | } |
280 | 0 | |
281 | 0 | if (!IsInRenderThread()) { |
282 | 0 | Loop()->PostTask( |
283 | 0 | NewRunnableMethod<wr::WindowId>("wr::RenderThread::WakeUp", |
284 | 0 | this, |
285 | 0 | &RenderThread::WakeUp, |
286 | 0 | aWindowId)); |
287 | 0 | return; |
288 | 0 | } |
289 | 0 | |
290 | 0 | if (IsDestroyed(aWindowId)) { |
291 | 0 | return; |
292 | 0 | } |
293 | 0 | |
294 | 0 | if (mHandlingDeviceReset) { |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | |
298 | 0 | auto it = mRenderers.find(aWindowId); |
299 | 0 | MOZ_ASSERT(it != mRenderers.end()); |
300 | 0 | if (it != mRenderers.end()) { |
301 | 0 | it->second->Update(); |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | void |
306 | | RenderThread::RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent) |
307 | 0 | { |
308 | 0 | if (!IsInRenderThread()) { |
309 | 0 | Loop()->PostTask( |
310 | 0 | NewRunnableMethod<wr::WindowId, UniquePtr<RendererEvent>&&>( |
311 | 0 | "wr::RenderThread::RunEvent", |
312 | 0 | this, |
313 | 0 | &RenderThread::RunEvent, |
314 | 0 | aWindowId, |
315 | 0 | std::move(aEvent))); |
316 | 0 | return; |
317 | 0 | } |
318 | 0 | |
319 | 0 | aEvent->Run(*this, aWindowId); |
320 | 0 | aEvent = nullptr; |
321 | 0 | } |
322 | | |
323 | | static void |
324 | | NotifyDidRender(layers::CompositorBridgeParent* aBridge, |
325 | | wr::WrPipelineInfo aInfo, |
326 | | TimeStamp aStart, |
327 | | TimeStamp aEnd) |
328 | 0 | { |
329 | 0 | if (aBridge->GetWrBridge()) { |
330 | 0 | aBridge->GetWrBridge()->RecordFrame(); |
331 | 0 | } |
332 | 0 |
|
333 | 0 | for (uintptr_t i = 0; i < aInfo.epochs.length; i++) { |
334 | 0 | aBridge->NotifyPipelineRendered( |
335 | 0 | aInfo.epochs.data[i].pipeline_id, |
336 | 0 | aInfo.epochs.data[i].epoch, |
337 | 0 | aStart, |
338 | 0 | aEnd); |
339 | 0 | } |
340 | 0 |
|
341 | 0 | wr_pipeline_info_delete(aInfo); |
342 | 0 | } |
343 | | |
344 | | void |
345 | | RenderThread::UpdateAndRender(wr::WindowId aWindowId, |
346 | | const TimeStamp& aStartTime, |
347 | | bool aReadback) |
348 | 0 | { |
349 | 0 | AUTO_PROFILER_TRACING("Paint", "Composite"); |
350 | 0 | MOZ_ASSERT(IsInRenderThread()); |
351 | 0 |
|
352 | 0 | auto it = mRenderers.find(aWindowId); |
353 | 0 | MOZ_ASSERT(it != mRenderers.end()); |
354 | 0 | if (it == mRenderers.end()) { |
355 | 0 | return; |
356 | 0 | } |
357 | 0 | |
358 | 0 | auto& renderer = it->second; |
359 | 0 |
|
360 | 0 | bool ret = renderer->UpdateAndRender(aReadback); |
361 | 0 | if (!ret) { |
362 | 0 | // Render did not happen, do not call NotifyDidRender. |
363 | 0 | return; |
364 | 0 | } |
365 | 0 | |
366 | 0 | TimeStamp end = TimeStamp::Now(); |
367 | 0 |
|
368 | 0 | auto info = renderer->FlushPipelineInfo(); |
369 | 0 | RefPtr<layers::AsyncImagePipelineManager> pipelineMgr = |
370 | 0 | renderer->GetCompositorBridge()->GetAsyncImagePipelineManager(); |
371 | 0 | // pipelineMgr should always be non-null here because it is only nulled out |
372 | 0 | // after the WebRenderAPI instance for the CompositorBridgeParent is |
373 | 0 | // destroyed, and that destruction blocks until the renderer thread has |
374 | 0 | // removed the relevant renderer. And after that happens we should never reach |
375 | 0 | // this code at all; it would bail out at the mRenderers.find check above. |
376 | 0 | MOZ_ASSERT(pipelineMgr); |
377 | 0 | pipelineMgr->NotifyPipelinesUpdated(info); |
378 | 0 |
|
379 | 0 | layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction( |
380 | 0 | "NotifyDidRenderRunnable", |
381 | 0 | &NotifyDidRender, |
382 | 0 | renderer->GetCompositorBridge(), |
383 | 0 | info, |
384 | 0 | aStartTime, end |
385 | 0 | )); |
386 | 0 | } |
387 | | |
388 | | void |
389 | | RenderThread::Pause(wr::WindowId aWindowId) |
390 | 0 | { |
391 | 0 | MOZ_ASSERT(IsInRenderThread()); |
392 | 0 |
|
393 | 0 | auto it = mRenderers.find(aWindowId); |
394 | 0 | MOZ_ASSERT(it != mRenderers.end()); |
395 | 0 | if (it == mRenderers.end()) { |
396 | 0 | return; |
397 | 0 | } |
398 | 0 | auto& renderer = it->second; |
399 | 0 | renderer->Pause(); |
400 | 0 | } |
401 | | |
402 | | bool |
403 | | RenderThread::Resume(wr::WindowId aWindowId) |
404 | 0 | { |
405 | 0 | MOZ_ASSERT(IsInRenderThread()); |
406 | 0 |
|
407 | 0 | auto it = mRenderers.find(aWindowId); |
408 | 0 | MOZ_ASSERT(it != mRenderers.end()); |
409 | 0 | if (it == mRenderers.end()) { |
410 | 0 | return false; |
411 | 0 | } |
412 | 0 | auto& renderer = it->second; |
413 | 0 | return renderer->Resume(); |
414 | 0 | } |
415 | | |
416 | | bool |
417 | | RenderThread::TooManyPendingFrames(wr::WindowId aWindowId) |
418 | 0 | { |
419 | 0 | const int64_t maxFrameCount = 1; |
420 | 0 |
|
421 | 0 | // Too many pending frames if pending frames exit more than maxFrameCount |
422 | 0 | // or if RenderBackend is still processing a frame. |
423 | 0 |
|
424 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
425 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
426 | 0 | if (it == mWindowInfos.end()) { |
427 | 0 | MOZ_ASSERT(false); |
428 | 0 | return true; |
429 | 0 | } |
430 | 0 | WindowInfo* info = it->second; |
431 | 0 |
|
432 | 0 | if (info->mPendingCount > maxFrameCount) { |
433 | 0 | return true; |
434 | 0 | } |
435 | 0 | MOZ_ASSERT(info->mPendingCount >= info->mRenderingCount); |
436 | 0 | return info->mPendingCount > info->mRenderingCount; |
437 | 0 | } |
438 | | |
439 | | bool |
440 | | RenderThread::IsDestroyed(wr::WindowId aWindowId) |
441 | 0 | { |
442 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
443 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
444 | 0 | if (it == mWindowInfos.end()) { |
445 | 0 | return true; |
446 | 0 | } |
447 | 0 | |
448 | 0 | return it->second->mIsDestroyed; |
449 | 0 | } |
450 | | |
451 | | void |
452 | | RenderThread::SetDestroyed(wr::WindowId aWindowId) |
453 | 0 | { |
454 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
455 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
456 | 0 | if (it == mWindowInfos.end()) { |
457 | 0 | MOZ_ASSERT(false); |
458 | 0 | return; |
459 | 0 | } |
460 | 0 | it->second->mIsDestroyed = true; |
461 | 0 | } |
462 | | |
463 | | void |
464 | | RenderThread::IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime) |
465 | 0 | { |
466 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
467 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
468 | 0 | if (it == mWindowInfos.end()) { |
469 | 0 | MOZ_ASSERT(false); |
470 | 0 | return; |
471 | 0 | } |
472 | 0 | it->second->mPendingCount++; |
473 | 0 | it->second->mStartTimes.push(aStartTime); |
474 | 0 | } |
475 | | |
476 | | void |
477 | | RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) |
478 | 0 | { |
479 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
480 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
481 | 0 | if (it == mWindowInfos.end()) { |
482 | 0 | MOZ_ASSERT(false); |
483 | 0 | return; |
484 | 0 | } |
485 | 0 | WindowInfo* info = it->second; |
486 | 0 | MOZ_ASSERT(info->mPendingCount > 0); |
487 | 0 | if (info->mPendingCount <= 0) { |
488 | 0 | return; |
489 | 0 | } |
490 | 0 | info->mPendingCount--; |
491 | 0 | // This function gets called for "nop frames" where nothing was rendered or |
492 | 0 | // composited. But we count this time because the non-WR codepath equivalent |
493 | 0 | // in CompositorBridgeParent::ComposeToTarget also counts such frames. And |
494 | 0 | // anyway this should be relatively infrequent so it shouldn't skew the |
495 | 0 | // numbers much. |
496 | 0 | mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, |
497 | 0 | info->mStartTimes.front()); |
498 | 0 | info->mStartTimes.pop(); |
499 | 0 | } |
500 | | |
501 | | void |
502 | | RenderThread::IncRenderingFrameCount(wr::WindowId aWindowId) |
503 | 0 | { |
504 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
505 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
506 | 0 | if (it == mWindowInfos.end()) { |
507 | 0 | MOZ_ASSERT(false); |
508 | 0 | return; |
509 | 0 | } |
510 | 0 | it->second->mRenderingCount++; |
511 | 0 | } |
512 | | |
513 | | void |
514 | | RenderThread::FrameRenderingComplete(wr::WindowId aWindowId) |
515 | 0 | { |
516 | 0 | MutexAutoLock lock(mFrameCountMapLock); |
517 | 0 | auto it = mWindowInfos.find(AsUint64(aWindowId)); |
518 | 0 | if (it == mWindowInfos.end()) { |
519 | 0 | MOZ_ASSERT(false); |
520 | 0 | return; |
521 | 0 | } |
522 | 0 | WindowInfo* info = it->second; |
523 | 0 | MOZ_ASSERT(info->mPendingCount > 0); |
524 | 0 | MOZ_ASSERT(info->mRenderingCount > 0); |
525 | 0 | if (info->mPendingCount <= 0) { |
526 | 0 | return; |
527 | 0 | } |
528 | 0 | info->mPendingCount--; |
529 | 0 | info->mRenderingCount--; |
530 | 0 | // The start time is from WebRenderBridgeParent::CompositeToTarget. From that |
531 | 0 | // point until now (when the frame is finally pushed to the screen) is |
532 | 0 | // equivalent to the COMPOSITE_TIME metric in the non-WR codepath. |
533 | 0 | mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, |
534 | 0 | info->mStartTimes.front()); |
535 | 0 | info->mStartTimes.pop(); |
536 | 0 | } |
537 | | |
538 | | void |
539 | | RenderThread::RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture) |
540 | 0 | { |
541 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
542 | 0 |
|
543 | 0 | if (mHasShutdown) { |
544 | 0 | return; |
545 | 0 | } |
546 | 0 | MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end()); |
547 | 0 | mRenderTextures.emplace(aExternalImageId, std::move(aTexture)); |
548 | 0 | } |
549 | | |
550 | | void |
551 | | RenderThread::UnregisterExternalImage(uint64_t aExternalImageId) |
552 | 0 | { |
553 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
554 | 0 | if (mHasShutdown) { |
555 | 0 | return; |
556 | 0 | } |
557 | 0 | auto it = mRenderTextures.find(aExternalImageId); |
558 | 0 | MOZ_ASSERT(it != mRenderTextures.end()); |
559 | 0 | if (it == mRenderTextures.end()) { |
560 | 0 | return; |
561 | 0 | } |
562 | 0 | if (!IsInRenderThread()) { |
563 | 0 | // The RenderTextureHost should be released in render thread. So, post the |
564 | 0 | // deletion task here. |
565 | 0 | // The shmem and raw buffer are owned by compositor ipc channel. It's |
566 | 0 | // possible that RenderTextureHost is still exist after the shmem/raw buffer |
567 | 0 | // deletion. Then the buffer in RenderTextureHost becomes invalid. It's fine |
568 | 0 | // for this situation. Gecko will only release the buffer if WR doesn't need |
569 | 0 | // it. So, no one will access the invalid buffer in RenderTextureHost. |
570 | 0 | RefPtr<RenderTextureHost> texture = it->second; |
571 | 0 | mRenderTextures.erase(it); |
572 | 0 | mRenderTexturesDeferred.emplace_back(std::move(texture)); |
573 | 0 | Loop()->PostTask(NewRunnableMethod( |
574 | 0 | "RenderThread::DeferredRenderTextureHostDestroy", |
575 | 0 | this, &RenderThread::DeferredRenderTextureHostDestroy |
576 | 0 | )); |
577 | 0 | } else { |
578 | 0 | mRenderTextures.erase(it); |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | void |
583 | | RenderThread::UpdateRenderTextureHost(uint64_t aSrcExternalImageId, uint64_t aWrappedExternalImageId) |
584 | 0 | { |
585 | 0 | MOZ_ASSERT(aSrcExternalImageId != aWrappedExternalImageId); |
586 | 0 |
|
587 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
588 | 0 | if (mHasShutdown) { |
589 | 0 | return; |
590 | 0 | } |
591 | 0 | auto src = mRenderTextures.find(aSrcExternalImageId); |
592 | 0 | auto wrapped = mRenderTextures.find(aWrappedExternalImageId); |
593 | 0 | if (src == mRenderTextures.end() || wrapped == mRenderTextures.end()) { |
594 | 0 | return; |
595 | 0 | } |
596 | 0 | MOZ_ASSERT(src->second->AsRenderTextureHostWrapper()); |
597 | 0 | MOZ_ASSERT(!wrapped->second->AsRenderTextureHostWrapper()); |
598 | 0 | RenderTextureHostWrapper* wrapper = src->second->AsRenderTextureHostWrapper(); |
599 | 0 | if (!wrapper) { |
600 | 0 | MOZ_ASSERT_UNREACHABLE("unexpected to happen"); |
601 | 0 | return; |
602 | 0 | } |
603 | 0 | if (!wrapper->IsInited()) { |
604 | 0 | wrapper->UpdateRenderTextureHost(wrapped->second); |
605 | 0 | MOZ_ASSERT(wrapper->IsInited()); |
606 | 0 | } else { |
607 | 0 | Loop()->PostTask(NewRunnableMethod<RenderTextureHost*>( |
608 | 0 | "RenderTextureHostWrapper::UpdateRenderTextureHost", |
609 | 0 | wrapper, &RenderTextureHostWrapper::UpdateRenderTextureHost, wrapped->second |
610 | 0 | )); |
611 | 0 | } |
612 | 0 | } |
613 | | |
614 | | void |
615 | | RenderThread::UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId) |
616 | 0 | { |
617 | 0 | MOZ_ASSERT(IsInRenderThread()); |
618 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
619 | 0 | MOZ_ASSERT(mHasShutdown); |
620 | 0 | MOZ_ASSERT(mRenderTextures.find(aExternalImageId) != mRenderTextures.end()); |
621 | 0 | mRenderTextures.erase(aExternalImageId); |
622 | 0 | } |
623 | | |
624 | | void |
625 | | RenderThread::DeferredRenderTextureHostDestroy() |
626 | 0 | { |
627 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
628 | 0 | mRenderTexturesDeferred.clear(); |
629 | 0 | } |
630 | | |
631 | | RenderTextureHost* |
632 | | RenderThread::GetRenderTexture(wr::WrExternalImageId aExternalImageId) |
633 | 0 | { |
634 | 0 | MOZ_ASSERT(IsInRenderThread()); |
635 | 0 |
|
636 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
637 | 0 | auto it = mRenderTextures.find(aExternalImageId.mHandle); |
638 | 0 | MOZ_ASSERT(it != mRenderTextures.end()); |
639 | 0 | if (it == mRenderTextures.end()) { |
640 | 0 | return nullptr; |
641 | 0 | } |
642 | 0 | return it->second; |
643 | 0 | } |
644 | | |
645 | | void |
646 | | RenderThread::ProgramCacheTask() |
647 | 0 | { |
648 | 0 | ProgramCache(); |
649 | 0 | } |
650 | | |
651 | | void |
652 | | RenderThread::HandleDeviceReset(const char* aWhere, bool aNotify) |
653 | 0 | { |
654 | 0 | MOZ_ASSERT(IsInRenderThread()); |
655 | 0 |
|
656 | 0 | if (mHandlingDeviceReset) { |
657 | 0 | return; |
658 | 0 | } |
659 | 0 | |
660 | 0 | if (aNotify) { |
661 | 0 | gfxCriticalNote << "GFX: RenderThread detected a device reset in " << aWhere; |
662 | 0 | if (XRE_IsGPUProcess()) { |
663 | 0 | gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); |
664 | 0 | } |
665 | 0 | } |
666 | 0 |
|
667 | 0 | { |
668 | 0 | MutexAutoLock lock(mRenderTextureMapLock); |
669 | 0 | mRenderTexturesDeferred.clear(); |
670 | 0 | for (const auto& entry : mRenderTextures) { |
671 | 0 | entry.second->ClearCachedResources(); |
672 | 0 | } |
673 | 0 | } |
674 | 0 |
|
675 | 0 | mHandlingDeviceReset = true; |
676 | 0 | // All RenderCompositors will be destroyed by GPUChild::RecvNotifyDeviceReset() |
677 | 0 | } |
678 | | |
679 | | bool |
680 | | RenderThread::IsHandlingDeviceReset() |
681 | 0 | { |
682 | 0 | MOZ_ASSERT(IsInRenderThread()); |
683 | 0 | return mHandlingDeviceReset; |
684 | 0 | } |
685 | | |
686 | | void |
687 | | RenderThread::SimulateDeviceReset() |
688 | 0 | { |
689 | 0 | if (!IsInRenderThread()) { |
690 | 0 | Loop()->PostTask(NewRunnableMethod( |
691 | 0 | "RenderThread::SimulateDeviceReset", |
692 | 0 | this, &RenderThread::SimulateDeviceReset |
693 | 0 | )); |
694 | 0 | } else { |
695 | 0 | // When this function is called GPUProcessManager::SimulateDeviceReset() already |
696 | 0 | // triggers destroying all CompositorSessions before re-creating them. |
697 | 0 | HandleDeviceReset("SimulateDeviceReset", /* aNotify */ false); |
698 | 0 | } |
699 | 0 | } |
700 | | |
701 | | WebRenderProgramCache* |
702 | | RenderThread::ProgramCache() |
703 | 0 | { |
704 | 0 | MOZ_ASSERT(IsInRenderThread()); |
705 | 0 |
|
706 | 0 | if (!mProgramCache) { |
707 | 0 | mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw()); |
708 | 0 | } |
709 | 0 | return mProgramCache.get(); |
710 | 0 | } |
711 | | |
712 | | WebRenderThreadPool::WebRenderThreadPool() |
713 | 0 | { |
714 | 0 | mThreadPool = wr_thread_pool_new(); |
715 | 0 | } |
716 | | |
717 | | WebRenderThreadPool::~WebRenderThreadPool() |
718 | 0 | { |
719 | 0 | wr_thread_pool_delete(mThreadPool); |
720 | 0 | } |
721 | | |
722 | | WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool) |
723 | 0 | { |
724 | 0 | MOZ_ASSERT(aThreadPool); |
725 | 0 |
|
726 | 0 | nsAutoString path; |
727 | 0 | if (gfxVars::UseWebRenderProgramBinaryDisk()) { |
728 | 0 | path.Append(gfx::gfxVars::ProfDirectory()); |
729 | 0 | } |
730 | 0 | mProgramCache = wr_program_cache_new(&path, aThreadPool); |
731 | 0 | wr_try_load_shader_from_disk(mProgramCache); |
732 | 0 | } |
733 | | |
734 | | WebRenderProgramCache::~WebRenderProgramCache() |
735 | 0 | { |
736 | 0 | wr_program_cache_delete(mProgramCache); |
737 | 0 | } |
738 | | |
739 | | } // namespace wr |
740 | | } // namespace mozilla |
741 | | |
742 | | extern "C" { |
743 | | |
744 | | static void NewFrameReady(mozilla::wr::WrWindowId aWindowId) |
745 | 0 | { |
746 | 0 | mozilla::wr::RenderThread::Get()->IncRenderingFrameCount(aWindowId); |
747 | 0 | mozilla::wr::RenderThread::Get()->NewFrameReady(aWindowId); |
748 | 0 | } |
749 | | |
750 | | void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId) |
751 | 0 | { |
752 | 0 | mozilla::wr::RenderThread::Get()->WakeUp(aWindowId); |
753 | 0 | } |
754 | | |
755 | | void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId) |
756 | 0 | { |
757 | 0 | NewFrameReady(aWindowId); |
758 | 0 | } |
759 | | |
760 | | void wr_notifier_nop_frame_done(mozilla::wr::WrWindowId aWindowId) |
761 | 0 | { |
762 | 0 | mozilla::wr::RenderThread::Get()->DecPendingFrameCount(aWindowId); |
763 | 0 | } |
764 | | |
765 | | void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId, size_t aRawEvent) |
766 | 0 | { |
767 | 0 | mozilla::UniquePtr<mozilla::wr::RendererEvent> evt( |
768 | 0 | reinterpret_cast<mozilla::wr::RendererEvent*>(aRawEvent)); |
769 | 0 | mozilla::wr::RenderThread::Get()->RunEvent(mozilla::wr::WindowId(aWindowId), |
770 | 0 | std::move(evt)); |
771 | 0 | } |
772 | | |
773 | | void wr_schedule_render(mozilla::wr::WrWindowId aWindowId) |
774 | 0 | { |
775 | 0 | RefPtr<mozilla::layers::CompositorBridgeParent> cbp = |
776 | 0 | mozilla::layers::CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId); |
777 | 0 | if (cbp) { |
778 | 0 | cbp->ScheduleRenderOnCompositorThread(); |
779 | 0 | } |
780 | 0 | } |
781 | | |
782 | | } // extern C |