mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1383916. Prep and flush draw targets on the paint thread with OMTP. r=dvander
This commit is contained in:
parent
91cfa95229
commit
788361e0d9
@ -113,12 +113,33 @@ PaintThread::IsOnPaintThread()
|
||||
void
|
||||
PaintThread::PaintContentsAsync(CompositorBridgeChild* aBridge,
|
||||
gfx::DrawTargetCapture* aCapture,
|
||||
gfx::DrawTarget* aTarget)
|
||||
CapturedPaintState* aState,
|
||||
PrepDrawTargetForPaintingCallback aCallback)
|
||||
{
|
||||
MOZ_ASSERT(IsOnPaintThread());
|
||||
MOZ_ASSERT(aCapture);
|
||||
MOZ_ASSERT(aState);
|
||||
|
||||
DrawTarget* target = aState->mTarget;
|
||||
|
||||
Matrix oldTransform = target->GetTransform();
|
||||
target->SetTransform(aState->mTargetTransform);
|
||||
|
||||
if (!aCallback(aState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw all the things into the actual dest target.
|
||||
aTarget->DrawCapturedDT(aCapture, Matrix());
|
||||
target->DrawCapturedDT(aCapture, Matrix());
|
||||
target->SetTransform(oldTransform);
|
||||
|
||||
// Textureclient forces a flush once we "end paint", so
|
||||
// users of this texture expect all the drawing to be complete.
|
||||
// Force a flush now.
|
||||
// TODO: This might be a performance bottleneck because
|
||||
// main thread painting only does one flush at the end of all paints
|
||||
// whereas we force a flush after each draw target paint.
|
||||
target->Flush();
|
||||
|
||||
if (aBridge) {
|
||||
aBridge->NotifyFinishedAsyncPaint();
|
||||
@ -127,9 +148,12 @@ PaintThread::PaintContentsAsync(CompositorBridgeChild* aBridge,
|
||||
|
||||
void
|
||||
PaintThread::PaintContents(DrawTargetCapture* aCapture,
|
||||
DrawTarget* aTarget)
|
||||
CapturedPaintState* aState,
|
||||
PrepDrawTargetForPaintingCallback aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCapture);
|
||||
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
|
||||
@ -139,14 +163,17 @@ PaintThread::PaintContents(DrawTargetCapture* aCapture,
|
||||
cbc = CompositorBridgeChild::Get();
|
||||
cbc->NotifyBeginAsyncPaint();
|
||||
}
|
||||
|
||||
RefPtr<DrawTargetCapture> capture(aCapture);
|
||||
RefPtr<DrawTarget> target(aTarget);
|
||||
RefPtr<CapturedPaintState> state(aState);
|
||||
|
||||
RefPtr<PaintThread> self = this;
|
||||
RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintContents",
|
||||
[self, cbc, capture, target]() -> void
|
||||
[self, cbc, capture, state, aCallback]() -> void
|
||||
{
|
||||
self->PaintContentsAsync(cbc, capture, target);
|
||||
self->PaintContentsAsync(cbc, capture,
|
||||
state,
|
||||
aCallback);
|
||||
});
|
||||
|
||||
if (cbc) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define MOZILLA_LAYERS_PAINTTHREAD_H
|
||||
|
||||
#include "base/platform_thread.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -20,6 +21,38 @@ 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* aTarget,
|
||||
gfx::DrawTarget* aTargetOnWhite,
|
||||
gfx::Matrix aTargetTransform,
|
||||
SurfaceMode aSurfaceMode,
|
||||
gfxContentType aContentType)
|
||||
: mRegionToDraw(aRegionToDraw)
|
||||
, mTarget(aTarget)
|
||||
, mTargetOnWhite(aTargetOnWhite)
|
||||
, mTargetTransform(aTargetTransform)
|
||||
, mSurfaceMode(aSurfaceMode)
|
||||
, mContentType(aContentType)
|
||||
{}
|
||||
|
||||
nsIntRegion mRegionToDraw;
|
||||
RefPtr<gfx::DrawTarget> mTarget;
|
||||
RefPtr<gfx::DrawTarget> mTargetOnWhite;
|
||||
gfx::Matrix mTargetTransform;
|
||||
SurfaceMode mSurfaceMode;
|
||||
gfxContentType mContentType;
|
||||
|
||||
protected:
|
||||
virtual ~CapturedPaintState() {}
|
||||
};
|
||||
|
||||
typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
|
||||
|
||||
class CompositorBridgeChild;
|
||||
|
||||
class PaintThread final
|
||||
@ -31,7 +64,8 @@ public:
|
||||
static void Shutdown();
|
||||
static PaintThread* Get();
|
||||
void PaintContents(gfx::DrawTargetCapture* aCapture,
|
||||
gfx::DrawTarget* aTarget);
|
||||
CapturedPaintState* aState,
|
||||
PrepDrawTargetForPaintingCallback aCallback);
|
||||
|
||||
// Sync Runnables need threads to be ref counted,
|
||||
// But this thread lives through the whole process.
|
||||
@ -49,7 +83,8 @@ private:
|
||||
void InitOnPaintThread();
|
||||
void PaintContentsAsync(CompositorBridgeChild* aBridge,
|
||||
gfx::DrawTargetCapture* aCapture,
|
||||
gfx::DrawTarget* aTarget);
|
||||
CapturedPaintState* aState,
|
||||
PrepDrawTargetForPaintingCallback aCallback);
|
||||
|
||||
static StaticAutoPtr<PaintThread> sSingleton;
|
||||
static StaticRefPtr<nsIThread> sThread;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "mozilla/gfx/Point.h" // for IntSize
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsLayoutUtils.h" // for invalidation debugging
|
||||
#include "PaintThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -730,6 +731,77 @@ RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
|
||||
return result;
|
||||
}
|
||||
|
||||
DrawTarget*
|
||||
RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
DrawIterator* aIter /* = nullptr */)
|
||||
{
|
||||
if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
|
||||
BUFFER_BOTH, aIter);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*static */ bool
|
||||
RotatedContentBuffer::PrepareDrawTargetForPainting(CapturedPaintState* aState)
|
||||
{
|
||||
RefPtr<DrawTarget> target = aState->mTarget;
|
||||
RefPtr<DrawTarget> whiteTarget = aState->mTargetOnWhite;
|
||||
|
||||
if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
if (!target || !target->IsValid() ||
|
||||
!aState->mTargetOnWhite || !aState->mTargetOnWhite->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;
|
||||
}
|
||||
|
||||
void
|
||||
RotatedContentBuffer::ExpandDrawRegion(PaintState& aPaintState,
|
||||
DrawIterator* aIter,
|
||||
BackendType aBackendType)
|
||||
{
|
||||
nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
|
||||
if (aIter) {
|
||||
// The iterators draw region currently only contains the bounds of the region,
|
||||
// this makes it the precise region.
|
||||
aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
|
||||
drawPtr = &aIter->mDrawRegion;
|
||||
}
|
||||
if (aBackendType == BackendType::DIRECT2D ||
|
||||
aBackendType == BackendType::DIRECT2D1_1) {
|
||||
// Simplify the draw region to avoid hitting expensive drawing paths
|
||||
// for complex regions.
|
||||
drawPtr->SimplifyOutwardByArea(100 * 100);
|
||||
}
|
||||
}
|
||||
|
||||
DrawTarget*
|
||||
RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
|
||||
DrawIterator* aIter /* = nullptr */)
|
||||
@ -744,41 +816,18 @@ RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
|
||||
if (aIter) {
|
||||
// The iterators draw region currently only contains the bounds of the region,
|
||||
// this makes it the precise region.
|
||||
aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
|
||||
drawPtr = &aIter->mDrawRegion;
|
||||
}
|
||||
if (result->GetBackendType() == BackendType::DIRECT2D ||
|
||||
result->GetBackendType() == BackendType::DIRECT2D1_1) {
|
||||
// Simplify the draw region to avoid hitting expensive drawing paths
|
||||
// for complex regions.
|
||||
drawPtr->SimplifyOutwardByArea(100 * 100);
|
||||
}
|
||||
ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
|
||||
// Can't stack allocate refcounted objects.
|
||||
RefPtr<CapturedPaintState> capturedPaintState =
|
||||
MakeAndAddRef<CapturedPaintState>(aPaintState.mRegionToDraw,
|
||||
mDTBuffer,
|
||||
mDTBufferOnWhite,
|
||||
Matrix(),
|
||||
aPaintState.mMode,
|
||||
aPaintState.mContentType);
|
||||
|
||||
if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
if (!mDTBuffer || !mDTBuffer->IsValid() ||
|
||||
!mDTBufferOnWhite || !mDTBufferOnWhite->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 nullptr;
|
||||
}
|
||||
for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& rect = iter.Get();
|
||||
mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
|
||||
ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
|
||||
mDTBufferOnWhite->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
|
||||
ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
|
||||
}
|
||||
} else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
|
||||
// HaveBuffer() => we have an existing buffer that we must clear
|
||||
for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& rect = iter.Get();
|
||||
result->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
|
||||
}
|
||||
if (!RotatedContentBuffer::PrepareDrawTargetForPainting(capturedPaintState)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -22,6 +22,10 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class CapturedPaintState;
|
||||
|
||||
typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
|
||||
|
||||
class TextureClient;
|
||||
class PaintedLayer;
|
||||
|
||||
@ -293,6 +297,14 @@ public:
|
||||
gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
|
||||
DrawIterator* aIter = nullptr);
|
||||
|
||||
gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
DrawIterator* aIter = nullptr);
|
||||
|
||||
void ExpandDrawRegion(PaintState& aPaintState,
|
||||
DrawIterator* aIter,
|
||||
gfx::BackendType aBackendType);
|
||||
|
||||
static bool PrepareDrawTargetForPainting(CapturedPaintState*);
|
||||
enum {
|
||||
BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
|
||||
// component alpha.
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "nsRect.h" // for mozilla::gfx::IntRect
|
||||
#include "PaintThread.h"
|
||||
#include "ReadbackProcessor.h"
|
||||
#include "RotatedBuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
@ -188,6 +189,29 @@ ClientPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUp
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 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.
|
||||
*/
|
||||
bool
|
||||
ClientPaintedLayer::PaintOffMainThread()
|
||||
{
|
||||
@ -202,7 +226,8 @@ ClientPaintedLayer::PaintOffMainThread()
|
||||
|
||||
bool didUpdate = false;
|
||||
RotatedContentBuffer::DrawIterator iter;
|
||||
while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
|
||||
// Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
|
||||
while (DrawTarget* target = mContentClient->BorrowDrawTargetForRecording(state, &iter)) {
|
||||
if (!target || !target->IsValid()) {
|
||||
if (target) {
|
||||
mContentClient->ReturnDrawTargetToBuffer(target);
|
||||
@ -210,14 +235,15 @@ ClientPaintedLayer::PaintOffMainThread()
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't clear the rect here like WRPaintedBlobLayers do
|
||||
// because ContentClient already clears the surface for us during BeginPaint.
|
||||
RefPtr<DrawTargetCapture> captureDT =
|
||||
Factory::CreateCaptureDrawTarget(target->GetBackendType(),
|
||||
target->GetSize(),
|
||||
target->GetFormat());
|
||||
captureDT->SetTransform(target->GetTransform());
|
||||
|
||||
Matrix capturedTransform = target->GetTransform();
|
||||
captureDT->SetTransform(capturedTransform);
|
||||
|
||||
// TODO: Capture AA Flags and reset them in PaintThread
|
||||
SetAntialiasingFlags(this, captureDT);
|
||||
SetAntialiasingFlags(this, target);
|
||||
|
||||
@ -234,12 +260,23 @@ ClientPaintedLayer::PaintOffMainThread()
|
||||
|
||||
ctx = nullptr;
|
||||
|
||||
PaintThread::Get()->PaintContents(captureDT, target);
|
||||
// TODO: Fixup component alpha
|
||||
DrawTarget* targetOnWhite = nullptr;
|
||||
RefPtr<CapturedPaintState> capturedState
|
||||
= MakeAndAddRef<CapturedPaintState>(state.mRegionToDraw,
|
||||
target, targetOnWhite,
|
||||
capturedTransform,
|
||||
state.mMode,
|
||||
state.mContentType);
|
||||
|
||||
PaintThread::Get()->PaintContents(captureDT,
|
||||
capturedState,
|
||||
RotatedContentBuffer::PrepareDrawTargetForPainting);
|
||||
|
||||
mContentClient->ReturnDrawTargetToBuffer(target);
|
||||
|
||||
didUpdate = true;
|
||||
}
|
||||
|
||||
mContentClient->EndPaint(nullptr);
|
||||
|
||||
if (didUpdate) {
|
||||
|
@ -96,7 +96,8 @@ public:
|
||||
virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
|
||||
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
|
||||
|
||||
virtual gfx::DrawTarget* BorrowDrawTargetForRecording(RotatedContentBuffer::PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
|
||||
// Called as part of the layers transation reply. Conveys data about our
|
||||
// buffer(s) from the compositor. If appropriate we should swap references
|
||||
// to our buffers.
|
||||
@ -151,6 +152,11 @@ public:
|
||||
{
|
||||
return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
|
||||
}
|
||||
virtual gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter = nullptr) override
|
||||
{
|
||||
return RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter);
|
||||
}
|
||||
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
|
||||
{
|
||||
BorrowDrawTarget::ReturnDrawTarget(aReturned);
|
||||
@ -234,6 +240,11 @@ public:
|
||||
{
|
||||
return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
|
||||
}
|
||||
virtual gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
RotatedContentBuffer::DrawIterator* aIter = nullptr) override
|
||||
{
|
||||
return RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter);
|
||||
}
|
||||
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
|
||||
{
|
||||
BorrowDrawTarget::ReturnDrawTarget(aReturned);
|
||||
|
@ -5855,3 +5855,6 @@ pref("toolkit.crashreporter.include_context_heap", true);
|
||||
|
||||
// Open noopener links in a new process
|
||||
pref("dom.noopener.newprocess.enabled", true);
|
||||
|
||||
pref("layers.omtp.enabled", false);
|
||||
pref("layers.omtp.force-sync", false);
|
Loading…
Reference in New Issue
Block a user