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/. */
7 #include "PersistentBufferProvider.h"
10 #include "mozilla/layers/ShadowLayers.h"
11 #include "mozilla/layers/TextureClient.h"
12 #include "mozilla/gfx/gfxVars.h"
13 #include "mozilla/gfx/Logging.h"
14 #include "mozilla/StaticPrefs_layers.h"
16 #include "gfxPlatform.h"
24 PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget
* aDt
)
26 MOZ_COUNT_CTOR(PersistentBufferProviderBasic
);
29 PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
30 MOZ_COUNT_DTOR(PersistentBufferProviderBasic
);
34 already_AddRefed
<gfx::DrawTarget
>
35 PersistentBufferProviderBasic::BorrowDrawTarget(
36 const gfx::IntRect
& aPersistedRect
) {
37 MOZ_ASSERT(!mSnapshot
);
38 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
42 bool PersistentBufferProviderBasic::ReturnDrawTarget(
43 already_AddRefed
<gfx::DrawTarget
> aDT
) {
44 RefPtr
<gfx::DrawTarget
> dt(aDT
);
45 MOZ_ASSERT(mDrawTarget
== dt
);
47 // Since SkiaGL default to storing drawing command until flush
48 // we have to flush it before present.
54 already_AddRefed
<gfx::SourceSurface
>
55 PersistentBufferProviderBasic::BorrowSnapshot() {
56 mSnapshot
= mDrawTarget
->Snapshot();
57 RefPtr
<SourceSurface
> snapshot
= mSnapshot
;
58 return snapshot
.forget();
61 void PersistentBufferProviderBasic::ReturnSnapshot(
62 already_AddRefed
<gfx::SourceSurface
> aSnapshot
) {
63 RefPtr
<SourceSurface
> snapshot
= aSnapshot
;
64 MOZ_ASSERT(!snapshot
|| snapshot
== mSnapshot
);
68 void PersistentBufferProviderBasic::Destroy() {
70 mDrawTarget
= nullptr;
74 already_AddRefed
<PersistentBufferProviderBasic
>
75 PersistentBufferProviderBasic::Create(gfx::IntSize aSize
,
76 gfx::SurfaceFormat aFormat
,
77 gfx::BackendType aBackend
) {
78 RefPtr
<DrawTarget
> dt
=
79 gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend
, aSize
,
83 // This is simply to ensure the DrawTarget gets initialized, and will detect
84 // a device reset, even if we're on the main thread.
85 dt
->ClearRect(Rect(0, 0, 0, 0));
88 if (!dt
|| !dt
->IsValid()) {
92 RefPtr
<PersistentBufferProviderBasic
> provider
=
93 new PersistentBufferProviderBasic(dt
);
95 return provider
.forget();
99 already_AddRefed
<PersistentBufferProviderShared
>
100 PersistentBufferProviderShared::Create(gfx::IntSize aSize
,
101 gfx::SurfaceFormat aFormat
,
102 KnowsCompositor
* aKnowsCompositor
) {
103 if (!aKnowsCompositor
|| !aKnowsCompositor
->GetTextureForwarder() ||
104 !aKnowsCompositor
->GetTextureForwarder()->IPCOpen() ||
105 // Bug 1556433 - shared buffer provider and direct texture mapping do not
106 // synchronize properly
107 aKnowsCompositor
->SupportsTextureDirectMapping()) {
111 if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
116 // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
117 // instability, unless we are remoting the canvas drawing to the GPU process.
118 if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
119 BackendType::DIRECT2D1_1
&&
120 !TextureData::IsRemote(aKnowsCompositor
, BackendSelector::Canvas
)) {
125 RefPtr
<TextureClient
> texture
= TextureClient::CreateForDrawing(
126 aKnowsCompositor
, aFormat
, aSize
, BackendSelector::Canvas
,
127 TextureFlags::DEFAULT
| TextureFlags::NON_BLOCKING_READ_LOCK
,
128 TextureAllocationFlags::ALLOC_DEFAULT
);
134 RefPtr
<PersistentBufferProviderShared
> provider
=
135 new PersistentBufferProviderShared(aSize
, aFormat
, aKnowsCompositor
,
137 return provider
.forget();
140 PersistentBufferProviderShared::PersistentBufferProviderShared(
141 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
,
142 KnowsCompositor
* aKnowsCompositor
, RefPtr
<TextureClient
>& aTexture
)
146 mKnowsCompositor(aKnowsCompositor
),
148 MOZ_ASSERT(aKnowsCompositor
);
149 if (mTextures
.append(aTexture
)) {
150 mBack
= Some
<uint32_t>(0);
153 // If we are using webrender and our textures don't have an intermediate
154 // buffer, then we have to hold onto the textures for longer to make sure that
155 // the GPU has finished using them. So, we need to allow more TextureClients
157 if (!aTexture
->HasIntermediateBuffer() && gfxVars::UseWebRender()) {
158 ++mMaxAllowedTextures
;
159 if (gfxVars::UseWebRenderTripleBufferingWin()) {
160 ++mMaxAllowedTextures
;
164 MOZ_COUNT_CTOR(PersistentBufferProviderShared
);
167 PersistentBufferProviderShared::~PersistentBufferProviderShared() {
168 MOZ_COUNT_DTOR(PersistentBufferProviderShared
);
170 if (IsActivityTracked()) {
171 mKnowsCompositor
->GetActiveResourceTracker()->RemoveObject(this);
177 LayersBackend
PersistentBufferProviderShared::GetType() {
178 if (mKnowsCompositor
->GetCompositorBackendType() ==
179 LayersBackend::LAYERS_WR
) {
180 return LayersBackend::LAYERS_WR
;
182 return LayersBackend::LAYERS_CLIENT
;
186 bool PersistentBufferProviderShared::SetKnowsCompositor(
187 KnowsCompositor
* aKnowsCompositor
) {
188 MOZ_ASSERT(aKnowsCompositor
);
189 if (!aKnowsCompositor
) {
193 if (mKnowsCompositor
== aKnowsCompositor
) {
194 // The forwarder should not change most of the time.
198 if (IsActivityTracked()) {
199 mKnowsCompositor
->GetActiveResourceTracker()->RemoveObject(this);
202 if (mKnowsCompositor
->GetTextureForwarder() !=
203 aKnowsCompositor
->GetTextureForwarder() ||
204 mKnowsCompositor
->GetCompositorBackendType() !=
205 aKnowsCompositor
->GetCompositorBackendType()) {
206 // We are going to be used with an different and/or incompatible forwarder.
207 // This should be extremely rare. We have to copy the front buffer into a
208 // texture that is compatible with the new forwarder.
210 // Grab the current front buffer.
211 RefPtr
<TextureClient
> prevTexture
= GetTexture(mFront
);
213 // Get rid of everything else
217 RefPtr
<TextureClient
> newTexture
= TextureClient::CreateForDrawing(
218 aKnowsCompositor
, mFormat
, mSize
, BackendSelector::Canvas
,
219 TextureFlags::DEFAULT
| TextureFlags::NON_BLOCKING_READ_LOCK
,
220 TextureAllocationFlags::ALLOC_DEFAULT
);
222 MOZ_ASSERT(newTexture
);
227 // If we early-return in one of the following branches, we will
228 // leave the buffer provider in an empty state, since we called
229 // Destroy. Not ideal but at least we won't try to use it with a
230 // an incompatible ipc channel.
232 if (!newTexture
->Lock(OpenMode::OPEN_WRITE
)) {
236 if (!prevTexture
->Lock(OpenMode::OPEN_READ
)) {
237 newTexture
->Unlock();
242 prevTexture
->CopyToTextureClient(newTexture
, nullptr, nullptr);
244 prevTexture
->Unlock();
245 newTexture
->Unlock();
251 if (!mTextures
.append(newTexture
)) {
254 mFront
= Some
<uint32_t>(mTextures
.length() - 1);
259 mKnowsCompositor
= aKnowsCompositor
;
264 TextureClient
* PersistentBufferProviderShared::GetTexture(
265 const Maybe
<uint32_t>& aIndex
) {
266 if (aIndex
.isNothing() || !CheckIndex(aIndex
.value())) {
269 return mTextures
[aIndex
.value()];
272 already_AddRefed
<gfx::DrawTarget
>
273 PersistentBufferProviderShared::BorrowDrawTarget(
274 const gfx::IntRect
& aPersistedRect
) {
275 if (!mKnowsCompositor
->GetTextureForwarder() ||
276 !mKnowsCompositor
->GetTextureForwarder()->IPCOpen()) {
280 MOZ_ASSERT(!mSnapshot
);
282 if (IsActivityTracked()) {
283 mKnowsCompositor
->GetActiveResourceTracker()->MarkUsed(this);
285 mKnowsCompositor
->GetActiveResourceTracker()->AddObject(this);
289 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
293 auto previousBackBuffer
= mBack
;
295 TextureClient
* tex
= GetTexture(mBack
);
297 // First try to reuse the current back buffer. If we can do that it means
298 // we can skip copying its content to the new back buffer.
299 if ((mTextureLockIsUnreliable
.isSome() &&
300 mTextureLockIsUnreliable
== mBack
) ||
301 (tex
&& tex
->IsReadLocked())) {
302 // The back buffer is currently used by the compositor, we can't draw
308 // Try to grab an already allocated texture if any is available.
309 for (uint32_t i
= 0; i
< mTextures
.length(); ++i
) {
310 if (!mTextures
[i
]->IsReadLocked() &&
311 !(mTextureLockIsUnreliable
.isSome() &&
312 mTextureLockIsUnreliable
.ref() == i
)) {
321 // We have to allocate a new texture.
322 if (mTextures
.length() >= mMaxAllowedTextures
) {
323 // We should never need to buffer that many textures, something's wrong.
324 // In theory we throttle the main thread when the compositor can't keep
325 // up, so we shoud never get in a situation where we sent 4 textures to
326 // the compositor and the latter has not released any of them. In
327 // practice, though, the throttling mechanism appears to have some issues,
328 // especially when switching between layer managers (during tab-switch).
329 // To make sure we don't get too far ahead of the compositor, we send a
330 // sync ping to the compositor thread...
331 mKnowsCompositor
->SyncWithCompositor();
333 for (uint32_t i
= 0; i
< mTextures
.length(); ++i
) {
334 if (!mTextures
[i
]->IsReadLocked()) {
335 gfxCriticalNote
<< "Managed to allocate after flush.";
343 gfxCriticalNote
<< "Unexpected BufferProvider over-production.";
344 // It would be pretty bad to keep piling textures up at this point so we
345 // call NotifyInactive to remove some of our textures.
347 // Give up now. The caller can fall-back to a non-shared buffer
353 RefPtr
<TextureClient
> newTexture
= TextureClient::CreateForDrawing(
354 mKnowsCompositor
, mFormat
, mSize
, BackendSelector::Canvas
,
355 TextureFlags::DEFAULT
| TextureFlags::NON_BLOCKING_READ_LOCK
,
356 TextureAllocationFlags::ALLOC_DEFAULT
);
358 MOZ_ASSERT(newTexture
);
360 if (mTextures
.append(newTexture
)) {
362 mBack
= Some
<uint32_t>(mTextures
.length() - 1);
367 if (!tex
|| !tex
->Lock(OpenMode::OPEN_READ_WRITE
)) {
371 // Clear dirty texture, since new back texture is selected.
372 mTextureLockIsUnreliable
= Nothing();
374 mDrawTarget
= tex
->BorrowDrawTarget();
375 if (mBack
!= previousBackBuffer
&& !aPersistedRect
.IsEmpty()) {
376 if (mPreviousSnapshot
) {
377 mDrawTarget
->CopySurface(mPreviousSnapshot
, aPersistedRect
,
378 gfx::IntPoint(0, 0));
380 TextureClient
* previous
= GetTexture(previousBackBuffer
);
381 if (previous
&& previous
->Lock(OpenMode::OPEN_READ
)) {
382 DebugOnly
<bool> success
=
383 previous
->CopyToTextureClient(tex
, &aPersistedRect
, nullptr);
390 mPreviousSnapshot
= nullptr;
393 // This is simply to ensure the DrawTarget gets initialized, and will detect
394 // a device reset, even if we're on the main thread.
395 mDrawTarget
->ClearRect(Rect(0, 0, 0, 0));
397 if (!mDrawTarget
->IsValid()) {
398 mDrawTarget
= nullptr;
402 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
406 bool PersistentBufferProviderShared::ReturnDrawTarget(
407 already_AddRefed
<gfx::DrawTarget
> aDT
) {
408 RefPtr
<gfx::DrawTarget
> dt(aDT
);
409 MOZ_ASSERT(mDrawTarget
== dt
);
410 // Can't change the current front buffer while its snapshot is borrowed!
411 MOZ_ASSERT(!mSnapshot
);
413 TextureClient
* back
= GetTexture(mBack
);
416 // If our TextureClients have internal synchronization then, if locks are
417 // needed for reading and writing, this can cause locking issues with the
418 // compositor. To prevent this we take a snapshot when the DrawTarget is
419 // returned, so this can be used when our own BorrowSnapshot is called and
420 // also for copying to the next TextureClient. Using this snapshot outside of
421 // the locks is safe, because the TextureClient calls DetachAllSnapshots on
422 // its DrawTarget when we Unlock below.
423 if (back
->HasSynchronization()) {
424 mPreviousSnapshot
= back
->BorrowSnapshot();
427 mDrawTarget
= nullptr;
438 TextureClient
* PersistentBufferProviderShared::GetTextureClient() {
439 // Can't access the front buffer while drawing.
440 MOZ_ASSERT(!mDrawTarget
);
441 TextureClient
* texture
= GetTexture(mFront
);
444 << "PersistentBufferProviderShared: front buffer unavailable";
449 already_AddRefed
<gfx::SourceSurface
>
450 PersistentBufferProviderShared::BorrowSnapshot() {
451 if (mPreviousSnapshot
) {
452 mSnapshot
= mPreviousSnapshot
;
453 return do_AddRef(mSnapshot
);
457 auto back
= GetTexture(mBack
);
458 MOZ_ASSERT(back
&& back
->IsLocked());
459 mSnapshot
= back
->BorrowSnapshot();
460 return do_AddRef(mSnapshot
);
463 auto front
= GetTexture(mFront
);
464 if (!front
|| front
->IsLocked()) {
469 if (!front
->Lock(OpenMode::OPEN_READ
)) {
473 mSnapshot
= front
->BorrowSnapshot();
475 return do_AddRef(mSnapshot
);
478 void PersistentBufferProviderShared::ReturnSnapshot(
479 already_AddRefed
<gfx::SourceSurface
> aSnapshot
) {
480 RefPtr
<SourceSurface
> snapshot
= aSnapshot
;
481 MOZ_ASSERT(!snapshot
|| snapshot
== mSnapshot
);
486 if (mPreviousSnapshot
|| mDrawTarget
) {
490 auto front
= GetTexture(mFront
);
496 void PersistentBufferProviderShared::NotifyInactive() {
497 ClearCachedResources();
500 void PersistentBufferProviderShared::ClearCachedResources() {
501 RefPtr
<TextureClient
> front
= GetTexture(mFront
);
502 RefPtr
<TextureClient
> back
= GetTexture(mBack
);
504 // Clear all textures (except the front and back ones that we just kept).
508 if (mTextures
.append(back
)) {
509 mBack
= Some
<uint32_t>(0);
516 if (front
&& front
!= back
) {
517 if (mTextures
.append(front
)) {
518 mFront
= Some
<uint32_t>(mTextures
.length() - 1);
521 // Set front texture as dirty texture.
522 // The texture's read lock is unreliable after this function call.
523 mTextureLockIsUnreliable
= mFront
;
526 void PersistentBufferProviderShared::Destroy() {
528 mPreviousSnapshot
= nullptr;
529 mDrawTarget
= nullptr;
531 for (auto& mTexture
: mTextures
) {
532 TextureClient
* texture
= mTexture
;
533 if (texture
&& texture
->IsLocked()) {
542 } // namespace layers
543 } // namespace mozilla