/src/mozilla-central/gfx/layers/ipc/SharedSurfacesChild.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 "SharedSurfacesChild.h" |
8 | | #include "SharedSurfacesParent.h" |
9 | | #include "CompositorManagerChild.h" |
10 | | #include "mozilla/gfx/gfxVars.h" |
11 | | #include "mozilla/layers/IpcResourceUpdateQueue.h" |
12 | | #include "mozilla/layers/SourceSurfaceSharedData.h" |
13 | | #include "mozilla/layers/WebRenderBridgeChild.h" |
14 | | #include "mozilla/layers/WebRenderLayerManager.h" |
15 | | #include "mozilla/SystemGroup.h" // for SystemGroup |
16 | | |
17 | | namespace mozilla { |
18 | | namespace layers { |
19 | | |
20 | | using namespace mozilla::gfx; |
21 | | |
22 | | class SharedSurfacesChild::ImageKeyData final |
23 | | { |
24 | | public: |
25 | | ImageKeyData(WebRenderLayerManager* aManager, |
26 | | const wr::ImageKey& aImageKey) |
27 | | : mManager(aManager) |
28 | | , mImageKey(aImageKey) |
29 | 0 | { } |
30 | | |
31 | | ImageKeyData(ImageKeyData&& aOther) |
32 | | : mManager(std::move(aOther.mManager)) |
33 | | , mDirtyRect(std::move(aOther.mDirtyRect)) |
34 | | , mImageKey(aOther.mImageKey) |
35 | 0 | { } |
36 | | |
37 | | ImageKeyData& operator=(ImageKeyData&& aOther) |
38 | 0 | { |
39 | 0 | mManager = std::move(aOther.mManager); |
40 | 0 | mDirtyRect = std::move(aOther.mDirtyRect); |
41 | 0 | mImageKey = aOther.mImageKey; |
42 | 0 | return *this; |
43 | 0 | } |
44 | | |
45 | | void MergeDirtyRect(const Maybe<IntRect>& aDirtyRect) |
46 | 0 | { |
47 | 0 | if (mDirtyRect) { |
48 | 0 | if (aDirtyRect) { |
49 | 0 | mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect.ref()); |
50 | 0 | } |
51 | 0 | } else { |
52 | 0 | mDirtyRect = aDirtyRect; |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | Maybe<IntRect> TakeDirtyRect() |
57 | 0 | { |
58 | 0 | return std::move(mDirtyRect); |
59 | 0 | } |
60 | | |
61 | | ImageKeyData(const ImageKeyData&) = delete; |
62 | | ImageKeyData& operator=(const ImageKeyData&) = delete; |
63 | | |
64 | | RefPtr<WebRenderLayerManager> mManager; |
65 | | Maybe<IntRect> mDirtyRect; |
66 | | wr::ImageKey mImageKey; |
67 | | }; |
68 | | |
69 | | class SharedSurfacesChild::SharedUserData final |
70 | | { |
71 | | public: |
72 | | explicit SharedUserData(const wr::ExternalImageId& aId) |
73 | | : mId(aId) |
74 | | , mShared(false) |
75 | 0 | { } |
76 | | |
77 | | ~SharedUserData() |
78 | 0 | { |
79 | 0 | if (mShared) { |
80 | 0 | mShared = false; |
81 | 0 | if (NS_IsMainThread()) { |
82 | 0 | SharedSurfacesChild::Unshare(mId, mKeys); |
83 | 0 | } else { |
84 | 0 | class DestroyRunnable final : public Runnable |
85 | 0 | { |
86 | 0 | public: |
87 | 0 | DestroyRunnable(const wr::ExternalImageId& aId, |
88 | 0 | nsTArray<ImageKeyData>&& aKeys) |
89 | 0 | : Runnable("SharedSurfacesChild::SharedUserData::DestroyRunnable") |
90 | 0 | , mId(aId) |
91 | 0 | , mKeys(std::move(aKeys)) |
92 | 0 | { } |
93 | 0 |
|
94 | 0 | NS_IMETHOD Run() override |
95 | 0 | { |
96 | 0 | SharedSurfacesChild::Unshare(mId, mKeys); |
97 | 0 | return NS_OK; |
98 | 0 | } |
99 | 0 |
|
100 | 0 | private: |
101 | 0 | wr::ExternalImageId mId; |
102 | 0 | AutoTArray<ImageKeyData, 1> mKeys; |
103 | 0 | }; |
104 | 0 |
|
105 | 0 | nsCOMPtr<nsIRunnable> task = new DestroyRunnable(mId, std::move(mKeys)); |
106 | 0 | SystemGroup::Dispatch(TaskCategory::Other, task.forget()); |
107 | 0 | } |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | const wr::ExternalImageId& Id() const |
112 | 0 | { |
113 | 0 | return mId; |
114 | 0 | } |
115 | | |
116 | | void SetId(const wr::ExternalImageId& aId) |
117 | 0 | { |
118 | 0 | mId = aId; |
119 | 0 | mKeys.Clear(); |
120 | 0 | mShared = false; |
121 | 0 | } |
122 | | |
123 | | bool IsShared() const |
124 | 0 | { |
125 | 0 | return mShared; |
126 | 0 | } |
127 | | |
128 | | void MarkShared() |
129 | 0 | { |
130 | 0 | MOZ_ASSERT(!mShared); |
131 | 0 | mShared = true; |
132 | 0 | } |
133 | | |
134 | | wr::ImageKey UpdateKey(WebRenderLayerManager* aManager, |
135 | | wr::IpcResourceUpdateQueue& aResources, |
136 | | const Maybe<IntRect>& aDirtyRect) |
137 | 0 | { |
138 | 0 | MOZ_ASSERT(aManager); |
139 | 0 | MOZ_ASSERT(!aManager->IsDestroyed()); |
140 | 0 |
|
141 | 0 | // We iterate through all of the items to ensure we clean up the old |
142 | 0 | // WebRenderLayerManager references. Most of the time there will be few |
143 | 0 | // entries and this should not be particularly expensive compared to the |
144 | 0 | // cost of duplicating image keys. In an ideal world, we would generate a |
145 | 0 | // single key for the surface, and it would be usable on all of the |
146 | 0 | // renderer instances. For now, we must allocate a key for each WR bridge. |
147 | 0 | wr::ImageKey key; |
148 | 0 | bool found = false; |
149 | 0 | auto i = mKeys.Length(); |
150 | 0 | while (i > 0) { |
151 | 0 | --i; |
152 | 0 | ImageKeyData& entry = mKeys[i]; |
153 | 0 | if (entry.mManager->IsDestroyed()) { |
154 | 0 | mKeys.RemoveElementAt(i); |
155 | 0 | } else if (entry.mManager == aManager) { |
156 | 0 | WebRenderBridgeChild* wrBridge = aManager->WrBridge(); |
157 | 0 | MOZ_ASSERT(wrBridge); |
158 | 0 |
|
159 | 0 | // Even if the manager is the same, its underlying WebRenderBridgeChild |
160 | 0 | // can change state. If our namespace differs, then our old key has |
161 | 0 | // already been discarded. |
162 | 0 | bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace; |
163 | 0 | if (!ownsKey) { |
164 | 0 | entry.mImageKey = wrBridge->GetNextImageKey(); |
165 | 0 | entry.TakeDirtyRect(); |
166 | 0 | aResources.AddExternalImage(mId, entry.mImageKey); |
167 | 0 | } else { |
168 | 0 | entry.MergeDirtyRect(aDirtyRect); |
169 | 0 | Maybe<IntRect> dirtyRect = entry.TakeDirtyRect(); |
170 | 0 | if (dirtyRect) { |
171 | 0 | aResources.UpdateExternalImage(mId, entry.mImageKey, |
172 | 0 | ViewAs<ImagePixel>(dirtyRect.ref())); |
173 | 0 | } |
174 | 0 | } |
175 | 0 |
|
176 | 0 | key = entry.mImageKey; |
177 | 0 | found = true; |
178 | 0 | } else { |
179 | 0 | // We don't have the resource update queue for this manager, so just |
180 | 0 | // accumulate the dirty rects until it is requested. |
181 | 0 | entry.MergeDirtyRect(aDirtyRect); |
182 | 0 | } |
183 | 0 | } |
184 | 0 |
|
185 | 0 | if (!found) { |
186 | 0 | key = aManager->WrBridge()->GetNextImageKey(); |
187 | 0 | ImageKeyData data(aManager, key); |
188 | 0 | mKeys.AppendElement(std::move(data)); |
189 | 0 | aResources.AddExternalImage(mId, key); |
190 | 0 | } |
191 | 0 |
|
192 | 0 | return key; |
193 | 0 | } |
194 | | |
195 | | private: |
196 | | AutoTArray<ImageKeyData, 1> mKeys; |
197 | | wr::ExternalImageId mId; |
198 | | bool mShared : 1; |
199 | | }; |
200 | | |
201 | | /* static */ void |
202 | | SharedSurfacesChild::DestroySharedUserData(void* aClosure) |
203 | 0 | { |
204 | 0 | MOZ_ASSERT(aClosure); |
205 | 0 | auto data = static_cast<SharedUserData*>(aClosure); |
206 | 0 | delete data; |
207 | 0 | } |
208 | | |
209 | | /* static */ nsresult |
210 | | SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface, |
211 | | SharedUserData** aUserData) |
212 | 0 | { |
213 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
214 | 0 | MOZ_ASSERT(aSurface); |
215 | 0 | MOZ_ASSERT(aUserData); |
216 | 0 |
|
217 | 0 | CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); |
218 | 0 | if (NS_WARN_IF(!manager || !manager->CanSend() || !gfxVars::UseWebRender())) { |
219 | 0 | // We cannot try to share the surface, most likely because the GPU process |
220 | 0 | // crashed. Ideally, we would retry when it is ready, but the handles may be |
221 | 0 | // a scarce resource, which can cause much more serious problems if we run |
222 | 0 | // out. Better to copy into a fresh buffer later. |
223 | 0 | aSurface->FinishedSharing(); |
224 | 0 | return NS_ERROR_NOT_INITIALIZED; |
225 | 0 | } |
226 | 0 | |
227 | 0 | static UserDataKey sSharedKey; |
228 | 0 | SharedUserData* data = |
229 | 0 | static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); |
230 | 0 | if (!data) { |
231 | 0 | data = new SharedUserData(manager->GetNextExternalImageId()); |
232 | 0 | aSurface->AddUserData(&sSharedKey, data, DestroySharedUserData); |
233 | 0 | } else if (!manager->OwnsExternalImageId(data->Id())) { |
234 | 0 | // If the id isn't owned by us, that means the bridge was reinitialized, due |
235 | 0 | // to the GPU process crashing. All previous mappings have been released. |
236 | 0 | data->SetId(manager->GetNextExternalImageId()); |
237 | 0 | } else if (data->IsShared()) { |
238 | 0 | // It has already been shared with the GPU process. |
239 | 0 | *aUserData = data; |
240 | 0 | return NS_OK; |
241 | 0 | } |
242 | 0 | |
243 | 0 | // Ensure that the handle doesn't get released until after we have finished |
244 | 0 | // sending the buffer to the GPU process and/or reallocating it. |
245 | 0 | // FinishedSharing is not a sufficient condition because another thread may |
246 | 0 | // decide we are done while we are in the processing of sharing our newly |
247 | 0 | // reallocated handle. Once it goes out of scope, it may release the handle. |
248 | 0 | SourceSurfaceSharedData::HandleLock lock(aSurface); |
249 | 0 |
|
250 | 0 | // If we live in the same process, then it is a simple matter of directly |
251 | 0 | // asking the parent instance to store a pointer to the same data, no need |
252 | 0 | // to map the data into our memory space twice. |
253 | 0 | auto pid = manager->OtherPid(); |
254 | 0 | if (pid == base::GetCurrentProcId()) { |
255 | 0 | SharedSurfacesParent::AddSameProcess(data->Id(), aSurface); |
256 | 0 | data->MarkShared(); |
257 | 0 | *aUserData = data; |
258 | 0 | return NS_OK; |
259 | 0 | } |
260 | 0 | |
261 | 0 | // Attempt to share a handle with the GPU process. The handle may or may not |
262 | 0 | // be available -- it will only be available if it is either not yet finalized |
263 | 0 | // and/or if it has been finalized but never used for drawing in process. |
264 | 0 | ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle(); |
265 | 0 | nsresult rv = aSurface->ShareToProcess(pid, handle); |
266 | 0 | if (rv == NS_ERROR_NOT_AVAILABLE) { |
267 | 0 | // It is at least as expensive to copy the image to the GPU process if we |
268 | 0 | // have already closed the handle necessary to share, but if we reallocate |
269 | 0 | // the shared buffer to get a new handle, we can save some memory. |
270 | 0 | if (NS_WARN_IF(!aSurface->ReallocHandle())) { |
271 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
272 | 0 | } |
273 | 0 | |
274 | 0 | // Reattempt the sharing of the handle to the GPU process. |
275 | 0 | rv = aSurface->ShareToProcess(pid, handle); |
276 | 0 | } |
277 | 0 |
|
278 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
279 | 0 | MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE); |
280 | 0 | return rv; |
281 | 0 | } |
282 | 0 |
|
283 | 0 | SurfaceFormat format = aSurface->GetFormat(); |
284 | 0 | MOZ_RELEASE_ASSERT(format == SurfaceFormat::B8G8R8X8 || |
285 | 0 | format == SurfaceFormat::B8G8R8A8, "bad format"); |
286 | 0 |
|
287 | 0 | data->MarkShared(); |
288 | 0 | manager->SendAddSharedSurface(data->Id(), |
289 | 0 | SurfaceDescriptorShared(aSurface->GetSize(), |
290 | 0 | aSurface->Stride(), |
291 | 0 | format, handle)); |
292 | 0 | *aUserData = data; |
293 | 0 | return NS_OK; |
294 | 0 | } |
295 | | |
296 | | /* static */ void |
297 | | SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) |
298 | 0 | { |
299 | 0 | MOZ_ASSERT(aSurface); |
300 | 0 |
|
301 | 0 | // The IPDL actor to do sharing can only be accessed on the main thread so we |
302 | 0 | // need to dispatch if off the main thread. However there is no real danger if |
303 | 0 | // we end up racing because if it is already shared, this method will do |
304 | 0 | // nothing. |
305 | 0 | if (!NS_IsMainThread()) { |
306 | 0 | class ShareRunnable final : public Runnable |
307 | 0 | { |
308 | 0 | public: |
309 | 0 | explicit ShareRunnable(SourceSurfaceSharedData* aSurface) |
310 | 0 | : Runnable("SharedSurfacesChild::Share") |
311 | 0 | , mSurface(aSurface) |
312 | 0 | { } |
313 | 0 |
|
314 | 0 | NS_IMETHOD Run() override |
315 | 0 | { |
316 | 0 | SharedUserData* unused = nullptr; |
317 | 0 | SharedSurfacesChild::ShareInternal(mSurface, &unused); |
318 | 0 | return NS_OK; |
319 | 0 | } |
320 | 0 |
|
321 | 0 | private: |
322 | 0 | RefPtr<SourceSurfaceSharedData> mSurface; |
323 | 0 | }; |
324 | 0 |
|
325 | 0 | SystemGroup::Dispatch(TaskCategory::Other, |
326 | 0 | MakeAndAddRef<ShareRunnable>(aSurface)); |
327 | 0 | return; |
328 | 0 | } |
329 | 0 |
|
330 | 0 | SharedUserData* unused = nullptr; |
331 | 0 | SharedSurfacesChild::ShareInternal(aSurface, &unused); |
332 | 0 | } |
333 | | |
334 | | /* static */ nsresult |
335 | | SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface, |
336 | | WebRenderLayerManager* aManager, |
337 | | wr::IpcResourceUpdateQueue& aResources, |
338 | | wr::ImageKey& aKey) |
339 | 0 | { |
340 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
341 | 0 | MOZ_ASSERT(aSurface); |
342 | 0 | MOZ_ASSERT(aManager); |
343 | 0 |
|
344 | 0 | // Each time the surface changes, the producers of SourceSurfaceSharedData |
345 | 0 | // surfaces promise to increment the invalidation counter each time the |
346 | 0 | // surface has changed. We can use this counter to determine whether or not |
347 | 0 | // we should update our paired ImageKey. |
348 | 0 | Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect(); |
349 | 0 | SharedUserData* data = nullptr; |
350 | 0 | nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); |
351 | 0 | if (NS_SUCCEEDED(rv)) { |
352 | 0 | MOZ_ASSERT(data); |
353 | 0 | aKey = data->UpdateKey(aManager, aResources, dirtyRect); |
354 | 0 | } |
355 | 0 |
|
356 | 0 | return rv; |
357 | 0 | } |
358 | | |
359 | | /* static */ nsresult |
360 | | SharedSurfacesChild::Share(ImageContainer* aContainer, |
361 | | WebRenderLayerManager* aManager, |
362 | | wr::IpcResourceUpdateQueue& aResources, |
363 | | wr::ImageKey& aKey) |
364 | 0 | { |
365 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
366 | 0 | MOZ_ASSERT(aContainer); |
367 | 0 | MOZ_ASSERT(aManager); |
368 | 0 |
|
369 | 0 | if (aContainer->IsAsync()) { |
370 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
371 | 0 | } |
372 | 0 | |
373 | 0 | AutoTArray<ImageContainer::OwningImage,4> images; |
374 | 0 | aContainer->GetCurrentImages(&images); |
375 | 0 | if (images.IsEmpty()) { |
376 | 0 | return NS_ERROR_NOT_AVAILABLE; |
377 | 0 | } |
378 | 0 | |
379 | 0 | RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface(); |
380 | 0 | if (!surface) { |
381 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
382 | 0 | } |
383 | 0 | |
384 | 0 | if (surface->GetType() != SurfaceType::DATA_SHARED) { |
385 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
386 | 0 | } |
387 | 0 | |
388 | 0 | auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get()); |
389 | 0 | return Share(sharedSurface, aManager, aResources, aKey); |
390 | 0 | } |
391 | | |
392 | | /* static */ nsresult |
393 | | SharedSurfacesChild::Share(SourceSurface* aSurface, |
394 | | wr::ExternalImageId& aId) |
395 | 0 | { |
396 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
397 | 0 | MOZ_ASSERT(aSurface); |
398 | 0 |
|
399 | 0 | if (aSurface->GetType() != SurfaceType::DATA_SHARED) { |
400 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
401 | 0 | } |
402 | 0 | |
403 | 0 | // The external image ID does not change with the invalidation counter. The |
404 | 0 | // caller of this should be aware of the invalidations of the surface through |
405 | 0 | // another mechanism (e.g. imgRequestProxy listener notifications). |
406 | 0 | auto sharedSurface = static_cast<SourceSurfaceSharedData*>(aSurface); |
407 | 0 | SharedUserData* data = nullptr; |
408 | 0 | nsresult rv = ShareInternal(sharedSurface, &data); |
409 | 0 | if (NS_SUCCEEDED(rv)) { |
410 | 0 | MOZ_ASSERT(data); |
411 | 0 | aId = data->Id(); |
412 | 0 | } |
413 | 0 |
|
414 | 0 | return rv; |
415 | 0 | } |
416 | | |
417 | | /* static */ void |
418 | | SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId, |
419 | | nsTArray<ImageKeyData>& aKeys) |
420 | 0 | { |
421 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
422 | 0 |
|
423 | 0 | for (const auto& entry : aKeys) { |
424 | 0 | if (entry.mManager->IsDestroyed()) { |
425 | 0 | continue; |
426 | 0 | } |
427 | 0 | |
428 | 0 | entry.mManager->AddImageKeyForDiscard(entry.mImageKey); |
429 | 0 | WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge(); |
430 | 0 | if (wrBridge) { |
431 | 0 | wrBridge->DeallocExternalImageId(aId); |
432 | 0 | } |
433 | 0 | } |
434 | 0 |
|
435 | 0 | CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); |
436 | 0 | if (MOZ_UNLIKELY(!manager || !manager->CanSend())) { |
437 | 0 | return; |
438 | 0 | } |
439 | 0 | |
440 | 0 | if (manager->OtherPid() == base::GetCurrentProcId()) { |
441 | 0 | // We are in the combined UI/GPU process. Call directly to it to remove its |
442 | 0 | // wrapper surface to free the underlying buffer, but only if the external |
443 | 0 | // image ID is owned by the manager. It can be different if the surface was |
444 | 0 | // last shared with the GPU process, which crashed several times, and its |
445 | 0 | // job was moved into the parent process. |
446 | 0 | if (manager->OwnsExternalImageId(aId)) { |
447 | 0 | SharedSurfacesParent::RemoveSameProcess(aId); |
448 | 0 | } |
449 | 0 | } else if (manager->OwnsExternalImageId(aId)) { |
450 | 0 | // Only attempt to release current mappings in the GPU process. It is |
451 | 0 | // possible we had a surface that was previously shared, the GPU process |
452 | 0 | // crashed / was restarted, and then we freed the surface. In that case |
453 | 0 | // we know the mapping has already been freed. |
454 | 0 | manager->SendRemoveSharedSurface(aId); |
455 | 0 | } |
456 | 0 | } |
457 | | |
458 | | } // namespace layers |
459 | | } // namespace mozilla |