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 "mozilla/layers/ContentClient.h"
8 #include "BasicLayers.h" // for BasicLayerManager
9 #include "gfxContext.h" // for gfxContext, etc
10 #include "gfxPlatform.h" // for gfxPlatform
11 #include "gfxEnv.h" // for gfxEnv
13 #include "gfxPoint.h" // for IntSize, gfxPoint
14 #include "gfxUtils.h" // for gfxUtils
15 #include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
16 #include "mozilla/ArrayUtils.h" // for ArrayLength
17 #include "mozilla/gfx/2D.h" // for DrawTarget, Factory
18 #include "mozilla/gfx/BasePoint.h" // for BasePoint
19 #include "mozilla/gfx/BaseSize.h" // for BaseSize
20 #include "mozilla/gfx/Rect.h" // for Rect
21 #include "mozilla/gfx/Types.h"
22 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
23 #include "mozilla/layers/LayerManagerComposite.h"
24 #include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
25 #include "mozilla/layers/LayersTypes.h"
26 #include "mozilla/layers/PaintThread.h"
27 #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
28 #include "nsISupportsImpl.h" // for gfxContext::Release, etc
29 #include "nsIWidget.h" // for nsIWidget
30 #include "nsLayoutUtils.h"
32 # include "gfxWindowsPlatform.h"
35 # include "gfxPlatformGtk.h"
37 #include "ReadbackLayer.h"
48 static TextureFlags
TextureFlagsForContentClientFlags(uint32_t aBufferFlags
) {
49 TextureFlags result
= TextureFlags::NO_FLAGS
;
51 if (aBufferFlags
& ContentClient::BUFFER_COMPONENT_ALPHA
) {
52 result
|= TextureFlags::COMPONENT_ALPHA
;
58 static IntRect
ComputeBufferRect(const IntRect
& aRequestedRect
) {
59 IntRect
rect(aRequestedRect
);
60 // Set a minimum width to guarantee a minimum size of buffers we
61 // allocate (and work around problems on some platforms with smaller
62 // dimensions). 64 used to be the magic number needed to work around
63 // a rendering glitch on b2g (see bug 788411). Now that we don't support
64 // this device anymore we should be fine with 8 pixels as the minimum.
65 rect
.SetWidth(std::max(aRequestedRect
.Width(), 8));
70 already_AddRefed
<ContentClient
> ContentClient::CreateContentClient(
71 CompositableForwarder
* aForwarder
) {
72 LayersBackend backend
= aForwarder
->GetCompositorBackendType();
73 if (backend
!= LayersBackend::LAYERS_OPENGL
&&
74 backend
!= LayersBackend::LAYERS_D3D11
&&
75 backend
!= LayersBackend::LAYERS_WR
&&
76 backend
!= LayersBackend::LAYERS_BASIC
) {
80 bool useDoubleBuffering
= false;
83 if (backend
== LayersBackend::LAYERS_D3D11
) {
84 useDoubleBuffering
= gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
88 if (gfxPlatformGtk::GetPlatform()->UseWaylandDMABufTextures()) {
89 useDoubleBuffering
= true;
94 // We can't use double buffering when using image content with
95 // Xrender support on Linux, as ContentHostDoubleBuffered is not
96 // suited for direct uploads to the server.
97 if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
98 !gfxVars::UseXRender())
101 useDoubleBuffering
= backend
== LayersBackend::LAYERS_BASIC
;
105 if (useDoubleBuffering
|| gfxEnv::ForceDoubleBuffering()) {
106 return MakeAndAddRef
<ContentClientDoubleBuffered
>(aForwarder
);
108 return MakeAndAddRef
<ContentClientSingleBuffered
>(aForwarder
);
111 void ContentClient::Clear() { mBuffer
= nullptr; }
113 ContentClient::PaintState
ContentClient::BeginPaint(PaintedLayer
* aLayer
,
115 BufferDecision dest
= CalculateBufferForPaint(aLayer
, aFlags
);
118 result
.mAsyncPaint
= (aFlags
& PAINT_ASYNC
);
119 result
.mContentType
= dest
.mBufferContentType
;
121 if (!dest
.mCanKeepBufferContents
) {
122 // We're effectively clearing the valid region, so we need to draw
123 // the entire needed region now.
124 MOZ_ASSERT(!dest
.mCanReuseBuffer
);
125 MOZ_ASSERT(dest
.mValidRegion
.IsEmpty());
127 result
.mRegionToInvalidate
= aLayer
->GetValidRegion();
129 #if defined(MOZ_DUMP_PAINTING)
130 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
131 if (result
.mContentType
!= mBuffer
->GetContentType()) {
133 "Invalidating entire rotated buffer (layer %p): content type "
136 } else if ((dest
.mBufferMode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
) !=
137 mBuffer
->HaveBufferOnWhite()) {
139 "Invalidating entire rotated buffer (layer %p): component alpha "
148 result
.mRegionToDraw
.Sub(dest
.mNeededRegion
, dest
.mValidRegion
);
150 if (result
.mRegionToDraw
.IsEmpty()) return result
;
152 // We need to disable rotation if we're going to be resampled when
153 // drawing, because we might sample across the rotation boundary.
154 // Also disable buffer rotation when using webrender.
155 bool canHaveRotation
=
156 gfxPlatform::BufferRotationEnabled() &&
157 !(aFlags
& (PAINT_WILL_RESAMPLE
| PAINT_NO_ROTATION
)) &&
158 !(aLayer
->Manager()->AsWebRenderLayerManager());
159 bool canDrawRotated
= aFlags
& PAINT_CAN_DRAW_ROTATED
;
161 result
.mAsyncPaint
? OpenMode::OPEN_READ_ASYNC
: OpenMode::OPEN_READ
;
162 OpenMode writeMode
= result
.mAsyncPaint
? OpenMode::OPEN_READ_WRITE_ASYNC
163 : OpenMode::OPEN_READ_WRITE
;
165 IntRect drawBounds
= result
.mRegionToDraw
.GetBounds();
167 if (result
.mAsyncPaint
) {
168 result
.mAsyncTask
.reset(new PaintTask());
171 // Try to acquire the back buffer, copy over contents if we are using a new
172 // buffer, and rotate or unrotate the buffer as necessary
173 if (mBuffer
&& dest
.mCanReuseBuffer
) {
174 if (mBuffer
->Lock(writeMode
)) {
175 auto newParameters
= mBuffer
->AdjustedParameters(dest
.mBufferRect
);
178 (!canHaveRotation
&& newParameters
.IsRotated()) ||
179 (!canDrawRotated
&& newParameters
.RectWrapsBuffer(drawBounds
));
181 !result
.mAsyncPaint
|| mBuffer
->BufferRotation() == IntPoint(0, 0);
183 // Only begin a frame and copy over the previous frame if we don't need
184 // to unrotate, or we can try to unrotate it. This is to ensure that we
185 // don't have a paint task that depends on another paint task.
186 if (!needsUnrotate
|| canUnrotate
) {
187 // If we're async painting then begin to capture draw commands
188 if (result
.mAsyncPaint
) {
189 mBuffer
->BeginCapture();
192 // Do not modify result.mRegionToDraw or result.mContentType after this
194 FinalizeFrame(result
);
197 // Try to rotate the buffer or unrotate it if we cannot be rotated
199 if (canUnrotate
&& mBuffer
->UnrotateBufferTo(newParameters
)) {
200 newParameters
.SetUnrotated();
201 mBuffer
->SetParameters(newParameters
);
203 MOZ_ASSERT(GetFrontBuffer());
205 dest
.mBufferRect
= ComputeBufferRect(dest
.mNeededRegion
.GetBounds());
206 dest
.mCanReuseBuffer
= false;
209 mBuffer
->SetParameters(newParameters
);
212 result
.mRegionToDraw
= dest
.mNeededRegion
;
213 dest
.mCanReuseBuffer
= false;
218 MOZ_ASSERT(dest
.mBufferRect
.Contains(result
.mRegionToDraw
.GetBounds()));
220 NS_ASSERTION(!(aFlags
& PAINT_WILL_RESAMPLE
) ||
221 dest
.mBufferRect
== dest
.mNeededRegion
.GetBounds(),
222 "If we're resampling, we need to validate the entire buffer");
224 // We never had a buffer, the buffer wasn't big enough, the content changed
225 // types, or we failed to unrotate the buffer when requested. In any case,
226 // we need to allocate a new one and prepare it for drawing.
227 if (!dest
.mCanReuseBuffer
) {
228 uint32_t bufferFlags
= 0;
229 if (dest
.mBufferMode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
) {
230 bufferFlags
|= BUFFER_COMPONENT_ALPHA
;
233 RefPtr
<RotatedBuffer
> newBuffer
=
234 CreateBuffer(result
.mContentType
, dest
.mBufferRect
, bufferFlags
);
237 if (Factory::ReasonableSurfaceSize(
238 IntSize(dest
.mBufferRect
.Width(), dest
.mBufferRect
.Height()))) {
239 gfxCriticalNote
<< "Failed buffer for " << dest
.mBufferRect
.X() << ", "
240 << dest
.mBufferRect
.Y() << ", "
241 << dest
.mBufferRect
.Width() << ", "
242 << dest
.mBufferRect
.Height();
244 result
.mAsyncTask
= nullptr;
249 if (!newBuffer
->Lock(writeMode
)) {
250 gfxCriticalNote
<< "Failed to lock new back buffer.";
251 result
.mAsyncTask
= nullptr;
256 if (result
.mAsyncPaint
) {
257 newBuffer
->BeginCapture();
260 // If we have an existing front buffer, copy it into the new back buffer
261 RefPtr
<RotatedBuffer
> frontBuffer
= GetFrontBuffer();
263 if (frontBuffer
&& frontBuffer
->Lock(readMode
)) {
264 nsIntRegion updateRegion
= newBuffer
->BufferRect();
265 updateRegion
.Sub(updateRegion
, result
.mRegionToDraw
);
267 if (!updateRegion
.IsEmpty()) {
268 newBuffer
->UpdateDestinationFrom(*frontBuffer
,
269 updateRegion
.GetBounds());
272 frontBuffer
->Unlock();
274 result
.mRegionToDraw
= dest
.mNeededRegion
;
281 NS_ASSERTION(canHaveRotation
|| mBuffer
->BufferRotation() == IntPoint(0, 0),
282 "Rotation disabled, but we have nonzero rotation?");
284 if (result
.mAsyncPaint
) {
285 result
.mAsyncTask
->mTarget
= mBuffer
->GetBufferTarget();
286 result
.mAsyncTask
->mClients
.AppendElement(mBuffer
->GetClient());
287 if (mBuffer
->GetClientOnWhite()) {
288 result
.mAsyncTask
->mClients
.AppendElement(mBuffer
->GetClientOnWhite());
292 nsIntRegion invalidate
;
293 invalidate
.Sub(aLayer
->GetValidRegion(), dest
.mBufferRect
);
294 result
.mRegionToInvalidate
.Or(result
.mRegionToInvalidate
, invalidate
);
296 result
.mClip
= DrawRegionClip::DRAW
;
297 result
.mMode
= dest
.mBufferMode
;
302 void ContentClient::EndPaint(
303 PaintState
& aPaintState
,
304 nsTArray
<ReadbackProcessor::Update
>* aReadbackUpdates
) {
305 if (aPaintState
.mAsyncTask
) {
306 aPaintState
.mAsyncTask
->mCapture
= mBuffer
->EndCapture();
310 static nsIntRegion
ExpandDrawRegion(ContentClient::PaintState
& aPaintState
,
311 RotatedBuffer::DrawIterator
* aIter
,
312 BackendType aBackendType
) {
313 nsIntRegion
* drawPtr
= &aPaintState
.mRegionToDraw
;
315 // The iterators draw region currently only contains the bounds of the
316 // region, this makes it the precise region.
317 aIter
->mDrawRegion
.And(aIter
->mDrawRegion
, aPaintState
.mRegionToDraw
);
318 drawPtr
= &aIter
->mDrawRegion
;
320 if (aBackendType
== BackendType::DIRECT2D
||
321 aBackendType
== BackendType::DIRECT2D1_1
) {
322 // Simplify the draw region to avoid hitting expensive drawing paths
323 // for complex regions.
324 drawPtr
->SimplifyOutwardByArea(100 * 100);
329 DrawTarget
* ContentClient::BorrowDrawTargetForPainting(
330 ContentClient::PaintState
& aPaintState
,
331 RotatedBuffer::DrawIterator
* aIter
/* = nullptr */) {
332 if (aPaintState
.mMode
== SurfaceMode::SURFACE_NONE
|| !mBuffer
) {
336 DrawTarget
* result
= mBuffer
->BorrowDrawTargetForQuadrantUpdate(
337 aPaintState
.mRegionToDraw
.GetBounds(), aIter
);
338 if (!result
|| !result
->IsValid()) {
340 mBuffer
->ReturnDrawTarget(result
);
345 nsIntRegion regionToDraw
=
346 ExpandDrawRegion(aPaintState
, aIter
, result
->GetBackendType());
348 if (aPaintState
.mMode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
||
349 aPaintState
.mContentType
== gfxContentType::COLOR_ALPHA
) {
350 // HaveBuffer() => we have an existing buffer that we must clear
351 for (auto iter
= regionToDraw
.RectIter(); !iter
.Done(); iter
.Next()) {
352 const IntRect
& rect
= iter
.Get();
353 result
->ClearRect(Rect(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height()));
360 void ContentClient::ReturnDrawTarget(gfx::DrawTarget
*& aReturned
) {
361 mBuffer
->ReturnDrawTarget(aReturned
);
364 ContentClient::BufferDecision
ContentClient::CalculateBufferForPaint(
365 PaintedLayer
* aLayer
, uint32_t aFlags
) {
366 gfxContentType layerContentType
= aLayer
->CanUseOpaqueSurface()
367 ? gfxContentType::COLOR
368 : gfxContentType::COLOR_ALPHA
;
371 gfxContentType contentType
;
372 IntRect destBufferRect
;
373 nsIntRegion neededRegion
;
374 nsIntRegion validRegion
= aLayer
->GetValidRegion();
376 bool canReuseBuffer
= !!mBuffer
;
377 bool canKeepBufferContents
= true;
380 mode
= aLayer
->GetSurfaceMode();
381 neededRegion
= aLayer
->GetVisibleRegion().ToUnknownRegion();
384 ValidBufferSize(mBufferSizePolicy
, mBuffer
->BufferRect().Size(),
385 neededRegion
.GetBounds().Size());
386 contentType
= layerContentType
;
388 if (canReuseBuffer
) {
389 if (mBuffer
->BufferRect().Contains(neededRegion
.GetBounds())) {
390 // We don't need to adjust mBufferRect.
391 destBufferRect
= mBuffer
->BufferRect();
392 } else if (neededRegion
.GetBounds().Size() <=
393 mBuffer
->BufferRect().Size()) {
394 // The buffer's big enough but doesn't contain everything that's
395 // going to be visible. We'll move it.
396 destBufferRect
= IntRect(neededRegion
.GetBounds().TopLeft(),
397 mBuffer
->BufferRect().Size());
399 destBufferRect
= neededRegion
.GetBounds();
402 destBufferRect
= ComputeBufferRect(neededRegion
.GetBounds());
405 if (mode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
) {
406 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
407 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
409 if (!aLayer
->GetParent() ||
410 !aLayer
->GetParent()->SupportsComponentAlphaChildren() ||
411 !aLayer
->AsShadowableLayer() ||
412 !aLayer
->AsShadowableLayer()->HasShadow()) {
413 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
415 contentType
= gfxContentType::COLOR
;
420 if ((aFlags
& PAINT_WILL_RESAMPLE
) &&
421 (!neededRegion
.GetBounds().IsEqualInterior(destBufferRect
) ||
422 neededRegion
.GetNumRects() > 1)) {
423 // The area we add to neededRegion might not be painted opaquely.
424 if (mode
== SurfaceMode::SURFACE_OPAQUE
) {
425 contentType
= gfxContentType::COLOR_ALPHA
;
426 mode
= SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA
;
429 // We need to validate the entire buffer, to make sure that only valid
430 // pixels are sampled.
431 neededRegion
= destBufferRect
;
434 // If we have an existing buffer, but the content type has changed or we
435 // have transitioned into/out of component alpha, then we need to recreate
437 bool needsComponentAlpha
= (mode
== SurfaceMode::SURFACE_COMPONENT_ALPHA
);
438 bool backBufferChangedSurface
=
439 mBuffer
&& (contentType
!= mBuffer
->GetContentType() ||
440 needsComponentAlpha
!= mBuffer
->HaveBufferOnWhite());
441 if (canKeepBufferContents
&& backBufferChangedSurface
) {
442 // Restart the decision process; we won't re-enter since we guard on
443 // being able to keep the buffer contents.
444 canReuseBuffer
= false;
445 canKeepBufferContents
= false;
446 validRegion
.SetEmpty();
452 NS_ASSERTION(destBufferRect
.Contains(neededRegion
.GetBounds()),
453 "Destination rect doesn't contain what we need to paint");
456 dest
.mNeededRegion
= std::move(neededRegion
);
457 dest
.mValidRegion
= std::move(validRegion
);
458 dest
.mBufferRect
= destBufferRect
;
459 dest
.mBufferMode
= mode
;
460 dest
.mBufferContentType
= contentType
;
461 dest
.mCanReuseBuffer
= canReuseBuffer
;
462 dest
.mCanKeepBufferContents
= canKeepBufferContents
;
466 bool ContentClient::ValidBufferSize(BufferSizePolicy aPolicy
,
467 const gfx::IntSize
& aBufferSize
,
468 const gfx::IntSize
& aVisibleBoundsSize
) {
470 aVisibleBoundsSize
== aBufferSize
||
471 (SizedToVisibleBounds
!= aPolicy
&& aVisibleBoundsSize
< aBufferSize
));
474 void ContentClient::PrintInfo(std::stringstream
& aStream
, const char* aPrefix
) {
476 aStream
<< nsPrintfCString("ContentClient (0x%p)", this).get();
479 // We pass a null pointer for the ContentClient Forwarder argument, which means
480 // this client will not have a ContentHost on the other side.
481 ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend
)
482 : ContentClient(nullptr, ContainsVisibleBounds
), mBackend(aBackend
) {}
484 void ContentClientBasic::DrawTo(PaintedLayer
* aLayer
, gfx::DrawTarget
* aTarget
,
485 float aOpacity
, gfx::CompositionOp aOp
,
486 gfx::SourceSurface
* aMask
,
487 const gfx::Matrix
* aMaskTransform
) {
492 mBuffer
->DrawTo(aLayer
, aTarget
, aOpacity
, aOp
, aMask
, aMaskTransform
);
495 RefPtr
<RotatedBuffer
> ContentClientBasic::CreateBuffer(gfxContentType aType
,
496 const IntRect
& aRect
,
498 MOZ_ASSERT(!(aFlags
& BUFFER_COMPONENT_ALPHA
));
499 if (aFlags
& BUFFER_COMPONENT_ALPHA
) {
500 gfxDevCrash(LogReason::AlphaWithBasicClient
)
501 << "Asking basic content client for component alpha";
504 IntSize
size(aRect
.Width(), aRect
.Height());
505 RefPtr
<gfx::DrawTarget
> drawTarget
;
508 if (mBackend
== BackendType::CAIRO
&&
509 (aType
== gfxContentType::COLOR
||
510 aType
== gfxContentType::COLOR_ALPHA
)) {
511 RefPtr
<gfxASurface
> surf
= new gfxWindowsSurface(
512 size
, aType
== gfxContentType::COLOR
? gfxImageFormat::X8R8G8B8_UINT32
513 : gfxImageFormat::A8R8G8B8_UINT32
);
514 drawTarget
= gfxPlatform::CreateDrawTargetForSurface(surf
, size
);
519 drawTarget
= gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
521 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType
));
528 return new DrawTargetRotatedBuffer(drawTarget
, nullptr, aRect
,
532 class RemoteBufferReadbackProcessor
: public TextureReadbackSink
{
534 RemoteBufferReadbackProcessor(
535 nsTArray
<ReadbackProcessor::Update
>* aReadbackUpdates
,
536 const IntRect
& aBufferRect
, const nsIntPoint
& aBufferRotation
)
537 : mReadbackUpdates(*aReadbackUpdates
),
538 mBufferRect(aBufferRect
),
539 mBufferRotation(aBufferRotation
) {
540 for (uint32_t i
= 0; i
< mReadbackUpdates
.Length(); ++i
) {
541 mLayerRefs
.push_back(mReadbackUpdates
[i
].mLayer
);
545 virtual void ProcessReadback(
546 gfx::DataSourceSurface
* aSourceSurface
) override
{
547 SourceRotatedBuffer
rotBuffer(aSourceSurface
, nullptr, mBufferRect
,
550 for (uint32_t i
= 0; i
< mReadbackUpdates
.Length(); ++i
) {
551 ReadbackProcessor::Update
& update
= mReadbackUpdates
[i
];
552 nsIntPoint offset
= update
.mLayer
->GetBackgroundLayerOffset();
554 ReadbackSink
* sink
= update
.mLayer
->GetSink();
560 if (!aSourceSurface
) {
561 sink
->SetUnknown(update
.mSequenceCounter
);
565 RefPtr
<DrawTarget
> dt
= sink
->BeginUpdate(update
.mUpdateRect
+ offset
,
566 update
.mSequenceCounter
);
571 dt
->SetTransform(Matrix::Translation(offset
.x
, offset
.y
));
573 rotBuffer
.DrawBufferWithRotation(dt
);
575 update
.mLayer
->GetSink()->EndUpdate(update
.mUpdateRect
+ offset
);
580 nsTArray
<ReadbackProcessor::Update
> mReadbackUpdates
;
581 // This array is used to keep the layers alive until the callback.
582 std::vector
<RefPtr
<Layer
>> mLayerRefs
;
585 nsIntPoint mBufferRotation
;
588 void ContentClientRemoteBuffer::EndPaint(
589 PaintState
& aPaintState
,
590 nsTArray
<ReadbackProcessor::Update
>* aReadbackUpdates
) {
591 MOZ_ASSERT(!mBuffer
|| !mBuffer
->HaveBufferOnWhite() || !aReadbackUpdates
||
592 aReadbackUpdates
->Length() == 0);
594 RemoteRotatedBuffer
* remoteBuffer
= GetRemoteBuffer();
596 if (remoteBuffer
&& remoteBuffer
->IsLocked()) {
597 if (aReadbackUpdates
&& aReadbackUpdates
->Length() > 0) {
598 RefPtr
<TextureReadbackSink
> readbackSink
=
599 new RemoteBufferReadbackProcessor(aReadbackUpdates
,
600 remoteBuffer
->BufferRect(),
601 remoteBuffer
->BufferRotation());
603 remoteBuffer
->GetClient()->SetReadbackSink(readbackSink
);
606 remoteBuffer
->Unlock();
607 remoteBuffer
->SyncWithObject(mForwarder
->GetSyncObject());
610 ContentClient::EndPaint(aPaintState
, aReadbackUpdates
);
613 RefPtr
<RotatedBuffer
> ContentClientRemoteBuffer::CreateBuffer(
614 gfxContentType aType
, const IntRect
& aRect
, uint32_t aFlags
) {
615 // If we hit this assertion, then it might be due to an empty transaction
616 // followed by a real transaction. Our buffers should be created (but not
617 // painted in the empty transaction) and then painted (but not created) in the
618 // real transaction. That is kind of fragile, and this assert will catch
619 // circumstances where we screw that up, e.g., by unnecessarily recreating our
621 MOZ_ASSERT(!mIsNewBuffer
,
622 "Bad! Did we create a buffer twice without painting?");
624 gfx::SurfaceFormat format
=
625 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType
);
627 TextureFlags textureFlags
= TextureFlagsForContentClientFlags(aFlags
);
628 if (aFlags
& BUFFER_COMPONENT_ALPHA
) {
629 textureFlags
|= TextureFlags::COMPONENT_ALPHA
;
632 RefPtr
<RotatedBuffer
> buffer
=
633 CreateBufferInternal(aRect
, format
, textureFlags
);
640 mTextureFlags
= textureFlags
;
645 RefPtr
<RotatedBuffer
> ContentClientRemoteBuffer::CreateBufferInternal(
646 const gfx::IntRect
& aRect
, gfx::SurfaceFormat aFormat
,
647 TextureFlags aFlags
) {
648 TextureAllocationFlags textureAllocFlags
=
649 TextureAllocationFlags::ALLOC_DEFAULT
;
651 RefPtr
<TextureClient
> textureClient
= CreateTextureClientForDrawing(
652 aFormat
, aRect
.Size(), BackendSelector::Content
,
653 aFlags
| ExtraTextureFlags() | TextureFlags::BLOCKING_READ_LOCK
,
656 if (!textureClient
|| !AddTextureClient(textureClient
)) {
660 RefPtr
<TextureClient
> textureClientOnWhite
;
661 if (aFlags
& TextureFlags::COMPONENT_ALPHA
) {
662 TextureAllocationFlags allocFlags
= TextureAllocationFlags::ALLOC_DEFAULT
;
663 if (mForwarder
->SupportsTextureDirectMapping()) {
665 TextureAllocationFlags(allocFlags
| ALLOC_ALLOW_DIRECT_MAPPING
);
667 textureClientOnWhite
=
668 textureClient
->CreateSimilar(mForwarder
->GetCompositorBackendType(),
669 aFlags
| ExtraTextureFlags(), allocFlags
);
670 if (!textureClientOnWhite
|| !AddTextureClient(textureClientOnWhite
)) {
673 // We don't enable the readlock for the white buffer since we always
674 // use them together and waiting on the lock for the black
675 // should be sufficient.
678 return new RemoteRotatedBuffer(textureClient
, textureClientOnWhite
, aRect
,
682 nsIntRegion
ContentClientRemoteBuffer::GetUpdatedRegion(
683 const nsIntRegion
& aRegionToDraw
, const nsIntRegion
& aVisibleRegion
) {
684 nsIntRegion updatedRegion
;
685 if (mIsNewBuffer
|| mBuffer
->DidSelfCopy()) {
686 // A buffer reallocation clears both buffers. The front buffer has all the
687 // content by now, but the back buffer is still clear. Here, in effect, we
688 // are saying to copy all of the pixels of the front buffer to the back.
689 // Also when we self-copied in the buffer, the buffer space
690 // changes and some changed buffer content isn't reflected in the
691 // draw or invalidate region (on purpose!). When this happens, we
692 // need to read back the entire buffer too.
693 updatedRegion
= aVisibleRegion
.GetBounds();
694 mIsNewBuffer
= false;
696 updatedRegion
= aRegionToDraw
;
699 MOZ_ASSERT(mBuffer
, "should have a back buffer by now");
700 NS_ASSERTION(mBuffer
->BufferRect().Contains(aRegionToDraw
.GetBounds()),
701 "Update outside of buffer rect!");
703 return updatedRegion
;
706 void ContentClientRemoteBuffer::Updated(const nsIntRegion
& aRegionToDraw
,
707 const nsIntRegion
& aVisibleRegion
) {
708 nsIntRegion updatedRegion
= GetUpdatedRegion(aRegionToDraw
, aVisibleRegion
);
710 RemoteRotatedBuffer
* remoteBuffer
= GetRemoteBuffer();
712 MOZ_ASSERT(remoteBuffer
&& remoteBuffer
->GetClient());
713 if (remoteBuffer
->HaveBufferOnWhite()) {
714 mForwarder
->UseComponentAlphaTextures(this, remoteBuffer
->GetClient(),
715 remoteBuffer
->GetClientOnWhite());
717 AutoTArray
<CompositableForwarder::TimedTextureClient
, 1> textures
;
718 CompositableForwarder::TimedTextureClient
* t
= textures
.AppendElement();
719 t
->mTextureClient
= remoteBuffer
->GetClient();
720 IntSize size
= remoteBuffer
->GetClient()->GetSize();
721 t
->mPictureRect
= nsIntRect(0, 0, size
.width
, size
.height
);
723 GetForwarder()->UseTextures(this, textures
, Nothing());
726 // This forces a synchronous transaction, so we can swap buffers now
727 // and know that we'll have sole ownership of the old front buffer
728 // by the time we paint next.
729 mForwarder
->UpdateTextureRegion(
731 ThebesBufferData(remoteBuffer
->BufferRect(),
732 remoteBuffer
->BufferRotation()),
734 SwapBuffers(updatedRegion
);
737 void ContentClientRemoteBuffer::Dump(std::stringstream
& aStream
,
738 const char* aPrefix
, bool aDumpHtml
,
739 TextureDumpMode aCompress
) {
740 RemoteRotatedBuffer
* remoteBuffer
= GetRemoteBuffer();
742 // TODO We should combine the OnWhite/OnBlack here an just output a single
745 aStream
<< "\n" << aPrefix
<< "Surface: ";
747 CompositableClient::DumpTextureClient(
748 aStream
, remoteBuffer
? remoteBuffer
->GetClient() : nullptr, aCompress
);
751 void ContentClientDoubleBuffered::Dump(std::stringstream
& aStream
,
752 const char* aPrefix
, bool aDumpHtml
,
753 TextureDumpMode aCompress
) {
754 // TODO We should combine the OnWhite/OnBlack here an just output a single
757 aStream
<< "\n" << aPrefix
<< "Surface: ";
759 CompositableClient::DumpTextureClient(
760 aStream
, mFrontBuffer
? mFrontBuffer
->GetClient() : nullptr, aCompress
);
763 void ContentClientDoubleBuffered::Clear() {
764 ContentClient::Clear();
765 mFrontBuffer
= nullptr;
768 void ContentClientDoubleBuffered::SwapBuffers(
769 const nsIntRegion
& aFrontUpdatedRegion
) {
770 mFrontUpdatedRegion
= aFrontUpdatedRegion
;
772 RefPtr
<RemoteRotatedBuffer
> frontBuffer
= mFrontBuffer
;
773 RefPtr
<RemoteRotatedBuffer
> backBuffer
= GetRemoteBuffer();
775 std::swap(frontBuffer
, backBuffer
);
777 mFrontBuffer
= frontBuffer
;
778 mBuffer
= backBuffer
;
780 mFrontAndBackBufferDiffer
= true;
783 ContentClient::PaintState
ContentClientDoubleBuffered::BeginPaint(
784 PaintedLayer
* aLayer
, uint32_t aFlags
) {
785 EnsureBackBufferIfFrontBuffer();
787 mIsNewBuffer
= false;
789 if (!mFrontBuffer
|| !mBuffer
) {
790 mFrontAndBackBufferDiffer
= false;
793 if (mFrontAndBackBufferDiffer
) {
794 if (mFrontBuffer
->DidSelfCopy()) {
795 // We can't easily draw our front buffer into us, since we're going to be
796 // copying stuff around anyway it's easiest if we just move our situation
797 // to non-rotated while we're at it. If this situation occurs we'll have
798 // hit a self-copy path in PaintThebes before as well anyway.
799 gfx::IntRect backBufferRect
= mBuffer
->BufferRect();
800 backBufferRect
.MoveTo(mFrontBuffer
->BufferRect().TopLeft());
802 mBuffer
->SetBufferRect(backBufferRect
);
803 mBuffer
->SetBufferRotation(IntPoint(0, 0));
805 mBuffer
->SetBufferRect(mFrontBuffer
->BufferRect());
806 mBuffer
->SetBufferRotation(mFrontBuffer
->BufferRotation());
810 return ContentClient::BeginPaint(aLayer
, aFlags
);
813 // Sync front/back buffers content
814 // After executing, the new back buffer has the same (interesting) pixels as
815 // the new front buffer, and mValidRegion et al. are correct wrt the new
816 // back buffer (i.e. as they were for the old back buffer)
817 void ContentClientDoubleBuffered::FinalizeFrame(PaintState
& aPaintState
) {
818 if (!mFrontAndBackBufferDiffer
) {
819 MOZ_ASSERT(!mFrontBuffer
|| !mFrontBuffer
->DidSelfCopy(),
820 "If the front buffer did a self copy then our front and back "
821 "buffer must be different.");
825 MOZ_ASSERT(mFrontBuffer
&& mBuffer
);
826 if (!mFrontBuffer
|| !mBuffer
) {
831 ("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>", this,
832 mFrontUpdatedRegion
.GetBounds().X(), mFrontUpdatedRegion
.GetBounds().Y(),
833 mFrontUpdatedRegion
.GetBounds().Width(),
834 mFrontUpdatedRegion
.GetBounds().Height()));
836 mFrontAndBackBufferDiffer
= false;
838 nsIntRegion updateRegion
= mFrontUpdatedRegion
;
839 if (mFrontBuffer
->DidSelfCopy()) {
840 mFrontBuffer
->ClearDidSelfCopy();
841 updateRegion
= mBuffer
->BufferRect();
844 // No point in sync'ing what we are going to draw over anyway. And if there is
845 // nothing to sync at all, there is nothing to do and we can go home early.
846 updateRegion
.Sub(updateRegion
, aPaintState
.mRegionToDraw
);
847 if (updateRegion
.IsEmpty()) {
851 OpenMode openMode
= aPaintState
.mAsyncPaint
? OpenMode::OPEN_READ_ASYNC
852 : OpenMode::OPEN_READ_ONLY
;
854 if (mFrontBuffer
->Lock(openMode
)) {
855 mBuffer
->UpdateDestinationFrom(*mFrontBuffer
, updateRegion
.GetBounds());
857 if (aPaintState
.mAsyncPaint
) {
858 aPaintState
.mAsyncTask
->mClients
.AppendElement(mFrontBuffer
->GetClient());
859 if (mFrontBuffer
->GetClientOnWhite()) {
860 aPaintState
.mAsyncTask
->mClients
.AppendElement(
861 mFrontBuffer
->GetClientOnWhite());
865 mFrontBuffer
->Unlock();
869 void ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer() {
870 if (!mBuffer
&& mFrontBuffer
) {
871 mBuffer
= CreateBufferInternal(mFrontBuffer
->BufferRect(),
872 mFrontBuffer
->GetFormat(), mTextureFlags
);
877 } // namespace layers
878 } // namespace mozilla