Bug 1371037 - Support for record and replay of ClientPaintedLayers on the main thread. r=dvander

This commit is contained in:
Mason Chang 2017-06-15 16:32:59 -07:00
parent adb899a58e
commit 86004b9db3
2 changed files with 230 additions and 37 deletions

View File

@ -9,10 +9,13 @@
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
#include "gfxContext.h" // for gfxContext
#include "gfx2DGlue.h"
#include "gfxRect.h" // for gfxRect
#include "gfxPrefs.h" // for gfxPrefs
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/gfx/2D.h" // for DrawTarget
#include "mozilla/gfx/DrawEventRecorder.h"
#include "mozilla/gfx/InlineTranslator.h"
#include "mozilla/gfx/Matrix.h" // for Matrix
#include "mozilla/gfx/Rect.h" // for Rect, IntRect
#include "mozilla/gfx/Types.h" // for Float, etc
@ -21,7 +24,7 @@
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "gfx2DGlue.h"
#include "PaintThread.h"
#include "ReadbackProcessor.h"
namespace mozilla {
@ -29,17 +32,48 @@ namespace layers {
using namespace mozilla::gfx;
void
ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
bool
ClientPaintedLayer::EnsureContentClient()
{
PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
js::ProfileEntry::Category::GRAPHICS);
if (!mContentClient) {
mContentClient = ContentClient::CreateContentClient(
ClientManager()->AsShadowForwarder());
NS_ASSERTION(ClientManager()->InDrawing(),
"Can only draw in drawing phase");
mContentClient->BeginPaint();
if (!mContentClient) {
return false;
}
mContentClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
MOZ_ASSERT(mContentClient->GetForwarder());
}
return true;
}
void
ClientPaintedLayer::UpdateContentClient(PaintState& aState)
{
Mutated();
mValidRegion.Or(mValidRegion, aState.mRegionToDraw);
ContentClientRemote *contentClientRemote =
static_cast<ContentClientRemote *>(mContentClient.get());
MOZ_ASSERT(contentClientRemote->GetIPCHandle());
// Hold(this) ensures this layer is kept alive through the current transaction
// The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
// so deleting this Hold for whatever reason will break things.
ClientManager()->Hold(this);
contentClientRemote->Updated(aState.mRegionToDraw,
mVisibleRegion.ToUnknownRegion(),
aState.mDidSelfCopy);
}
uint32_t
ClientPaintedLayer::GetPaintFlags()
{
uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
if (ClientManager()->CompositorMightResample()) {
@ -51,22 +85,47 @@ ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUp
}
}
#endif
PaintState state =
mContentClient->BeginPaintBuffer(this, flags);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
return flags;
}
if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
bool
ClientPaintedLayer::UpdatePaintRegion(PaintState& aState)
{
mValidRegion.Sub(mValidRegion, aState.mRegionToInvalidate);
if (!aState.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
ClientManager()->SetTransactionIncomplete();
mContentClient->EndPaint(nullptr);
return;
return false;
}
// The area that became invalid and is visible needs to be repainted
// (this could be the whole visible area if our buffer switched
// from RGB to RGBA, because we might need to repaint with
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate,
GetLocalVisibleRegion().ToUnknownRegion());
aState.mRegionToInvalidate.And(aState.mRegionToInvalidate,
GetLocalVisibleRegion().ToUnknownRegion());
return true;
}
void
ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
{
PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
js::ProfileEntry::Category::GRAPHICS);
NS_ASSERTION(ClientManager()->InDrawing(),
"Can only draw in drawing phase");
mContentClient->BeginPaint();
uint32_t flags = GetPaintFlags();
PaintState state =
mContentClient->BeginPaintBuffer(this, flags);
if (!UpdatePaintRegion(state)) {
return;
}
bool didUpdate = false;
RotatedContentBuffer::DrawIterator iter;
@ -77,7 +136,7 @@ ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUp
}
continue;
}
SetAntialiasingFlags(this, target);
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
@ -99,36 +158,158 @@ ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUp
mContentClient->EndPaint(aReadbackUpdates);
if (didUpdate) {
Mutated();
UpdateContentClient(state);
}
}
mValidRegion.Or(mValidRegion, state.mRegionToDraw);
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;
}
ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
MOZ_ASSERT(contentClientRemote->GetIPCHandle());
// Not supported yet
if (aReadback && UsedForReadback()) {
return false;
}
// Hold(this) ensures this layer is kept alive through the current transaction
// The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
// so deleting this Hold for whatever reason will break things.
ClientManager()->Hold(this);
contentClientRemote->Updated(state.mRegionToDraw,
mVisibleRegion.ToUnknownRegion(),
state.mDidSelfCopy);
// If we have mask layers, we have to render those first
// In this case, don't record for now.
if (GetMaskLayer()) {
return false;
}
return GetAncestorMaskLayerCount() == 0;
}
already_AddRefed<DrawEventRecorderMemory>
ClientPaintedLayer::RecordPaintedLayer()
{
LayerIntRegion visibleRegion = GetVisibleRegion();
LayerIntRect bounds = visibleRegion.GetBounds();
LayerIntSize size = bounds.Size();
if (visibleRegion.IsEmpty()) {
if (gfxPrefs::LayersDump()) {
printf_stderr("PaintedLayer %p skipping\n", this);
}
return nullptr;
}
nsIntRegion regionToPaint;
regionToPaint.Sub(mVisibleRegion.ToUnknownRegion(), mValidRegion);
if (regionToPaint.IsEmpty()) {
// Do we ever have to do anything if the region to paint is empty
// but we have a painted layer callback?
return nullptr;
}
if (!ClientManager()->GetPaintedLayerCallback()) {
ClientManager()->SetTransactionIncomplete();
return nullptr;
}
// I know this is slow and we should probably use DrawTargetCapture
// But for now, the recording draw target / replay should actually work
// Replay for WR happens in Moz2DIMageRenderer
IntSize imageSize(size.ToUnknownSize());
// DrawTargetRecording also plays back the commands while
// recording, hence the dummy DT. DummyDT will actually have
// the drawn painted layer.
RefPtr<DrawEventRecorderMemory> recorder =
MakeAndAddRef<DrawEventRecorderMemory>();
RefPtr<DrawTarget> dummyDt =
Factory::CreateDrawTarget(gfx::BackendType::SKIA, imageSize, gfx::SurfaceFormat::B8G8R8A8);
RefPtr<DrawTarget> dt =
Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageSize);
dt->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
dt->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(dt);
MOZ_ASSERT(ctx); // already checked the target above
ClientManager()->GetPaintedLayerCallback()(this,
ctx,
visibleRegion.ToUnknownRegion(),
visibleRegion.ToUnknownRegion(),
DrawRegionClip::DRAW,
nsIntRegion(),
ClientManager()->GetPaintedLayerCallbackData());
return recorder.forget();
}
void
ClientPaintedLayer::ReplayPaintedLayer(DrawEventRecorderMemory* aRecorder)
{
LayerIntRegion visibleRegion = GetVisibleRegion();
mContentClient->BeginPaint();
uint32_t flags = GetPaintFlags();
PaintState state =
mContentClient->BeginPaintBuffer(this, flags);
if (!UpdatePaintRegion(state)) {
return;
}
bool didUpdate = false;
RotatedContentBuffer::DrawIterator iter;
while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
if (!target || !target->IsValid()) {
if (target) {
mContentClient->ReturnDrawTargetToBuffer(target);
}
continue;
}
SetAntialiasingFlags(this, target);
// Draw all the things into the actual content client
// This shouldn't exist in the future. For now, its just testing
// to make sure we properly record and can replay all the draw
// commands
std::istream& stream = aRecorder->GetInputStream();
InlineTranslator translator(target, nullptr);
translator.TranslateRecording(stream);
mContentClient->ReturnDrawTargetToBuffer(target);
didUpdate = true;
}
// ending paint w/o any readback updates
// TODO: Fix me
mContentClient->EndPaint(nullptr);
if (didUpdate) {
UpdateContentClient(state);
}
}
void
ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
{
RenderMaskLayers(this);
if (!mContentClient) {
mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
if (!mContentClient) {
if (CanRecordLayer(aReadback)) {
RefPtr<DrawEventRecorderMemory> recorder = RecordPaintedLayer();
if (recorder) {
if (!EnsureContentClient()) {
return;
}
ReplayPaintedLayer(recorder);
return;
}
mContentClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
MOZ_ASSERT(mContentClient->GetForwarder());
}
RenderMaskLayers(this);
if (!EnsureContentClient()) {
return;
}
nsTArray<ReadbackProcessor::Update> readbackUpdates;

View File

@ -19,8 +19,11 @@
#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
namespace mozilla {
namespace layers {
namespace gfx {
class DrawEventRecorderMemory;
};
namespace layers {
class CompositableClient;
class ShadowableLayer;
class SpecificLayerAttributes;
@ -109,6 +112,15 @@ public:
protected:
void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
void RecordThebes();
bool CanRecordLayer(ReadbackProcessor* aReadback);
bool HasMaskLayers();
already_AddRefed<gfx::DrawEventRecorderMemory> RecordPaintedLayer();
void ReplayPaintedLayer(DrawEventRecorderMemory* aRecorder);
bool EnsureContentClient();
uint32_t GetPaintFlags();
void UpdateContentClient(PaintState& aState);
bool UpdatePaintRegion(PaintState& aState);
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;