Bug 1478815 part 8 - Remove buffer operations for ContentClient. r=nical

This commit moves ContentClient from creating a CapturedBufferState for
buffer operations, to performing all of those operations on the
DrawTarget(Capture). Creating a DrawTargetCapture is now performed
by the RotatedBuffer when we BeginPaint, all operations are performed
on this capture, and then it's returned to the ClientPaintedLayer
as a PaintTask.

This commit is an involved refactoring of ContentClient and RotatedBuffer
to get this all to work. Here are the major parts:

1. RotatedBuffer is refactored to always perform operations on a single
   DrawTarget, which may be a single DT, dual DT, or capture.
2. RotatedBuffer adds BeginCapture and EndCapture methods to switch
   which DT is used in operations
3. ContentClient uses the RB capture methods when we are async painting
4. CC::BeginPaint is refactored to only perform capturing on a single
   RotatedBuffer. This is because we can't have the output of one
   PaintTask be the input of a different PaintTask due to the design
   of the Snapshot API.
      a. This can occur, today, by doing a FinalizeFrame only to later
         fail to Unrotate the buffer, causing a new RB to be created
         and painted into
      b. The previous PaintThread code worked because it used the
         buffer operations which didn't use Snapshot's
      c. This is fixed by not doing FinalizeFrame on a buffer if we
         realize we cannot unrotate it, and switching to initializing
         a buffer using the front buffer which should be up to date.
      d. I don't like touching this code, but it passes reftests,
         might be a performance improvement, and I've tested it on
         known regressions from the last time I messed up this code.
5. CC::PrepareForPaint is inlined into BeginPaint because dual draw
   targets can be cleared correctly from a previous commit
6. The code paths in ClientPaintedLayer are unified because we no
   longer need to special case this beyond setting the correct
   ContentClient flag.
7. CapturedPaintState and CapturedBufferState are removed in favor
   of PaintTask. Additionally EndLayer is no longer needed as all
   quadrants of a rotated buffer are in the same capture, so we
   don't need special case flushing code.

MozReview-Commit-ID: 9UI40dwran

--HG--
extra : rebase_source : 2f63464c1f8ca03992700b33838c4aa56608f872
This commit is contained in:
Ryan Hunt 2018-07-26 11:23:26 -05:00
Родитель 6c76c39c97
Коммит af49011c30
8 изменённых файлов: 296 добавлений и 975 удалений

Просмотреть файл

