Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "base/task.h"
7 : #include "GeckoProfiler.h"
8 : #include "RenderThread.h"
9 : #include "nsThreadUtils.h"
10 : #include "mtransport/runnable_utils.h"
11 : #include "mozilla/layers/CompositorThread.h"
12 : #include "mozilla/layers/CompositorBridgeParent.h"
13 : #include "mozilla/StaticPtr.h"
14 : #include "mozilla/webrender/RendererOGL.h"
15 : #include "mozilla/webrender/RenderTextureHost.h"
16 : #include "mozilla/widget/CompositorWidget.h"
17 :
18 : namespace mozilla {
19 : namespace wr {
20 :
21 3 : static StaticRefPtr<RenderThread> sRenderThread;
22 :
23 0 : RenderThread::RenderThread(base::Thread* aThread)
24 : : mThread(aThread)
25 : , mPendingFrameCountMapLock("RenderThread.mPendingFrameCountMapLock")
26 : , mRenderTextureMapLock("RenderThread.mRenderTextureMapLock")
27 0 : , mHasShutdown(false)
28 : {
29 :
30 0 : }
31 :
32 0 : RenderThread::~RenderThread()
33 : {
34 0 : delete mThread;
35 0 : }
36 :
37 : // static
38 : RenderThread*
39 0 : RenderThread::Get()
40 : {
41 0 : return sRenderThread;
42 : }
43 :
44 : // static
45 : void
46 0 : RenderThread::Start()
47 : {
48 0 : MOZ_ASSERT(NS_IsMainThread());
49 0 : MOZ_ASSERT(!sRenderThread);
50 :
51 0 : base::Thread* thread = new base::Thread("Renderer");
52 :
53 0 : base::Thread::Options options;
54 : // TODO(nical): The compositor thread has a bunch of specific options, see
55 : // which ones make sense here.
56 0 : if (!thread->StartWithOptions(options)) {
57 0 : delete thread;
58 0 : return;
59 : }
60 :
61 0 : sRenderThread = new RenderThread(thread);
62 : }
63 :
64 : // static
65 : void
66 0 : RenderThread::ShutDown()
67 : {
68 0 : MOZ_ASSERT(NS_IsMainThread());
69 0 : MOZ_ASSERT(sRenderThread);
70 :
71 : {
72 0 : MutexAutoLock lock(sRenderThread->mRenderTextureMapLock);
73 0 : sRenderThread->mHasShutdown = true;
74 : }
75 :
76 0 : layers::SynchronousTask task("RenderThread");
77 0 : RefPtr<Runnable> runnable = WrapRunnable(
78 0 : RefPtr<RenderThread>(sRenderThread.get()),
79 : &RenderThread::ShutDownTask,
80 0 : &task);
81 0 : sRenderThread->Loop()->PostTask(runnable.forget());
82 0 : task.Wait();
83 :
84 0 : sRenderThread = nullptr;
85 0 : }
86 :
87 : void
88 0 : RenderThread::ShutDownTask(layers::SynchronousTask* aTask)
89 : {
90 0 : layers::AutoCompleteTask complete(aTask);
91 0 : MOZ_ASSERT(IsInRenderThread());
92 0 : }
93 :
94 : // static
95 : MessageLoop*
96 0 : RenderThread::Loop()
97 : {
98 0 : return sRenderThread ? sRenderThread->mThread->message_loop() : nullptr;
99 : }
100 :
101 : // static
102 : bool
103 0 : RenderThread::IsInRenderThread()
104 : {
105 0 : return sRenderThread && sRenderThread->mThread->thread_id() == PlatformThread::CurrentId();
106 : }
107 :
108 : void
109 0 : RenderThread::AddRenderer(wr::WindowId aWindowId, UniquePtr<RendererOGL> aRenderer)
110 : {
111 0 : MOZ_ASSERT(IsInRenderThread());
112 :
113 0 : if (mHasShutdown) {
114 0 : return;
115 : }
116 :
117 0 : mRenderers[aWindowId] = Move(aRenderer);
118 :
119 0 : MutexAutoLock lock(mPendingFrameCountMapLock);
120 0 : mPendingFrameCounts.Put(AsUint64(aWindowId), 0);
121 : }
122 :
123 : void
124 0 : RenderThread::RemoveRenderer(wr::WindowId aWindowId)
125 : {
126 0 : MOZ_ASSERT(IsInRenderThread());
127 :
128 0 : if (mHasShutdown) {
129 0 : return;
130 : }
131 :
132 0 : mRenderers.erase(aWindowId);
133 :
134 0 : MutexAutoLock lock(mPendingFrameCountMapLock);
135 0 : mPendingFrameCounts.Remove(AsUint64(aWindowId));
136 : }
137 :
138 : RendererOGL*
139 0 : RenderThread::GetRenderer(wr::WindowId aWindowId)
140 : {
141 0 : MOZ_ASSERT(IsInRenderThread());
142 :
143 0 : auto it = mRenderers.find(aWindowId);
144 0 : MOZ_ASSERT(it != mRenderers.end());
145 :
146 0 : if (it == mRenderers.end()) {
147 0 : return nullptr;
148 : }
149 :
150 0 : return it->second.get();
151 : }
152 :
153 : void
154 0 : RenderThread::NewFrameReady(wr::WindowId aWindowId)
155 : {
156 0 : if (mHasShutdown) {
157 0 : return;
158 : }
159 :
160 0 : if (!IsInRenderThread()) {
161 0 : Loop()->PostTask(
162 0 : NewRunnableMethod<wr::WindowId>("wr::RenderThread::NewFrameReady",
163 : this,
164 : &RenderThread::NewFrameReady,
165 0 : aWindowId));
166 0 : return;
167 : }
168 :
169 0 : UpdateAndRender(aWindowId);
170 0 : DecPendingFrameCount(aWindowId);
171 : }
172 :
173 : void
174 0 : RenderThread::RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent)
175 : {
176 0 : if (!IsInRenderThread()) {
177 0 : Loop()->PostTask(
178 0 : NewRunnableMethod<wr::WindowId, UniquePtr<RendererEvent>&&>(
179 : "wr::RenderThread::RunEvent",
180 : this,
181 : &RenderThread::RunEvent,
182 : aWindowId,
183 0 : Move(aEvent)));
184 0 : return;
185 : }
186 :
187 0 : aEvent->Run(*this, aWindowId);
188 0 : aEvent = nullptr;
189 : }
190 :
191 : static void
192 0 : NotifyDidRender(layers::CompositorBridgeParentBase* aBridge,
193 : WrRenderedEpochs* aEpochs,
194 : TimeStamp aStart,
195 : TimeStamp aEnd)
196 : {
197 : WrPipelineId pipeline;
198 : WrEpoch epoch;
199 0 : while (wr_rendered_epochs_next(aEpochs, &pipeline, &epoch)) {
200 0 : aBridge->NotifyDidCompositeToPipeline(pipeline, epoch, aStart, aEnd);
201 : }
202 0 : wr_rendered_epochs_delete(aEpochs);
203 0 : }
204 :
205 : void
206 0 : RenderThread::UpdateAndRender(wr::WindowId aWindowId)
207 : {
208 0 : AutoProfilerTracing tracing("Paint", "Composite");
209 0 : MOZ_ASSERT(IsInRenderThread());
210 :
211 0 : auto it = mRenderers.find(aWindowId);
212 0 : MOZ_ASSERT(it != mRenderers.end());
213 0 : if (it == mRenderers.end()) {
214 0 : return;
215 : }
216 :
217 0 : auto& renderer = it->second;
218 0 : renderer->Update();
219 :
220 0 : TimeStamp start = TimeStamp::Now();
221 :
222 0 : bool ret = renderer->Render();
223 0 : if (!ret) {
224 : // Render did not happen, do not call NotifyDidRender.
225 0 : return;
226 : }
227 :
228 0 : TimeStamp end = TimeStamp::Now();
229 :
230 0 : auto epochs = renderer->FlushRenderedEpochs();
231 0 : layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
232 : &NotifyDidRender,
233 0 : renderer->GetCompositorBridge(),
234 : epochs,
235 : start, end
236 0 : ));
237 : }
238 :
239 : void
240 0 : RenderThread::Pause(wr::WindowId aWindowId)
241 : {
242 0 : MOZ_ASSERT(IsInRenderThread());
243 :
244 0 : auto it = mRenderers.find(aWindowId);
245 0 : MOZ_ASSERT(it != mRenderers.end());
246 0 : if (it == mRenderers.end()) {
247 0 : return;
248 : }
249 0 : auto& renderer = it->second;
250 0 : renderer->Pause();
251 : }
252 :
253 : bool
254 0 : RenderThread::Resume(wr::WindowId aWindowId)
255 : {
256 0 : MOZ_ASSERT(IsInRenderThread());
257 :
258 0 : auto it = mRenderers.find(aWindowId);
259 0 : MOZ_ASSERT(it != mRenderers.end());
260 0 : if (it == mRenderers.end()) {
261 0 : return false;
262 : }
263 0 : auto& renderer = it->second;
264 0 : return renderer->Resume();
265 : }
266 :
267 : uint32_t
268 0 : RenderThread::GetPendingFrameCount(wr::WindowId aWindowId)
269 : {
270 0 : MutexAutoLock lock(mPendingFrameCountMapLock);
271 0 : uint32_t count = 0;
272 0 : MOZ_ASSERT(mPendingFrameCounts.Get(AsUint64(aWindowId), &count));
273 0 : mPendingFrameCounts.Get(AsUint64(aWindowId), &count);
274 0 : return count;
275 : }
276 :
277 : void
278 0 : RenderThread::IncPendingFrameCount(wr::WindowId aWindowId)
279 : {
280 0 : MutexAutoLock lock(mPendingFrameCountMapLock);
281 : // Get the old count.
282 0 : uint32_t oldCount = 0;
283 0 : if (!mPendingFrameCounts.Get(AsUint64(aWindowId), &oldCount)) {
284 0 : MOZ_ASSERT(false);
285 : return;
286 : }
287 : // Update pending frame count.
288 0 : mPendingFrameCounts.Put(AsUint64(aWindowId), oldCount + 1);
289 0 : }
290 :
291 : void
292 0 : RenderThread::DecPendingFrameCount(wr::WindowId aWindowId)
293 : {
294 0 : MutexAutoLock lock(mPendingFrameCountMapLock);
295 : // Get the old count.
296 0 : uint32_t oldCount = 0;
297 0 : if (!mPendingFrameCounts.Get(AsUint64(aWindowId), &oldCount)) {
298 0 : MOZ_ASSERT(false);
299 : return;
300 : }
301 0 : MOZ_ASSERT(oldCount > 0);
302 0 : if (oldCount <= 0) {
303 0 : return;
304 : }
305 : // Update pending frame count.
306 0 : mPendingFrameCounts.Put(AsUint64(aWindowId), oldCount - 1);
307 : }
308 :
309 : void
310 0 : RenderThread::RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture)
311 : {
312 0 : MutexAutoLock lock(mRenderTextureMapLock);
313 :
314 0 : if (mHasShutdown) {
315 0 : return;
316 : }
317 0 : MOZ_ASSERT(!mRenderTextures.GetWeak(aExternalImageId));
318 0 : mRenderTextures.Put(aExternalImageId, Move(aTexture));
319 : }
320 :
321 : void
322 0 : RenderThread::UnregisterExternalImage(uint64_t aExternalImageId)
323 : {
324 0 : MutexAutoLock lock(mRenderTextureMapLock);
325 0 : if (mHasShutdown) {
326 0 : return;
327 : }
328 0 : MOZ_ASSERT(mRenderTextures.GetWeak(aExternalImageId));
329 0 : if (!IsInRenderThread()) {
330 : // The RenderTextureHost should be released in render thread. So, post the
331 : // deletion task here.
332 : // The shmem and raw buffer are owned by compositor ipc channel. It's
333 : // possible that RenderTextureHost is still exist after the shmem/raw buffer
334 : // deletion. Then the buffer in RenderTextureHost becomes invalid. It's fine
335 : // for this situation. Gecko will only release the buffer if WR doesn't need
336 : // it. So, no one will access the invalid buffer in RenderTextureHost.
337 0 : RefPtr<RenderTextureHost> texture;
338 0 : mRenderTextures.Remove(aExternalImageId, getter_AddRefs(texture));
339 0 : Loop()->PostTask(NewRunnableMethod<RefPtr<RenderTextureHost>>(
340 : "RenderThread::DeferredRenderTextureHostDestroy",
341 0 : this, &RenderThread::DeferredRenderTextureHostDestroy, Move(texture)
342 0 : ));
343 : } else {
344 0 : mRenderTextures.Remove(aExternalImageId);
345 : }
346 : }
347 :
348 : void
349 0 : RenderThread::DeferredRenderTextureHostDestroy(RefPtr<RenderTextureHost>)
350 : {
351 : // Do nothing. Just decrease the ref-count of RenderTextureHost.
352 0 : }
353 :
354 : RenderTextureHost*
355 0 : RenderThread::GetRenderTexture(WrExternalImageId aExternalImageId)
356 : {
357 0 : MOZ_ASSERT(IsInRenderThread());
358 :
359 0 : MutexAutoLock lock(mRenderTextureMapLock);
360 0 : MOZ_ASSERT(mRenderTextures.GetWeak(aExternalImageId.mHandle));
361 0 : return mRenderTextures.GetWeak(aExternalImageId.mHandle);
362 : }
363 :
364 0 : WebRenderThreadPool::WebRenderThreadPool()
365 : {
366 0 : mThreadPool = wr_thread_pool_new();
367 0 : }
368 :
369 0 : WebRenderThreadPool::~WebRenderThreadPool()
370 : {
371 0 : wr_thread_pool_delete(mThreadPool);
372 0 : }
373 :
374 : } // namespace wr
375 : } // namespace mozilla
376 :
377 : extern "C" {
378 :
379 0 : void wr_notifier_new_frame_ready(WrWindowId aWindowId)
380 : {
381 0 : mozilla::wr::RenderThread::Get()->IncPendingFrameCount(aWindowId);
382 0 : mozilla::wr::RenderThread::Get()->NewFrameReady(mozilla::wr::WindowId(aWindowId));
383 0 : }
384 :
385 0 : void wr_notifier_new_scroll_frame_ready(WrWindowId aWindowId, bool aCompositeNeeded)
386 : {
387 : // It is not necessary to update rendering with new_scroll_frame_ready.
388 : // WebRenderBridgeParent::CompositeToTarget() is implemented to call
389 : // WebRenderAPI::GenerateFrame() if it is necessary to trigger UpdateAndRender().
390 : // See Bug 1377688.
391 0 : }
392 :
393 0 : void wr_notifier_external_event(WrWindowId aWindowId, size_t aRawEvent)
394 : {
395 : mozilla::UniquePtr<mozilla::wr::RendererEvent> evt(
396 0 : reinterpret_cast<mozilla::wr::RendererEvent*>(aRawEvent));
397 0 : mozilla::wr::RenderThread::Get()->RunEvent(mozilla::wr::WindowId(aWindowId),
398 0 : mozilla::Move(evt));
399 0 : }
400 :
401 : } // extern C
|