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"
9 #include "mozilla/layers/TextureClient.h"
10 #include "mozilla/layers/TextureForwarder.h"
11 #include "mozilla/gfx/gfxVars.h"
12 #include "mozilla/gfx/DrawTargetWebgl.h"
13 #include "mozilla/gfx/Logging.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/StaticPrefs_layers.h"
17 #include "gfxPlatform.h"
25 PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget
* aDt
)
27 MOZ_COUNT_CTOR(PersistentBufferProviderBasic
);
30 PersistentBufferProviderBasic::~PersistentBufferProviderBasic() {
31 MOZ_COUNT_DTOR(PersistentBufferProviderBasic
);
35 already_AddRefed
<gfx::DrawTarget
>
36 PersistentBufferProviderBasic::BorrowDrawTarget(
37 const gfx::IntRect
& aPersistedRect
) {
38 MOZ_ASSERT(!mSnapshot
);
39 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
43 bool PersistentBufferProviderBasic::ReturnDrawTarget(
44 already_AddRefed
<gfx::DrawTarget
> aDT
) {
45 RefPtr
<gfx::DrawTarget
> dt(aDT
);
46 MOZ_ASSERT(mDrawTarget
== dt
);
48 // Since SkiaGL default to storing drawing command until flush
49 // we have to flush it before present.
55 already_AddRefed
<gfx::SourceSurface
>
56 PersistentBufferProviderBasic::BorrowSnapshot(gfx::DrawTarget
* aTarget
) {
57 mSnapshot
= mDrawTarget
->Snapshot();
58 RefPtr
<SourceSurface
> snapshot
= mSnapshot
;
59 return snapshot
.forget();
62 void PersistentBufferProviderBasic::ReturnSnapshot(
63 already_AddRefed
<gfx::SourceSurface
> aSnapshot
) {
64 RefPtr
<SourceSurface
> snapshot
= aSnapshot
;
65 MOZ_ASSERT(!snapshot
|| snapshot
== mSnapshot
);
69 void PersistentBufferProviderBasic::Destroy() {
71 mDrawTarget
= nullptr;
75 already_AddRefed
<PersistentBufferProviderBasic
>
76 PersistentBufferProviderBasic::Create(gfx::IntSize aSize
,
77 gfx::SurfaceFormat aFormat
,
78 gfx::BackendType aBackend
) {
79 RefPtr
<DrawTarget
> dt
=
80 gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend
, aSize
,
84 // This is simply to ensure the DrawTarget gets initialized, and will detect
85 // a device reset, even if we're on the main thread.
86 dt
->ClearRect(Rect(0, 0, 0, 0));
89 if (!dt
|| !dt
->IsValid()) {
93 RefPtr
<PersistentBufferProviderBasic
> provider
=
94 new PersistentBufferProviderBasic(dt
);
96 return provider
.forget();
99 PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
101 : PersistentBufferProviderBasic(aDt
) {
102 MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated
);
103 MOZ_ASSERT(aDt
->GetBackendType() == BackendType::WEBGL
);
106 PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
107 MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated
);
110 inline gfx::DrawTargetWebgl
*
111 PersistentBufferProviderAccelerated::GetDrawTargetWebgl() const {
112 return static_cast<gfx::DrawTargetWebgl
*>(mDrawTarget
.get());
115 Maybe
<layers::SurfaceDescriptor
>
116 PersistentBufferProviderAccelerated::GetFrontBuffer() {
117 return GetDrawTargetWebgl()->GetFrontBuffer();
120 already_AddRefed
<gfx::DrawTarget
>
121 PersistentBufferProviderAccelerated::BorrowDrawTarget(
122 const gfx::IntRect
& aPersistedRect
) {
123 GetDrawTargetWebgl()->BeginFrame(aPersistedRect
);
124 return PersistentBufferProviderBasic::BorrowDrawTarget(aPersistedRect
);
127 bool PersistentBufferProviderAccelerated::ReturnDrawTarget(
128 already_AddRefed
<gfx::DrawTarget
> aDT
) {
129 bool result
= PersistentBufferProviderBasic::ReturnDrawTarget(std::move(aDT
));
130 GetDrawTargetWebgl()->EndFrame();
134 already_AddRefed
<gfx::SourceSurface
>
135 PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget
* aTarget
) {
136 mSnapshot
= GetDrawTargetWebgl()->GetOptimizedSnapshot(aTarget
);
137 return do_AddRef(mSnapshot
);
140 bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
141 return GetDrawTargetWebgl()->RequiresRefresh();
144 void PersistentBufferProviderAccelerated::OnMemoryPressure() {
145 GetDrawTargetWebgl()->OnMemoryPressure();
148 static already_AddRefed
<TextureClient
> CreateTexture(
149 KnowsCompositor
* aKnowsCompositor
, gfx::SurfaceFormat aFormat
,
150 gfx::IntSize aSize
, bool aWillReadFrequently
) {
151 TextureAllocationFlags flags
= ALLOC_DEFAULT
;
152 if (aWillReadFrequently
) {
153 flags
= TextureAllocationFlags(flags
| ALLOC_DO_NOT_ACCELERATE
);
155 return TextureClient::CreateForDrawing(
156 aKnowsCompositor
, aFormat
, aSize
, BackendSelector::Canvas
,
157 TextureFlags::DEFAULT
| TextureFlags::NON_BLOCKING_READ_LOCK
, flags
);
161 already_AddRefed
<PersistentBufferProviderShared
>
162 PersistentBufferProviderShared::Create(gfx::IntSize aSize
,
163 gfx::SurfaceFormat aFormat
,
164 KnowsCompositor
* aKnowsCompositor
,
165 bool aWillReadFrequently
) {
166 if (!aKnowsCompositor
|| !aKnowsCompositor
->GetTextureForwarder() ||
167 !aKnowsCompositor
->GetTextureForwarder()->IPCOpen()) {
171 if (!StaticPrefs::layers_shared_buffer_provider_enabled()) {
176 // Bug 1285271 - Disable shared buffer provider on Windows with D2D due to
177 // instability, unless we are remoting the canvas drawing to the GPU process.
178 if (gfxPlatform::GetPlatform()->GetPreferredCanvasBackend() ==
179 BackendType::DIRECT2D1_1
&&
180 !TextureData::IsRemote(aKnowsCompositor
, BackendSelector::Canvas
)) {
185 RefPtr
<TextureClient
> texture
=
186 CreateTexture(aKnowsCompositor
, aFormat
, aSize
, aWillReadFrequently
);
191 RefPtr
<PersistentBufferProviderShared
> provider
=
192 new PersistentBufferProviderShared(aSize
, aFormat
, aKnowsCompositor
,
193 texture
, aWillReadFrequently
);
194 return provider
.forget();
197 PersistentBufferProviderShared::PersistentBufferProviderShared(
198 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
,
199 KnowsCompositor
* aKnowsCompositor
, RefPtr
<TextureClient
>& aTexture
,
200 bool aWillReadFrequently
)
204 mKnowsCompositor(aKnowsCompositor
),
206 mWillReadFrequently(aWillReadFrequently
) {
207 MOZ_ASSERT(aKnowsCompositor
);
208 if (mTextures
.append(aTexture
)) {
209 mBack
= Some
<uint32_t>(0);
212 // XXX KnowsCompositor could be used for mMaxAllowedTextures
213 if (gfxVars::UseWebRenderTripleBufferingWin()) {
214 ++mMaxAllowedTextures
;
217 MOZ_COUNT_CTOR(PersistentBufferProviderShared
);
220 PersistentBufferProviderShared::~PersistentBufferProviderShared() {
221 MOZ_COUNT_DTOR(PersistentBufferProviderShared
);
223 if (IsActivityTracked()) {
224 mKnowsCompositor
->GetActiveResourceTracker()->RemoveObject(this);
230 bool PersistentBufferProviderShared::SetKnowsCompositor(
231 KnowsCompositor
* aKnowsCompositor
, bool& aOutLostFrontTexture
) {
232 MOZ_ASSERT(aKnowsCompositor
);
233 MOZ_ASSERT(!aOutLostFrontTexture
);
234 if (!aKnowsCompositor
) {
238 if (mKnowsCompositor
== aKnowsCompositor
) {
239 // The forwarder should not change most of the time.
243 if (IsActivityTracked()) {
244 mKnowsCompositor
->GetActiveResourceTracker()->RemoveObject(this);
247 if (mKnowsCompositor
->GetTextureForwarder() !=
248 aKnowsCompositor
->GetTextureForwarder() ||
249 mKnowsCompositor
->GetCompositorBackendType() !=
250 aKnowsCompositor
->GetCompositorBackendType()) {
251 // We are going to be used with an different and/or incompatible forwarder.
252 // This should be extremely rare. We have to copy the front buffer into a
253 // texture that is compatible with the new forwarder.
255 // Grab the current front buffer.
256 RefPtr
<TextureClient
> prevTexture
= GetTexture(mFront
);
258 // Get rid of everything else
261 if (prevTexture
&& !prevTexture
->IsValid()) {
262 aOutLostFrontTexture
= true;
263 } else if (prevTexture
&& prevTexture
->IsValid()) {
264 RefPtr
<TextureClient
> newTexture
=
265 CreateTexture(aKnowsCompositor
, mFormat
, mSize
, mWillReadFrequently
);
267 MOZ_ASSERT(newTexture
);
272 // If we early-return in one of the following branches, we will
273 // leave the buffer provider in an empty state, since we called
274 // Destroy. Not ideal but at least we won't try to use it with a
275 // an incompatible ipc channel.
277 if (!newTexture
->Lock(OpenMode::OPEN_WRITE
)) {
281 if (!prevTexture
->Lock(OpenMode::OPEN_READ
)) {
282 newTexture
->Unlock();
287 prevTexture
->CopyToTextureClient(newTexture
, nullptr, nullptr);
289 prevTexture
->Unlock();
290 newTexture
->Unlock();
296 if (!mTextures
.append(newTexture
)) {
299 mFront
= Some
<uint32_t>(mTextures
.length() - 1);
304 mKnowsCompositor
= aKnowsCompositor
;
309 TextureClient
* PersistentBufferProviderShared::GetTexture(
310 const Maybe
<uint32_t>& aIndex
) {
311 if (aIndex
.isNothing() || !CheckIndex(aIndex
.value())) {
314 return mTextures
[aIndex
.value()];
317 already_AddRefed
<gfx::DrawTarget
>
318 PersistentBufferProviderShared::BorrowDrawTarget(
319 const gfx::IntRect
& aPersistedRect
) {
320 if (!mKnowsCompositor
->GetTextureForwarder() ||
321 !mKnowsCompositor
->GetTextureForwarder()->IPCOpen()) {
325 MOZ_ASSERT(!mSnapshot
);
327 if (IsActivityTracked()) {
328 mKnowsCompositor
->GetActiveResourceTracker()->MarkUsed(this);
330 mKnowsCompositor
->GetActiveResourceTracker()->AddObject(this);
334 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
338 auto previousBackBuffer
= mBack
;
340 TextureClient
* tex
= GetTexture(mBack
);
342 // First try to reuse the current back buffer. If we can do that it means
343 // we can skip copying its content to the new back buffer.
344 if (tex
&& tex
->IsReadLocked()) {
345 // The back buffer is currently used by the compositor, we can't draw
351 // Try to grab an already allocated texture if any is available.
352 for (uint32_t i
= 0; i
< mTextures
.length(); ++i
) {
353 if (!mTextures
[i
]->IsReadLocked()) {
362 // We have to allocate a new texture.
363 if (mTextures
.length() >= mMaxAllowedTextures
) {
364 // We should never need to buffer that many textures, something's wrong.
365 // In theory we throttle the main thread when the compositor can't keep
366 // up, so we shoud never get in a situation where we sent 4 textures to
367 // the compositor and the latter has not released any of them. In
368 // practice, though, the throttling mechanism appears to have some issues,
369 // especially when switching between layer managers (during tab-switch).
370 // To make sure we don't get too far ahead of the compositor, we send a
371 // sync ping to the compositor thread...
372 mKnowsCompositor
->SyncWithCompositor();
374 for (uint32_t i
= 0; i
< mTextures
.length(); ++i
) {
375 if (!mTextures
[i
]->IsReadLocked()) {
376 gfxCriticalNote
<< "Managed to allocate after flush.";
384 gfxCriticalNote
<< "Unexpected BufferProvider over-production.";
385 // It would be pretty bad to keep piling textures up at this point so we
386 // call NotifyInactive to remove some of our textures.
388 // Give up now. The caller can fall-back to a non-shared buffer
394 RefPtr
<TextureClient
> newTexture
=
395 CreateTexture(mKnowsCompositor
, mFormat
, mSize
, mWillReadFrequently
);
397 MOZ_ASSERT(newTexture
);
399 if (mTextures
.append(newTexture
)) {
401 mBack
= Some
<uint32_t>(mTextures
.length() - 1);
410 if (mPermanentBackBuffer
) {
411 // If we have a permanent back buffer lock the selected one and switch to
412 // the permanent one before borrowing the DrawTarget. We will copy back into
413 // the selected one when ReturnDrawTarget is called, before we make it the
415 if (!tex
->Lock(OpenMode::OPEN_WRITE
)) {
418 tex
= mPermanentBackBuffer
;
420 // Copy from the previous back buffer if required.
421 Maybe
<TextureClientAutoLock
> autoReadLock
;
422 TextureClient
* previous
= nullptr;
423 if (mBack
!= previousBackBuffer
&& !aPersistedRect
.IsEmpty()) {
424 if (tex
->HasSynchronization()) {
425 // We are about to read lock a texture that is in use by the compositor
426 // and has synchronization. To prevent possible future contention we
427 // switch to using a permanent back buffer.
428 mPermanentBackBuffer
= CreateTexture(mKnowsCompositor
, mFormat
, mSize
,
429 mWillReadFrequently
);
430 if (!mPermanentBackBuffer
) {
433 if (!tex
->Lock(OpenMode::OPEN_WRITE
)) {
436 tex
= mPermanentBackBuffer
;
439 previous
= GetTexture(previousBackBuffer
);
441 autoReadLock
.emplace(previous
, OpenMode::OPEN_READ
);
445 if (!tex
->Lock(OpenMode::OPEN_READ_WRITE
)) {
449 if (autoReadLock
.isSome() && autoReadLock
->Succeeded() && previous
) {
450 DebugOnly
<bool> success
=
451 previous
->CopyToTextureClient(tex
, &aPersistedRect
, nullptr);
456 mDrawTarget
= tex
->BorrowDrawTarget();
458 // This is simply to ensure the DrawTarget gets initialized, and will detect
459 // a device reset, even if we're on the main thread.
460 mDrawTarget
->ClearRect(Rect(0, 0, 0, 0));
462 if (!mDrawTarget
->IsValid()) {
463 mDrawTarget
= nullptr;
467 RefPtr
<gfx::DrawTarget
> dt(mDrawTarget
);
471 bool PersistentBufferProviderShared::ReturnDrawTarget(
472 already_AddRefed
<gfx::DrawTarget
> aDT
) {
473 RefPtr
<gfx::DrawTarget
> dt(aDT
);
474 MOZ_ASSERT(mDrawTarget
== dt
);
475 // Can't change the current front buffer while its snapshot is borrowed!
476 MOZ_ASSERT(!mSnapshot
);
478 TextureClient
* back
= GetTexture(mBack
);
481 mDrawTarget
= nullptr;
484 // If we have a permanent back buffer we have actually been drawing to that,
485 // so now we must copy to the shared one.
486 if (mPermanentBackBuffer
&& back
) {
487 DebugOnly
<bool> success
=
488 mPermanentBackBuffer
->CopyToTextureClient(back
, nullptr, nullptr);
491 // Let our permanent back buffer know that we have finished drawing.
492 mPermanentBackBuffer
->EndDraw();
503 TextureClient
* PersistentBufferProviderShared::GetTextureClient() {
504 // Can't access the front buffer while drawing.
505 MOZ_ASSERT(!mDrawTarget
);
506 TextureClient
* texture
= GetTexture(mFront
);
509 << "PersistentBufferProviderShared: front buffer unavailable";
513 // Sometimes, for example on tab switch, we re-forward our texture. So if we
514 // are and it is still read locked, then borrow and return our DrawTarget to
515 // force it to be copied to a texture that we will safely read lock.
516 if (texture
->IsReadLocked()) {
517 RefPtr
<DrawTarget
> dt
=
518 BorrowDrawTarget(IntRect(0, 0, mSize
.width
, mSize
.height
));
520 // If we failed to borrow a DrawTarget then all our textures must be read
521 // locked or we failed to create one, so we'll just return the current front
522 // buffer even though that might lead to contention.
524 ReturnDrawTarget(dt
.forget());
525 texture
= GetTexture(mFront
);
528 << "PersistentBufferProviderShared: front buffer unavailable";
533 // If it isn't read locked then make sure it is set as updated, so that we
534 // will always read lock even if we've forwarded the texture before.
535 texture
->SetUpdated();
541 already_AddRefed
<gfx::SourceSurface
>
542 PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget
* aTarget
) {
543 // If we have a permanent back buffer we can always use that to snapshot.
544 if (mPermanentBackBuffer
) {
545 mSnapshot
= mPermanentBackBuffer
->BorrowSnapshot();
546 return do_AddRef(mSnapshot
);
550 auto back
= GetTexture(mBack
);
551 MOZ_ASSERT(back
&& back
->IsLocked());
552 mSnapshot
= back
->BorrowSnapshot();
553 return do_AddRef(mSnapshot
);
556 auto front
= GetTexture(mFront
);
557 if (!front
|| front
->IsLocked()) {
562 if (front
->IsReadLocked() && front
->HasSynchronization()) {
563 // We are about to read lock a texture that is in use by the compositor and
564 // has synchronization. To prevent possible future contention we switch to
565 // using a permanent back buffer.
566 mPermanentBackBuffer
=
567 CreateTexture(mKnowsCompositor
, mFormat
, mSize
, mWillReadFrequently
);
568 if (!mPermanentBackBuffer
||
569 !mPermanentBackBuffer
->Lock(OpenMode::OPEN_READ_WRITE
)) {
573 if (!front
->Lock(OpenMode::OPEN_READ
)) {
577 DebugOnly
<bool> success
=
578 front
->CopyToTextureClient(mPermanentBackBuffer
, nullptr, nullptr);
581 mSnapshot
= mPermanentBackBuffer
->BorrowSnapshot();
582 return do_AddRef(mSnapshot
);
585 if (!front
->Lock(OpenMode::OPEN_READ
)) {
589 mSnapshot
= front
->BorrowSnapshot();
591 return do_AddRef(mSnapshot
);
594 void PersistentBufferProviderShared::ReturnSnapshot(
595 already_AddRefed
<gfx::SourceSurface
> aSnapshot
) {
596 RefPtr
<SourceSurface
> snapshot
= aSnapshot
;
597 MOZ_ASSERT(!snapshot
|| snapshot
== mSnapshot
);
602 if (mDrawTarget
|| mPermanentBackBuffer
) {
606 auto front
= GetTexture(mFront
);
612 void PersistentBufferProviderShared::NotifyInactive() {
613 ClearCachedResources();
616 void PersistentBufferProviderShared::ClearCachedResources() {
617 RefPtr
<TextureClient
> front
= GetTexture(mFront
);
618 RefPtr
<TextureClient
> back
= GetTexture(mBack
);
620 // Clear all textures (except the front and back ones that we just kept).
622 mPermanentBackBuffer
= nullptr;
625 if (mTextures
.append(back
)) {
626 mBack
= Some
<uint32_t>(0);
633 if (front
&& front
!= back
) {
634 if (mTextures
.append(front
)) {
635 mFront
= Some
<uint32_t>(mTextures
.length() - 1);
640 void PersistentBufferProviderShared::Destroy() {
642 mDrawTarget
= nullptr;
644 if (mPermanentBackBuffer
) {
645 mPermanentBackBuffer
->Unlock();
646 mPermanentBackBuffer
= nullptr;
649 for (auto& texture
: mTextures
) {
650 if (texture
&& texture
->IsLocked()) {
659 bool PersistentBufferProviderShared::IsAccelerated() const {
661 // Detect if we're using D2D canvas.
662 if (mWillReadFrequently
|| mTextures
.empty() || !mTextures
[0]) {
665 auto* data
= mTextures
[0]->GetInternalData();
666 if (data
&& data
->AsD3D11TextureData()) {
673 } // namespace layers
674 } // namespace mozilla