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