@ -33,31 +33,6 @@ namespace layers {
using namespace gfx;
bool
CapturedBufferState::Copy::CopyBuffer()
{
if (mSource->Lock(OpenMode::OPEN_READ_ONLY)) {
mDestination->UpdateDestinationFrom(*mSource, mBounds);
mSource->Unlock();
return true;
}
return false;
}
bool
CapturedBufferState::Unrotate::UnrotateBuffer()
{
return mBuffer->UnrotateBufferTo(mParameters);
}
bool
CapturedBufferState::PrepareBuffer()
{
return (!mBufferFinalize || mBufferFinalize->CopyBuffer()) &&
(!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer()) &&
(!mBufferInitialize || mBufferInitialize->CopyBuffer());
}
StaticAutoPtr<PaintThread> PaintThread::sSingleton;
StaticRefPtr<nsIThread> PaintThread::sThread;
PlatformThreadId PaintThread::sThreadId;
@ -214,132 +189,6 @@ PaintThread::UpdateRenderMode()
}
}
void
PaintThread::PrepareBuffer(CapturedBufferState* aState)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aState);
// If painting asynchronously, we need to acquire the compositor bridge which
// owns the underlying MessageChannel. Otherwise we leave it null and use
// synchronous dispatch.
RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
RefPtr<CapturedBufferState> state(aState);
cbc->NotifyBeginAsyncPaint(state);
RefPtr<PaintThread> self = this;
RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PrepareBuffer",
[self, cbc, state]() -> void
{
self->AsyncPrepareBuffer(cbc,
state);
});
#ifndef OMTP_FORCE_SYNC
sThread->Dispatch(task.forget());
#else
SyncRunnable::DispatchToThread(sThread, task);
#endif
}
void
PaintThread::AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
CapturedBufferState* aState)
{
AUTO_PROFILER_LABEL("PaintThread::AsyncPrepareBuffer", GRAPHICS);
MOZ_ASSERT(IsOnPaintThread());
MOZ_ASSERT(aState);
if (!aState->PrepareBuffer()) {
gfxCriticalNote << "Failed to prepare buffers on the paint thread.";
}
if (aBridge->NotifyFinishedAsyncWorkerPaint(aState)) {
// We need to dispatch this task to ourselves so it runs after
// AsyncEndLayer
DispatchEndLayerTransaction(aBridge);
}
}
void
PaintThread::PaintContents(CapturedPaintState* aState,
PrepDrawTargetForPaintingCallback aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aState);
if (gfxPrefs::LayersOMTPDumpCapture() && aState->mCapture) {
aState->mCapture->Dump();
}
RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
RefPtr<CapturedPaintState> state(aState);
cbc->NotifyBeginAsyncPaint(state);
RefPtr<PaintThread> self = this;
RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintContents",
[self, cbc, state, aCallback]() -> void
{
self->AsyncPaintContents(cbc,
state,
aCallback);
});
#ifndef OMTP_FORCE_SYNC
sThread->Dispatch(task.forget());
#else
SyncRunnable::DispatchToThread(sThread, task);
#endif
}
void
PaintThread::AsyncPaintContents(CompositorBridgeChild* aBridge,
CapturedPaintState* aState,
PrepDrawTargetForPaintingCallback aCallback)
{
AUTO_PROFILER_LABEL("PaintThread::AsyncPaintContents", GRAPHICS);
MOZ_ASSERT(IsOnPaintThread());
MOZ_ASSERT(aState);
DrawTarget* target = aState->mTargetDual;
DrawTargetCapture* capture = aState->mCapture;
Matrix oldTransform = target->GetTransform();
bool oldPermitsSubpixelAA = target->GetPermitSubpixelAA();
target->SetTransform(capture->GetTransform());
target->SetPermitSubpixelAA(capture->GetPermitSubpixelAA());
if (aCallback(aState)) {
// Draw all the things into the actual dest target.
target->DrawCapturedDT(capture, Matrix());
if (!mDrawTargetsToFlush.Contains(target)) {
mDrawTargetsToFlush.AppendElement(target);
}
if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
// This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
// gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
// DrawTargets do not themselves hold on to UnscaledFonts.
NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
}
}
target->SetTransform(oldTransform);
target->SetPermitSubpixelAA(oldPermitsSubpixelAA);
if (aBridge->NotifyFinishedAsyncWorkerPaint(aState)) {
// We need to dispatch this task to ourselves so it runs after
// AsyncEndLayer
DispatchEndLayerTransaction(aBridge);
}
}
void
PaintThread::QueuePaintTask(PaintTask* aTask)
{
@ -424,25 +273,6 @@ PaintThread::AsyncPaintTaskFinished(CompositorBridgeChild* aBridge,
}
}
void
PaintThread::EndLayer()
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<PaintThread> self = this;
RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncEndLayer",
[self]() -> void
{
self->AsyncEndLayer();
});
#ifndef OMTP_FORCE_SYNC
sThread->Dispatch(task.forget());
#else
SyncRunnable::DispatchToThread(sThread, task);
#endif
}
void
PaintThread::Dispatch(RefPtr<Runnable>& aRunnable)
{
@ -453,21 +283,6 @@ PaintThread::Dispatch(RefPtr<Runnable>& aRunnable)
#endif
}
void
PaintThread::AsyncEndLayer()
{
MOZ_ASSERT(IsOnPaintThread());
// Textureclient forces a flush once we "end paint", so
// users of this texture expect all the drawing to be complete.
// Force a flush now.
for (size_t i = 0; i < mDrawTargetsToFlush.Length(); i++) {
mDrawTargetsToFlush[i]->Flush();
}
mDrawTargetsToFlush.Clear();
}
void
PaintThread::EndLayerTransaction(SyncObjectClient* aSyncObject)
{

Просмотреть файл

@ -25,164 +25,6 @@ class DrawTargetCapture;
namespace layers {
// Holds the key parts from a RotatedBuffer::PaintState
// required to draw the captured paint state
class CapturedPaintState {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CapturedPaintState)
public:
CapturedPaintState(nsIntRegion& aRegionToDraw,
gfx::DrawTarget* aTargetDual,
gfx::DrawTarget* aTarget,
gfx::DrawTarget* aTargetOnWhite,
const gfx::Matrix& aTargetTransform,
SurfaceMode aSurfaceMode,
gfxContentType aContentType)
: mRegionToDraw(aRegionToDraw)
, mTargetDual(aTargetDual)
, mTarget(aTarget)
, mTargetOnWhite(aTargetOnWhite)
, mTargetTransform(aTargetTransform)
, mSurfaceMode(aSurfaceMode)
, mContentType(aContentType)
{}
template<typename F>
void ForEachTextureClient(F aClosure) const
{
aClosure(mTextureClient);
if (mTextureClientOnWhite) {
aClosure(mTextureClientOnWhite);
}
}
void DropTextureClients()
{
mTextureClient = nullptr;
mTextureClientOnWhite = nullptr;
}
nsIntRegion mRegionToDraw;
RefPtr<TextureClient> mTextureClient;
RefPtr<TextureClient> mTextureClientOnWhite;
RefPtr<gfx::DrawTargetCapture> mCapture;
RefPtr<gfx::DrawTarget> mTargetDual;
RefPtr<gfx::DrawTarget> mTarget;
RefPtr<gfx::DrawTarget> mTargetOnWhite;
gfx::Matrix mTargetTransform;
SurfaceMode mSurfaceMode;
gfxContentType mContentType;
protected:
virtual ~CapturedPaintState() {}
};
// Holds the key operations for a ContentClient to prepare
// its buffers for painting
class CapturedBufferState final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CapturedBufferState)
public:
struct Copy {
Copy(RefPtr<RotatedBuffer> aSource,
RefPtr<RotatedBuffer> aDestination,
gfx::IntRect aBounds)
: mSource(aSource)
, mDestination(aDestination)
, mBounds(aBounds)
{}
bool CopyBuffer();
RefPtr<RotatedBuffer> mSource;
RefPtr<RotatedBuffer> mDestination;
gfx::IntRect mBounds;
};
struct Unrotate {
Unrotate(RotatedBuffer::Parameters aParameters,
RefPtr<RotatedBuffer> aBuffer)
: mParameters(aParameters)
, mBuffer(aBuffer)
{}
bool UnrotateBuffer();
RotatedBuffer::Parameters mParameters;
RefPtr<RotatedBuffer> mBuffer;
};
/**
* Prepares the rotated buffers for painting by copying a previous frame
* into the buffer and/or unrotating the pixels and returns whether the
* operations were successful. If this fails a new buffer should be created
* for the frame.
*/
bool PrepareBuffer();
bool HasOperations() const
{
return mBufferFinalize || mBufferUnrotate || mBufferInitialize;
}
template<typename F>
void ForEachTextureClient(F aClosure) const
{
if (mBufferFinalize) {
if (TextureClient* source = mBufferFinalize->mSource->GetClient()) {
aClosure(source);
}
if (TextureClient* sourceOnWhite = mBufferFinalize->mSource->GetClientOnWhite()) {
aClosure(sourceOnWhite);
}
if (TextureClient* destination = mBufferFinalize->mDestination->GetClient()) {
aClosure(destination);
}
if (TextureClient* destinationOnWhite = mBufferFinalize->mDestination->GetClientOnWhite()) {
aClosure(destinationOnWhite);
}
}
if (mBufferUnrotate) {
if (TextureClient* client = mBufferUnrotate->mBuffer->GetClient()) {
aClosure(client);
}
if (TextureClient* clientOnWhite = mBufferUnrotate->mBuffer->GetClientOnWhite()) {
aClosure(clientOnWhite);
}
}
if (mBufferInitialize) {
if (TextureClient* source = mBufferInitialize->mSource->GetClient()) {
aClosure(source);
}
if (TextureClient* sourceOnWhite = mBufferInitialize->mSource->GetClientOnWhite()) {
aClosure(sourceOnWhite);
}
if (TextureClient* destination = mBufferInitialize->mDestination->GetClient()) {
aClosure(destination);
}
if (TextureClient* destinationOnWhite = mBufferInitialize->mDestination->GetClientOnWhite()) {
aClosure(destinationOnWhite);
}
}
}
void DropTextureClients()
{
mBufferFinalize = Nothing();
mBufferUnrotate = Nothing();
mBufferInitialize = Nothing();
}
Maybe<Copy> mBufferFinalize;
Maybe<Unrotate> mBufferUnrotate;
Maybe<Copy> mBufferInitialize;
protected:
~CapturedBufferState() {}
};
typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
// Holds the key operations needed to update a tiled content client on the
// paint thread.
class PaintTask {
@ -228,18 +70,8 @@ public:
void UpdateRenderMode();
void PrepareBuffer(CapturedBufferState* aState);
void PaintContents(CapturedPaintState* aState,
PrepDrawTargetForPaintingCallback aCallback);
void QueuePaintTask(PaintTask* aTask);
// Must be called on the main thread. Signifies that the current
// batch of CapturedPaintStates* for PaintContents have been recorded
// and the main thread is finished recording this layer.
void EndLayer();
// This allows external users to run code on the paint thread.
void Dispatch(RefPtr<Runnable>& aRunnable);
@ -268,18 +100,11 @@ private:
void InitOnPaintThread();
void InitPaintWorkers();
void AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
CapturedBufferState* aState);
void AsyncPaintContents(CompositorBridgeChild* aBridge,
CapturedPaintState* aState,
PrepDrawTargetForPaintingCallback aCallback);
void AsyncPaintTask(CompositorBridgeChild* aBridge,
PaintTask* aTask);
void AsyncPaintTaskFinished(CompositorBridgeChild* aBridge,
PaintTask* aTask);
void AsyncEndLayer();
void AsyncEndLayerTransaction(CompositorBridgeChild* aBridge);
void DispatchEndLayerTransaction(CompositorBridgeChild* aBridge);
@ -289,10 +114,6 @@ private:
static PlatformThreadId sThreadId;
RefPtr<nsIThreadPool> mPaintWorkers;
// This shouldn't be very many elements, so a list should be fine.
// Should only be accessed on the paint thread.
nsTArray<RefPtr<gfx::DrawTarget>> mDrawTargetsToFlush;
};
} // namespace layers

Просмотреть файл

