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
This commit is contained in:
Ryan Hunt
2018-07-26 11:23:26 -05:00
parent 2d40d6d758
commit bd008ef409
11 changed files with 344 additions and 976 deletions

View File

@@ -116,9 +116,10 @@ ContentClient::PaintState
ContentClient::BeginPaint(PaintedLayer* aLayer,
uint32_t aFlags)
{
PaintState result;
BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
PaintState result;
result.mAsyncPaint = (aFlags & PAINT_ASYNC);
result.mContentType = dest.mBufferContentType;
if (!dest.mCanKeepBufferContents) {
@@ -154,26 +155,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 = result.mAsyncPaint ? OpenMode::OPEN_READ_ASYNC
: OpenMode::OPEN_READ;
OpenMode writeMode = result.mAsyncPaint ? 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();
if (result.mAsyncPaint) {
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 = !result.mAsyncPaint || 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 (result.mAsyncPaint) {
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(!result.mAsyncPaint);
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 +211,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 +241,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 (result.mAsyncPaint) {
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 +274,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 +292,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 +321,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 +360,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 +537,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 +578,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 +594,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 +614,7 @@ ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadba
remoteBuffer->SyncWithObject(mForwarder->GetSyncObject());
}
ContentClient::EndPaint(aReadbackUpdates);
ContentClient::EndPaint(aPaintState, aReadbackUpdates);
}
RefPtr<RotatedBuffer>
@@ -933,18 +843,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 +874,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