/src/mozilla-central/gfx/layers/PersistentBufferProvider.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 "PersistentBufferProvider.h" |
8 | | |
9 | | #include "Layers.h" |
10 | | #include "mozilla/layers/ShadowLayers.h" |
11 | | #include "mozilla/layers/TextureClient.h" |
12 | | #include "mozilla/gfx/Logging.h" |
13 | | #include "pratom.h" |
14 | | #include "gfxPlatform.h" |
15 | | |
16 | | namespace mozilla { |
17 | | |
18 | | using namespace gfx; |
19 | | |
20 | | namespace layers { |
21 | | |
22 | | PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt) |
23 | | : mDrawTarget(aDt) |
24 | 0 | { |
25 | 0 | MOZ_COUNT_CTOR(PersistentBufferProviderBasic); |
26 | 0 | } |
27 | | |
28 | | PersistentBufferProviderBasic::~PersistentBufferProviderBasic() |
29 | 0 | { |
30 | 0 | MOZ_COUNT_DTOR(PersistentBufferProviderBasic); |
31 | 0 | Destroy(); |
32 | 0 | } |
33 | | |
34 | | already_AddRefed<gfx::DrawTarget> |
35 | | PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect) |
36 | 0 | { |
37 | 0 | MOZ_ASSERT(!mSnapshot); |
38 | 0 | RefPtr<gfx::DrawTarget> dt(mDrawTarget); |
39 | 0 | return dt.forget(); |
40 | 0 | } |
41 | | |
42 | | bool |
43 | | PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) |
44 | 0 | { |
45 | 0 | RefPtr<gfx::DrawTarget> dt(aDT); |
46 | 0 | MOZ_ASSERT(mDrawTarget == dt); |
47 | 0 | if (dt) { |
48 | 0 | // Since SkiaGL default to storing drawing command until flush |
49 | 0 | // we have to flush it before present. |
50 | 0 | dt->Flush(); |
51 | 0 | } |
52 | 0 | return true; |
53 | 0 | } |
54 | | |
55 | | already_AddRefed<gfx::SourceSurface> |
56 | | PersistentBufferProviderBasic::BorrowSnapshot() |
57 | 0 | { |
58 | 0 | mSnapshot = mDrawTarget->Snapshot(); |
59 | 0 | RefPtr<SourceSurface> snapshot = mSnapshot; |
60 | 0 | return snapshot.forget(); |
61 | 0 | } |
62 | | |
63 | | void |
64 | | PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) |
65 | 0 | { |
66 | 0 | RefPtr<SourceSurface> snapshot = aSnapshot; |
67 | 0 | MOZ_ASSERT(!snapshot || snapshot == mSnapshot); |
68 | 0 | mSnapshot = nullptr; |
69 | 0 | } |
70 | | |
71 | | void |
72 | | PersistentBufferProviderBasic::Destroy() |
73 | 0 | { |
74 | 0 | mSnapshot = nullptr; |
75 | 0 | mDrawTarget = nullptr; |
76 | 0 | } |
77 | | |
78 | | //static |
79 | | already_AddRefed<PersistentBufferProviderBasic> |
80 | | PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, |
81 | | gfx::BackendType aBackend) |
82 | 0 | { |
83 | 0 | RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat); |
84 | 0 |
|
85 | 0 | if (!dt) { |
86 | 0 | return nullptr; |
87 | 0 | } |
88 | 0 | |
89 | 0 | RefPtr<PersistentBufferProviderBasic> provider = |
90 | 0 | new PersistentBufferProviderBasic(dt); |
91 | 0 |
|
92 | 0 | return provider.forget(); |
93 | 0 | } |
94 | | |
95 | | |
96 | | //static |
97 | | already_AddRefed<PersistentBufferProviderShared> |
98 | | PersistentBufferProviderShared::Create(gfx::IntSize aSize, |
99 | | gfx::SurfaceFormat aFormat, |
100 | | KnowsCompositor* aKnowsCompositor) |
101 | 0 | { |
102 | 0 | if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) { |
103 | 0 | return nullptr; |
104 | 0 | } |
105 | 0 | |
106 | 0 | RefPtr<TextureClient> texture = TextureClient::CreateForDrawing( |
107 | 0 | aKnowsCompositor, aFormat, aSize, |
108 | 0 | BackendSelector::Canvas, |
109 | 0 | TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, |
110 | 0 | TextureAllocationFlags::ALLOC_DEFAULT |
111 | 0 | ); |
112 | 0 |
|
113 | 0 | if (!texture) { |
114 | 0 | return nullptr; |
115 | 0 | } |
116 | 0 | |
117 | 0 | RefPtr<PersistentBufferProviderShared> provider = |
118 | 0 | new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor, texture); |
119 | 0 | return provider.forget(); |
120 | 0 | } |
121 | | |
122 | | PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize, |
123 | | gfx::SurfaceFormat aFormat, |
124 | | KnowsCompositor* aKnowsCompositor, |
125 | | RefPtr<TextureClient>& aTexture) |
126 | | |
127 | | : mSize(aSize) |
128 | | , mFormat(aFormat) |
129 | | , mKnowsCompositor(aKnowsCompositor) |
130 | | , mFront(Nothing()) |
131 | 0 | { |
132 | 0 | MOZ_ASSERT(aKnowsCompositor); |
133 | 0 | if (mTextures.append(aTexture)) { |
134 | 0 | mBack = Some<uint32_t>(0); |
135 | 0 | } |
136 | 0 | MOZ_COUNT_CTOR(PersistentBufferProviderShared); |
137 | 0 | } |
138 | | |
139 | | PersistentBufferProviderShared::~PersistentBufferProviderShared() |
140 | 0 | { |
141 | 0 | MOZ_COUNT_DTOR(PersistentBufferProviderShared); |
142 | 0 |
|
143 | 0 | if (IsActivityTracked()) { |
144 | 0 | mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this); |
145 | 0 | } |
146 | 0 |
|
147 | 0 | Destroy(); |
148 | 0 | } |
149 | | |
150 | | LayersBackend |
151 | | PersistentBufferProviderShared::GetType() |
152 | 0 | { |
153 | 0 | if (mKnowsCompositor->GetCompositorBackendType() == LayersBackend::LAYERS_WR) { |
154 | 0 | return LayersBackend::LAYERS_WR; |
155 | 0 | } else { |
156 | 0 | return LayersBackend::LAYERS_CLIENT; |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | bool |
161 | | PersistentBufferProviderShared::SetKnowsCompositor(KnowsCompositor* aKnowsCompositor) |
162 | 0 | { |
163 | 0 | MOZ_ASSERT(aKnowsCompositor); |
164 | 0 | if (!aKnowsCompositor) { |
165 | 0 | return false; |
166 | 0 | } |
167 | 0 | |
168 | 0 | if (mKnowsCompositor == aKnowsCompositor) { |
169 | 0 | // The forwarder should not change most of the time. |
170 | 0 | return true; |
171 | 0 | } |
172 | 0 | |
173 | 0 | if (IsActivityTracked()) { |
174 | 0 | mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this); |
175 | 0 | } |
176 | 0 |
|
177 | 0 | if (mKnowsCompositor->GetTextureForwarder() != aKnowsCompositor->GetTextureForwarder() || |
178 | 0 | mKnowsCompositor->GetCompositorBackendType() != aKnowsCompositor->GetCompositorBackendType()) { |
179 | 0 | // We are going to be used with an different and/or incompatible forwarder. |
180 | 0 | // This should be extremely rare. We have to copy the front buffer into a |
181 | 0 | // texture that is compatible with the new forwarder. |
182 | 0 |
|
183 | 0 | // Grab the current front buffer. |
184 | 0 | RefPtr<TextureClient> prevTexture = GetTexture(mFront); |
185 | 0 |
|
186 | 0 | // Get rid of everything else |
187 | 0 | Destroy(); |
188 | 0 |
|
189 | 0 | if (prevTexture) { |
190 | 0 | RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing( |
191 | 0 | aKnowsCompositor, mFormat, mSize, |
192 | 0 | BackendSelector::Canvas, |
193 | 0 | TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, |
194 | 0 | TextureAllocationFlags::ALLOC_DEFAULT |
195 | 0 | ); |
196 | 0 |
|
197 | 0 | MOZ_ASSERT(newTexture); |
198 | 0 | if (!newTexture) { |
199 | 0 | return false; |
200 | 0 | } |
201 | 0 | |
202 | 0 | // If we early-return in one of the following branches, we will |
203 | 0 | // leave the buffer provider in an empty state, since we called |
204 | 0 | // Destroy. Not ideal but at least we won't try to use it with a |
205 | 0 | // an incompatible ipc channel. |
206 | 0 | |
207 | 0 | if (!newTexture->Lock(OpenMode::OPEN_WRITE)) { |
208 | 0 | return false; |
209 | 0 | } |
210 | 0 | |
211 | 0 | if (!prevTexture->Lock(OpenMode::OPEN_READ)) { |
212 | 0 | newTexture->Unlock(); |
213 | 0 | return false; |
214 | 0 | } |
215 | 0 | |
216 | 0 | bool success = prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr); |
217 | 0 |
|
218 | 0 | prevTexture->Unlock(); |
219 | 0 | newTexture->Unlock(); |
220 | 0 |
|
221 | 0 | if (!success) { |
222 | 0 | return false; |
223 | 0 | } |
224 | 0 | |
225 | 0 | if (!mTextures.append(newTexture)) { |
226 | 0 | return false; |
227 | 0 | } |
228 | 0 | mFront = Some<uint32_t>(mTextures.length() - 1); |
229 | 0 | mBack = mFront; |
230 | 0 | } |
231 | 0 | } |
232 | 0 |
|
233 | 0 | mKnowsCompositor = aKnowsCompositor; |
234 | 0 |
|
235 | 0 | return true; |
236 | 0 | } |
237 | | |
238 | | TextureClient* |
239 | | PersistentBufferProviderShared::GetTexture(const Maybe<uint32_t>& aIndex) |
240 | 0 | { |
241 | 0 | if (aIndex.isNothing() || !CheckIndex(aIndex.value())) { |
242 | 0 | return nullptr; |
243 | 0 | } |
244 | 0 | return mTextures[aIndex.value()]; |
245 | 0 | } |
246 | | |
247 | | already_AddRefed<gfx::DrawTarget> |
248 | | PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect) |
249 | 0 | { |
250 | 0 | if (!mKnowsCompositor->GetTextureForwarder()->IPCOpen()) { |
251 | 0 | return nullptr; |
252 | 0 | } |
253 | 0 | |
254 | 0 | MOZ_ASSERT(!mSnapshot); |
255 | 0 |
|
256 | 0 | if (IsActivityTracked()) { |
257 | 0 | mKnowsCompositor->GetActiveResourceTracker()->MarkUsed(this); |
258 | 0 | } else { |
259 | 0 | mKnowsCompositor->GetActiveResourceTracker()->AddObject(this); |
260 | 0 | } |
261 | 0 |
|
262 | 0 | if (mDrawTarget) { |
263 | 0 | RefPtr<gfx::DrawTarget> dt(mDrawTarget); |
264 | 0 | return dt.forget(); |
265 | 0 | } |
266 | 0 | |
267 | 0 | auto previousBackBuffer = mBack; |
268 | 0 |
|
269 | 0 | TextureClient* tex = GetTexture(mBack); |
270 | 0 |
|
271 | 0 | // First try to reuse the current back buffer. If we can do that it means |
272 | 0 | // we can skip copying its content to the new back buffer. |
273 | 0 | if (tex && tex->IsReadLocked()) { |
274 | 0 | // The back buffer is currently used by the compositor, we can't draw |
275 | 0 | // into it. |
276 | 0 | tex = nullptr; |
277 | 0 | } |
278 | 0 |
|
279 | 0 | if (!tex) { |
280 | 0 | // Try to grab an already allocated texture if any is available. |
281 | 0 | for (uint32_t i = 0; i < mTextures.length(); ++i) { |
282 | 0 | if (!mTextures[i]->IsReadLocked()) { |
283 | 0 | mBack = Some(i); |
284 | 0 | tex = mTextures[i]; |
285 | 0 | break; |
286 | 0 | } |
287 | 0 | } |
288 | 0 | } |
289 | 0 |
|
290 | 0 | if (!tex) { |
291 | 0 | // We have to allocate a new texture. |
292 | 0 | if (mTextures.length() >= 4) { |
293 | 0 | // We should never need to buffer that many textures, something's wrong. |
294 | 0 | // In theory we throttle the main thread when the compositor can't keep up, |
295 | 0 | // so we shoud never get in a situation where we sent 4 textures to the |
296 | 0 | // compositor and the latter has not released any of them. |
297 | 0 | // In practice, though, the throttling mechanism appears to have some issues, |
298 | 0 | // especially when switching between layer managers (during tab-switch). |
299 | 0 | // To make sure we don't get too far ahead of the compositor, we send a |
300 | 0 | // sync ping to the compositor thread... |
301 | 0 | mKnowsCompositor->SyncWithCompositor(); |
302 | 0 | // ...and try again. |
303 | 0 | for (uint32_t i = 0; i < mTextures.length(); ++i) { |
304 | 0 | if (!mTextures[i]->IsReadLocked()) { |
305 | 0 | gfxCriticalNote << "Managed to allocate after flush."; |
306 | 0 | mBack = Some(i); |
307 | 0 | tex = mTextures[i]; |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | } |
311 | 0 |
|
312 | 0 | if (!tex) { |
313 | 0 | gfxCriticalError() << "Unexpected BufferProvider over-production."; |
314 | 0 | // It would be pretty bad to keep piling textures up at this point so we |
315 | 0 | // call NotifyInactive to remove some of our textures. |
316 | 0 | NotifyInactive(); |
317 | 0 | // Give up now. The caller can fall-back to a non-shared buffer provider. |
318 | 0 | return nullptr; |
319 | 0 | } |
320 | 0 | } |
321 | 0 |
|
322 | 0 | RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing( |
323 | 0 | mKnowsCompositor, mFormat, mSize, |
324 | 0 | BackendSelector::Canvas, |
325 | 0 | TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, |
326 | 0 | TextureAllocationFlags::ALLOC_DEFAULT |
327 | 0 | ); |
328 | 0 |
|
329 | 0 | MOZ_ASSERT(newTexture); |
330 | 0 | if (newTexture) { |
331 | 0 | if (mTextures.append(newTexture)) { |
332 | 0 | tex = newTexture; |
333 | 0 | mBack = Some<uint32_t>(mTextures.length() - 1); |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | 0 |
|
338 | 0 | if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) { |
339 | 0 | return nullptr; |
340 | 0 | } |
341 | 0 | |
342 | 0 | if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) { |
343 | 0 | TextureClient* previous = GetTexture(previousBackBuffer); |
344 | 0 | if (previous && previous->Lock(OpenMode::OPEN_READ)) { |
345 | 0 | DebugOnly<bool> success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr); |
346 | 0 | MOZ_ASSERT(success); |
347 | 0 |
|
348 | 0 | previous->Unlock(); |
349 | 0 | } |
350 | 0 | } |
351 | 0 |
|
352 | 0 | mDrawTarget = tex->BorrowDrawTarget(); |
353 | 0 |
|
354 | 0 | RefPtr<gfx::DrawTarget> dt(mDrawTarget); |
355 | 0 | return dt.forget(); |
356 | 0 | } |
357 | | |
358 | | bool |
359 | | PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) |
360 | 0 | { |
361 | 0 | RefPtr<gfx::DrawTarget> dt(aDT); |
362 | 0 | MOZ_ASSERT(mDrawTarget == dt); |
363 | 0 | // Can't change the current front buffer while its snapshot is borrowed! |
364 | 0 | MOZ_ASSERT(!mSnapshot); |
365 | 0 |
|
366 | 0 | mDrawTarget = nullptr; |
367 | 0 | dt = nullptr; |
368 | 0 |
|
369 | 0 | TextureClient* back = GetTexture(mBack); |
370 | 0 | MOZ_ASSERT(back); |
371 | 0 |
|
372 | 0 | if (back) { |
373 | 0 | back->Unlock(); |
374 | 0 | mFront = mBack; |
375 | 0 | } |
376 | 0 |
|
377 | 0 | return !!back; |
378 | 0 | } |
379 | | |
380 | | TextureClient* |
381 | | PersistentBufferProviderShared::GetTextureClient() |
382 | 0 | { |
383 | 0 | // Can't access the front buffer while drawing. |
384 | 0 | MOZ_ASSERT(!mDrawTarget); |
385 | 0 | TextureClient* texture = GetTexture(mFront); |
386 | 0 | if (!texture) { |
387 | 0 | gfxCriticalNote << "PersistentBufferProviderShared: front buffer unavailable"; |
388 | 0 | } |
389 | 0 | return texture; |
390 | 0 | } |
391 | | |
392 | | already_AddRefed<gfx::SourceSurface> |
393 | | PersistentBufferProviderShared::BorrowSnapshot() |
394 | 0 | { |
395 | 0 | MOZ_ASSERT(!mDrawTarget); |
396 | 0 |
|
397 | 0 | auto front = GetTexture(mFront); |
398 | 0 | if (!front || front->IsLocked()) { |
399 | 0 | MOZ_ASSERT(false); |
400 | 0 | return nullptr; |
401 | 0 | } |
402 | 0 |
|
403 | 0 | if (!front->Lock(OpenMode::OPEN_READ)) { |
404 | 0 | return nullptr; |
405 | 0 | } |
406 | 0 | |
407 | 0 | RefPtr<DrawTarget> dt = front->BorrowDrawTarget(); |
408 | 0 |
|
409 | 0 | if (!dt) { |
410 | 0 | front->Unlock(); |
411 | 0 | return nullptr; |
412 | 0 | } |
413 | 0 | |
414 | 0 | mSnapshot = dt->Snapshot(); |
415 | 0 |
|
416 | 0 | RefPtr<SourceSurface> snapshot = mSnapshot; |
417 | 0 | return snapshot.forget(); |
418 | 0 | } |
419 | | |
420 | | void |
421 | | PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) |
422 | 0 | { |
423 | 0 | RefPtr<SourceSurface> snapshot = aSnapshot; |
424 | 0 | MOZ_ASSERT(!snapshot || snapshot == mSnapshot); |
425 | 0 |
|
426 | 0 | mSnapshot = nullptr; |
427 | 0 | snapshot = nullptr; |
428 | 0 |
|
429 | 0 | auto front = GetTexture(mFront); |
430 | 0 | if (front) { |
431 | 0 | front->Unlock(); |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | void |
436 | | PersistentBufferProviderShared::NotifyInactive() |
437 | 0 | { |
438 | 0 | ClearCachedResources(); |
439 | 0 | } |
440 | | |
441 | | void |
442 | | PersistentBufferProviderShared::ClearCachedResources() |
443 | 0 | { |
444 | 0 | RefPtr<TextureClient> front = GetTexture(mFront); |
445 | 0 | RefPtr<TextureClient> back = GetTexture(mBack); |
446 | 0 |
|
447 | 0 | // Clear all textures (except the front and back ones that we just kept). |
448 | 0 | mTextures.clear(); |
449 | 0 |
|
450 | 0 | if (back) { |
451 | 0 | if (mTextures.append(back)) { |
452 | 0 | mBack = Some<uint32_t>(0); |
453 | 0 | } |
454 | 0 | if (front == back) { |
455 | 0 | mFront = mBack; |
456 | 0 | } |
457 | 0 | } |
458 | 0 |
|
459 | 0 | if (front && front != back) { |
460 | 0 | if (mTextures.append(front)) { |
461 | 0 | mFront = Some<uint32_t>(mTextures.length() - 1); |
462 | 0 | } |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | | void |
467 | | PersistentBufferProviderShared::Destroy() |
468 | 0 | { |
469 | 0 | mSnapshot = nullptr; |
470 | 0 | mDrawTarget = nullptr; |
471 | 0 |
|
472 | 0 | for (auto& mTexture : mTextures) { |
473 | 0 | TextureClient* texture = mTexture; |
474 | 0 | if (texture && texture->IsLocked()) { |
475 | 0 | MOZ_ASSERT(false); |
476 | 0 | texture->Unlock(); |
477 | 0 | } |
478 | 0 | } |
479 | 0 |
|
480 | 0 | mTextures.clear(); |
481 | 0 | } |
482 | | |
483 | | } // namespace layers |
484 | | } // namespace mozilla |