@ -42,9 +42,7 @@ BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
MOZ_ASSERT(mLoanedDrawTarget);
MOZ_ASSERT(aReturned == mLoanedDrawTarget);
if (mLoanedDrawTarget) {
if (mSetTransform) {
mLoanedDrawTarget->SetTransform(mLoanedTransform);
}
mLoanedDrawTarget->SetTransform(mLoanedTransform);
mLoanedDrawTarget = nullptr;
}
aReturned = nullptr;
@ -78,6 +76,26 @@ RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
return result;
}
void
RotatedBuffer::BeginCapture()
{
RefPtr<gfx::DrawTarget> target = GetBufferTarget();
MOZ_ASSERT(!mCapture);
MOZ_ASSERT(target);
mCapture =
Factory::CreateCaptureDrawTarget(target->GetBackendType(),
target->GetSize(),
target->GetFormat());
}
RefPtr<gfx::DrawTargetCapture>
RotatedBuffer::EndCapture()
{
MOZ_ASSERT(mCapture);
return std::move(mCapture);
}
/**
* @param aXSide LEFT means we draw from the left side of the buffer (which
* is drawn on the right side of mBufferRect). RIGHT means we draw from
@ -91,7 +109,6 @@ RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
void
RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
XSide aXSide, YSide aYSide,
ContextSource aSource,
float aOpacity,
gfx::CompositionOp aOperator,
gfx::SourceSurface* aMask,
@ -108,8 +125,7 @@ RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
gfx::Point quadrantTranslation(quadrantRect.X(), quadrantRect.Y());
MOZ_ASSERT(aSource != BUFFER_BOTH);
RefPtr<SourceSurface> snapshot = GetSourceSurface(aSource);
RefPtr<SourceSurface> snapshot = GetBufferSource();
if (!snapshot) {
gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
@ -176,7 +192,7 @@ RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
}
void
RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget,
float aOpacity,
gfx::CompositionOp aOperator,
gfx::SourceSurface* aMask,
@ -187,10 +203,10 @@ RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aS
// See above, in Azure Repeat should always be a safe, even faster choice
// though! Particularly on D2D Repeat should be a lot faster, need to look
// into that. TODO[Bas]
DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, LEFT, TOP, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity, aOperator, aMask, aMaskTransform);
DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity, aOperator,aMask, aMaskTransform);
}
bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion)
@ -228,7 +244,7 @@ RotatedBuffer::DrawTo(PaintedLayer* aLayer,
clipped = true;
}
DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
DrawBufferWithRotation(aTarget, aOpacity, aOp, aMask, aMaskTransform);
if (clipped) {
aTarget->PopClip();
}
@ -240,35 +256,18 @@ RotatedBuffer::UpdateDestinationFrom(const RotatedBuffer& aSource,
{
DrawIterator iter;
while (DrawTarget* destDT =
BorrowDrawTargetForQuadrantUpdate(aUpdateRect, BUFFER_BLACK, &iter)) {
BorrowDrawTargetForQuadrantUpdate(aUpdateRect, &iter)) {
bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
if (isClippingCheap) {
gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
}
aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
aSource.DrawBufferWithRotation(destDT, 1.0, CompositionOp::OP_SOURCE);
if (isClippingCheap) {
destDT->PopClip();
}
ReturnDrawTarget(destDT);
}
if (aSource.HaveBufferOnWhite() && HaveBufferOnWhite()) {
DrawIterator whiteIter;
while (DrawTarget* destDT =
BorrowDrawTargetForQuadrantUpdate(aUpdateRect, BUFFER_WHITE, &whiteIter)) {
bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
if (isClippingCheap) {
gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
}
aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
if (isClippingCheap) {
destDT->PopClip();
}
ReturnDrawTarget(destDT);
}
}
}
static void
@ -330,52 +329,18 @@ RotatedBuffer::AdjustedParameters(const gfx::IntRect& aDestBufferRect) const
bool
RotatedBuffer::UnrotateBufferTo(const Parameters& aParameters)
{
RefPtr<gfx::DrawTarget> dtBuffer = GetDTBuffer();
RefPtr<gfx::DrawTarget> dtBufferOnWhite = GetDTBufferOnWhite();
RefPtr<gfx::DrawTarget> drawTarget = GetDrawTarget();
MOZ_ASSERT(drawTarget && drawTarget->IsValid());
if (mBufferRotation == IntPoint(0,0)) {
IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
IntPoint dest = mBufferRect.TopLeft() - aParameters.mBufferRect.TopLeft();
MOZ_ASSERT(dtBuffer && dtBuffer->IsValid());
dtBuffer->CopyRect(srcRect, dest);
if (HaveBufferOnWhite()) {
MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
dtBufferOnWhite->CopyRect(srcRect, dest);
}
drawTarget->CopyRect(srcRect, dest);
return true;
} else {
// With azure and a data surface perform an buffer unrotate
// (SelfCopy).
unsigned char* data;
IntSize size;
int32_t stride;
SurfaceFormat format;
if (dtBuffer->LockBits(&data, &size, &stride, &format)) {
uint8_t bytesPerPixel = BytesPerPixel(format);
BufferUnrotate(data,
size.width * bytesPerPixel,
size.height, stride,
aParameters.mBufferRotation.x * bytesPerPixel,
aParameters.mBufferRotation.y);
dtBuffer->ReleaseBits(data);
if (HaveBufferOnWhite()) {
MOZ_ASSERT(dtBufferOnWhite && dtBufferOnWhite->IsValid());
dtBufferOnWhite->LockBits(&data, &size, &stride, &format);
uint8_t bytesPerPixel = BytesPerPixel(format);
BufferUnrotate(data,
size.width * bytesPerPixel,
size.height, stride,
aParameters.mBufferRotation.x * bytesPerPixel,
aParameters.mBufferRotation.y);
dtBufferOnWhite->ReleaseBits(data);
}
} else {
return false;
}
return drawTarget->Unrotate(aParameters.mBufferRotation);
}
return true;
}
void
@ -394,10 +359,7 @@ RotatedBuffer::GetContentType() const
DrawTarget*
RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
ContextSource aSource,
DrawIterator* aIter,
bool aSetTransform,
Matrix* aOutMatrix)
DrawIterator* aIter)
{
IntRect bounds = aBounds;
if (aIter) {
@ -422,19 +384,8 @@ RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
bounds = aIter->mDrawRegion.GetBounds();
}
gfx::DrawTarget* dtBuffer = GetDTBuffer();
gfx::DrawTarget* dtBufferOnWhite = GetDTBufferOnWhite();
MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
MOZ_ASSERT(dtBuffer && dtBuffer->IsValid() && dtBufferOnWhite && dtBufferOnWhite->IsValid());
mLoanedDrawTarget = Factory::CreateDualDrawTarget(dtBuffer, dtBufferOnWhite);
} else if (aSource == BUFFER_WHITE) {
mLoanedDrawTarget = dtBufferOnWhite;
} else {
// BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
mLoanedDrawTarget = dtBuffer;
}
mLoanedDrawTarget = GetDrawTarget();
// Figure out which quadrant to draw in
int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
@ -444,18 +395,11 @@ RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
if (aSetTransform) {
mLoanedTransform = mLoanedDrawTarget->GetTransform();
Matrix transform = Matrix(mLoanedTransform)
.PreTranslate(-quadrantRect.X(),
-quadrantRect.Y());
mLoanedDrawTarget->SetTransform(transform);
mSetTransform = true;
} else {
MOZ_ASSERT(aOutMatrix);
*aOutMatrix = Matrix::Translation(-quadrantRect.X(), -quadrantRect.Y());
mSetTransform = false;
}
mLoanedTransform = mLoanedDrawTarget->GetTransform();
Matrix transform = Matrix(mLoanedTransform)
.PreTranslate(-quadrantRect.X(),
-quadrantRect.Y());
mLoanedDrawTarget->SetTransform(transform);
return mLoanedDrawTarget;
}
@ -504,6 +448,19 @@ RemoteRotatedBuffer::Lock(OpenMode aMode)
}
}
if (mTargetOnWhite) {
mTargetDual = Factory::CreateDualDrawTarget(mTarget, mTargetOnWhite);
if (!mTargetDual || !mTargetDual->IsValid()) {
gfxCriticalNote << "Invalid dual draw target " << hexa(mTargetDual)
<< " in RemoteRotatedBuffer::Lock";
Unlock();
return false;
}
} else {
mTargetDual = mTarget;
}
return true;
}
@ -512,6 +469,7 @@ RemoteRotatedBuffer::Unlock()
{
mTarget = nullptr;
mTargetOnWhite = nullptr;
mTargetDual = nullptr;
if (mClient->IsLocked()) {
mClient->Unlock();
@ -538,27 +496,10 @@ RemoteRotatedBuffer::Clear()
mClientOnWhite = nullptr;
}
already_AddRefed<gfx::SourceSurface>
RemoteRotatedBuffer::GetSourceSurface(ContextSource aSource) const
{
if (aSource == ContextSource::BUFFER_BLACK) {
return mTarget->Snapshot();
} else {
MOZ_ASSERT(aSource == ContextSource::BUFFER_WHITE);
return mTargetOnWhite->Snapshot();
}
}
gfx::DrawTarget*
RemoteRotatedBuffer::GetDTBuffer() const
RemoteRotatedBuffer::GetBufferTarget() const
{
return mTarget;
}
gfx::DrawTarget*
RemoteRotatedBuffer::GetDTBufferOnWhite() const
{
return mTargetOnWhite;
return mTargetDual;
}
gfx::SurfaceFormat
@ -567,27 +508,10 @@ DrawTargetRotatedBuffer::GetFormat() const
return mTarget->GetFormat();
}
already_AddRefed<gfx::SourceSurface>
DrawTargetRotatedBuffer::GetSourceSurface(ContextSource aSource) const
{
if (aSource == ContextSource::BUFFER_BLACK) {
return mTarget->Snapshot();
} else {
MOZ_ASSERT(aSource == ContextSource::BUFFER_WHITE);
return mTargetOnWhite->Snapshot();
}
}
gfx::DrawTarget*
DrawTargetRotatedBuffer::GetDTBuffer() const
DrawTargetRotatedBuffer::GetBufferTarget() const
{
return mTarget;
}
gfx::DrawTarget*
DrawTargetRotatedBuffer::GetDTBufferOnWhite() const
{
return mTargetOnWhite;
return mTargetDual;
}
gfx::SurfaceFormat
@ -597,18 +521,10 @@ SourceRotatedBuffer::GetFormat() const
}
already_AddRefed<SourceSurface>
SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const
SourceRotatedBuffer::GetBufferSource() const
{
RefPtr<SourceSurface> surf;
if (aSource == BUFFER_BLACK) {
surf = mSource;
} else {
MOZ_ASSERT(aSource == BUFFER_WHITE);
surf = mSourceOnWhite;
}
MOZ_ASSERT(surf);
return surf.forget();
RefPtr<SourceSurface> sourceDual = mSourceDual;
return sourceDual.forget();
}
} // namespace layers

Просмотреть файл

@ -24,7 +24,6 @@ namespace mozilla {
namespace layers {
class PaintedLayer;
class CapturedBufferState;
class ContentClient;
// Mixin class for classes which need logic for loaning out a draw target.
@ -40,11 +39,6 @@ protected:
// correctly restore state when it is returned.
RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
gfx::Matrix mLoanedTransform;
// This flag denotes whether or not a transform was already applied
// to mLoanedDrawTarget and thus needs to be reset to mLoanedTransform
// upon returning the drawtarget.
bool mSetTransform;
};
/**
@ -71,29 +65,44 @@ public:
RotatedBuffer(const gfx::IntRect& aBufferRect,
const gfx::IntPoint& aBufferRotation)
: mBufferRect(aBufferRect)
: mCapture(nullptr)
, mBufferRect(aBufferRect)
, mBufferRotation(aBufferRotation)
, mDidSelfCopy(false)
{ }
RotatedBuffer()
: mDidSelfCopy(false)
: mCapture(nullptr)
, mDidSelfCopy(false)
{ }
/*
* Which buffer should be drawn to/read from.
/**
* Initializes the rotated buffer to begin capturing all drawing performed
* on it, to be eventually replayed. Callers must call EndCapture, or
* FlushCapture before the rotated buffer is destroyed.
*/
enum ContextSource {
BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha.
BUFFER_WHITE, // The buffer with white background, only valid with component alpha.
BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading.
};
void BeginCapture();
/**
* Finishes a capture and returns it. The capture must be replayed to the
* buffer before it is presented or it will contain invalid contents.
*/
RefPtr<gfx::DrawTargetCapture> EndCapture();
/**
* Returns whether the RotatedBuffer is currently capturing all drawing
* performed on it, to be eventually replayed.
*/
bool IsCapturing() const
{
return !!mCapture;
}
/**
* Draws the contents of this rotated buffer into the specified draw target.
* It is the callers repsonsibility to ensure aTarget is flushed after calling
* this method.
*/
void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource,
void DrawBufferWithRotation(gfx::DrawTarget* aTarget,
float aOpacity = 1.0,
gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
gfx::SourceSurface* aMask = nullptr,
@ -150,10 +159,7 @@ public:
*/
gfx::DrawTarget*
BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
ContextSource aSource,
DrawIterator* aIter,
bool aSetTransform = true,
gfx::Matrix* aOutTransform = nullptr);
DrawIterator* aIter);
struct Parameters {
Parameters(const gfx::IntRect& aBufferRect,
@ -238,10 +244,11 @@ public:
virtual gfx::SurfaceFormat GetFormat() const = 0;
virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
virtual gfx::DrawTarget* GetDTBuffer() const = 0;
virtual gfx::DrawTarget* GetDTBufferOnWhite() const = 0;
virtual already_AddRefed<gfx::SourceSurface> GetBufferSource() const
{
return GetBufferTarget()->Snapshot();
}
virtual gfx::DrawTarget* GetBufferTarget() const = 0;
virtual TextureClient* GetClient() const {
return nullptr;
@ -250,15 +257,11 @@ public:
return nullptr;
}
/**
* Creates a shallow copy of the rotated buffer with the same underlying
* texture clients and draw targets. Rotated buffers are not thread safe,
* so a copy needs to be sent for off main thread painting.
*/
virtual RefPtr<RotatedBuffer> ShallowCopy() const = 0;
protected:
virtual ~RotatedBuffer() {}
virtual ~RotatedBuffer()
{
MOZ_ASSERT(!mCapture);
}
enum XSide {
LEFT, RIGHT
@ -270,18 +273,27 @@ protected:
gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const;
gfx::DrawTarget* GetDrawTarget() const
{
if (mCapture) {
return mCapture;
}
return GetBufferTarget();
}
/*
* If aMask is non-null, then it is used as an alpha mask for rendering this
* buffer. aMaskTransform must be non-null if aMask is non-null, and is used
* to adjust the coordinate space of the mask.
*/
void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
ContextSource aSource,
float aOpacity,
gfx::CompositionOp aOperator,
gfx::SourceSurface* aMask,
const gfx::Matrix* aMaskTransform) const;
RefPtr<gfx::DrawTargetCapture> mCapture;
/** The area of the PaintedLayer that is covered by the buffer as a whole */
gfx::IntRect mBufferRect;
/**
@ -329,28 +341,17 @@ public:
virtual gfx::SurfaceFormat GetFormat() const override;
virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
virtual gfx::DrawTarget* GetDTBuffer() const override;
virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
virtual gfx::DrawTarget* GetBufferTarget() const override;
virtual TextureClient* GetClient() const override { return mClient; }
virtual TextureClient* GetClientOnWhite() const override { return mClientOnWhite; }
virtual RefPtr<RotatedBuffer> ShallowCopy() const override {
return new RemoteRotatedBuffer {
mClient, mClientOnWhite,
mTarget, mTargetOnWhite,
mBufferRect, mBufferRotation
};
}
void SyncWithObject(SyncObjectClient* aSyncObject);
void Clear();
private:
RemoteRotatedBuffer(TextureClient* aClient, TextureClient* aClientOnWhite,
gfx::DrawTarget* aTarget, gfx::DrawTarget* aTargetOnWhite,
gfx::DrawTarget* aTarget, gfx::DrawTarget* aTargetOnWhite, gfx::DrawTarget* aTargetDual,
const gfx::IntRect& aBufferRect,
const gfx::IntPoint& aBufferRotation)
: RotatedBuffer(aBufferRect, aBufferRotation)
@ -358,6 +359,7 @@ private:
, mClientOnWhite(aClientOnWhite)
, mTarget(aTarget)
, mTargetOnWhite(aTargetOnWhite)
, mTargetDual(aTargetDual)
{ }
RefPtr<TextureClient> mClient;
@ -365,6 +367,7 @@ private:
RefPtr<gfx::DrawTarget> mTarget;
RefPtr<gfx::DrawTarget> mTargetOnWhite;
RefPtr<gfx::DrawTarget> mTargetDual;
};
/**
@ -380,32 +383,29 @@ public:
: RotatedBuffer(aBufferRect, aBufferRotation)
, mTarget(aTarget)
, mTargetOnWhite(aTargetOnWhite)
{ }
{
if (mTargetOnWhite) {
mTargetDual = gfx::Factory::CreateDualDrawTarget(mTarget, mTargetOnWhite);
} else {
mTargetDual = mTarget;
}
}
virtual bool IsLocked() override { return false; }
virtual bool Lock(OpenMode aMode) override { return true; }
virtual void Unlock() override {}
virtual bool HaveBuffer() const override { return !!mTarget; }
virtual bool HaveBuffer() const override { return !!mTargetDual; }
virtual bool HaveBufferOnWhite() const override { return !!mTargetOnWhite; }
virtual gfx::SurfaceFormat GetFormat() const override;
virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
virtual gfx::DrawTarget* GetDTBuffer() const override;
virtual gfx::DrawTarget* GetDTBufferOnWhite() const override;
virtual RefPtr<RotatedBuffer> ShallowCopy() const override {
return new DrawTargetRotatedBuffer {
mTarget, mTargetOnWhite,
mBufferRect, mBufferRotation
};
}
virtual gfx::DrawTarget* GetBufferTarget() const override;
private:
RefPtr<gfx::DrawTarget> mTarget;
RefPtr<gfx::DrawTarget> mTargetOnWhite;
RefPtr<gfx::DrawTarget> mTargetDual;
};
/**
@ -421,29 +421,27 @@ public:
: RotatedBuffer(aBufferRect, aBufferRotation)
, mSource(aSource)
, mSourceOnWhite(aSourceOnWhite)
{ }
{
mSourceDual = gfx::Factory::CreateDualSourceSurface(mSource, mSourceOnWhite);
}
virtual bool IsLocked() override { return false; }
virtual bool Lock(OpenMode aMode) override { return false; }
virtual void Unlock() override {}
virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const override;
virtual already_AddRefed<gfx::SourceSurface> GetBufferSource() const override;
virtual gfx::SurfaceFormat GetFormat() const override;
virtual bool HaveBuffer() const override { return !!mSource; }
virtual bool HaveBuffer() const override { return !!mSourceDual; }
virtual bool HaveBufferOnWhite() const override { return !!mSourceOnWhite; }
virtual gfx::DrawTarget* GetDTBuffer() const override { return nullptr; }
virtual gfx::DrawTarget* GetDTBufferOnWhite() const override { return nullptr; }
virtual RefPtr<RotatedBuffer> ShallowCopy() const override {
return nullptr;
}
virtual gfx::DrawTarget* GetBufferTarget() const override { return nullptr; }
private:
RefPtr<gfx::SourceSurface> mSource;
RefPtr<gfx::SourceSurface> mSourceOnWhite;
RefPtr<gfx::SourceSurface> mSourceDual;
};
} // namespace layers

Просмотреть файл

@ -52,23 +52,6 @@ ClientPaintedLayer::EnsureContentClient()
return true;
}
bool
ClientPaintedLayer::CanRecordLayer(ReadbackProcessor* aReadback)
{
// If we don't have a paint thread, this is either not the content
// process or the pref is disabled.
if (!PaintThread::Get()) {
return false;
}
// Not supported yet
if (aReadback && UsedForReadback()) {
return false;
}
return true;
}
void
ClientPaintedLayer::UpdateContentClient(PaintState& aState)
{
@ -95,7 +78,6 @@ ClientPaintedLayer::UpdatePaintRegion(PaintState& aState)
if (!aState.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
ClientManager()->SetTransactionIncomplete();
mContentClient->EndPaint(nullptr);
return false;
}
@ -108,8 +90,17 @@ ClientPaintedLayer::UpdatePaintRegion(PaintState& aState)
return true;
}
void
ClientPaintedLayer::FinishPaintState(PaintState& aState)
{
if (aState.mAsyncTask) {
ClientManager()->SetQueuedAsyncPaints();
PaintThread::Get()->QueuePaintTask(aState.mAsyncTask);
}
}
uint32_t
ClientPaintedLayer::GetPaintFlags()
ClientPaintedLayer::GetPaintFlags(ReadbackProcessor* aReadback)
{
uint32_t flags = ContentClient::PAINT_CAN_DRAW_ROTATED;
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
@ -122,34 +113,43 @@ ClientPaintedLayer::GetPaintFlags()
}
}
#endif
if ((!aReadback || !UsedForReadback()) && PaintThread::Get()) {
flags |= ContentClient::PAINT_ASYNC;
}
return flags;
}
void
ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
{
AUTO_PROFILER_LABEL("ClientPaintedLayer::PaintThebes", GRAPHICS);
AUTO_PROFILER_LABEL("ClientPaintedLayer::RenderLayerWithReadback", GRAPHICS);
NS_ASSERTION(ClientManager()->InDrawing(),
"Can only draw in drawing phase");
uint32_t flags = GetPaintFlags();
RenderMaskLayers(this);
if (!EnsureContentClient()) {
return;
}
nsTArray<ReadbackProcessor::Update> readbackUpdates;
nsIntRegion readbackRegion;
if (aReadback && UsedForReadback()) {
aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
}
uint32_t flags = GetPaintFlags(aReadback);
PaintState state = mContentClient->BeginPaint(this, flags);
if (!UpdatePaintRegion(state)) {
mContentClient->EndPaint(state, nullptr);
FinishPaintState(state);
return;
}
bool didUpdate = false;
RotatedBuffer::DrawIterator iter;
while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
if (!target || !target->IsValid()) {
if (target) {
mContentClient->ReturnDrawTarget(target);
}
continue;
}
SetAntialiasingFlags(this, target);
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
@ -168,152 +168,14 @@ ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUp
didUpdate = true;
}
mContentClient->EndPaint(aReadbackUpdates);
mContentClient->EndPaint(state, &readbackUpdates);
FinishPaintState(state);
if (didUpdate) {
UpdateContentClient(state);
}
}
class MOZ_RAII AutoQueuedAsyncPaint
{
public:
explicit AutoQueuedAsyncPaint(ClientLayerManager* aLayerManager)
: mLayerManager(aLayerManager)
, mQueuedAsyncPaints(false)
{ }
void Queue() { mQueuedAsyncPaints = true; }
~AutoQueuedAsyncPaint()
{
if (mQueuedAsyncPaints) {
mLayerManager->SetQueuedAsyncPaints();
}
}
private:
ClientLayerManager* mLayerManager;
bool mQueuedAsyncPaints;
};
/***
* If we can, let's paint this ClientPaintedLayer's contents off the main thread.
* The essential idea is that we ask the ContentClient for a DrawTarget and record
* the moz2d commands. On the Paint Thread, we replay those commands to the
* destination draw target. There are a couple of lifetime issues here though:
*
* 1) TextureClient owns the underlying buffer and DrawTarget. Because of this
* we have to keep the TextureClient and DrawTarget alive but trick the
* TextureClient into thinking it's already returned the DrawTarget
* since we iterate through different Rects to get DrawTargets*. If
* the TextureClient goes away, the DrawTarget and thus buffer can too.
* 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have
* to Reflush on the Paint Thread
* 3) DrawTarget API is NOT thread safe. We get around this by recording
* on the main thread and painting on the paint thread. Logically,
* ClientLayerManager will force a flushed paint and block the main thread
* if we have another transaction. Thus we have a gap between when the main
* thread records, the paint thread paints, and we block the main thread
* from trying to paint again. The underlying API however is NOT thread safe.
* 4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread
* but block the main thread while the paint thread paints. Async OMTP doesn't block
* the main thread. Sync OMTP is only meant to be used as a debugging tool.
*/
void
ClientPaintedLayer::PaintOffMainThread()
{
AutoQueuedAsyncPaint asyncPaints(ClientManager());
uint32_t flags = GetPaintFlags();
PaintState state = mContentClient->BeginPaint(this, flags | ContentClient::PAINT_ASYNC);
if (state.mBufferState && state.mBufferState->HasOperations()) {
PaintThread::Get()->PrepareBuffer(state.mBufferState);
asyncPaints.Queue();
}
if (!UpdatePaintRegion(state)) {
return;
}
bool didUpdate = false;
RotatedBuffer::DrawIterator iter;
// Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
while (RefPtr<CapturedPaintState> captureState =
mContentClient->BorrowDrawTargetForRecording(state, &iter))
{
DrawTarget* target = captureState->mTargetDual;
if (!target || !target->IsValid()) {
if (target) {
mContentClient->ReturnDrawTarget(target);
}
continue;
}
RefPtr<DrawTargetCapture> captureDT =
Factory::CreateCaptureDrawTarget(target->GetBackendType(),
target->GetSize(),
target->GetFormat());
captureDT->SetTransform(captureState->mTargetTransform);
SetAntialiasingFlags(this, captureDT);
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT);
MOZ_ASSERT(ctx); // already checked the target above
ClientManager()->GetPaintedLayerCallback()(this,
ctx,
iter.mDrawRegion,
iter.mDrawRegion,
state.mClip,
state.mRegionToInvalidate,
ClientManager()->GetPaintedLayerCallbackData());
ctx = nullptr;
captureState->mCapture = captureDT.forget();
PaintThread::Get()->PaintContents(captureState,
ContentClient::PrepareDrawTargetForPainting);
mContentClient->ReturnDrawTarget(target);
asyncPaints.Queue();
didUpdate = true;
}
PaintThread::Get()->EndLayer();
mContentClient->EndPaint(nullptr);
if (didUpdate) {
UpdateContentClient(state);
}
}
void
ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
{
RenderMaskLayers(this);
if (!EnsureContentClient()) {
return;
}
if (CanRecordLayer(aReadback)) {
PaintOffMainThread();
return;
}
nsTArray<ReadbackProcessor::Update> readbackUpdates;
nsIntRegion readbackRegion;
if (aReadback && UsedForReadback()) {
aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
}
PaintThebes(&readbackUpdates);
}
already_AddRefed<PaintedLayer>
ClientLayerManager::CreatePaintedLayer()
{

Просмотреть файл

@ -113,15 +113,13 @@ public:
}
protected:
void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
void RecordThebes();
bool CanRecordLayer(ReadbackProcessor* aReadback);
bool HasMaskLayers();
bool EnsureContentClient();
uint32_t GetPaintFlags();
uint32_t GetPaintFlags(ReadbackProcessor* aReadback);
void UpdateContentClient(PaintState& aState);
bool UpdatePaintRegion(PaintState& aState);
void PaintOffMainThread();
void FinishPaintState(PaintState& aState);
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;

Просмотреть файл

@ -116,9 +116,12 @@ ContentClient::PaintState
ContentClient::BeginPaint(PaintedLayer* aLayer,
uint32_t aFlags)
{
PaintState result;
BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
bool asyncPaint = (aFlags & PAINT_ASYNC);
PaintState result;
result.mAsyncPaint = asyncPaint;
result.mContentType = dest.mBufferContentType;
if (!dest.mCanKeepBufferContents) {
@ -154,26 +157,54 @@ ContentClient::BeginPaint(PaintedLayer* aLayer,
!(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
!(aLayer->Manager()->AsWebRenderLayerManager());
bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
bool asyncPaint = (aFlags & PAINT_ASYNC);
OpenMode readMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC
: OpenMode::OPEN_READ;
OpenMode writeMode = asyncPaint ? OpenMode::OPEN_READ_WRITE_ASYNC
: OpenMode::OPEN_READ_WRITE;
IntRect drawBounds = result.mRegionToDraw.GetBounds();
OpenMode lockMode = asyncPaint ? OpenMode::OPEN_READ_WRITE_ASYNC
: OpenMode::OPEN_READ_WRITE;
if (asyncPaint) {
result.mBufferState = new CapturedBufferState();
result.mAsyncTask = new PaintTask();
}
if (mBuffer) {
if (mBuffer->Lock(lockMode)) {
// Do not modify result.mRegionToDraw or result.mContentType after this call.
Maybe<CapturedBufferState::Copy> bufferFinalize =
FinalizeFrame(result.mRegionToDraw);
// Try to acquire the back buffer, copy over contents if we are using a new buffer,
// and rotate or unrotate the buffer as necessary
if (mBuffer && dest.mCanReuseBuffer) {
if (mBuffer->Lock(writeMode)) {
auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
if (asyncPaint) {
result.mBufferState->mBufferFinalize = std::move(bufferFinalize);
} else if (bufferFinalize) {
bufferFinalize->CopyBuffer();
bool needsUnrotate = (!canHaveRotation && newParameters.IsRotated()) ||
(!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds));
bool canUnrotate = !asyncPaint || mBuffer->BufferRotation() == IntPoint(0,0);
// Only begin a frame and copy over the previous frame if we don't need
// to unrotate, or we can try to unrotate it. This is to ensure that we
// don't have a paint task that depends on another paint task.
if (!needsUnrotate || canUnrotate) {
// If we're async painting then begin to capture draw commands
if (asyncPaint) {
mBuffer->BeginCapture();
}
// Do not modify result.mRegionToDraw or result.mContentType after this call.
FinalizeFrame(result);
}
// Try to rotate the buffer or unrotate it if we cannot be rotated
if (needsUnrotate) {
if (canUnrotate && mBuffer->UnrotateBufferTo(newParameters)) {
newParameters.SetUnrotated();
mBuffer->SetParameters(newParameters);
} else {
MOZ_ASSERT(!asyncPaint);
MOZ_ASSERT(GetFrontBuffer());
mBuffer->Unlock();
dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
dest.mCanReuseBuffer = false;
}
} else {
mBuffer->SetParameters(newParameters);
}
} else {
result.mRegionToDraw = dest.mNeededRegion;
@ -182,55 +213,6 @@ ContentClient::BeginPaint(PaintedLayer* aLayer,
}
}
if (dest.mCanReuseBuffer) {
MOZ_ASSERT(mBuffer);
bool canReuseBuffer = false;
auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
Maybe<CapturedBufferState::Unrotate> bufferUnrotate = Nothing();
if ((!canHaveRotation && newParameters.IsRotated()) ||
(!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
bufferUnrotate = Some(CapturedBufferState::Unrotate {
newParameters,
mBuffer->ShallowCopy(),
});
}
// If we're async painting then return the buffer state to
// be dispatched to the paint thread, otherwise do it now
if (asyncPaint) {
// We cannot do a buffer unrotate if the buffer is already rotated
// and we're async painting as that may fail
if (!bufferUnrotate ||
mBuffer->BufferRotation() == IntPoint(0,0)) {
result.mBufferState->mBufferUnrotate = std::move(bufferUnrotate);
// We can then assume that preparing the buffer will always
// succeed and update our parameters unconditionally
if (result.mBufferState->mBufferUnrotate) {
newParameters.SetUnrotated();
}
mBuffer->SetParameters(newParameters);
canReuseBuffer = true;
}
} else {
if (!bufferUnrotate || bufferUnrotate->UnrotateBuffer()) {
if (bufferUnrotate) {
newParameters.SetUnrotated();
}
mBuffer->SetParameters(newParameters);
canReuseBuffer = true;
}
}
if (!canReuseBuffer) {
dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
dest.mCanReuseBuffer = false;
}
}
MOZ_ASSERT(dest.mBufferRect.Contains(result.mRegionToDraw.GetBounds()));
NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
@ -261,39 +243,30 @@ ContentClient::BeginPaint(PaintedLayer* aLayer,
return result;
}
if (!newBuffer->Lock(lockMode)) {
if (!newBuffer->Lock(writeMode)) {
gfxCriticalNote << "Failed to lock new back buffer.";
Clear();
return result;
}
// If we have an existing front buffer, copy it into the new back buffer
if (mBuffer) {
if (mBuffer->IsLocked()) {
mBuffer->Unlock();
}
if (asyncPaint) {
newBuffer->BeginCapture();
}
// If we have an existing front buffer, copy it into the new back buffer
RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
if (frontBuffer && frontBuffer->Lock(readMode)) {
nsIntRegion updateRegion = newBuffer->BufferRect();
updateRegion.Sub(updateRegion, result.mRegionToDraw);
if (!updateRegion.IsEmpty()) {
auto bufferInitialize = CapturedBufferState::Copy {
mBuffer->ShallowCopy(),
newBuffer->ShallowCopy(),
updateRegion.GetBounds(),
};
// If we're async painting then return the buffer state to
// be dispatched to the paint thread, otherwise do it now
if (asyncPaint) {
result.mBufferState->mBufferInitialize = Some(std::move(bufferInitialize));
} else {
if (!bufferInitialize.CopyBuffer()) {
gfxCriticalNote << "Failed to copy front buffer to back buffer.";
return result;
}
}
newBuffer->UpdateDestinationFrom(*frontBuffer, updateRegion.GetBounds());
}
frontBuffer->Unlock();
} else {
result.mRegionToDraw = dest.mNeededRegion;
}
Clear();
@ -303,6 +276,14 @@ ContentClient::BeginPaint(PaintedLayer* aLayer,
NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
"Rotation disabled, but we have nonzero rotation?");
if (result.mAsyncPaint) {
result.mAsyncTask->mTarget = mBuffer->GetBufferTarget();
result.mAsyncTask->mClients.push_back(mBuffer->GetClient());
if (mBuffer->GetClientOnWhite()) {
result.mAsyncTask->mClients.push_back(mBuffer->GetClientOnWhite());
}
}
nsIntRegion invalidate;
invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
@ -313,22 +294,12 @@ ContentClient::BeginPaint(PaintedLayer* aLayer,
return result;
}
DrawTarget*
ContentClient::BorrowDrawTargetForPainting(ContentClient::PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter /* = nullptr */)
void
ContentClient::EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
{
RefPtr<CapturedPaintState> capturedState =
ContentClient::BorrowDrawTargetForRecording(aPaintState, aIter, true);
if (!capturedState) {
return nullptr;
if (aPaintState.mAsyncTask) {
aPaintState.mAsyncTask->mCapture = mBuffer->EndCapture();
}
if (!ContentClient::PrepareDrawTargetForPainting(capturedState)) {
return nullptr;
}
return capturedState->mTargetDual;
}
nsIntRegion
@ -352,37 +323,37 @@ ExpandDrawRegion(ContentClient::PaintState& aPaintState,
return *drawPtr;
}
RefPtr<CapturedPaintState>
ContentClient::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform)
DrawTarget*
ContentClient::BorrowDrawTargetForPainting(ContentClient::PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter /* = nullptr */)
{
if (aPaintState.mMode == SurfaceMode::SURFACE_NONE || !mBuffer) {
return nullptr;
}
Matrix transform;
DrawTarget* result = mBuffer->BorrowDrawTargetForQuadrantUpdate(
aPaintState.mRegionToDraw.GetBounds(),
RotatedBuffer::BUFFER_BOTH, aIter,
aSetTransform,
&transform);
if (!result) {
aIter);
if (!result || !result->IsValid()) {
if (result) {
mBuffer->ReturnDrawTarget(result);
}
return nullptr;
}
nsIntRegion regionToDraw =
ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
RefPtr<CapturedPaintState> state =
new CapturedPaintState(regionToDraw,
result,
mBuffer->GetDTBuffer(),
mBuffer->GetDTBufferOnWhite(),
transform,
aPaintState.mMode,
aPaintState.mContentType);
return state;
if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA ||
aPaintState.mContentType == gfxContentType::COLOR_ALPHA) {
// HaveBuffer() => we have an existing buffer that we must clear
for (auto iter = regionToDraw.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
result->ClearRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()));
}
}
return result;
}
void
@ -391,40 +362,6 @@ ContentClient::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
mBuffer->ReturnDrawTarget(aReturned);
}
/*static */ bool
ContentClient::PrepareDrawTargetForPainting(CapturedPaintState* aState)
{
MOZ_ASSERT(aState);
RefPtr<DrawTarget> target = aState->mTarget;
RefPtr<DrawTarget> whiteTarget = aState->mTargetOnWhite;
if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
if (!target || !target->IsValid() ||
!whiteTarget || !whiteTarget->IsValid()) {
// This can happen in release builds if allocating one of the two buffers
// failed. This in turn can happen if unreasonably large textures are
// requested.
return false;
}
for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
target->FillRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()),
ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
whiteTarget->FillRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()),
ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
}
} else if (aState->mContentType == gfxContentType::COLOR_ALPHA &&
target->IsValid()) {
// HaveBuffer() => we have an existing buffer that we must clear
for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
target->ClearRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()));
}
}
return true;
}
ContentClient::BufferDecision
ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
uint32_t aFlags)
@ -602,31 +539,6 @@ ContentClientBasic::CreateBuffer(gfxContentType aType,
return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect, IntPoint(0,0));
}
RefPtr<CapturedPaintState>
ContentClientBasic::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform)
{
// BasicLayers does not yet support OMTP.
return nullptr;
}
RefPtr<CapturedPaintState>
ContentClientRemoteBuffer::BorrowDrawTargetForRecording(ContentClient::PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform)
{
RefPtr<CapturedPaintState> cps = ContentClient::BorrowDrawTargetForRecording(aPaintState, aIter, aSetTransform);
if (!cps) {
return nullptr;
}
RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
cps->mTextureClient = remoteBuffer->GetClient();
cps->mTextureClientOnWhite = remoteBuffer->GetClientOnWhite();
return cps.forget();
}
class RemoteBufferReadbackProcessor : public TextureReadbackSink
{
public:
@ -668,7 +580,7 @@ public:
dt->SetTransform(Matrix::Translation(offset.x, offset.y));
rotBuffer.DrawBufferWithRotation(dt, RotatedBuffer::BUFFER_BLACK);
rotBuffer.DrawBufferWithRotation(dt);
update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
}
@ -684,7 +596,7 @@ private:
};
void
ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
ContentClientRemoteBuffer::EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
{
MOZ_ASSERT(!mBuffer || !mBuffer->HaveBufferOnWhite() ||
!aReadbackUpdates || aReadbackUpdates->Length() == 0);
@ -704,7 +616,7 @@ ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadba
remoteBuffer->SyncWithObject(mForwarder->GetSyncObject());
}
ContentClient::EndPaint(aReadbackUpdates);
ContentClient::EndPaint(aPaintState, aReadbackUpdates);
}
RefPtr<RotatedBuffer>
@ -933,18 +845,18 @@ ContentClientDoubleBuffered::BeginPaint(PaintedLayer* aLayer,
// After executing, the new back buffer has the same (interesting) pixels as
// the new front buffer, and mValidRegion et al. are correct wrt the new
// back buffer (i.e. as they were for the old back buffer)
Maybe<CapturedBufferState::Copy>
ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
void
ContentClientDoubleBuffered::FinalizeFrame(PaintState& aPaintState)
{
if (!mFrontAndBackBufferDiffer) {
MOZ_ASSERT(!mFrontBuffer || !mFrontBuffer->DidSelfCopy(),
"If the front buffer did a self copy then our front and back buffer must be different.");
return Nothing();
return;
}
MOZ_ASSERT(mFrontBuffer && mBuffer);
if (!mFrontBuffer || !mBuffer) {
return Nothing();
return;
}
MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
@ -964,16 +876,27 @@ ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
// No point in sync'ing what we are going to draw over anyway. And if there is
// nothing to sync at all, there is nothing to do and we can go home early.
updateRegion.Sub(updateRegion, aRegionToDraw);
updateRegion.Sub(updateRegion, aPaintState.mRegionToDraw);
if (updateRegion.IsEmpty()) {
return Nothing();
return;
}
return Some(CapturedBufferState::Copy {
mFrontBuffer->ShallowCopy(),
mBuffer->ShallowCopy(),
updateRegion.GetBounds(),
});
OpenMode openMode = aPaintState.mAsyncPaint
? OpenMode::OPEN_READ_ASYNC
: OpenMode::OPEN_READ_ONLY;
if (mFrontBuffer->Lock(openMode)) {
mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion.GetBounds());
if (aPaintState.mAsyncPaint) {
aPaintState.mAsyncTask->mClients.push_back(mFrontBuffer->GetClient());
if (mFrontBuffer->GetClientOnWhite()) {
aPaintState.mAsyncTask->mClients.push_back(mFrontBuffer->GetClientOnWhite());
}
}
mFrontBuffer->Unlock();
}
}
void

Просмотреть файл

@ -22,7 +22,7 @@
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/layers/PaintThread.h" // for CapturedBufferState
#include "mozilla/layers/PaintThread.h" // for PaintTask
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/mozalloc.h" // for operator delete
#include "ReadbackProcessor.h" // For ReadbackProcessor::Update
@ -40,10 +40,6 @@ class DrawTarget;
namespace layers {
class PaintedLayer;
class CapturedPaintState;
class CapturedBufferState;
typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
/**
* A compositable client for PaintedLayers. These are different to Image/Canvas
@ -111,6 +107,8 @@ public:
, mMode(SurfaceMode::SURFACE_NONE)
, mClip(DrawRegionClip::NONE)
, mContentType(gfxContentType::SENTINEL)
, mAsyncPaint(false)
, mAsyncTask(nullptr)
{}
nsIntRegion mRegionToDraw;
@ -118,7 +116,8 @@ public:
SurfaceMode mMode;
DrawRegionClip mClip;
gfxContentType mContentType;
RefPtr<CapturedBufferState> mBufferState;
bool mAsyncPaint;
RefPtr<PaintTask> mAsyncTask;
};
enum {
@ -149,7 +148,7 @@ public:
* this.
*/
virtual PaintState BeginPaint(PaintedLayer* aLayer, uint32_t aFlags);
virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) {}
virtual void EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
/**
* Fetch a DrawTarget for rendering. The DrawTarget remains owned by
@ -170,20 +169,8 @@ public:
PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter = nullptr);
/**
* Borrow a draw target for recording. The required transform for correct painting
* is not applied to the returned DrawTarget by default, BUT it is
* required to be whenever drawing does happen.
*/
virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(
PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform = false);
void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
static bool PrepareDrawTargetForPainting(CapturedPaintState*);
enum {
BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
// component alpha.
@ -219,8 +206,12 @@ protected:
* aRegionToDraw is the region which is guaranteed to be overwritten when
* drawing the next frame.
*/
virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) {
return Nothing();
virtual void FinalizeFrame(PaintState& aPaintState) {
}
virtual RefPtr<RotatedBuffer> GetFrontBuffer() const
{
return mBuffer;
}
/**
@ -241,10 +232,6 @@ class ContentClientBasic final : public ContentClient
public:
explicit ContentClientBasic(gfx::BackendType aBackend);
virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform) override;
void DrawTo(PaintedLayer* aLayer,
gfx::DrawTarget* aTarget,
float aOpacity,
@ -294,11 +281,7 @@ public:
bool aDumpHtml=false,
TextureDumpMode aCompress=TextureDumpMode::Compress) override;
virtual RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
RotatedBuffer::DrawIterator* aIter,
bool aSetTransform) override;
virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
virtual void EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
virtual void Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion);
@ -364,7 +347,12 @@ public:
virtual PaintState BeginPaint(PaintedLayer* aLayer, uint32_t aFlags) override;
virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
virtual void FinalizeFrame(PaintState& aPaintState) override;
virtual RefPtr<RotatedBuffer> GetFrontBuffer() const override
{
return mFrontBuffer;
}
virtual TextureInfo GetTextureInfo() const override
{